What you’ll build
- A filter toolbar (search, date range, city, venue, status, simulated, campaigns, include archived).
- An infinite-scroll or paginated table.
- A KPI strip above the table.
- Row-level actions: flag, archive, open detail.
Prerequisites
- An authenticated user.
X-Preferred-Partner-Idset.
The call chain
On mount:| # | Method | Path | Purpose |
|---|---|---|---|
| 1 | GET | /events/statistics | KPI strip header. |
| 2 | GET | /events/?page=1&limit=50&... | First page of events. |
| Method | Path | Notes |
|---|---|---|
| GET | /events/?... | Re-fetch with new filters. Cancel the previous request with a CancelToken (the reference webapp uses axios.CancelToken). |
| Method | Path | Notes |
|---|---|---|
| GET | /events/?page=N+1&... | Append to existing list. |
| Method | Path | Notes |
|---|---|---|
| GET | /events/{eid} | Drawer-or-detail event data. |
| Method | Path | Notes |
|---|---|---|
| PUT | /events/{eid}/setup_frontend_status | { frontend_status: 'FLAGGED' | 'DEFAULT' | 'HIDDEN' }. Returns 204. |
| PUT | [cr_api] /events/last_updated_on_fe/{eid} | { status: 'RUNNING' | 'CANCELLED' }. Both must succeed. |
| Method | Path | Notes |
|---|---|---|
| GET | /events/venues?city=... | For the venue dropdown grouped by city. |
Filter parameters
All sent toGET /events/. Empty values must be omitted, not sent as
empty strings.
| Param | Type | Notes |
|---|---|---|
page | int (1-based) | Default 1. |
limit | int | Default 50. |
q | string | Free-text search. |
since / until | YYYY-MM-DD | Validate with moment before sending. |
status | string | Backend lifecycle status. |
frontend_status | array | Repeated query param. Values: DEFAULT, FLAGGED, HIDDEN. |
marketing_campaign_status | string | |
simulated | bool | Filter sim events. |
flagged_to_top | bool | Pin flagged rows. |
city | array | Repeated query param. |
vh | array | Venue/hall ids. |
include_archived | bool | |
campaigns | bool | Only events with campaigns. |
exclude_products | bool | Lookout sends true; Wave sends false/absent. |
descending | bool | Sort order. |
repeat style:
city[]= and not city=Berlin,Munich. The backend rejects both.
Response envelope
formatApiEvent into a flatter
shape — { benchmark, now, state } — used by the row component. Don’t
normalise too early; the detail page reads the raw benchmark again.
Reference implementation
Flag / archive — the two-step dance
Gotchas
Trailing slash on /events/
Trailing slash on /events/
/events/ and /events/flagged/ both keep their trailing slash.LOADING vs LOADING_PAGE
LOADING vs LOADING_PAGE
The reference webapp distinguishes “page 1 load” (replace) from
“page N+1 load” (append) so already-rendered rows stay visible during
pagination. Replicate or your UI flickers on every scroll-load.
Cancellation must be silently swallowed
Cancellation must be silently swallowed
A cancelled request throws
AbortError (or axios Cancel). Don’t
surface those to the user — they’re expected on every filter change.Date validation before sending
Date validation before sending
The reference webapp validates
since / until with moment before
sending; invalid strings get passed through unchanged. Validate
server-side too, but reject early in your UI.The benchmark `state` field
The benchmark `state` field
state is one of above, on_track, below. The reference webapp
uses it to colour-code the benchmark cell; it also auto-detects whether
value is a fraction (0-1) or a percentage on each call (peak < 1.5 ? scale ×100 : ×1).Related
- Dashboard — same endpoint, different defaults.
- Event detail — what a row opens.
- Wave list — same data, different presentation.
- Pagination cookbook.