EPIC: Sponsor family card redesign — image-backed destination tiles, achievements strip, parent-gated age #868

Open
opened 2026-07-27 14:29:11 +00:00 by spikerj · 3 comments
Owner

Redesign app-sponsor-family-card to match the approved mock (this-looks-better.png). Extends epic #827 card work (#831/#749/#752).

Scope (8 PRs, tickets below):

  1. Backend: anonymous /destination-images MediaRoute streaming assets/destinations/* from MinIO (+ Cache-Control on MediaRoute).
  2. Angular: full card re-skin — header (FAMILY pill, verified mark, View Family Profile button, tagline), 64px ringed member avatars with role tags + bio lines, image-backed destination tiles (2-col, star=shared, check=parent-approved) with graceful no-image fallback, legend, gradient CTA banner with slim combined-goal bar, dotted world-map decor, violet accent palette. Member progress moves into the spotlight (#752).
  3. Backend: TopAwards (≤4) on SponsorFamilyListItem — server-side join of FundPrizeAward/EarnedSkill (Keycloak-keyed, never on the wire) in SponsorFamilyBuilder. Ranking: prizes by ThresholdCents desc, then skills gold>silver>bronze, newest tiebreak.
  4. Angular: ui-award-chips achievements strip replacing the mock's static benefits tiles (+ fix skills.service.ts 404 routes).
  5. Backend: exposeAgePublicly parental permission + Age on SponsorFamilyMember (see consent-boundary ticket).
  6. Angular: parent-dashboard toggle + "Age N" tag on card.
  7. Backend: skill→money wiring — server skill-awarded activity from AwardSkillCommandHandler + registry entry + earned-skill-count state provider, so staff rules like "gold skill → $X toward dream destination" work in the existing Location-funds admin page.
  8. Fund-management admin fixes: recurrence/prize-kind enum drift (guaranteed 400s today) + domain/action pickers from KnowledgeDomainRegistry.

Destination image spec (Joey): AVIF sRGB q~80, 768×256 (3:1), center-weighted with calm left third, named {code-lowercase}.avif (us-la.avif, mex.avif, mx-oax.avif), uploaded to MinIO assets bucket under destinations/. Start set: 51 US states + 13 offered countries. Missing codes degrade gracefully.

Deploy order: backend before angular per pair (1→2, 3→4, 5→6). 7 and 8 independent.

Redesign `app-sponsor-family-card` to match the approved mock (`this-looks-better.png`). Extends epic #827 card work (#831/#749/#752). **Scope (8 PRs, tickets below):** 1. Backend: anonymous `/destination-images` MediaRoute streaming `assets/destinations/*` from MinIO (+ Cache-Control on MediaRoute). 2. Angular: full card re-skin — header (FAMILY pill, verified mark, View Family Profile button, tagline), 64px ringed member avatars with role tags + bio lines, image-backed destination tiles (2-col, star=shared, check=parent-approved) with graceful no-image fallback, legend, gradient CTA banner with slim combined-goal bar, dotted world-map decor, violet accent palette. Member progress moves into the spotlight (#752). 3. Backend: `TopAwards` (≤4) on `SponsorFamilyListItem` — server-side join of `FundPrizeAward`/`EarnedSkill` (Keycloak-keyed, never on the wire) in `SponsorFamilyBuilder`. Ranking: prizes by ThresholdCents desc, then skills gold>silver>bronze, newest tiebreak. 4. Angular: `ui-award-chips` achievements strip replacing the mock's static benefits tiles (+ fix `skills.service.ts` 404 routes). 5. Backend: `exposeAgePublicly` parental permission + `Age` on `SponsorFamilyMember` (see consent-boundary ticket). 6. Angular: parent-dashboard toggle + "Age N" tag on card. 7. Backend: skill→money wiring — server `skill-awarded` activity from `AwardSkillCommandHandler` + registry entry + `earned-skill-count` state provider, so staff rules like "gold skill → $X toward dream destination" work in the existing Location-funds admin page. 8. Fund-management admin fixes: recurrence/prize-kind enum drift (guaranteed 400s today) + domain/action pickers from `KnowledgeDomainRegistry`. **Destination image spec (Joey):** AVIF sRGB q~80, **768×256 (3:1)**, center-weighted with calm left third, named `{code-lowercase}.avif` (`us-la.avif`, `mex.avif`, `mx-oax.avif`), uploaded to MinIO `assets` bucket under `destinations/`. Start set: 51 US states + 13 offered countries. Missing codes degrade gracefully. **Deploy order:** backend before angular per pair (1→2, 3→4, 5→6). 7 and 8 independent.
Author
Owner

All 8 PRs are open:

# Ticket PR Base
PR-1 #869 spikersoft-backend #493 master
PR-2 #870 spikersoft-angular #587 master
PR-3 #871 spikersoft-backend #494 master
PR-4 #872 spikersoft-angular #589 stacked on #587
PR-5 #873 spikersoft-backend #495 stacked on #494
PR-6 #873 spikersoft-angular #590 stacked on #589
PR-7 #874 spikersoft-backend #496 master
PR-8 #875 spikersoft-angular #591 master

Merge order: backend 493 → angular 587; backend 494 → 495; angular 589 → 590 (retarget each stacked PR to master after its base merges, or merge down the stack). 496 and 591 anytime; deploy 496 before 591 matters only for the Skills picker entry appearing.

Joey's follow-ups:

  • Produce destination images per the spec in this epic (AVIF 768×256, {code}.avif lowercase) and mc cp to assets/destinations/ — start set 51 US states + 13 countries.
  • Post-deploy QA (COPPA rule, #873): e2e-parent1/e2e-child1 — toggle age on/off, verify anonymous /sponsor, verify a book grant doesn't reset the toggle.
  • Rewards smoke test: create a learning.skills / skill-awarded rule in the Location-funds tab, staff-award a skill, confirm FundEarning in the ledger.

Deferred/filed separately: #876 (InfoVault reset in ApproveParentalLink), condition editor (noted in #875).

All 8 PRs are open: | # | Ticket | PR | Base | |---|---|---|---| | PR-1 | #869 | spikersoft-backend #493 | master | | PR-2 | #870 | spikersoft-angular #587 | master | | PR-3 | #871 | spikersoft-backend #494 | master | | PR-4 | #872 | spikersoft-angular #589 | **stacked on #587** | | PR-5 | #873 | spikersoft-backend #495 | **stacked on #494** | | PR-6 | #873 | spikersoft-angular #590 | **stacked on #589** | | PR-7 | #874 | spikersoft-backend #496 | master | | PR-8 | #875 | spikersoft-angular #591 | master | **Merge order**: backend 493 → angular 587; backend 494 → 495; angular 589 → 590 (retarget each stacked PR to master after its base merges, or merge down the stack). 496 and 591 anytime; deploy 496 before 591 matters only for the Skills picker entry appearing. **Joey's follow-ups**: - Produce destination images per the spec in this epic (AVIF 768×256, `{code}.avif` lowercase) and `mc cp` to `assets/destinations/` — start set 51 US states + 13 countries. - Post-deploy QA (COPPA rule, #873): e2e-parent1/e2e-child1 — toggle age on/off, verify anonymous /sponsor, verify a book grant doesn't reset the toggle. - Rewards smoke test: create a `learning.skills / skill-awarded` rule in the Location-funds tab, staff-award a skill, confirm FundEarning in the ledger. Deferred/filed separately: #876 (InfoVault reset in ApproveParentalLink), condition editor (noted in #875).
Author
Owner

Consolidated per Joey's request — one PR per repo:

  • spikersoft-backend #497 (feat/family-card-epic-868) — replaces #493/#494/#495/#496. Covers #869, #871, #873, #874.
  • spikersoft-angular #592 (feat/family-card-epic-868) — replaces #587/#589/#590/#591. Covers #870, #872, #873, #875.

The 8 individual PRs are closed (branches kept). Both consolidated branches re-verified after merging: backend SpikerSoft.UnitTests.slnf 0 errors + 115 tests green across the touched suites; angular full app suite 3,447 green + all lib suites, nx build spikersoft clean.

Merge/deploy order: backend #497 first, then angular #592 (everything in #592 degrades gracefully in between). Follow-ups unchanged: destination AVIFs → assets/destinations/, COPPA age-toggle QA (#873), rewards smoke test (#874).

**Consolidated per Joey's request — one PR per repo:** - **spikersoft-backend #497** (`feat/family-card-epic-868`) — replaces #493/#494/#495/#496. Covers #869, #871, #873, #874. - **spikersoft-angular #592** (`feat/family-card-epic-868`) — replaces #587/#589/#590/#591. Covers #870, #872, #873, #875. The 8 individual PRs are closed (branches kept). Both consolidated branches re-verified after merging: backend `SpikerSoft.UnitTests.slnf` 0 errors + 115 tests green across the touched suites; angular full app suite 3,447 green + all lib suites, `nx build spikersoft` clean. **Merge/deploy order**: backend #497 first, then angular #592 (everything in #592 degrades gracefully in between). Follow-ups unchanged: destination AVIFs → `assets/destinations/`, COPPA age-toggle QA (#873), rewards smoke test (#874).
Author
Owner

Epic status roll-up — audited every child against origin/master (both consolidated PRs merged: backend #497 c8f692d7, angular #592 501a3ada, plus angular #593 lint and backend #500/#503 test mocks).

Child State
#869 destination-images MediaRoute closed
#870 card re-skin closed
#871 TopAwards aggregation closed
#873 exposeAgePublicly + Age closed
#875 fund-management enums + pickers closed
#872 award-chips strip ⚠️ one cosmetic item left
#874 skill-award fund payouts ⚠️ implementation done, required tests missing

Epic stays open on three things:

  1. The destination AVIFs (Joey). 768×256 q~80 {code}.avif for 51 US states + 13 countries into MinIO assets/destinations/. These live in MinIO, not git, so their absence can't be proven from the repo — reporting them outstanding on the authority of this epic's own follow-up list (comment 13322, unchanged since 2026-07-27). Until they land, every destination tile takes the hideBrokenImage fallback path — i.e. the card's headline visual, the whole point of the redesign, is not actually delivered in prod. This is the single highest-value remaining item.
  2. #874's missing RewardEngine tests. No end-to-end test that a skill-awarded rule matches and produces a completion + FundEarning, no metadata-condition-on-tier test, no EarnedSkillCountProvider coverage. The payout path is real money and is currently unverified in both automation and (per the tracker) production.
  3. Post-deploy COPPA QA for #873 — parent toggle on/off against anonymous /sponsor, plus the book-grant non-clobber walk. The automated regression exists (SetChildBookApprovalCommandHandlerTests.cs:112); the manual walk is unrecorded.

Related but not part of this epic and separately open: #876ApproveParentalLinkCommandHandler still silently resets InfoVault to false. #873 added an ExposeAgePublicly = false line to that same wholesale-replacement block, which does not fix it.

**Epic status roll-up** — audited every child against `origin/master` (both consolidated PRs merged: backend #497 `c8f692d7`, angular #592 `501a3ada`, plus angular #593 lint and backend #500/#503 test mocks). | Child | State | |---|---| | #869 destination-images MediaRoute | ✅ closed | | #870 card re-skin | ✅ closed | | #871 TopAwards aggregation | ✅ closed | | #873 exposeAgePublicly + Age | ✅ closed | | #875 fund-management enums + pickers | ✅ closed | | #872 award-chips strip | ⚠️ one cosmetic item left | | #874 skill-award fund payouts | ⚠️ implementation done, required tests missing | **Epic stays open on three things:** 1. **The destination AVIFs (Joey).** 768×256 q~80 `{code}.avif` for 51 US states + 13 countries into MinIO `assets/destinations/`. These live in MinIO, not git, so their absence can't be proven from the repo — reporting them outstanding on the authority of this epic's own follow-up list (comment 13322, unchanged since 2026-07-27). **Until they land, every destination tile takes the `hideBrokenImage` fallback path — i.e. the card's headline visual, the whole point of the redesign, is not actually delivered in prod.** This is the single highest-value remaining item. 2. **#874's missing RewardEngine tests.** No end-to-end test that a `skill-awarded` rule matches and produces a completion + FundEarning, no metadata-condition-on-`tier` test, no `EarnedSkillCountProvider` coverage. The payout path is real money and is currently unverified in both automation and (per the tracker) production. 3. **Post-deploy COPPA QA for #873** — parent toggle on/off against anonymous `/sponsor`, plus the book-grant non-clobber walk. The automated regression exists (`SetChildBookApprovalCommandHandlerTests.cs:112`); the manual walk is unrecorded. Related but *not* part of this epic and separately open: **#876** — `ApproveParentalLinkCommandHandler` still silently resets `InfoVault` to false. #873 added an `ExposeAgePublicly = false` line to that same wholesale-replacement block, which does not fix it.
Sign in to join this conversation.