TenderQueryDto gains validated plz (@MaxLength(5)), region, and
bundesland fields. buildTenderWhere adds three conditional AND-branches:
plz startsWith, bundesland exact-match against the now-backfilled indexed
column (Pitfall 1), and region startsWith (usable independent of the
bundesland column). FilterPanel gets a PLZ input and a 16-Land Bundesland
dropdown (mirrors NUTS1_BUNDESLAND — web/api are separate packages) that
write plz/bundesland into the URL searchParams; ResultsList already
forwards the full URLSearchParams to listTenders(), so no additional
fetch wiring was needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the Phase 10 module stub with a Master-Container page.tsx
(Suspense-wrapped for useSearchParams, Next 16 App Router) rendering
CoverageBanner + FilterPanel + ResultsList.
- ResultsList: URL-param-driven fetch via listTenders(), sortable
Frist/Wert/Veröffentlicht column headers, "keine Wertangabe" for
estimatedValue=null rows (D-05), pagination.
- FilterPanel: Freitext (q), Sortierung, "nur noch offene" toggle
(openOnly, default on), Abgabefrist von/bis date inputs — param names
match the TenderQueryDto field names 1:1 for the Plan 11-06
Saved-Search serialization contract.
- CoverageBanner: German coverage hint shown while GET /coverage
reports only the doe-opendata source (D-12, UI-05).
All strings hardcoded German (i18n = Phase 14). Plain fetch throughout,
no TanStack Query (not installed, per RESEARCH Open Question 4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends tender-radar-api.ts with the Tender type, listTenders(params)
and fetchCoverage() (plain fetch, credentials:'include', matching the
established fetchSourceConfig pattern — TanStack Query is not installed).
Adds the RED-first ResultsList.test.tsx: render items with
title/buyerName/deadline/value, "keine Wertangabe" for null estimatedValue
(D-05), and an empty-state message. ResultsList.tsx does not exist yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- actions.ts: mergeCertsAction(files, outputFormat, password?) builds FormData with
multiple file fields; delegates to postForm('merge', ...) (T-09-02/T-09-04)
- MergeTab.tsx: multi-file state (local), file input (multiple), output selector
(pem|pfx), Zusammenfuehren button disabled when < 2 files (data-testid for tests),
onOutputFormatChange callback to page.tsx for shared PasswordField visibility
- ConvertTab.tsx: gains pfx option + onTargetFormatChange callback (same pattern)
- page.tsx: lifts mergeOutputFormat + convertOutputFormat state; showPassword now
also true when active tab's output format is 'pfx'; passes callbacks to tabs
- cert-manager.test.tsx: 5 new tests — MergeTab disabled/enabled by file count,
shared PasswordField appears on pfx output, downloadBase64 called on success;
ConvertTab pfx option present; all 19/19 web tests green
- All production cert-manager files type-clean (pre-existing test type issues unchanged)
- Add FileResponse interface to actions.ts
- Add convertCertAction(input, targetFormat): builds FormData with
file/pemText/password + targetFormat, calls postForm convert endpoint
- Implement ConvertTab: native select for pem/der/p7b targetFormat,
Konvertieren button with loading swap, error classification, empty state
- On success: calls downloadBase64(filename, content, mimeType)
- 3 new ConvertTab tests: format selector options, downloadBase64 invoked
on success, text-destructive error on format rejection
- All 14 web cert-manager tests green
- Fix orphaned DkvModuleConfig: tenantId pointed to deleted tenant, updated to Default tenant
- DKV controller: return 404 instead of HTTP 200 null when no config exists
- IMAP provider: also detect PDFs sent as application/octet-stream (check filename extension)
- IMAP provider: add seen:false filter so already-processed emails are skipped on re-poll
- IMAP provider: mark email as \Seen after successful PDF download to prevent reprocessing
- Frontend dkv-api: handle 404 from fetchConfig as "not yet configured" (returns null)
- InboxConfigForm: show warning banner when config not yet saved in DB
- InboxConfigForm: add "Jetzt prüfen" button to manually trigger POST /dkv/check-now
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
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.
- 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>
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>
- Create [moduleSlug] page that loads module component via MODULE_REGISTRY whitelist
- Only registered slugs trigger dynamic imports; unknown slugs show not-found state (T-03-09)
- Create api.ts with getActiveModules and getModuleBySlug utility functions
- Back-link navigation from expanded view to category page
- Create module-loader.ts with MODULE_REGISTRY mapping slugs to dynamic imports (ssr:false)
- Create [category] page fetching active modules from API, filtering by category
- Create ModuleCard component with icon, name, description, and link to expanded view
- Add i18n keys for modules namespace (de + en)
- DomainInput component with text input and submit button
- ResultList component with green/red status badges (D-01)
- checkDomainAction fetches POST /modules/domaincheck/check with auth cookie
- Page renders within portal AppShell at /modules/domaincheck
- i18n keys added for both DE and EN locales