feat: enhanced timeline hover tooltip with role, dates, status

The tooltip shown when hovering over project strips in the timeline
now includes additional information:
- Role name (e.g. "3D Artist", "Project Manager")
- Assignment date range (2026-03-01 → 2026-06-30)
- Status badge when not CONFIRMED (shows PROPOSED, DRAFT, etc.)
- Lead person and order type on the same line

Data comes from already-loaded timeline entries — no extra API calls.
Safe change: tooltip is pointer-events-none and read-only.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-23 07:30:08 +01:00
parent d46937300a
commit 3c0befb7db
2 changed files with 42 additions and 6 deletions
@@ -141,6 +141,10 @@ function TimelineResourcePanelInner({
orderType: string;
hoursPerDay: number;
responsiblePerson?: string | null;
role?: string | null;
status?: string;
startDate?: string;
endDate?: string;
}[];
} | null>(null);
@@ -295,6 +299,10 @@ function TimelineResourcePanelInner({
orderType: string;
hours: number;
responsiblePerson?: string | null;
role?: string | null;
status?: string;
startDate?: string;
endDate?: string;
}
>();
for (const alloc of a) {
@@ -314,6 +322,10 @@ function TimelineResourcePanelInner({
hours: alloc.hoursPerDay,
responsiblePerson:
(alloc.project as { responsiblePerson?: string | null }).responsiblePerson ?? null,
role: alloc.role ?? alloc.roleEntity?.name ?? null,
status: alloc.status,
startDate: new Date(alloc.startDate).toISOString().slice(0, 10),
endDate: new Date(alloc.endDate).toISOString().slice(0, 10),
});
}
}
@@ -13,6 +13,10 @@ export type HeatmapHoverData = {
orderType: string;
hoursPerDay: number;
responsiblePerson?: string | null;
role?: string | null;
status?: string;
startDate?: string;
endDate?: string;
}[];
};
@@ -81,10 +85,20 @@ export function TimelineTooltip({
{entry.projectName}
</div>
<div className="truncate text-[11px] text-gray-400">
{entry.responsiblePerson
? `Lead: ${entry.responsiblePerson}`
: entry.orderType}
{[
entry.role,
entry.responsiblePerson ? `Lead: ${entry.responsiblePerson}` : null,
entry.orderType,
].filter(Boolean).join(" · ")}
</div>
{entry.startDate && entry.endDate && (
<div className="text-[10px] text-gray-500">
{entry.startDate} {entry.endDate}
{entry.status && entry.status !== "CONFIRMED" && (
<span className="ml-1 uppercase text-amber-400">{entry.status}</span>
)}
</div>
)}
</div>
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
{entry.hoursPerDay}h
@@ -146,10 +160,20 @@ export function TimelineTooltip({
{entry.projectName}
</div>
<div className="truncate text-[11px] text-gray-400">
{entry.responsiblePerson
? `Lead: ${entry.responsiblePerson}`
: entry.orderType}
{[
entry.role,
entry.responsiblePerson ? `Lead: ${entry.responsiblePerson}` : null,
entry.orderType,
].filter(Boolean).join(" · ")}
</div>
{entry.startDate && entry.endDate && (
<div className="text-[10px] text-gray-500">
{entry.startDate} {entry.endDate}
{entry.status && entry.status !== "CONFIRMED" && (
<span className="ml-1 uppercase text-amber-400">{entry.status}</span>
)}
</div>
)}
</div>
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
{entry.hoursPerDay}h