"use client"; import dynamic from "next/dynamic"; import { trpc } from "~/lib/trpc/client.js"; import type { WidgetProps } from "~/components/dashboard/widget-registry.js"; import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; const PeakTimesChart = dynamic( () => import("~/components/dashboard/widgets/PeakTimesChart.js"), { ssr: false, loading: () =>
}, ); export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) { const granularity = (config.granularity as "week" | "month") || "month"; const groupBy = (config.groupBy as "project" | "chapter" | "resource") || "project"; const now = new Date(); const startDate = new Date(now.getFullYear(), now.getMonth() - 2, 1).toISOString(); const endDate = new Date(now.getFullYear(), now.getMonth() + 6, 0).toISOString(); const { data, isLoading } = trpc.dashboard.getPeakTimes.useQuery( { startDate, endDate, granularity, groupBy }, { staleTime: 120_000, placeholderData: (prev) => prev }, ); if (isLoading) { return (
{[...Array(12)].map((_, i) => (
))}
); } const periods = data ?? []; // Collect all group names const allGroups = new Set(); for (const p of periods) { for (const g of p.groups) allGroups.add(g.name); } const groups = [...allGroups].slice(0, 10); // Build recharts data const chartData = periods.map((p) => { const row: Record = { period: p.period, capacity: p.capacityHours }; for (const g of p.groups) { row[g.name] = g.hours; } return row; }); return (
{/* Controls + info */}
Stacked bars = booked hours per group per period (last 2 months to next 6 months).
Red dashed line = total capacity estimate (all active resources × available hours per day × working days).
Bars exceeding the capacity line indicate over-allocation risk. } width="w-80" position="bottom" />
{/* Chart */}
); }