"use client"; import { useState, useEffect, useCallback } from "react"; import { Eye, Pencil, Link2, Clock, GitBranch, User, Settings2, Paperclip, Reply, CheckCircle2, Copy, Check, ArrowDownLeft, ArrowUpRight, Radio, } from "lucide-react"; import { Button } from "@/shared/components/ui/button"; import { Badge } from "@/shared/components/ui/badge"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/shared/components/ui/tooltip"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/shared/components/ui/dropdown-menu"; import type { RegistryEntry } from "../types"; import { DEFAULT_DOC_TYPE_LABELS, EXTERNAL_STATUS_LABELS } from "../types"; import { getOverdueDays } from "../services/registry-service"; import { cn } from "@/shared/lib/utils"; /** Format a contact for display: "Name – Company" / "Company" / "Name" */ function formatContactLabel(c: { name?: string; company?: string }): string { const name = (c.name ?? "").trim(); const company = (c.company ?? "").trim(); if (name && company) return `${name} – ${company}`; return name || company || ""; } interface ContactInfo { name: string; company: string; } interface RegistryTableProps { entries: RegistryEntry[]; loading: boolean; onView: (entry: RegistryEntry) => void; onEdit: (entry: RegistryEntry) => void; onDelete: (id: string) => void; onClose: (entry: RegistryEntry) => void; /** Create a new entry linked as reply (conex) to this entry */ onReply?: (entry: RegistryEntry) => void; /** Live contact data map: contactId → { name, company } for live sync */ contactMap?: Map; } // ── Column definitions ── export type ColumnId = | "number" | "date" | "direction" | "type" | "subject" | "sender" | "recipient" | "assignee" | "deadline" | "status"; interface ColumnDef { id: ColumnId; /** Short header label */ label: string; /** Full tooltip explanation */ tooltip: string; /** Whether visible by default */ defaultVisible: boolean; } const COLUMNS: ColumnDef[] = [ { id: "number", label: "Nr.", tooltip: "Număr de înregistrare (format: PREFIX-NNNN/AN)", defaultVisible: true, }, { id: "date", label: "Data", tooltip: "Data documentului (nu data înregistrării în sistem)", defaultVisible: true, }, { id: "direction", label: "Dir.", tooltip: "Direcție: Intrat = primit, Ieșit = trimis", defaultVisible: true, }, { id: "type", label: "Tip", tooltip: "Tipul documentului (contract, cerere, aviz etc.)", defaultVisible: false, }, { id: "subject", label: "Subiect", tooltip: "Subiectul sau descrierea pe scurt a documentului", defaultVisible: true, }, { id: "sender", label: "Exped.", tooltip: "Expeditor — persoana sau instituția care a trimis documentul", defaultVisible: true, }, { id: "recipient", label: "Dest.", tooltip: "Destinatar — persoana sau instituția căreia i se adresează documentul", defaultVisible: true, }, { id: "assignee", label: "Resp.", tooltip: "Responsabil intern — persoana din echipă alocată pe acest document", defaultVisible: false, }, { id: "deadline", label: "Termen", tooltip: "Termen limită intern (nu termen legal — acela apare în tab-ul Termene)", defaultVisible: false, }, { id: "status", label: "Status", tooltip: "Deschis = în lucru, Închis = finalizat/arhivat", defaultVisible: true, }, ]; const DEFAULT_VISIBLE = new Set( COLUMNS.filter((c) => c.defaultVisible).map((c) => c.id), ); const STORAGE_KEY = "registratura:visible-columns"; const DIRECTION_LABELS: Record = { intrat: "Intrat", iesit: "Ieșit", }; function getDocTypeLabel(type: string): string { const label = DEFAULT_DOC_TYPE_LABELS[type]; if (label) return label; return type.replace(/-/g, " ").replace(/^\w/, (c) => c.toUpperCase()); } const STATUS_LABELS: Record = { deschis: "Deschis", inchis: "Închis", reserved: "Rezervat", }; function loadVisibleColumns(): Set { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) { const arr = JSON.parse(raw) as ColumnId[]; if (Array.isArray(arr) && arr.length > 0) return new Set(arr); } } catch { // ignore } return new Set(DEFAULT_VISIBLE); } export function RegistryTable({ entries, loading, onView, onEdit, onDelete, onClose, onReply, contactMap, }: RegistryTableProps) { const [visibleCols, setVisibleCols] = useState>( () => DEFAULT_VISIBLE, ); // Load from localStorage on mount (client-only) useEffect(() => { setVisibleCols(loadVisibleColumns()); }, []); const toggleColumn = useCallback((id: ColumnId) => { setVisibleCols((prev) => { const next = new Set(prev); if (next.has(id)) { // Don't allow hiding all columns if (next.size > 2) next.delete(id); } else { next.add(id); } localStorage.setItem(STORAGE_KEY, JSON.stringify([...next])); return next; }); }, []); const resetColumns = useCallback(() => { const def = new Set(DEFAULT_VISIBLE); setVisibleCols(def); localStorage.setItem(STORAGE_KEY, JSON.stringify([...def])); }, []); const visibleColumns = COLUMNS.filter((c) => visibleCols.has(c.id)); if (loading) { return (

