Eliminates O(resource × allocation × days) iteration in findCapacityWindows
by pre-computing vacation date sets and using direct range overlap math.
Adds performance regression test (50 resources × 20 allocs × 365 days < 500ms).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dailyCostCents, hoursPerDay, percentage now validated at API boundary
- vacation router no longer uses ctx.db as any
- scenarioData reads through typed Zod schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents orphaned Assignment rows when a DemandRequirement is deleted.
Adds (resourceId, status, endDate) and (projectId, status, endDate)
indexes to support capacity range queries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Read-only capacity snapshot with utilization donut, top 5 active projects,
open demand alert banner, and quick-link grid — single-column card layout
optimised for PWA standalone mode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sends a Monday digest to all ADMIN + MANAGER users with:
- Team utilization % for the next 4 weeks
- Overbooked resource count
- Open demand count
- Upcoming vacation count
- Top 5 most utilized resources
Route: GET /api/cron/weekly-digest (secured by CRON_SECRET).
HTML template and plain-text fallback included.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows resources with available capacity in a selected date window.
- Filter by date range (with DateRangePresets), min hours/day slider, and free-text search
- Cards show role, chapter, available h/day with color-coded capacity bar
- Links to individual resource profiles
- "Bench" nav entry added to Resources section in AppShell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allocation bars that have active optimistic overrides (post-drag,
awaiting server confirmation) now pulse subtly via animate-pulse.
The pending set is derived from the existing optimisticAllocations
map keys, requiring no additional state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tracks scroll position via requestAnimationFrame to avoid re-renders
on every pixel. Allocation bars outside the visible horizontal window
(+ 10-column overscan) are skipped during render, reducing DOM nodes
significantly at day zoom (365 days × 40px = 14,600px canvas).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Double-clicking an allocation bar opens an inline editor overlay
with start date, end date, and hours/day fields. Saves via
trpc.allocation.update, closes on Escape or click outside.
Only visible to users with manage permissions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an Export button that downloads visible/filtered allocation rows
as an xlsx file via the existing downloadWorkbookSheets utility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows top 3 resource suggestions (name, utilization, available h/d) below the
demand details using the existing staffing.getProjectStaffingSuggestions query.
Includes a shimmer loading skeleton while fetching. Each "Fill" button opens
the fill demand modal with the demand pre-loaded.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add "Shift Dates…" action to the batch action bar. Opens a modal with a
signed integer input; on confirm calls the existing timeline.batchShiftAllocations
procedure (allocationIds, daysDelta, mode="move").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EmptyState shared component; replace AllocationsClient inline empty state
- DateRangePresets (this month/quarter/3 months/year) integrated into AllocationModal
- Debounce conflict-check inputs in AllocationModal (400ms) using existing useDebounce
- Dashboard layout save feedback via SuccessToast after DB write completes
- Scenarios nav item in Planning sidebar + /scenarios list page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: useDashboardLayout initialised React state with createDefaultDashboardLayout()
(1 widget), so the wrong default rendered during the ~100–500ms window while React Query
fetched the user session and DB layout after login. On reload within staleTime the cache
hit resolved instantly, masking the bug.
Fix: add isHydrated boolean state that becomes true only once localStorage OR DB
hydration has settled; DashboardClient renders a GridLayoutSkeleton until then.
Also adds router.refresh() in the sign-in handler to bust the Next.js Router Cache
so the post-login navigation always lands on a fresh server component tree.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New ConflictWarningPanel component: amber box with per-day overbooking
table (capacity / already booked / new / overage) and sky-blue info box
for vacation overlap. Overbooking section has an 'I understand' checkbox
that must be ticked before Save is enabled; vacation overlap is
informational only.
- AllocationModal: fires allocation.checkConflicts reactively when
resourceId, dates and hoursPerDay are all set. Shows ConflictWarningPanel
between form body and footer. Passes allowOverbooking: true to the
createAssignment mutation when the user acknowledges. Acknowledgment
resets whenever key fields change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New allocation.checkConflicts managerProcedure: returns per-day overbooking
breakdown (availableHours, existingHours, requestedHours, overageHours,
maxOverbookPercent) plus vacation overlap list for the requested period.
Read-only — used by AllocationModal for pre-submission warnings.
- createAssignment(): replace the hard >5-day overbooking block with a soft
CONFLICT error. When allowOverbooking: true is passed the assignment is
created and overbookingAcknowledged is set to true on the record.
- allowOverbooking field added to CreateAssignmentBaseSchema (optional)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Assignment.overbookingAcknowledged Boolean @default(false) — audit trail
for intentional overbookings
- CreateAssignmentBaseSchema gets allowOverbooking?: boolean flag so callers
can explicitly opt in to overbooking
- Export AllocationConflictCheckResult, AllocationConflictDay,
AllocationVacationOverlap types from @capakraken/shared for use in the
new conflict-check API procedure and AllocationModal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- event-bus: wrap each subscriber.fn call in try/catch so one throwing subscriber cannot kill delivery to all others
- event-bus: log Redis parse errors instead of swallowing them silently; add .catch() on Redis publish promise for async fallback to local delivery
- pruning.ts: new runPruning() deletes expired invite tokens, expired password-reset tokens, and read notifications older than 90 days
- settings.runPruning: expose pruning as adminProcedure mutation
- trpc.ts: E2E_TEST_MODE rate-limit bypass is now a no-op in production (NODE_ENV=production); logs a startup warning if misconfigured
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- applyProjectScenario: wrap assignment loop in db.$transaction to prevent partial updates
- vacation approve/reject: fix TOCTOU race via updateMany with status-guard in WHERE + CONFLICT on count=0
- vacation cancel: wrap vacation.update + entitlement.updateMany in $transaction
- batchApprove: collect mutations, wrap in $transaction, dispatch SSE/notifications after commit
- Fix dead-code bug in createHappyPathDb where $transaction was assigned after return
- Add atomicity and concurrency tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AnimatedModal: add disableBackdropClose prop (default false, no impact
on existing consumers). When true, overlay onClick is removed.
ProjectWizard: remove handleBackdropClick — backdrop click no longer
closes the wizard. Only the X button and Cancel close it.
EstimateWizard already had no backdrop-click handler; no change needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract EMPTY_VALIDATE_INPUT as module constant (prevents new object on every render)
- Extract IssueList component + ISSUE_STYLES map (eliminates blocker/warning copy-paste)
- Extract ReadinessIssue type from ReadinessReport
- Reuse buildValidateInput in handleSubmit (single source for path mapping)
- Guard setValidateInput(null) in onChange — only resets when not already null
- Remove unnecessary `as ReadinessReport` cast (tRPC infers the type)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This override was blocking all dark: Tailwind classes on amber-50 elements.
Components now use explicit dark:bg-amber-950/30 instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hardcoded dates (2026-03-20 / 2026-04-05) were now in the past, causing
the demand window filter (endDate >= now) to exclude the mock demand
requirement and miss the expected staffing anomaly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a "Validate" button that calls the existing `validateImportBatch`
tRPC query before staging. Shows a readiness report inline:
- Green/amber/red status line based on canCommitWithStrictSourceData
- Record counts (resources, projects, assignments, vacations)
- Blocker issues in red with resolution hints
- Warnings in amber with resolution hints
- Fallback assumptions listed in gray
Also fixes a pre-existing bug where handleSubmit mapped wrong filePaths
keys to API fields (keys are resources/projects/assignments, not the
API field names).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The warning was showing briefly on every page load because
`!aiConfigQuery.data?.configured` evaluated to true while the query
was still in-flight (data === undefined). Guard with `!isLoading` so
the amber box only appears after the query resolves with configured=false.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All chapter text inputs now show autocomplete suggestions from the
database (distinct chapter values from active resources) via HTML
<datalist> wired to trpc.resource.chapters:
- ResourceModal: chapter input
- RateCardsClient: rate card line chapter input
- EffortRulesClient: effort rule chapter input
- ExperienceMultipliersClient: replaces hardcoded CHAPTER_PRESETS
with live data, falls back to presets when no data available
Also revert blueprintRolePresetsInputSchema to z.array(z.unknown())
to restore compatibility with StaffingRequirement[] call sites.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add batchHardDelete adminProcedure to resource-mutations router
- Per-row Delete button visible to ADMIN role only
- Delete Selected button in BatchActionBar for ADMIN role only
- Two-step confirmation dialogs with permanent-action warnings
- Audit log written for each deleted resource
Co-Authored-By: claude-flow <ruv@ruv.net>
#49 — upgrade MyVacationsClient account-not-linked inline text to a
prominent centred amber card with icon, heading, and admin-action guidance.
#57 — replace vague AI suggestions hint with actionable copy explaining
the Step 3 dependency before the user wonders why the list is empty.
Co-Authored-By: claude-flow <ruv@ruv.net>
#58: Split the merged "Type: BD / INT" field in the wizard review step into
separate "Order Type", "Allocation Type", and "Status on create" rows so
users can clearly distinguish commercial classification from lifecycle status.
#60: Relabel FillOpenDemandModal staging CTA from "+ Add to Plan" to
"+ Queue Assignment" and the proceed CTA from "Review (N)" to
"Review Queued (N)" to make the staged/non-final nature of the action clear.
Also correct the project detail Assignments label from "N active" to
"N planned" and update the tooltip to include PROPOSED in the definition.
Co-Authored-By: claude-flow <ruv@ruv.net>
#66: Project detail "Open Demands" summary incorrectly counted COMPLETED
demands as open. Fix: add `status !== "COMPLETED"` to the activeDemands
filter in /projects/[id]/page.tsx.
#59/#67: Project creation and edit had two bugs:
1. Both invalidated `project.list` but the page queries `project.listWithCosts`
— the list never refreshed after a save.
2. Success toasts were either absent (ProjectModal) or mounted inside the
wizard component that unmounts before the toast finishes.
Fix: correct invalidation key to listWithCosts; add optional onSuccess prop
to both ProjectWizard and ProjectModal; ProjectsClient wires onSuccess to a
persistent SuccessToast rendered outside the modals.
Co-Authored-By: claude-flow <ruv@ruv.net>
#55: Add SuccessToast after new resource is created. ResourceModal gains an
optional onSuccess(displayName) prop; ResourcesClient wires it to a toast
that auto-dismisses after 2.5 s.
#56: Fix useFocusTrap stale-closure bug. Focusable elements are now queried
dynamically inside handleKeyDown (not captured once at mount), so Tab
navigation stays correct as the form re-renders. Initial focus is deferred
via requestAnimationFrame so the browser layout is stable before focus() fires.
Co-Authored-By: claude-flow <ruv@ruv.net>
Adds a transactional hard-delete procedure behind adminProcedure that
removes a resource's assignments and vacations first, then the record
itself, and writes an audit log entry. The ResourceModal exposes a
"Delete Resource" button (edit mode, ADMIN role only) with an inline
confirm step before the mutation fires.
Co-Authored-By: claude-flow <ruv@ruv.net>
- #51: Add permanent redirect /login → /auth/signin in next.config.ts
so users/testers who type the common alias land on the correct auth page
- #53: Add "Allocations → New Planning Entry" link to empty states of
ProjectDemandsTable and ProjectAssignmentsTable; add shortcut link in
demands table header for canEdit users
- #54: Track confirmed dropdown selection in ResourcePersonPicker —
green ring + checkmark icon shown when user picks from suggestions;
cleared on any manual keypress so free-text is clearly unconfirmed
Co-Authored-By: claude-flow <ruv@ruv.net>
- e2e/dev-system/nav-smoke.spec.ts: new Playwright spec in the dev-system
suite; iterates every href from navSections + adminNavEntries, asserts
HTTP status ≠ 404 and no "page not found" text for an authenticated admin
- e2e/navigation.spec.ts: add "all nav routes resolve" smoke block covering
16 routes not previously tested in the isolated test suite
All 35 routes pass against live dev server. Catches dead nav links before
users encounter them.
Co-Authored-By: claude-flow <ruv@ruv.net>