League Day Recap Email
What & Why
When a coordinator locks a league day (the existing "Lock Day" action), automatically email each participating athlete and their parents a personalized recap of how the day went and where they stand overall. Today coordinators only have the manual end-of-season recap; there is no per-day summary, so families have to ask. This closes that gap by reusing the data the lock already produces.
Done looks like
- The moment a coordinator locks a session (), the system queues a per-athlete day recap email (one email per athlete, also CC'd / sent to each linked parent/guardian).
- Each email is personalized to that athlete and clearly states what kind of game day it was:
-
Sorting Day — copy explains that points are not awarded toward standings; shows games played, wins/losses/ties for the day, and the initial tier the athlete will start in next session. -
Regular Day — points-based; shows games played, W/T/L, points earned today, current tier, current rank (and rank movement vs. prior session), and a small "season so far" block (record, total points, tier history). -
Play / Scrimmage Day — explicitly states "no points awarded"; shows games played and W/T/L for fun, no rank changes. -
Final Day — shows the day's results, the athlete's final season position (podium if 1–3), and a single "Season Recap available" link instead of duplicating the existing season recap email.
- Athlete name in subject ("Volleyball recap for {FirstName} — {League name}, Day {N}"). - A short header showing day type, league, session number, and date. - Today's results: games played, W/T/L, points earned (or "no points — sorting day / scrimmage"). - Standings block (skipped for scrimmage and sorting): current rank, rank delta, current tier, total points season-to-date. - A friendly footer with the next session date and court assignment if already generated. - A link back to the schedule page for live details.
- One sender per athlete: parents linked via get the same email as a CC (or a per-recipient send if any parent has the email channel disabled — see preferences).
- Sends are throttled and idempotent: locking the same session twice does not re-send. Failed sends are logged in like all other transactional emails.
- A new email preference category "League day recap" is added so families can opt out without losing other league email categories. Defaults to ON. Honored by both email and (where applicable) push.
- Coordinator sees a small confirmation toast ("Day recap emails queued for X athletes") and can re-trigger the batch from the locked-session view if needed (rate-limited to once per hour).
Out of scope
- A per-game (mid-day) email — recap is only on lock.
- Changing how points are calculated or how tiers are computed.
- Awards / MVP detection beyond what and already record.
- Multi-language email content (English only, matching today's other recap emails).
- A new push notification category for the day recap (push parity can be a follow-up).
Steps
1.
Wire the lock endpoint to enqueue recaps. When succeeds, capture the session id, league id, session type, and the athlete ids that have at least one row (or row) for that session. Skip athletes who were marked absent for every game. 2.
Build a per-athlete recap payload. New helper that, given (athlete, session), returns: today's games, W/T/L, points earned, current rank + delta, current tier, season totals, next-session info, and the day type. Re-use the existing ranking helpers () rather than duplicating math. 3.
New email template + sender. Add / and a in mirroring the pattern. Subject and body branch on day type. Honor the new "League day recap" email preference (default ON) via . Log every send/skip/fail to . 4.
Resolve and notify parents. For each athlete recipient, look up to find linked parents, dedupe emails, and send the same recap to each parent — gated by the same preference key on the parent's preferences row. 5.
Idempotency + re-trigger. Tag each entry with and . Before queuing, query for prior recap sends of this session to skip already-emailed (athlete, parent) pairs. Add a (coordinator-only) that requeues only the recipients with no prior successful send (or a forced-resend mode for admin debugging, rate-limited). 6.
Add the email preference. Extend with (default true), add it to the preference categories list, and surface a toggle on the existing notification settings page next to other league email categories. 7.
Coordinator UI hook. On the coordinator page's locked-session view, add a small "Day recap emails sent" status block (count + last sent timestamp, derived from ) and a "Resend day recap" button calling the new endpoint. 8.
Tests. Add server tests for: (a) recap payload shape per day type (sorting / regular / play / final), (b) idempotency (no duplicate sends on second lock attempt), (c) preference gating (skipped row written when off), (d) parent fan-out via . Add a small page test for the coordinator-side resend button.
Architectural notes
- Do not introduce a new transactional email provider — reuse the existing Resend sender and the pattern used by every other email in .
- Day recap and Season Recap must not overlap: on the final day, send only the day recap (which mentions the season recap) and let the existing manual season recap remain coordinator-triggered, OR auto-trigger the existing season recap on final day lock — pick the day recap path (no automatic season recap) to avoid duplicate emails. The existing manual "Send Season Recap" button stays as is.
- Performance: for a 54-player league locking a day, we should send ~54 athlete emails plus parent fan-out (≤2x). Send through the existing per-call Resend path; no batching changes required, but the enqueue should be with a small concurrency cap (e.g. 5) to avoid burst rate limits. Failures are logged, not surfaced to the coordinator.
- Schema changes: only one new boolean column on (, default true). Use per repo convention. Do not add new tables.
Relevant files
- - - - - - - - -
No comments:
Post a Comment