feat(planning): ship holiday-aware planning and assistant upgrades

This commit is contained in:
2026-03-28 22:49:28 +01:00
parent 2a005794e7
commit 4f48afe7b4
151 changed files with 17738 additions and 1940 deletions
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react";
import type { AllocationLike, AllocationReadModel, Assignment } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "~/hooks/useInvalidatePlanningViews.js";
import { useViewportPopover } from "~/hooks/useViewportPopover.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { DateInput } from "~/components/ui/DateInput.js";
@@ -28,9 +29,14 @@ export function AllocationPopover({
anchorX,
anchorY,
}: AllocationPopoverProps) {
const ref = useRef<HTMLDivElement>(null);
const utils = trpc.useUtils();
const invalidateTimeline = useInvalidateTimeline();
const { ref, style } = useViewportPopover({
anchor: { kind: "point", x: anchorX, y: anchorY },
width: 300,
estimatedHeight: 360,
onClose,
});
const { data: allocationView, isLoading } = trpc.allocation.listView.useQuery(
{ projectId },
@@ -63,17 +69,6 @@ export function AllocationPopover({
},
});
// Close on outside click
useEffect(() => {
function handleClick(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) {
onClose();
}
}
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, [onClose]);
function toDateInput(d: Date): string {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
@@ -93,18 +88,9 @@ export function AllocationPopover({
});
}
// Position popover so it stays on screen
const popoverStyle: React.CSSProperties = {
position: "fixed",
left: Math.min(anchorX, window.innerWidth - 320),
top: Math.min(anchorY + 8, window.innerHeight - 360),
zIndex: 50,
width: 300,
};
if (isLoading || !allocation) {
return (
<div ref={ref} style={popoverStyle} className="bg-white border border-gray-200 rounded-xl shadow-xl p-4 text-sm text-gray-500">
<div ref={ref} style={style} className="bg-white border border-gray-200 rounded-xl shadow-xl p-4 text-sm text-gray-500">
Loading...
</div>
);
@@ -115,7 +101,7 @@ export function AllocationPopover({
return (
<div
ref={ref}
style={popoverStyle}
style={style}
className="bg-white border border-gray-200 rounded-xl shadow-xl overflow-hidden"
>
{/* Header */}