Commit Graph

262 Commits

Author SHA1 Message Date
schalli f4ece4890d fix(web): redirect from server action after password change
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 40s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m19s
client-side router.push races with Set-Cookie processing. redirect() in the
server action sends cookie + redirect in one response — browser applies the
new JWT before navigating, so middleware sees mustChangePassword=false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:20:14 +02:00
schalli 195354cd7a debug(web): log set-cookie header value from API change-password response
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 38s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m18s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:12:45 +02:00
schalli f96a0db769 fix(web): forward new session cookie after password change
Tessera CI/CD / Lint & Type Check (push) Successful in 39s
Tessera CI/CD / Tests (push) Successful in 35s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m21s
After changePassword the API issues a new JWT with mustChangePassword=false.
The server action now reads Set-Cookie from the API response and sets it
in the browser so the middleware sees the updated flag and allows /dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 16:47:55 +02:00
schalli c8210a2abc debug(web): add logging to changePasswordAction to diagnose Verbindungsfehler
Tessera CI/CD / Lint & Type Check (push) Successful in 42s
Tessera CI/CD / Tests (push) Successful in 36s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m22s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 16:38:48 +02:00
schalli 46ba8868c7 fix(web): change password via server action instead of client-side fetch
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Successful in 36s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m23s
Client-side fetch to NEXT_PUBLIC_API_URL was unreachable in production.
Replace with a server action that uses API_INTERNAL_URL (http://api:3001)
server-to-server — no browser connectivity required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 15:54:29 +02:00
schalli 6020021370 fix(web): use http://api:3001 as default rewrite destination
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Successful in 37s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m24s
next.config rewrites() runs at build time — API_INTERNAL_URL is not set
in CI, so the previous localhost:3001 fallback was baked into the bundle.
Default to http://api:3001 which is always correct in Docker network.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 15:42:44 +02:00
schalli c42ab5dc6f fix(web): proxy client API calls through Next.js to fix production connectivity
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 36s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m33s
NEXT_PUBLIC_API_URL was undefined at build time, causing client bundles to
fall back to http://localhost:3001 — unreachable from the browser in prod.

- Add /api-proxy rewrite in next.config.ts (forwards to API_INTERNAL_URL at runtime)
- Bake NEXT_PUBLIC_API_URL=/api-proxy at build time in Dockerfile
- Fix api.ts to prefer API_INTERNAL_URL for server-side calls
- Fix docker-compose.prod.yml: set NEXT_PUBLIC_API_URL=http://api:3001 for runtime server-side code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 15:36:06 +02:00
schalli 28d4fa5c82 fix(ci): fix sidebar tests + add registry publish + prod compose
Tessera CI/CD / Lint & Type Check (push) Successful in 46s
Tessera CI/CD / Tests (push) Successful in 38s
Tessera CI/CD / Build & Publish Images (push) Failing after 8m32s
- sidebar.test: expand category before asserting on module names
  (categories are collapsed by default since UI-Umbau)
- ci.yml: replace build-deploy with publish job that pushes images
  to git.vicolab.de container registry
- docker-compose.prod.yml: pull-only compose for server deployments
  using registry images

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:14:34 +02:00
schalli 3b2a36cd61 feat(07): add CSV format help text and Exchange 401 diagnostic hints
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
- CSV import dialog shows format line + example before mode selection
- Exchange connection test: on 401, inline hint lists common causes
  (wrong credentials, domain format, username prefix, O365 not supported)
- Both de/en translations updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 09:42:08 +02:00
schalli 9b453e3ed3 fix(07): propagate Exchange connection error, add folder selection for Exchange
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
- testConnection now returns { success, message? } instead of boolean so
  admins see the actual EWS/IMAP error in the UI rather than "Unbekannter Fehler"
- Exchange provider: resolveFolder() maps folder string to WellKnownFolderName
  (Inbox, SentItems, DeletedItems, Drafts, JunkEmail + German aliases)
- InboxConfigForm: folder field now shown for both IMAP and Exchange protocols
  with Exchange-specific help text listing valid well-known names
- Controller returns testConnection result directly (no more redundant wrapping)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 09:33:13 +02:00
schalli 01ff137f43 fix(07): UAT fixes — SMTP test email, DKV routing, Exchange domain field
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
- SMTP: add test-to field, send real email via sendMail() instead of verify()
- SMTP: fix no_auth warning shown as error for open-relay servers
- DKV: register dkv-fleet in MODULE_REGISTRY (fixes "Modul nicht gefunden")
- DKV: add dynamic [category]/[moduleSlug]/settings + vehicles sub-routes
- DKV: settings/vehicles links use useParams for consistent URLs
- DKV: add gear icon settings link to module main page
- DKV: rename module to "DKV-Rechnung" in seed + translations
- DKV: add optional domain field for Exchange (WebCredentials 3rd arg)
- DKV: hide IMAP-only fields (Port/Verschlüsselung/Ordner) when Exchange selected
- DKV: hide Exchange-only field (Domain) when IMAP selected
- Prisma: add domain column to DkvModuleConfig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 21:58:26 +02:00
schalli 4de87a8ea2 fix(07): SMTP test warns when connection passes but no auth configured
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Server reachable without credentials (port 25 open relay) returns
{ success: true, warning: 'no_auth' } instead of green success.
Frontend shows red warning: server reachable but emails will fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 01:29:53 +02:00
schalli 338655c57b fix(07): WR-03 replace dead try/catch in formatDateTime with isNaN guard
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
new Date() never throws so the catch was unreachable. Invalid dates rendered
as NaN.NaN.NaN, NaN:NaN Uhr. Use isNaN(d.getTime()) guard to fall back to
the raw string instead.
2026-06-27 17:21:51 +02:00
schalli f3f610f9e1 fix(07): CR-01 rename import return field imported→count to match frontend
Tessera CI/CD / Lint & Type Check (push) Failing after 37s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Deploy (push) Has been skipped
Backend dkv.service.ts returned { imported } but frontend read result.count,
causing the success toast to always display "undefined Fahrzeuge importiert".
Align backend field name to count and update the dkv-api.ts return type to
include mode for completeness.
2026-06-27 17:19:44 +02:00
schalli 75aec38dae feat(07-06): extend SettingsSidebar with Allgemein > SMTP category
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
- Add 'Allgemein' category section ABOVE existing Dashboard category
- Single SMTP link to /settings/general/smtp with identical active/inactive styling and aria-current
- isActive('/settings/general/smtp') works via pathname.startsWith branch
- Existing Dashboard/Widgets/Calendar items unchanged
2026-06-27 17:04:25 +02:00
schalli fd2dda69cb feat(07-06): settings-api client + SmtpSettingsForm + SMTP page (DKV-05)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 41s
Tessera CI/CD / Tests (push) Waiting to run
- settings-api.ts: fetchSmtp/saveSmtp/testSmtp with credentials:'include'; SmtpConfig exposes only hasPassword (T-07-17)
- smtp-settings-form.tsx: Surface C form with show/hide password toggle, test-feedback auto-clears 6s
- settings/general/smtp/page.tsx: SmtpSettingsPage heading + SmtpSettingsForm
- de.json + en.json: add smtp.showPassword/hidePassword/testTesting/testSuccess/testFailed keys
- TDD GREEN: 5/5 tests pass; aria-hidden* on required markers removed for correct getByLabelText matching
2026-06-27 17:03:57 +02:00
schalli a96d79ad52 test(07-06): add failing SMTP settings form tests (RED — DKV-05)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 38s
Tessera CI/CD / Tests (push) Waiting to run
- 5 Vitest tests for SmtpSettingsForm: load config, save, test success/failure, password toggle
- Mocks @/lib/settings-api (fetchSmtp/saveSmtp/testSmtp) and next-intl
- Mirrors calendar-settings.test.tsx mocking pattern
- RED: SmtpSettingsForm does not yet exist
2026-06-27 17:00:27 +02:00
schalli 4d81b8b2d5 feat(07-05): VehicleTable + CsvImportButton + vehicles page — GREEN (DKV-03)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 42s
Tessera CI/CD / Tests (push) Waiting to run
- VehicleTable: list, inline edit, create row, delete with confirm dialog
- All 4 UI-SPEC aria-labels on icon-only buttons (Rule 2: accessibility correctness)
- CsvImportButton: merge/replace modes, destructive confirm, importVehiclesCsv
- vehicles/page.tsx: heading + back link to settings + VehicleTable
- VehicleTable.test.tsx: fix mock sequencing bug (mockResolvedValueOnce for init load)
- All 5 Vitest tests pass (GREEN)
2026-06-27 00:41:03 +02:00
schalli c739d2788b test(07-05): add failing VehicleTable tests (RED — DKV-03)
Tessera CI/CD / Lint & Type Check (push) Failing after 39s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Deploy (push) Has been skipped
- Covers: render list, empty state, delete with confirm, inline edit + save
- Mocks @/lib/dkv-api + next-intl (mirrors calendar-settings.test.tsx pattern)
- Verifies all 4 UI-SPEC aria-labels on icon-only action buttons
2026-06-27 00:38:38 +02:00
schalli 45e9a3591f feat(07-05): settings page + InboxConfigForm — Surface B inbox tab (DKV-01)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 37s
Tessera CI/CD / Tests (push) Waiting to run
- settings/page.tsx: tab bar (Posteingang / Fahrzeuge), Vehicles tab links to /vehicles
- InboxConfigForm: all UI-SPEC Tab-1 fields in order, show/hide password (T-07-12)
- Active toggle: role=switch, pill shape, primary/muted colors
- testConnection + saveConfig wired; inline green/destructive feedback
- Password never pre-filled from server (hasPassword only) per T-07-12
2026-06-27 00:37:50 +02:00
schalli 2fe7bca6e8 feat(07-05): dkv-api client + Surface A — history table + export list + check-now
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Waiting to run
- dkv-api.ts: typed fetch client for all /dkv/* routes (credentials: include)
- StatusBadge: OKLCH inline styles per status (T-07-14: React text nodes only)
- InvoiceHistoryTable: 6-column table, 5 skeleton rows, pagination >25, refreshKey
- ExportFileList: up to 10 unique export filenames derived from history (T-07-13)
- page.tsx: Jetzt prüfen trigger, error banner, refreshKey lift, DKV-04/DKV-01
2026-06-27 00:36:28 +02:00
schalli 2a3d1c1e85 feat(07-04): DkvModule + registry seed + AppModule registration + i18n keys
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
- dkv.seed.ts: seedDkvModule with slug=dkv-fleet, category=fleet, isSystem=true
- dkv.module.ts: imports ModuleRegistryModule + CalendarModule (CalendarCryptoService)
  provides all 7 DKV services + 2 providers + controller; OnModuleInit seeds registry
- app.module.ts: DkvModule added to imports (ScheduleModule + SettingsModule from Plans 01/03)
- de.json + en.json: complete dkvFleet namespace (50+ keys incl. status, col, form, errors)
- settings namespace extended with categoryGeneral, categorySmtp, smtp sub-object
2026-06-27 00:30:08 +02:00
schalli ba02b25cba refactor(ui): restructure sidebar and admin navigation
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Failing after 45s
Tessera CI/CD / Build & Deploy (push) Has been skipped
- Sidebar: remove footer (user info, settings, locale switcher), remove admin section, make category headers static with per-category collapse toggle
- Header dropdown: replace inline admin links with single "Administrator" entry
- Admin section: dedicated layout with AdminSidebar (mirrors Settings pattern) at /admin/*
- Disable self-delete button in user management (backend already rejects with 403)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 08:34:19 +02:00
schalli 9f78580606 feat(domaincheck): support all TLDs with suggestion alternatives
Tessera CI/CD / Lint & Type Check (push) Successful in 1m8s
Tessera CI/CD / Tests (push) Successful in 1m13s
Tessera CI/CD / Build & Deploy (push) Successful in 2m39s
- Accept full domains (e.g. "example.xyz") not just labels
- Check the entered TLD as primary result
- Show .de, .com, .net, .org as alternative suggestions below
- Primary result highlighted with accent border
- Input without TLD still works (shows all 4 suggestions)
- Updated i18n placeholders and added "suggestions" label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:54:30 +02:00
schalli 4d94a254fd fix(06-03): add .gitkeep to apps/web/public for Docker build
Tessera CI/CD / Lint & Type Check (push) Successful in 45s
Tessera CI/CD / Tests (push) Successful in 47s
Tessera CI/CD / Build & Deploy (push) Successful in 1m58s
Git doesn't track empty directories, so apps/web/public is missing
in CI checkout. The web Dockerfile COPY --from=builder fails when
public dir doesn't exist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 13:19:33 +02:00
schalli 184370b759 fix(05): convert flat i18n widget keys to nested structure for next-intl
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 22:04:16 +02:00
schalli 24de136b9b feat(05-04): calendar settings page with source management and visibility toggle
- calendar-settings-panel.tsx: source list with color dots, type badges, visibility toggle (CAL-02), edit/delete actions, connection test auto-run, delete confirmation dialog
- calendar-source-form.tsx: add/edit form with name/type/URL/credentials/color; Exchange-mode select for exchange type; username/password hidden for ICS; client-side https-only validation (T-05-14)
- settings/dashboard/calendar/page.tsx: route page rendering CalendarSettingsPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:26:06 +02:00
schalli f99ff9a498 test(05-04): add failing tests for calendar settings panel
- Three test cases: source list with name/type/visibility, visibility toggle calls updateSource, form validation
- Mocks calendar-api functions following existing test patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:24:09 +02:00
schalli c0f2f4ca51 feat(05-04): calendar API client, calendar widget, registry wiring
- calendar-api.ts: fetchSources/addSource/updateSource/deleteSource/testSource/fetchEvents with credentials:'include'
- calendar-widget.tsx: upcoming-events list with source color dots, three empty states (no sources/no events/loading)
- widget-registry.tsx: wireCalendarWidget() replaces placeholder with real CalendarWidget
- page.tsx: wires CalendarWidget into registry on mount
- i18n: added calendar.loading key to de.json and en.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:23:27 +02:00
schalli 2d8699e45c test(05-04): add failing tests for calendar widget
- Three test cases: event rendering with color dots, no-events empty state, no-sources empty state
- Mocks calendar-api and next-intl following existing test patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:21:37 +02:00
schalli 7e2592b2af feat(05-02): add widget settings panel with search provider form
- Settings > Dashboard page with per-widget-instance config
- Clock config: timezone select (IANA list) + date toggle (D-12/D-13)
- Note config: editable title field (D-17)
- Search config: SearchProviderForm with add/delete and {query} validation (D-15)
- Calendar config: link to calendar-specific settings
- i18n keys for search provider management (en + de)
- Fix useRef initialization for React 19 strict mode (note-widget)
- Fix unknown type narrowing in widget title display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 14:58:42 +02:00
schalli dd0209898b feat(05-02): add search and note widgets with tests
- SearchWidget: provider dropdown, text input, button; opens search in new tab via window.open (D-14/D-15)
- NoteWidget: MDEditor with compact toolbar, debounced autosave (1500ms), AbortController for in-flight cancellation (D-16/D-17/D-18)
- rehype-sanitize enabled for Markdown XSS prevention (T-05-05)
- Widget registry updated with wireSearchWidget/wireNoteWidget (no more placeholders)
- dashboard-api.ts: added fetchSearchProviders, addSearchProvider, removeSearchProvider, signal support on updateWidgetConfig
- 9 new tests passing (search: 5, note: 4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:33:01 +02:00
schalli 97c13809f5 test(05-02): add failing tests for search and note widgets
- SearchWidget: provider selection, window.open with encoded query, Enter key trigger
- NoteWidget: debounced autosave, rapid typing collapse, AbortController usage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:30:45 +02:00
schalli 8a9c1ae6a9 feat(05-01): settings shell, header link, and i18n keys for dashboard phase
- Create settings/layout.tsx with SettingsSidebar and back-to-dashboard link
- Create settings/page.tsx with redirect to /settings/dashboard
- Create settings-sidebar.tsx with Widgets and Calendar nav items, aria-current
- Add Settings link in header user dropdown (gear icon, before logout)
- Add settings namespace (DE+EN) with all category and action keys
- Add widgets namespace (DE+EN) with all widget names, descriptions, error states
- Add header.settings key ("Einstellungen"/"Settings")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:23:47 +02:00
schalli d5e1c42e64 feat(05-01): dashboard grid, clock widget, widget registry, store, and tests
- Install react-grid-layout@2.2.3 and react-resizable
- Create widget-registry.tsx with all 4 widget types, WIDGET_CONSTRAINTS, WidgetProps
- Create dashboard-api.ts with fetch/save layout and widget CRUD functions
- Create dashboard-store.ts (Zustand, NO persist — D-05) with edit mode and auto-save on exit
- Create DashboardGrid with react-grid-layout v2 Responsive, ResizeObserver width
- Create ClockWidget using Intl.DateTimeFormat (no manual UTC offsets)
- Create WidgetWrapper with drag handle and delete button in edit mode
- Create EditModeToggle (pencil/checkmark), WidgetCatalogModal (2x2 grid)
- Rewrite portal page.tsx as dashboard with grid, edit toggle, widget catalog
- Add ResizeObserver polyfill in test setup, CSS mock support in vitest config
- All 5 tests green (dashboard grid + clock widget)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:21:43 +02:00
schalli 792b7661d8 test(05-01): add failing tests for dashboard grid and clock widget
- DashboardGrid test: empty state, widget rendering, edit mode affordances
- ClockWidget test: timezone rendering, showDate toggle behavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:15:18 +02:00
schalli e967ea9660 feat(04-03): migrate sidebar to Link/usePathname, add search and refresh signal
Replace all raw <a> with Next.js Link. Add usePathname-based active
highlighting, SidebarSearch with category/module filtering, and
sidebarRefreshKey subscription for live activation updates. 26 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 10:21:27 +02:00
schalli fdf2c38f67 feat(04-02): add module detail page at /marketplace/[slug]
Compact detail view with back link, full description (no line-clamp),
status indicator, and activation controls. Reuses ActivationDialog for
deactivation. 3 tests pass, 21 total green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 09:25:00 +02:00
schalli 0f3379f263 feat(04-02): add TenantContextSelector, ActivationDialog, and asymmetric toggle UX
Super-Admin tenant dropdown fetches /tenants, sets selectedTenantId for
per-tenant activation. Deactivation gated by confirmation dialog;
activation immediate with toast. 18 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 09:23:04 +02:00
schalli 06fb52da82 feat(04-02): add search, status tabs, category chips, toast, and client-side filtering
Build MarketplaceSearch (debounced 300ms), StatusFilter (3 tabs with
counts), CategoryFilter (horizontal chip row), and Toast (Zustand store
+ container). Wire useMemo filtering into page with filtered-empty state.
All 14 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 09:20:08 +02:00
schalli fb6a340799 feat(04-01): implement marketplace page and marketplace-store
Create marketplace-store (Zustand) with sidebarRefreshKey signal and
tenant context. Build /marketplace page with parallel fetch, activation
Map, role gate, empty state, and responsive card grid. All 9 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:51:54 +02:00
schalli 0b7cf66abb test(04-01): add failing tests for marketplace page
- 4 tests: card grid rendering, activation status display,
  access-denied for non-admin, empty state
- Tests fail as expected (RED phase) - page and store not yet implemented
2026-06-22 14:48:39 +02:00
schalli 1e494b36ea feat(04-01): implement MarketplaceCard component and marketplace i18n namespace
- Add marketplace namespace to de.json and en.json with all copywriting contract strings
- Add sidebar.search and sidebar.noResults i18n keys
- Create MarketplaceCard with icon, name, localized description, category badge,
  status badge (role="status"), and activate/deactivate button
- All 5 unit tests pass (GREEN phase)
2026-06-22 14:47:53 +02:00
schalli a64f889251 test(04-01): add failing tests for MarketplaceCard component
- 5 tests: name+description rendering, category badge, activate button,
  activated status badge, onToggle callback
- Tests fail as expected (RED phase) - component not yet implemented
2026-06-22 14:46:30 +02:00
schalli e50b3c76c5 chore(04-01): install and configure Vitest test infrastructure for apps/web
- Add vitest, @testing-library/react, @testing-library/jest-dom, jsdom, @vitejs/plugin-react as dev deps
- Create vitest.config.ts with jsdom environment and @ path alias
- Create test setup file importing jest-dom/vitest matchers
- Add test scripts to apps/web and root package.json
- Add test task to turbo.json pipeline
2026-06-22 14:45:54 +02:00
schalli 576e311262 fix(03): strip TLD from domaincheck input before sending to API
Users naturally type full domains (e.g. "google.de") but the API
validates DNS labels only. Extract the label part before the first
dot to prevent 400 validation errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 09:37:39 +02:00
schalli 46a6e277b8 fix(03): login redirect + API internal URL for Docker networking
- Use window.location.href for full page reload after login (ensures auth state)
- Add API_INTERNAL_URL for server-side requests within Docker network
- Remove unnecessary credentials:'include' from SSR fetch calls
- Update planning state for Phase 03 progress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 09:28:05 +02:00
schalli 9b2b1eafcb fix(03): show actual error message in domaincheck page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:43:27 +02:00
schalli b8ef870d1a fix(03): resolve tenant context for module activation
Controller now falls back to user.tenantId from JWT when
req.tenantId is null (SUPER_ADMIN without x-tenant-id header).
Also added error display to admin modules page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:38:37 +02:00
schalli dad9a779df fix(03): add module settings page, dynamic sidebar categories
- Admin modules page at /admin/modules with toggle switches
- Sidebar categories now fetch active modules from API dynamically
- Added "Module" link under admin section in sidebar
- Added i18n keys for module management (DE + EN)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:00:50 +02:00