Email blast — registration + coach invite emails (Phase 3b) #20
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api#20
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lineage
plan-2026-03-08-tryout-prep→ Phase 3bRepo
forgejo_admin/basketball-apiUser Story
As an admin,
I want to send personalized emails to 34 paid families and 4 coaches,
So that families can complete their child's tryout profile via a one-click link and coaches can begin onboarding immediately.
Context
The gmail-sdk integration already exists —
services/email.pyhasget_gmail_client()andsend_confirmation_email(). The GmailClient takes anaccountalias that maps to a token file:gmail-{account}.jsonin the secrets dir.Available token:
gmail-draneylucas.jsonat~/secrets/google-oauth/. This is our POC sender. Later we switch towestsidebasketballwhen Marcus provides access.Phase 3a (Issue #19) adds
registration_tokento the Parent model andPOST /admin/generate-tokens. This issue builds on that — the email contains the personalized token link.The existing email HTML in
services/email.pyuses blue/gold colors (#0a1628,#f5b731). Phase 3a will update the server-rendered pages to red/black. This issue must also use the Westside red/black brand in email templates.config.pyhas abase_urlsetting (defaulthttp://localhost:8000) — emails need this to build full URLs like{base_url}/register?token=abc123. In production this must behttps://basketball-api.tail5b443a.ts.net.File Targets
Files to modify:
src/basketball_api/services/email.py— addsend_registration_blast_email()andsend_coach_invite_email()functions. Update existingsend_confirmation_email()to use red/black brand. All email HTML must use Westside red/black brand tokens (--color-red: #d42026,--color-black: #0a0a0a, etc.)src/basketball_api/routes/admin.py— addPOST /admin/send-registration-emailsandPOST /admin/send-coach-invitesendpoints (if file created by Phase 3a; otherwise create it)src/basketball_api/main.py— include admin router if not already includedFiles to read for context:
src/basketball_api/models.py— Parent.registration_token (added by Phase 3a), Coach.invite_tokensrc/basketball_api/config.py— base_url setting, gmail_secrets_dir~/west-side-basketball/css/style.css— design tokens for email HTMLFiles NOT to touch:
src/basketball_api/routes/register.py— modified by Phase 3asrc/basketball_api/routes/webhooks.py— unchangedAcceptance Criteria
send_registration_blast_email(tenant, parent, player)— sends branded HTML email with personalized token link{base_url}/register?token={parent.registration_token}. Subject: "Complete [Player Name]'s Westside Tryout Profile". Westside red/black branded.send_coach_invite_email(tenant, coach)— sends branded HTML email with onboarding link{base_url}/coach/onboard?token={coach.invite_token}. Subject: "You're Invited to Coach — Westside Kings & Queens". Westside red/black branded.send_confirmation_email()updated to use red/black brand (replace blue/gold)POST /admin/send-registration-emails— auth-protected (admin role). Generates tokens for any paid parent missing one (calls generate-tokens logic). Sends personalized email to every paid parent. Returns{"sent": N, "skipped": M, "errors": []}. Skips parents who already received an email (addregistration_email_sentbool or check a flag).POST /admin/send-coach-invites— auth-protected (admin role). Accepts JSON body with coach email list (or sends to all coaches in DB). Generates invite tokens if missing. Sends invite email to each coach. Returns{"sent": N, "errors": []}.draneylucasas the tenant gmail_account — emails send from draneylucas@gmail.combase_urlconfig used for all links — no hardcoded URLs in email templatesTest Expectations
send_registration_blast_emailbuilds correct HTML with token link (mock GmailClient)send_coach_invite_emailbuilds correct HTML with invite link (mock GmailClient)pytest tests/ -vConstraints
#d42026(red),#0a0a0a(black),#141414(dark),#e2e8f0(text)config.settings.base_urlfor all links — never hardcode the URLGmailClient(account=tenant.gmail_account, secrets_dir=settings.gmail_secrets_dir)gmail_accountto"draneylucas"in tests/seeds. Production will be"westsidebasketball"later.Checklist
Related
project-westside-basketballplan-2026-03-08-tryout-prep→ Phase 3b