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.
- 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
- DkvSchedulerService: SchedulerRegistry.addCronJob (dynamic interval, not static @Cron)
- onModuleInit loads first active config (v1 single-tenant, documented in SUMMARY)
- setInterval() replaces existing job and registers new one with */ cron expression
- stopJob() removes job when config.isActive=false
- cron package resolved via require() workaround (pnpm strict isolation: transitive dep)
- DkvController: 12 handlers all carrying @Roles(Role.ADMIN, Role.SUPER_ADMIN)
- Routes: GET/PUT config, POST check-now, POST test-connection, GET history,
GET exports/:filename, GET/POST/PUT/DELETE vehicles, POST vehicles/import
- vehicles/import uses FileInterceptor('file') for CSV multipart upload
- exports/:filename streams file as attachment; traversal guard in DkvService
- Controller coordinates scheduler after PUT /dkv/config (no circular dep)
- createTransport() called per-send from DB SmtpConfig (Pitfall 3 mitigation — not at startup)
- Injects SettingsService to load decrypted SMTP config per tenant
- secure/requireTLS mapped from encryption field (ssl-tls / starttls / none)
- Auth omitted when username absent (anonymous relay support)
- Attachment contentType: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- Error path rethrows after generic log (T-07-10) so DkvService can run 3-retry backoff (D-16)
- Does not import @nestjs-modules/mailer abstractions
- buildExcelBuffer: 5-column xlsx per D-13 (Lieferdatum as string, never Date), SheetJS aoa_to_sheet
- resolveFahrzeug: replaces {Marke}/{Modell}/{Kennzeichen}/{Fahrer} tokens in format string (D-19)
- writeAndPrune: server-side filename DKV_YYYY-MM_<nr>.xlsx (T-07-09 path-traversal prevention),
writes to user-files/ (resolved from monorepo root, not request input), prunes to last 10 DKV_*.xlsx
files sorted by mtime ascending (D-15, Pitfall 7 atomicity)
- dkv-parser.validate.ts: empirical validation script against user-files/invoice.pdf
- 27 vehicle blocks, 66 transactions extracted (matches expected count)
- Handles two PDF extraction formats: single-tx (tab-separated) + multi-tx (columnar)
- German number parsing: replace(/\./g,'').replace(',','.') applied to km and menge
- Exits 1 with full raw text dump if zero vehicle blocks parsed (assertion guard)
- dkv-parser.service.ts: @Injectable() NestJS service wrapping validated logic
- parsePdf(buffer: Buffer): Promise<DkvVehicleBlock[]>
- Uses pdf-parse v2 class API: new PDFParse({data:buffer}) — NOT v1 pdfParse()
- Calls destroy() after extraction (T-07-01 memory safety)
- Generic error messages only on parse failure (T-07-02 info disclosure)
Regex adjustment vs Research Pattern 4: space-based regex replaced with
tab-split (single-tx) + columnar transpose (multi-tx) after empirical analysis
of actual invoice.pdf text extraction output.
- 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>
- Add GET /health/version public endpoint to API
- Extend Tauri with 4 plugins: notification, autostart, window-state, store
- Implement close-to-tray with prevent_close + prevent_exit (Pitfall 2)
- Tray menu with Oeffnen/Beenden (German labels)
- Async startup version check against server /health/version
- Generate Tessera-branded icons (yellow T on dark bg, OKLCH palette)
- Update capabilities for notification, autostart, window-state permissions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
In Docker deps stage only package.json files are copied (no schema),
causing prisma generate to fail. Builder stage already runs explicit
prisma generate with full source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create setup.html with centered card on dark OKLCH background
- Validate URL via URL constructor, reject malformed input (T-06-01)
- Warn on non-HTTPS non-localhost URLs but allow (internal LAN support)
- Persist server_url to tauri-plugin-store config.json
- Navigate WebView to configured server after save
- All visible strings in German (Verbinden, Server-URL eingeben, etc.)
- Design tokens match Tessera OKLCH color system (primary, dark bg)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create Tauri project structure with Cargo.toml, tauri.conf.json, build.rs
- Implement lib.rs with store plugin and server URL navigation on startup
- Configure capabilities with core:default and store:default permissions
- Set frontendDist to ../src for local setup page (no bundled frontend)
- Add placeholder icons for build compatibility (to be replaced in 06-02)
- Add Cargo target/ to .gitignore
- Window config: 1280x800, centered, native decorations, resizable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
- 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>
- 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>
- Implement aggregateEvents with Promise.allSettled across visible sources
- Dispatch to ICS/CalDAV/Exchange providers by source.type with credential decryption
- In-memory per-user event cache with 5-minute TTL (Pitfall 4)
- Background cache refresh when close to expiry
- Implement testConnection with lastSyncAt/lastSyncError updates
- Default window: now to now+30 days
- Events sorted by start ascending with source color included
- Prisma model SearchProvider with userId/tenantId scoping
- Three default providers (Google/Bing/DuckDuckGo) as constants, always returned without DB seed
- GET/POST/DELETE search-providers endpoints on DashboardController
- Ownership verification on delete (T-05-07), default providers cannot be deleted
- CreateSearchProviderDto with class-validator: urlTemplate must contain {query} (T-05-08)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
- 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>
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>
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>
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>