feature(sponsor): 'Sponsor a Journey' — family cards with shared destinations, destination chips, family detail page #749

Closed
opened 2026-07-20 21:18:13 +00:00 by spikerj · 1 comment
Owner

Approved UX pass on the public sponsorship surfaces (design reviewed with @spikerj 2026-07-20):

Rename: dashboard "Kids to Sponsor" section + /sponsor landing → "Sponsor a Journey" (parents seek sponsorship too).

Family cards: parent + linked child accounts that are both sponsorship-enabled+staff-approved render as ONE rich family card: stacked avatars, privacy-safe family name ("The S. Family"; generic when any member is HandleOnly), "Shared dream: Louisiana" banner when wishlists overlap, top-5 destination chips (union of members — chips shared by ALL members starred/accent-colored; child parent-approved destinations get a check), combined family goal bar, per-member rows with mini progress + Donate (donations stay per-member).

Solo cards gain the same destination chips (today destinations are invisible on cards despite being in the DTO).

Family detail page: new anonymous /sponsor/family/{familyKey} with a Leaflet map coloring shared vs individual wishlist destinations, per-member cost breakdowns + existing donate flow.

Backend: new anonymous GET api/sponsor/families (+ /families/{familyKey}) — additive, GET /profiles unchanged; opaque SHA-256 family key (never exposes keycloak ids); grouping via ParentalControls.ParentKeycloakUserId; shared = intersection of members' exposed destination sets; child-approved = code also in ParentalControls.Permissions.ApprovedCountries; per-member showDestinations gating everywhere — also fixes the pre-existing leak where GET /profiles returns wishlist codes when showDestinations=false.

Reference case: spikerj (Joseph S., US-LA wishlisted) + toddcan (Todd S., US-LA wishlisted AND parent-approved) → one "The S. Family" card, Louisiana starred + checked.

Lands as two PRs: spikersoft-backend (contract first), spikersoft-angular.

Approved UX pass on the public sponsorship surfaces (design reviewed with @spikerj 2026-07-20): **Rename**: dashboard "Kids to Sponsor" section + /sponsor landing → **"Sponsor a Journey"** (parents seek sponsorship too). **Family cards**: parent + linked child accounts that are both sponsorship-enabled+staff-approved render as ONE rich family card: stacked avatars, privacy-safe family name ("The S. Family"; generic when any member is HandleOnly), "Shared dream: Louisiana" banner when wishlists overlap, top-5 destination chips (union of members — chips shared by ALL members starred/accent-colored; child parent-approved destinations get a check), combined family goal bar, per-member rows with mini progress + Donate (donations stay per-member). **Solo cards** gain the same destination chips (today destinations are invisible on cards despite being in the DTO). **Family detail page**: new anonymous `/sponsor/family/{familyKey}` with a Leaflet map coloring shared vs individual wishlist destinations, per-member cost breakdowns + existing donate flow. **Backend**: new anonymous `GET api/sponsor/families` (+ `/families/{familyKey}`) — additive, `GET /profiles` unchanged; opaque SHA-256 family key (never exposes keycloak ids); grouping via `ParentalControls.ParentKeycloakUserId`; shared = intersection of members' exposed destination sets; child-approved = code also in `ParentalControls.Permissions.ApprovedCountries`; per-member `showDestinations` gating everywhere — **also fixes the pre-existing leak where `GET /profiles` returns wishlist codes when `showDestinations=false`**. Reference case: spikerj (Joseph S., US-LA wishlisted) + toddcan (Todd S., US-LA wishlisted AND parent-approved) → one "The S. Family" card, Louisiana starred + checked. Lands as two PRs: spikersoft-backend (contract first), spikersoft-angular.
Author
Owner

Shipped — both PRs merged to master:

  • spikersoft-backend PR #438 (26905193): anonymous GET api/sponsor/families + /families/{familyKey} — family grouping via the parental link with an opaque SHA-256 family key (payload provably free of keycloak ids), privacy-safe family naming, top-5 destination chips (shared-by-all / child-approved flags), combined funding rollups, and the showDestinations wishlist-leak fix on GET profiles.
  • spikersoft-angular PR #480 (35873170): "Sponsor a Journey" rename (en+es), rich family cards (stacked avatars, "The S. Family", shared-dream banner, star/check chips with flags, family goal bar, per-member donate rows), destination chips on solo cards, and the new /sponsor/family/{familyKey} page with the shared-vs-individual dream map (world + state level).

Verified against production data: spikerj + toddcan render as one "The S. Family" card — Louisiana starred (shared) and checked (parent-approved for Todd). Note: funding bars appear once staff sets trip costs for the two profiles (admin → sponsorship management).

Follow-up tracked separately: #750 (Sponsor controller → CQRS). Closing.

Shipped — both PRs merged to master: - **spikersoft-backend PR #438** (`26905193`): anonymous `GET api/sponsor/families` + `/families/{familyKey}` — family grouping via the parental link with an opaque SHA-256 family key (payload provably free of keycloak ids), privacy-safe family naming, top-5 destination chips (shared-by-all / child-approved flags), combined funding rollups, and the `showDestinations` wishlist-leak fix on `GET profiles`. - **spikersoft-angular PR #480** (`35873170`): "Sponsor a Journey" rename (en+es), rich family cards (stacked avatars, "The S. Family", shared-dream banner, star/check chips with flags, family goal bar, per-member donate rows), destination chips on solo cards, and the new `/sponsor/family/{familyKey}` page with the shared-vs-individual dream map (world + state level). Verified against production data: spikerj + toddcan render as one "The S. Family" card — Louisiana starred (shared) and checked (parent-approved for Todd). Note: funding bars appear once staff sets trip costs for the two profiles (admin → sponsorship management). Follow-up tracked separately: #750 (Sponsor controller → CQRS). Closing.
Sign in to join this conversation.