spec: fix upcoming events date filter default and picker bounds (WealthLedger/frontend#63)
This commit is contained in:
@@ -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.
|
||||||
+39
@@ -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.
|
||||||
Reference in New Issue
Block a user