- | {entry.number} |
- {formatDate(entry.date)} |
+
+ {entry.number}
+ |
+
+ {formatDate(entry.date)}
+ |
- {DIRECTION_LABELS[entry.direction] ?? entry.direction ?? '—'}
+ {DIRECTION_LABELS[entry.direction] ??
+ entry.direction ??
+ "—"}
|
- {DOC_TYPE_LABELS[entry.documentType] ?? entry.documentType ?? '—'} |
+
+ {getDocTypeLabel(entry.documentType)}
+ |
{entry.subject}
+ {entry.threadParentId && (
+
+ )}
{(entry.linkedEntryIds ?? []).length > 0 && (
)}
@@ -107,17 +139,39 @@ export function RegistryTable({ entries, loading, onEdit, onDelete, onClose }: R
)}
|
- {entry.sender} |
- {entry.recipient} |
+
+ {entry.sender}
+ |
+
+ {entry.recipient}
+ |
+
+ {entry.assignee ? (
+
+
+ {entry.assignee}
+
+ ) : (
+ —
+ )}
+ |
{entry.deadline ? (
-
+
{formatDate(entry.deadline)}
{overdueDays !== null && overdueDays > 0 && (
- ({overdueDays}z depășit)
+
+ ({overdueDays}z depășit)
+
)}
{overdueDays !== null && overdueDays < 0 && (
- ({Math.abs(overdueDays)}z)
+
+ ({Math.abs(overdueDays)}z)
+
)}
) : (
@@ -125,21 +179,39 @@ export function RegistryTable({ entries, loading, onEdit, onDelete, onClose }: R
)}
|
-
+
{STATUS_LABELS[entry.status]}
|
- {entry.status === 'deschis' && (
-
@@ -155,7 +227,11 @@ export function RegistryTable({ entries, loading, onEdit, onDelete, onClose }: R
function formatDate(iso: string): string {
try {
- return new Date(iso).toLocaleDateString('ro-RO', { day: '2-digit', month: '2-digit', year: 'numeric' });
+ return new Date(iso).toLocaleDateString("ro-RO", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ });
} catch {
return iso;
}
diff --git a/src/modules/registratura/components/thread-view.tsx b/src/modules/registratura/components/thread-view.tsx
new file mode 100644
index 0000000..e0732f3
--- /dev/null
+++ b/src/modules/registratura/components/thread-view.tsx
@@ -0,0 +1,151 @@
+"use client";
+
+import { CornerDownRight, GitBranch } from "lucide-react";
+import { Badge } from "@/shared/components/ui/badge";
+import type { RegistryEntry } from "../types";
+import { cn } from "@/shared/lib/utils";
+
+interface ThreadViewProps {
+ /** The current entry being viewed */
+ entry: RegistryEntry;
+ /** All entries in the registry (to resolve references) */
+ allEntries: RegistryEntry[];
+ /** Click on an entry to navigate to it */
+ onNavigate?: (entry: RegistryEntry) => void;
+}
+
+/**
+ * Shows thread relationships for a registry entry:
+ * - Parent entry (this is a reply to...)
+ * - Child entries (replies to this entry)
+ * Displays as an indented tree with direction badges.
+ */
+export function ThreadView({ entry, allEntries, onNavigate }: ThreadViewProps) {
+ // Find the parent entry (if this is a reply)
+ const parent = entry.threadParentId
+ ? allEntries.find((e) => e.id === entry.threadParentId)
+ : null;
+
+ // Find child entries (replies to this entry)
+ const children = allEntries.filter((e) => e.threadParentId === entry.id);
+
+ // Find siblings (other replies to the same parent, excluding this one)
+ const siblings = entry.threadParentId
+ ? allEntries.filter(
+ (e) => e.threadParentId === entry.threadParentId && e.id !== entry.id,
+ )
+ : [];
+
+ if (!parent && children.length === 0) return null;
+
+ return (
+
+
+
+ Fir conversație
+
+
+ {/* Parent */}
+ {parent && (
+
+
+ Răspuns la:
+
+
+ {/* Siblings — other branches from same parent */}
+ {siblings.length > 0 && (
+
+
+ Alte ramuri ({siblings.length}):
+
+ {siblings.map((s) => (
+
+ ))}
+
+ )}
+
+ )}
+
+ {/* Current entry marker */}
+
+
+
+ {entry.number}
+
+
+ {entry.subject}
+
+
+
+ {/* Children — replies to this entry */}
+ {children.length > 0 && (
+
+
+ Răspunsuri ({children.length}):
+
+ {children.map((child) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+function ThreadEntryChip({
+ entry,
+ onNavigate,
+ dimmed,
+}: {
+ entry: RegistryEntry;
+ onNavigate?: (entry: RegistryEntry) => void;
+ dimmed?: boolean;
+}) {
+ return (
+ onNavigate?.(entry)}
+ className={cn(
+ "flex items-center gap-1.5 rounded border px-2 py-1 text-left text-xs transition-colors hover:bg-accent w-full",
+ dimmed && "opacity-60",
+ )}
+ >
+
+ {entry.direction === "intrat" ? "↓" : "↑"}
+
+ {entry.number}
+
+ {entry.subject.length > 40
+ ? entry.subject.slice(0, 40) + "…"
+ : entry.subject}
+
+
+ {formatDate(entry.date)}
+
+
+ );
+}
+
+function formatDate(iso: string): string {
+ try {
+ return new Date(iso).toLocaleDateString("ro-RO", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ });
+ } catch {
+ return iso;
+ }
+}
diff --git a/src/modules/registratura/index.ts b/src/modules/registratura/index.ts
index 3c9ba69..d646583 100644
--- a/src/modules/registratura/index.ts
+++ b/src/modules/registratura/index.ts
@@ -1,7 +1,14 @@
-export { registraturaConfig } from './config';
-export { RegistraturaModule } from './components/registratura-module';
+export { registraturaConfig } from "./config";
+export { RegistraturaModule } from "./components/registratura-module";
export type {
- RegistryEntry, RegistryDirection, RegistryStatus, DocumentType,
- DeadlineDayType, DeadlineResolution, DeadlineCategory,
- DeadlineTypeDef, TrackedDeadline,
-} from './types';
+ RegistryEntry,
+ RegistryDirection,
+ RegistryStatus,
+ DocumentType,
+ DeadlineDayType,
+ DeadlineResolution,
+ DeadlineCategory,
+ DeadlineTypeDef,
+ TrackedDeadline,
+} from "./types";
+export { DEFAULT_DOCUMENT_TYPES, DEFAULT_DOC_TYPE_LABELS } from "./types";
diff --git a/src/modules/registratura/types.ts b/src/modules/registratura/types.ts
index 6020a92..99f4703 100644
--- a/src/modules/registratura/types.ts
+++ b/src/modules/registratura/types.ts
@@ -1,23 +1,44 @@
-import type { Visibility } from '@/core/module-registry/types';
-import type { CompanyId } from '@/core/auth/types';
+import type { Visibility } from "@/core/module-registry/types";
+import type { CompanyId } from "@/core/auth/types";
/** Document direction — simplified from the old 3-way type */
-export type RegistryDirection = 'intrat' | 'iesit';
+export type RegistryDirection = "intrat" | "iesit";
-/** Document type categories */
-export type DocumentType =
- | 'contract'
- | 'oferta'
- | 'factura'
- | 'scrisoare'
- | 'aviz'
- | 'nota-de-comanda'
- | 'raport'
- | 'cerere'
- | 'altele';
+/** Default document types — user can add custom types that sync with Tag Manager */
+export const DEFAULT_DOCUMENT_TYPES = [
+ "contract",
+ "oferta",
+ "factura",
+ "scrisoare",
+ "aviz",
+ "nota-de-comanda",
+ "raport",
+ "cerere",
+ "apel-telefonic",
+ "videoconferinta",
+ "altele",
+] as const;
+
+/** Document type — string-based for dynamic types from Tag Manager */
+export type DocumentType = (typeof DEFAULT_DOCUMENT_TYPES)[number] | string;
+
+/** Labels for default document types */
+export const DEFAULT_DOC_TYPE_LABELS: Record = {
+ contract: "Contract",
+ oferta: "Ofertă",
+ factura: "Factură",
+ scrisoare: "Scrisoare",
+ aviz: "Aviz",
+ "nota-de-comanda": "Notă de comandă",
+ raport: "Raport",
+ cerere: "Cerere",
+ "apel-telefonic": "Apel telefonic",
+ videoconferinta: "Videoconferință",
+ altele: "Altele",
+};
/** Status — simplified to open/closed */
-export type RegistryStatus = 'deschis' | 'inchis';
+export type RegistryStatus = "deschis" | "inchis";
/** File attachment */
export interface RegistryAttachment {
@@ -32,11 +53,21 @@ export interface RegistryAttachment {
// ── Deadline tracking types ──
-export type DeadlineDayType = 'calendar' | 'working';
+export type DeadlineDayType = "calendar" | "working";
-export type DeadlineResolution = 'pending' | 'completed' | 'aprobat-tacit' | 'respins' | 'anulat';
+export type DeadlineResolution =
+ | "pending"
+ | "completed"
+ | "aprobat-tacit"
+ | "respins"
+ | "anulat";
-export type DeadlineCategory = 'avize' | 'completari' | 'analiza' | 'autorizare' | 'publicitate';
+export type DeadlineCategory =
+ | "avize"
+ | "completari"
+ | "analiza"
+ | "autorizare"
+ | "publicitate";
export interface DeadlineTypeDef {
id: string;
@@ -86,6 +117,11 @@ export interface RegistryEntry {
status: RegistryStatus;
/** Deadline date (YYYY-MM-DD) */
deadline?: string;
+ /** Assignee — person responsible (ERP-ready field) */
+ assignee?: string;
+ assigneeContactId?: string;
+ /** Thread parent — ID of the entry this is a reply to */
+ threadParentId?: string;
/** Linked entry IDs (for closing/archiving related entries) */
linkedEntryIds: string[];
/** File attachments */
|