feat(registratura): fix thread clear, close via conex entry

- Fix threadParentId clear: always show "Sterge" button even when parent not found in allEntries
- Bigger clear button with text label instead of tiny X icon
- Fallback display when parent not in current list (shows truncated ID)
- Close via conex: table/detail "Inchide" now creates a new reply entry that closes the original
- Header shows "Conex la BTG-XXX" + "Inchide originala" badge when closing via conex
- After saving the new conex entry, parent is auto-closed with resolution "finalizat"
- onClose signature changed from (id: string) to (entry: RegistryEntry) across table + detail

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-10 21:15:40 +02:00
parent f5e19ce3d1
commit 85077251f3
4 changed files with 68 additions and 21 deletions
@@ -72,6 +72,8 @@ export function RegistraturaModule() {
const [viewingEntry, setViewingEntry] = useState<RegistryEntry | null>(null); const [viewingEntry, setViewingEntry] = useState<RegistryEntry | null>(null);
/** Entry to reply to (conex) — pre-sets threadParentId in new form */ /** Entry to reply to (conex) — pre-sets threadParentId in new form */
const [replyToEntry, setReplyToEntry] = useState<RegistryEntry | null>(null); const [replyToEntry, setReplyToEntry] = useState<RegistryEntry | null>(null);
/** If set, the parent entry will be closed after saving the new conex entry */
const [closesEntryId, setClosesEntryId] = useState<string | null>(null);
const [closingId, setClosingId] = useState<string | null>(null); const [closingId, setClosingId] = useState<string | null>(null);
const [linkCheckId, setLinkCheckId] = useState<string | null>(null); const [linkCheckId, setLinkCheckId] = useState<string | null>(null);
@@ -138,6 +140,25 @@ export function RegistraturaModule() {
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">, data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
) => { ) => {
await addEntry(data); await addEntry(data);
// If this new entry closes a parent, close the parent automatically
if (closesEntryId) {
const parentEntry = allEntries.find((e) => e.id === closesEntryId);
if (parentEntry && parentEntry.status === "deschis") {
const closureInfo: ClosureInfo = {
resolution: "finalizat",
reason: "Inchis prin inregistrare conex",
closedBy: "",
closedAt: new Date().toISOString(),
hadActiveDeadlines:
(parentEntry.trackedDeadlines ?? []).some(
(d) => d.resolution === "pending",
),
};
await updateEntry(closesEntryId, { closureInfo });
await closeEntry(closesEntryId, false);
}
setClosesEntryId(null);
}
setReplyToEntry(null); setReplyToEntry(null);
setViewMode("list"); setViewMode("list");
}; };
@@ -157,6 +178,16 @@ export function RegistraturaModule() {
const handleReply = (entry: RegistryEntry) => { const handleReply = (entry: RegistryEntry) => {
setReplyToEntry(entry); setReplyToEntry(entry);
setClosesEntryId(null);
setViewingEntry(null);
setEditingEntry(null);
setViewMode("add");
};
/** Close entry via conex: create reply entry that closes the parent */
const handleCloseViaConex = (entry: RegistryEntry) => {
setReplyToEntry(entry);
setClosesEntryId(entry.id);
setViewingEntry(null); setViewingEntry(null);
setEditingEntry(null); setEditingEntry(null);
setViewMode("add"); setViewMode("add");
@@ -217,6 +248,7 @@ export function RegistraturaModule() {
setViewMode("list"); setViewMode("list");
setEditingEntry(null); setEditingEntry(null);
setReplyToEntry(null); setReplyToEntry(null);
setClosesEntryId(null);
}; };
// ── Dashboard deadline resolve/chain handlers ── // ── Dashboard deadline resolve/chain handlers ──
@@ -394,7 +426,7 @@ export function RegistraturaModule() {
onView={handleView} onView={handleView}
onEdit={handleEdit} onEdit={handleEdit}
onDelete={handleDelete} onDelete={handleDelete}
onClose={handleCloseRequest} onClose={handleCloseViaConex}
onReply={handleReply} onReply={handleReply}
/> />
@@ -416,6 +448,11 @@ export function RegistraturaModule() {
<Badge variant="outline" className="text-xs text-blue-600 border-blue-300"> <Badge variant="outline" className="text-xs text-blue-600 border-blue-300">
Raspuns Raspuns
</Badge> </Badge>
{closesEntryId && (
<Badge variant="outline" className="text-xs text-green-600 border-green-300">
Inchide originala
</Badge>
)}
</> </>
) : ( ) : (
<> <>
@@ -467,7 +504,7 @@ export function RegistraturaModule() {
if (!open) setViewingEntry(null); if (!open) setViewingEntry(null);
}} }}
onEdit={handleEdit} onEdit={handleEdit}
onClose={handleCloseRequest} onClose={handleCloseViaConex}
onDelete={handleDelete} onDelete={handleDelete}
onReply={handleReply} onReply={handleReply}
allEntries={allEntries} allEntries={allEntries}
@@ -48,7 +48,7 @@ interface RegistryEntryDetailProps {
open: boolean; open: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
onEdit: (entry: RegistryEntry) => void; onEdit: (entry: RegistryEntry) => void;
onClose: (id: string) => void; onClose: (entry: RegistryEntry) => void;
onDelete: (id: string) => void; onDelete: (id: string) => void;
/** Create a new entry linked as reply (conex) to this entry */ /** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void; onReply?: (entry: RegistryEntry) => void;
@@ -224,7 +224,7 @@ export function RegistryEntryDetail({
className="text-green-600 border-green-300 hover:bg-green-50 dark:border-green-700 dark:hover:bg-green-950/30" className="text-green-600 border-green-300 hover:bg-green-50 dark:border-green-700 dark:hover:bg-green-950/30"
onClick={() => { onClick={() => {
onOpenChange(false); onOpenChange(false);
onClose(entry.id); onClose(entry);
}} }}
> >
<CheckCircle2 className="mr-1.5 h-3.5 w-3.5" /> Inchide <CheckCircle2 className="mr-1.5 h-3.5 w-3.5" /> Inchide
@@ -1311,28 +1311,38 @@ export function RegistryEntryForm({
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
</Label> </Label>
{threadParent && ( {threadParentId && (
<div className="mt-1.5 flex items-center gap-2 rounded border border-primary/30 bg-primary/5 px-2 py-1.5 text-sm"> <div className="mt-1.5 flex items-center gap-2 rounded border border-primary/30 bg-primary/5 px-2 py-1.5 text-sm">
<Badge {threadParent ? (
variant={ <>
threadParent.direction === "intrat" ? "default" : "secondary" <Badge
} variant={
className="text-[10px]" threadParent.direction === "intrat" ? "default" : "secondary"
> }
{threadParent.direction === "intrat" ? "↓" : "↑"} className="text-[10px]"
</Badge> >
<span className="font-mono text-xs">{threadParent.number}</span> {threadParent.direction === "intrat" ? "\u2193" : "\u2191"}
<span className="truncate text-muted-foreground text-xs"> </Badge>
{threadParent.subject} <span className="font-mono text-xs">{threadParent.number}</span>
</span> <span className="truncate text-muted-foreground text-xs">
{threadParent.subject}
</span>
</>
) : (
<span className="text-xs text-muted-foreground italic">
Legat de o inregistrare (ID: {threadParentId.slice(0, 8)}...)
</span>
)}
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
size="sm" size="sm"
className="ml-auto h-6 w-6 p-0" className="ml-auto h-7 px-2 text-xs text-destructive hover:text-destructive"
onClick={() => setThreadParentId("")} onClick={() => setThreadParentId("")}
title="Sterge legatura"
> >
<X className="h-3.5 w-3.5" /> <X className="h-3.5 w-3.5 mr-0.5" />
Sterge
</Button> </Button>
</div> </div>
)} )}
@@ -40,7 +40,7 @@ interface RegistryTableProps {
onView: (entry: RegistryEntry) => void; onView: (entry: RegistryEntry) => void;
onEdit: (entry: RegistryEntry) => void; onEdit: (entry: RegistryEntry) => void;
onDelete: (id: string) => void; onDelete: (id: string) => void;
onClose: (id: string) => void; onClose: (entry: RegistryEntry) => void;
/** Create a new entry linked as reply (conex) to this entry */ /** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void; onReply?: (entry: RegistryEntry) => void;
} }
@@ -495,7 +495,7 @@ export function RegistryTable({
className="h-7 w-7 text-green-600" className="h-7 w-7 text-green-600"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onClose(entry.id); onClose(entry);
}} }}
title="Inchide" title="Inchide"
> >