Files
tvone/app/(page)/create-news/create-news.tsx
T

277 lines
11 KiB
TypeScript
Raw Normal View History

2026-04-19 00:53:01 +01:00
"use client";
import React, { useState, useRef, useEffect } from 'react';
import Image from 'next/image';
import {
LayoutDashboard, Newspaper, Users, BarChart3,
Settings, HelpCircle, Image as ImageIcon,
Type, Calendar, Clock, Tag, User, Save, Eye, Send
} from 'lucide-react';
// Importe o componente que criámos (ajuste o caminho se necessário)
import MultiAspectEditor from '../../components/MultiAspectEditor';
import dynamic from "next/dynamic";
import { AdminHeaderPage } from '@/app/components/layout/admin/header';
import { AdminSideBar } from '@/app/components/layout/admin/sidebar';
// import { keycloak } from '@/app/feature/auth/keycloak-config';
const Editor = dynamic(
() => import("@tinymce/tinymce-react").then((mod) => mod.Editor),
{ ssr: false }
);
export const CreateNewsPage = () => {
// Configuração do Design do Editor para combinar com seu layout
const editorConfig = {
height: 500,
menubar: false,
plugins: [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount'
],
// Adicionei 'blockquote' na toolbar
toolbar: 'undo redo | blocks | ' +
'bold italic forecolor | alignleft aligncenter ' +
'alignright alignjustify | bullist numlist | ' +
'blockquote link image | removeformat | help',
// Customização do aspeto da citação dentro do editor
content_style: `
body {
font-family: Inter, Helvetica, Arial, sans-serif;
font-size: 14px;
color: #334155;
line-height: 1.6;
}
blockquote {
border-left: 4px solid #2563eb; /* Azul da TVone */
padding-left: 1.5rem;
color: #475569;
font-style: italic;
margin: 1.5rem 0;
background: #f8fafc;
padding: 1rem 1.5rem;
border-radius: 0 8px 8px 0;
}
`,
skin: 'oxide',
promotion: false, // Remove o botão "Upgrade" do Tiny
branding: false, // Remove o "Powered by Tiny"
};
// 1. Estados para o Crop
const [tempImage, setTempImage] = useState<string | null>(null);
const [isEditorOpen, setIsEditorOpen] = useState(false);
const [finalCrops, setFinalCrops] = useState<any>(null); // Guarda os Base64 finais
const [content, setContent] = useState('');
const [user, setUser] = useState<{
email?: string;
name?: string;
picture?: string;
} | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
// 2. Lógica de Upload
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = () => {
setTempImage(reader.result as string);
setIsEditorOpen(true);
};
}
};
const triggerUpload = () => {
fileInputRef.current?.click();
};
// Avoid hydration mismatch by waiting for mount
// useEffect(() => {
// keycloak.init({
// onLoad: "check-sso",
// pkceMethod: "S256",
// }).then(async (authenticated) => {
// if (authenticated) {
// const token = keycloak.token!;
// localStorage.setItem("token", token);
// // 👉 send token to Next.js backend
// fetch("/api/session", {
// method: "POST",
// body: JSON.stringify({ token }),
// });
// const res = await fetch("http://localhost:3001/profile/", {
// headers: {
// //Authorization: `Bearer ${token}`,
// },
// });
// const profile = await res.json();
// var keycloakData : {
// email: string,
// email_verified: boolean,
// name: string,
// picture: string,
// roles: string[]
// } = profile.keycloak
// setUser(keycloakData);
// console.log("Profile:", keycloakData);
// }
// });
// }, []);
return (
<div className="p-8 max-w-6xl mx-auto">
<div className="flex justify-between items-center mb-8">
<h1 className="text-2xl font-bold tracking-tight">Criar Nova Notícia</h1>
<div className="flex gap-3">
<button className="px-5 py-2 rounded-lg border border-slate-200 bg-white text-sm font-medium hover:bg-slate-50 transition-all flex items-center gap-2">
<Save size={16}/> Salvar Rascunho
</button>
<button className="px-6 py-2 rounded-lg bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 transition-all shadow-lg shadow-blue-200 flex items-center gap-2">
<Send size={16}/> Publicar Artigo
</button>
</div>
</div>
<div className="grid grid-cols-3 gap-8">
{/* Coluna Principal */}
<div className="col-span-2 space-y-6">
<section className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
{/* Título */}
<div className="p-6 pb-0">
<label className="block text-xs font-bold uppercase text-slate-400 mb-2">Título do Artigo</label>
<input
type="text"
placeholder="Insira o título principal da notícia..."
className="w-full text-xl font-bold border-none focus:ring-0 placeholder:text-slate-300 p-0"
/>
<hr className="my-6 border-slate-100" />
<label className="block text-xs font-bold uppercase text-slate-400 mb-2">Conteúdo Principal</label>
</div>
{/* TinyMCE Editor */}
<div className="border-t border-slate-50">
<Editor
apiKey='dmg1hghyf25x09mtg04hik0034yeadt1h6ai2ou68zhdvw11' // Obtenha em tiny.cloud ou use 'no-api-key' para teste
init={editorConfig}
value={content}
onEditorChange={(newContent) => setContent(newContent)}
/>
</div>
</section>
{/* Resumo (Lead) */}
<div className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm">
<label className="block text-xs font-bold uppercase text-slate-400 mb-2 leading-8">Resumo (Lead)</label>
<textarea
rows={3}
placeholder="Escreva um resumo curto para visualização..."
className="w-full border-none focus:ring-0 resize-none text-slate-600 text-sm italic p-0"
/>
</div>
{/* Tags */}
<div className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm">
<label className="block text-xs font-bold uppercase text-slate-400 mb-2 leading-8">Tags</label>
<input type="text" placeholder="Insira as tags da notícia..." className="w-full border-none focus:ring-0 resize-none text-slate-600 text-sm italic p-0" />
</div>
</div>
{/* Coluna Lateral */}
<div className="space-y-6">
<section className="bg-white rounded-2xl p-6 border border-slate-200 shadow-sm space-y-6">
<div>
<label className="flex items-center gap-2 text-xs font-bold uppercase text-slate-400 mb-3">
<Tag size={14}/> Categoria
</label>
<select className="w-full bg-slate-50 border border-slate-100 rounded-lg px-3 py-2 text-sm outline-none">
<option>Negócios</option>
<option>Tecnologia</option>
<option>Desporto</option>
</select>
</div>
{/* --- INPUT DE UPLOAD ATUALIZADO --- */}
<div>
<label className="flex items-center gap-2 text-xs font-bold uppercase text-slate-400 mb-3">
<ImageIcon size={14}/> Imagem de Capa
</label>
<input
type="file"
hidden
ref={fileInputRef}
accept="image/*"
onChange={handleFileChange}
/>
<div
onClick={triggerUpload}
className={`border-2 border-dashed rounded-xl p-8 flex flex-col items-center justify-center transition-all cursor-pointer group
${finalCrops ? 'border-emerald-200 bg-emerald-50/30' : 'border-slate-100 bg-slate-50/50 hover:bg-slate-50'}`}
>
{finalCrops ? (
<div className="text-center">
<div className="w-12 h-12 bg-emerald-100 text-emerald-600 rounded-full flex items-center justify-center mx-auto mb-2">
<ImageIcon size={24}/>
</div>
<p className="text-[10px] font-bold text-emerald-700 uppercase">3 Formatos Gerados</p>
<p className="text-[9px] text-emerald-500 mt-1">Clique para alterar</p>
</div>
) : (
<>
<div className="w-10 h-10 bg-white rounded-full shadow-sm flex items-center justify-center text-blue-500 mb-2 group-hover:scale-110 transition-transform">
<ImageIcon size={20}/>
</div>
<p className="text-[10px] font-medium text-slate-500 text-center">Clique para carregar e enquadrar</p>
<p className="text-[9px] text-slate-400">Suporta JPG, PNG</p>
</>
)}
</div>
</div>
<div>
<label className="flex items-center gap-2 text-xs font-bold uppercase text-slate-400 mb-3">
<Calendar size={14}/> Agendamento
</label>
<div className="grid grid-cols-2 gap-2">
<input type="date" className="bg-slate-50 border border-slate-100 rounded-lg px-2 py-2 text-[11px] outline-none" />
<input type="time" className="bg-slate-50 border border-slate-100 rounded-lg px-2 py-2 text-[11px] outline-none" />
</div>
</div>
</section>
<button className="w-full py-4 rounded-2xl border border-slate-200 bg-white text-sm font-bold flex items-center justify-center gap-2 hover:bg-slate-50 transition-all text-slate-600">
<Eye size={18}/> Pré-visualizar Notícia
</button>
</div>
</div>
{/* --- MODAL DO EDITOR (Renderiza fora do fluxo quando ativo) --- */}
{isEditorOpen && tempImage && (
<MultiAspectEditor
image={tempImage}
onClose={() => setIsEditorOpen(false)}
onExport={(data) => {
setFinalCrops(data); // Aqui tens o objeto com hero, news, square em Base64
setIsEditorOpen(false);
console.log("Imagens prontas para envio:", data);
}}
/>
)}
</div>
);
};
export default CreateNewsPage;