Commit Graph

278 Commits

Author SHA1 Message Date
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 1a2f7de5bd fix(ui): replace hardcoded purple values with accent-adaptive CSS variables
- Dark surface vars changed from purple-navy (8 8 22) to neutral near-black (10 10 12)
  so sky/emerald/amber accent themes no longer have a purple cast
- Light surface vars made nearly neutral (252 252 253) — lavender tint removed
- All rgba(100, 80, 160, ...) replaced with rgb(var(--accent-400) / ...) in
  .app-surface, .app-surface-strong, and .app-toolbar shadows/borders
- .app-page-title dark gradient midpoint changed from hardcoded #e8e4ff to
  rgb(var(--accent-100)) so it adapts to the chosen accent color
- Body light-mode background gradient opacity reduced to avoid over-tinting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:17:50 +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 97cfd0ed90 fix(security): raise password minimum to 12 chars, hide raw error messages, add audit script
- Password validation: min(8) → min(12) across auth.ts, user-procedure-support.ts,
  and invite.ts (aligns with NIST SP 800-63B modern recommendations)
- Error boundary: stop rendering raw error.message which could leak internal
  details; always show the generic fallback text
- Add `pnpm audit` script (--audit-level=high) for dependency vulnerability scanning

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:48:51 +02:00
Hartmut afabaa0b7a fix(security): prevent TOTP replay attacks and fix user enumeration in verifyTotp
Adds lastTotpAt timestamp to User model. After a successful TOTP validation,
the timestamp is recorded. Any reuse of the same code within the 30-second
window is rejected as a replay attack.

verifyTotp now returns a single generic UNAUTHORIZED error regardless of
whether the user ID is invalid or TOTP is not enabled, preventing enumeration
of user IDs and MFA status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:41:09 +02:00
Hartmut 1833182e90 fix(security): harden input validation schemas and fix SSR sanitize bypass
- blueprint rolePresets: cap array at 100 items to prevent storage abuse
- notification CreateManagedNotification: add .max() on title (500),
  body (2000), type (100), entityType/entityId (200), link (1000),
  taskAction (200)
- settings: add .max() on all string config fields; add regex allowlist
  (/^[a-zA-Z0-9._-]+$/) on model name fields (geminiModel,
  azureDalleDeployment, azureOpenAiDeployment) to prevent path manipulation
- sanitizeHtml: fix SSR bypass — server-side branch now strips HTML tags
  instead of returning the raw string unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:38:16 +02:00
Hartmut 9e31c6d972 fix(security): harden cron and API route authentication
- public-holidays cron: replace fail-open inline auth check with verifyCronSecret
  (was open to unauthenticated access when CRON_SECRET unset)
- /api/perf: replace timing-unsafe string comparison with verifyCronSecret
- /api/health: strip baseUrl and latency fields from response to avoid
  leaking infrastructure details (NEXTAUTH_URL config, internal timings)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:38:02 +02:00
Hartmut ebeb180f3f fix(ui): make project detail and scenario pages full width
Replace max-w-5xl/max-w-7xl constrained wrappers with the app-page utility
class, consistent with other full-width pages like the projects list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:14:42 +02:00
Hartmut 5cc177ccf9 fix(dashboard): prevent stale DB data from overwriting newer localStorage layout
Two related fixes:
1. When localStorage has a saved layout, mark it as authoritative so a DB
   response carrying older data (e.g. from a save cancelled by navigating away
   within the 2-second debounce window) cannot overwrite it.
2. Flush any pending debounced DB save immediately on component unmount so
   that navigating away within the window doesn't silently lose changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:14:36 +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 6c7245db93 test(e2e): add Playwright tests for core workflows
Covers auth redirect, resource/project list filtering, timeline render,
sidebar navigation, and staffing panel. Gives deploy confidence for
the main happy paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 19:30:43 +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 485e220c49 fix(api,web): env startup validation, QueryClient defaults, warn on missing REDIS_URL
- Throw at startup in production if REDIS_URL/DATABASE_URL/NEXTAUTH_SECRET missing
- Warn in development when REDIS_URL falls back to localhost
- QueryClient: add gcTime, disable refetchOnWindowFocus, skip retry on 4xx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:42:34 +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 ab4ec91e02 feat(digest): add weekly capacity digest email cron
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>
2026-04-09 13:33:12 +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 444fa70a19 fix(ui): remove !important amber-50 dark override from globals.css
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>
2026-04-06 10:39:34 +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 fba65387fe feat(resources): add hard-delete action to resource list (per-row and batch)
- 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>
2026-04-03 23:18:30 +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 bc0bb5bdb8 test: add coverage for gitlooper ticket sweep
- useFocusTrap.test.ts: 7 unit tests covering rAF-deferred focus,
  Tab/Shift+Tab wrapping, empty-list guard, and cleanup (#56)
- nextConfig.test.ts: 3 tests for /login and /blueprints redirects (#51 #52);
  5 tests for COMPLETED demand exclusion filter logic (#66)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-03 17:33:27 +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