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:
@@ -141,6 +141,10 @@ function TimelineResourcePanelInner({
|
|||||||
orderType: string;
|
orderType: string;
|
||||||
hoursPerDay: number;
|
hoursPerDay: number;
|
||||||
responsiblePerson?: string | null;
|
responsiblePerson?: string | null;
|
||||||
|
role?: string | null;
|
||||||
|
status?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}[];
|
}[];
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
@@ -295,6 +299,10 @@ function TimelineResourcePanelInner({
|
|||||||
orderType: string;
|
orderType: string;
|
||||||
hours: number;
|
hours: number;
|
||||||
responsiblePerson?: string | null;
|
responsiblePerson?: string | null;
|
||||||
|
role?: string | null;
|
||||||
|
status?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
for (const alloc of a) {
|
for (const alloc of a) {
|
||||||
@@ -314,6 +322,10 @@ function TimelineResourcePanelInner({
|
|||||||
hours: alloc.hoursPerDay,
|
hours: alloc.hoursPerDay,
|
||||||
responsiblePerson:
|
responsiblePerson:
|
||||||
(alloc.project as { responsiblePerson?: string | null }).responsiblePerson ?? null,
|
(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;
|
orderType: string;
|
||||||
hoursPerDay: number;
|
hoursPerDay: number;
|
||||||
responsiblePerson?: string | null;
|
responsiblePerson?: string | null;
|
||||||
|
role?: string | null;
|
||||||
|
status?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,10 +85,20 @@ export function TimelineTooltip({
|
|||||||
{entry.projectName}
|
{entry.projectName}
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-[11px] text-gray-400">
|
<div className="truncate text-[11px] text-gray-400">
|
||||||
{entry.responsiblePerson
|
{[
|
||||||
? `Lead: ${entry.responsiblePerson}`
|
entry.role,
|
||||||
: entry.orderType}
|
entry.responsiblePerson ? `Lead: ${entry.responsiblePerson}` : null,
|
||||||
|
entry.orderType,
|
||||||
|
].filter(Boolean).join(" · ")}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
|
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
|
||||||
{entry.hoursPerDay}h
|
{entry.hoursPerDay}h
|
||||||
@@ -146,10 +160,20 @@ export function TimelineTooltip({
|
|||||||
{entry.projectName}
|
{entry.projectName}
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-[11px] text-gray-400">
|
<div className="truncate text-[11px] text-gray-400">
|
||||||
{entry.responsiblePerson
|
{[
|
||||||
? `Lead: ${entry.responsiblePerson}`
|
entry.role,
|
||||||
: entry.orderType}
|
entry.responsiblePerson ? `Lead: ${entry.responsiblePerson}` : null,
|
||||||
|
entry.orderType,
|
||||||
|
].filter(Boolean).join(" · ")}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
|
<span className="whitespace-nowrap text-[11px] font-semibold text-gray-200">
|
||||||
{entry.hoursPerDay}h
|
{entry.hoursPerDay}h
|
||||||
|
|||||||
Reference in New Issue
Block a user