diff --git a/src/modules/registratura/components/deadline-dashboard.tsx b/src/modules/registratura/components/deadline-dashboard.tsx index bf4ac82..c0f386c 100644 --- a/src/modules/registratura/components/deadline-dashboard.tsx +++ b/src/modules/registratura/components/deadline-dashboard.tsx @@ -291,6 +291,7 @@ function EntryDeadlineCard({ deadline={item.deadline} status={item.status} entryId={entry.id} + allDeadlines={entry.trackedDeadlines ?? []} chainCount={chainCount} isExpanded={isExpanded} onToggleChain={() => onToggleChain(item.deadline.id)} @@ -319,10 +320,88 @@ function EntryDeadlineCard({ // ── Single deadline row ── +// ── Milestone data for progress bar ── + +interface ProgressMilestone { + position: number; // 0..1 on the progress bar + label: string; + isPassed: boolean; + typeId: string; +} + +function computeMilestones( + mainDeadline: TrackedDeadline, + allDeadlines: TrackedDeadline[], +): ProgressMilestone[] { + const mainStart = new Date(mainDeadline.startDate).getTime(); + const mainDue = new Date(mainDeadline.dueDate).getTime(); + const totalSpan = mainDue - mainStart; + if (totalSpan <= 0) return []; + + const now = Date.now(); + const milestones: ProgressMilestone[] = []; + + for (const dl of allDeadlines) { + if (dl.id === mainDeadline.id) continue; + const def = getDeadlineType(dl.typeId); + if (!def?.autoTrack) continue; + if (def.backgroundOnly) continue; + + const dlDue = new Date(dl.dueDate).getTime(); + // Only show milestones that fall within the main deadline's time range + if (dlDue <= mainStart || dlDue >= mainDue) continue; + + const pos = (dlDue - mainStart) / totalSpan; + if (pos < 0.03 || pos > 0.97) continue; // too close to edges + + milestones.push({ + position: pos, + label: getMilestoneLabel(def.id, dlDue < now), + isPassed: dlDue < now, + typeId: def.id, + }); + } + + milestones.sort((a, b) => a.position - b.position); + return milestones; +} + +function getMilestoneLabel(typeId: string, isPassed: boolean): string { + if (typeId.includes("verificare")) { + return isPassed + ? "Verificare expirata — nu se mai pot solicita clarificari" + : "Termen verificare cerere"; + } + if (typeId.includes("completari-limit")) { + return isPassed + ? "Termen solicitare completari expirat" + : "Limita solicitare completari"; + } + if (typeId.includes("comunicare")) { + return isPassed ? "Termen comunicare expirat" : "Termen comunicare"; + } + if (typeId.includes("solicitare-aviz")) { + return isPassed + ? "Termen solicitare aviz expirat" + : "Solicitare aviz primar"; + } + if (typeId.includes("aviz-primar")) { + return isPassed ? "Aviz primar expirat" : "Aviz primar"; + } + if (typeId.includes("depunere-comisie")) { + return isPassed ? "Depunere comisie expirata" : "Depunere la comisie"; + } + const def = getDeadlineType(typeId); + return def?.label ?? typeId; +} + +// ── Single deadline row ── + function DeadlineRow({ deadline, status, entryId, + allDeadlines, chainCount, isExpanded, isChainChild, @@ -332,6 +411,7 @@ function DeadlineRow({ deadline: TrackedDeadline; status: DeadlineDisplayStatus; entryId: string; + allDeadlines?: TrackedDeadline[]; chainCount?: number; isExpanded?: boolean; isChainChild?: boolean; @@ -358,98 +438,150 @@ function DeadlineRow({ ? Math.min(100, Math.max(0, (elapsedDays / totalDays) * 100)) : 100; - return ( -