'use client' import { useState, useMemo } from 'react' import { Note } from '@/lib/types' import { formatDistanceToNow } from 'date-fns' import { fr } from 'date-fns/locale/fr' import { enUS } from 'date-fns/locale/en-US' import { faIR } from 'date-fns/locale/fa-IR' import { formatAbsoluteDateLocalized } from '@/lib/utils/format-localized-date' import { X, Info, Clock, Hash, Book, FileText, Calendar, Tag, ChevronRight, Trash2, RotateCcw, Loader2, Check, History as HistoryIcon, Network, Copy, ExternalLink } from 'lucide-react' import { cn } from '@/lib/utils' import { useLanguage } from '@/lib/i18n' import { useNotebooks } from '@/context/notebooks-context' import { LabelBadge } from './label-badge' import { NoteHistoryModal } from './note-history-modal' import { NoteNetworkTab } from './note-network-tab' import { enableNoteHistory, commitNoteHistory, getNoteHistory, deleteNoteHistoryEntry, restoreNoteVersion } from '@/app/actions/notes' import { useEffect } from 'react' type Tab = 'info' | 'versions' | 'network' interface NoteDocumentInfoPanelProps { note: Note content: string onClose: () => void onNoteRestored?: (note: Note) => void } function getLocale(lang: string) { if (lang === 'fr') return fr if (lang === 'fa') return faIR return enUS } function wordCount(text: string) { return text.replace(/<[^>]+>/g, ' ').trim().split(/\s+/).filter(Boolean).length } function charCount(text: string) { return text.replace(/<[^>]+>/g, '').length } function lineCount(text: string) { const plain = text.replace(/<[^>]+>/g, '\n') return plain.trim() ? plain.split('\n').length : 0 } function equationCount(text: string) { const block = (text.match(/\$\$[\s\S]+?\$\$/g) || []).length const inline = (text.match(/\$[^$\n]+?\$/g) || []).length return block + inline } function imageCount(text: string) { const md = (text.match(/!\[[^\]]*\]\([^)]+\)/g) || []).length const html = (text.match(/('info') const [showHistory, setShowHistory] = useState(false) const [historyEnabled, setHistoryEnabled] = useState(note.historyEnabled ?? false) const [isSavingVersion, setIsSavingVersion] = useState(false) const [versionSaved, setVersionSaved] = useState(false) const [historyEntries, setHistoryEntries] = useState([]) const [isLoadingHistory, setIsLoadingHistory] = useState(false) const [isDeleting, setIsDeleting] = useState(null) const [isRestoring, setIsRestoring] = useState(null) const [copiedId, setCopiedId] = useState(false) const locale = getLocale(language) const displayNoteType = useMemo(() => { if (note.sourceUrl) return t('notes.noteTypes.clip') const map: Record = { richtext: t('notes.noteTypes.richtext'), markdown: t('notes.noteTypes.markdown'), text: t('notes.noteTypes.text'), checklist: t('notes.noteTypes.checklist'), } return map[note.type] || note.type }, [t, note.type, note.sourceUrl]) useEffect(() => { if (activeTab === 'versions' && historyEnabled) { loadHistory() } }, [activeTab, historyEnabled, note.id]) const loadHistory = async () => { setIsLoadingHistory(true) try { const entries = await getNoteHistory(note.id, 50) setHistoryEntries(entries) } catch (e) { console.error(e) } finally { setIsLoadingHistory(false) } } const handleDeleteVersion = async (entryId: string) => { if (!confirm(t('documentInfo.deleteVersionConfirm'))) return setIsDeleting(entryId) try { await deleteNoteHistoryEntry(note.id, entryId) setHistoryEntries(prev => prev.filter(e => e.id !== entryId)) } catch (e) { console.error(e) } finally { setIsDeleting(null) } } const handleRestoreVersion = async (entryId: string) => { setIsRestoring(entryId) try { const restored = await restoreNoteVersion(note.id, entryId) onNoteRestored?.(restored) loadHistory() } catch (e) { console.error(e) } finally { setIsRestoring(null) } } const notebook = useMemo( () => notebooks.find(nb => nb.id === note.notebookId), [notebooks, note.notebookId] ) const words = useMemo(() => wordCount(content), [content]) const chars = useMemo(() => charCount(content), [content]) const lines = useMemo(() => lineCount(content), [content]) const equations = useMemo(() => equationCount(content), [content]) const images = useMemo(() => imageCount(content), [content]) const createdAt = note.createdAt ? new Date(note.createdAt as unknown as string) : null const updatedAt = note.contentUpdatedAt ? new Date(note.contentUpdatedAt as unknown as string) : null return ( <>
{/* Header tabs */}
{(['info', 'versions', 'network'] as Tab[]).map(tab => ( ))}
{/* Body */}
{/* ── INFO TAB ── */} {activeTab === 'info' && (
{/* Stats — grand display numbers */}
{words} {t('documentInfo.wordsLabel')}
{chars} {t('documentInfo.charactersLabel')}
{[ { value: lines, label: t('documentInfo.linesLabel') }, { value: equations, label: t('documentInfo.equationsLabel') }, { value: images, label: t('documentInfo.imagesLabel') }, ].map(({ value, label }) => (
{value} {label}
))}
{notebook && (

{t('documentInfo.notebookLabel')}

{notebook.name}

)}

{t('documentInfo.typeLabel')}

{displayNoteType}

{note.sourceUrl && (

{t('documentInfo.sourceWebLabel')}

{note.sourceUrl}
)} {createdAt && (

{t('documentInfo.createdLabel')}

{formatAbsoluteDateLocalized(createdAt, language, 'd MMM yyyy', locale)}

{formatDistanceToNow(createdAt, { addSuffix: true, locale })}

)} {updatedAt && (

{t('documentInfo.modifiedLabel')}

{formatAbsoluteDateLocalized(updatedAt, language, 'd MMM yyyy · HH:mm', locale)}

{formatDistanceToNow(updatedAt, { addSuffix: true, locale })}

)} {(note.labels ?? []).length > 0 && (

{t('documentInfo.labelsSection')}

{(note.labels ?? []).map(label => ( ))}
)}

{t('documentInfo.idLabel')}

{note.id}

)} {/* ── VERSIONS TAB ── */} {activeTab === 'versions' && (
{!historyEnabled ? (

{t('documentInfo.historyDisabled')}

) : (

{t('documentInfo.savedVersions')}

{/* Save version button */}
{/* Timeline */} {isLoadingHistory && historyEntries.length === 0 ? (

{t('documentInfo.loading')}

) : historyEntries.length === 0 ? (

{t('documentInfo.noVersion')}

) : (
{historyEntries.map((entry, idx) => { const colors = ['#E2E8F0', '#ACB995', '#E9ECEF'] const dotColor = colors[idx % colors.length] const isLatest = idx === 0 return (
{/* Dot */}
v{entry.version} {isLatest && ( {t('documentInfo.latestBadge')} )}

{formatAbsoluteDateLocalized(new Date(entry.createdAt), language, 'd MMM · HH:mm', locale)} · {formatDistanceToNow(new Date(entry.createdAt), { addSuffix: true, locale })}

) })}
)} {/* Button to open the full modal (optional, but good to keep if user wants diff) */}
)}
)} {/* ── NETWORK TAB ── */} {activeTab === 'network' && ( )}
{/* NoteHistoryModal with correct props */} {showHistory && ( setShowHistory(v)} note={note} enabled={historyEnabled} onEnableHistory={async () => { await enableNoteHistory(note.id) setHistoryEnabled(true) }} onRestored={(restored) => { setShowHistory(false) onNoteRestored?.(restored) }} /> )} ) }