feat(registratura): linked-entry search filter, remove 20-item cap

This commit is contained in:
AI Assistant
2026-02-19 07:08:59 +02:00
parent 1f2af98f51
commit cd4b0de1e9

View File

@@ -1,76 +1,130 @@
'use client'; "use client";
import { useState, useMemo, useRef } from 'react'; import { useState, useMemo, useRef } from "react";
import { Paperclip, X, Clock, Plus } from 'lucide-react'; import { Paperclip, X, Clock, Plus } from "lucide-react";
import type { CompanyId } from '@/core/auth/types'; import type { CompanyId } from "@/core/auth/types";
import type { RegistryEntry, RegistryDirection, RegistryStatus, DocumentType, RegistryAttachment, TrackedDeadline, DeadlineResolution } from '../types'; import type {
import { Input } from '@/shared/components/ui/input'; RegistryEntry,
import { Label } from '@/shared/components/ui/label'; RegistryDirection,
import { Textarea } from '@/shared/components/ui/textarea'; RegistryStatus,
import { Button } from '@/shared/components/ui/button'; DocumentType,
import { Badge } from '@/shared/components/ui/badge'; RegistryAttachment,
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/components/ui/select'; TrackedDeadline,
import { useContacts } from '@/modules/address-book/hooks/use-contacts'; DeadlineResolution,
import { v4 as uuid } from 'uuid'; } from "../types";
import { DeadlineCard } from './deadline-card'; import { Input } from "@/shared/components/ui/input";
import { DeadlineAddDialog } from './deadline-add-dialog'; import { Label } from "@/shared/components/ui/label";
import { DeadlineResolveDialog } from './deadline-resolve-dialog'; import { Textarea } from "@/shared/components/ui/textarea";
import { createTrackedDeadline, resolveDeadline as resolveDeadlineFn } from '../services/deadline-service'; import { Button } from "@/shared/components/ui/button";
import { getDeadlineType } from '../services/deadline-catalog'; import { Badge } from "@/shared/components/ui/badge";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select";
import { useContacts } from "@/modules/address-book/hooks/use-contacts";
import { v4 as uuid } from "uuid";
import { DeadlineCard } from "./deadline-card";
import { DeadlineAddDialog } from "./deadline-add-dialog";
import { DeadlineResolveDialog } from "./deadline-resolve-dialog";
import {
createTrackedDeadline,
resolveDeadline as resolveDeadlineFn,
} from "../services/deadline-service";
import { getDeadlineType } from "../services/deadline-catalog";
interface RegistryEntryFormProps { interface RegistryEntryFormProps {
initial?: RegistryEntry; initial?: RegistryEntry;
allEntries?: RegistryEntry[]; allEntries?: RegistryEntry[];
onSubmit: (data: Omit<RegistryEntry, 'id' | 'number' | 'createdAt' | 'updatedAt'>) => void; onSubmit: (
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
) => void;
onCancel: () => void; onCancel: () => void;
} }
const DOC_TYPE_LABELS: Record<DocumentType, string> = { const DOC_TYPE_LABELS: Record<DocumentType, string> = {
contract: 'Contract', contract: "Contract",
oferta: 'Ofertă', oferta: "Ofertă",
factura: 'Factură', factura: "Factură",
scrisoare: 'Scrisoare', scrisoare: "Scrisoare",
aviz: 'Aviz', aviz: "Aviz",
'nota-de-comanda': 'Notă de comandă', "nota-de-comanda": "Notă de comandă",
raport: 'Raport', raport: "Raport",
cerere: 'Cerere', cerere: "Cerere",
altele: 'Altele', altele: "Altele",
}; };
export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: RegistryEntryFormProps) { export function RegistryEntryForm({
initial,
allEntries,
onSubmit,
onCancel,
}: RegistryEntryFormProps) {
const { allContacts } = useContacts(); const { allContacts } = useContacts();
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const [direction, setDirection] = useState<RegistryDirection>(initial?.direction ?? 'intrat'); const [direction, setDirection] = useState<RegistryDirection>(
const [documentType, setDocumentType] = useState<DocumentType>(initial?.documentType ?? 'scrisoare'); initial?.direction ?? "intrat",
const [subject, setSubject] = useState(initial?.subject ?? ''); );
const [date, setDate] = useState(initial?.date ?? new Date().toISOString().slice(0, 10)); const [documentType, setDocumentType] = useState<DocumentType>(
const [sender, setSender] = useState(initial?.sender ?? ''); initial?.documentType ?? "scrisoare",
const [senderContactId, setSenderContactId] = useState(initial?.senderContactId ?? ''); );
const [recipient, setRecipient] = useState(initial?.recipient ?? ''); const [subject, setSubject] = useState(initial?.subject ?? "");
const [recipientContactId, setRecipientContactId] = useState(initial?.recipientContactId ?? ''); const [date, setDate] = useState(
const [company, setCompany] = useState<CompanyId>(initial?.company ?? 'beletage'); initial?.date ?? new Date().toISOString().slice(0, 10),
const [status, setStatus] = useState<RegistryStatus>(initial?.status ?? 'deschis'); );
const [deadline, setDeadline] = useState(initial?.deadline ?? ''); const [sender, setSender] = useState(initial?.sender ?? "");
const [notes, setNotes] = useState(initial?.notes ?? ''); const [senderContactId, setSenderContactId] = useState(
const [linkedEntryIds, setLinkedEntryIds] = useState<string[]>(initial?.linkedEntryIds ?? []); initial?.senderContactId ?? "",
const [attachments, setAttachments] = useState<RegistryAttachment[]>(initial?.attachments ?? []); );
const [trackedDeadlines, setTrackedDeadlines] = useState<TrackedDeadline[]>(initial?.trackedDeadlines ?? []); const [recipient, setRecipient] = useState(initial?.recipient ?? "");
const [recipientContactId, setRecipientContactId] = useState(
initial?.recipientContactId ?? "",
);
const [company, setCompany] = useState<CompanyId>(
initial?.company ?? "beletage",
);
const [status, setStatus] = useState<RegistryStatus>(
initial?.status ?? "deschis",
);
const [deadline, setDeadline] = useState(initial?.deadline ?? "");
const [notes, setNotes] = useState(initial?.notes ?? "");
const [linkedEntryIds, setLinkedEntryIds] = useState<string[]>(
initial?.linkedEntryIds ?? [],
);
const [attachments, setAttachments] = useState<RegistryAttachment[]>(
initial?.attachments ?? [],
);
const [trackedDeadlines, setTrackedDeadlines] = useState<TrackedDeadline[]>(
initial?.trackedDeadlines ?? [],
);
const [linkedSearch, setLinkedSearch] = useState("");
// ── Deadline dialogs ── // ── Deadline dialogs ──
const [deadlineAddOpen, setDeadlineAddOpen] = useState(false); const [deadlineAddOpen, setDeadlineAddOpen] = useState(false);
const [resolvingDeadline, setResolvingDeadline] = useState<TrackedDeadline | null>(null); const [resolvingDeadline, setResolvingDeadline] =
useState<TrackedDeadline | null>(null);
const handleAddDeadline = (typeId: string, startDate: string, chainParentId?: string) => { const handleAddDeadline = (
typeId: string,
startDate: string,
chainParentId?: string,
) => {
const tracked = createTrackedDeadline(typeId, startDate, chainParentId); const tracked = createTrackedDeadline(typeId, startDate, chainParentId);
if (tracked) setTrackedDeadlines((prev) => [...prev, tracked]); if (tracked) setTrackedDeadlines((prev) => [...prev, tracked]);
}; };
const handleResolveDeadline = (resolution: DeadlineResolution, note: string, chainNext: boolean) => { const handleResolveDeadline = (
resolution: DeadlineResolution,
note: string,
chainNext: boolean,
) => {
if (!resolvingDeadline) return; if (!resolvingDeadline) return;
const resolved = resolveDeadlineFn(resolvingDeadline, resolution, note); const resolved = resolveDeadlineFn(resolvingDeadline, resolution, note);
setTrackedDeadlines((prev) => setTrackedDeadlines((prev) =>
prev.map((d) => (d.id === resolved.id ? resolved : d)) prev.map((d) => (d.id === resolved.id ? resolved : d)),
); );
// Handle chain // Handle chain
@@ -78,7 +132,11 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
const def = getDeadlineType(resolvingDeadline.typeId); const def = getDeadlineType(resolvingDeadline.typeId);
if (def?.chainNextTypeId) { if (def?.chainNextTypeId) {
const resolvedDate = new Date().toISOString().slice(0, 10); const resolvedDate = new Date().toISOString().slice(0, 10);
const chained = createTrackedDeadline(def.chainNextTypeId, resolvedDate, resolvingDeadline.id); const chained = createTrackedDeadline(
def.chainNextTypeId,
resolvedDate,
resolvingDeadline.id,
);
if (chained) setTrackedDeadlines((prev) => [...prev, chained]); if (chained) setTrackedDeadlines((prev) => [...prev, chained]);
} }
} }
@@ -96,13 +154,25 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
const senderSuggestions = useMemo(() => { const senderSuggestions = useMemo(() => {
if (!sender || sender.length < 2) return []; if (!sender || sender.length < 2) return [];
const q = sender.toLowerCase(); const q = sender.toLowerCase();
return allContacts.filter((c) => c.name.toLowerCase().includes(q) || c.company.toLowerCase().includes(q)).slice(0, 5); return allContacts
.filter(
(c) =>
c.name.toLowerCase().includes(q) ||
c.company.toLowerCase().includes(q),
)
.slice(0, 5);
}, [allContacts, sender]); }, [allContacts, sender]);
const recipientSuggestions = useMemo(() => { const recipientSuggestions = useMemo(() => {
if (!recipient || recipient.length < 2) return []; if (!recipient || recipient.length < 2) return [];
const q = recipient.toLowerCase(); const q = recipient.toLowerCase();
return allContacts.filter((c) => c.name.toLowerCase().includes(q) || c.company.toLowerCase().includes(q)).slice(0, 5); return allContacts
.filter(
(c) =>
c.name.toLowerCase().includes(q) ||
c.company.toLowerCase().includes(q),
)
.slice(0, 5);
}, [allContacts, recipient]); }, [allContacts, recipient]);
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => { const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -126,7 +196,7 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
if (fileInputRef.current) fileInputRef.current.value = ''; if (fileInputRef.current) fileInputRef.current.value = "";
}; };
const removeAttachment = (id: string) => { const removeAttachment = (id: string) => {
@@ -149,10 +219,11 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
deadline: deadline || undefined, deadline: deadline || undefined,
linkedEntryIds, linkedEntryIds,
attachments, attachments,
trackedDeadlines: trackedDeadlines.length > 0 ? trackedDeadlines : undefined, trackedDeadlines:
trackedDeadlines.length > 0 ? trackedDeadlines : undefined,
notes, notes,
tags: initial?.tags ?? [], tags: initial?.tags ?? [],
visibility: initial?.visibility ?? 'all', visibility: initial?.visibility ?? "all",
}); });
}; };
@@ -162,8 +233,13 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<div className="grid gap-4 sm:grid-cols-3"> <div className="grid gap-4 sm:grid-cols-3">
<div> <div>
<Label>Direcție</Label> <Label>Direcție</Label>
<Select value={direction} onValueChange={(v) => setDirection(v as RegistryDirection)}> <Select
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger> value={direction}
onValueChange={(v) => setDirection(v as RegistryDirection)}
>
<SelectTrigger className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="intrat">Intrat</SelectItem> <SelectItem value="intrat">Intrat</SelectItem>
<SelectItem value="iesit">Ieșit</SelectItem> <SelectItem value="iesit">Ieșit</SelectItem>
@@ -172,25 +248,44 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
</div> </div>
<div> <div>
<Label>Tip document</Label> <Label>Tip document</Label>
<Select value={documentType} onValueChange={(v) => setDocumentType(v as DocumentType)}> <Select
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger> value={documentType}
onValueChange={(v) => setDocumentType(v as DocumentType)}
>
<SelectTrigger className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent> <SelectContent>
{(Object.entries(DOC_TYPE_LABELS) as [DocumentType, string][]).map(([key, label]) => ( {(
<SelectItem key={key} value={key}>{label}</SelectItem> Object.entries(DOC_TYPE_LABELS) as [DocumentType, string][]
).map(([key, label]) => (
<SelectItem key={key} value={key}>
{label}
</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div> <div>
<Label>Data</Label> <Label>Data</Label>
<Input type="date" value={date} onChange={(e) => setDate(e.target.value)} className="mt-1" /> <Input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
className="mt-1"
/>
</div> </div>
</div> </div>
{/* Subject */} {/* Subject */}
<div> <div>
<Label>Subiect *</Label> <Label>Subiect *</Label>
<Input value={subject} onChange={(e) => setSubject(e.target.value)} className="mt-1" required /> <Input
value={subject}
onChange={(e) => setSubject(e.target.value)}
className="mt-1"
required
/>
</div> </div>
{/* Sender / Recipient with autocomplete */} {/* Sender / Recipient with autocomplete */}
@@ -199,7 +294,10 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<Label>Expeditor</Label> <Label>Expeditor</Label>
<Input <Input
value={sender} value={sender}
onChange={(e) => { setSender(e.target.value); setSenderContactId(''); }} onChange={(e) => {
setSender(e.target.value);
setSenderContactId("");
}}
onFocus={() => setSenderFocused(true)} onFocus={() => setSenderFocused(true)}
onBlur={() => setTimeout(() => setSenderFocused(false), 200)} onBlur={() => setTimeout(() => setSenderFocused(false), 200)}
className="mt-1" className="mt-1"
@@ -219,7 +317,11 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
}} }}
> >
<span className="font-medium">{c.name}</span> <span className="font-medium">{c.name}</span>
{c.company && <span className="ml-1 text-muted-foreground text-xs">{c.company}</span>} {c.company && (
<span className="ml-1 text-muted-foreground text-xs">
{c.company}
</span>
)}
</button> </button>
))} ))}
</div> </div>
@@ -229,7 +331,10 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<Label>Destinatar</Label> <Label>Destinatar</Label>
<Input <Input
value={recipient} value={recipient}
onChange={(e) => { setRecipient(e.target.value); setRecipientContactId(''); }} onChange={(e) => {
setRecipient(e.target.value);
setRecipientContactId("");
}}
onFocus={() => setRecipientFocused(true)} onFocus={() => setRecipientFocused(true)}
onBlur={() => setTimeout(() => setRecipientFocused(false), 200)} onBlur={() => setTimeout(() => setRecipientFocused(false), 200)}
className="mt-1" className="mt-1"
@@ -243,13 +348,19 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
type="button" type="button"
className="w-full rounded px-2 py-1.5 text-left text-sm hover:bg-accent" className="w-full rounded px-2 py-1.5 text-left text-sm hover:bg-accent"
onMouseDown={() => { onMouseDown={() => {
setRecipient(c.company ? `${c.name} (${c.company})` : c.name); setRecipient(
c.company ? `${c.name} (${c.company})` : c.name,
);
setRecipientContactId(c.id); setRecipientContactId(c.id);
setRecipientFocused(false); setRecipientFocused(false);
}} }}
> >
<span className="font-medium">{c.name}</span> <span className="font-medium">{c.name}</span>
{c.company && <span className="ml-1 text-muted-foreground text-xs">{c.company}</span>} {c.company && (
<span className="ml-1 text-muted-foreground text-xs">
{c.company}
</span>
)}
</button> </button>
))} ))}
</div> </div>
@@ -261,8 +372,13 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<div className="grid gap-4 sm:grid-cols-3"> <div className="grid gap-4 sm:grid-cols-3">
<div> <div>
<Label>Companie</Label> <Label>Companie</Label>
<Select value={company} onValueChange={(v) => setCompany(v as CompanyId)}> <Select
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger> value={company}
onValueChange={(v) => setCompany(v as CompanyId)}
>
<SelectTrigger className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="beletage">Beletage</SelectItem> <SelectItem value="beletage">Beletage</SelectItem>
<SelectItem value="urban-switch">Urban Switch</SelectItem> <SelectItem value="urban-switch">Urban Switch</SelectItem>
@@ -273,8 +389,13 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
</div> </div>
<div> <div>
<Label>Status</Label> <Label>Status</Label>
<Select value={status} onValueChange={(v) => setStatus(v as RegistryStatus)}> <Select
<SelectTrigger className="mt-1"><SelectValue /></SelectTrigger> value={status}
onValueChange={(v) => setStatus(v as RegistryStatus)}
>
<SelectTrigger className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="deschis">Deschis</SelectItem> <SelectItem value="deschis">Deschis</SelectItem>
<SelectItem value="inchis">Închis</SelectItem> <SelectItem value="inchis">Închis</SelectItem>
@@ -283,7 +404,12 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
</div> </div>
<div> <div>
<Label>Termen limită</Label> <Label>Termen limită</Label>
<Input type="date" value={deadline} onChange={(e) => setDeadline(e.target.value)} className="mt-1" /> <Input
type="date"
value={deadline}
onChange={(e) => setDeadline(e.target.value)}
className="mt-1"
/>
</div> </div>
</div> </div>
@@ -291,26 +417,50 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
{allEntries && allEntries.length > 0 && ( {allEntries && allEntries.length > 0 && (
<div> <div>
<Label>Înregistrări legate</Label> <Label>Înregistrări legate</Label>
<Input
className="mt-1.5"
placeholder="Caută după număr, subiect sau expeditor…"
value={linkedSearch}
onChange={(e) => setLinkedSearch(e.target.value)}
/>
<div className="mt-1.5 flex flex-wrap gap-1.5"> <div className="mt-1.5 flex flex-wrap gap-1.5">
{allEntries {allEntries
.filter((e) => e.id !== initial?.id) .filter((e) => {
.slice(0, 20) if (e.id === initial?.id) return false;
if (!linkedSearch.trim()) return true;
const q = linkedSearch.toLowerCase();
return (
e.number.toLowerCase().includes(q) ||
e.subject.toLowerCase().includes(q) ||
(e.sender ?? "").toLowerCase().includes(q)
);
})
.map((e) => ( .map((e) => (
<button <button
key={e.id} key={e.id}
type="button" type="button"
onClick={() => { onClick={() => {
setLinkedEntryIds((prev) => setLinkedEntryIds((prev) =>
prev.includes(e.id) ? prev.filter((id) => id !== e.id) : [...prev, e.id] prev.includes(e.id)
? prev.filter((id) => id !== e.id)
: [...prev, e.id],
); );
}} }}
className={`rounded border px-2 py-0.5 text-xs transition-colors ${ className={`rounded border px-2 py-0.5 text-xs transition-colors ${
linkedEntryIds.includes(e.id) linkedEntryIds.includes(e.id)
? 'border-primary bg-primary/10 text-primary' ? "border-primary bg-primary/10 text-primary"
: 'border-muted-foreground/30 text-muted-foreground hover:border-primary/50' : "border-muted-foreground/30 text-muted-foreground hover:border-primary/50"
}`} }`}
> >
{e.number} {e.number}
{e.subject && (
<span className="ml-1 opacity-60">
·{" "}
{e.subject.length > 30
? e.subject.slice(0, 30) + "…"
: e.subject}
</span>
)}
</button> </button>
))} ))}
</div> </div>
@@ -324,7 +474,12 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<Clock className="h-3.5 w-3.5" /> <Clock className="h-3.5 w-3.5" />
Termene legale Termene legale
</Label> </Label>
<Button type="button" variant="outline" size="sm" onClick={() => setDeadlineAddOpen(true)}> <Button
type="button"
variant="outline"
size="sm"
onClick={() => setDeadlineAddOpen(true)}
>
<Plus className="mr-1 h-3.5 w-3.5" /> Adaugă termen <Plus className="mr-1 h-3.5 w-3.5" /> Adaugă termen
</Button> </Button>
</div> </div>
@@ -342,7 +497,8 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
)} )}
{trackedDeadlines.length === 0 && ( {trackedDeadlines.length === 0 && (
<p className="mt-2 text-xs text-muted-foreground"> <p className="mt-2 text-xs text-muted-foreground">
Niciun termen legal. Apăsați &quot;Adaugă termen&quot; pentru a urmări un termen. Niciun termen legal. Apăsați &quot;Adaugă termen&quot; pentru a
urmări un termen.
</p> </p>
)} )}
</div> </div>
@@ -357,7 +513,9 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<DeadlineResolveDialog <DeadlineResolveDialog
open={resolvingDeadline !== null} open={resolvingDeadline !== null}
deadline={resolvingDeadline} deadline={resolvingDeadline}
onOpenChange={(open) => { if (!open) setResolvingDeadline(null); }} onOpenChange={(open) => {
if (!open) setResolvingDeadline(null);
}}
onResolve={handleResolveDeadline} onResolve={handleResolveDeadline}
/> />
@@ -365,7 +523,12 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
<div> <div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Label>Atașamente</Label> <Label>Atașamente</Label>
<Button type="button" variant="outline" size="sm" onClick={() => fileInputRef.current?.click()}> <Button
type="button"
variant="outline"
size="sm"
onClick={() => fileInputRef.current?.click()}
>
<Paperclip className="mr-1 h-3.5 w-3.5" /> Adaugă fișier <Paperclip className="mr-1 h-3.5 w-3.5" /> Adaugă fișier
</Button> </Button>
<input <input
@@ -380,13 +543,20 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
{attachments.length > 0 && ( {attachments.length > 0 && (
<div className="mt-2 space-y-1"> <div className="mt-2 space-y-1">
{attachments.map((att) => ( {attachments.map((att) => (
<div key={att.id} className="flex items-center gap-2 rounded border px-2 py-1 text-sm"> <div
key={att.id}
className="flex items-center gap-2 rounded border px-2 py-1 text-sm"
>
<Paperclip className="h-3 w-3 text-muted-foreground" /> <Paperclip className="h-3 w-3 text-muted-foreground" />
<span className="flex-1 truncate">{att.name}</span> <span className="flex-1 truncate">{att.name}</span>
<Badge variant="outline" className="text-[10px]"> <Badge variant="outline" className="text-[10px]">
{(att.size / 1024).toFixed(0)} KB {(att.size / 1024).toFixed(0)} KB
</Badge> </Badge>
<button type="button" onClick={() => removeAttachment(att.id)} className="text-destructive"> <button
type="button"
onClick={() => removeAttachment(att.id)}
className="text-destructive"
>
<X className="h-3.5 w-3.5" /> <X className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
@@ -398,12 +568,19 @@ export function RegistryEntryForm({ initial, allEntries, onSubmit, onCancel }: R
{/* Notes */} {/* Notes */}
<div> <div>
<Label>Note</Label> <Label>Note</Label>
<Textarea value={notes} onChange={(e) => setNotes(e.target.value)} rows={3} className="mt-1" /> <Textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={3}
className="mt-1"
/>
</div> </div>
<div className="flex justify-end gap-2 pt-2"> <div className="flex justify-end gap-2 pt-2">
<Button type="button" variant="outline" onClick={onCancel}>Anulează</Button> <Button type="button" variant="outline" onClick={onCancel}>
<Button type="submit">{initial ? 'Actualizează' : 'Adaugă'}</Button> Anulează
</Button>
<Button type="submit">{initial ? "Actualizează" : "Adaugă"}</Button>
</div> </div>
</form> </form>
); );