feat: project colors, timeline filters, sidebar fix, GitLooper agent, and misc improvements

- Fix sidebar double-highlight on /vacations/my (Gitea #6): add isNavItemActive() helper
- Add project color picker (schema + API + modal + timeline rendering)
- Add ProjectCombobox/ResourceCombobox to timeline toolbar
- Show PENDING vacations on timeline with dashed/dimmed style
- Add "show demand projects" preference with localStorage persistence
- Add ProjectAssignmentsTable with total hours/cost columns
- Extend vacation API to accept status arrays
- Add GitLooper formal YAML agent configuration
- Extend user admin with permission overrides UI
- Add delete-assignment use case tests
- Add status-styles.ts shared badge constants
- Centralize formatMoney/formatCents in format.ts

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-17 10:22:52 +01:00
parent b0e55786c3
commit eb283147d1
34 changed files with 1545 additions and 255 deletions
@@ -3,6 +3,7 @@
import { useRef, useState, useMemo, useCallback } from "react";
import { AllocationStatus } from "@planarchy/shared";
import { useFocusTrap } from "~/hooks/useFocusTrap.js";
import { formatDateMedium } from "~/lib/format.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { trpc } from "~/lib/trpc/client.js";
@@ -41,11 +42,6 @@ interface PlannedResource {
estimatedCostCents: number;
}
function fmtDate(date: Date | string): string {
const d = typeof date === "string" ? new Date(date) : date;
return d.toLocaleDateString("en-GB", { year: "numeric", month: "short", day: "numeric" });
}
export function FillOpenDemandModal({ allocation, onClose, onSuccess }: FillOpenDemandModalProps) {
// ─── Phase: "plan" (select resources) → "confirm" (review & submit) ──
const [phase, setPhase] = useState<"plan" | "confirm">("plan");
@@ -209,7 +205,7 @@ export function FillOpenDemandModal({ allocation, onClose, onSuccess }: FillOpen
<div className="flex-1">
<div className="font-medium text-gray-900 dark:text-gray-100">{roleName}</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
{allocation.project?.name} · {fmtDate(allocation.startDate)} {fmtDate(allocation.endDate)}
{allocation.project?.name} · {formatDateMedium(allocation.startDate)} {formatDateMedium(allocation.endDate)}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400">
{allocation.hoursPerDay}h/day · {totalDemandHours.toLocaleString()}h total