Commit Graph

156 Commits

Author SHA1 Message Date
Hartmut 63db4a09e6 refactor(web): set up component test infra + decompose ProjectWizard
Phase 4a: Add @testing-library/react, user-event, jest-dom, jsdom.
Switch vitest environment to jsdom, add setup file, create test-utils
with QueryClient wrapper.

Phase 4b: Extract ProjectWizard form logic into project-wizard/ subdir:
- types.ts: WizardState, Assignment, constants, factory functions
- useProjectWizardForm.ts: form state hook + canGoNext pure function

Phase 4c: 32 tests for canGoNext validation (all 5 steps), makeDefaultState,
and makeReq factory function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:00:45 +02:00
Hartmut 9bd3781c03 fix(types): flatten tRPC Zod schema types to resolve TS2589 inference depth errors
Cast Zod schemas with .refine()/.superRefine() to z.ZodType<InferredType> at the
procedure level. This short-circuits TypeScript's deep type recursion through
tRPC's middleware chain, eliminating 4 of 5 @ts-expect-error TS2589 suppressions
in web components (VacationModal, ProjectModal, UsersClient, CountriesClient).

Applied same pattern to allocation, timeline, staffing, dashboard, project, and
resource query/mutation procedures to reduce client-side type depth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:28:12 +02:00
Hartmut 0d79f97d7a fix(types): remove unnecessary as any casts in web components
- ProjectHealthWidget: row already typed as ProjectHealthRow with id field
- ResourceDetail: use narrowed unknown cast instead of any for error code
- provider.tsx: same pattern for TRPCClientError data access
- ChatPanel: use intersection type for Next.js typed route push

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:13:06 +02:00
Hartmut 82acc56b8d chore: add pre-commit hooks, tighten ESLint, activate Sentry DSN, publish CI coverage (Phase 1)
- Install husky v9 + lint-staged: pre-commit runs eslint --fix and prettier on staged files
- Tighten ESLint base config: no-console→error, ban-ts-comment (ts-ignore banned, ts-expect-error with description allowed), reportUnusedDisableDirectives→error
- Migrate web app from deprecated `next lint` to `eslint src/` with flat config and react-hooks plugin
- Convert all 5 @ts-ignore to @ts-expect-error with descriptions, remove stale disable comments
- Add NEXT_PUBLIC_SENTRY_DSN to docker-compose.prod.yml and .env.example
- Add coverage artifact upload step to CI test job
- Pre-existing violations (102 warnings) downgraded to warn in web config for Phase 2 cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 14:49:29 +02:00
Hartmut e4bf121b33 feat(ui): weekend/vacation/checkbox colors follow accent theme
- Unify Saturday+Sunday into single isWeekend flag (header + grid lines)
- Replace hardcoded amber vacation bar/tooltip colors with brand-* classes
- Add global accent-color for checkboxes and radio buttons via CSS variable
- Update VACATION_TIMELINE_COLORS/BORDER to use brand palette (SICK stays red)
- Vacation-only tooltip uses neutral dark surface with brand accent border

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 14:06:44 +02:00
Hartmut 0339b11038 fix(ui): remove utilization row background tint from timeline
Remove the colored background tint for 50-100% utilized rows entirely.
Only over-utilized rows (>100%) keep the red warning tint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:53:21 +02:00
Hartmut 5afc6c8c94 fix(ui): remove blue-shifted hardcoded colors from timeline components
Replace hardcoded blue-shifted rgba values and slate-* classes with neutral
CSS variable references in timeline resource/project panels, tooltips,
constants, and heatmap mono palette. Change utilization row tint from blue
to green. Replace slate-950 open demand backgrounds with --surface-card.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:46:41 +02:00
Hartmut 60d89a1bc8 fix(ui): replace blue-shifted hardcoded gradient in WidgetContainer dark mode
The widget wrapper had a hardcoded dark gradient using rgba(22,23,26) and
rgba(16,17,19) which are blue-shifted. Replace with CSS variable references
--surface-elevated and --surface-card for neutral dark backgrounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:35:44 +02:00
Hartmut 05aa864359 refactor(ui): replace inline INPUT_CLS/LABEL_CLS/BTN_DANGER constants and action link classes with CSS component classes
Remove duplicated Tailwind class string constants from 15 component files.
Use app-input, app-select, app-label, app-action-danger-btn, and
app-action-delete CSS component classes from globals.css instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:21:03 +02:00
Hartmut 9ba49c9ab8 fix(ui): add dark mode variants to dashboard, layout, notification and chargeability components
Add missing dark: class variants for backgrounds, borders, and text across
dashboard widgets, AppShell sidebar, notification cards, and the chargeability
report table. Replace hardcoded slate/gray hex values with CSS variable
references. Fix chargeability hover tint and remove ineffective sticky thead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:20:50 +02:00
Hartmut 9b5cd8549d refactor(ui): replace inline INPUT_CLS/BTN_DANGER/action link constants with component classes
- Replace 13 local INPUT_CLS/SELECT_CLS/LABEL_CLS/BTN_DANGER constants with
  app-input, app-select, app-label, app-action-danger-btn component classes
  (CustomFieldFilterBar, RolePresetsEditor, FieldCard, BlueprintFieldCatalog,
  BlueprintFieldEditor, BlueprintsClient, EstimateWizard, EstimateWorkspace-
  DraftEditor, DemandLineEditor, ScopeItemEditor, AssumptionEditor,
  ProjectWizard, BulkEditModal)
