[1.25.10] - 2026-06-11
Fixed
- Changelog page: Hides maintainer-only preamble (version rules, current-version metadata) from the public
/changelogrender. - Berechnungen page: Corrected Tagesvergleich percent-change docs — per-route uses A as reference
((B - A) / A), network summary uses B as reference((A - B) / B); rush-hour hours aligned with code (07:00–08:59, 15:00–17:59).
[1.25.9] - 2026-06-11
Added
- Changelog page:
CHANGELOG.mdis now rendered as a public subpage at/changelog. - Berechnungen page: New methodology page at
/berechnungenexplaining averages, delay percentages, heatmap logic, outlier detection, boxplots, and comparison metrics.
Changed
- Sitemap: Added
/changelogand/berechnungen. - Footer: Added links to the new public pages.
[1.25.8] - 2026-06-10
Added
- Upper outlier PLATEAU/NIGHT pass: Second detection pass in
lib/outlier-detection-service.tsfor sustained or off-peak implausible readings (e.g. paired 35 min Bonn night artifacts) that fail the isolated SPIKE rule. - `npm run outliers:detect-upper`: One-off CLI for full-database upper outlier detection (
--applyto writedata_status = 'outlier'). - `npm run outliers:detect-lower`: Alias for the existing lower-outlier script.
Changed
- Bonn worker: Runs upper outlier detection (SPIKE + PLATEAU) after each successful import, alongside lower outlier detection.
[1.25.7] - 2026-06-09
Fixed
- Home route modal loop: Removed automatic
?period=URL sync on mount/refetch inRouteStatsContent— it fought the@modalparallel route and caused an infinite/↔/strecke/…RSC loop that remounted the detail map on every tick. Period is now synced only when the user changes the chart range. - Route detail map:
RouteDetailMapeffect deps use stable route fields instead of the wholerouteobject so parent re-renders do not tear down Leaflet. - URL comparison: Added
normalizeRoutePath/routePathsEqualso encoded umlauts and?period=3match their canonical paths.
[1.25.6] - 2026-06-09
Fixed
- Tests: Aligned
route-urlandroute-seoexpectations with v1.25.5 (umlaut paths withoutencodeURIComponent, two-item breadcrumb JSON-LD). - Leaflet `_leaflet_pos` TypeError: All Leaflet maps now call
map.stop()beforemap.remove()in their cleanup to cancel any pending zoom/pan CSS transitions before the container is destroyed (RouteDetailMap,Map,ConstructionSitesMap,BonnRoutesMap). - `RouteDetailMap`: Added
zoomAnimation: falsesofitBoundson mount cannot start a CSS zoom transition that outlives the component lifecycle.
[1.25.5] - 2026-06-09
Fixed
- Route URLs: Removed
encodeURIComponentfromroutePath— umlauts in slugs (ä,ö,ü,ß) are now kept as-is, consistent with the canonical URL and sitemap entries (prevents%C3%BCURLs differing from canonical). - Admin route link:
RoutesTablenow usestoSlug()(with NFC normalization) instead of inline regex that was missing.normalize('NFC'), preventing slug mismatches for routes with umlaut names. - Structured data: BreadcrumbList no longer has a position-2 entry pointing to the same URL as position 1 (there is no
/strecke/listing page); breadcrumb is nowStart → <Route name>. - TypeScript:
closeButtonRefinRouteStatsModaltyped asHTMLElement | nullto avoid union-ref assignment error on<button>. - Dead code: Removed
lastOpenedCardRef(was only used by the now-removed modal open/close flow).
[1.25.4] - 2026-06-09
Changed
- Route SEO title: „Verkehr und Stau in Bonn“ instead of „Verkehr Bonn“.
- Route SEO description: „in Bonn, NRW“,
Durchschnittsfahrzeit, andTrend: Stabil / Verspätung / Verbessernd(from live vs. average duration).
[1.25.3] - 2026-06-09
Changed
- Route detail page (`/strecke/`): Removed visible SEO-style intro paragraph; back link now reads „Zurück zur Verkehrs-Übersicht“.
Fixed
- Route SEO meta description: No longer includes start/end location addresses.
- Hydration mismatch: Route notice alert renders after client mount so
sanitizeHtml(browser-only) does not differ between SSR and hydration.
[1.25.2] - 2026-06-09
Fixed
- Route modal close: Added
@modal/page.tsx(returnsnull) so the parallel modal slot unmounts on soft navigation to/; close button usesLink, overlay/Escape trigger the same home navigation — fixes modal staying open after URL changed to/.
[1.25.1] - 2026-06-09
Fixed
- Route modal close: Close button, overlay click, and Escape on the home intercept modal now call
router.back()so the overlay dismisses and the URL returns to/without a full reload (replaces ineffectiverouter.push('/')).
[1.25.0] - 2026-06-09
Added
- Route SEO tests: Vitest coverage for
route-url,route-seo,toSlug, slug normalization (NFC/NFD), sitemap deduplication, andgetRouteBySlug/getRouteWithStatsBySlug(mocked DB).
Fixed
- Umlaut route URLs: Slug lookup normalizes URL segments to NFC so
/strecke/pages withü,ö,äresolve correctly (fixes NFD vs NFC mismatch from browsers). - Legacy hash `?period=`:
RouteStatsContentinitializes chart range from query param and skips the first URL sync so?period=7is not stripped on load. - Sitemap duplicates: One entry per slug when multiple routes collide on
toSlug(). - Header hydration: Theme logo uses a stable SSR placeholder until mount; image aspect-ratio warning reduced.
[1.24.15] - 2026-06-09
Added
- Route SEO tests: Vitest coverage for
route-url,route-seo,toSlug, and slug resolution (getRouteBySlug/getRouteWithStatsBySlugvia mocked DB).
[1.24.14] - 2026-06-09
Added
- Route SEO: Crawlable per-route pages at
/strecke/[slug]with unique metadata, Open Graph tags, JSON-LD (WebPage, BreadcrumbList, Place), and server-rendered intro text. - Modal intercept: Soft navigation from the home page opens route stats as a modal overlay (Next.js
@modalparallel route) while the URL updates to/strecke/[slug]. - `getRouteBySlug` / `getRouteWithStatsBySlug`: Server-side slug resolution for route pages and sitemap.
Changed
- Route URLs: Hash deep links (
/#slug&period=N) replaced by path URLs (/strecke/slug?period=N); legacy hash bookmarks redirect on load. - Sitemap: Route entries now use
/strecke/{slug}instead of/#{slug}. - Share links: “Link kopieren” and admin route links point to
/strecke/.... - `llms.txt`: Documents per-route URL pattern.
[1.24.13] - 2026-06-08
Changed
- Umami analytics: Basic anonymous page views load on every visit again (cookieless, no consent). „Alle akzeptieren“ gates extended events via
trackUmamiDetailedEvent()inlib/umami-consent.ts. Cookie banner, privacy page, and README updated.
[1.24.12] - 2026-06-08
Fixed
- Umami analytics: CSP
connect-srcnow allowshttps://gateway.umami.is. Umami Cloud’s tracker script posts events to that host; production CSP only listedapi-gateway.umami.dev, so page views and click events were blocked after consent (browser console: CSP violation on/api/send).
[1.24.11] - 2026-06-08
Changed
- Dashboard map: Leaflet frame is taller on tablet (620px) and desktop (720px); mobile height unchanged.
[1.24.10] - 2026-06-08
Fixed
- Route detail modal chart: Weather icons and temperatures above the historical duration chart are no longer clipped at the top; extra top margin and top-aligned reference labels match the Tagesvergleich chart layout.
[1.24.9] - 2026-06-05
Fixed
- Route cards / modal preview: „Keine Daten“ no longer appears when valid readings exist but the latest poll is
not_available(0 s). Live display again uses the most recent usable reading; live delay ranking still requires the latest reading to be valid (consecutive_error_streak === 0).
[1.24.8] - 2026-06-05
Fixed
- CSV export:
day_annotationcolumn no longer repeats single-day labels on every later date. Single-day annotations (no end date) apply only to their start date; multi-day annotations still span start through end date inclusive.
[1.24.7] - 2026-06-05
Changed
- „Aktuell am stärksten verzögert“: Consecutive error streak threshold for excluding routes raised from 3 to 10 readings (~2.5 h at 15 min polling).
[1.24.6] - 2026-06-05
Fixed
- „Aktuell am stärksten verzögert“: Routes with ongoing data errors (e.g. Nordbrücke with many consecutive
not_available/ 0 s readings) are no longer ranked using stale last-valid values. Live duration now requires the latest reading to be usable; routes with many consecutive bad readings are excluded from the top-delay list.
[1.24.5] - 2026-06-05
Changed
- Admin Routes Management: Datenquelle filter restored with three options — Alle, Google Maps, and Stadt Bonn / Amt 66. The route list again includes actively parsed Bonn city routes (removed
manual_onlydefault).
Fixed
- Admin Routes Management: Hides unimported bonn-worker stubs (
Bonn Route {id}) viaexclude_bonn_stubs— auto-created routes with only „nicht ermittelbar“ readings no longer clutter the list.
[1.24.4] - 2026-06-04
Changed
- Admin Routes Management: List shows only manually created routes (
manual_only— nobonn_citymapping). Bonn/worker imports stay under „Official Bonn Routes“. Datenvergleich config uses separate Bonn vs manual route lists.
Added
- `npm run db:cleanup-recent-imports`: Deletes
traffic_readings,bonn_strecke_readings, and inactive worker auto-routes (Bonn Route N) from the last 3 hours (override withCLEANUP_HOURS).
Changed
- Docs: README, QUICKSTART, and CLAUDE.md updated for 1.24.4 (admin route lists, OSM tile layer, cleanup script).
- Dependencies: Patch/minor npm updates within current major versions (Next.js 16.2.x, Vitest 4.1.x, pg, dompurify, TypeScript tooling).
[1.24.3] - 2026-06-04
Fixed
- Admin maps (OSM / Leaflet): Bonn routes and construction-site maps showed a gray tile grid and missing route overlays because the map initialized before layout settled and
fitBoundsran without a follow-upinvalidateSize(). Admin maps now use the sharedmap-shelllayout, load Leaflet CSS vialib/osm-tile-layer.ts, wait for map readiness before drawing layers, and refresh tile layout after bounds changes.
[1.24.2] - 2026-06-04
Fixed
- Dashboard map stats: Stopped refetching
/api/routes/stats?all=trueevery time the map section re-enters the viewport (e.g. scrolling back to “Aktuell am stärksten verzögert”). Map routes load once on first visibility; filter changes still trigger a single refetch.
[1.24.1] - 2026-06-04
Fixed
- OpenStreetMap tiles: Dashboard and embedded maps had a zero-height Leaflet container (
map-shell--embeddedwithheight: 100%but no sized parent after the responsive refactor), so tiles never loaded. Embedded shell now fills.map-section__container; maps callinvalidateSize()on resize. Shared tile URLtile.openstreetmap.orgwithreferrerPolicyper OSM tile policy ([lib/osm-tile-layer.ts](lib/osm-tile-layer.ts)).
[1.24.0] - 2026-06-04
Security
- Secret remediation: Removed tracked
.env.local.bakand.env.example.bak; added*.bakand.env.local.bakto.gitignore. Purged both files from full git history viagit filter-repoand force-pushed rewritten history. - Action required: Rotate the Google Maps API key and Postgres password that were present in
.env.local.bak(history scrub does not revoke already-leaked credentials).
Changed
- Repo hygiene: Stopped tracking generated
scripts/compiled/(regenerated bynpm run build/postbuild); workers continue to prefertsxat runtime. - Readability refactor: Split monolithic client pages into focused modules with top-of-file responsibility comments:
app/datenvergleich/—types.ts,aggregation.ts,useDatenvergleichData.ts,_components/ViolinPlot.tsx,DatenvergleichComparisonCharts.tsx,DatenvergleichSecondComparisonCharts.tsxapp/admin/—useAdminRoutes.ts,_components/RouteFormModal.tsx,DeleteRouteDialog.tsx,RoutesTable.tsx,BonnRoutesSection.tsxapp/tagesvergleich/—parsing.ts,types.ts,useTagesvergleichData.ts,_components/*components/RouteStatsModal/—charts/,tabs/,useRouteStatsDerivedData.ts, slimmerindex.tsx- Shared shell: Restored
components/SiteHeader.tsxandcomponents/SiteFooter.tsx(responsive nav/footer used across dashboard, FAQ, errors, logs, comparison pages).
[1.23.3] - 2026-06-04
Changed
- Responsive overhaul: Unified breakpoints to
640 / 768 / 900, introduced--primaryand--primary-coloraliases, added spacing tokens (--space-1to--space-8), and aligned map filter rhythm with consistent compact spacing. - Dashboard + pages: Reworked the
<900pxmap filter panel (flex top row with compact live/time pill), made map height container-driven, added a tablet modal rule (max-width: 900px), improved comparison-page grids/popovers, and added stacked mobile behavior for admin route tables with 44px touch targets. - Shared shell components: Extracted reusable
SiteHeader/SiteFooterand migrated dashboard, FAQ, errors, logs, tagesvergleich, and datenvergleich to reduce duplicated responsive navigation/footer logic. - Interaction accessibility: Updated
DelayMatrixwith tap-to-pin tooltips and improved cell readability on small/touch devices.
[1.23.2] - 2026-06-04
Added
- Dashboard: New section below the map and above Getrackte Verbindungen shows the top 3 routes with the highest live relative delay (
+%vs. route average), including rank, current minutes, and status accent.
Changed
- Delay ranking logic: Extracted shared helper
getLiveDelayPercent(lib/route-delay.ts) and reused it both for the new top-3 dashboard section and list sorting option Größte Verspätung (live) to keep ranking behavior consistent.
[1.23.1] - 2026-03-22
Fixed
- Statistik-Modal „Fahrtdauer (historisch)“: Wetter-Marken nutzen für Zeiträume unter 30 Tagen jetzt 4h-Slots (wie der Worker), statt nur einer Messung pro Kalendertag — damit erscheinen alle gespeicherten Wetterpunkte im gewählten Fenster (z. B. Letzte 3 Tage). API-Zeitraum mit ±1 Tag Puffer und lokalen
yyyy-MM-dd-Grenzen statttoISOString().slice(0,10)(UTC), plus Filter auf den sichtbaren Chart-Zeitraum. Chart-Höhe 400→520 px, etwas mehr Rand unten für Achsen/Wetter-Labels.
[1.23.0] - 2026-03-22
Changed
- Version: Minor release 1.23.0 (synchronized across
package.json,lib/version.ts, README, and lockfile).
[1.22.67] - 2026-03-22
Changed
- Dunkelmodus: Das bisherige Erscheinungsbild Studio (tiefere Flächen, Cyan-Akzente, Hintergrund-Gradient, Karten-/Recharts-Styling) ist jetzt der einzige Dunkelmodus; die klassische Slate-Palette entfällt. `--bg-sunken` nutzt `#1c2832` statt `#0a0d11` (weicherer Kontrast).
- Footer: Umschalter Klassisch / Studio entfernt; Hell/Dunkel steuert das Theme wie zuvor. `visualStyle` in
localStoragewird beim Laden migriert (`studio` → `theme: dark`, Key entfernt); gleiche Logik im Head-Boot-Skript und in `migrateLegacyVisualStyle` ([lib/theme-storage-migration.ts](lib/theme-storage-migration.ts)), abgedeckt durch Vitest.
[1.22.66] - 2026-03-22
Changed
- Dashboard-Routenkarten: Status-Icons im Kartenkopf entfernt; der farbige Status-Balken und die Karten-Variante (
route-card--fast/moderate/slow) zeigen den Zustand bereits.
[1.22.65] - 2026-03-22
Fixed
- Dashboard-Kartenfilter: Die 3-Spalten-Zeile für Richtung/Quelle/Live stand ohne Media Query nach den mobilen Regeln und hat damit auf schmalen Viewports die 2-Spalten-Layouts überschrieben (alles in einer gequetschten Zeile). Jetzt: kompaktes Standard-Layout (Label über Select, 2 Spalten, Live/Zeit volle Breite mit Padding) bis 899px, ab 900px die horizontale 3-Spalten-Variante. Selects unter 900px `width: 100%`, Panel ab 768px `width: min(100% - 2rem, 42rem)`.
- Studio „Live“: Text und Puls wieder rot (
#fca5a5/var(--status-slow)), Glas-/Opacity-Panel unverändert; Timeline/Zeit-UI bleibt cyan.
[1.22.64] - 2026-03-22
Changed
- Dashboard Kartenfilter (obere Zeile): Layout auf CSS-Grid umgestellt (Desktop: Richtung | Quelle | Live/Zeit rechtsbündig). Mobile: größere Touch-Ziele (
min-height~44px), klarere Uppercase-Labels, Live-/Zeit-Zeile als eigene Karte über volle Breite; unter 400px stapeln sich Richtung und Quelle untereinander. Live-Ping aus Inline-Styles in.map-section__live-pulsemit bestehendem@keyframes ping. - Studio: Panel mit leichtem Glas-/Gradient-Hintergrund, cyan Rand + Schatten, Trennlinie unter der oberen Zeile, Live in Akzentfarbe mit cyan Puls, Zeit-Leiste „Live“-Button und Timeline-Daumen an die Studio-Palette angeglichen.
[1.22.63] - 2026-03-22
Changed
- Karten Start/Ziel: Harte 2px-Rahmen an den Punktmarkern entfernt; dezenter Schlagschatten, leicht größere Punkte (Hauptkarte + RouteDetailMap in den Statistik-Modals).
- Dashboard-Karte: Option `showLegend={false}` auf der Startseite – die eingebettete `map-shell__legend` wird dort nicht mehr gerendert (Legende bleibt u. a. auf Tagesvergleich und in den Routen-Statistik-Modals über
RouteDetailMap). - Datenvergleich: An Tagesvergleich angeglichen: Untertitel mit Icon (
IconArrowsLeftRight),mainmitmaxWidth: 1100px, Beschreibungskarte wie Info-Box formatiert, Vergleichs-Umschalter mitvar(--bg-sunken)/var(--interactive-primary), Zeitraum-Zeile in einer card, Admin-Link im Leerzustand mitvar(--interactive-primary).
[1.22.62] - 2026-03-22
Changed
- Header / Navigation: Startseite aligned with subpages:
header__page-infowithheader__page-title(Dashboard) andheader__page-subtitle(previous tagline); first nav item Dashboard withheader__nav-link--active. Datenvergleich empty-config state: same subtitle as main view (Stadt Bonn vs. Google Maps). Tagesvergleich: subtitle row uses shared classheader__page-subtitle--inline-iconinstead of inline flex styles. - Focus (a11y): Stronger
:focus-visiblerings (3px outline, larger offset) for.btn,.header__nav-link,.header__menu-toggle, chart segmented controls, map filter select / timeline controls, footer visual-style options; Studio.btn:focus-visiblematches. - Karten-UI & Design-Tokens: Leaflet-Attribution, Zoom-Leiste und Popups nutzen
var(--card-bg),--text-primary,--text-secondary,--border-color,--interactive-primary(inkl. Studio über Variablen). Neue Hilfsklassen `map-shell` / `map-shell__legend` / `map-shell__loading`; `RouteMap`: eingebettete Legende (Farben, Start/Ziel, optional Pfeile ab Zoom 14, Baustellen-Hinweis), Ladehinweis mit Tokens, Marker/Popups mit CSS-Variablen; optional `embeddedInFrame` auf Dashboard und Tagesvergleich gegen doppelten Rahmen. `RouteDetailMap`: Legende mit Tabler-Icons,STATUS_COLORS, dunkler Verlauf nur im Dark-Mode, Ziel-Marker mit Token-Farben. - Recharts: Globale Cursor-/Legendentext-Defaults an Tokens gebunden; gemeinsame Konstanten in `lib/recharts-chart-styles.ts`; Datenvergleich, RouteStatsModal, Tagesvergleich nutzen diese für Tooltip (inkl.
labelStyle) und Legende wo sinnvoll.
Added
- `@media print` in `globals.css`: Hides non-content chrome (header menu toggle, theme button in nav, visual-style footer block, studio theme hint, map filter overlay, copy toast). Print-friendly Recharts (darker grid/ticks, black labels, hide tooltips), static header, white page background,
print-color-adjust: exacton chart/card surfaces. - Tabler-Icons & IA (Startseite, FAQ, Fehler, Logs):
SectionHeadingWithIconund dekorative@tabler/icons-react-Symbole auf Dashboard (Überschrift „Getrackte Verbindungen“, Info-Abschnitte inkl. „Über dieses Angebot“, Footer-Links; Status-Icons in Routenkarten; Fehlermeldungen Karte/Liste mitIconAlertCircle). FAQ: Icon im Seitentitel, Einleitung mit Unterüberschriften „Fragen und Antworten“ / „Themenübersicht“, Accordion mit Chevron-Icons, sichtbares Label „Antwort“ pro Eintrag, Footer „Kontakt & Ideen“ bei Fallback-Text, „Weiterführende Links“ mit Icon. Fehler: Icon im Titel, „Hinweis zur Auswertung“ und „Routen mit Meldungen“ als Abschnittsüberschriften. Fehler (intern): Schloss-Icon im Titel, Abschnitt „Bonn-Strecken (stadtplan.bonn.de)“. Worker-Logs: Überschrift mit Datei-Icon, Frontend-Bereich mit Browser-Icon, Filterzeile mit Filter-Icon; Header-Logo hell/dunkel wie auf den anderen Unterseiten. - Admin: Tabler-Icons und
SectionHeadingWithIcon/TableHeaderCellfür Überschriften, Navigations-Shortcuts, Bonn-Bereich, Aktivitätsprotokoll und Routen-Tabellen; Ladezustand mit Spinner und kurzem Text. Admin-Layout: Icons für Titel, Passwort, Logout und Dashboard-Link; Ladehinweis beim Auth-Check. - Vergleichsseiten: Neue Hilfskomponenten
ComparisonFlowLoading/ComparisonEmptyHintsowieTableHeaderCellfür Tagesvergleich und Datenvergleich (Lade- und Leerzustände, Tabellenköpfe Outlier / Top-Routen). - Startseite: Leerzustand der Routenliste mit
IconDatabaseOffund Kurztext „Keine Routen für die aktuelle Auswahl.“
[1.22.61] - 2026-03-21
Added
- Tabler-Icons für Kennzahlen und Diagramme: Neue Hilfskomponente
SectionHeadingWithIcon(Überschrift + Icon). RouteStatsModal: Icons für Trend, Distanz, Durchschnitt (Uhr/Tacho je nach Hover), Baseline/Stunde, Tages-⌀, Stau-Intensität, Rush-Hour-Distanz; Umschalter Fahrtdauer / Geschwindigkeit; Diagrammtitel (Verteilung, Tageszeit, Stau-Intensität, Rush-Hour-Analyse) und Historien-Titel. Tagesvergleich: Untertitel Netzwerk-Analyse, Filter-Banner, Karten Ø Fahrtdauer / Gesamte Verzögerung, Tagesverlauf, Top-10-Tabelle, Kartenansicht. Datenvergleich: Abschnittsüberschriften (Kennzahlen, Statistiken, Violin/Bland-Altman/CDF, gleitender Mittelwert, Outlier, Zeitverlauf, Tageszeit) mit passenden Tabler-Icons (beide Vergleichs-Blöcke).
[1.22.60] - 2026-03-21
Fixed
- Hydration mismatch (Logo Hell/Dunkel):
ThemeProviderinitialisierttheme/visualStyle/resolvedThemejetzt fest mit SSR-kompatiblen Defaults (light/classic/light). Gespeicherte Werte kommen erst imuseLayoutEffect(vor dem Paint), passend zum<head>-Boot-Skript aufdocument.documentElement. Damit stimmt die erste Client-Render-Ausgabe mit dem Server-HTML überein.
[1.22.59] - 2026-03-21
Added
- Tabler Icons ([
@tabler/icons-react](https://github.com/tabler/tabler-icons), npm-Build des offiziellen GitHub-Projekts): Icons für Hell/Dunkel (ThemeToggle), Studio-Hinweis (IconBrightnessHalf), Erscheinungsbild (IconLayoutNavbar/IconSparkles), Teilen und Link kopieren (Startseite, Datenvergleich), Worker-Logs (Kategorie statt Emoji in der Liste; Export-Text weiter mit Emoji ausCATEGORY_EMOJI), Fehlerzeile mitIconAlertCircle. - `WeatherConditionIcon` + `WeatherInlineChartLabel`: Wetter-Referenzlinien in Tagesvergleich und RouteStatsModal nutzen Tabler-Wetter-Icons (SVG
foreignObject) statt Emoji.
[1.22.58] - 2026-03-21
Changed
- Erscheinungsbild Studio: Feinschliff an Typo (ui-sans-serif-Stack, Kerning, Überschriften-Tracking, Tabular-Zahlen für
.data-value), Buttons (weichere Transitions, Primary-Gradient mit Innenlicht, Hover-Glow, Secondary/Ghost/Danger-Hover), Route-Karten und Kartenfilter-Selects, Header-Navigation (cyan Unterstreich-Shadow, aktiver Link), Footer-Erscheinungsbild-Umschalter und Recharts (cyan Grid, Tooltip mit Rand/Glow, Cursor-Linie, Tooltip-Textfarben). Weiterhin keine externen Schriftarten.
[1.22.57] - 2026-03-21
Removed
- Tagesvergleich: Cursor-Debug-Instrumentierung entfernt (
agentDebugLog,fetchComparison-Log, Chart-Pipeline-useEffect) sowie `POST /api/debug-agent`.
[1.22.56] - 2026-03-21
Added
- Erscheinungsbild „Studio“: Zweites, dunkles Farbschema (cyan Akzente, tiefere Flächen, leichte Hintergrund-Atmosphäre) per CSS-Variablen unter
html[data-visual-style="studio"].dark; Klassisch bleibt unverändert. - Footer-Umschalter „Klassisch“ / „Studio“ (
VisualStyleToggle) auf Startseite, FAQ, Datenvergleich, Tagesvergleich, Fehler und Fehler (intern); Auswahl inlocalStorageuntervisualStyle(Standard: Klassisch). - Anti-FOUC: Synchrones Skript im
<head>(mit CSP-nonce) setztdata-visual-styleunddarkvor dem ersten Paint. - Hell/Dunkel: Im Modus Studio ist nur das dunkle Erscheinungsbild aktiv; der Header-Schalter wird durch einen kurzen Hinweis ersetzt. Zurück zu Klassisch stellt den gespeicherten Hell/Dunkel-Modus wieder her.
Changed
- ThemeContext: Unterstützt
visualStylenebentheme; bei Studio wird immerdarkauf<html>gesetzt undresolvedThemeistdark(Karte, Recharts, Logos). - Footer-Links: Harte Blaufarbe
#2563ebin den mitgeänderten Footern durchvar(--interactive-primary)ersetzt.
[1.22.55] - 2026-03-21
Fixed
- Tagesvergleich / Linien fehlen (nur Achsen + Baseline):
experimental.optimizePackageImports: ['recharts']ließLineChartdie JSX-<Line>-Kinder nicht als grafische Serie erkennen (leerepoints→ keinrecharts-lineim SVG). Recharts aus `optimizePackageImports` entfernt (nurdate-fnsbleibt). Zusätzlich Tag-A/B-Linien als Array mit Keys wie beim Wochentage-.map()statt Fragment. - Next.js:
next.config.jskurz dokumentiert, warum Recharts nicht mehr tree-shake-importiert wird.
[1.22.54] - 2026-03-21
Fixed
- Tagesvergleich / Tagesverlauf (einzelne Tage):
Line-Komponenten nutztendot={{ r: 2 }}— bei jedem Render ein neues Objekt, was Recharts-Updates stören kann (Wochentage-Modus nutztdot={false}). Stattdessen stabiles Modul-ObjektTAGESVERGLEICH_DAY_LINE_DOT. Zusätzlich `key` am `LineChart`, damit beim Wechsel Modus/Daten ein sauberer Remount erfolgt.
[1.22.53] - 2026-03-21
Removed
- Tagesvergleich: Temporary Cursor debug instrumentation (
agentDebugLog, chart pipelineuseEffect) and dev-only `/api/debug-agent` proxy removed after post-fix log verification.
[1.22.52] - 2026-03-21
Fixed
- Tagesvergleich / leeres Diagramm: Superseded
fetchComparison/fetchWeekdayruns returned early (id !== fetchIdRef.current) but still ranfinally { setLoading(false) }, leaving `result` null with loading false (empty chart, no error). Loading is now cleared infinallyonly when `id === fetchIdRef.current` so the in-flight request owns the loading flag.
[1.22.51] - 2026-03-21
Fixed
- Tagesvergleich / debug ingest: Browser requests to
127.0.0.1:7619fromlocalhost:3000are cross-origin and typically blocked by CORS, so no NDJSON lines appeared. Added `POST /api/debug-agent` (dev only) to forward payloads server-side to the Cursor ingest;agentDebugLognow posts to that route.
[1.22.50] - 2026-03-21
Added
- Tagesvergleich / debugging: Client-side instrumentation (
agentDebugLog) posts chart pipeline metrics to the Cursor debug ingest (session-scoped): raw vs normalizedhourlyBreakdown, fetch errors, andchartData/ Y-domain / numeric point counts. Intended for diagnosing empty Recharts series; remove after diagnosis.
[1.22.49] - 2026-03-21
Fixed
- „Link kopieren“ / Toast: Die Meldung wurde von
.stats-modal__controls { overflow: hidden }abgeschnitten. Toast erscheint nun fix unten im Viewport (.copy-toast,z-indexüber dem Modal). Zusätzlichlib/copy-to-clipboard.tsmit Fallback (Textarea +execCommand), fallsnavigator.clipboardfehlschlägt..stats-modal__controlsnutztoverflow: visible.
[1.22.48] - 2026-03-21
Changed
- „Link kopieren“: Kurzes Toast-Feedback einheitlich „Erfolgreich kopiert!“ (Route-Statistik-Modal, Startseite, Tagesvergleich).
[1.22.47] - 2026-03-21
Changed
- Route stats modal — Auswertung: Layout nicht mehr über
chart-container__headermitspace-between; Label und Segmentleiste sitzen als zentrierte Gruppe (route-stats-modal__auswertung). Unter ~520px Breite untereinander mit gleichmäßiger Button-Zeilung (flex: 1, kleinere Schrift). Zusätzliche Innenabstände undsafe-areafür schmalere Viewports.
[1.22.46] - 2026-03-21
Changed
- Chart segmented controls: Behälter wie auf
/datenvergleich(position: relative,display: inline-flex,background: var(--bg-tertiary),border-radius: 8px,padding: 4px). Aktives Segment wieder klar hervorgehoben mitvar(--interactive-primary), weißer Schrift undfont-weight: 600(Hover:var(--interactive-hover)); inaktive Segmentevar(--text-primary), dezenter Hover mit--bg-elevated.
[1.22.45] - 2026-03-21
Changed
- Chart segmented controls (
.chart-container__segmented-button): ruhigeres Erscheinungsbild — flacher Rahmen auf--bg-sunken, aktive Option wie eine kleine „Karte“ (--bg-elevated, dezente Border, leichter Schatten) statt voller Primärfarbe; kein Hover-Lift, keine Glanz-Overlay-Animation; Fokus sichtbar perfocus-visible.
[1.22.44] - 2026-03-21
Changed
- Route stats modal: Auswertung (Alle | Wochentage | Wochenende) steht nur noch unter „Relative Verspätung nach Wochentag und Uhrzeit“ und vor „Verteilung der Fahrtdauern“ (segmentierte Steuerung wie bei Stau-Intensität). Der Filter wirkt nur noch auf die Abschnitte darunter (Boxplot, Fahrtdauer nach Tageszeit, Stau-Intensität inkl. Perioden-Fallback, Rush Hour). KPI-Karten, Historienchart, Top-Tage und Vorjahr nutzen wieder durchgehend den vollen gewählten Zeitraum; die Matrix bleibt unverändert all-time.
[1.22.43] - 2026-03-21
Added
- Route stats modal: Auswertung Alle | Wochentage | Wochenende für den gewählten Zeitraum (Ø-Dauer, Stau-Intensität, Tageszeit-Chart, Boxplot, Rush-Hour-Karten, wöchentliche/monatliche Stau-Intensität; Baseline „schnellste Fahrt“ bleibt Zeitraum gesamt). Aufklappbare Tabelle Top 10 Tage mit höchster relativer Verspätung.
- Vitest:
tests/lib/weekday-weekend-stats.test.tsfür die reinen Hilfsfunktionen.
Changed
- Umami: Skript wird nur nach „Alle akzeptieren“ geladen (
ClientCookiesAndAnalytics); Root-Layout bindet Banner + Analytics gebündelt. - Cookie-Banner & Datenschutz: Texte an tatsächliches Verhalten angepasst (OSM-Kacheln bei Karte, Umami nur bei Zustimmung, httpOnly-Admin-Session, Google Routes API in der Datenschutzerklärung).
- SEO: Root-
keywordsund Fallback-seo_description(site-texts-server) sowiepublic/llms.txtmit Bonn-Verkehrs-Begriffen ergänzt; Readme-Hinweis zu Admin-Texten und Keywords.
[1.22.42] - 2026-03-21
Fixed
- /tagesvergleich: Tag A / Tag B now initialize from the latest two local data days with valid readings instead of blindly defaulting to yesterday/day-before-yesterday, so the hourly Recharts view no longer boots empty when recent data is missing.
Added
- API: Added
/api/day-comparison/defaultsto expose the latest comparable day pair with data for the day-comparison UI. - Vitest: Added regression coverage for the new day-comparison defaults route and lookup helper.
[1.22.41] - 2026-03-21
Changed
- Traffic worker: Added a startup backfill for missing
route_alltime_stats.monthly_congestioncache entries so redeploying the worker repairs old rows immediately instead of waiting for the next daily all-time refresh.
Added
- Vitest: Added coverage for the targeted monthly-congestion cache backfill helper in
alltime-stats-service.
[1.22.40] - 2026-03-21
Fixed
- Route stats API:
alltime_stats.monthly_congestionis now calculated on demand ingetTrafficStatswhen the cached monthly series is missing inroute_alltime_stats, so the route modal monthly Stau-Intensität no longer falls back to selected-period data. - Vitest: Added a regression test covering the missing-cache monthly-congestion fallback in
traffic-readings-service.
[1.22.39] - 2026-03-21
Changed
- Rate limiting: All limited public endpoints now return the German message
Zu viele Anfragen. Später erneut versuchen.on429. - Rate limiting: Hitting the limiter now bans the offending IP for 120 seconds across the limited endpoints instead of only rejecting the single overflowing request.
- Frontend error handling: Homepage map/list loading, route modal stats, and
/logsnow surface the rate-limit message directly instead of generic HTTP failures.
[1.22.38] - 2026-03-21
Added
- Rate limiting tests: Added Vitest coverage for the shared request limiter on the hot public GET endpoints and for
alltime_stats.monthly_congestionin the route stats payload.
Changed
- Route modal: Replaced the historical chart mode pills with the same segmented-control styling used for the congestion switch and tightened the animation/active-state design.
- Tagesvergleich: Normalized hourly chart data at the fetch boundary and split the annotation selector data source from the chart-range annotations.
- Logs: Hide legacy Google Maps progress entries in
/logsand only persist the final Google Maps summary moving forward. - Leaflet/mobile map: Scoped the mobile bottom-right Leaflet control hiding to the homepage map and narrowed dark-mode Leaflet link styling to attribution/bar controls only.
- All-time stats: Added cached monthly congestion data so the route modal can show all-time monthly Stau-Intensität independently from the selected period.
Fixed
- Rate limiting: Raised the shared limit to
26 requests / 10 secondsso the homepage timeline slider can scroll without tripping the limiter. - API route imports: Switched the new rate-limit helper imports to working relative paths in the Next.js route handlers.
[1.22.37] - 2026-03-21
Added
- Vitest coverage: Replaced the temporary Jest smoke tests with a Vitest-based suite covering API handlers, login flow, admin API-settings edits, toast UX behavior, and Bonn data helpers.
- Deployment docs: Documented the production worker flow (
npm run build,npm run build:scripts,npm run worker:start) and clarified thatscripts/compiled/remains committed.
Changed
- Admin UI polish: Refined the extracted route-mapping modal and export/import section with clearer hierarchy, badges, stronger dialog styling, and more intentional action states.
- Script runtime safety: Updated worker start scripts to use the TS-or-compiled runner while still keeping compiled worker artifacts tracked in Git.
[1.22.36] - 2026-03-21
Added
- Admin extraction: Moved reusable Bonn route row and construction-sites map UI out of
app/admin/page.tsx.
Changed
- Worker: Removed per-route mapping lookups and the per-route Google time-window check in
traffic-worker; mappings and fetch settings are now loaded once per run. - Routes stats API: Consolidated paginated route stats and total count into a single backend query and moved direction filtering into SQL.
- Repo hygiene: Added
turbopack.root, updated README framework docs to Next.js 16, and documented the script runner fallback and deployment expectations.
[1.22.35] - 2026-02-12
Fixed
- 404 page: Use Next.js
Imagefor the SVG to satisfy linting.
[1.22.34] - 2026-02-12
Added
- /logs retention: Auto-prune worker logs older than 60 days and cap to the latest 4000 entries.
- 404 site: custom message and SVG for error pages
Changed
- Map (mobile): Reworked the timeline time selector with larger touch targets, a clear Live reset button, and endpoint labels.
- Map (mobile): Increased the map height for better touch controls.
[1.22.33] - 2026-02-10
Added
- README: Added a dedicated SEO & AI discoverability section (metadata, structured data, robots/sitemap,
llms.txt) and a deployment-ready “adapt to another town” checklist with concrete file paths and migration scope.
Changed
- README version header: Updated README top-level version display to match the current release.
[1.22.32] - 2026-02-10
Added
- Sitemap: Added
/llms.txttositemap.xmlgeneration so LLM-focused metadata is discoverable via sitemap. - Homepage (hidden hint): Added a visually hidden hint on
/pointing LLMs to/llms.txtfirst.
[1.22.31] - 2026-02-10
Added
- SEO metadata layouts: Added dedicated metadata for
/datenvergleich,/tagesvergleich,/errorsand internal noindex metadata for/errors-internal. - Structured data: Added global JSON-LD (
Organization,WebSite) and FAQ JSON-LD (FAQPage) for better search and AI parsing. - AI discovery: Added
public/llms.txtin German with site purpose, public pages, and collaboration note (without API links).
Changed
- Root metadata: Added
metadataBase, OpenGraph/Twitter image metadata, and robust site URL normalization. - Canonical URLs: Added explicit canonical URLs for
/faqand/datenschutz, plus the new route-level layouts above. - robots.txt: Now disallows
/log,/logs,/login, and/api(existing disallows preserved).
[1.22.30] - 2026-02-10
Fixed
- SEO metadata + static HTML source: Root metadata now resolves
seo_titleandseo_descriptionfrom the samesite_textssource used for embedded HTML, with placeholder cleanup for legacy values so Google-facing metadata no longer falls back to old placeholder text. - Admin SEO saves: Saving
seo_titleorseo_descriptionvia/admin#textsnow sanitizes placeholder input before persistence and keeps layout revalidation behavior. - /errors + /errors-internal: Added period toggle (
Gesamt (all-time)vsLetzte 7 Tage) so users can view error stats for last week in addition to all-time.
Changed
- /api/bonn-routes: Added optional
daysquery handling (same semantics as/api/routes/errors) and returns selected window in the response.
[1.22.29] - 2026-02-10
Changed
- /tagesvergleich: Reduced duplicate UI/data code by centralizing hour-label formatting, weather-line generation, and hour-filter preset button rendering for both compare modes.
- /tagesvergleich: Normalized chart value parsing via the shared numeric parser so localized numeric strings are handled consistently.
[1.22.28] - 2026-02-08
Fixed
- /tagesvergleich: Preserve raw hourly breakdown values from the API and parse localized numeric strings defensively so the day chart renders even when the payload contains formatted numbers.
[1.22.27] - 2026-02-08
Fixed
- /tagesvergleich: Ensured day-comparison chart uses numeric
avgDurationA/Bvalues consistently (debuggable via dev helper).
[1.22.26] - 2026-02-08
Fixed
- /tagesvergleich: Restored day-comparison sparklines by switching to
avgDurationA/Bvalues.
[1.22.25] - 2026-02-08
Added
- /tagesvergleich (dev): Exposed
window.__tagesvergleich_debugto inspect chart inputs in development.
[1.22.24] - 2026-02-08
Fixed
- /tagesvergleich: Day-comparison hourly chart now uses raw
avgDurationA/Bvalues without extra normalization to prevent null-only series.
[1.22.23] - 2026-02-08
Fixed
- /tagesvergleich: Day-comparison lines now bind directly to
avgDurationA/Bwith visible dots to confirm rendering.
[1.22.22] - 2026-02-08
Fixed
- /tagesvergleich: Day-comparison hourly chart now uses compare-mode rendering and function data keys, preventing missing lines when result shape is valid but the renderer mis-detects mode.
[1.22.21] - 2026-02-08
Fixed
- /tagesvergleich: Day-comparison hourly chart now binds to normalized
avgDurationA/Bvalues (preserving raw keys) and avoids missing lines. - /tagesvergleich: Day comparison now returns the requested date strings without timezone drift (fixes weather day off-by-one).
Changed
- Map (mobile): Refined the floating control panel layout for smaller screens (grid layout, tighter spacing, clearer time row).
[1.22.20] - 2026-02-08
Fixed
- /tagesvergleich: Hardened hourly breakdown parsing and numeric extraction (supports mixed key formats) so the day-comparison hourly chart renders reliably.
Changed
- Map (mobile): Refined the floating control panel layout for smaller screens (grid layout, tighter spacing, clearer time row).
[1.22.19] - 2026-02-08
Fixed
- /logs: Consolidated Google Maps per-route fetch logging into a single entry with progress ("Fetching completed: x/y routes") and combined details.
- /tagesvergleich: Normalized hourly breakdown parsing (snake_case + string hour fallbacks) so "Tagesverlauf (Ø Fahrtdauer pro Stunde)" renders in day comparison.
- Fahrtdauer (historisch): Added extra chart padding so the y-axis label and legend text have enough room.
Changed
- Map (mobile): Refined the floating control panel layout for smaller screens (grid layout, tighter spacing, clearer time row).
[1.22.18] - 2026-02-07
Changed
- Worker logs: Reduced Bonn noise to a single summary entry per run (plus per-route errors) and filtered console mirroring to only keep critical lines.
Added
- Worker logs: Per-route “nicht ermittelbar” entries for Bonn and Google Maps imports.
[1.22.17] - 2026-02-07
Fixed
- Worker build: Added
postbuildto always compile worker scripts afternpm run build, preventing stale compiled workers from missing weather/log changes.
[1.22.16] - 2026-02-07
Added
- Deployment docs: Added optional
npx next telemetry disablestep for Coolify/production setup.
[1.22.15] - 2026-02-07
Added
- /logs filters: Added UI filters for errors, outliers, Bonn import, Google Maps import, database pool, weather, and “worker is running”.
Removed
- Docs/PDF cleanup: Removed VibeSec.md and the generated PDF from the repository tree.
[1.22.14] - 2026-02-07
Added
- Worker logs: Added per-fetch logging for Bonn GeoJSON, Google Maps route fetches, weather fetch attempts, and outlier runs. Increased /logs API limit (default 1000, max 5000) and UI default to 2000 entries.
[1.22.13] - 2026-02-07
Fixed
- Linting: Migrated to flat ESLint config and updated lint script to work with ESLint 9 + Next 16.
- Build metadata: Skip DB-backed metadata/site-texts during production build phase to avoid EPERM errors when DB access is unavailable.
[1.22.12] - 2026-02-07
Changed
- GeoJSON fetch retries: Centralized Bonn + construction-site GeoJSON fetch retry/timeout logic into a shared helper to remove duplication and keep retry behavior consistent.
[1.22.11] - 2026-02-07
Fixed
- Scripts build path: Removed
rootDirrestriction and includedlib/**/*.tsin scripts build to avoid TS6059 in Coolify. Worker start commands point back toscripts/compiled/scripts/*.js.
[1.22.10] - 2026-02-06
Fixed
- Worker build output: Set
rootDirfor script compilation so compiled workers land inscripts/compiled/root. Updated worker start commands accordingly to prevent stale builds and missing weather fetches.
[1.22.9] - 2026-02-06
Changed
- Apple touch icon: Metadata now points to
/apple-touch-icon.png.
[1.22.8] - 2026-02-06
Changed
- Favicon metadata: Removed PNG fallback; all icon metadata now points to
/favicon.ico.
[1.22.7] - 2026-02-06
Added
- Favicon fallbacks: Added
/favicon.icoand/favicon-48x48.pngso Google and other crawlers can fetch a standard icon.
Changed
- Metadata icons: Root metadata now points to
/favicon.icowith PNG apple fallback.
[1.22.6] - 2026-02-06
Changed
- Umami: Added
data-do-not-track="true"to respect browser DNT settings (per tracker configuration).
[1.22.5] - 2026-02-06
Changed
- Umami: Explicitly set
data-exclude-search="false"so query params (UTM) are preserved client-side.
[1.22.4] - 2026-02-06
Added
- Worker console mirroring: Worker console output is now persisted into
worker_logs(configurable viaLOG_WORKER_CONSOLE=falseto disable)./logsshows up to 500 entries by default.
[1.22.3] - 2026-02-06
Added
- build:scripts: New npm script to compile worker scripts (
tsc -p tsconfig.scripts.json). Use in production builds.
Changed
- Docs: Build commands updated to include
npm run build:scripts(README, DEPLOYMENT, COOLIFY_SETUP, QUICKSTART).
[1.22.2] - 2026-02-06
Fixed
- Build (Coolify):
generateMetadata()now skips DB access whenDATABASE_URLis not set, preventing build-time errors/log spam. - Worker cron typing: Weather cron now uses wrapper callbacks (avoids node-cron type mismatch in scripts).
- Script compilation errors: Fixed several TS errors in scripts (null routeId guards,
hasBonnscope, SSL config typing, safe GOOGLE_MAPS_API_KEY handling).
[1.22.1] - 2026-02-06
Added
- Weather manual fetch: New one-shot script
npm run weather:onceto fetch Open-Meteo and insert a weather reading into the DB (useful for production backfill/debug).
Changed
- SEO metadata source: Root
<title>and<meta name="description">now come from site texts (seo_title,seo_description) via server-sidegenerateMetadata(). OpenGraph/Twitter metadata follow the same values. Client-side overrides on the homepage were removed. - robots.txt: Added disallow rules for
/admin,/api,/log,/errors-internaland set absolute sitemap URL.
[1.22.0] - 2026-02-06
Added
- Fehlerfall Karte/Routen: Sichtbare Fehlermeldung mit kurzer Erklärung und „Erneut versuchen“-Button, wenn Karte oder Routen-API fehlschlagen (statt stillem Hängen bei „wird geladen“).
- API /api/health: Health-Check für Monitoring mit Worker-Heartbeat:
lastRun,lastSuccess,errorCount24h(Frontend/Errors aus worker_logs). - 404/500 auf /logs: Sektion „Frontend-Fehler (404/5xx)“ auf der Worker-Logs-Seite: zeigt die letzten 50 vom Client gemeldeten Fehler gebündelt an; Intro-Text ergänzt.
- Typo: „Durchschnittsgeschwinigkeit“ wird bei der Auslieferung von site-texts automatisch zu „Durchschnittsgeschwindigkeit“ korrigiert (Farbgebung/Infobox).
- Meta/SEO: Wenn
seo_titleim Backend englisch ist (z. B. „Traffic Monitoring Dashboard - Bonn“), wird im Layout der deutsche Fallback-Titel verwendet – einheitlicher deutscher Titel für alle Kanäle. - Error Boundary (ChunkLoadError):
app/error.tsxfängt ChunkLoadError / dynamische Import-Fehler ab und zeigt „Seite konnte nicht geladen werden“ mit Buttons „Erneut versuchen“ und „Seite neu laden“.
[1.21.49] - 2026-02-06
Added
- site-texts im HTML (Crawler + Cache): Seitentexte werden serverseitig ins erste HTML eingebettet (
<script id="site-texts" type="application/json">im Root-Layout). Ein einfacher HTTP-GET kann die Texte nun ohne JavaScript auslesen. Nach Speicherung unter Admin → Texte (/admin#texts) wird das Layout perrevalidatePath('/', 'layout')invalidiert, sodass der nächste Abruf aktualisierte Texte liefert. Client nutzt die eingebetteten Texte zuerst (kein Extra-Request), Fallback auf/api/site-textsbei SPA-Navigation.
[1.21.48] - 2026-02-06
Changed
- site-texts / FAQ lazy load: FAQ-Texte werden nicht mehr auf der Startseite mitgeladen. API
GET /api/site-textsunterstützt optional?keys=key1,key2; Startseite fordert nurinfo_ueber,info_quellen_*,info_einschraenkungen,footer_ideasan; FAQ-Seite lädt nur bei Aufruf von/faqdie Keysfaq_itemsundfooter_ideas. Spart Ladezeit und Payload auf der Homepage.
[1.21.47] - 2026-02-06
Added
- db:migrate: Nach erfolgreicher Migration werden vorhandene Tabellen (inkl. materialisierte Views) mit Zeilenanzahl ausgegeben.
- FEATURE_PROPOSALS.md: Live-Site-Optimierungen (Besuch bundesstaustadt.de) ergänzt: Ladezustände/Skeletons, Filter auf Mobile, Meta/SEO, Barrierefreiheit, Fehlerfall Karte/Routen, Karten-Performance.
[1.21.46] - 2026-02-06
Fixed
- Deploy: /logs 404:
.gitignoreenthieltlogs/und ignorierte damitapp/logs/– die Worker-Logs-Seite wurde nie ins Repo committed. Geändert zu/logs/(nur Root-Ordner), sodassapp/logs/nun tracked wird.
[1.21.45] - 2026-02-06
Fixed
- Fahrtdauer (historisch) – Tages-Annotationen: Annotationen wurden teilweise außerhalb des sichtbaren Zeitraums angezeigt (z.B. „Großveranstaltung Innenstadt“ am 03.02. bei „Letzte 3 Tage“). API
getDayAnnotationsByDateRange: Einzel-Tage-Annotationen nur zurückgeben, wennstart_dateim Bereich liegt; bisher wurden auch Annotationen mitstart_datevor dem Bereich einbezogen. Chart: Kein Fallback mehr auf den nächsten Datenpunkt – Annotationen ohne Chart-Daten im Zeitraum werden nicht mehr angezeigt.
[1.21.44] - 2026-02-06
Added
- Worker-Logs: Kopier-Version-Umschalter (Tab-getrennt, alt→neu), Fehler rot hervorgehoben, Worker Start/End (▶️/⏹️), Frontend-Fehler (404, 500 etc.) werden automatisch geloggt via globalem Fetch-Wrapper und erscheinen unter /logs.
Changed
- Worker-Logs: Worker schreiben jetzt worker_start und worker_end Events; Frontend-Fehler (Kategorie frontend) erscheinen mit 🌐.
[1.21.43] - 2026-02-06
Added
- Worker-Logs (/logs): Front-facing Aktivitätslog der Daten-Worker. Zeigt Bonn, Google Maps, Wetter, Baustellen, Ausreißer, Fehler. Kategorien mit Emoji (🏛️ 🗺️ 🌤️ 📊 ❌ 🚧 🛣️). Logs werden sanitized (keine API-Keys, Tokens, Passwörter). Nicht in sitemap.xml (robots: noindex). Standalone-Fallback:
npm run logs:standalonestartet einen einfachen HTTP-Server (Port 3999 oder LOG_VIEWER_PORT), der HTML aus der Datenbank liefert – funktioniert auch wenn Next.js nicht baut.
Changed
- Migration: Neue Tabelle
worker_logs(category, message, details_json). Traffic-Worker und Bonn-Worker schreiben bei jedem Run Logs dorthin.
[1.21.42] - 2026-02-06
Fixed
- Deploy (build): TypeScript error in
app/api/auth/change-password/route.ts:requireAuthexpectsNextRequestbut the route usedRequest. The handler parameter type is nowNextRequest, resolving the build failure during deployment.
[1.21.41] - 2026-02-06
Fixed
- Build (Coolify): JWT_SECRET missing during Next.js build no longer hard-fails the build; the secret is enforced at runtime while emitting a build-time warning.
- Admin password in production: Changing
DEFAULT_ADMIN_PASSWORDin the environment had no effect because the migration only created the admin user once and never updated the password. The migration now updates the existing admin user’s password whenDEFAULT_ADMIN_PASSWORDis set. Coolify: Rolling updates do not run the migration; to apply a new password, set the env var and run One-time Command →npm run db:migrateonce. Documented in COOLIFY_SETUP.md and DEPLOYMENT.md.
- worker:bonn:dev:
npm run worker:bonn:devfailed with "DATABASE_URL environment variable is not set". The Bonn worker now loads.env.localfirst, then.env(same asworker:dev/ traffic-worker), so local development works when DATABASE_URL is in.env.local. - Site texts: Placeholder "hä?" in
info_quellenremoved: GET /api/site-texts returns empty string for that key when value contains "hä?", and migration clears such values in the database. - Dashboard (/): Hard-coded "Informationen" h2 heading removed from the info section on the homepage.
- Weather: Weather (Bonn, Open-Meteo) was only fetched by the main traffic worker (
worker:start). If you ran only the Bonn worker (worker:bonn:start), no weather was fetched. The Bonn worker now also fetches and stores weather (same 4‑hour slots; cron at 00:00, 04:00, 08:00, 12:00, 16:00, 20:00). SetTZ=Europe/Berlinso slots match Bonn time.
Changed
- db:migrate logs: Migration output is clearer: on success, "Migration completed successfully."; on failure, "Migration failed. Error occurred in: <step>" with the failing step name (e.g. "schema (tables, indexes, columns)" or "admin user") and the error message. Failed runs exit with code 1.
Added
- Admin: Passwort ändern: Im Admin-Header neben „Logout“ gibt es jetzt einen Button „Passwort ändern“. Ein Modal fragt nach aktuellem Passwort, neuem Passwort und Wiederholung (min. 8 Zeichen), validiert und aktualisiert das Passwort in der Datenbank. API:
POST /api/auth/change-password(auth + CSRF erforderlich).
[1.21.40] - 2026-02-06
Fixed
- Admin Backup Import: TypeScript build failure fixed by making error extraction type-safe when the API response shape doesn't include
error/detailsfields.
[1.21.39] - 2026-02-06
Fixed
- Tagesvergleich (/tagesvergleich): Der Graph „Tagesverlauf (Ø Fahrtdauer pro Stunde)“ zeigt wieder Daten an. Ursache waren mögliche NaN-Werte aus der API (parseFloat bei ungültigen DB-Werten). Backend (day-comparison-service) nutzt jetzt
safeFloat()(null statt NaN), Frontend filtert Chart-Werte und Baseline defensiv (null/endliche Zahlen).
Added
- Backup Export/Import (vollständig zu db:migrate): Alle Tabellen aus der Migration sind jetzt im Backup enthalten: zusätzlich zu den bisherigen werden exportiert/importiert: api_fetch_settings (Zeitfenster, Polling-Intervall), activity_logs (Audit-Log), bonn_geocode_cache (Reverse-Geocode-Cache), bonn_strecke_readings (Verfügbarkeit pro Strecke/Timestamp, max. 100.000 Zeilen). Datenvergleich (
datenvergleich_config) war bereits enthalten und bleibt unverändert.
Changed
- Docs – Umami: Umami Analytics (
NEXT_PUBLIC_UMAMI_WEBSITE_ID) in README (Setup: Umami optional), DEPLOYMENT.md (Env-Tabelle), COOLIFY_SETUP.md (Optional-Block) und DEPLOYMENT_TROUBLESHOOTING.md (Environment-Checkliste) ergänzt.
- Backup Wipe (Gefahrenzone): Beim „Alle Daten löschen“ werden nun alle per Backup wiederherstellbaren Tabellen geleert: zusätzlich datenvergleich_config, api_fetch_settings, activity_logs, bonn_geocode_cache, bonn_strecke_readings. Reihenfolge der Löschung berücksichtigt Foreign Keys (datenvergleich_config vor routes).
[1.21.38] - 2026-02-06
Added
- CSP via proxy: Content-Security-Policy is applied via
proxy.ts(Next.js 16 proxy convention). API and static assets are excluded from the matcher. No separate middleware file – Next.js 16 uses proxy only.
Changed
- CSP (proxy.ts): In production,
style-srcnow includes'unsafe-inline'so Leaflet markers (createIcon) and other third-party inline styles are no longer blocked. Script-src remains nonce-based. - Sitemap:
dynamic = 'force-dynamic'andrevalidate = 0so the sitemap is generated on each request and reflects current routes after import. Structure clarified (static pages + route entries with lastModified from route when available). - FEATURE_PROPOSALS.md: After-deployment errors section marked as resolved; note on public vs. protected APIs.
Fixed
- /api/route-geometry 400: Request body is validated defensively (coordinates accepted as numbers or strings); invalid JSON returns 400 with a clear message. New GET
?routeId=…returns cached geometry only (no external routing API call). Map component handles non-ok responses without breaking (fallback to straight line).
[1.21.37] - 2026-02-06
Added
- DEPLOYMENT_TROUBLESHOOTING.md: New guide for deployment conflicts (e.g. https://bundesstaustadt.de): checklist (SITE_URL, API JSON, admin login), common symptoms (wrong sitemap domain, admin stuck on Loading, "Unexpected token '<'", cookies), and optional CSP note.
- DEPLOYMENT.md: Post-deployment verification section and link to DEPLOYMENT_TROUBLESHOOTING.md.
Changed
- Sitemap: Uses
SITE_URLwith fallbackhttps://bundesstaustadt.de; logs a warning whenSITE_URLis not set so production can be configured correctly. - Auth cookie: Login response sets
path: '/'explicitly on the auth-token cookie for reliable behavior behind reverse proxies.
Fixed
- Admin panel: When
/api/auth/mefails or returns non-JSON (e.g. HTML error page), the admin no longer stays on "Loading…" indefinitely. A 15s timeout and safe response handling were added; on timeout or invalid response the user sees an error message and "Erneut versuchen" / "Zur Anmeldung" instead of hanging.
[1.21.36] - 2026-02-06
Added
- Backup Export/Import: Baustellen (construction sites) inkl. Archiv werden ins Backup aufgenommen und beim Import wiederhergestellt (
construction_sites). - Backup Export/Import: Datenvergleich-Beschreibungstexte: Konfiguration der Seite „Datenvergleich“ (Routenpaare, Info-Texte,
description_text) wird exportiert und importiert (datenvergleich_config). - Gefahrenzone (Alle Daten löschen): Beim Wipe werden nun auch alle Baustellen gelöscht.
[1.21.35] - 2026-02-06
Added
- Admin Export/Import (/admin#export-import): Backup JSON now includes a version field (BACKUP_FORMAT_VERSION = 1). Import checks version and rejects backups from newer app versions; legacy backups without version are treated as version 1.
- Admin Export/Import: Route geometry is included in backup export and restored on import (route_geometry JSONB).
- Admin Export/Import: Danger zone: "Alle Daten löschen" – deletes all routes, mappings, traffic readings, site texts, alltime stats, weather, and day annotations. Deletion is only possible after typing exactly: I am absolutely sure!
- Deployment: DEPLOYMENT.md and COOLIFY_SETUP.md state that production deployments must use
NODE_ENV=productionand production tag/branch (not development).
Fixed
- Admin Bonn routes: "Unexpected token '<'" when fetching Bonn routes was caused by the server returning HTML (e.g. login page) instead of JSON. The admin page now uses
credentials: 'include'for/api/bonn-routesand handles non-JSON responses (content-type check and safe parse); shows a clear message if the server returns a page instead of data. - Admin Import: Improved error handling: invalid JSON in the file is caught with a clear message; invalid JSON in the server response (e.g. HTML error page) is caught and reported instead of throwing "is not valid JSON".
- Backup import API: Invalid JSON body returns 400 with a JSON error message instead of 500.
[1.21.34] - 2026-02-03
Added
- Admin Baustellen (/admin#constructions): Beim Klick auf eine Baustelle (in der Tabelle oder auf der Karte) wird der Punkt auf der Karte hervorgehoben (größerer Marker, Amber-Farbe, dunkler Rand) und die Karte bewegt sich dorthin (panTo mit Animation, Zoom mindestens 15).
[1.21.33] - 2026-02-03
Added
- Admin Baustellen (/admin#constructions): Tabelle „Aktuelle Baustellen“ ist nach Baustelle ID, Von, Bis und Status sortierbar. Klick auf die Spaltenüberschrift wählt die Sortierung; erneuter Klick wechselt zwischen auf- und absteigend. Sortierrichtung wird durch ↑/↓ angezeigt.
[1.21.32] - 2026-02-03
Fixed
- Admin Texte (/admin#texts): Runtime
ReactDOM.findDOMNode is not a functionbeim Öffnen der Texte-Sektion. React 18 hatfindDOMNodeentfernt; react-quill nutzt es noch intern. Polyfill inlib/findDOMNodePolyfill.tsergänzt: stelltfindDOMNodewieder her (für DOM-Knoten, Ref-Objekte und Legacy-Komponenteninstanzen). Wird beim Laden der Admin-Seite vor ReactQuill eingebunden.
[1.21.31] - 2026-02-03
Fixed
- Admin Build:
getCsrfHeaderswas used in three standalone components (ApiFetchSettingsSection,DayAnnotationsAdminSection,ConstructionSitesAdminSection) without callinguseAdminAuth(), causing TypeScript "Cannot find name 'getCsrfHeaders'" and build failure. Each of these components now callsuseAdminAuth()and destructuresgetCsrfHeadersat the top of the component.
[1.21.30] - 2026-02-03
Fixed
- Gleitender Durchschnitt der Abweichung: 95% und 99% Konfidenzlinien: Im Chart wurden bisher nur die 95%-Konfidenzgrenzen (rot) und der Mittelwert gezeichnet; die 99%-Konfidenzintervalle waren nur in den Textboxen angegeben. Beide Vergleiche zeichnen jetzt 95%- und 99%-Referenzlinien im Graphen: 95% rot (gestrichelt 2 2), 99% amber (#f59e0b, gestrichelt 3 3), mit Labels „95%“ bzw. „99%“. ReferenceLines werden als Array direkter Kinder gerendert (kein Fragment), damit Recharts sie zuverlässig anzeigt.
[1.21.29] - 2026-02-03
Fixed
- Datenvergleich Streuung Vergleich 1: Scatter-Punkte sichtbar: Scatter-Punkte für den ersten Vergleich wurden weiterhin nicht angezeigt, obwohl Daten und Y-Domain stimmten. Ursache: Recharts
renderByOrderverarbeitet nur bekannte Kind-Typen (Scatter, ReferenceLine, …). Ein React-Fragment<>…</>hat keinen Handler und wird übersprungen – die darin enthaltenen Scatter-Komponenten wurden nie gerendert. Vergleich 2 hatte keine Fragment-Umhüllung und zeigte Punkte. Anpassung: Streuung Vergleich 1 nutzt jetzt dieselbe Struktur wie Vergleich 2 – alle Scatter-Komponenten sind direkte Kinder des ScatterChart (kein Fragment). Erstes Scatter erhältdata={… ? scatterData : scatterWithinBoth}als Fallback, wenn alle drei Kategorien leer sind.
[1.21.28] - 2026-02-03
Fixed
- Datenvergleich Streuung Vergleich 1: Scatter-Punkte & Y-Achse: Scatter-Punkte für den ersten Vergleich erschienen nicht, und die Y-Achse zeigte einen ungewöhnlichen unteren Wert (z. B. 4,27 min statt 0). Ursache: Die Y-Domain war
[yDomainLo, SCATTER_MAX]mit datenabhängigemyDomainLo. Punkte mit y < yDomainLo lagen außerhalb des sichtbaren Bereichs und wurden durch den Chart-ClipPath abgeschnitten. Behebung: Y-Domain für beide Streuungs-Vergleiche auf festes[0, SCATTER_MAX]gesetzt; Y-Achse beginnt damit immer bei 0 min und alle Punkte im Bereich 0–45 min (bzw. 0–20 min bei Vergleich 2) werden gezeichnet. Berechnung vonyDomainLo/lo/minValfür die Streuung entfernt.
[1.21.27] - 2026-02-03
Fixed
- Datenvergleich Streuung: Relative Abweichung & Standardabweichung Linien sichtbar: Die blauen (Relative Abweichung) und roten (±3σ) Referenzlinien erschienen im SVG nicht, obwohl die Bedingungen erfüllt waren. Ursache: Recharts
ReferenceLinenutzt standardmäßigifOverflow="discard". Liegt ein Segment-Endpunkt außerhalb des Achsenbereichs (z. B. (0,0) wenn die Y-Domain beiyDomainLo > 0beginnt), gibtgetEndPointsnull zurück und die Linie wird nicht gezeichnet. Alle betroffenen ReferenceLines (Diagonale, beide blauen und beide roten) haben jetztifOverflow="extendDomain", sodass sie auch bei Randfällen gezeichnet werden. Zusätzlich werden die blauen/roten Linien als Array direkter Kinder zurückgegeben (statt Fragment) für zuverlässiges Rendering in beiden Vergleichen.
[1.21.26] - 2026-02-03
Fixed
- Datenvergleich Streuung: Relative Abweichung & ±3σ immer sichtbar: Die blauen Referenzlinien (Relative Abweichung) und roten Linien (±3× Standardabweichung) wurden nur gezeichnet, wenn
pctAbw > 0bzw.stdAbwDiff > 0. Bei kleinem oder null Wert erschienen sie nicht. Beide Vergleiche zeichnen die Linien jetzt immer, sobald Vergleichsstatistiken vorhanden sind; bei 0 fallen sie mit der Diagonale zusammen. Fallback fürfactorUpper <= 0und finite-Check für Koordinaten ergänzt.
[1.21.25] - 2026-02-03
Fixed
- Datenvergleich Streuung Vergleich 1: Scatter-Punkte für den ersten Vergleich wurden nicht gerendert (nur für Vergleich 2). Anpassungen: Streuung Vergleich 1 nutzt dieselbe Struktur wie Vergleich 2 – Scatter-Arrays (scatterWithinBoth, scatterOnlyOutsidePct) werden im gleichen Scope berechnet und Scatter-Komponenten direkt gerendert (kein inneres IIFE). Fallback: Wenn die Aufteilung leer ist, wird ein einzelner Scatter mit allen scatterData gerendert. API/Config unverändert (GET /api/datenvergleich/config und GET /api/traffic-readings/[routeId] sind ungeschützt und liefern Daten für beide Vergleiche).
[1.21.24] - 2026-02-03
Fixed
- Datenvergleich Streuung: Scatter-Punkte sichtbar: Die Streuungs-Charts zeigten nur Achsen und Referenzlinien, keine Datenpunkte. Ursache: Zu viele ReferenceLine-Kinder (~200 für die Trendlinie) führten dazu, dass Recharts die nachfolgenden Scatter-Komponenten nicht mehr gerendert hat. Anpassungen: Scatter-Komponenten werden jetzt direkt nach der Diagonale (vor der Trendlinie) gerendert; Trendlinie mit 24 statt 200 Segmenten; gleiche Änderung für beide Vergleiche.
[1.21.23] - 2026-02-03
Fixed
- Datenvergleich: Gleitender Durchschnitt der Abweichung & Streuung: Both charts are fixed so they render correctly again. Moving average: filter out non-finite durations and deviations; only push entries with finite
avgDeviationand validdate; XAxistype="category", YAxistype="number"withdomain={['auto','auto']}; ReferenceLines for confidence intervals only when all values are finite. Streuung: scatter data filtered to finite x/y; domain calculation guarded (finite vals, safeyDomainLo);std3derived fromcomparisonStats.stdAbwDiffonly when finite. Same guards applied to the second comparison section.
[1.21.22] - 2026-02-03
Fixed
- RouteStatsModal Fahrtdauer chart: ReferenceLine for gap markers now always uses the category value (
gap.date) instead ofgap.indexwhen there are many data points. The XAxis istype="category"withdataKey="date", so passing a numeric index caused Recharts to resolve to NaN (x1 error) with 15‑min interval data.
[1.21.21] - 2026-02-03
Fixed
- proxy.ts config: Removed
as constfrom matchermissingentries so Next.js can statically parsetype: 'header'(fixes "matcher[0].missing[0].type must be one of: 'header', 'cookie', 'query', 'host'").
[1.21.20] - 2026-02-03
Security – Reverse tabnabbing
- window.open: Admin bulk CSV export now uses
window.open(url, '_blank', 'noopener,noreferrer')so the new tab cannot accesswindow.opener. Alltarget="_blank"links in the repo already hadrel="noopener noreferrer"; no anchor changes needed.
[1.21.19] - 2026-02-03
Security – Strict CSP in production
- CSP: Production now uses a strict Content-Security-Policy without
unsafe-inlineorunsafe-eval. A per-request nonce is generated inproxy.ts;script-srcandstyle-srcuse'nonce-{value}'and'strict-dynamic'(scripts). Umami remains allowed viahttps://cloud.umami.is. - Development: CSP stays relaxed (
unsafe-inline,unsafe-eval) for HMR and dev tools. - proxy.ts: Next.js 16 proxy sets CSP and
x-nonceon the request so Next.js can inject the nonce into framework scripts; root layout passes nonce toUmamiScriptfor the analytics script. - next.config.js: CSP header removed from
headers(); CSP is set only in proxy.
[1.21.18] - 2026-02-03
Security – CSRF protection for cookie-based auth
- Double-submit CSRF: State-changing requests (POST, PATCH, DELETE, PUT) that use cookie auth now require a valid CSRF token.
requireAuthinlib/auth-middleware.tschecksX-CSRF-Tokenheader against thecsrf-tokencookie when auth is from cookie; Bearer auth is unchanged and does not require CSRF. - Token lifecycle: Login sets a cryptographically random
csrf-tokencookie (httpOnly, SameSite=Strict) and returnscsrfTokenin the JSON body.GET /api/auth/csrf(authenticated) issues a fresh token and sets the cookie. Logout deletes bothauth-tokenandcsrf-token. - Admin: Layout fetches CSRF token after auth and provides
getCsrfHeaders()viaAdminAuthContext. All mutating API calls from the admin page sendX-CSRF-Tokenandcredentials: 'include'.
[1.21.17] - 2026-02-03
Security
- XSS protection via sanitizeHtml(): All
dangerouslySetInnerHTMLusages now consistently usesanitizeHtml()(DOMPurify). Fixed 9 unsanitized instances in:app/datenvergleich/page.tsx,app/errors/page.tsx,app/faq/page.tsx,components/RouteStatsModal/index.tsx.
[1.21.16] - 2026-02-03
Security
- JWT token removed from response body: Login endpoint (
/api/auth/login) no longer returns the JWT token in the JSON response. Token is now sent only via httpOnly cookie, preventing exposure to frontend JavaScript and XSS attacks.
[1.21.15] - 2026-02-03
Security
- Next.js 16 upgrade: Updated from
^14.0.4to^16.1.6(latest) to address all known vulnerabilities including react2shell and PPR endpoint issues. Also updatedeslintto^9.26.0andeslint-config-nextto^16.1.6. - Fail-fast for secrets: Application now throws on startup if
JWT_SECRETis missing in production (lib/auth.ts). Migration script throws ifDEFAULT_ADMIN_PASSWORDis missing in production (scripts/migrate.js). Insecure defaults are only allowed in development mode.
Changed
- Outlier detection refactor: Replaced subprocess spawning with direct function import (
lib/outlier-detection.ts). This is cleaner, more efficient, and compatible with Turbopack. - Dev-only defaults: JWT secret and admin password fallbacks are now clearly marked as dev-only and will never be used in production.
- Next.js 15/16 API migration: Updated all dynamic API routes to use async
params(Promise-based). Affected routes:/api/routes/[id],/api/routes/[id]/data-sources,/api/routes/[id]/data-sources/[mappingId],/api/traffic-readings/[routeId]. - next.config.js: Removed deprecated
swcMinifyoption.
Known Issues
- react-quill XSS (GHSA-4943-9vgg-gr5r):
quill <=1.3.7has a moderate XSS vulnerability. No patched react-quill version available yet. Tracked inFEATURE_PROPOSALS.md.
[1.21.14] - 2026-02-03
Security
- Next.js upgrade: Updated from
^14.0.4to^15.1.9to address known vulnerabilities (react2shell and related CVEs). Also updatedeslint-config-nextto match. - Fail-fast for secrets: Application now throws on startup if
JWT_SECRETis missing in production (lib/auth.ts). Migration script throws ifDEFAULT_ADMIN_PASSWORDis missing in production (scripts/migrate.js). Insecure defaults are only allowed in development mode.
Changed
- Dev-only defaults: JWT secret and admin password fallbacks are now clearly marked as dev-only and will never be used in production.
- Next.js 15 API migration: Updated all dynamic API routes to use async
params(Promise-based) as required by Next.js 15. Affected routes:/api/routes/[id],/api/routes/[id]/data-sources,/api/routes/[id]/data-sources/[mappingId],/api/traffic-readings/[routeId]. - next.config.js: Removed deprecated
swcMinifyoption (now default in Next.js 15).
[1.21.13] - 2026-02-03
Security
- Command injection fix: Replaced
exec()withexecFile()inapp/api/traffic-readings/route.tsto prevent potential command injection.execFile()does not invoke a shell and passes arguments as an array, eliminating shell metacharacter interpretation.
Changed
- Security audit: Added VibeSec security findings to
FEATURE_PROPOSALS.mdwith 17 issues categorized by severity (CRITICAL/HIGH/MEDIUM/LOW).
[1.21.12] - 2026-02-03
Added
- Feature proposals: Added a new recommendations section with optimization, UX, and reliability ideas in
FEATURE_PROPOSALS.md.
[1.21.11] - 2026-02-02
Added
- Weather fetch retry: If Open-Meteo fetch/store fails (e.g. HTTP 504), the traffic worker schedules a single retry in 15 minutes. Same behaviour for
workerandworker:dev; no further retries after the first retry.
[1.21.10] - 2026-02-02
Changed
- Changelog: Entries 1.21.2–1.21.9 rewritten in English.
- Umami: Website ID moved to env var
NEXT_PUBLIC_UMAMI_WEBSITE_ID. Set in.env.localonly (do not commit) so forks and other deployments do not use your analytics dashboard; leave unset to disable. Documented in README and.env.example.
[1.21.9] - 2026-02-02
Fixed
- CSP: Umami script from cloud.umami.is was blocked by the Content-Security-Policy. Added
https://cloud.umami.istoscript-srcandconnect-src; addedhttps://api-gateway.umami.devtoconnect-src(API for sending analytics data).
[1.21.8] - 2026-02-02
Changed – Umami loads on every visit
- Umami tracker: The Umami script now loads on every visit (no longer only when "Accept all" is clicked). Umami does not set cookies and collects only anonymised data; GDPR-compliant without prior consent.
- Cookie banner & privacy page: Copy updated to describe Umami as "loaded on every visit", "no cookies", "GDPR-compliant".
[1.21.7] - 2026-02-02
Added – Umami web analytics
- Umami tracker: Script (cloud.umami.is) with website ID is included via the global layout component.
- Cookie banner: Umami added to the list of external services (provider, purpose, cookies/data, link to Umami privacy policy).
- Privacy page (/datenschutz): Umami listed as an external service with provider, purpose, data notice, and link to https://umami.is/privacy.
[1.21.6] - 2026-02-02
Added – Day comparison enhancements
- Share button: "Link kopieren" button with green popup feedback to copy the comparison URL.
- Rush hour shading: Subtle orange background shading for rush hour periods (7–9, 15–18) in the hourly comparison chart, with legend indicator.
- Route filter: Multi-select route filter dropdown to focus comparison on specific routes. Summary stats, table, and CSV export all respect the filter.
- Trend sparklines: Mini sparkline charts in summary cards showing hourly patterns for Tag A/Tag B (days mode) or per-week trends (weekday mode).
Changed
- Link kopieren feedback (dashboard): Replaced browser
alert()with a green popup notification above the button that auto-dismisses after 2 seconds.
[1.21.5] - 2026-02-02
Fixed
- Admin single-route CSV export: Single-route CSV export now uses
fetch+ blob download instead of a direct<a href download>. On JSON error response (e.g. 500), an alert shows the error message instead of "Page unavailable"; console errors remain visible. - Export API 500: Single- and multi-route export use safe timestamp output (
toSafeIso/toSafeDateStr). Invalid or missingtimestampvalues in readings no longer causetoISOString()to throw and return 500; affected rows are exported with an empty date.
[1.21.4] - 2026-02-02
Added – Admin bulk actions (FEATURE_PROPOSALS 5.2)
- Routes list: Checkbox per row; header checkbox "All on this page" (indeterminate when partially selected). State:
selectedRouteIds. - Bulk bar: When at least one route is selected: bar with "X selected", buttons Activate, Deactivate, Export CSV, Clear selection. Success/error message with
aria-live, shown for 5s after action. - Confirmation: "Deactivate" shows confirm dialog "Really deactivate X route(s)?"
- API:
PATCH /api/routes/bulkwith body{ routeIds: number[], active: boolean };lib/routes-service.updateRoutesBulk(single transaction); activity log for bulk. Export uses existing/api/routes/export?routeIds=…. - Accessibility: Checkboxes with
<label>/aria-label, bulk bar withrole="status"andaria-live="polite".
[1.21.3] - 2026-02-02
Changed – WCAG AA contrast (FEATURE_PROPOSALS 5.1.3)
- Text: Light theme
--text-tertiaryfrom #71717a to #5c5c66 (≥4.5:1 on #fafafa/#fff); dark from #64748b to #94a3b8 (≥4.5:1 on #0f172a). - Status colours:
--status-fast/#059669,--status-slow/#dc2626 andSTATUS_COLORSaligned. Orange (moderate) kept at #d97706 (contrast on white below 4.5:1, visual preference). - Documentation: globals.css design system updated with WCAG-AA note and contrast key pairs.
[1.21.2] - 2026-02-02
Added – Accessibility (WCAG AA, FEATURE_PROPOSALS 5.1)
- Skip link: "Zum Inhalt springen" in root layout; all
<main>withid="main-content"for skip target. - Focus: Clear focus rings for inputs, selects, timeline slider, and skip link (
:focus-visible) inglobals.css. - Modals: RouteStatsModal sets focus on the close button when opened; on close (Escape or click), focus returns to the triggering card (dashboard) or row (day comparison).
- Keyboard: Clickable route cards and day-comparison table rows activatable with Enter/Space;
role="button",tabIndex={0},aria-labelwhere needed. - Screen reader:
<nav aria-label="Hauptnavigation">on all pages with header; toast witharia-live="polite"; chart containers in RouteStatsModal and day comparison witharia-label(short description).
[1.21.1] - 2026-02-02
Changed
- Gesamte Verzögerung: Kurzer Explainer ergänzt („Differenz zur schnellsten Fahrtzeit pro Route × Messungen, summiert über alle Routen“).
- Gesamte Verzögerung: Mittlere Kennzahl „Ø X min pro Route“ ergänzt – Gesamte Verzögerung geteilt durch die Anzahl der im Zeitraum erfassten Routen (Vergleich einzelner Tage: pro Tag A/B; Vergleich Wochentage: pro Woche mit
routesWithData).
[1.21.0] - 2026-02-02
Feature - Tagesvergleich: Zwei Modi (einzelne Tage / Wochentage)
Added
- Modus-Slider: Oben auf der Tagesvergleich-Seite wählbar: „Vergleich einzelner Tage“ und „Vergleich Wochentage“.
- Vergleich einzelner Tage: Unverändert – Tag A / Tag B, Stunden, Heute vs. Gestern, Gestern vs. Vorgestern, Gestern vs. Vor 1 Woche, „Vergleiche mit“ (annotierter Tag). Option „Nur gleicher Wochentag“ entfernt.
- Vergleich Wochentage: Dropdown Wochentag (Mo–So), Dropdown „Anzahl Wochen“ (2, 3, 4 Standard, 6, 8). Zeigt eine Linie pro Woche im Tagesverlauf-Chart (z. B. Mo 03.02., Mo 27.01., …), jede Woche eigene Farbe. Ø Fahrtdauer (alle Routen) und Gesamte Verzögerung zeigen pro Woche einen Eintrag (bis zu 8 Wochen), Farb-Swatch wie im Chart. Tabelle weiterhin über alle gewählten Wochen aggregiert.
- API
GET /api/day-comparison/weekday?dow=0–6&weeks=1–12(dow: 0=So, 1=Mo, …, 6=Sa). Optional:hoursFrom,hoursTo. - lib/day-comparison-service:
getWeekdayAggregation(dow, weeks, options?)liefertWeekdaySummaryResultmitweekLabels,weekDates,summaryByWeek(pro Woche: label, date, avgDuration, totalDelay),hourlyBreakdown(ein Eintrag pro Stunde, pro Wochentag-Label ein Wert).
Changed
- URL-Hash: Beide Modi unterstützt –
#mode=days&a=ddmmyyyy&b=ddmmyyyyund#mode=weekday&dow=1&weeks=4. Hash wird beim Wechsel und bei Änderung der Parameter aktualisiert.
[1.20.4] - 2026-02-02
Changed - Tagesvergleich UX
- Tag A / Tag B Farben: Einheitliche Farbcodierung – Tag A rot (#ef4444), Tag B blau (#3b82f6) in Graph, Legende, Karten, Tabelle und Übersichtskarten.
- Wetter-Overlay: Mehr Abstand (Chart-Margins top 44 / bottom 32), Wetter-Labels farbcodiert (Tag A rot, Tag B blau), etwas größere Schrift und Offset.
- Nur gleicher Wochentag: Logisch platziert unter „Vergleichsoptionen“ mit kurzer Erklärung „(nur Messungen mit gleichem Wochentag wie gewählter Tag)“; getrennt von den Stunden-Presets.
- Kartenansicht Differenz: Farben korrigiert – Grün = besser als Tag A, Rot = (viel) schlechter als Tag A. Beschreibung angepasst.
[1.20.3] - 2026-02-02
Feature - Tagesvergleich Erweiterungen (FEATURE_PROPOSALS 1.1)
Added
- Stunden-Filter: Compare only certain hours – presets "Ganzer Tag", "06:00–20:00", "Rush 7–9", "Rush 15–18". API supports
hoursFromandhoursToquery params. - Wochentag-Hinweis: Weekday shown next to each date (e.g. "Mo 02.02.") in chart legend, map caption, and under date inputs for weekend vs weekday clarity.
- Messungen-Spalte: Top-10 table now has a "Messungen" column (Tag A / Tag B data point counts) for comparison trustworthiness.
- Wetter-Overlay im Chart: Weather for both days (☀️/🌧️ + °C) shown as ReferenceLines above (Tag A) and below (Tag B) the Tagesverlauf chart, similar to RouteStatsModal.
- Nur gleicher Wochentag: Option "Nur gleicher Wochentag" – filters readings to same weekday (DOW) on both sides to exclude weekend vs weekday mix.
Changed
- API
GET /api/day-comparison: Optional query paramshoursFrom,hoursTo(0–23),sameWeekdayOnly=true. - lib/day-comparison-service:
compareDays(..., options?)withDayComparisonOptions(hoursFrom,hoursTo,sameWeekdayOnly).
[1.20.2] - 2026-02-02
Fixed
- Turbopack: Leaflet CSS now imported at top level (
import 'leaflet/dist/leaflet.css') so it resolves correctly withnext dev --turbo(was:require()inside useEffect caused "could not resolve" error)
Changed
- Dev: Use Turbopack (
next dev --turbo) for faster compilation and Hot Reload - Dev:
optimizePackageImportsfor date-fns and recharts to reduce bundle compile time - Dev: DB pool log only once per process to avoid duplicate messages
[1.20.1] - 2026-02-02
Fixed
- ESLint: Resolved
react-hooks/exhaustive-depsin admin layout (checkAuth wrapped in useCallback) - ESLint: Replaced
<img>with<Image />in datenvergleich and faq pages - ESLint: Added RUSH_HOURS deps to RouteStatsModal useMemo hooks
[1.20.0] - 2026-02-02
Feature - Tagesvergleich (Day Comparison)
Added
- Tagesvergleich page (
/tagesvergleich): Compare two days or date ranges across all routes - Tag A vs Tag B with date pickers; Einzeltag or Zeitraum (3 Tage) per side
- Presets: Heute vs. Gestern, Gestern vs. Vorgestern, Gestern vs. Vor 1 Woche
- Network overview: Ø Fahrtdauer, Gesamte Verzögerung, Betroffene Routen (>10% Stau), Peak-Stunde
- Tagesverlauf chart (Ø Fahrtdauer pro Stunde) with baseline (dashed)
- Top 10 most affected routes table
- Map view toggle: Tag A / Tag B / Differenz
- Day Annotations: Labels for annotated days; "Vergleiche mit annotiertem Tag" suggestions
- API:
GET /api/day-comparison?dateA=&dateB=&dateAEnd=&dateBEnd=(public) - URL hash: Shareable links via
#a=01022026&b=02022026(ddMMyyyy format)
Changed
- Navigation: Tagesvergleich link added to main nav (Dashboard, FAQ, Datenvergleich, Fehler)
- Unterschied: Sign reversed – positive % when Tag B takes longer than Tag A
- Vergleiche mit: Dropdown with dates (dd.mm.yy) instead of buttons for many annotations
- Tagesvergleich: Preset buttons (Heute vs. Gestern, etc.) highlight when that preset is active
- Tagesvergleich: Top text box editable in Admin → Texte (info_tagesvergleich)
- Admin Texte: Sections (Datenvergleich, Texte & SEO, Tagesvergleich, Fehlerseite) as collapsible accordions
- Date picker:
color-schemefor dark-mode native calendar popup
[1.19.4] - 2026-02-02
Performance
- Removed date-fns from main page: Replaced
date-fnsformatting with nativeIntl.DateTimeFormatfor timeline labels. Reduces main page First Load from 132 KB to 123 KB (-9 KB / -7%). - Bundle analyzer: Added
@next/bundle-analyzerfor future bundle analysis (ANALYZE=true npm run build).
[1.19.3] - 2026-02-02
Performance
- New composite index: Added
idx_traffic_readings_route_status_durationon(route_id, data_status, duration_seconds)for faster aggregation queries with status filtering. Runnpm run db:migrateto apply. - FILTER aggregates: Replaced 3 separate COUNT subqueries with single JOIN using
COUNT(*) FILTER (WHERE ...)ingetAllRoutesWithDataPoints(). Reduces database round-trips from 3N to 1. - Removed on-demand delay_matrix calculation: Delay matrix is now only fetched from pre-computed
route_alltime_statstable. No longer runs expensive PERCENTILE_CONT query on every stats request. Runnpm run worker:alltime-statsto populate missing matrices.
[1.19.2] - 2026-02-01
Fixed
- Weather strip (15-min data): Weather emoji + °C in "Fahrtdauer (historisch)" now displays correctly when using raw 15-minute data (Ergebnisse glätten OFF). Previously it showed NaN for ReferenceLine positions. The fix uses date strings instead of indices for Recharts category axis resolution.
- Build: day-annotations-service
rowCountnull check for TypeScript strict mode.
[1.19.1] - 2026-01-30
Changed
- Weather fetch schedule: Worker and worker:dev now fetch weather at fixed hours 00:00, 04:00, 08:00, 12:00, 16:00, 20:00 (cron
0 0,4,8,12,16,20 * * *) in server local time. UseTZ=Europe/Berlinso Bonn slots align. Stored slot timestamp uses local time (4-hour alignment) so one reading per slot stays consistent.
[1.19.0] - 2026-01-30
Feature - Weather and Day Annotations
Added
- Weather (Bonn, Open-Meteo): Every 4 hours the worker stores current weather (condition + temperature °C) for Bonn. New table
weather_readings; data in admin full export and in single-route rohdaten CSV (weather_condition,weather_temp_c). In the stats modal, a weather strip above "Fahrtdauer (historisch)" shows emoji + °C at 4-hour intervals. - Day annotations: Admin section "Tages-Annotationen" (
/admin#annotations) to add/edit/delete annotations for arbitrary days (e.g. "Großveranstaltung Innenstadt", "Sperrung Endenicher Ei"). Each annotation has start date, optional "Multi-day event?" with end date, and label. Annotations are stored inday_annotations, included in backup export/import, and shown in the stats modal on the duration chart as vertical reference lines with labels. - API:
GET /api/weather?startDate=&endDate=(public),GET/POST /api/day-annotationsandGET/PATCH/DELETE /api/day-annotations/:id(POST/PATCH/DELETE admin-only).
Changed
- Worker: Runs weather fetch every 4 hours (cron
0 */4 * * *) and onRUN_ONCE=true. - Routes export (rohdaten CSV): Adds columns
weather_conditionandweather_temp_cper row (nearest 4h weather slot). - Backup export/import: Includes
weather_readingsandday_annotations.
[1.18.5] - 2026-01-30
Feature - Automatic Outlier Detection
Added
- Outlier Detection Service (
lib/outlier-detection-service.ts): New service for detecting statistical outliers in traffic readings - Daily Outlier Detection: Worker now runs outlier detection BEFORE updating all-time stats, ensuring outliers are excluded from statistics
- Conservative SPIKE Algorithm: Only flags isolated extreme readings that are clearly measurement errors, not sustained congestion
Algorithm Criteria (ALL must be met):
1. Duration > 5× median (extremely high compared to typical values) 2. Duration > 2× P99 (exceeds even the 99th percentile significantly) 3. Duration > 3× previous reading (sudden spike up) 4. Duration > 3× next reading (sudden drop back down) 5. Duration > median + 10 minutes (absolute threshold to avoid false positives on short routes)
Changed
- Worker: Renamed
maybeUpdateAllTimeStats()tomaybeRunDailyMaintenance()to reflect its expanded role - Outliers excluded from stats: Readings with
data_status = 'outlier'are automatically excluded from all statistics (same as errors) - Statistics Modal UI: Combined data quality alerts into a single unified alert under "Statistiken" showing:
- Datenpunkte mit Status "aktuell nicht ermittelbar" (rot markiert)
- Datenpunkte mit fehlerhaften Teildaten bei Multi-Routen (rot markiert)
- Datenpunkte als statistische Ausreißer erkannt (orange markiert)
[1.18.4] - 2026-01-30
Refactor - Traffic Worker Cleanup
Changed
- Worker output: Replaced verbose per-route logging with clean summary per data source:
stadtplan.bonn.de: successfully imported: X/Y. errors / nicht ermittelbar: ZGoogle Maps API: successfully imported: X/Y. errors / nicht ermittelbar: Z- Code quality: Extracted duplicated all-time stats update logic into shared
maybeUpdateAllTimeStats()function - Code quality: Simplified next-run time calculation with
getNextAlignedTime()andbuildCronSchedule()helpers - Code quality: Replaced runtime
require()with top-level imports - Code quality: Now uses
getPollingIntervalMinutes()from database settings
Fixed
- Double traffic reading bug: Fixed issue where some error paths created duplicate readings
- Removed unused imports: Cleaned up
formatExportTimestampand other unused code - Removed ineffective settings check: Removed hourly settings change detection that could only log but not reschedule
Updated
- package.json: Fixed compiled worker paths to match TypeScript output structure
[1.18.3] - 2026-01-29
Performance - Query Consolidation
Changed
- getTrafficStats: Consolidated 6+ separate database queries into a single optimized query using Common Table Expressions (CTEs) and conditional aggregation. This significantly reduces database round-trips and improves dashboard responsiveness.
- Cleanup: Removed unused private helper functions
calculateTrendTemporalandcalculateDelayFrequency.
[1.18.2] - 2026-01-29
Performance - Materialized Views
Added
- route_daily_stats Materialized View: Pre-aggregated daily statistics (avg, min, max, median, count) for all routes to optimize historical data analysis.
- Concurrent Refresh: Support for non-blocking statistics refresh.
Changed
- Workers:
traffic-worker.tsandalltime-stats-worker.tsnow refresh theroute_daily_statsmaterialized view daily.
[1.18.1] - 2026-01-29
Analysis - Gemini Recommendations
Added
- gemini.md: New file containing a comprehensive analysis of the codebase with recommendations for efficiency, design, and additional statistics.
[1.18.0] - 2026-01-29
Feature - Map Timeline (Last 24 Hours)
Added
- Timeline in map filters: In
map-section__filters, a "Zeitraum" slider lets users scroll through the last 24 hours of traffic data for all routes on the map. - Hourly granularity: Data is shown per hour (not per data point). Missing data for a given hour is interpolated from adjacent hours (previous/next hour average, or nearest hour).
- Lazy loading: Timeline data is only requested when the user moves the slider (no preload of 24 hours).
- Live vs. time display: When the slider is at "Jetzt" (right), the map shows live data and the "Live" indicator is shown. When the user selects a past hour, "Live" is hidden and the selected time is shown (e.g. "Heute, 14:00 Uhr", "Gestern, 18:00 Uhr").
Backend
- GET /api/routes/stats?at=ISO: Optional query param
at(ISO timestamp of hour start) returns routes with stats for that hour: hourly average duration per route, with interpolation when the hour has no readings. Same filters (direction, data_source, all) apply. - getRoutesWithStatsAt() in
lib/routes-service.ts: Fetches routes with all-time averages, then overlays hourly averages (and interpolates from adjacent hours) and recomputes status for the map.
Changed
- Map filter bar: added timeline range input (0 = vor 24 h, 24 = Jetzt). When a past hour is selected, the map shows that hour’s traffic; switching back to "Jetzt" restores live data.
[1.17.18] - 2026-01-28
Fix - Weekly Congestion Chart Uses All-Time Data
Fixed
- Weekly congestion chart now uses all-time data: Previously used the user's selected time period; it now uses cached all-time weekly congestion (same pattern as "Relative Verspätung nach Wochentag und Uhrzeit").
- Default congestion view: "Wöchentlich" (weekly) is now selected by default instead of "Monatlich" (monthly).
Added
- Cached weekly congestion: New
weekly_congestionJSONB column onroute_alltime_statsstores precomputed weekly congestion entries (weekKey, week, congestion, startDate, dataPoints). - Database migration:
scripts/migrate.jscreates/altersroute_alltime_statsto addweekly_congestion; safe for existing DBs viaADD COLUMN IF NOT EXISTS.
Changed
- All-time stats service:
updateAllTimeStats()now computes and storesweekly_congestionfrom all valid readings (ISO week grouping, congestion % vs fastest trip). - Traffic readings API:
getTrafficStats()returnsalltime_stats.weekly_congestionwhen present; modal uses it for the weekly chart and falls back to period-based data if not yet populated. - Workers: No code change needed—existing daily all-time stats runs (traffic-worker and alltime-stats-worker) already call
updateAllTimeStats(), so weekly congestion is recalculated daily with delay matrix and other all-time stats.
[1.17.17] - 2026-01-29
Feature - Weekly Congestion Chart Toggle
Added
- Weekly/Monthly toggle for Stau-Intensität chart: Users can now switch between weekly and monthly views
- Weekly view shows congestion per calendar week (Monday to Sunday) with date labels (e.g., "13.01")
- Tooltip shows number of data points per week for transparency
- Chart header now includes toggle buttons matching the Fahrtdauer/Geschwindigkeit pattern
- Adaptive X-axis: labels rotate and thin out when many weeks are displayed
Changed
- Chart title dynamically updates: "Monatliche Stau-Intensität" / "Wöchentliche Stau-Intensität"
- Subtitle explains the selected period
- Both views use same color coding: green (<5%), yellow (5-15%), orange (15-30%), red (>30%)
[1.17.16] - 2026-01-29
UI Reorganization - Zeitraum Moved to Chart
Changed
- Zeitraum dropdown relocated: Moved from top control bar to the chart header, alongside the Fahrtdauer/Geschwindigkeit toggle buttons
- Makes it clearer that Zeitraum controls the chart data range
- Top control bar now contains: Vergleich, Datenquelle, Jetzt teilen
Added
- New
.chart-container__selectCSS class for styled dropdown in chart header
[1.17.15] - 2026-01-29
UI Polish - Traffic Modal Spacing
Changed
- Link button label: Added "Jetzt teilen" label above "Link kopieren" button to align with other control items
- Alert spacing: Refined margins - tighter gap between consecutive alerts (0.5rem), 1rem after last alert before controls
- Control bar margin: Reduced margin-bottom from 1.25rem to 1rem for tighter spacing to "Streckenverlauf"
- Removed unused
.stats-modal__control-item--actionCSS class
[1.17.14] - 2026-01-29
UI Redesign - Traffic Modal Top Section
Changed
- Alert boxes redesigned: Refined
stats-alert--warningandstats-alert--errorwith: - Subtle gradient backgrounds for depth
- Left accent bar with gradient (3px) instead of full border
- Custom SVG icons replacing emoji for cleaner, more professional look
- Improved spacing and typography
- Dark mode optimizations with slightly brighter backgrounds
- Control bar redesigned: Replaced inline-styled flexbox with new
.stats-modal__controlscomponent: - Unified segmented control bar with consistent styling
- Vertical dividers between control items
- Uppercase micro-labels for each control
- Custom select styling with smaller dropdown arrow
- Checkbox toggle with active state highlighting
- Link button with inline SVG icon
- Fully responsive: stacks vertically on mobile
- Consistent alerts: Updated inline alerts in statistics section (multipart not available, outlier counts) to use the same refined alert styling
Added
- New CSS classes:
.stats-modal__controls,.stats-modal__control-item,.stats-modal__control-label,.stats-modal__control-select,.stats-modal__control-value,.stats-modal__control-checkbox,.stats-modal__control-btn - Custom SVG icons for warning (triangle) and error (circle) alerts
- Dark mode support for alert gradients
[1.17.13] - 2026-01-28
Mobile Map Improvements
Changed
- Map height on mobile: Increased map container height from
450pxto500pxon mobile screens (<768px) for better visibility - Live indicator spacing: Added
margin-left: 0.5remto.map-section__live-indicatoron mobile to better separate the red dot from the "Quelle" dropdown filter
[1.17.12] - 2026-01-28
Mobile Optimization - Map Filters
Changed
- Mobile filter panel optimization: Optimized
.map-section__filtersfor mobile/responsive use: - Reduced padding from
0.75rem 1remto0.4rem 0.5remon mobile (<768px) - Reduced gap between filter groups from
0.75remto0.4remon mobile - Smaller font sizes for labels (
0.7rem) and selects (var(--text-xs)) on mobile - Reduced select min-width from
120pxto100pxon mobile - Positioned filters closer to edges (
left: 0.5rem, right: 0.5rem) withmax-width: calc(100% - 1rem)for better mobile fit - Live indicator on mobile: Hide "Live" text on mobile screens (<768px) while keeping the red animated dot visible. Added
.map-section__live-textclass for targeted hiding.
[1.17.11] - 2026-01-28
UI Spacing Tweaks
Changed
- Map section spacing: Added
margin-bottom: 2remto.map-sectionso the gap to the next card matches the spacing before the “Informationen” card. - Route card density: Reduced vertical padding on
.route-cardand tightened spacing in.route-card__header/.route-card__metricsso route cards are a bit more compact.
[1.17.9] - 2026-01-28
Header & FAQ Refinements
Changed
- Header title: Restored full description text "Verkehrsdaten und -Monitoring für Straßen in Bonn" on main page
- Header title styling: Increased font size from
--text-smto--text-base, changed color to--text-secondaryfor better visibility - FAQ container width: Increased from 720px to 900px for better readability
- FAQ answer padding: Added proper top padding (1rem) to expanded accordion answers for better visual breathing room
Note
- Hamburger menu is hidden on desktop by default via CSS (
display: none), only shown on mobile (<640px)
[1.17.8] - 2026-01-28
Header Redesign & Mobile Navigation
Added
- Mobile hamburger menu: All pages now have a responsive hamburger menu that appears on screens < 640px
- Menu icon with CSS-only animation
- Full-width dropdown nav on mobile
- Nav links close menu on click
- Consistent header across all pages: Unified header design for main page, FAQ, Datenvergleich, and Errors pages
- Logo links back to dashboard
- Subpage variant with smaller logo and page title/subtitle
- Active nav link styling (highlighted current page)
Changed
- Header CSS refactored: BEM-style classes with comprehensive responsive breakpoints
- Desktop (901px+): Full logo, subtitle visible
- Tablet (641-900px): Compact logo, hidden subtitle
- Mobile (<640px): Small logo, hamburger menu, dropdown nav
- Subpage headers: Now show logo + page title instead of just back link
/faq: "FAQ" title with "Häufige Fragen" subtitle/datenvergleich: "Datenvergleich" title with "Stadt Bonn vs. Google Maps" subtitle/errors: "Fehler" title with „nicht ermittelbar" subtitle- Navigation consistency: All pages show same nav links (Dashboard, FAQ, Datenvergleich, Fehler) + ThemeToggle
Technical
- Added
mobileMenuOpenstate to all page components - Added
Imageimport from next/image to subpages - Added
useThemehook to pages that needed it
[1.17.7] - 2026-01-28
UI Refinements
Changed
- Map height: Increased map container height from 500px to 550px (450px on mobile) for better visibility
- Map filter panel: Repositioned to bottom-left corner to avoid overlapping with Leaflet/OSM attribution
- Rush Hour Analyse: Redesigned with card-based layout for each time period (morning, evening, normal). Each card now has proper background, border, and improved typography hierarchy
- Boxplot chart: Improved contrast for both light and dark mode:
- Box uses interactive colors (blue fill/stroke) instead of muted grays
- Whiskers and caps use primary text color for better visibility
- Labels repositioned (P90/P95 moved to top) with bolder fonts
- Median line color now matches interactive primary
Removed
- "Baustellen ausblenden" checkbox: Removed from map filter panel
Fixed
- Leaflet/OSM attribution: Properly styled and visible in bottom-right corner of map
- Attribution has proper background (light: white 85%, dark: slate 90%)
- Z-index set to 800 to appear above other map elements
- Links properly colored (light: #0078a8, dark: #60a5fa)
- Required per OpenStreetMap/Leaflet license terms
[1.17.6] - 2026-01-28
Design System Phase 3 - Polish & Accessibility
Added
- Floating map filter panel: Map filters now overlay the map with glass-morphism effect (backdrop blur, shadow). Filters are positioned at bottom center on desktop, full-width on mobile.
- Chart container styling: Unified chart container design with BEM-style classes (
chart-container,chart-container__header,chart-container__title,chart-container__subtitle,chart-container__controls,chart-container__toggle,chart-container__description) - Accessibility enhancements:
- Live region (
aria-live="polite") for dynamic route count announcements - High contrast mode support (
@media (prefers-contrast: high)) - Improved focus management in modals
Changed
- RouteStatsModal charts: All chart sections now use consistent
.chart-containerstyling instead of mixed card/inline styles - Map section: Converted from
.cardto.map-sectionwith proper container structure - Filter controls: Updated to use semantic class names (
.map-section__filter-group,.map-section__filter-label, etc.)
[1.17.5] - 2026-01-28
Performance - API Response & CSS Optimization
Changed
- API response number rounding: Implemented rounding of numeric values in API responses to reduce payload size:
- Durations: Rounded to 1 decimal place (minutes) -
Math.round((seconds / 60) * 10) / 10 - Distances: Rounded to nearest meter -
Math.round(distance_meters) - Coordinates: Rounded to 6 decimal places (~10cm precision) -
Math.round(coord * 1000000) / 1000000 - Percentages: Rounded to 1 decimal place
- Distances (km): Rounded to 2 decimal places
- Duration seconds: Rounded to nearest second (no precision loss)
- Applied rounding in
lib/routes-service.ts(route durations, distances, coordinates) andlib/traffic-readings-service.ts(traffic stats, hourly breakdowns, delay matrices, all-time stats) - CSS optimization: Enabled
swcMinify: trueandcompress: trueinnext.config.jsfor better CSS minification in production builds
Impact
- API response size reduction: 10-20% smaller JSON payloads due to fewer decimal places
- Faster data loading: Smaller payloads = faster network transfer and JSON parsing
- No precision loss: All rounding maintains sufficient precision for display purposes
- Better CSS minification: Production builds now have more aggressive CSS minification
[1.17.4] - 2026-01-28
Performance - RouteStatsModal Optimization
Added
- Preload RouteStatsModal on hover: Added
onMouseEnterhandler to route cards that preloads the RouteStatsModal component when user hovers over a route card. This improves perceived performance by starting to load the modal (and its heavy dependencies like recharts) before the user clicks, making the modal appear to open instantly.
Verified
- RouteStatsModal lazy loading: Confirmed that RouteStatsModal is properly code-split and lazy loaded using Next.js
dynamic()import. The modal and its dependencies (~200-300KB including recharts, date-fns) are not included in the initial bundle and only load when needed.
[1.17.3] - 2026-01-28
Performance - Additional Optimizations
Changed
- Lazy loaded RouteStatsModal: Converted RouteStatsModal to dynamic import with
ssr: false. This removes ~200-300KB of heavy dependencies (recharts, date-fns, chart components) from the initial bundle. Modal now loads only when user clicks a route card, significantly reducing first load size. - Next.js Image optimization: Replaced
<img>tags with Next.js<Image>component for SVG logos: - Header logo uses
priorityflag for above-the-fold content - Footer image uses
loading="lazy"for below-the-fold content - Better image optimization and caching
Updated Documentation
- OPTIMIZATION_PLAN.md: Cleaned up completed optimizations and added new optimization opportunities:
- Lazy Load RouteStatsModal (✅ completed)
- Code splitting for heavy dependencies
- Image optimization (✅ completed)
- Bundle analysis recommendations
- Font optimization (already using system fonts)
- CSS optimization
- API response optimization
Performance Impact
- Initial bundle reduced from ~300KB+ to ~140KB (main page)
- RouteStatsModal (~200-300KB) now loads on-demand
- Total initial load reduction: ~300-400KB
- Faster First Contentful Paint and Time to Interactive
[1.17.2] - 2026-01-28
Changed
- Removed automatic refresh intervals: Removed 60-second auto-refresh for routes data. The application now uses "fake-live" approach:
- Data is fetched once on initial page load
- Data refreshes when filters change or page is refreshed
- The "Live" indicator shows that data represents current traffic conditions, but updates require user interaction
- This reduces unnecessary API calls and improves performance
- Intersection Observer for map routes: Implemented Intersection Observer to only load map routes when the map container becomes visible (with 50px margin and 10% threshold). Falls back to deferred loading if Intersection Observer is not supported. This further reduces initial data transfer for users who don't scroll to the map immediately.
[1.17.1] - 2026-01-28
Fixed
- Infinite loop in map routes fetching: Fixed issue where
fetchMapRouteswas being called repeatedly due toloadingMapRoutesbeing in the dependency array, causing the callback to be recreated on every state change. Replaced with refs (mapRoutesLoadingRefandmapRoutesLoadedRef) to track loading state and prevent duplicate requests without causing re-renders.
[1.17.0] - 2026-01-28
Performance - Lazy Loading & Reduced Data Transfer
Major Changes
- Progressive loading implementation: Removed blocking loading state and implemented progressive data loading strategy:
- Routes list loads first (critical path for First Contentful Paint)
- Map routes load deferred using
requestIdleCallbackorsetTimeoutafter initial render - Site texts load deferred after routes list is rendered
Added
- Skeleton loaders: Added skeleton route cards that display while routes list is loading, eliminating blank screen
- Skeleton for info section: Added skeleton loader for site texts section while content loads
- Independent loading states: Separate loading states for routes list (
loadingRoutesList), map routes (loadingMapRoutes), and site texts (loadingSiteTexts) for better error handling and UX
Changed
- Split route fetching: Separated
fetchRoutes()intofetchRoutesList()(critical) andfetchMapRoutes()(deferred) - Non-blocking render: Page now renders immediately with skeleton loaders instead of blocking until all data loads
- Error handling: Independent error handling per section - one section failing doesn't block others
Performance Improvements
- Faster First Contentful Paint: Routes list appears ~0.5-1s instead of ~2-3s
- Reduced initial data transfer: Only paginated routes list (~20-30KB) loads initially instead of all routes + site texts (~100-200KB)
- Better perceived performance: Users see skeleton loaders immediately, then content progressively appears
[1.16.4] - 2026-01-28
Fixed
- Filter bar alignment: Fixed alignment in the filter bar below the map. Filter controls (Richtung, Datenquelle, Baustellen ausblenden) are now left-aligned, while the Live indicator remains on the right side. Used
justifyContent: 'space-between'on the parent container and wrapped filter controls in a left-aligned flex container.
[1.16.3] - 2026-01-28
Changed
- Live indicator positioning and animation: Moved the "Live" indicator with animated dot from the top of the map card to the filter bar below the map. Enhanced the ping animation to be more impactful:
- Increased dot size from 8px to 12px
- Increased animation scale from 1.5x to 2.5x for a more dramatic pulsing effect
- Added intermediate opacity step at 50% for smoother animation
- Positioned at the right side of the filter bar using
marginLeft: 'auto'
[1.16.2] - 2026-01-28
Added
- Live indicator with animated dot: Added a "Live" label with a red animated pinging dot to the first card (map card) on the homepage. The indicator is positioned at the top-right of the card and features a pulsing animation to indicate real-time data status.
[1.16.1] - 2026-01-28
Fixed
- Quill editor dark mode icon colors: Enhanced dark mode styling for Quill editor icons in
/adminroute editor: - Changed icon stroke and fill colors from
var(--border-color)/var(--text-secondary)tovar(--text-primary)for better visibility - Added hover and active state styles with
var(--interactive-primary)color for better feedback - Enhanced picker (dropdown) hover and expanded states
- Added styles for picker items (hover and selected states)
- Improved tooltip input and link colors for dark mode
[1.16.0] - 2026-01-28
Changed
- Map loading optimization: Added loading placeholder for the RouteMap component that reserves horizontal space (600px height, 100% width) while Leaflet loads, preventing layout shift and ensuring the map card loads before the routes-grid. The placeholder displays "Karte wird geladen..." with appropriate styling.
[1.15.9] - 2026-01-28
Changed
- Sorting by delay now uses percentage values: Updated "Größte Verspätung (live)" and "Größte Verspätung (Durchschnitt)" sorting to use percentage delay relative to standard driving time instead of absolute minutes:
- Live delay: Calculates
((current_duration - average_duration) / average_duration) * 100, usingaverage_durationas the baseline (standard driving time) - Average delay: Compares
average_durationto the minimumaverage_durationacross all routes, showing which routes have the highest average delay percentage relative to the best-performing route - Routes without valid data are sorted to the bottom (using
-Infinityas fallback)
[1.15.8] - 2026-01-28
Added
- Sorting functionality for tracked routes: Added a sort dropdown in the "Getrackte Verbindungen" section with options for:
- Alphabetisch (alphabetical by route name, default)
- Streckenlänge (by route distance, descending)
- Größte Verspätung (live) (by current delay, descending)
- Größte Verspätung (Durchschnitt) (by average delay, descending)
- Sorting automatically resets pagination to page 1 when changed
- Sorting is memoized for performance
[1.15.7] - 2026-01-28
Changed
- Date formatting consolidation: Added
formatGermanDateTime()function tolib/utils.tsfor consistent datetime formatting (DD.MM.YYYY, HH:MM). Replaced inlinetoLocaleString()andtoLocaleDateString()calls inapp/admin/page.tsxwith centralized utilities (formatGermanDateTimefor activity logs,formatGermanDatefor archived dates). All date formatting now uses shared utilities fromlib/utils.ts.
[1.15.6] - 2026-01-28
Changed
- Code quality improvements:
- Centralized magic numbers: Moved hardcoded values to
lib/constants.ts: MAP_CONSTANTS:MAX_ZOOM(19),OFFSET_AT_MAX_ZOOM(0.000045), arrow spacing values (200/300/500 meters)PAGINATION:ROUTES_PAGE_SIZE(20)RUSH_HOURS: Morning (7-9) and Evening (15-18) time ranges- Updated
components/Map.tsx,app/page.tsx,app/admin/page.tsx,components/RouteStatsModal/index.tsx, andlib/alltime-stats-service.tsto use centralized constants. - Standardized error messages: Created
lib/errors.tswithcreateError()andcreateErrorObject()helpers for consistent error formatting across services. Updatedlib/routes-service.tscoordinate validation to use the new error helper.
[1.15.5] - 2026-01-28
Performance
- Database index optimization: Added composite index
idx_traffic_readings_route_statusontraffic_readings(route_id, data_status)in migration script. This improves query performance for common queries that filter by bothroute_idanddata_statustogether (e.g., countingnot_availableoroutlierreadings per route, filtering valid readings per route). Migration applied successfully.
[1.15.4] - 2026-01-28
Fixed
- Silent error swallowing: Added user-visible error notifications for previously silent error handlers:
- app/page.tsx: When route fetching fails, users now see a toast notification ("Fehler beim Laden der Routen. Bitte versuchen Sie es später erneut.") instead of silently showing empty lists.
- components/Map.tsx: When route geometry fetching fails, users now see a warning toast ("Routen-Geometrie für {route.name} konnte nicht geladen werden. Gerade Linie wird verwendet.") instead of silently falling back to straight lines. Errors are tracked per route to avoid spam.
- Created new
components/Toast.tsxcomponent withToastContainerfor displaying error/warning/info/success messages. Toasts auto-dismiss after 5 seconds and can be stacked.
[1.15.3] - 2026-01-28
Fixed
- Error boundary for RouteStatsModal: Wrapped
RouteStatsModalinapp/page.tsxwithreact-error-boundary'sErrorBoundarycomponent. If the modal crashes (e.g., due to malformed data), it displays a user-friendly error message with a close button instead of making the entire page unresponsive. Errors are logged to console for debugging.
[1.15.2] - 2026-01-28
Fixed
- Coordinate validation: Added input validation for latitude (-90 to 90) and longitude (-180 to 180) in
lib/routes-service.ts. Coordinates are validated increateRoute,createRouteWithoutGeometry, andupdateRoutefunctions, throwing descriptive errors for invalid values. Added defensive validation inmapRowToRoute(logs warnings for corrupted DB data but doesn't throw to avoid breaking existing routes).
[1.15.1] - 2026-01-28
Security
- HTML injection mitigation: Site texts from the database (rendered with
dangerouslySetInnerHTMLinapp/page.tsx) are now sanitized with DOMPurify before rendering. Addedlib/sanitize.tswithsanitizeHtml()and use client-onlydompurify(loaded dynamically) so server prerender does not require jsdom. Mitigates XSS risk from compromised admin-editable content.
[1.15.0] - 2026-01-28
Changed
- TypeScript Type Safety: Significantly improved type safety by replacing
anytypes with proper interfaces across the codebase: - RouteStatsModal: Replaced
any[]for readings state withTrafficReadingApi[]type. AddedTrafficReadingApiinterface for API responses (timestamp as string). - Map.tsx: Replaced
anyrefs with proper Leaflet types (LeafletMap,Marker,Polyline,Layer,Popup). CreatedPolylineWithArrowheadsinterface for arrowheads extension. - routes-service.ts: Replaced
anydatabase row types withRouteRow,RouteRowWithStats, andRouteDataSourceMappingRowinterfaces. Fixed allmapRowToRouteand query result mappings. - traffic-readings-service.ts: Replaced
anydatabase row types withTrafficReadingRowinterface. FixedmapRowToReadingand query result mappings. Improved type safety for query parameters (unknown[]instead ofany[]). - lib/types.ts: Added new interfaces:
TrafficReadingApi,RouteRow,TrafficReadingRow,RouteDataSourceMappingRow,RouteRowWithStatsfor proper database row typing. - Fixed type compatibility issues with
parseInt/parseFloatto handle both string and number types from database results.
[1.14.10] - 2026-01-28
Fixed
- traffic-worker.ts: Cached GeoJSON fetch at the start of
collectBonnData()cycle usinggetBonnGeoJSON()helper. Prevents redundant fetches when processing multiple routes; if initial fetch fails, routes skip processing instead of retrying individually.
[1.14.9] - 2026-01-28
Changed
- API Pagination: Added server-side pagination to
/api/routes/statswithlimit,offset, andallquery parameters. Client (app/page.tsx) now fetches all routes for map display (all=true) and paginated routes for list display (limit=20&offset=...). Pagination UI uses server-providedtotalcount. This reduces data transfer for large deployments while keeping all routes visible on the map.
[1.14.8] - 2026-01-28
Fixed
- RouteStatsModal BoxplotChart scaling: Fixed extreme compression when outliers are present (e.g., max=10.9 vs median=1.7). Changed from min-max scaling to IQR-based scaling: main scale focuses on Q1-Q3 range (or extends to P95 if available) with 20% padding, preventing the box and percentiles from being compressed into a tiny space. Outliers beyond the visible range are indicated with a break marker.
[1.14.7] - 2026-01-28
Fixed
- RouteStatsModal BoxplotChart: Fixed boxplot data not displaying properly after v1.14.5 useMemo changes. Added validation to filter out invalid values (NaN, Infinity, non-positive) from
durationMinutes, added bounds checking in percentile calculation, and validate all computed values are finite before returning box object. P90/P95 are only included if finite.
[1.14.6] - 2026-01-28
Fixed
- Map re-renders: Memoized route click callback in
app/page.tsxwithuseCallback(openModal)soMapno longer re-runs its effect on every parent re-render whenonRouteClickis unchanged
[1.14.5] - 2026-01-28
Fixed
- RouteStatsModal: Wrapped expensive calculations in
useMemowith proper dependency arrays:durationMinutes(readings),notAvailableCount(readings, dateRange),outlierCount(readings),multipartNotAvailableCount(readings, dateRange, isMultiRoute),box(boxplot percentiles from durationMinutes)
[1.14.4] - 2026-01-28
Changed
- RouteStatsModal structure: Split into folder
components/RouteStatsModal/; extractedBoxplotCharttoRouteStatsModal/BoxplotChart.tsx, added presentationalStatisticsCard.tsx. Modal entry point isRouteStatsModal/index.tsx(import path@/components/RouteStatsModalunchanged). No behavior change;/datenvergleichunaffected.
[1.14.3] - 2026-01-28
Fixed
- Code Duplication: Centralized status color mapping in
lib/constants.ts(STATUS_COLORS,STATUS_COLORS_STROKE);app/page.tsx(route card class only),components/Map.tsx, andcomponents/RouteStatsModal.tsxnow use shared constants
[1.14.2] - 2026-01-28
Fixed
- Code Duplication: Consolidated duplicated
toSlug()into shared utilitylib/utils.ts(previously inapp/sitemap.ts,app/page.tsx,components/RouteStatsModal.tsx)
[1.14.1] - 2026-01-28
Fixed
- Dark Mode: Improved route card border visibility (stronger border color in dark mode)
[1.14.0] - 2026-01-28
Added
- Design System Phase 2: Component refinements
- Route cards redesigned with status accent bar, improved metrics layout, and hover animations
- Sticky header with improved navigation styling
- Modal header improvements with better section separation
- Stats alert classes for consistent warning/error styling
- Chart container styling classes
Changed
- Route Cards: Replaced inline styles with BEM-style CSS classes (
route-card,route-card__status-bar, etc.) - Header: Converted to CSS classes with sticky positioning, improved responsive behavior
- Modal: Added structured header with title/subtitle, consistent alert styling
- Code Cleanup: Removed unused
statusColorfunction
[1.13.1] - 2026-01-28
Changed
- Dark Mode Colors: Changed from grey/black (Zinc) to dark blue (Slate) palette for a warmer appearance
[1.13.0] - 2026-01-28
Added
- Design System Phase 1: New CSS custom properties based design system
- Typography scale (
--text-xsthrough--text-4xl) - Layered background system (
--bg-base,--bg-elevated,--bg-sunken) - Skeleton loading animations (
.skeleton,.skeleton-card,.skeleton-chart) - Modal entrance animations
- Staggered card animations on route grid
prefers-reduced-motionsupport for accessibility- Skip link and screen reader utilities (
.sr-only) - Bottom-sheet modal behavior on mobile
Changed
- Typography: Removed Google Fonts (Roboto) dependency - now using system font stack for GDPR compliance
- Colors: Updated color palette with improved contrast ratios
- Buttons: Refined button styles with new variants (
btn-primary,btn-secondary,btn-ghost,btn-danger) - Forms: Improved form controls with better focus states
- Dark Mode: Enhanced dark mode with proper color tokens
Removed
- External Fonts: Removed Google Fonts dependency for GDPR compliance (zero external requests)
[1.12.4] - 2025-01
Changed
- Rush Hour: Evening rush hour window adjusted from 16:00–19:00 to 15:00–18:00 (RouteStatsModal, alltime-stats-service)
- Footer Image: Removed fixed width from footer SVG; image left-aligned and responsive (page, datenvergleich, faq)
- CSS: Normalized Quill-generated inline styles in
.datenvergleich-info; links (including strong links) stay blue
Documentation
- FEATURE_PROPOSALS.md: Added analysis for weekday vs. weekend statistics split (recommendations and implementation notes)
[1.12.3] - 2025-01
Added
- DelayMatrix Enhancements: Added "Show values" toggle to display delay percentages directly in heatmap cells
- Checkbox toggle above the matrix: "Werte in Zellen anzeigen"
- When enabled, displays delay percentage (e.g., "+15%", "-5%") in each cell
- Text color adapts based on delay value (white for high delays, dark for low delays) for better readability
- Boxplot Percentile Reference Lines: Added P90 and P95 percentile reference lines to the duration distribution boxplot
- P90 displayed as orange dashed line (
#f59e0b) with label - P95 displayed as red dashed line (
#ef4444) with label - Percentile values and descriptions shown in statistics section below the chart
- Format: "P90: X.X min (90% aller Fahrten schneller) · P95: X.X min (95% aller Fahrten schneller)"
Changed
- Boxplot Statistics Layout: Moved P90/P95 descriptions from chart description paragraph to statistics section
- P90 and P95 now appear alongside "Schnellste Fahrt", "Typische Fahrt (Median)", and "Langsamste Fahrt"
- Uses same flex container styling with corresponding colors (orange for P90, red for P95)
[1.12.2] - 2025-01
Added
- Authentication: Re-enabled password-protected admin backend
- Login page with form (
/login) - Admin layout with authentication check and logout functionality
- Protected all admin API routes (POST/PUT/DELETE operations require authentication)
- Logo and Branding: Added theme-aware logo and favicon
- Logo switches between light/dark mode automatically (
bundesstaustadt-lightmode.svg/bundesstaustadt-darkmode.svg) - Favicon configured in metadata (
bundesstaustadt-favicon.svg) - Logo placed in header replacing h1 heading
- Roboto Font: Added Roboto font via
next/font/google(self-hosted, no external requests) - Available as CSS variable
--font-roboto - Used for h2 subtitle in header
Changed
- Header Layout: Improved header design and responsiveness
- Logo increased from 60px to 80px height
- H2 subtitle placed next to logo (responsive - wraps on smaller screens)
- Navigation links (FAQ, Datenvergleich, Fehler) now have hover highlighting with background color
- Environment Configuration: Removed
NODE_ENVfrom.env.local(Next.js manages it automatically) - Deployment: Fixed
.dockerignoreto includescriptsdirectory (needed for database migrations)
Fixed
- NODE_ENV Warning: Fixed Next.js warning about non-standard NODE_ENV value
- Deployment: Fixed migration script not found error by ensuring scripts directory is included in builds
[1.12.1] - 2025-01
Fixed
- Build Errors: Fixed React Hooks error in
DelayMatrix.tsx(moved hooks before early returns) - Build Errors: Fixed duplicate
whiteSpaceproperty inRouteStatsModal.tsx - Build Warnings: Fixed
useMemodependency warnings by including rush hour constants - Security: Updated
react-quillto 2.0.0 vianpm audit fix --force - Documentation: Added Security Notes section to README documenting known vulnerabilities
Changed
- Documentation: Added security notes explaining acceptable risks for
glob(dev dependencies) andquill(admin panel only)
[1.12.0] - 2025-01
Changed
- Delay Matrix: Now ALWAYS uses all-time historical data, regardless of selected period
- Delay matrix shows all historical data points (Monday-Sunday, 00:00-23:00) independent of the selected time period
- Period-specific delay matrix calculation has been removed
- If all-time stats don't exist in database, delay matrix is calculated on-the-fly from ALL historical data
- Statistics Modal: Reordered statistics grid - Trend is now first, "Anzahl Datenpunkte" and "Erster Datenpunkt" moved to end
- Statistics Modal: Removed "Distanz in 15 Min" (kept "Distanz in 15 Min (Rush Hour)")
- Statistics Modal: Re-added "Schlechtester Tag" and removed "Höchste Stau-Intensität"
- Statistics Modal: "Langsamste Stunde" now shows speed on hover (same behavior as "Durchschnittsdauer" and "Schnellstmögliche Fahrtdauer")
- Statistics Modal: Rush hour markers in "Fahrtdauer nach Tageszeit" chart changed from lines to colored background areas
- Statistics Modal: Renamed "Stau-Intensität" to "Ø Stau-Intensität" with improved explanation
Fixed
- Delay Matrix: Fixed to always use all-time data instead of period-specific data
- All-Time Stats Worker: Fixed environment variable loading (added dotenv configuration)
[1.11.0] - 2025-01
Added
- All-Time Statistics: Statistics in "Statistiken" section now use all-time historical data instead of selected period
- Worst Day to Travel, Peak Congestion Time, Distance in 15 Min metrics now calculated from all historical data
- Delay Matrix now uses all-time data when available (falls back to period-specific data with warning if not calculated yet)
- All-time stats are automatically updated once per day by the traffic worker
- Congestion Level Explanation: Added tooltip and description explaining what the congestion percentage means
- All-Time Stats Worker: New script
npm run worker:alltime-statsto manually calculate/update all-time statistics for all routes - Should be run once per day (e.g., via cron at 02:00) or manually when needed
- Required for delay matrix and other all-time statistics to appear in the dashboard
Changed
- Statistics Modal: Removed orange rush hour markers from "Fahrtdauer nach Tageszeit" chart
- Traffic Worker: Integrated all-time stats updates into the main worker loop (once per day check)
- Delay Matrix: Now prioritizes all-time data, shows warning if only period-specific data is available
Fixed
- Database Migration: Added
delay_matrixcolumn toroute_alltime_statstable - Delay Matrix Calculation: Changed from average (AVG) to median (PERCENTILE_CONT) for more accurate representation
[1.10.0] - 2025-01
Added
- Feature Proposals: Comprehensive implementation plan
- Congestion Level metrics (percentage-based)
- Distance-Driven-in-Time metrics
- Monthly Congestion Levels chart
- Enhanced Rush Hour Analysis
- Time Lost Due to Traffic calculations
- Year-over-Year comparison framework
- Live Traffic Map integration plan
- Contextual Insights and Interactive Tooltips
[1.9.1] - 2025-01
Fixed
- Statistics Modal - Gap Lines: Fixed gap line positioning for both 3-day and 7-day periods by using data point count threshold (index-based positioning for >200 points, date strings for fewer points)
[1.9.0] - 2025-01
Changed
- Statistics Modal - Default Period: Changed default time period from 7 days to 3 days
- Statistics Modal - Period Options: Added "Letzte 3 Tage" (3 days) as a new option
- Datenvergleich Page - Default Period: Changed default time period from 30 days to 7 days
- Statistics Modal - Alerts: Data quality alerts now only count data points within the selected time period
- Statistics Modal - Multi-part Routes: For multi-part routes, only the multi-part error alert is shown (not the general "nicht ermittelbar" alert)
- Statistics Modal - Alert Position: Data quality alerts ("fehlerhafte Daten" and "Outlier") moved to appear under the "Statistiken" section
- Mobile - Swipe Back: Swipe back gesture now closes modal smoothly instead of reloading the page
[1.8.2] - 2025-01
Added
- Mobile Optimizations:
- Modal close button now fixed/sticky on mobile devices for easy access while scrolling
- Browser back button now navigates to home page (
/) instead of closing modal - Increased Leaflet route stroke width on mobile (10px main, 14px outline) for better touch targets
- Minimum touch target sizes (44x44px) for all buttons and interactive elements
- Touch-friendly spacing and improved form input sizing on mobile
- Prevented double-tap zoom on buttons using
touch-action: manipulation - Improved modal layout on mobile (full-width, no margins, fixed close button)
- Historical Chart: Added explanatory text about orange gap lines: "Orangene Linien zeigen Pausen in der Datenerhebung."
Changed
- Mobile UX: Form inputs use 16px font size to prevent iOS zoom on focus
- Map Routes: Route polylines are thicker on mobile devices for easier tapping
[1.8.1] - 2025-01
Added
- FAQ Page: Auto-scroll to FAQ question when visiting page with hash in URL (e.g.,
/faq#Was-ist-das-Verkehrsdashboard) - Statistics Modal - API Settings Info: Display current Google Maps API fetch settings (time window and interval) for routes using Google Maps data
- Historical Chart - Gap Visualization: Orange dashed vertical lines indicate data gaps longer than 4 hours in both "Fahrtdauer (historisch)" and "Geschwindigkeit (historisch)" charts
- Historical Chart - Data Info: Display total number of data points and selected time period (e.g., "Zeigt alle 672 Datenpunkte der letzten 7 Tage")
Changed
- Historical Chart - Default Period: Changed default time period from 30 days to 7 days
- Historical Chart - Data Limit: Removed 100 data point limit - now displays all available data points within the selected time period
- DelayMatrix Colors: Improved color gradient by removing white-ish colors, using more shades between dark green and dark red for better visual distinction
- Worker - Time Window: Fixed time window logic to include the end hour (e.g., 20:00 is now included when window is 06:00-20:00)
Fixed
- Gap Lines: Fixed gap line positioning in unsmoothed mode by using index-based positioning instead of date strings for better reliability with many data points
[1.8.0] - 2025-01
Added
- Second data comparison section on
/datenvergleichpage with toggle between comparisons - API fetch settings configuration in
/admin#api-settingsfor quota optimization - FAQ URL hash routing (adds/removes hash when expanding/collapsing questions)
- Statistics modal info for routes from stadtplan.bonn.de and multi-part routes
- Outlier pagination for both comparisons on
/datenvergleichpage - Various chart improvements and adjustments for second comparison
Changed
- Refactored traffic worker to separate Bonn data and Google Maps data collection
- Worker now waits for next interval before first data collection to avoid uneven data points
- Multiple UI/UX improvements across the application
---
[1.9.0]: https://github.com/your-repo/verspaetung-bonn/compare/v1.8.2...v1.9.0 [1.12.4]: https://github.com/your-repo/verspaetung-bonn/compare/v1.12.3...v1.12.4 [1.12.3]: https://github.com/your-repo/verspaetung-bonn/compare/v1.12.2...v1.12.3 [1.12.2]: https://github.com/your-repo/verspaetung-bonn/compare/v1.12.1...v1.12.2 [1.12.1]: https://github.com/your-repo/verspaetung-bonn/compare/v1.12.0...v1.12.1 [1.12.0]: https://github.com/your-repo/verspaetung-bonn/compare/v1.11.0...v1.12.0 [1.11.0]: https://github.com/your-repo/verspaetung-bonn/compare/v1.10.0...v1.11.0 [1.10.0]: https://github.com/your-repo/verspaetung-bonn/compare/v1.9.1...v1.10.0 [1.9.1]: https://github.com/your-repo/verspaetung-bonn/compare/v1.9.0...v1.9.1 [1.8.2]: https://github.com/your-repo/verspaetung-bonn/compare/v1.8.1...v1.8.2 [1.8.1]: https://github.com/your-repo/verspaetung-bonn/compare/v1.8.0...v1.8.1 [1.8.0]: https://github.com/your-repo/verspaetung-bonn/compare/v1.7.0...v1.8.0