The shoring indicator logic was backwards. In the business context,
higher offshore = more cost-efficient = GOOD.
Inverted logic:
- Green: offshore >= threshold (target met, e.g. >= 55%)
- Yellow: offshore close to threshold (threshold-10 to threshold)
- Red: offshore below threshold (too little offshore, too expensive)
Updated:
- ShoringIndicator: getSeverity() inverted, badge text updated
- ProjectModal: "Max Offshore" renamed to "Min Offshore" with new tooltip
- AI Tool: status text reflects "target met" vs "below target"
- Tool description: "higher offshore is better, threshold is minimum"
Co-Authored-By: claude-flow <ruv@ruv.net>
Project names in the health widget now link to /projects/[id] detail page.
Hover: brand color transition for visual feedback.
Co-Authored-By: claude-flow <ruv@ruv.net>
The static import of @sentry/nextjs at module level triggered worker
thread creation even when withSentryConfig was only called in production.
This caused recurring "Cannot find module vendor-chunks/lib/worker.js"
crashes that killed the dev server mid-request.
Fix: replaced static import with dynamic require() inside a
NODE_ENV === "production" block. In dev mode, the Sentry module
is never loaded at all.
Co-Authored-By: claude-flow <ruv@ruv.net>
Widget: added "Shoring" column with ShoringBadge per project showing
offshore % with color indicator (green/yellow/red).
Backend: added id field to ProjectHealthRow for badge queries.
Database: assigned diverse countries to 11 resources for realistic
shoring data (25 DE, 5 ES, 4 IN, 2 US instead of all-DE).
Co-Authored-By: claude-flow <ruv@ruv.net>
The Project View used its own buildProjectRowGridBackground() which
rendered CSS gradients with hardcoded rgba colors (no dark mode).
The Resource View used shared gridLines from useTimelineLayout which
renders React div elements with proper dark: Tailwind classes.
Fix: replaced the CSS gradient approach with the shared gridLines
in both resource rows and open demand rows within the Project View.
Removed the now-unused buildProjectRowGridBackground function (~40 LOC).
Both views now use identical grid lines with:
- Brand-colored today marker
- Amber weekend highlights
- Proper dark mode colors via Tailwind classes
Co-Authored-By: claude-flow <ruv@ruv.net>
resource.list returns { resources: [...], total } but ScenarioPlanner
expected a flat array. Fixed by extracting .resources from the response.
Co-Authored-By: claude-flow <ruv@ruv.net>
The withSentryConfig() wrapper caused recurring worker.js crashes
in Next.js dev mode (vendor-chunks/lib/worker.js MODULE_NOT_FOUND).
This crashed the server mid-request during image generation and
other long-running operations.
Fix: only apply withSentryConfig in production. In dev mode, use
the raw Next.js config. Sentry instrumentation also gated to
production only.
Co-Authored-By: claude-flow <ruv@ruv.net>
Corrected model names (per Google AI docs):
- gemini-2.5-flash-image (was gemini-2.0-flash-preview-image-generation)
- gemini-3-pro-image-preview (Nano Banana Pro)
- gemini-3.1-flash-image-preview (Nano Banana 2)
UI: replaced text input with dropdown selector showing all 3 models
with human-readable descriptions.
Default changed to gemini-2.5-flash-image (fast, high-volume).
AI Assistant: generate_project_cover tool description updated to be
provider-agnostic (works with both DALL-E and Gemini).
Co-Authored-By: claude-flow <ruv@ruv.net>
API: new testGeminiConnection adminProcedure
- Generates a simple test image via Gemini API
- Returns { ok, model } on success, { ok: false, error } on failure
- Audit logged: "Gemini test succeeded/failed"
UI: "Test Gemini" button next to "Save Image Settings"
- Only visible when Gemini provider is selected
- Shows green success or red error result below the buttons
- Displays the model name on success
Model: gemini-2.0-flash-preview-image-generation (correct name)
Co-Authored-By: claude-flow <ruv@ruv.net>
Schema:
- SystemSettings: geminiApiKey, geminiModel, imageProvider fields
- imageProvider: "dalle" (default) or "gemini"
Gemini Client (packages/api/src/gemini-client.ts):
- Direct HTTP call to Gemini REST API with responseModalities: [TEXT, IMAGE]
- Returns base64 data URL
- Error parsing with user-friendly messages
Router (project.ts):
- generateCover: routes to DALL-E or Gemini based on imageProvider setting
- New isImageGenConfigured query returning { configured, provider }
Admin UI (SystemSettingsClient.tsx):
- "Image Generation" section with provider radio buttons (DALL-E / Gemini)
- Conditional fields: DALL-E config or Gemini API key + model
- Separate save button for image settings
Security:
- geminiApiKey sanitized in audit logs (SENSITIVE_FIELDS)
- API key stored server-side only, never sent to client
Co-Authored-By: claude-flow <ruv@ruv.net>
Both import pages are now grouped under the ACN-Orga collapsible
section in the admin sidebar instead of being separate top-level items.
ACN-Orga submenu now contains:
- Countries
- Org Units
- Util. Categories
- Mgmt Levels
- Dispo Import (moved)
- Skill Import (moved)
Co-Authored-By: claude-flow <ruv@ruv.net>
API: new updateName adminProcedure in user router
- Input: userId + name (min 1, max 200 chars)
- Argon2 not involved (name only, not password)
- Audit log: "Changed name from X to Y"
UI: "Display Name" editable section in user edit modal
- Shows current name with "Edit" link
- Click Edit: inline input with Save/Cancel + Enter/Escape
- Auto-focuses input, saves on Enter
- Invalidates user list on success
Co-Authored-By: claude-flow <ruv@ruv.net>
React Rules of Hooks violation: filteredTop and filteredWatch useMemo
hooks were placed AFTER the isLoading early return, causing "Rendered
more hooks than during the previous render" error that crashed the
entire dashboard.
Fix: moved rawTop/rawWatch/month declarations and filteredTop/filteredWatch
useMemo hooks BEFORE the isLoading early return so hooks always run
in the same order.
Verified in Chrome: dashboard loads with zero console errors.
Co-Authored-By: claude-flow <ruv@ruv.net>