Frontend: C3 — Variety review (score breakdown) #28
Reference in New Issue
Block a user
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?
Summary
Detailed breakdown of the variety score with protein distribution analysis, sub-scores, and actionable warnings.
Journey: J2 — Plan the week
Role: Planner only
Screen: C3
Layout
Mobile (< 768px)
Stacked layout:
--yellowfill, 6px height, 120px wide--yellow-tintcards with title + explanationDesktop (> 1024px)
Protein Grid (Desktop)
Effort Bar (Desktop)
Warnings
--yellow-tintbg,--radius-lgAcceptance Criteria
Spec file:
specs/frontend/j2-plan-the-week.html— screen C3 with mobile (stacked score + breakdown) + desktop (2-col score/protein grid + effort bar + warnings) previews, agent table, and LLM implementation guide.👨💻 Kai — Frontend Engineer
C3 is a data visualization screen — the most visually interesting layout in the planner section, and all of it is derived/read-only data. That makes it simpler on the write side but the rendering has a few tricky bits.
Component split for C3
VarietyScoreHero— the big Fraunces number (56px mobile / 72px desktop) + progress bar + color descriptionScoreBreakdownTable— 3 rows: protein diversity, ingredient overlap, effort balance (sub-scores)WarningCards—--yellow-tintcards, one per warning, full-widthProteinGrid— desktop-only, 7-column grid, colored cells, repeat highlightsEffortBar— desktop-only, proportional flex row with colored segments + labelsC3Layout— orchestrates the 2-column desktop layout (flex row top + full-width bottom for warnings)The protein grid — color mapping
--yellowborder. How do I define "repeated"? Same day-adjacent, or anywhere in the week?The effort bar
flex: 3, mediumflex: 3, hardflex: 1.Progress bar — 120px wide on mobile
Questions
🛠️ Backend Engineer — Variety Score API
C3 is a read-heavy, computation-heavy screen. The key backend question is whether the variety score and sub-scores are computed on-demand or cached. Let me work through the design.
Variety score API — what does the response look like?
GET /api/week-plans/{weekPlanId}/variety-scoreshould return:Computation strategy
/variety-scoreendpoint should always return the current computed value, not a stale cached one.Sub-score formulas — need agreement before implementation
Planner-only access
GET /api/week-plans/{weekPlanId}/variety-scoremust return 403 for members.Questions
proteinTypefield come from a recipe attribute or is it derived from ingredients? If derived, what's the mapping (e.g., recipes containing chicken breast → CHICKEN protein type)?🧪 QA Engineer — Test Coverage Plan for C3
C3 is a read-only display screen, which makes the frontend testing lighter — but the backend algorithm powering the scores needs rigorous coverage since it's a core business rule.
Backend unit tests — the score algorithm is the heart of this
shouldCalculateFullScoreForCompleteWeekWithDiverseProtein()shouldReturnZeroScoreForEmptyWeekPlan()shouldReturnPartialScoreWhenOnlySomeDaysArePlanned()shouldDetectRepeatedProteinAndFlagInWarnings()shouldDetectHighIngredientOverlapAndFlagInWarnings()shouldCalculateEffortBalanceCorrectlyForAllEasyWeek()shouldCalculateEffortBalanceCorrectlyForAllHardWeek()shouldIncludeAffectedDaysInWarningPayload()— so the UI can highlight the right daysThese are pure business logic tests — no DB needed, just a service method that takes a list of meal slots and returns a score response.
Backend integration tests
shouldReturn403WhenMemberRequestsVarietyScore()— planner-onlyshouldReturn404WhenWeekPlanDoesNotExist()shouldReturnConsistentScoreAfterSwapOperation()— swap a meal, then GET score, verify it reflects the new planshouldReturnCorrectProteinGridForWeekPlan()— verify the 7-element array matches the actual planned mealsFrontend component tests
VarietyScoreHero: renders correct score number, correct color description text, progress bar width proportional to scoreScoreBreakdownTable: renders all 3 sub-score rows with correct valuesWarningCards: renders N cards when N warnings are present, renders nothing (or a "no warnings" state) when warnings array is emptyProteinGrid: renders 7 cells, applies yellow border to repeated protein typesEffortBar: segments are proportional, labels show correct counts, handles edge case where one effort type is 0Parameterized test cases for the score algorithm
@ParameterizedTestacross representative week configurations:Questions
🔐 Sable — Security Engineer
C3 is read-only and planner-only, which makes the attack surface smaller than D1 or J4. But there are a few things worth flagging given the score algorithm and data exposure.
Planner-only access — the obvious check
GET /api/week-plans/{weekPlanId}/variety-scoremust enforce planner role at the service layer. A member should not be able to access C3 data even by direct API call.weekPlanIdmust belong to the caller's household. A planner from Household A should not be able to query the variety score for Household B's week plan by guessing or enumerating UUIDs.Data exposure in the variety score response
affectedDaysand meal details — make sure warning text doesn't accidentally expose recipe names or ingredient details that the score algorithm shouldn't be surfacing (e.g., if warnings are generated from raw DB queries).The score algorithm as a potential information oracle
Error response information leakage
weekPlanIddoesn't exist, return 404. Do not return different error messages for "plan not found" vs "plan exists but belongs to another household" — the latter would confirm existence and enable enumeration.No user-generated content on C3
{@html}risk, no injection surface. This is good from a frontend security perspective.Questions
🎨 Atlas — UI/UX Designer
C3 is a data-dense screen and the design needs to communicate nutritional intelligence without overwhelming. The existing spec has a solid skeleton — here's what needs to be locked down before implementation.
The big score number — color description
--color-errortext, "Needs improvement"--yellow-texttext, "Getting there"--color-text, "Good variety"--green-darktext, "Excellent variety"Progress bar — 120px fixed width is intentional but needs clarification
--color-bordertrack background and--yellowfill (matching the issue spec). Ensure the track has--radius-fulland the fill bar does too.Protein grid — color tokens needed
--protein-poultry)--protein-fish)--protein-red-meat)--green-tint/--green-darktextEffort bar — label placement for narrow segments
Warning cards —
--radius-lg--radius-lgfor warning cards — that's 10px. This matches our "elevated/notable surface" pattern. Good. Ensure consistent 16px horizontal padding and 12px vertical padding inside the card.--yellow-texttitle (13px/500) and--color-mutedexplanation text (13px/400). The title should summarize the problem; the explanation should give context.Questions