feat: timeline multi-select, demand popover, resource hover card, merged tooltips, dark mode fixes

Major timeline enhancements:
- Right-click drag multi-selection with floating action bar (batch delete/assign)
- DemandPopover for demand strip details (replaces broken "Loading" modal)
- ResourceHoverCard on name hover showing skills, rates, role, chapter
- Merged heatmap+vacation tooltips into unified TimelineTooltip component
- Fixed overbooking blink animation (date normalization, z-index ordering)
- Fixed dark mode sticky column bleed-through in project view
- System roles admin page, notification task management, performance review docs

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-18 23:43:51 +01:00
parent d0f04f13f8
commit ddec3a927a
67 changed files with 4930 additions and 1166 deletions
@@ -8,7 +8,7 @@ import type {
function EmptyState({ children }: { children: React.ReactNode }) {
return (
<div className="rounded-3xl border border-dashed border-gray-200 bg-white px-5 py-10 text-center text-sm text-gray-400">
<div className="rounded-3xl border border-dashed border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-5 py-10 text-center text-sm text-gray-400">
{children}
</div>
);
@@ -24,25 +24,25 @@ export function AssumptionsTab({ estimate }: { estimate: EstimateWorkspaceView }
}
return (
<div className="rounded-3xl border border-gray-200 bg-white shadow-sm">
<div className="border-b border-gray-100 px-6 py-4">
<h2 className="text-sm font-semibold uppercase tracking-wide text-gray-600">Commercial and delivery assumptions <InfoTooltip content="Preconditions that affect the estimate validity. If any assumption changes, the estimate may need revision." /></h2>
<div className="rounded-3xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm">
<div className="border-b border-gray-100 dark:border-gray-700/50 px-6 py-4">
<h2 className="text-sm font-semibold uppercase tracking-wide text-gray-600 dark:text-gray-400">Commercial and delivery assumptions <InfoTooltip content="Preconditions that affect the estimate validity. If any assumption changes, the estimate may need revision." /></h2>
</div>
<div className="divide-y divide-gray-100">
<div className="divide-y divide-gray-100 dark:divide-gray-700/50">
{assumptions.map((assumption) => (
<div key={assumption.id} className="grid gap-3 px-6 py-4 md:grid-cols-[160px,1fr,1fr]">
<div>
<p className="text-xs uppercase tracking-wide text-gray-400">Category <InfoTooltip content="Groups assumptions by topic, e.g. 'commercial', 'delivery', 'technical'." /></p>
<p className="mt-1 text-sm font-medium text-gray-900">{assumption.category}</p>
<p className="mt-1 text-sm font-medium text-gray-900 dark:text-gray-100">{assumption.category}</p>
</div>
<div>
<p className="text-xs uppercase tracking-wide text-gray-400">Label <InfoTooltip content="Human-readable description of the assumption. The key below is the machine-readable identifier." /></p>
<p className="mt-1 text-sm text-gray-800">{assumption.label}</p>
<p className="mt-1 text-sm text-gray-800 dark:text-gray-200">{assumption.label}</p>
<p className="mt-1 text-xs text-gray-400">{assumption.key}</p>
</div>
<div>
<p className="text-xs uppercase tracking-wide text-gray-400">Value <InfoTooltip content="The concrete value or condition for this assumption." /></p>
<p className="mt-1 text-sm text-gray-800">{String(assumption.value)}</p>
<p className="mt-1 text-sm text-gray-800 dark:text-gray-200">{String(assumption.value)}</p>
</div>
</div>
))}