Public search now deduplicates to one card per series with an edition
count badge. Admin list uses a grouped endpoint with expandable rows.
Market detail page shows an edition year switcher when multiple editions
exist. Admin detail page includes series edition management.
- Search box for name/city filtering
- Clickable column headers for sorting (name, city, status, date, created)
- Sort direction toggle (asc/desc) with arrow indicators
- All filters preserved through pagination and sort changes
- AI research panel with structured display (opening hours, admission)
- Shared MarketForm component with admin/public mode
- Geocode button to derive coordinates from address
- Opening hours, admission info fields in all forms
- Currency-aware pricing, full European country list
- Public submit now includes all market fields
- Weekday normalization from date for AI research results
- Duplicate detection warning on admin detail view
Backend returns code '2fa_required' but frontend checked for
'totp_required', so the TOTP input field never appeared. Also fix
the same Svelte pattern brace escaping issue as in TOTPSetup.
- Replace inline nav items with UserMenu dropdown (display name trigger,
Profil/Sicherheit/Admin/Abmelden, click-outside/Escape to close)
- Add password set/change form to profile security section
- Fix Turnstile site key (extra A, swapped l/1)
The page uses $env/dynamic/public which reads env vars at runtime,
not build time. The Docker build ARG/ENV only exists in the builder
stage and doesn't propagate to the Node.js runtime container.
Add PUBLIC_TURNSTILE_SITE_KEY to the Helm ConfigMap so it's
available as a process.env var when the SSR server runs.
- Add PUBLIC_TURNSTILE_SITE_KEY as Docker build arg and Woodpecker CI arg
- Uncomment auth nav in Header and MobileNav (login/logout/profile links)
- Move ThemeToggle from header to footer
- Expand country dropdown from DACH-only to all European countries
- Replace profile route redirect with requireAuth guard
- Set cookie secure flag based on environment (secure in prod)
- Add error handling to admin markets page (403 instead of 500)
- Admin layout with auth guard at /admin
- Admin market list with status filter tabs (pending/approved/rejected)
- Admin market detail/review with approve/reject actions
- Admin market create and edit forms with shared MarketForm component
- Anonymous market submission form at /markt/einreichen with Turnstile
- Optional latitude/longitude fields on submission form
- Admin and submission types added to API types
- requireAdmin guard for role-based frontend access
- Header/mobile nav updated with admin and submission links
- Auth layout redirect removed to re-enable login flow
- Login form action renamed to fix named actions conflict
- Impressum updated with user-submitted content section
- Datenschutz updated with submission form and Turnstile sections
Parse semi-structured notes text (e.g. "Samstag: Erw. 15 €, Kinder 8 €")
into grouped table rows instead of showing raw text below the table.
- Split notes on sentence boundaries (". " + uppercase), handling
abbreviations like "Erw." correctly
- Extract "Label: Category Price €" patterns into table groups
- Expand common abbreviations (Erw. → Erwachsene, Erm. → Ermäßigt)
- Guard against "Cat: Price, Cat: Price" format (colons in pricesStr)
- Guard against bare prices after colon (letter-start requirement)
- Graceful fallback to flat table + raw notes when parsing fails
Add /healthz handler in hooks.server.ts that returns early without auth
or SSR processing. Update Helm probes from / to /healthz to avoid
unnecessary log noise and wasted SSR renders.
Add /maerkte/ browse-by-state overview, /maerkte/[state]/ state detail
pages, and /maerkte/[state]/[city]/ city detail pages. Each page fetches
all markets and filters client-side (SSR). Includes slug utilities with
umlaut handling, JSON-LD structured data, breadcrumb navigation on all
pages including market detail, and sitemap entries for all new routes.
- Enable kit.version.pollInterval (60s) so SvelteKit detects new
deploys via version.json
- Add beforeNavigate guard that forces a full page reload when a
new version is detected, preventing stale client-side state
- Preload fonts, JS, and CSS for faster initial paint
With 900m/1000m CPU limits used, rolling updates fail because the
new pod cannot be created alongside the old one. Setting maxSurge=0
and maxUnavailable=1 kills the old pod first, avoiding quota exhaustion
at the cost of brief downtime during deploys.
The auth and profile layout redirects break SvelteKit's type
generation for child page loads, causing false-positive type
errors where PageData requires `user` from the root layout.
- Add dynamic sitemap.xml with static pages and market entries
- Add canonical URLs and base OG/Twitter meta tags in root layout
- Add JSON-LD WebSite schema with SearchAction on home page
- Add JSON-LD Event schema on market detail pages
- Add twitter:card summary_large_image for markets with images
- Add OG tags to impressum and datenschutz pages
- Add font preloading for critical rendering path fonts
- Add Sitemap directive to robots.txt
Add legal pages required by German law (TMG §5, DSGVO).
Impressum includes operator identity, contact, and liability notices.
Datenschutz covers all data processing: registration, OAuth, sessions,
cookies, geolocation, map tiles, and user rights.
Introduces brand identity with a medieval signet ring motif:
gold fleur-de-lis on dark green signet face. Adds SVG favicon,
raster fallbacks (ICO/PNG), apple-touch-icon, web manifest,
and inline signet ring logo in the header next to the wordmark.
- Add MedievalSharp + Crimson Pro fonts (woff2)
- Implement OKLCH color theme: forest green primary, gold accent,
warm stone neutrals, parchment/vellum surfaces, brick red danger
- Add dark/light mode with system preference detection and user toggle
(ThemeToggle component cycling system/light/dark)
- Prevent FOUC with inline script in app.html
- Replace native <select> with custom accessible dropdown (combobox/
listbox pattern, keyboard nav, type-ahead, app-themed styling)
- Add IP geolocation fallback via geojs.io when native geolocation
fails, with visual location badge showing resolved city name
- Shared form control styles via @layer base
- WCAG/a11y: skip-to-content link, aria-invalid, aria-describedby,
aria-live, focus-visible, role="search", color-scheme:dark
- Update all 23 component/page files with new color system and
dark: variants