Files
ArchiTools/src/modules/registratura/components/closure-banner.tsx
T
AI Assistant 8042df481f fix(registratura): prevent duplicate numbers, add upload progress, submission lock, unified close/resolve, backdating support
- generateRegistryNumber: parse max existing number instead of counting entries
- addEntry: fetch fresh entries before generating number (race condition fix)
- Form: isSubmitting lock prevents double-click submission
- Form: uploadingCount tracks FileReader progress, blocks submit while uploading
- Form: submit button shows Loader2 spinner during save/upload
- CloseGuardDialog: added ClosureResolution selector (finalizat/aprobat-tacit/respins/retras/altele)
- ClosureBanner: displays resolution badge
- Types: ClosureResolution type, registrationDate field on RegistryEntry
- Date field renamed 'Data document' with tooltip explaining backdating
- Registry table shows '(înr. DATE)' when registrationDate differs from document date
2026-02-27 21:56:47 +02:00

143 lines
4.3 KiB
TypeScript

"use client";
import {
Lock,
Calendar,
User,
Link2,
FileText,
AlertTriangle,
Download,
} from "lucide-react";
import { Badge } from "@/shared/components/ui/badge";
import { Button } from "@/shared/components/ui/button";
import type { ClosureInfo, RegistryEntry } from "../types";
interface ClosureBannerProps {
closureInfo: ClosureInfo;
/** Navigate to the linked continuation entry */
onNavigateLinked?: (entry: RegistryEntry) => void;
/** All entries — to find the linked entry for navigation */
allEntries?: RegistryEntry[];
}
const RESOLUTION_LABELS: Record<string, string> = {
finalizat: "Finalizat",
"aprobat-tacit": "Aprobat tacit",
respins: "Respins / Negativ",
retras: "Retras",
altele: "Altele",
};
/**
* Read-only banner displayed at the top of a closed entry,
* showing who closed it, when, why, linked entry, and attachment.
*/
export function ClosureBanner({
closureInfo,
onNavigateLinked,
allEntries,
}: ClosureBannerProps) {
const linkedEntry =
closureInfo.linkedEntryId && allEntries
? allEntries.find((e) => e.id === closureInfo.linkedEntryId)
: null;
const closedDate = new Date(closureInfo.closedAt).toLocaleDateString(
"ro-RO",
{ day: "2-digit", month: "long", year: "numeric" },
);
const handleDownload = () => {
if (!closureInfo.attachment) return;
const a = document.createElement("a");
a.href = closureInfo.attachment.data;
a.download = closureInfo.attachment.name;
a.click();
};
return (
<div className="rounded-lg border border-muted bg-muted/30 p-4 space-y-3">
<div className="flex items-center gap-2">
<Lock className="h-4 w-4 text-muted-foreground" />
<span className="text-sm font-semibold">Înregistrare închisă</span>
{closureInfo.resolution && (
<Badge variant="secondary" className="text-[10px]">
{RESOLUTION_LABELS[closureInfo.resolution] ??
closureInfo.resolution}
</Badge>
)}
{closureInfo.hadActiveDeadlines && (
<Badge
variant="outline"
className="text-[10px] text-amber-600 border-amber-300 dark:text-amber-400 dark:border-amber-700"
>
<AlertTriangle className="mr-1 h-3 w-3" />
Avea termene active
</Badge>
)}
</div>
{/* Who + When */}
<div className="flex flex-wrap gap-x-6 gap-y-1 text-sm text-muted-foreground">
<span className="flex items-center gap-1.5">
<User className="h-3.5 w-3.5" />
{closureInfo.closedBy}
</span>
<span className="flex items-center gap-1.5">
<Calendar className="h-3.5 w-3.5" />
{closedDate}
</span>
</div>
{/* Reason */}
<div className="text-sm">
<span className="font-medium">Motiv: </span>
{closureInfo.reason}
</div>
{/* Linked entry */}
{(linkedEntry || closureInfo.linkedEntryNumber) && (
<div className="flex items-center gap-2 text-sm">
<Link2 className="h-3.5 w-3.5 text-primary" />
<span className="text-muted-foreground">Continuat în:</span>
{linkedEntry && onNavigateLinked ? (
<button
type="button"
className="text-primary hover:underline font-medium"
onClick={() => onNavigateLinked(linkedEntry)}
>
{linkedEntry.number} {linkedEntry.subject}
</button>
) : (
<span className="font-mono text-xs font-medium">
{closureInfo.linkedEntryNumber ?? closureInfo.linkedEntryId}
</span>
)}
</div>
)}
{/* Attached document */}
{closureInfo.attachment && (
<div className="flex items-center gap-2 text-sm">
<FileText className="h-3.5 w-3.5 text-muted-foreground" />
<span className="truncate">{closureInfo.attachment.name}</span>
<Badge variant="outline" className="text-[10px]">
{(closureInfo.attachment.size / 1024).toFixed(0)} KB
</Badge>
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 px-2 text-xs"
onClick={handleDownload}
>
<Download className="mr-1 h-3 w-3" />
Descarcă
</Button>
</div>
)}
</div>
);
}