From 826f888e87e4264adb47a5a21cf4c85fdebb133d Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 23 Apr 2026 14:23:45 +1000 Subject: [PATCH] Memory: session-log 2026-04-23c + registry catch-up; retire NearbyPackageCard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session log entry for today (2026-04-23c) covers the 8-commit demo update pass: MiniCard-based nearby-verified grid, package fixtures cleanup (drop 'unknown' treatment) + 10 new verified packages, Vite envDir fix for Google Maps, always-recommended Comparison route, responsive PackageDetail CTAs, ComparisonTable tier-aware Unknown rendering, and info-card un-stick. Plus the pre-drafted 2026-04-23b session-log entry (extractions, CompareBar pattern, PackageDetail toggle, basket persistence) and the matching component-registry updates for those prior-session changes. Registry today: NearbyPackageCard row removed (now orphaned and deleted — no consumers after MiniCard swap). PackagesStep, PackageDetail, and ComparisonTable rows updated for this session's changes. Decisions-log D052–D056 live locally; decisions-log.md is gitignored so those additions aren't in this commit. Flagging the gitignore mismatch — the session log references decision IDs that won't resolve for anyone pulling from the remote until that's sorted. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/memory/component-registry.md | 15 ++- docs/memory/session-log.md | 85 ++++++++++++++ .../NearbyPackageCard.stories.tsx | 93 --------------- .../NearbyPackageCard/NearbyPackageCard.tsx | 106 ------------------ .../molecules/NearbyPackageCard/index.ts | 1 - 5 files changed, 94 insertions(+), 206 deletions(-) delete mode 100644 src/components/molecules/NearbyPackageCard/NearbyPackageCard.stories.tsx delete mode 100644 src/components/molecules/NearbyPackageCard/NearbyPackageCard.tsx delete mode 100644 src/components/molecules/NearbyPackageCard/index.ts diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index c106e15..5a9fc49 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -54,12 +54,15 @@ duplicates) and MUST update it after completing one. | LineItem | done | Typography + Tooltip + InfoOutlinedIcon | Name + optional info tooltip + optional price. Supports allowance asterisk, total variant (bold + top border). Font weight 500 (D019), prices text.secondary for readability hierarchy. Audit: 19/20. | | ProviderCardCompact | done | Card (outlined) + Typography | Horizontal compact provider card — image left, name + location + rating right. Used at top of Package Select page. Separate from vertical ProviderCard. | | CartButton | done | Button + DialogShell + LineItem + Divider + Typography | Outlined pill trigger: receipt icon + "Your Plan" + formatted total in brand colour. Click opens DialogShell with items grouped by section via LineItem, total row. Mobile: icon + price only. Lives in WizardLayout `runningTotal` slot. | -| CompareBar | done | Badge + Button + IconButton + Typography + Paper + Slide | Floating comparison basket pill. Fixed bottom, slide-up/down. Package count badge + provider names + remove buttons + Compare CTA. Max 3 user packages. Disabled CTA when <2. Inline error for max-reached. Mobile: compact count + CTA only. Audit: 18/20. | +| CompareBar | done | Badge + Button + IconButton + Typography + Paper + Slide + Chevron{Left/Right}RoundedIcon | Floating comparison basket pill. Fixed bottom, slide-up via MUI Slide on initial appearance (count: 0 → ≥1). Package count badge (`N/3`) + status text + Compare CTA. Max 3 user packages; disabled CTA when <2; inline error for max-reached. **Sizing**: Badge large / body1 text / Button medium on md+; Badge medium-circle / body2 / Button small on xs. **Mobile collapse (D049)**: single Paper right-anchored via `right: t.spacing(4); left: auto`; grey-filled right-chevron retracts the pill by animating the middle content Box to `max-width: 0` — the pill's right edge stays pinned, so the whole thing appears to shrink into the corner as one unit. Left-chevron expands. Collapsed fraction-badge shows just `count` (not `count/3`) with pinned min-width for circular look. Auto-peek: new add while collapsed re-expands the full bar for 3s, then re-collapses. Desktop stays permanently expanded. **Centering**: uses `left: 0; right: 0; mx: auto; width: fit-content` (the `left:50%; translateX(-50%)` centering trick is clobbered by Slide's own transform). **z-index**: `theme.zIndex.drawer` (1200) — below MapProviderDrawer's `modal` (1300) so the drawer covers it on mobile map view. `bottom: theme.spacing(16)` (64px, FA 4px base) clears the sticky HelpBar with ~25px breathing. See D049. | | ComparisonPackageCard | done | Card + Button + Divider + Typography + Tooltip + LocationOnOutlinedIcon + VerifiedOutlinedIcon + StarRoundedIcon + CheckCircleOutlineIcon + InfoOutlinedIcon | Mobile full-width package card for ComparisonPage tabpanels. Provider header (inline VerifiedOutlinedIcon left of name when verified, name, location + rating, divider, package name, price block, full-width CTA) + itemised sections with left-accent headings. **Warm tint confined to header only** (not Card body) — Card is white (`background.paper`), header has `surface-warm` (recommended) or `surface-subtle` (verified) bg. **2px brand-600 border** when recommended (matches desktop ComparisonColumnCard). Header `px: 3, pt: 3, pb: 4`. Package-info subgroup (name/label/price) in tight nested flex columns. Generous section spacing (`mb: 5` between sections, `py: 2` per item). Recommended banner at top. Shadow (shadow-sm). Medium full-width button. Reuses `ComparisonPackage` type from ComparisonTable. Shared by ComparisonPage V2 and V1 (extracted 2026-04-09). | | ComparisonColumnCard | done | Card + Badge + Button + Divider + Typography + Tooltip + Link + StarRoundedIcon + VerifiedOutlinedIcon | Desktop column header card for ComparisonTable. Floating badge: **medium** (26px) filled brand + StarRoundedIcon for recommended; soft brand + VerifiedOutlinedIcon for verified. Provider name **wraps to 2 lines** (`WebkitLineClamp: 2`) in a reserved 36px minHeight slot bottom-aligned so 1-line names anchor with location/rating/price at a consistent baseline. Recommended card: 2px brand-600 border + warm `selected` Card state + inline VerifiedOutlinedIcon left of name. `pt: 5` (40px breathing above name), uniform regardless of verified/recommended. Remove link always renders as the same Link element (visibility-hidden when not applicable) so CTA+footer align across all cards. Per-column wrapper in ComparisonTable is `display: flex` with `flex: 1` passed to the card root so all cards stretch to row height. Extracted from ComparisonTable (2026-04-12). | | ComparisonTabCard | done | Card + Badge + Typography + StarRoundedIcon | Mobile tab rail card for ComparisonPage. Provider name + package name + price. Recommended badge in normal flow with negative margin overlap — **filled brand + StarRoundedIcon** (matches desktop ComparisonColumnCard treatment, size="small" at 14px icon). **Fixed 235px width** (was 210). Border `brand-600` when recommended (consistent with primary). No glow — uses standard `shadow-sm` like other cards. `pt: 3.5` inside card. Shared by V1 and V2 (extracted 2026-04-12). | -| NearbyPackageCard | done | Card (outlined, interactive) + Typography + StarRoundedIcon + LocationOnOutlinedIcon | Compact card representing a package offered by a nearby verified provider — package name + price + provider + rating + location. Used in the "Similar packages from verified providers nearby" section of PackagesStep for unverified tiers. Click is a route change to that verified provider's PackagesStep with this package loaded. Extracted from UnverifiedPackageT2/T3 during 2026-04-17 consolidation. | | ClusterPopup | done | Paper + Typography + IconButton + ButtonBase + inline provider-rows (fixed verified-icon slot + name + location + rating) | Cluster list popup — appears when a cluster marker is clicked. Header bar ("N providers in this area" + close X), scrollable stack of image-free provider rows (reserved verified-icon slot so titles align across tiers, name in copper for verified / neutral for unverified, location + rating meta). Verified-first sort order. 320px wide, matches MapPopup's card + nub + drop-shadow. Click on a row calls `onSelectProvider(id)`; in the ProviderMap flow that pans+zooms the map to the provider's coords (zoom 15) and opens their single-provider popup — there's no back-to-list. See D043, D044. | +| MapProviderDrawer | done | Paper + IconButton + ProviderCard + inline cluster-rows (verified-slot + name + location + rating + "From $X") | Bottom drawer surfacing ProviderMap's popup content outside the map (used by the mobile map-first layout on ProvidersStep). Two content states driven by `active: ProviderMapActiveState \| null`: (a) `active.provider` → full-width ProviderCard edge-to-edge, entire card clickable → `onSelectProvider(id)`; (b) `active.cluster` → verified-first list of rows with verified-icon slot aligned to name top-line (`alignItems: flex-start` + `1.25em` slot height + `lineHeight: 1.25` on name), name copper for verified, right-aligned "From $X" price column; row click → `onDrillIntoProvider(id)` (typically wired to map's imperative `drillIntoProvider` — pans + zooms + swaps `active` to that provider). 40px header strip holds the close X + cluster count heading; `px: 2` aligns with row content beneath. Slides up via `transform: translateY()` + 220ms transition; stays in DOM during exit via `visibility`. z-index `theme.zIndex.modal` (1300) so it covers the floating CompareBar on mobile map view. Requires a relatively-positioned parent container. See D045, D047. | +| LocationSearchInput | done | Autocomplete (multiple + freeSolo capped to 1) + TextField + IconButton + Chip + LocationOn/Search icons | Committed-chip location search. Typing produces a local draft; Enter or the primary-filled circular magnifying-glass commit button promotes the draft to an FA Chip rendered inside the input. Tap the chip's X → clears to empty. Molecule owns the non-obvious correctness CSS: the end-adornment is absolute-anchored via selector `.MuiAutocomplete-inputRoot .MuiInputAdornment-positionEnd` (MUI's stock Autocomplete does this on `.MuiAutocomplete-endAdornment` but overriding `InputProps.endAdornment` bypasses that, leaving the button sliding left as content fills); `pr: 5` reserves the right-edge lane so chips can't run under. Chrome (bgcolor, shadow, border, radius) is caller-controlled via `sx` — internal selector keys use `.MuiAutocomplete-inputRoot` so they don't collide with caller sx for `.MuiOutlinedInput-root`. API: `value`, `onChange` (fires on commit OR chip-delete), `onCommit?` (explicit commit only), `placeholder?`, `aria-label?`, `sx?`. Extracted from ProvidersStep (D046, D048). | +| HelpBar | done | Box + Typography + Link + PhoneIcon | Sticky help footer — phone-icon prefix + "Need help? Call us on" + tel-linked support number. White fill, top border, `position: sticky; bottom: 0; z-index: 10`. Responsive px (2 / 4) and centered text. Used by WizardLayout and by pages that bypass WizardLayout's chrome (ProvidersStep mobile-map branch). Promoted from a WizardLayout-internal component so both sources render an identical footer — preventing drift if the phone number or style ever changes. API: `phone?` (defaults to '1800 987 888', spaces preserved in label, stripped in tel href), `sx?`. See D048. | +| SortMenu | done | Button (outlined, secondary, small) + Menu + MenuItem + SwapVertIcon (verbose only) | Dropdown sort control. Tap the trigger Button → anchored Menu opens at bottom-right; pick an option → menu closes and `onChange(value)` fires. Two label variants: `compact` (default) renders just "Sort by" — current value surfaces only in the menu's selected-state + aria-label (best for narrow surfaces); `verbose` renders `Sort: ` with a leading swap-vertical icon (best for desktop). Non-generic string-typed API so `forwardRef` stays clean; callers with typed sort unions cast at the boundary. `aria-haspopup="listbox"` + `aria-label` carries the current sort for SR users. API: `value`, `onChange`, `options: SortOption[]`, `variant?: 'compact' \| 'verbose'`, `sx?`. Extracted from ProvidersStep (D048); intended for VenueStep, CoffinsStep reuse. | ## Organisms @@ -67,8 +70,8 @@ duplicates) and MUST update it after completing one. |-----------|--------|-------------|-------| | ServiceSelector | done | ServiceOption × n + Typography + Button | Single-select service panel for arrangement flow. Heading + subheading + ServiceOption list (radiogroup) + optional continue Button. Manages selection state via selectedId/onSelect. maxDescriptionLines pass-through. | | PricingTable | planned | PriceCard × n + Typography | Comparative pricing display | -| PackageDetail | done | LineItem × n + Typography + Button + Divider | Right-side package detail panel. Warm header band (surface.warm) with "Package" overline, name, price (brand colour), Make Arrangement + Compare (with loading) buttons. Sections: Essentials + Optionals (before total) + total + Extras (after total, with subtext). `priceLabel` pass-through to LineItem (D039). T&C grey footer. Audit: 19/20. | -| ComparisonTable | done | Typography + Card + Tooltip + ComparisonColumnCard | Side-by-side package comparison. **Fixed column widths** (300px row-label + 300px per package, exported as `COMPARISON_TABLE_COL_WIDTH`). Natural width = `300 × (n+1)`. **Sticky-left** on row-label column across every per-section mini-table. **Tiered hover**: base cells → `surface-subtle`, recommended column cells → `surface-warm` (resting 50% opacity, promotes to full on row hover via color-mix). Per-section mini-tables with left-accent brand heading. **"Not Included"** rendering: `unavailable` cell values in `Optionals`/`Extras` sections render "Not Included" (neutral-500 weight 500); `Essentials` keeps em-dash. `CellIconText` local helper applies `lineHeight: 1` to icon+text rows for optical centre alignment. ARIA table roles. Desktop only (mobile in ComparisonPage). Audit: 17/20 (pre-restructure). | +| PackageDetail | done | LineItem × n + Typography + Button + Divider + CheckRoundedIcon | Right-side package detail panel. Warm header band (surface.warm) with "Package" overline, name, price (brand colour), Make Arrangement + Compare buttons. Sections: Essentials + Optionals (before total) + total + Extras (after total, with subtext). `priceLabel` pass-through to LineItem (D039). T&C grey footer. **Compare toggle (D050)**: `inCart?: boolean` prop controls the Compare button state. Both states share the same soft/secondary chrome and "Compare" label; when `inCart=true`, a trailing `CheckRoundedIcon` is added (endIcon). Click fires `onCompare` in both states — caller wires it to a toggle (e.g. `basket.toggle(key)`). `aria-pressed={inCart}` + aria-label ("Add to comparison" / "Remove from comparison") for SR users. Rejected alternatives: inert brand-tinted pill (too much space); "Added" label swap (cognitive overhead). **CTAs responsive (2026-04-23c)**: `flexDirection: 'row'` on all viewports — never stacks. `size: isMobile ? 'medium' : 'large'` (40/48px) via `useMediaQuery(theme.breakpoints.down('sm'))` so Make Arrangement + Compare fit side-by-side in a ~360px mobile column. Audit: 19/20 (pre-D050). | +| ComparisonTable | done | Typography + Card + Tooltip + ComparisonColumnCard | Side-by-side package comparison. **Fixed column widths** (300px row-label + 300px per package, exported as `COMPARISON_TABLE_COL_WIDTH`). Natural width = `300 × (n+1)`. **Sticky-left** on row-label column across every per-section mini-table. Info card (top-left "Package Comparison" card) **scrolls** with the package columns (was sticky-left — that pinned it over the recommended column per D040). **Tiered hover**: base cells → `surface-subtle`, recommended column cells → `surface-warm` (resting 50% opacity, promotes to full on row hover via color-mix). Per-section mini-tables with left-accent brand heading. **Tier-aware missing-cell rendering (D054)**: `lookupValue` branches on `pkg.provider.verified` — unverified packages render missing cells as `{ type: 'unknown' }` ("Unknown" + trailing info icon, neutral-500); verified packages render as `{ type: 'unavailable' }` → "Not Included" in Optionals/Extras, em-dash in Essentials (safety net — canonical-essentials rule means verified providers itemise all 9, so this path shouldn't fire in practice). No disclaimer footer. `CellIconText` local helper applies `lineHeight: 1` to icon+text rows for optical centre alignment. ARIA table roles. Desktop only (mobile in ComparisonPage). Audit: 17/20 (pre-restructure). | | FuneralFinder (V3) | done | Typography + Button + Divider + Select + MenuItem + OutlinedInput + custom StatusCard/SectionLabel | **Production version.** Hero search widget — clean form with status cards. Standard card container (surface.raised, card shadow). "How Can We Help" section: two side-by-side StatusCards (Immediate Need default-selected / Pre-planning) — white bg, neutral border, brand border + warm bg when selected, stack on mobile. "Funeral Type" Select + "Location" OutlinedInput with pin icon — standard outlined fields, no focus ring (per design). Overline section labels (text.secondary). CTA "Find Funeral Directors →" always active — validates on click, scrolls to first missing field. Required: status + location. Funeral type defaults to "show all". Dividers after header and before CTA. WAI-ARIA roving tabindex on radiogroup. aria-labelledby via useId(). Critique: 33/40 (Good). Audit: 18/20 (Excellent). | | FuneralFinder V1 | archived | Typography + Button + Chip + Input + Divider + Link + custom ChoiceCard/TypeCard/CompletedRow/StepHeading | Archived — viewable in Storybook under Archive/. Stepped conversational flow. Audit: 14/20. Critique: 29/40. | | FuneralFinder V2 | archived | Typography + Button + Input + Divider + Select + MenuItem + custom StepCircle | Archived — viewable in Storybook under Archive/. Quick-form with step circles. Audit: 18/20. Critique: 33/40. | @@ -89,8 +92,8 @@ duplicates) and MUST update it after completing one. | Component | Status | Composed of | Notes | |-----------|--------|-------------|-------| | IntroStep | done | WizardLayout (centered-form) + ToggleButtonGroup × 2 + Collapse + Typography + Button + Divider | Wizard step 1 — entry point. forWhom (Myself/Someone else) + hasPassedAway (Yes/No) with progressive disclosure. Auto-sets hasPassedAway="no" for "Myself". `
` wrapper, aria-live subheading, grief-sensitive copy. Pure presentation. Audit: 18/20 → 20/20 after fixes. | -| ProvidersStep | done | WizardLayout (list-map) + ProviderCard + ProviderMap + FilterPanel + Autocomplete (chip search) + Chip + Typography + Button + ToggleButtonGroup | Wizard step 2 — provider selection. **Desktop + mobile-list**: list-map WizardLayout — provider cards (click-to-navigate) + sticky bar with committed-chip search (Autocomplete multiple+freeSolo capped to 1, primary-filled search commit button — D046) + Filters dialog + `Sort: ` button + mobile-only `List|Map` toggle. Results count bolded. **Mobile + viewMode=map (D045)**: custom layout — full-bleed map + floating card-shaped control strip (search + Filters + `Sort by` + `List|Map` toggle, all white-fill/neutral-300/shadow-sm/32px/14px-600, matching chrome across modes) + bottom drawer (slides up on pin/cluster tap, close X in a 40px header strip). Single-pin drawer renders ProviderCard edge-to-edge with top-only rounded corners; cluster drawer renders inline list of verified-slot + name + location + rating + "From $X" rows, tap a row → drill-in via `mapRef.drillIntoProvider`. Drawer close fully clears via `mapRef.clearActive`. Header + subhead hidden on mobile map. Filter dialog children extracted into a shared `filterDialogChildren` JSX used by both desktop + mobile FilterPanel instances; Location field removed, Funeral-type chips size=medium, Reset filters always visible (disabled when 0 active), provider-feature switches align to first text line. ProviderMap instantiated internally for mobile map view (with `externalisePopups` + `onActiveChange` + `ref`); desktop continues to use the `mapPanel` slot. Audit: 18/20 (pre-2026-04-23 expansion). | -| PackagesStep | done | WizardLayout (list-detail) + ProviderCardCompact + ServiceOption + NearbyPackageCard + PackageDetail + Divider + Link + Typography | Wizard step 3 — package selection. **Tier-aware unified page** (replaces the old PackagesStep + UnverifiedPackageT2 + UnverifiedPackageT3 trio, 2026-04-17). `providerTier: 'verified' \| 'tier3' \| 'tier2'` drives heading, subhead, `arrangeLabel`, `priceDisclaimer`, and `itemizedUnavailable` via a `TIER_COPY` map. Discriminated `secondaryList`: `same-provider-more` (ServiceOption list, verified) or `nearby-verified` (NearbyPackageCard list, unverified). Same-provider-more **shows top 3 inline**; at >3 shows 3 + `See all N packages from [Provider] →` Link that fires `onSeeAllPackages`. `showAllFromProvider` prop renders a flat "All packages from [Provider]" variant. Primary list suppresses the "Matching your preferences" accent-bar heading when no secondary list is present. **Mobile drill-in (2026-04-23 fix):** local `hasDrilledIn` flag — the mobile layout only swaps to the detail view after an explicit user tap on a package, so parent-seeded `selectedPackageId` (common on desktop for auto-display) doesn't force mobile users straight into detail. Back resets the flag. | +| ProvidersStep | done | WizardLayout (list-map) + ProviderCard + ProviderMap + FilterPanel + LocationSearchInput + SortMenu + HelpBar + MapProviderDrawer + Chip + Typography + ToggleButtonGroup | Wizard step 2 — provider selection. **Desktop + mobile-list**: list-map WizardLayout — provider cards (click-to-navigate) + sticky bar with LocationSearchInput (committed-chip search — D046/D048) + FilterPanel trigger + SortMenu (`variant: isMobile ? 'compact' : 'verbose'` — D048) + mobile-only `List\|Map` toggle. Results count bolded. **Mobile + viewMode=map (D045)**: custom layout — full-bleed map + floating control strip (search + Filters + Sort by + `List\|Map` toggle) + MapProviderDrawer + shared HelpBar molecule. Header + subhead hidden on mobile map. **Control chrome**: page-local `CONTROL_CHROME` const (height 32 / neutral-300 border / button-radius / paper fill / shadow-sm) + derived `controlButtonSx` / `controlToggleSx` / `controlInputSx` / `filterTriggerSx` objects applied across Search + Filters + SortMenu + ToggleGroup so all four controls read as one coherent chip set. Filter dialog children in a shared `filterDialogChildren` JSX used by both desktop + mobile FilterPanel instances; Location field removed (sticky search is primary), Funeral-type chips size=medium, Reset filters always visible (disabled when 0 active), provider-feature switches align to first text line. ProviderMap instantiated internally for mobile map view (with `externalisePopups` + `onActiveChange` + imperative ref); desktop continues to use the `mapPanel` slot. Audit: 18/20 (pre-2026-04-23 expansion). | +| PackagesStep | done | WizardLayout (list-detail) + ProviderCardCompact + ServiceOption + MiniCard + PackageDetail + Divider + Link + Typography | Wizard step 3 — package selection. **Tier-aware unified page** (replaces the old PackagesStep + UnverifiedPackageT2 + UnverifiedPackageT3 trio, 2026-04-17). `providerTier: 'verified' \| 'tier3' \| 'tier2'` drives heading, subhead, `arrangeLabel`, `priceDisclaimer`, and `itemizedUnavailable` via a `TIER_COPY` map. Discriminated `secondaryList`: `same-provider-more` (ServiceOption list, verified, `packages` field) or `nearby-verified` (MiniCard grid, unverified, `providers` field). **Nearby-verified (D052)**: 2-col `repeat(2, 1fr)` grid on sm+, 1-col on xs, capped at 4 via `NEARBY_VERIFIED_LIMIT`. Each card is a verified provider (image + `verified={true}` implicit + location + rating + "From $X"); click routes to that provider's PackagesStep via `onNearbyProviderClick(id)`. Heading: "Similar packages from verified providers" (`VerifiedOutlinedIcon` aligned to top line via `alignItems: flex-start` + `mt: '3px'`). Same-provider-more **shows top 3 inline**; at >3 shows 3 + `See all N packages from [Provider] →` Link that fires `onSeeAllPackages`. `showAllFromProvider` prop renders a flat "All packages from [Provider]" variant. Primary list suppresses the "Matching your preferences" accent-bar heading when no secondary list is present. `selectedPackage` lookup falls back from primary `packages` to `same-provider-more` secondary list so tapping a package in "Other packages from X" surfaces PackageDetail correctly (2026-04-23c fix). **Mobile drill-in (2026-04-23 fix):** local `hasDrilledIn` flag — the mobile layout only swaps to the detail view after an explicit user tap on a package, so parent-seeded `selectedPackageId` (common on desktop for auto-display) doesn't force mobile users straight into detail. Back resets the flag. | | ~~PreviewStep~~ | removed | — | Replaced by ArrangementDialog organism (D-E). Package preview + "what's next" checklist now in the dialog's preview step. | | ~~AuthGateStep~~ | removed | — | Replaced by ArrangementDialog organism (D-E). SSO/email auth flow now in the dialog's auth step. | | DateTimeStep | done | WizardLayout (centered-form) + Input + TextField (date) + RadioGroup + Collapse + Divider + Button + Link | Wizard step 6 — details & scheduling. Deceased name (Input atom, external label) + preferred dates (up to 3, progressive disclosure) + time-of-day radios. Service tradition removed (flows from provider/package). Dividers between sections. Grief-sensitive labels. Save-and-exit CTA. | diff --git a/docs/memory/session-log.md b/docs/memory/session-log.md index bf31f58..981f7b6 100644 --- a/docs/memory/session-log.md +++ b/docs/memory/session-log.md @@ -26,6 +26,91 @@ Each entry follows this structure: ## Sessions +### Session 2026-04-23c — Nearby-verified MiniCard grid, package fixtures + dashes, recommended column, map env fix + +**Agent(s):** Claude Opus 4.7 (1M context) + +**Context:** User walked through a series of demo-visible issues after reviewing `https://parsons.tensordesign.com.au/arrangement/`. Each landed as its own commit + deploy; total 8 commits. + +**Work completed:** + +- **Nearby-verified section on PackagesStep now surfaces providers, not packages (D052):** the `nearby-verified` `SecondaryList` arm was rebuilt around `MiniCard`. Data shape renamed `NearbyVerifiedPackage` → `NearbyVerifiedProvider` (`id, name, imageUrl, location, startingPrice, rating?, reviewCount?`); `verified` is implicit, not a data field, since the section is verified-only by definition. Callback `onNearbyPackageClick` → `onNearbyProviderClick`, routing directly on provider id. **Layout**: 2-col CSS grid (`repeat(2, 1fr)`) on sm+, 1-col on xs; capped at 4 via `NEARBY_VERIFIED_LIMIT`. Heading changed "Similar packages from verified providers nearby" → "Similar packages from verified providers" (dropped "nearby"). Heading icon aligned to top line of multi-line text via `alignItems: flex-start` + `mt: '3px'` (FA icon convention). Demo fixture `nearbyVerifiedSamples` replaced by `nearbyVerifiedProviders` derived from the main `providers` fixture filtered to `tier === 'verified' && imageUrl`. `NearbyPackageCard` molecule is orphaned and retired in the same pass (registry row + folder deleted). +- **Packages fixtures cleanup + 10 new verified packages:** + - **Dashes (D053):** removed all 10 fixture entries with `treatment: 'unknown'` from Optionals. Narrowed `Optional['treatment']` from `IncludedTreatment | 'unknown'` to `IncludedTreatment`. Simplified `optionalsForStep` / `optionalsForComparison` helpers — the `'unknown'` branches are gone. PackageDetail no longer renders bare em-dash rows; ComparisonTable's `lookupValue` already returned `unavailable` (→ "Not Included") for missing items via the section cross-join. + - **New packages:** distribution randomised, max 5 per verified provider. Parsons 3 → 5 (+ Traditional Burial, Memorial Service), Rankins 2 → 3 (+ Direct Cremation), Killick 2 → 3 (+ Traditional Burial), Mackay 1 → 4 (+ Premium, Simple Cremation, Memorial Service), Mannings 1 → 4 (+ Premium, Simple Cremation, Direct Cremation). Each new package follows the canonical-Essentials rule (same 9 line items, per-package prices/treatments). Mackay + Mannings comparison maps rewritten from the single-package `.map(pkg => ({...}))` shape to the indexed-array pattern used by parsons/rankins/killick. Their `packagesByProvider` entries now `slice(0,1)`/`slice(1)` so "Other packages from this provider" renders. +- **PackagesStep selection lookup** bug fix — `selectedPackage` was derived only from the primary `packages` array, so clicking a package in "Other packages from X" updated `selectedPackageId` but the detail panel stayed empty. Added a fallback to the `same-provider-more` secondary list. +- **Vite demo config: `envDir = __dirname` (D056).** The demo's Google Maps API key was never in the production bundle. `vite.demo.config.ts` sets `root: src/demo/apps//`, and Vite's default envDir is that `root` — no env files there, so `.env.local` at the repo root was silently ignored and `VITE_GOOGLE_MAPS_API_KEY` came out undefined. ProviderMap fell through to its "no API key" empty state on prod. Adding `envDir: __dirname` fixes it for every current and future slice. +- **Comparison route: always include a recommended column (D055).** Demo's Comparison.tsx resolves a static `DEMO_RECOMMENDED_KEY = 'parsons:deluxe'` and passes it as `recommendedPackage`. Dedupes against basket so if the user has Parsons Deluxe selected it only appears once (as the recommended column). Basket mechanics unchanged — `useComparisonBasket` still caps user selections at 3; the recommended is layered as an editorial suggestion. Empty state demoted to defensive (now only fires when both `packages` AND `recommendedPackage` miss). +- **PackageDetail CTAs side-by-side on mobile.** Buttons stacked on xs because of `flexDirection: { xs: 'column', sm: 'row' }` + `size="large"` — labels couldn't fit a ~360px column side by side. Fixed `flexDirection: 'row'` for all viewports and `size: isMobile ? 'medium' : 'large'` (40px/48px). `useMediaQuery(theme.breakpoints.down('sm'))` drives the switch. +- **ComparisonTable: unverified providers show "Unknown" (D054).** `lookupValue` now checks `pkg.provider.verified` — unverified missing cells return `{ type: 'unknown' }` (existing "Unknown" + info-icon render), verified missing cells keep `{ type: 'unavailable' }` (→ "Not Included" in Optionals/Extras; em-dash in Essentials as the safety net — canonical-essentials rule means that path shouldn't fire in practice). Dropped the "* Some providers have not provided an itemised pricing breakdown. Their items are shown as '—' above." footer and the now-unused `Z_HEADER_ROW` constant. +- **ComparisonTable info card un-stuck.** The top-left "Package Comparison" info card had `position: sticky, left: 0` which pinned it over the leftmost package column (the recommended one since D040) on horizontal scroll. Dropped the sticky positioning — info card scrolls with the package headers; row-label column below stays sticky on its own (those labels genuinely help orient the reader). + +**Decisions made:** D052, D053, D054, D055, D056. + +**Verified visually (user eyes, Playwright not used):** +- MiniCard grid renders 2-col desktop / 1-col mobile, max 4; icon aligns with heading top line on mobile. +- New packages visible in each provider's "Other packages from X" section; "Other from Rankins → Premium" selection now surfaces PackageDetail. +- Dashes gone from verified-provider PackageDetail Optionals. +- Google Maps loads on prod (grep of built bundle confirmed `AIzaSy` key baked in). +- ComparisonPage shows Parsons Deluxe as leftmost recommended column with the filled-brand + star badge on every visit. +- CTAs side-by-side on mobile at medium size. +- Botanical (tier2) comparison cells render "Unknown" — no more em-dashes except the canonical-Essentials safety-net path (which verified providers never trigger in practice). +- Info card scrolls away on horizontal scroll; recommended card fully visible. + +**Preflight + deploy:** Each commit preflighted (TS + ESLint + Prettier clean; Storybook built once) and deployed via `npm run demo:publish` to `https://parsons.tensordesign.com.au/arrangement/`. Hardcoded-hex warnings all pre-existing in HomePage / FuneralFinderV2-4 / PackageDetail — none introduced this session. + +**Open questions:** +- None. + +**Next steps:** +- Push commits to `backup` + `fa-dev` git remotes at `git.tensordesign.com.au`. + +--- + +### Session 2026-04-23b — Extractions, CompareBar pattern, PackageDetail toggle, basket persistence + +**Agent(s):** Claude Opus 4.7 (1M context) + +**Context:** Continuation of 2026-04-23 work. User asked for a polish + cleanup pass on the comparison feature and its surrounding components, ending with a deploy. Several discrete changes delivered across component + demo layers. + +**Work completed:** + +- **Three molecule extractions (D048)** from ProvidersStep, each with its own barrel export + Storybook stories: + - `LocationSearchInput` (`src/components/molecules/LocationSearchInput/`) — committed-chip location search. Owns the subtle endAdornment absolute-anchor CSS so future Autocomplete-with-custom-endAdornment users don't rediscover it. + - `HelpBar` (`src/components/molecules/HelpBar/`) — promoted from a WizardLayout-internal component so WizardLayout and ProvidersStep's mobile-map branch share one source of truth (prevents the phone/styling drift that had already crept in). + - `SortMenu` (`src/components/molecules/SortMenu/`) — trigger Button + anchored Menu with `variant: 'compact' \| 'verbose'`. Intended for VenueStep / CoffinsStep reuse; today only ProvidersStep consumes it. + ProvidersStep shed ~240 lines net (1087 → ~850) and now reads as a composition of small molecules. +- **CompareBar**: + - Sized up: Badge large (32px), body1 text, Button medium (40px) on desktop; responsive size-down on xs. + - Compare icon removed — label alone at the larger size. + - Horizontal centering fixed — was 171px off-centre on desktop because Slide's internal transform was clobbering `translateX(-50%)`. Switched to `left:0; right:0; mx:auto; width:fit-content` which Slide doesn't fight. + - `bottom` bumped from `theme.spacing(9)` to `theme.spacing(16)` — the FA theme uses a 4px spacing base, so 9 was only 36px (overlapping the ~40px HelpBar). 16 = 64px with clean 25px gap. + - **Mobile collapse with auto-peek on add (D049)**: single-Paper width-animation approach. Right-anchored on xs, middle content (status + CTA) animates to `max-width: 0` while the pill's right edge stays pinned — visually retracts to the corner as one unit. Grey-filled chevron swaps between right (collapse) and left (expand) in place. Auto-peek: new add while collapsed → bar peeks back in for 3s. Collapsed badge shows just `count` (not `count/3`) with pinned min-width for circular look. + - z-index dropped from `snackbar` (1400) to `drawer` (1200). `MapProviderDrawer` raised from `3` to `modal` (1300). Drawer now visually covers CompareBar on mobile map view when a pin/cluster is active. +- **PackageDetail `inCart` toggle (D050)**: new `inCart?: boolean` prop. Both states share the same soft/secondary chrome + "Compare" label; added state adds a trailing `CheckRoundedIcon`. Click is a toggle (caller wires to `basket.toggle(key)`). Two earlier patterns were implemented then rejected by the user: an inert brand-tinted pill (too much space) and an "Added" label swap (cognitive overhead). `aria-pressed` + `aria-label` carry state for SR. Threaded via `PackagesStep.isSelectedPackageInCart` → `Packages.tsx` computes `basket.has(key)`. +- **Basket URL-sync sticky (D051)**: `useBasketUrlSync` pre-fix silently wiped the basket on back-navigation from `/providers/:id/packages?compare=foo` to `/` because the new empty `?compare=` was treated as a "set to empty" signal. Fix: when in-app navigation drops the param but the store still has items, re-attach the store's keys to the new URL instead of wiping. Shared links + manual URL edits still hydrate the store. Demo-only change. +- **Minor polish**: + - MapProviderDrawer cluster rows: verified-icon alignment fix + 1.25 lineHeight on name + px:2 header padding aligns heading with row content (was px:1.5). + - Deployed checkpoint mid-session (commit `6434d113`) and end-of-session. + +**Decisions made:** D048, D049, D050, D051. + +**Verified visually (Playwright):** +- Basket persists across provider → map → provider → map (URL carries `?compare=...` through every route). +- Mobile CompareBar collapses/expands as one unit; collapsed badge is circular; grey chevron visible; auto-peek fires on add; HelpBar overlap gone. +- Drawer z-index: drawer covers CompareBar when active; CompareBar reappears when drawer dismissed. +- PackageDetail toggle: chrome identical between default and added states; trailing tick on right when `inCart`. + +**Preflight + deploy (early checkpoint):** all critical checks pass (TS, Storybook build, token sync, ESLint, Prettier). Deployed to `https://parsons.tensordesign.com.au/arrangement/`. + +**Open questions:** +- None blocking. If slide/peek on mobile feels wrong after user testing, the pattern is one `git revert` away. + +**Next steps:** +- Memory / registry catch-up pass (in progress at session end), then a second deploy. + +--- + ### Session 2026-04-23 — ProvidersStep polish + mobile map-first layout + deploy **Agent(s):** Claude Opus 4.7 (1M context) diff --git a/src/components/molecules/NearbyPackageCard/NearbyPackageCard.stories.tsx b/src/components/molecules/NearbyPackageCard/NearbyPackageCard.stories.tsx deleted file mode 100644 index bf56822..0000000 --- a/src/components/molecules/NearbyPackageCard/NearbyPackageCard.stories.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import Box from '@mui/material/Box'; -import { NearbyPackageCard } from './NearbyPackageCard'; - -const meta: Meta = { - title: 'Molecules/NearbyPackageCard', - component: NearbyPackageCard, - tags: ['autodocs'], - parameters: { - layout: 'centered', - }, - decorators: [ - (Story) => ( - - - - ), - ], -}; - -export default meta; -type Story = StoryObj; - -/** Default — full metadata including rating and review count */ -export const Default: Story = { - args: { - packageName: 'Everyday Cremation', - price: 4200, - providerName: 'H.Parsons Funerals', - location: 'Wentworth', - rating: 4.5, - reviewCount: 32, - onClick: () => alert('Navigate to provider package'), - }, -}; - -/** Without rating — provider has no reviews yet */ -export const WithoutRating: Story = { - args: { - packageName: 'Simple Farewell', - price: 3800, - providerName: 'Riverstone Funerals', - location: 'Mildura', - onClick: () => alert('Navigate to provider package'), - }, -}; - -/** Non-interactive — no onClick */ -export const Static: Story = { - args: { - packageName: 'Everyday Cremation', - price: 4200, - providerName: 'H.Parsons Funerals', - location: 'Wentworth', - rating: 4.5, - reviewCount: 32, - }, -}; - -/** Stacked — as rendered in the similar-packages list */ -export const Stacked: Story = { - render: () => ( - - {}} - /> - {}} - /> - {}} - /> - - ), -}; diff --git a/src/components/molecules/NearbyPackageCard/NearbyPackageCard.tsx b/src/components/molecules/NearbyPackageCard/NearbyPackageCard.tsx deleted file mode 100644 index bc4cc80..0000000 --- a/src/components/molecules/NearbyPackageCard/NearbyPackageCard.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import Box from '@mui/material/Box'; -import LocationOnOutlinedIcon from '@mui/icons-material/LocationOnOutlined'; -import StarRoundedIcon from '@mui/icons-material/StarRounded'; -import type { SxProps, Theme } from '@mui/material/styles'; -import { Card } from '../../atoms/Card'; -import { Typography } from '../../atoms/Typography'; - -// ─── Types ─────────────────────────────────────────────────────────────────── - -/** Props for the FA NearbyPackageCard molecule */ -export interface NearbyPackageCardProps { - /** Package display name */ - packageName: string; - /** Package price in dollars */ - price: number; - /** Provider display name */ - providerName: string; - /** Provider location (suburb, city) */ - location: string; - /** Provider rating (e.g. 4.5). Omit to hide. */ - rating?: number; - /** Number of reviews */ - reviewCount?: number; - /** Click handler — navigates to that provider's PackagesStep with this package loaded */ - onClick?: () => void; - /** MUI sx prop */ - sx?: SxProps; -} - -// ─── Component ─────────────────────────────────────────────────────────────── - -/** - * Compact card representing a package offered by a nearby verified provider. - * - * Surfaced in the "Similar packages from verified providers nearby" section - * of the unverified-tier PackagesStep pages. Clicking the card is a route - * change to that verified provider's PackagesStep with this package loaded. - * - * Composes Card + Typography. - */ -export const NearbyPackageCard = React.forwardRef( - ({ packageName, price, providerName, location, rating, reviewCount, onClick, sx }, ref) => { - return ( - - {/* Package name + price */} - - - {packageName} - - - ${price.toLocaleString('en-AU')} - - - - {/* Provider info */} - - - {providerName} - - {rating != null && ( - <> - - · - - - - {rating} - {reviewCount != null ? ` (${reviewCount})` : ''} - - - )} - - · - - - - {location} - - - - ); - }, -); - -NearbyPackageCard.displayName = 'NearbyPackageCard'; -export default NearbyPackageCard; diff --git a/src/components/molecules/NearbyPackageCard/index.ts b/src/components/molecules/NearbyPackageCard/index.ts deleted file mode 100644 index a8e76da..0000000 --- a/src/components/molecules/NearbyPackageCard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { NearbyPackageCard, type NearbyPackageCardProps } from './NearbyPackageCard';