feat(registratura): add Conex (reply) + Inchide buttons, reorder completari last

- Conex button on table rows (Reply icon, blue) — opens new entry with threadParentId pre-set + flipped direction
- Conex button on detail panel — same behavior
- Inchide button on table rows (CheckCircle2 icon, green) — only for open entries
- replyTo prop on RegistryEntryForm: pre-sets threadParentId + direction flip (intrat→iesit, iesit→intrat)
- Card header shows "Conex la BTG-0042/2026" with blue badge when replying
- Completari moved to last position in deadline category order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-10 20:40:12 +02:00
parent f01fe47af4
commit f5e19ce3d1
5 changed files with 91 additions and 9 deletions
@@ -70,6 +70,8 @@ export function RegistraturaModule() {
const [viewMode, setViewMode] = useState<ViewMode>("list"); const [viewMode, setViewMode] = useState<ViewMode>("list");
const [editingEntry, setEditingEntry] = useState<RegistryEntry | null>(null); const [editingEntry, setEditingEntry] = useState<RegistryEntry | null>(null);
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 */
const [replyToEntry, setReplyToEntry] = useState<RegistryEntry | 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);
@@ -136,6 +138,7 @@ export function RegistraturaModule() {
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">, data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
) => { ) => {
await addEntry(data); await addEntry(data);
setReplyToEntry(null);
setViewMode("list"); setViewMode("list");
}; };
@@ -152,6 +155,13 @@ export function RegistraturaModule() {
setViewingEntry(full ?? entry); setViewingEntry(full ?? entry);
}; };
const handleReply = (entry: RegistryEntry) => {
setReplyToEntry(entry);
setViewingEntry(null);
setEditingEntry(null);
setViewMode("add");
};
const handleNavigateEntry = async (entry: RegistryEntry) => { const handleNavigateEntry = async (entry: RegistryEntry) => {
const full = await loadFullEntry(entry.id); const full = await loadFullEntry(entry.id);
setEditingEntry(full ?? entry); setEditingEntry(full ?? entry);
@@ -206,6 +216,7 @@ export function RegistraturaModule() {
const handleCancel = () => { const handleCancel = () => {
setViewMode("list"); setViewMode("list");
setEditingEntry(null); setEditingEntry(null);
setReplyToEntry(null);
}; };
// ── Dashboard deadline resolve/chain handlers ── // ── Dashboard deadline resolve/chain handlers ──
@@ -384,6 +395,7 @@ export function RegistraturaModule() {
onEdit={handleEdit} onEdit={handleEdit}
onDelete={handleDelete} onDelete={handleDelete}
onClose={handleCloseRequest} onClose={handleCloseRequest}
onReply={handleReply}
/> />
{!loading && ( {!loading && (
@@ -398,15 +410,27 @@ export function RegistraturaModule() {
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
Înregistrare nouă {replyToEntry ? (
<>
Conex la {replyToEntry.number}
<Badge variant="outline" className="text-xs text-blue-600 border-blue-300">
Raspuns
</Badge>
</>
) : (
<>
Inregistrare noua
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
Nr. auto Nr. auto
</Badge> </Badge>
</>
)}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<RegistryEntryForm <RegistryEntryForm
allEntries={allEntries} allEntries={allEntries}
replyTo={replyToEntry ?? undefined}
onSubmit={handleAdd} onSubmit={handleAdd}
onCancel={handleCancel} onCancel={handleCancel}
onCreateContact={handleCreateContact} onCreateContact={handleCreateContact}
@@ -445,6 +469,7 @@ export function RegistraturaModule() {
onEdit={handleEdit} onEdit={handleEdit}
onClose={handleCloseRequest} onClose={handleCloseRequest}
onDelete={handleDelete} onDelete={handleDelete}
onReply={handleReply}
allEntries={allEntries} allEntries={allEntries}
/> />
@@ -19,6 +19,7 @@ import {
User, User,
X, X,
Image as ImageIcon, Image as ImageIcon,
Reply,
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
@@ -49,6 +50,8 @@ interface RegistryEntryDetailProps {
onEdit: (entry: RegistryEntry) => void; onEdit: (entry: RegistryEntry) => void;
onClose: (id: string) => void; onClose: (id: string) => void;
onDelete: (id: string) => void; onDelete: (id: string) => void;
/** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void;
allEntries: RegistryEntry[]; allEntries: RegistryEntry[];
} }
@@ -137,6 +140,7 @@ export function RegistryEntryDetail({
onEdit, onEdit,
onClose, onClose,
onDelete, onDelete,
onReply,
allEntries, allEntries,
}: RegistryEntryDetailProps) { }: RegistryEntryDetailProps) {
const [previewIndex, setPreviewIndex] = useState<number | null>(null); const [previewIndex, setPreviewIndex] = useState<number | null>(null);
@@ -200,6 +204,19 @@ export function RegistryEntryDetail({
> >
<Pencil className="mr-1.5 h-3.5 w-3.5" /> Editează <Pencil className="mr-1.5 h-3.5 w-3.5" /> Editează
</Button> </Button>
{onReply && (
<Button
size="sm"
variant="outline"
className="text-blue-600 border-blue-300 hover:bg-blue-50 dark:border-blue-700 dark:hover:bg-blue-950/30"
onClick={() => {
onOpenChange(false);
onReply(entry);
}}
>
<Reply className="mr-1.5 h-3.5 w-3.5" /> Conex
</Button>
)}
{entry.status === "deschis" && ( {entry.status === "deschis" && (
<Button <Button
size="sm" size="sm"
@@ -210,7 +227,7 @@ export function RegistryEntryDetail({
onClose(entry.id); onClose(entry.id);
}} }}
> >
<CheckCircle2 className="mr-1.5 h-3.5 w-3.5" /> Închide <CheckCircle2 className="mr-1.5 h-3.5 w-3.5" /> Inchide
</Button> </Button>
)} )}
<Button <Button
@@ -87,6 +87,8 @@ import { getDeadlineType } from "../services/deadline-catalog";
interface RegistryEntryFormProps { interface RegistryEntryFormProps {
initial?: RegistryEntry; initial?: RegistryEntry;
/** Pre-fill as reply (conex) to this entry — sets threadParentId, flips direction */
replyTo?: RegistryEntry;
allEntries?: RegistryEntry[]; allEntries?: RegistryEntry[];
onSubmit: ( onSubmit: (
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">, data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
@@ -106,6 +108,7 @@ interface RegistryEntryFormProps {
export function RegistryEntryForm({ export function RegistryEntryForm({
initial, initial,
replyTo,
allEntries, allEntries,
onSubmit, onSubmit,
onCancel, onCancel,
@@ -150,8 +153,12 @@ export function RegistryEntryForm({
return sorted; return sorted;
}, [docTypeTags, localCustomTypes]); }, [docTypeTags, localCustomTypes]);
// When replyTo is provided, flip direction (intrat→iesit, iesit→intrat)
const replyDirection: RegistryDirection | undefined = replyTo
? replyTo.direction === "intrat" ? "iesit" : "intrat"
: undefined;
const [direction, setDirection] = useState<RegistryDirection>( const [direction, setDirection] = useState<RegistryDirection>(
initial?.direction ?? "intrat", initial?.direction ?? replyDirection ?? "intrat",
); );
const defaultDocType = initial?.documentType ?? (direction === "intrat" ? "aviz" : "cerere"); const defaultDocType = initial?.documentType ?? (direction === "intrat" ? "aviz" : "cerere");
const [documentType, setDocumentType] = useState<DocumentType>(defaultDocType); const [documentType, setDocumentType] = useState<DocumentType>(defaultDocType);
@@ -182,7 +189,7 @@ export function RegistryEntryForm({
initial?.assigneeContactId ?? "", initial?.assigneeContactId ?? "",
); );
const [threadParentId, setThreadParentId] = useState( const [threadParentId, setThreadParentId] = useState(
initial?.threadParentId ?? "", initial?.threadParentId ?? replyTo?.id ?? "",
); );
const [notes, setNotes] = useState(initial?.notes ?? ""); const [notes, setNotes] = useState(initial?.notes ?? "");
const [linkedEntryIds, setLinkedEntryIds] = useState<string[]>( const [linkedEntryIds, setLinkedEntryIds] = useState<string[]>(
@@ -10,6 +10,8 @@ import {
User, User,
Settings2, Settings2,
Paperclip, Paperclip,
Reply,
CheckCircle2,
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
@@ -39,6 +41,8 @@ interface RegistryTableProps {
onEdit: (entry: RegistryEntry) => void; onEdit: (entry: RegistryEntry) => void;
onDelete: (id: string) => void; onDelete: (id: string) => void;
onClose: (id: string) => void; onClose: (id: string) => void;
/** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void;
} }
// ── Column definitions ── // ── Column definitions ──
@@ -175,6 +179,7 @@ export function RegistryTable({
onEdit, onEdit,
onDelete, onDelete,
onClose, onClose,
onReply,
}: RegistryTableProps) { }: RegistryTableProps) {
const [visibleCols, setVisibleCols] = useState<Set<ColumnId>>( const [visibleCols, setVisibleCols] = useState<Set<ColumnId>>(
() => DEFAULT_VISIBLE, () => DEFAULT_VISIBLE,
@@ -465,10 +470,38 @@ export function RegistryTable({
e.stopPropagation(); e.stopPropagation();
onEdit(entry); onEdit(entry);
}} }}
title="Editează" title="Editeaza"
> >
<Pencil className="h-3.5 w-3.5" /> <Pencil className="h-3.5 w-3.5" />
</Button> </Button>
{onReply && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7 text-blue-600"
onClick={(e) => {
e.stopPropagation();
onReply(entry);
}}
title="Conex — creeaza raspuns"
>
<Reply className="h-3.5 w-3.5" />
</Button>
)}
{entry.status === "deschis" && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7 text-green-600"
onClick={(e) => {
e.stopPropagation();
onClose(entry.id);
}}
title="Inchide"
>
<CheckCircle2 className="h-3.5 w-3.5" />
</Button>
)}
</div> </div>
</td> </td>
</tr> </tr>
@@ -639,7 +639,7 @@ export const DIRECTION_CATEGORIES: Record<
RegistryDirection, RegistryDirection,
DeadlineCategory[] DeadlineCategory[]
> = { > = {
iesit: ["certificat", "avize", "completari", "urbanism", "autorizare"], iesit: ["certificat", "avize", "urbanism", "autorizare", "completari"],
intrat: ["contestatie"], intrat: ["contestatie"],
}; };