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):
Backend: anonymous /destination-images MediaRoute streaming assets/destinations/* from MinIO (+ Cache-Control on MediaRoute).
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).
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.
Backend: exposeAgePublicly parental permission + Age on SponsorFamilyMember (see consent-boundary ticket).
Angular: parent-dashboard toggle + "Age N" tag on card.
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.
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).
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).
Epic status roll-up — audited every child against origin/master (both consolidated PRs merged: backend #497c8f692d7, angular #592501a3ada, plus angular #593 lint and backend #500/#503 test mocks).
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.
#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.
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.
**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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Redesign
app-sponsor-family-cardto match the approved mock (this-looks-better.png). Extends epic #827 card work (#831/#749/#752).Scope (8 PRs, tickets below):
/destination-imagesMediaRoute streamingassets/destinations/*from MinIO (+ Cache-Control on MediaRoute).TopAwards(≤4) onSponsorFamilyListItem— server-side join ofFundPrizeAward/EarnedSkill(Keycloak-keyed, never on the wire) inSponsorFamilyBuilder. Ranking: prizes by ThresholdCents desc, then skills gold>silver>bronze, newest tiebreak.ui-award-chipsachievements strip replacing the mock's static benefits tiles (+ fixskills.service.ts404 routes).exposeAgePubliclyparental permission +AgeonSponsorFamilyMember(see consent-boundary ticket).skill-awardedactivity fromAwardSkillCommandHandler+ registry entry +earned-skill-countstate provider, so staff rules like "gold skill → $X toward dream destination" work in the existing Location-funds admin page.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 MinIOassetsbucket underdestinations/. 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.
All 8 PRs are open:
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:
{code}.aviflowercase) andmc cptoassets/destinations/— start set 51 US states + 13 countries.learning.skills / skill-awardedrule 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).
Consolidated per Joey's request — one PR per repo:
feat/family-card-epic-868) — replaces #493/#494/#495/#496. Covers #869, #871, #873, #874.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.slnf0 errors + 115 tests green across the touched suites; angular full app suite 3,447 green + all lib suites,nx build spikersoftclean.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).Epic status roll-up — audited every child against
origin/master(both consolidated PRs merged: backend #497c8f692d7, angular #592501a3ada, plus angular #593 lint and backend #500/#503 test mocks).Epic stays open on three things:
{code}.aviffor 51 US states + 13 countries into MinIOassets/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 thehideBrokenImagefallback 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.skill-awardedrule matches and produces a completion + FundEarning, no metadata-condition-on-tiertest, noEarnedSkillCountProvidercoverage. The payout path is real money and is currently unverified in both automation and (per the tracker) production./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 —
ApproveParentalLinkCommandHandlerstill silently resetsInfoVaultto false. #873 added anExposeAgePublicly = falseline to that same wholesale-replacement block, which does not fix it.