- Replace inline text-blue-600/text-red-500 action link strings with
  app-action-edit / app-action-delete in AllocationsClient, ProjectsClient,
  ScenarioPlanner, ProjectDemandsTable, RolesClient, BlueprintsClient,
  CreateTaskModal, RateCardsClient, UsersClient, ManagementLevelsClient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:02:08 +02:00
Hartmut d1a21a79b2 fix(ui): comprehensive dark-theme hardcoded color pass
Phase 1 — globals.css: add ~45 new dark-mode override rules covering 250+
component instances at once:
- bg-*-50 (red/green/blue/yellow/amber/purple/indigo/orange/brand/emerald)
- border-*-200 (colored alert/badge borders)
- hover:bg-*-50/100 (colored hover states)
- text-amber-700/orange-600/green-600/emerald-700/brand-700 (missing overrides)
- divide-gray-50 (ChargeabilityWidget sticky section dividers)

Phase 2 — targeted component fixes:
- Button.tsx: add dark variants to secondary (bg-gray-800) and ghost variants
- DynamicFieldEditor.tsx: add dark variants to INPUT_NORMAL and INPUT_ERROR constants
- WidgetContainer.tsx: replace slate-900 (blue-tinted) gradient with neutral
  surface-card values (rgb 22,23,26 / 16,17,19)
- status-styles.ts: add explicit dark variants to PROJECT_STATUS_BADGE and
  ORDER_TYPE_BADGE (consistent with other badge maps in same file)

Phase 3 — dashboard widget tables:
- TopValueWidget: dark thead, tbody divider, row hover
- DemandWidget: dark thead, tbody divider, row hover
- ChargeabilityWidget: dark sticky h3 headers (bg-white→surface-card),
  border-gray-100 thead rows, divide-gray-50 tbodys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:37:43 +02:00
Hartmut 13262b5cec refactor(ui): unify dark theme — replace hardcoded hex with CSS variables
- Replace sidebar #0d0e22 hardcoded hex with .sidebar-panel class backed by
  --surface-card CSS variable so all three sidebar elements (desktop, mobile,
  mobile header) share the same neutral-dark color as the main content
