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>
#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>
- #47: Remove misleading asterisk from Budget (EUR) label in project
wizard — budget is optional per canGoNext() logic
- #48: Parse Zod validation JSON in wizard submit error handler so users
see "Responsible person is required" instead of raw JSON array
- #45: Expose isEntriesError from timeline query context; TimelineView
now renders an explicit error message instead of a silent empty canvas
when the getEntriesView query fails
Co-Authored-By: claude-flow <ruv@ruv.net>
- Split auth config into auth.config.ts (edge-safe, no argon2) and auth-edge.ts
for middleware use; auth.ts now spreads the shared config
- Middleware wraps with auth() to redirect unauthenticated requests to /auth/signin
before any page render; passes through /auth/, /api/, /invite/ paths
- SessionGuard client component watches useSession() and redirects on
status=unauthenticated, closing the SPA navigation gap
- QueryCache + MutationCache in TRPCProvider redirect on UNAUTHORIZED tRPC errors
without retrying; SessionProvider polls session state every 5 minutes
- Middleware tests updated for async auth wrapper and auth-edge mock
Co-Authored-By: claude-flow <ruv@ruv.net>
- useTimelineDrag: onProjectBarMouseDown and single-alloc drag path now reset
multiSelectRef + multiSelectState before starting a new drag, so the
FloatingActionBar is dismissed immediately when an unrelated drag begins
- FloatingActionBar.test.tsx: 4 regression tests for the null-render guard
(count=0) and all three label variants
- useTimelineSSE.test.ts: 2 new tests — tab hides during pending reconnect
timer (clears timer, resyncs on next open) and first-ever connection fails
before any open (retry open still resyncs correctly)
- assistant-tools-user-admin-inventory-read.test.ts: add isActive to expected
findMany select shape (already in production, test was stale)
Co-Authored-By: claude-flow <ruv@ruv.net>
- Invite flow: admin can invite users by email with role selection; accept-invite page
sets password and creates the account; 72-hour token expiry; E2E tests
- User deactivate/reactivate/delete: new tRPC procedures + UI buttons; deactivation
revokes all active sessions immediately; delete cascades vacation/broadcast records;
isActive field added via migration 20260402000000_user_isactive
- Auth: block login for inactive users with audit entry
- Favicon: SVG favicon + ICO/PNG fallbacks (16, 32, 180, 192, 512px); manifest updated
- Dashboard: GridLayout dynamic-import loading skeleton prevents blank dark area
on first login before react-grid-layout chunk is cached
- Admin users: remove max-w-5xl constraint so table uses full page width
- Dev: docker container restart workflow documented in LEARNINGS.md; Prisma generate
must run inside the container after schema changes (named node_modules volume)
Co-Authored-By: claude-flow <ruv@ruv.net>
#41 (critical): Replace plain Error throws in authorize() with CredentialsSignin
subclasses (MfaRequiredError / MfaRequiredSetupError / InvalidTotpError).
Auth.js v5 forwards CredentialsSignin.code to the client via SignInResponse.code;
plain throws become CallbackRouteError and the message is never visible.
Signin page now checks result.code ?? result.error for exact code matching.
#38: MfaPromptBanner converted to fully client-side component via
trpc.user.getMfaStatus.useQuery() — disappears immediately after MFA enable
without requiring page reload. Snooze key remains userId-scoped via useSession().
Server-side prisma.user.findUnique call removed from (app)/layout.tsx.
#40: NEXTAUTH_URL default fallback removed from docker-compose.yml.
The variable is now required (:?) — docker compose up fails with a descriptive
error if the value is missing, preventing silent localhost redirect bugs.
Tests: auth.test.ts (5), MfaPromptBanner.test.ts (7), reset-password.test.ts (6)
All new tests green. pnpm --filter @capakraken/web exec tsc --noEmit clean.
Co-Authored-By: claude-flow <ruv@ruv.net>
#28 - TOTP rate limiting (verifyTotp): added totpRateLimiter (10 req/30s),
throws TOO_MANY_REQUESTS before DB hit; 16 unit tests including rate-limit
exceeded + userId key isolation.
#29 - /api/reports/allocations role check: only ADMIN/MANAGER/CONTROLLER may
access; returns 403 otherwise; 9 unit tests (401 unauthenticated, 403 for
USER/VIEWER, 200 for allowed roles + xlsx format).
#31 - pgAdmin credentials moved out of docker-compose.yml into env vars;
PGADMIN_PASSWORD is now required (:?) to prevent accidental plaintext
exposure in committed files.
#34 - Server-side HTML sanitization for comment bodies via stripHtml():
strips all tags + decodes safe entities before persistence; 16 unit tests
covering passthrough, injection patterns, entity decoding.
#35 - MFA setup prompt banner (MfaPromptBanner): shown to ADMIN/MANAGER users
without TOTP enabled; user-scoped localStorage snooze (7 days); links to
/account/security; accessibility role=alert; 7 structural unit tests.
#33 - Auth anomaly alerting cron (/api/cron/auth-anomaly-check): detects
HIGH_GLOBAL_FAILURE_RATE and CONCENTRATED_FAILURES in 30-minute window;
CRITICAL notification to ADMINs; fail-closed via verifyCronSecret;
10 unit tests.
#32 - MFA enforcement policy: added requireMfaForRoles field to SystemSettings
schema + Prisma migration; auth.ts blocks login with MFA_REQUIRED_SETUP
signal if role is enforced but TOTP not set up; signin page redirects to
/account/security?mfa_required=1; settings schema + view model updated;
11 unit tests.
#30 - API keys architecture decision documented in LEARNINGS.md; no code
written — product decision required before implementation.
Co-Authored-By: claude-flow <ruv@ruv.net>
#19 MFA QR code: render locally via qrcode package, remove external qrserver.com request
#20 Webhook SSRF: add ssrf-guard.ts with DNS-verified IP blocklist; enforce on create/update/test/dispatch
#21 /api/perf: fail-closed when CRON_SECRET missing; remove query-string token auth
#22 CSP: remove unsafe-eval and unsafe-inline from script-src in production builds
#23 Active session registry: forward jti into session object; validate against ActiveSession on every tRPC request
#24 Docker: add missing packages/application to Dockerfile.dev; fix pnpm-lock.yaml glob;
run db:migrate:deploy on container start so a fresh checkout boots without manual steps
Also: fix pre-existing TS error in e2e/allocations.spec.ts (args.length literal type overlap)
Co-Authored-By: claude-flow <ruv@ruv.net>