feat: add deadline system guide/overview page (Ghid termene)
- New DeadlineConfigOverview component: read-only reference page showing all 18 deadline types organized by category with chain flow diagrams - Accessible via "Ghid termene" toggle in the "Termene legale" tab - Shows: summary stats, color legend, collapsible category sections, chain flow diagrams (fan-in + sequential), notification overview, document expiry settings, and legal reference index - All data derived from existing catalog (zero API calls) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,565 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Info,
|
||||
Clock,
|
||||
ShieldCheck,
|
||||
Bell,
|
||||
ArrowRight,
|
||||
Scale,
|
||||
FileWarning,
|
||||
Timer,
|
||||
Link2,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { Separator } from "@/shared/components/ui/separator";
|
||||
import {
|
||||
DEADLINE_CATALOG,
|
||||
CATEGORY_LABELS,
|
||||
getDeadlineType,
|
||||
getDeadlinesByCategory,
|
||||
} from "../services/deadline-catalog";
|
||||
import { NOTIFICATION_TYPES } from "@/core/notifications/types";
|
||||
import type { DeadlineCategory, DeadlineTypeDef } from "../types";
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
|
||||
// ── Constants ──
|
||||
|
||||
const ALL_CATEGORIES: DeadlineCategory[] = [
|
||||
"certificat",
|
||||
"avize",
|
||||
"completari",
|
||||
"urbanism",
|
||||
"autorizare",
|
||||
"litigii",
|
||||
];
|
||||
|
||||
const CATEGORY_ICONS: Record<DeadlineCategory, string> = {
|
||||
certificat: "CU",
|
||||
avize: "AV",
|
||||
completari: "CO",
|
||||
urbanism: "UR",
|
||||
autorizare: "AC",
|
||||
litigii: "LI",
|
||||
};
|
||||
|
||||
// ── Chain building ──
|
||||
|
||||
interface ChainGroup {
|
||||
sources: DeadlineTypeDef[];
|
||||
target: DeadlineTypeDef;
|
||||
}
|
||||
|
||||
function buildChainGroups(category: DeadlineCategory): ChainGroup[] {
|
||||
const types = getDeadlinesByCategory(category);
|
||||
const targetMap = new Map<string, DeadlineTypeDef[]>();
|
||||
|
||||
for (const t of types) {
|
||||
if (t.chainNextTypeId) {
|
||||
const sources = targetMap.get(t.chainNextTypeId) ?? [];
|
||||
sources.push(t);
|
||||
targetMap.set(t.chainNextTypeId, sources);
|
||||
}
|
||||
}
|
||||
|
||||
const groups: ChainGroup[] = [];
|
||||
for (const [targetId, sources] of targetMap) {
|
||||
const target = getDeadlineType(targetId);
|
||||
if (target) {
|
||||
groups.push({ sources, target });
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
// ── Legal reference index ──
|
||||
|
||||
function buildLegalIndex(): Map<string, DeadlineTypeDef[]> {
|
||||
const index = new Map<string, DeadlineTypeDef[]>();
|
||||
for (const t of DEADLINE_CATALOG) {
|
||||
if (t.legalReference) {
|
||||
const existing = index.get(t.legalReference) ?? [];
|
||||
existing.push(t);
|
||||
index.set(t.legalReference, existing);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// ── Component ──
|
||||
|
||||
export function DeadlineConfigOverview() {
|
||||
const [expandedCategories, setExpandedCategories] = useState<Set<DeadlineCategory>>(
|
||||
() => new Set(ALL_CATEGORIES),
|
||||
);
|
||||
const [expandedDescs, setExpandedDescs] = useState<Set<string>>(new Set());
|
||||
const [showLegalIndex, setShowLegalIndex] = useState(false);
|
||||
|
||||
const stats = useMemo(() => {
|
||||
const total = DEADLINE_CATALOG.length;
|
||||
const autoTracked = DEADLINE_CATALOG.filter((d) => d.autoTrack).length;
|
||||
const tacit = DEADLINE_CATALOG.filter((d) => d.tacitApprovalApplicable).length;
|
||||
const chained = DEADLINE_CATALOG.filter((d) => d.chainNextTypeId).length;
|
||||
return { total, autoTracked, tacit, chained };
|
||||
}, []);
|
||||
|
||||
const legalIndex = useMemo(() => buildLegalIndex(), []);
|
||||
|
||||
const toggleCategory = (cat: DeadlineCategory) => {
|
||||
setExpandedCategories((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(cat)) next.delete(cat);
|
||||
else next.add(cat);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleDesc = (id: string) => {
|
||||
setExpandedDescs((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* ── Summary stats ── */}
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<StatCard icon={<Scale className="h-4 w-4" />} value={stats.total} label="Tipuri termene" />
|
||||
<StatCard icon={<Zap className="h-4 w-4 text-purple-500" />} value={stats.autoTracked} label="Auto-tracked" />
|
||||
<StatCard icon={<ShieldCheck className="h-4 w-4 text-blue-500" />} value={stats.tacit} label="Aprobare tacita" />
|
||||
<StatCard icon={<Link2 className="h-4 w-4 text-amber-500" />} value={stats.chained} label="Termene in lant" />
|
||||
</div>
|
||||
|
||||
{/* ── Legend ── */}
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-lg border bg-muted/30 px-3 py-2">
|
||||
<span className="text-[10px] font-medium text-muted-foreground">Legenda:</span>
|
||||
<Badge variant="outline" className="bg-purple-100 text-purple-700 border-purple-300 text-[10px] dark:bg-purple-900/30 dark:text-purple-300 dark:border-purple-700">
|
||||
Auto
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-gray-100 text-gray-600 border-gray-300 text-[10px] dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600">
|
||||
Manual
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-blue-100 text-blue-700 border-blue-300 text-[10px] dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-700">
|
||||
Tacit
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-green-100 text-green-700 border-green-300 text-[10px] dark:bg-green-900/30 dark:text-green-300 dark:border-green-700">
|
||||
Lucratoare
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-orange-100 text-orange-700 border-orange-300 text-[10px] dark:bg-orange-900/30 dark:text-orange-300 dark:border-orange-700">
|
||||
Calendaristice
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-dashed text-muted-foreground text-[10px]">
|
||||
Fundal
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* ── Category sections ── */}
|
||||
{ALL_CATEGORIES.map((cat) => {
|
||||
const types = getDeadlinesByCategory(cat);
|
||||
if (types.length === 0) return null;
|
||||
const isExpanded = expandedCategories.has(cat);
|
||||
const chainGroups = buildChainGroups(cat);
|
||||
|
||||
return (
|
||||
<div key={cat} className="rounded-lg border bg-card">
|
||||
{/* Category header */}
|
||||
<button
|
||||
onClick={() => toggleCategory(cat)}
|
||||
className="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors"
|
||||
>
|
||||
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-[10px] font-bold text-primary">
|
||||
{CATEGORY_ICONS[cat]}
|
||||
</span>
|
||||
<span className="flex-1 text-sm font-medium">
|
||||
{CATEGORY_LABELS[cat]}
|
||||
</span>
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
{types.length} tip{types.length !== 1 ? "uri" : ""}
|
||||
</Badge>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Category body */}
|
||||
{isExpanded && (
|
||||
<div>
|
||||
<Separator />
|
||||
<div className="divide-y">
|
||||
{types.map((t) => (
|
||||
<DeadlineTypeRow
|
||||
key={t.id}
|
||||
type={t}
|
||||
descExpanded={expandedDescs.has(t.id)}
|
||||
onToggleDesc={() => toggleDesc(t.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Chain flow diagrams */}
|
||||
{chainGroups.length > 0 && (
|
||||
<div className="border-t bg-muted/20 px-4 py-3">
|
||||
<p className="mb-2 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Fluxuri de termene in lant
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
{chainGroups.map((group) => (
|
||||
<ChainFlowDiagram
|
||||
key={group.target.id}
|
||||
group={group}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* ── Notification overview ── */}
|
||||
<div className="rounded-lg border bg-card">
|
||||
<div className="flex items-center gap-3 px-4 py-3">
|
||||
<Bell className="h-4 w-4 text-primary" />
|
||||
<span className="text-sm font-medium">Notificari email</span>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Digest zilnic trimis luni-vineri la 8:00 prin Brevo SMTP, declansat automat de N8N cron.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{NOTIFICATION_TYPES.map((nt) => (
|
||||
<div key={nt.type} className="flex items-start gap-2">
|
||||
<span className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-primary" />
|
||||
<div>
|
||||
<span className="text-xs font-medium">{nt.label}</span>
|
||||
<span className="ml-2 text-xs text-muted-foreground">
|
||||
{nt.description}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-[10px] text-muted-foreground italic">
|
||||
Preferintele per utilizator se configureaza din iconita clopotel din bara de sus.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Document expiry overview ── */}
|
||||
<div className="rounded-lg border bg-card">
|
||||
<div className="flex items-center gap-3 px-4 py-3">
|
||||
<FileWarning className="h-4 w-4 text-amber-500" />
|
||||
<span className="text-sm font-medium">Expirare documente</span>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500" />
|
||||
<div className="text-xs">
|
||||
<span className="font-medium">Certificat de Urbanism (CU)</span>
|
||||
<span className="ml-1 text-muted-foreground">
|
||||
— data expirare + fereastra de alerta (implicit 30 zile). Se configureaza per inregistrare.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500" />
|
||||
<div className="text-xs">
|
||||
<span className="font-medium">Autorizatie de Construire (AC)</span>
|
||||
<span className="ml-1 text-muted-foreground">
|
||||
— validitate 12 luni de la emitere, executie 6/12/24/36 luni, prelungire +24 luni.
|
||||
Reminder lunar automat (snooze/dismiss disponibil).
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-blue-500" />
|
||||
<div className="text-xs">
|
||||
<span className="font-medium">Monitorizare externa</span>
|
||||
<span className="ml-1 text-muted-foreground">
|
||||
— verificare automata status la autoritate (Primaria Cluj etc.). Notificare la schimbare status.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[10px] text-muted-foreground italic">
|
||||
Transmiterea in termen (1 zi) se verifica automat in fundal cand un entry e inchis prin act administrativ.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Legal reference index ── */}
|
||||
<div className="rounded-lg border bg-card">
|
||||
<button
|
||||
onClick={() => setShowLegalIndex(!showLegalIndex)}
|
||||
className="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors"
|
||||
>
|
||||
<Scale className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="flex-1 text-sm font-medium">Index referinte legale</span>
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
{legalIndex.size} ref.
|
||||
</Badge>
|
||||
{showLegalIndex ? (
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
{showLegalIndex && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="px-4 py-3 space-y-2">
|
||||
{Array.from(legalIndex.entries())
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([ref, types]) => (
|
||||
<div key={ref} className="text-xs">
|
||||
<span className="font-medium text-primary">{ref}</span>
|
||||
<span className="ml-2 text-muted-foreground">
|
||||
{types.map((t) => t.label).join(", ")}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Stat card ──
|
||||
|
||||
function StatCard({
|
||||
icon,
|
||||
value,
|
||||
label,
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
value: number;
|
||||
label: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-3 rounded-lg border bg-card px-3 py-2.5">
|
||||
{icon}
|
||||
<div>
|
||||
<p className="text-lg font-semibold leading-none">{value}</p>
|
||||
<p className="text-[10px] text-muted-foreground">{label}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Deadline type row ──
|
||||
|
||||
function DeadlineTypeRow({
|
||||
type,
|
||||
descExpanded,
|
||||
onToggleDesc,
|
||||
}: {
|
||||
type: DeadlineTypeDef;
|
||||
descExpanded: boolean;
|
||||
onToggleDesc: () => void;
|
||||
}) {
|
||||
const chainTarget = type.chainNextTypeId
|
||||
? getDeadlineType(type.chainNextTypeId)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"px-4 py-2.5",
|
||||
type.backgroundOnly && "opacity-60",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* Label */}
|
||||
<span className="text-xs font-medium">{type.label}</span>
|
||||
|
||||
{/* Duration badge */}
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"text-[10px]",
|
||||
type.dayType === "working"
|
||||
? "bg-green-50 text-green-700 border-green-300 dark:bg-green-900/20 dark:text-green-300 dark:border-green-700"
|
||||
: "bg-orange-50 text-orange-700 border-orange-300 dark:bg-orange-900/20 dark:text-orange-300 dark:border-orange-700",
|
||||
)}
|
||||
>
|
||||
<Timer className="mr-0.5 inline h-2.5 w-2.5" />
|
||||
{type.days}z {type.dayType === "working" ? "lucr." : "cal."}
|
||||
</Badge>
|
||||
|
||||
{/* Auto/Manual badge */}
|
||||
{type.autoTrack ? (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="bg-purple-50 text-purple-700 border-purple-300 text-[10px] dark:bg-purple-900/20 dark:text-purple-300 dark:border-purple-700"
|
||||
>
|
||||
Auto
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant="outline" className="text-[10px] text-muted-foreground">
|
||||
Manual
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Tacit badge */}
|
||||
{type.tacitApprovalApplicable && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="bg-blue-50 text-blue-700 border-blue-300 text-[10px] dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-700"
|
||||
>
|
||||
<ShieldCheck className="mr-0.5 inline h-2.5 w-2.5" />
|
||||
Tacit
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Background badge */}
|
||||
{type.backgroundOnly && (
|
||||
<Badge variant="outline" className="border-dashed text-[10px] text-muted-foreground">
|
||||
Fundal
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Direction filter */}
|
||||
{type.directionFilter && type.directionFilter.length === 1 && (
|
||||
<Badge variant="outline" className="text-[10px] text-muted-foreground">
|
||||
{type.directionFilter[0] === "iesit" ? "Iesit" : "Intrat"}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Info toggle */}
|
||||
<button
|
||||
onClick={onToggleDesc}
|
||||
className="ml-auto shrink-0 text-muted-foreground hover:text-foreground"
|
||||
title="Detalii"
|
||||
>
|
||||
<Info className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chain indicator */}
|
||||
{chainTarget && (
|
||||
<div className="mt-1 flex items-center gap-1.5 text-[10px] text-amber-600 dark:text-amber-400">
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
<span>
|
||||
La rezolvare lanseaza: <span className="font-medium">{chainTarget.label}</span>
|
||||
{" "}({chainTarget.days}z {chainTarget.dayType === "working" ? "lucr." : "cal."})
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description + legal ref (expanded) */}
|
||||
{descExpanded && (
|
||||
<div className="mt-2 space-y-1 rounded-md bg-muted/30 px-3 py-2">
|
||||
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
||||
{type.description}
|
||||
</p>
|
||||
{type.legalReference && (
|
||||
<p className="text-[10px] text-primary/70">
|
||||
<Scale className="mr-0.5 inline h-2.5 w-2.5" />
|
||||
{type.legalReference}
|
||||
</p>
|
||||
)}
|
||||
{type.startDateHint && (
|
||||
<p className="text-[10px] text-muted-foreground italic">
|
||||
<Clock className="mr-0.5 inline h-2.5 w-2.5" />
|
||||
{type.startDateHint}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Chain flow diagram ──
|
||||
|
||||
function ChainFlowDiagram({ group }: { group: ChainGroup }) {
|
||||
const isFanIn = group.sources.length > 1;
|
||||
|
||||
if (isFanIn) {
|
||||
return (
|
||||
<div className="flex items-center gap-0">
|
||||
{/* Multiple sources stacked */}
|
||||
<div className="flex flex-col gap-1">
|
||||
{group.sources.map((src) => (
|
||||
<ChainNode key={src.id} type={src} small />
|
||||
))}
|
||||
</div>
|
||||
{/* Converging arrows */}
|
||||
<div className="flex items-center px-1">
|
||||
<div className="flex flex-col items-end">
|
||||
{group.sources.map((_, i) => (
|
||||
<div key={i} className="h-px w-4 bg-amber-400/60" />
|
||||
))}
|
||||
</div>
|
||||
<div className="h-px w-3 bg-amber-400" />
|
||||
<div className="border-y-[4px] border-l-[6px] border-y-transparent border-l-amber-400" />
|
||||
</div>
|
||||
{/* Target */}
|
||||
<ChainNode type={group.target} isTarget />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Single chain: source → target
|
||||
const src = group.sources[0];
|
||||
if (!src) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-0">
|
||||
<ChainNode type={src} />
|
||||
<div className="flex items-center px-1">
|
||||
<div className="h-px w-6 bg-amber-400" />
|
||||
<div className="border-y-[4px] border-l-[6px] border-y-transparent border-l-amber-400" />
|
||||
</div>
|
||||
<ChainNode type={group.target} isTarget />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ChainNode({
|
||||
type,
|
||||
isTarget,
|
||||
small,
|
||||
}: {
|
||||
type: DeadlineTypeDef;
|
||||
isTarget?: boolean;
|
||||
small?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-md border px-2 py-1",
|
||||
small ? "max-w-[160px]" : "max-w-[200px]",
|
||||
isTarget
|
||||
? "border-amber-400 bg-amber-50/50 dark:bg-amber-950/20 dark:border-amber-700"
|
||||
: "border-border bg-background",
|
||||
)}
|
||||
>
|
||||
<p className={cn("font-medium leading-tight", small ? "text-[9px]" : "text-[10px]")}>
|
||||
{type.label}
|
||||
</p>
|
||||
<p className="text-[9px] text-muted-foreground">
|
||||
{type.days}z {type.dayType === "working" ? "lucr." : "cal."}
|
||||
{type.tacitApprovalApplicable && (
|
||||
<span className="ml-1 text-blue-600 dark:text-blue-400">tacit</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import { RegistryTable } from "./registry-table";
|
||||
import { RegistryEntryForm } from "./registry-entry-form";
|
||||
import { RegistryEntryDetail } from "./registry-entry-detail";
|
||||
import { DeadlineDashboard } from "./deadline-dashboard";
|
||||
import { DeadlineConfigOverview } from "./deadline-config-overview";
|
||||
import { ThreadExplorer } from "./thread-explorer";
|
||||
import { CloseGuardDialog } from "./close-guard-dialog";
|
||||
import { getOverdueDays } from "../services/registry-service";
|
||||
@@ -81,6 +82,7 @@ export function RegistraturaModule() {
|
||||
const [conexToEntry, setConexToEntry] = useState<RegistryEntry | null>(null);
|
||||
/** If set, the parent entry will be closed after saving the new reply entry */
|
||||
const [closesEntryId, setClosesEntryId] = useState<string | null>(null);
|
||||
const [termeneView, setTermeneView] = useState<"active" | "guide">("active");
|
||||
const [closingId, setClosingId] = useState<string | null>(null);
|
||||
const [linkCheckId, setLinkCheckId] = useState<string | null>(null);
|
||||
|
||||
@@ -630,11 +632,34 @@ export function RegistraturaModule() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="termene">
|
||||
{/* Sub-navigation: active deadlines vs. system guide */}
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<Button
|
||||
variant={termeneView === "active" ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setTermeneView("active")}
|
||||
>
|
||||
Termene active
|
||||
</Button>
|
||||
<Button
|
||||
variant={termeneView === "guide" ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setTermeneView("guide")}
|
||||
>
|
||||
<BookOpen className="mr-1.5 h-3.5 w-3.5" />
|
||||
Ghid termene
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{termeneView === "active" ? (
|
||||
<DeadlineDashboard
|
||||
entries={allEntries}
|
||||
onResolveDeadline={handleDashboardResolve}
|
||||
onAddChainedDeadline={handleAddChainedDeadline}
|
||||
/>
|
||||
) : (
|
||||
<DeadlineConfigOverview />
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user