Add design for participation stats and podium stamps
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2e4321ac7
commit
0e94b1dba2
1 file changed
+47
@@ -0,0 +1,47 @@
|
||||
# Participation fun: live stats and podium stamps (short design)
|
||||
|
||||
## Intent
|
||||
Make the participant order screen (`ParticipantView.tsx`) feel rewarding: an anonymous, live "number go up" panel and a personal stamp for the first three people to submit.
|
||||
|
||||
## Goals
|
||||
- Live horizontal bar chart per category: how often each item was picked, plus a participant count.
|
||||
- A personal stamp (🥇🥈🥉) shown only to the participant themself, in their "Your order" card, if they are 1st, 2nd or 3rd to submit.
|
||||
- Live updates only while the order is open; stop when it closes, resume if reopened.
|
||||
|
||||
## Non-goals / anti-goals
|
||||
- No usernames, no emails, no way to tell who picked what or to reconstruct a full order.
|
||||
- No medals for most-picked items or any other item-based reward.
|
||||
- No websockets/SSE, no new infrastructure.
|
||||
- The order creator never receives a medal (their submission is skipped in the ranking).
|
||||
|
||||
## Constraints
|
||||
- Frontend already polls every 5s (`ParticipantView.tsx`); follow that pattern.
|
||||
- Admin status changes (accepted/paid/handed_out) bump `submissions.updated_at`, so it cannot be the ranking key.
|
||||
|
||||
## Approaches considered
|
||||
- **Rank key:** new `ranked_at` column (chosen) vs reusing `updated_at` (rejected: admin clicks would reshuffle medals).
|
||||
- **Delivery:** new `GET /api/orders/{id}/stats` polled only while open (chosen); extending `GET /api/orders/{id}` (rejected: no user identity, and its poll must keep running after close); SSE/websockets (rejected: new infra for a counter).
|
||||
|
||||
## Design
|
||||
**DB** (`db.py`): add `submissions.ranked_at TEXT` via the existing `PRAGMA table_info` + `ALTER TABLE` pattern; backfill from `created_at`.
|
||||
|
||||
**Backend**
|
||||
- `create_submission` and `update_submission` (the participant's own PUT) set `ranked_at = now`. `admin_update_submission_status` does not touch it. Deleting a submission removes it from the ranking naturally.
|
||||
- `GET /api/orders/{order_id}/stats`, public like `get_order`, optional `X-User-Id` header. Returns:
|
||||
`{ participant_count, categories: [{id, label, items: [{name, count}]}], my_rank: 1|2|3|null }`
|
||||
- Counts come from every submission's `choices_json`, including the creator's.
|
||||
- `my_rank` is the caller's position among non-creator submissions ordered by `ranked_at`, only if 1–3. The creator is found via `get_order_creator_info` and their submission via `user_order_tokens`. Null for the creator, for non-submitters, and for rank 4 and above.
|
||||
- The stats logic goes in a new function in `stats_service.py`, with a small test in `backend/tests/` (ranking: creator skipped, edit moves you back, admin status change does not, delete promotes others).
|
||||
|
||||
**Frontend**
|
||||
- `statsService` (or `ordersService`) gets a `getOrderStats(orderId)` call.
|
||||
- `ParticipantView`: a `useEffect` polls it every 5s while `!isClosed` and clears the interval when closed. The effect depends on `isClosed`, so a reopen restarts it. The last result stays on screen, frozen, after close.
|
||||
- A small stats card (antd horizontal bars per category, participant count) and a stamp inside the "Your order" card driven by `my_rank`.
|
||||
|
||||
## Left to the builder
|
||||
Bar styling, stamp wording and animation, number-change animation, how to lay out many categories or items (e.g. cap long lists).
|
||||
|
||||
## My calls (not discussed)
|
||||
- The count and bars include the creator; only the medal skips them.
|
||||
- After close the last stats stay visible but frozen.
|
||||
- Backfill `ranked_at` from `created_at`.
|
||||
Reference in new issue
Block a user