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 [editingEntry, setEditingEntry] = 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 [linkCheckId, setLinkCheckId] = useState<string | null>(null);
@@ -136,6 +138,7 @@ export function RegistraturaModule() {
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
) => {
await addEntry(data);
setReplyToEntry(null);
setViewMode("list");
};
@@ -152,6 +155,13 @@ export function RegistraturaModule() {
setViewingEntry(full ?? entry);
};
const handleReply = (entry: RegistryEntry) => {
setReplyToEntry(entry);
setViewingEntry(null);
setEditingEntry(null);
setViewMode("add");
};
const handleNavigateEntry = async (entry: RegistryEntry) => {
const full = await loadFullEntry(entry.id);
setEditingEntry(full ?? entry);
@@ -206,6 +216,7 @@ export function RegistraturaModule() {
const handleCancel = () => {
setViewMode("list");
setEditingEntry(null);
setReplyToEntry(null);
};
// ── Dashboard deadline resolve/chain handlers ──
@@ -384,6 +395,7 @@ export function RegistraturaModule() {
onEdit={handleEdit}
onDelete={handleDelete}
onClose={handleCloseRequest}
onReply={handleReply}
/>
{!loading && (
@@ -398,15 +410,27 @@ export function RegistraturaModule() {
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
Înregistrare nouă
<Badge variant="outline" className="text-xs">
Nr. auto
</Badge>
{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">
Nr. auto
</Badge>
</>
)}
</CardTitle>
</CardHeader>
<CardContent>
<RegistryEntryForm
allEntries={allEntries}
replyTo={replyToEntry ?? undefined}
onSubmit={handleAdd}
onCancel={handleCancel}
onCreateContact={handleCreateContact}
@@ -445,6 +469,7 @@ export function RegistraturaModule() {
onEdit={handleEdit}
onClose={handleCloseRequest}
onDelete={handleDelete}
onReply={handleReply}
allEntries={allEntries}
/>
@@ -19,6 +19,7 @@ import {
User,
X,
Image as ImageIcon,
Reply,
} from "lucide-react";
import { Button } from "@/shared/components/ui/button";
import { Badge } from "@/shared/components/ui/badge";
@@ -49,6 +50,8 @@ interface RegistryEntryDetailProps {
onEdit: (entry: RegistryEntry) => void;
onClose: (id: string) => void;
onDelete: (id: string) => void;
/** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void;
allEntries: RegistryEntry[];
}
@@ -137,6 +140,7 @@ export function RegistryEntryDetail({
onEdit,
onClose,
onDelete,
onReply,
allEntries,
}: RegistryEntryDetailProps) {
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ă
</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" && (
<Button
size="sm"
@@ -210,7 +227,7 @@ export function RegistryEntryDetail({
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
@@ -87,6 +87,8 @@ import { getDeadlineType } from "../services/deadline-catalog";
interface RegistryEntryFormProps {
initial?: RegistryEntry;
/** Pre-fill as reply (conex) to this entry — sets threadParentId, flips direction */
replyTo?: RegistryEntry;
allEntries?: RegistryEntry[];
onSubmit: (
data: Omit<RegistryEntry, "id" | "number" | "createdAt" | "updatedAt">,
@@ -106,6 +108,7 @@ interface RegistryEntryFormProps {
export function RegistryEntryForm({
initial,
replyTo,
allEntries,
onSubmit,
onCancel,
@@ -150,8 +153,12 @@ export function RegistryEntryForm({
return sorted;
}, [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>(
initial?.direction ?? "intrat",
initial?.direction ?? replyDirection ?? "intrat",
);
const defaultDocType = initial?.documentType ?? (direction === "intrat" ? "aviz" : "cerere");
const [documentType, setDocumentType] = useState<DocumentType>(defaultDocType);
@@ -182,7 +189,7 @@ export function RegistryEntryForm({
initial?.assigneeContactId ?? "",
);
const [threadParentId, setThreadParentId] = useState(
initial?.threadParentId ?? "",
initial?.threadParentId ?? replyTo?.id ?? "",
);
const [notes, setNotes] = useState(initial?.notes ?? "");
const [linkedEntryIds, setLinkedEntryIds] = useState<string[]>(
@@ -10,6 +10,8 @@ import {
User,
Settings2,
Paperclip,
Reply,
CheckCircle2,
} from "lucide-react";
import { Button } from "@/shared/components/ui/button";
import { Badge } from "@/shared/components/ui/badge";
@@ -39,6 +41,8 @@ interface RegistryTableProps {
onEdit: (entry: RegistryEntry) => void;
onDelete: (id: string) => void;
onClose: (id: string) => void;
/** Create a new entry linked as reply (conex) to this entry */
onReply?: (entry: RegistryEntry) => void;
}
// ── Column definitions ──
@@ -175,6 +179,7 @@ export function RegistryTable({
onEdit,
onDelete,
onClose,
onReply,
}: RegistryTableProps) {
const [visibleCols, setVisibleCols] = useState<Set<ColumnId>>(
() => DEFAULT_VISIBLE,
@@ -465,10 +470,38 @@ export function RegistryTable({
e.stopPropagation();
onEdit(entry);
}}
title="Editează"
title="Editeaza"
>
<Pencil className="h-3.5 w-3.5" />
</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>
</td>
</tr>
@@ -639,7 +639,7 @@ export const DIRECTION_CATEGORIES: Record<
RegistryDirection,
DeadlineCategory[]
> = {
iesit: ["certificat", "avize", "completari", "urbanism", "autorizare"],
iesit: ["certificat", "avize", "urbanism", "autorizare", "completari"],
intrat: ["contestatie"],
};