- Remove purple logo gradient (dark:from-[#0d0e22] dark:to-[#13162a]) — now uses
  --surface-elevated for a neutral, unified look
- Add .dark slate-*/gray-900 overrides: bg-slate-700/800/900, border-slate-800,
  hover:bg-slate-800 all map to --surface-elevated/--surface-card/--border-subtle
- Remove dead hardcoded rgb(45 51 71) rule for dark bg-gray-100 (was overridden
  further down anyway; now consistently uses --surface-elevated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:25:10 +02:00
Hartmut 1383169352 feat(ui): Aurora design system — glassmorphic dark mode, warm light mode, snappy animations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:10:52 +02:00
Hartmut 43de66e982 feat(api): add audit helpers, tool registry, shared tool manifest types, and UI primitives
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:14:26 +02:00
Hartmut 1a67af6761 fix(dashboard): measure grid container width after hydration completes, not just on mount 2026-04-09 20:58:57 +02:00
Hartmut 5ad1048519 feat(dashboard): expand grid to 16 columns with auto-migration for saved 12-col layouts 2026-04-09 20:50:40 +02:00
Hartmut 446aea5319 feat(dashboard): combine vertical + horizontal compaction for gap-free widget layout 2026-04-09 20:42:49 +02:00
Hartmut 4875096b13 fix(dashboard): restore vertical compaction so widgets auto-align when dragged 2026-04-09 20:19:24 +02:00
Hartmut 36900a1219 Merge branch 'worktree-agent-a7de1005' 2026-04-09 19:31:11 +02:00
Hartmut d7a35b2d7a feat(web): add React error boundaries and Next.js error.tsx fallbacks
Runtime errors in components now show a friendly "Something went wrong"
screen instead of a white page. Timeline and staffing panel are
individually wrapped. Route-level error.tsx handles server component errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 19:16:26 +02:00
Hartmut 6bf60c8e07 feat(web): persist list-page filters in URL search params
Resources, projects, and allocations filter state now syncs to/from
URL so filters survive refresh and can be shared via link.
Text inputs are debounced (300ms) to avoid URL churn.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:00:30 +02:00
Hartmut 7264f0728a perf(timeline): add useCallback/useMemo to timeline components
Prevents redundant re-renders when parent state changes by stabilising
event handler references and memoising expensive derived data in
TimelineView, TimelineResourcePanel, and TimelineProjectPanel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:54:38 +02:00
Hartmut 75167d6129 fix(merge): resolve post-merge type errors from batch-1 agents
- ScenarioPlanner.Baseline.shortCode: string → string | null (matches Prisma)
- ScenarioPlanner.SimulationResult.chargeabilityTarget: number → number | null
- Remove runtime Zod parse from scenario procedures (typed by Prisma already)
- Float64Array index access: add non-null assertions for noUncheckedIndexedAccess

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:38:32 +02:00
Hartmut f3cb75bfc7 feat(mobile): add mobile summary view for 320-428px viewports (Sprint 4c)
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>
2026-04-09 13:37:08 +02:00
Hartmut 607af1a857 feat(bench): add Resource Bench Board page
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>
2026-04-09 13:30:44 +02:00
Hartmut 1df208dbcc feat(timeline): add pulse animation for in-flight drag mutations
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>
2026-04-09 13:28:46 +02:00
Hartmut 7a5e98e2e9 perf(timeline): add horizontal virtualization for allocation bars
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>
2026-04-09 13:26:38 +02:00
Hartmut e75d966b8d feat(timeline): add inline allocation editor on double-click
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>
2026-04-09 13:24:43 +02:00
Hartmut fa54ef4cbd feat(timeline): add keyboard navigation with shortcut overlay
- Arrow left/right scrolls the timeline by 1 day (Shift: 1 week)
- Delete/Backspace deletes selected allocations
- ? toggles a keyboard shortcut overlay
- Floating ? button in bottom-right corner provides persistent access
- (Del) hint added to the FloatingActionBar delete button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 13:20:32 +02:00
Hartmut 05f6eba5d8 refactor(staffing): decompose 735-line StaffingPanel into focused components
Splits StaffingPanel.tsx into:
- StaffingSearchForm: skill tags, dates, hours input, submit button
- ScoringExplanation: the 3-column scoring breakdown card
- StaffingResultCard: individual suggestion card with details and assign form
- StaffingResultsList: list orchestration with loading/empty states
- StaffingPanel: thin orchestrator (~100 lines) managing state and tRPC query

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 13:17:55 +02:00
Hartmut 594ae4f10b feat(allocations): add Excel export button to allocations toolbar
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>
2026-04-09 13:14:58 +02:00
Hartmut 7435fdc125 feat(timeline): Sprint 2b — AI staffing suggestions in DemandPopover
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>
2026-04-09 13:11:36 +02:00
Hartmut 16ce6db07e feat(allocations): Sprint 2a — bulk date shift via BatchActionBar
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>
2026-04-09 13:10:06 +02:00
Hartmut 6831e199c6 feat(ux): Sprint 1 — quick wins: EmptyState, DateRangePresets, debounce, save feedback, scenarios nav
- 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>
2026-04-09 13:08:19 +02:00
Hartmut a16c41e739 fix(dashboard): show skeleton instead of default layout until hydration completes
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>
2026-04-09 10:20:50 +02:00
Hartmut 3e8df09cd8 feat(web): overbooking and vacation conflict warnings in AllocationModal
- 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>
2026-04-09 10:15:37 +02:00
Hartmut 472d87c829 feat(web): add error boundaries, loading skeletons, render fixes and tree-shaking
- Add error.tsx to all 13 route groups: admin, allocations, analytics, dashboard, estimates, notifications, projects, reports, resources, roles, staffing, timeline, vacations
- Add loading.tsx to 9 routes that were missing them: admin, analytics, dashboard, estimates, notifications, reports, roles, staffing, vacations
- ResourceDetail: wrap vacationStart in useMemo to stabilize query key, remove dead windowEnd variable
- node-renderer.ts: replace barrel import (import * as THREE) with named imports for tree-shaking
- next.config.ts: add framer-motion and @capakraken/shared to optimizePackageImports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 08:35:28 +02:00
Hartmut b103e79e92 feat(ux): prevent wizard close on backdrop click
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>
2026-04-07 16:09:22 +02:00
Hartmut 4accee95a4 refactor(dispo): clean up validate UI in NewImportModal
- 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>
2026-04-07 16:06:58 +02:00
Hartmut f89da5d93b fix(ui): use amber-950/30 for pending approvals dark background (not gray-800)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:34:49 +02:00
Hartmut 813b21d1a0 fix(ui): add dark mode styles to amber warning boxes app-wide
Amber alert boxes were missing dark: variants, rendering as muddy
dark-orange in dark mode with near-unreadable text. Fixed in:
- VacationClient (Pending Approvals banner)
- VacationModal (conflict warning)
- ResourceDetail (load error)
- SkillMatrixUpload (replace warning)
- AllocationModal (open demand toggle)
- ProjectWizard (budget bar, post-creation warnings)

Pattern: bg-amber-50 → dark:bg-amber-950/30, border-amber-200 →
dark:border-amber-800, text-amber-7/800 → dark:text-amber-300/400

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:28:35 +02:00
Hartmut 8f464f2150 feat(dispo): add dry-run Validate button to New Import modal
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>
2026-04-06 09:32:23 +02:00
Hartmut bdb55f23d3 fix(insights): suppress AI credentials warning during loading state
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>
2026-04-06 09:05:00 +02:00
Hartmut a9ad1ed8b6 feat(G-08): chapter field uses live datalist from resource.chapters
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>
2026-04-06 08:10:36 +02:00
Hartmut 4a49ec4f05 fix(sanity): resolve 15 gaps from sanity check audit (G-01 through G-15)
- G-01: ProjectWizard renders blueprint fieldDefs with DynamicFieldInput component
- G-02: Blueprint rolePresets validated via RolePresetsSchema in wizard; API keeps loose schema
- G-03: ProjectWizard step 2/3 validation (role, hoursPerDay, headcount required)
- G-04: EstimateWizard validates baseCurrency and demand line cost rates
- G-05: Project lifecycle transition guards with ALLOWED_TRANSITIONS map
- G-06: Blueprint validator extended for minLength/maxLength/pattern and DATE range checks
- G-07: assertBlueprintDynamicFields merges global blueprint fieldDefs into validation
- G-08: (tracked — chapter managed dropdown; deferred to backend ticket)
- G-09: JSDoc added to lcrCents/ucrCents clarifying LCR/UCR terminology
- G-10: Dispo route redirect already in place — closed as done
- G-11: packages/ui empty by design — closed as documented
- G-12: @deprecated JSDoc added to CreateAllocationSchema and UpdateAllocationSchema
- G-13: ProjectWizard review step enhanced with blueprint name, field values, skills, assignments
- G-14: ProjectWizard handleSubmit collects per-item warnings instead of silent swallowing
- G-15: Vacation cancel reverses usedDays entitlement for APPROVED ANNUAL/OTHER vacations

Tests: all 1575 passing (1 pre-existing failure in insights-summary unrelated to these changes)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 00:11:12 +02:00
Hartmut f5e41f7efe fix(ux): improve empty-state messaging for tickets #49 and #57
#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>
2026-04-03 17:51:14 +02:00
Hartmut 9241f22993 fix(ux): resolve tickets #58 #60 — wizard review labels and assignment CTA clarity
#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>
2026-04-03 16:17:16 +02:00
Hartmut 2da29c8191 fix(ux): resolve tickets #59 #66 #67 — project feedback and demand summary
#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>
2026-04-03 16:05:29 +02:00
Hartmut 65db330a4d fix(ux): resolve tickets #55 #56 — resource modal stability and success feedback
#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>
2026-04-03 15:43:12 +02:00