feat: Registratura thread explorer, AC validity tracker, interactive I/O toggle + Password Vault rework
Registratura improvements: - Thread Explorer: new 'Fire conversatie' tab with timeline view, search, stats, gap tracking (la noi/la institutie), export to text report - Interactive I/O toggle: replaced direction dropdown with visual blue/orange button group (Intrat/Iesit with icons) - Doc type UX: alphabetical sort + immediate selection after adding custom type - AC Validity Tracker: full Autorizatie de Construire lifecycle workflow (12mo validity, execution phases, extension request, required docs checklist, monthly reminders, abandonment/expiry tracking) Password Vault rework (renamed to 'Parole Uzuale' v0.3.0): - New categories: WiFi, Portale Primarii, Avize Online, PIN Semnatura, Software, Hardware (replaced server/database/api) - Category icons (lucide-react) throughout list and form - WiFi QR code dialog with connection string copy - Context-aware form (PIN vs password label, hide email for WiFi/PIN, hide URL for WiFi, hide generator for PIN) - Dynamic stat cards showing top 3 categories by count - Removed encryption banner - Updated i18n, flags, config
This commit is contained in:
@@ -13,6 +13,8 @@ import {
|
||||
AlertTriangle,
|
||||
Calendar,
|
||||
Globe,
|
||||
ArrowDownToLine,
|
||||
ArrowUpFromLine,
|
||||
} from "lucide-react";
|
||||
import type { CompanyId } from "@/core/auth/types";
|
||||
import type {
|
||||
@@ -22,6 +24,7 @@ import type {
|
||||
RegistryAttachment,
|
||||
TrackedDeadline,
|
||||
DeadlineResolution,
|
||||
ACValidityTracking,
|
||||
} from "../types";
|
||||
import { DEFAULT_DOC_TYPE_LABELS } from "../types";
|
||||
import { Input } from "@/shared/components/ui/input";
|
||||
@@ -53,6 +56,8 @@ import { DeadlineResolveDialog } from "./deadline-resolve-dialog";
|
||||
import { QuickContactDialog } from "./quick-contact-dialog";
|
||||
import { ThreadView } from "./thread-view";
|
||||
import { ClosureBanner } from "./closure-banner";
|
||||
import { ACValidityTracker } from "./ac-validity-tracker";
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
import {
|
||||
createTrackedDeadline,
|
||||
resolveDeadline as resolveDeadlineFn,
|
||||
@@ -91,7 +96,12 @@ export function RegistryEntryForm({
|
||||
const { tags: docTypeTags } = useTags("document-type");
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// ── Build dynamic doc type list from defaults + Tag Manager ──
|
||||
// Track locally-added custom types that may not yet be in Tag Manager
|
||||
const [localCustomTypes, setLocalCustomTypes] = useState<Map<string, string>>(
|
||||
new Map(),
|
||||
);
|
||||
|
||||
// ── Build dynamic doc type list from defaults + Tag Manager + local ──
|
||||
const allDocTypes = useMemo(() => {
|
||||
const map = new Map<string, string>();
|
||||
// Add defaults
|
||||
@@ -105,8 +115,18 @@ export function RegistryEntryForm({
|
||||
map.set(key, tag.label);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}, [docTypeTags]);
|
||||
// Add locally-created types (before Tag Manager syncs)
|
||||
for (const [key, label] of localCustomTypes) {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, label);
|
||||
}
|
||||
}
|
||||
// Sort alphabetically by label
|
||||
const sorted = new Map(
|
||||
[...map.entries()].sort((a, b) => a[1].localeCompare(b[1], "ro")),
|
||||
);
|
||||
return sorted;
|
||||
}, [docTypeTags, localCustomTypes]);
|
||||
|
||||
const [direction, setDirection] = useState<RegistryDirection>(
|
||||
initial?.direction ?? "intrat",
|
||||
@@ -169,6 +189,9 @@ export function RegistryEntryForm({
|
||||
const [externalTrackingId, setExternalTrackingId] = useState(
|
||||
initial?.externalTrackingId ?? "",
|
||||
);
|
||||
const [acValidity, setAcValidity] = useState<ACValidityTracking | undefined>(
|
||||
initial?.acValidity,
|
||||
);
|
||||
|
||||
// ── Submission lock + file upload tracking ──
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@@ -295,11 +318,14 @@ export function RegistryEntryForm({
|
||||
setQuickContactOpen(false);
|
||||
};
|
||||
|
||||
// ── Custom doc type creation ──
|
||||
// ── Custom doc type creation — adds to local state immediately + Tag Manager ──
|
||||
const handleAddCustomDocType = async () => {
|
||||
const label = customDocType.trim();
|
||||
if (!label) return;
|
||||
const key = label.toLowerCase().replace(/\s+/g, "-");
|
||||
// Add to local types immediately so it appears in the select
|
||||
setLocalCustomTypes((prev) => new Map(prev).set(key, label));
|
||||
// Select the newly created type
|
||||
setDocumentType(key);
|
||||
setCustomDocType("");
|
||||
if (onCreateDocType) {
|
||||
@@ -367,6 +393,7 @@ export function RegistryEntryForm({
|
||||
expiryAlertDays: expiryDate ? expiryAlertDays : undefined,
|
||||
externalStatusUrl: externalStatusUrl || undefined,
|
||||
externalTrackingId: externalTrackingId || undefined,
|
||||
acValidity: acValidity,
|
||||
linkedEntryIds,
|
||||
attachments,
|
||||
trackedDeadlines:
|
||||
@@ -465,18 +492,34 @@ export function RegistryEntryForm({
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<div>
|
||||
<Label>Direcție</Label>
|
||||
<Select
|
||||
value={direction}
|
||||
onValueChange={(v) => setDirection(v as RegistryDirection)}
|
||||
>
|
||||
<SelectTrigger className="mt-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="intrat">Intrat</SelectItem>
|
||||
<SelectItem value="iesit">Ieșit</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="mt-1 flex rounded-lg border bg-muted/30 p-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDirection("intrat")}
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-all",
|
||||
direction === "intrat"
|
||||
? "bg-blue-500 text-white shadow-sm"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-background",
|
||||
)}
|
||||
>
|
||||
<ArrowDownToLine className="h-4 w-4" />
|
||||
Intrat
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDirection("iesit")}
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-all",
|
||||
direction === "iesit"
|
||||
? "bg-orange-500 text-white shadow-sm"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-background",
|
||||
)}
|
||||
>
|
||||
<ArrowUpFromLine className="h-4 w-4" />
|
||||
Ieșit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Tip document</Label>
|
||||
@@ -859,6 +902,13 @@ export function RegistryEntryForm({
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* AC Validity Tracker */}
|
||||
<ACValidityTracker
|
||||
value={acValidity}
|
||||
onChange={setAcValidity}
|
||||
entryDate={date}
|
||||
/>
|
||||
|
||||
{/* Web scraping prep — external tracking */}
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user