spec: fix upcoming events date filter default and picker bounds (WealthLedger/frontend#63) #39

Merged
codex-bot merged 1 commits from specs/63-upcoming-events-date-filter into develop 2026-10-14 21:35:02 +03:00
8 changed files with 277 additions and 0 deletions
Showing only changes of commit b4c0589745 - Show all commits
@@ -0,0 +1,2 @@
schema: wealth-ledger-sdd-v2
created: 2026-10-14
@@ -0,0 +1,63 @@
# Design: Upcoming events date filter
## Context and Constraints
The account-events page already has a URL-backed filter with draft, Apply,
Reset, and date validation. Its view model currently exposes a `maxDate` set to
today, which blocks forecast dates; the date picker has no lower bound. The
shared `defaultPeriod()` currently subtracts six elapsed 24-hour intervals and
formats both instants as Moscow calendar dates, which can collapse to fewer
than seven dates around local midnight.
The `useUrlPeriod()` hook already supplies a default when the URL has no range,
preserves valid URL dates (including past dates), and strips invalid or reversed
range parameters. Preserve these semantics. The API already supports requested
date windows; no API or persistence changes are needed.
## Technical Goals / Non-Goals
### Goals
- Compute the exact seven-date default using Moscow calendar-date boundaries.
- Let the date picker select today and future dates only.
- Preserve valid past URL periods and existing Apply/Reset behavior.
- Keep malformed, impossible, and reversed URL periods on the default path.
### Non-Goals
- Change backend event filtering, UTC inclusivity, or saved forecast window
intersection behavior.
- Remove the page filter or turn the page into a read-only forecast list.
- Reject valid past dates at the URL/API boundary.
## Design
Use the shared calendar utility as the owner of the default range. Derive the
current Moscow calendar date, then calculate the inclusive end as six calendar
days later using calendar arithmetic rather than elapsed milliseconds. Keep
the output as `YYYY-MM-DD`, matching existing URL and API parameter formats.
In the account-events page view model, expose today's date as the picker's
minimum selectable date and remove the upper date bound. The picker constraint
is presentation-only: do not add a future/past check to `isValidPeriod()` or
`useUrlPeriod()`. That keeps valid direct URLs independent of what can be newly
selected from the UI.
Keep URL normalization and Apply/Reset ownership where they are. Invalid URL
periods continue to be removed with replacement navigation and resolve to the
same default range; valid one-sided periods retain current behavior.
## Data and State
No new state or API fields are needed. Existing URL parameters remain the
applied state; the view model retains draft state until Apply. Today's picker
minimum is refreshed when opening the calendar, consistent with the existing
today-bound refresh behavior.
## Verification
Verify the default range spans seven distinct dates at ordinary times and
around Moscow midnight, picker bounds allow future dates and block past dates,
valid past URL periods are preserved, and malformed/impossible/reversed ranges
fall back to the default. Existing history, Apply, and Reset behavior must
remain intact. Use the frontend repository's local verification commands.
@@ -0,0 +1,24 @@
# Docs Impact: Upcoming events date filter
## Decision
Update the existing canonical page `docs/domains/income/upcoming-events.md`
after implementation. It already documents the date filter and URL behavior,
so no new documentation page is needed.
## Durable Information to Promote
Clarify the user-visible default as seven calendar dates inclusive of today,
and explain that the date picker permits today and future dates while valid
past ranges remain usable when supplied through a direct URL. Invalid date
ranges continue to fall back to the default.
## Not Required
No API reference, architecture page, glossary, or ADR change is expected: the
API contract and forecast-window semantics do not change.
## Verification
After implementation, confirm the existing upcoming-events domain page matches
the shipped default range, picker bounds, and URL compatibility behavior.
@@ -0,0 +1,40 @@
# Fix upcoming events date filter
## Why
Страница «Будущие события» предназначена для просмотра прогноза, но календарь
сейчас запрещает выбрать будущие даты и разрешает выбирать прошедшие. Значение
периода по умолчанию также должно соответствовать прогнозному горизонту:
семь календарных дат, начиная с сегодняшней.
## What Changes
- Оставить URL-backed фильтр периода и действия Apply/Reset.
- По умолчанию показывать семь календарных дат включительно: от сегодняшней даты
до шестого следующего календарного дня.
- В календаре разрешить даты начиная с сегодняшней; не ограничивать доступные
даты сверху.
- Сохранять валидные периоды из URL, включая прошедшие даты. Некорректный формат,
невозможную календарную дату или обратный диапазон нормализовать к периоду по
умолчанию.
## Capabilities
- `brokerage-account-upcoming-events`
## Impact
- `frontend/`: фильтр и URL-период страницы будущих событий.
- `specs/`: уточнение пользовательского поведения периода по умолчанию и
календарных ограничений существующей capability.
Изменение сохраняет выбор владельца из frontend Issue #63: фильтр остаётся.
Оно не переводит страницу в режим read-only списка исходного прогноза.
## Success Criteria
- При первом открытии без параметров период включает сегодня и следующие шесть
календарных дат.
- Пользователь может выбирать будущие даты и не может выбрать даты до сегодня.
- Прямой URL с валидными прошедшими датами сохраняет указанный период.
- Некорректный URL-период заменяется периодом по умолчанию.
@@ -0,0 +1,37 @@
# QA Analysis: Upcoming events date filter
## Verification Scope
In scope: `brokerage-account-upcoming-events` — **User can filter upcoming
events by a forecast date range** and its four scenarios in the delta spec.
Out of scope: backend date-window behavior, event calculations, forecast
freshness, and API error mapping; these are unchanged by this frontend date
filter adjustment.
## Coverage Matrix
| Requirement / Scenario | Risk | Verification |
| --- | --- | --- |
| Default range starts today and spans seven calendar dates | Off-by-one or local-midnight error yields the wrong period | Check that the inclusive range contains today and the next six distinct Moscow calendar dates, including when opened around Moscow midnight. |
| Future dates can be selected and past dates cannot | Incorrect picker bounds preserve the reported defect or block forecast dates | Open the date picker; confirm yesterday is unavailable and today plus later dates are selectable. Apply a future range and confirm the displayed period reflects it. |
| Valid past URL range is preserved | UI restriction is accidentally enforced on deep links | Open a valid past range directly and confirm the same dates remain applied and in the URL. |
| Invalid URL range falls back to the default | Malformed or impossible dates leak into requests or remain in navigation state | Open malformed, impossible, and reversed ranges; confirm the default range is applied and invalid range parameters are removed. |
## Test Data and Boundaries
- Use a controlled current date and include a case near the Europe/Moscow
calendar-date boundary.
- Cover exactly today, the sixth following calendar date, and a later future
date in the picker.
- For URL normalization, include invalid syntax, an impossible date such as
February 30, and `from` later than `to`.
- Include a valid past range to distinguish URL compatibility from picker
restrictions.
## Risks and Limitations
The calendar is a visual interaction, so system-level verification should use
the existing browser-capable UI validation available to the frontend project.
If only component-level verification is available, it can establish URL and
range state but cannot fully prove the rendered calendar's disabled dates.
@@ -0,0 +1,39 @@
# Delta for Brokerage Account Upcoming Events
## Requirement: User can filter upcoming events by a forecast date range
The events page MUST retain its URL-backed date-range filter. When the URL has
no `from` or `to` parameters, the applied range MUST include the current date
and the following six calendar dates. The page MUST allow selecting today and
future dates in its date picker and MUST prevent selecting dates before today.
The date-picker restriction MUST NOT invalidate or rewrite a valid past date
range supplied directly in the URL. A malformed date, impossible calendar date,
or reversed range MUST be normalized to the default range, removing invalid
`from` and `to` parameters from the URL. Applying and resetting the filter MUST
continue to use the existing URL-backed period behavior.
#### Scenario: Default range starts today and spans seven calendar dates
- **WHEN** the user opens the events page without `from` or `to` parameters
- **THEN** the applied range starts on the current calendar date
- **AND** the inclusive end date is six calendar dates after the start date
#### Scenario: Future dates can be selected and past dates cannot
- **WHEN** the user opens the date picker
- **THEN** today and later dates are selectable
- **AND** dates before today are not selectable
#### Scenario: Valid past URL range is preserved
- **WHEN** the user opens the events page with valid `from` and `to` dates in
the past and `from` is not after `to`
- **THEN** the page applies that URL range without clamping or replacing it
#### Scenario: Invalid URL range falls back to the default
- **WHEN** the URL contains a malformed date, an impossible calendar date, or
a range whose `from` date is after its `to` date
- **THEN** the page applies the default seven-calendar-date range
- **AND** invalid `from` and `to` parameters are removed from the URL
@@ -0,0 +1,61 @@
# System Analysis: Upcoming events date filter
## Scope and ownership
This change affects the frontend events page and its existing date-range
contract. The backend API already accepts optional `from` and `to` dates and
returns events for the requested range; the canonical
`brokerage-account-upcoming-events` capability defines inclusive UTC date
bounds and behavior for invalid request ranges. No backend/API contract change
is needed.
The frontend owns picker availability, URL normalization, and the default
range. The user-approved product decision is to retain the filter (rather than
replace it with an unfiltered saved-forecast list, as considered by frontend
Issue #63).
## Observable flow
```text
URL query parameters
|
v
validate calendar dates and ordering
| valid | invalid
v v
preserve supplied range remove invalid range
| |
+------------+--------------+
v
no range -> seven dates from today
|
v
GET events with inclusive range
```
The date picker prevents new selection of dates before today, but this UI
constraint is intentionally not a validation rule for a valid URL-provided
past range. API date serialization and existing inclusive-boundary semantics
remain unchanged.
## Time-zone and calendar semantics
The existing frontend date utilities represent calendar dates in the
`Europe/Moscow` zone. The default must be computed as calendar-date arithmetic
in that same zone, not by subtracting elapsed 24-hour durations from the
current instant; elapsed-time arithmetic can yield the wrong number of distinct
dates around a local midnight or daylight offset change. The current-date
boundary used by the picker and default must be consistent with this existing
frontend calendar convention.
## Failure and compatibility behavior
- Valid past query ranges remain accepted even though the picker will not
create them.
- Malformed, impossible, or reversed ranges follow the existing URL
normalization path and resolve to the default period.
- One-sided valid ranges keep their existing supported behavior; this change
does not require both query parameters to be present.
- Apply and Reset retain their existing URL/history semantics.
- Backend `INVALID_EVENTS_PERIOD`, request parameters, and saved forecast window
intersection behavior are outside this change.
@@ -0,0 +1,11 @@
# Tasks: Upcoming events date filter
- [x] 1.1 [frontend] Implement the seven-calendar-date default and date-picker bounds while preserving URL period normalization and Apply/Reset behavior.
- **Covers:** `brokerage-account-upcoming-events` — `User can filter upcoming events by a forecast date range`.
- **Source:** Proposal **What Changes**; Specs scenarios **Default range starts today and spans seven calendar dates**, **Future dates can be selected and past dates cannot**, **Valid past URL range is preserved**, and **Invalid URL range falls back to the default**; Design **Design**.
- **Verification:** Frontend owner runs relevant existing page/calendar tests and confirms Moscow-midnight default, future/past picker bounds, valid past URL preservation, and invalid URL fallback.
- [x] 1.2 [docs] Update the canonical upcoming-events page to document the shipped default range and picker/URL behavior.
- **Covers:** `brokerage-account-upcoming-events` — `User can filter upcoming events by a forecast date range`.
- **Source:** Docs Impact **Durable Information to Promote**.
- **Verification:** Confirm `docs/domains/income/upcoming-events.md` describes seven inclusive calendar dates from today, picker restrictions, valid past URL behavior, and invalid-range fallback.