fix(erp): enable erp pages and menu items

This commit is contained in:
Erik Silva
2025-12-29 17:23:59 -03:00
parent e124a64a5d
commit adbff9bb1e
13990 changed files with 1110936 additions and 59 deletions

View File

@@ -0,0 +1,199 @@
'use client';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Document, docApi } from '@/lib/api-docs';
import {
XMarkIcon,
CheckIcon,
ArrowLeftIcon,
Bars3BottomLeftIcon,
HashtagIcon,
CloudArrowUpIcon,
SparklesIcon
} from "@heroicons/react/24/outline";
import NotionEditor from './NotionEditor';
import DocumentSidebar from './DocumentSidebar';
import { toast } from 'react-hot-toast';
interface DocumentEditorProps {
initialDocument: Partial<Document> | null;
onSave: (doc: Partial<Document>) => void;
onCancel: () => void;
}
export default function DocumentEditor({ initialDocument, onSave, onCancel }: DocumentEditorProps) {
const [document, setDocument] = useState<Partial<Document> | null>(initialDocument);
const [title, setTitle] = useState(initialDocument?.title || '');
const [content, setContent] = useState(initialDocument?.content || '[]');
const [showSidebar, setShowSidebar] = useState(true);
const [saving, setSaving] = useState(false);
// Refs para controle fino de salvamento
const saveTimeout = useRef<NodeJS.Timeout | null>(null);
const lastSaved = useRef({ title: initialDocument?.title || '', content: initialDocument?.content || '[]' });
useEffect(() => {
if (initialDocument) {
setDocument(initialDocument);
setTitle(initialDocument.title || '');
setContent(initialDocument.content || '[]');
lastSaved.current = {
title: initialDocument.title || '',
content: initialDocument.content || '[]'
};
}
}, [initialDocument]);
// Função de Auto-Save Robusta
const autoSave = useCallback(async (newTitle: string, newContent: string) => {
if (!document?.id) return;
setSaving(true);
console.log('💾 Inactivity detected. Saving document...', document.id);
try {
await docApi.updateDocument(document.id, {
title: newTitle,
content: newContent,
status: 'published'
});
// Atualiza o ref do último salvo para evitar loop
lastSaved.current = { title: newTitle, content: newContent };
console.log('✅ Document saved successfully');
} catch (e) {
console.error('❌ Auto-save failed', e);
toast.error('Erro ao salvar automaticamente');
} finally {
// Delay visual para o feedback de "Salvo"
setTimeout(() => setSaving(false), 800);
}
}, [document?.id]);
// Trigger de auto-save com debounce de 1 segundo
useEffect(() => {
if (!document?.id) return;
// Verifica se houve mudança real em relação ao último salvo
if (title === lastSaved.current.title && content === lastSaved.current.content) {
return;
}
if (saveTimeout.current) clearTimeout(saveTimeout.current);
saveTimeout.current = setTimeout(() => {
autoSave(title, content);
}, 1000); // Salva após 1 segundo de inatividade
return () => {
if (saveTimeout.current) clearTimeout(saveTimeout.current);
};
}, [title, content, document?.id, autoSave]);
const navigateToDoc = async (doc: Document) => {
// Antes de navegar, salva o atual se necessário
if (title !== lastSaved.current.title || content !== lastSaved.current.content) {
await autoSave(title, content);
}
setDocument(doc);
setTitle(doc.title);
setContent(doc.content);
lastSaved.current = { title: doc.title, content: doc.content };
toast.success(`Abrindo: ${doc.title || 'Untitled'}`, { duration: 1000, position: 'bottom-center' });
};
return (
<div className="fixed inset-0 z-[60] bg-white dark:bg-zinc-950 flex flex-col">
{/* Header Clean */}
<header className="h-16 border-b border-zinc-200 dark:border-zinc-800 px-6 flex items-center justify-between bg-white dark:bg-zinc-950 z-20 shrink-0">
<div className="flex items-center gap-4 flex-1">
<button
onClick={async () => {
if (title !== lastSaved.current.title || content !== lastSaved.current.content) {
await autoSave(title, content);
}
onCancel();
}}
className="p-2 text-zinc-400 hover:text-zinc-900 dark:hover:text-white rounded-xl hover:bg-zinc-100 dark:hover:bg-zinc-900 transition-all"
title="Back to list"
>
<ArrowLeftIcon className="w-5 h-5" />
</button>
<button
onClick={() => setShowSidebar(!showSidebar)}
className={`p-2 rounded-xl transition-all ${showSidebar ? 'text-brand-500 bg-brand-50/50 dark:bg-brand-500/10' : 'text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-900'}`}
title={showSidebar ? "Hide Navigation" : "Show Navigation"}
>
<Bars3BottomLeftIcon className="w-5 h-5" />
</button>
<div className="h-4 w-px bg-zinc-200 dark:bg-zinc-800 mx-2" />
<div className="flex items-center gap-2 flex-1">
<HashtagIcon className="w-4 h-4 text-zinc-300" />
<input
type="text"
placeholder="Untitled Document"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="text-lg font-bold bg-transparent border-none outline-none text-zinc-900 dark:text-white placeholder:text-zinc-300 dark:placeholder:text-zinc-600 w-full max-w-xl"
/>
</div>
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-zinc-50 dark:bg-zinc-900 border border-zinc-100 dark:border-zinc-800">
{saving ? (
<div className="flex items-center gap-2 text-brand-500">
<div className="w-1.5 h-1.5 bg-brand-500 rounded-full animate-pulse" />
<span className="text-[10px] font-black uppercase tracking-widest leading-none">Saving...</span>
</div>
) : (
<div className="flex items-center gap-2 text-emerald-500">
<CheckIcon className="w-3.5 h-3.5" />
<span className="text-[10px] font-black uppercase tracking-widest text-zinc-500 dark:text-zinc-400 leading-none">Sync Saved</span>
</div>
)}
</div>
</div>
</header>
<div className="flex-1 flex overflow-hidden">
{/* Lateral Sidebar (Navegação) */}
{showSidebar && document?.id && (
<DocumentSidebar
key={document.id} // Re-render sidebar on doc change to ensure fresh data
documentId={document.id}
onNavigate={navigateToDoc}
/>
)}
{/* Área do Editor */}
<main className="flex-1 overflow-y-auto bg-white dark:bg-zinc-950 flex flex-col items-center custom-scrollbar">
<div className="w-full max-w-[850px] px-12 md:px-24 py-16 animate-in slide-in-from-bottom-2 duration-500">
{/* Title Hero Display */}
<div className="mb-14 group">
<h1 className="text-5xl font-black text-zinc-900 dark:text-white tracking-tighter leading-tight">
{title || 'Untitled'}
</h1>
<div className="mt-6 flex items-center gap-4 text-zinc-400">
<SparklesIcon className="w-4 h-4 text-brand-500" />
<div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full bg-brand-500 flex items-center justify-center text-[10px] font-bold text-white uppercase">Me</div>
<span className="text-[10px] font-black uppercase tracking-widest">Editing Mode Real-time Sync</span>
</div>
</div>
</div>
<NotionEditor
documentId={document?.id}
initialContent={content}
onChange={setContent}
/>
</div>
</main>
</div>
</div>
);
}