Se încarcă...

); } if (entries.length === 0) { return (

Nicio înregistrare găsită. Adaugă prima înregistrare.

); } return (
{/* Column toggle button */}
Coloane vizibile {COLUMNS.map((col) => ( toggleColumn(col.id)} onSelect={(e) => e.preventDefault()} > {col.label}{" "} — {col.tooltip} ))} e.preventDefault()} > Resetează la implicit
{/* Table */}
{visibleColumns.map((col) => (

{col.tooltip}

))} {/* Actions column is always shown */} {entries.map((entry) => { const overdueDays = entry.status === "deschis" || !entry.status ? getOverdueDays(entry.deadline) : null; const isOverdue = overdueDays !== null && overdueDays > 0; return ( onView(entry)} > {visibleCols.has("number") && ( )} {visibleCols.has("date") && ( )} {visibleCols.has("direction") && ( )} {visibleCols.has("type") && ( )} {visibleCols.has("subject") && ( )} {visibleCols.has("sender") && ( )} {visibleCols.has("recipient") && ( )} {visibleCols.has("assignee") && ( )} {visibleCols.has("deadline") && ( )} {visibleCols.has("status") && ( )} {/* Actions — always visible */} ); })}
{col.label} Acț.
{formatDate(entry.date)} {entry.registrationDate && entry.registrationDate !== entry.date && ( (înr. {formatDate(entry.registrationDate)}) )} {DIRECTION_LABELS[entry.direction] ?? entry.direction ?? "—"} {getDocTypeLabel(entry.documentType)} {entry.subject} {/* Inline indicators */} {entry.threadParentId && ( )} {(entry.linkedEntryIds ?? []).length > 0 && ( )} {(entry.attachments ?? []).length > 0 && ( {entry.attachments.length} )} {(entry.trackedDeadlines ?? []).length > 0 && ( {(entry.trackedDeadlines ?? []).length} )} {entry.externalStatusTracking?.active && ( Status extern: {EXTERNAL_STATUS_LABELS[entry.externalStatusTracking.semanticStatus]} )} {(() => { const live = entry.senderContactId && contactMap?.get(entry.senderContactId); return (live ? formatContactLabel(live) : entry.sender) || ( ); })()} {(() => { const live = entry.recipientContactId && contactMap?.get(entry.recipientContactId); return (live ? formatContactLabel(live) : entry.recipient) || ( ); })()} {entry.assignee ? ( {entry.assignee} ) : ( )} {entry.deadline ? ( {formatDate(entry.deadline)} {overdueDays !== null && overdueDays > 0 && ( ({overdueDays}z) )} ) : ( )} {STATUS_LABELS[entry.status]}
{onReply && ( )} {entry.status === "deschis" && ( )}
); } function CopyNumberButton({ entry }: { entry: RegistryEntry }) { const [copied, setCopied] = useState(false); const handleCopy = (e: React.MouseEvent) => { e.stopPropagation(); // Extract plain number: "BTG-0042/2026" → "42", "US-0123/2026" → "123" const plain = entry.number.replace(/^[A-Z]+-0*/, "").replace(/\/.*$/, ""); const text = `nr. ${plain} din ${formatDate(entry.date)}`; void navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); }); }; const Icon = copied ? Check : Copy; return ( ); } // ── Company badge colors ── const COMPANY_BADGE: Record = { beletage: { label: "B", className: "bg-blue-600 text-white" }, "urban-switch": { label: "U", className: "bg-violet-600 text-white" }, "studii-de-teren": { label: "S", className: "bg-emerald-600 text-white" }, group: { label: "G", className: "bg-zinc-500 text-white" }, }; function CompactNumber({ entry }: { entry: RegistryEntry }) { // Extract plain number: "B-2026-00001" → "2026-00001" const plain = (entry.number ?? "").replace(/^[A-Z]+-/, ""); const badge = COMPANY_BADGE[entry.company ?? ""] ?? { label: "B", className: "bg-blue-600 text-white" }; // Direction icon (only intrat / iesit) const DirIcon = entry.direction === "intrat" ? ArrowDownLeft : ArrowUpRight; const dirColor = entry.direction === "intrat" ? "text-green-600" : "text-orange-500"; return ( {badge.label} {plain} ); } function formatDate(iso: string): string { try { return new Date(iso).toLocaleDateString("ro-RO", { day: "2-digit", month: "2-digit", year: "numeric", }); } catch { return iso; } }