Slice A complete. Builds on the additive backend constants (commit
34e3d71) by shipping the user-facing surface.
# Pure helpers (no DOM)
frontend/src/client/date-range-picker-pure.ts (190 LoC) — TimeSpec
shape, ALL_HORIZONS / PAST_HORIZONS / NEXT_HORIZONS registries,
horizonBounds (mirrors view_service.go), isValidHorizon, isValidISODate
(strict — rejects 2026-02-30 etc.), validateCustomRange, parseURL /
serializeURL (canonical ?horizon=...&horizon_from=...&horizon_to=...
with default-omission), isDefault.
frontend/src/client/date-range-picker-pure.test.ts (38 bun tests,
118 expect calls): registries, horizon bounds for all 14 values,
ISO-date validity rejects calendar-impossible dates, validateCustomRange
on every error branch, parseURL fallback to default, serializeURL
default-omission + key-override + custom-bounds, full round-trip.
# DOM mount
frontend/src/client/date-range-picker.ts (290 LoC) — mountDateRangePicker
returns {element, getValue, setValue, close, destroy}. Trigger button
in a .multi-anchor wrapper, popover panel reusing .multi-panel
positioning. Symmetric chip row: past fan (right-aligned) | ALLES
centre (target glyph U+2316) | next fan. 'Anpassen' chip toggles an
inline date-pair editor with Apply / Cancel + a live validation
message that surfaces only the meaningful 'inverted range' error
during typing (empty/format errors are visible via the disabled
Apply button). Outside-click + Esc close the popover, focus returns
to the trigger. setValue lets the host sync from URL changes.
# Filter-bar wiring
frontend/src/client/filter-bar/axes.ts:renderTimeAxis — the disabled
'Anpassen' stub (t-paliad-163 Phase 2 placeholder) is gone; the axis
mounts the picker instead. New default presets surface 6 chips +
ALLES centre + Anpassen, plus the per-surface timePresets override
filters down to whatever subset the surface declares. 'any' still
maps to BarState.time = undefined to keep the canonical URL short
and preserve the existing 'no overlay' semantics.
frontend/src/client/filter-bar/types.ts — TimeOverlay.horizon union
extended with next_1d / next_14d / next_all / past_1d / past_14d /
past_all.
frontend/src/client/filter-bar/url-codec.ts — parseHorizon accepts
the six new values; existing 9 values continue to round-trip.
frontend/src/client/filter-bar/url-codec.test.ts — round-trip
iteration extended to all 14 horizons.
frontend/src/client/views/types.ts — TimeHorizon TS mirror extended.
frontend/src/client/projects-detail.ts — horizonBounds covers the
six new values (open-ended for next_all/past_all so the upstream
filter treats nil bounds as 'no narrowing in that direction').
# i18n + retired legacy keys
frontend/src/client/i18n.ts — 30 new keys per language (date_range.*
namespace for the picker + 6 missing views.horizon.* labels for
existing dynamic-key composition in views.ts:317). Legacy
views.bar.time.* keys (10 per language) retired with a one-line
breadcrumb comment pointing at the date_range.* namespace.
frontend/src/i18n-keys.ts — regenerated by build.ts.
# CSS
frontend/src/styles/global.css — date-range-* class block (256 LoC).
Trigger button, popover panel, past/centre/next groups, custom-range
editor, mobile stack at <540px. Reuses --color-accent /
--color-accent-light / --color-bg-lime-tint / --color-border /
--color-text + .agenda-chip / .agenda-chip-active for chip styling
so every active state lights up with the same lime accent as every
other paliad filter chip — no new tokens, no fresh dark-mode
contrast risk (t-paliad-150 / fritz lesson held).
# Surfaces lit up by this single change
- /projects/:id Verlauf (filter-bar consumer)
- /views runtime
- /views/:id Custom-Views editor
- /inbox InboxFilterBar
All four pick up the picker on their next page load. Per-surface
presets (timePresets MountOpt) preserved exactly; Verlauf still
shows the past-only subset, /inbox the forward-leaning subset etc.
The custom chip that's been disabled-with-coming_soon since
t-paliad-163 now works.
# Tests + build hygiene
- go build ./... clean
- go test ./internal/services/ clean (filter_spec + new bounds test)
- bun test passes (150 tests, 8 files, 377 expect calls)
- bun run build clean (2848 i18n keys, data-i18n scan clean)
# What's NOT in this slice
- /agenda chip-row migration (Slice B).
- /admin/audit-log + /projects/:id/chart migration (Slice C).
- upckommentar-style range slicer for custom mode (Slice D, separate
task).