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
@@ -3,8 +3,10 @@
import { useState } from "react";
import { useSearchParams } from "next/navigation";
import { trpc } from "~/lib/trpc/client.js";
import { usePermissions } from "~/hooks/usePermissions.js";
import { TaskCard } from "./TaskCard.js";
import { ReminderModal } from "./ReminderModal.js";
import { CreateTaskModal } from "./CreateTaskModal.js";
type TabKey = "all" | "notifications" | "tasks" | "reminders" | "approvals";
@@ -27,6 +29,8 @@ export function NotificationCenterClient() {
const searchParams = useSearchParams();
const initialTab = (searchParams.get("tab") as TabKey) || "all";
const [activeTab, setActiveTab] = useState<TabKey>(initialTab);
const { canEdit } = usePermissions();
const [showTaskModal, setShowTaskModal] = useState(false);
const [reminderModal, setReminderModal] = useState<{
open: boolean;
reminder: {
@@ -124,6 +128,18 @@ export function NotificationCenterClient() {
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-50">Notification Center</h1>
<div className="flex items-center gap-3">
{canEdit && activeTab === "tasks" && (
<button
type="button"
onClick={() => setShowTaskModal(true)}
className="inline-flex items-center gap-1.5 rounded-lg bg-brand-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-brand-700 transition-colors"
>
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
Create Task
</button>
)}
{activeTab === "reminders" && (
<button
type="button"
@@ -389,6 +405,14 @@ export function NotificationCenterClient() {
onSuccess={() => setReminderModal({ open: false, reminder: null })}
/>
)}
{/* Create Task Modal */}
{showTaskModal && (
<CreateTaskModal
onClose={() => setShowTaskModal(false)}
onSuccess={() => setShowTaskModal(false)}
/>
)}
</div>
);
}