feat(pwa): closed-resto sheet at load (next-opening + stale-tab) + reusable delivery-address sheet on disabled Livraison#490
Merged
Conversation
…_VERDICT + readServiceStatus Red specs (TDD) for both features: - decideNextOpening / formatNextOpeningLabel / clientIsOpenNow (Feature A pure core) - decideDisabledDeliveryTap predicate (Feature B routing) - delivery-mode reducer ADOPT_VERDICT transition (Feature B) - backend readServiceStatus public query contract + tenant isolation (Feature A.1) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Feature A — closed-restaurant UX detected at LOAD (Uber-Eats parity):
- backend readServiceStatus public query exposes { isOpen, windows }
- lib/availability pure core: clientIsOpenNow + decideNextOpening (next
window-start in Europe/Paris: today/tomorrow/this-week/wrap) + FR label
- <ServiceStatusProvider>: Convex reactive sub + ~30s wall-clock tick so a
natural closing (no data change) flips the UI mid-browse (stale-tab)
- <ClosedRestoSheet>: dismissable bottom sheet, bypasses the address form
when closed, browse menu read-only (checkout gate left as safety net)
Feature B — reusable delivery-address sheet on a disabled Livraison tap:
- decideDisabledDeliveryTap predicate (closed-sheet / prompt / edit / none)
- ADOPT_VERDICT reducer transition (enable + select Livraison, persist)
- shared usePlacesAutocomplete + useAddressQuoteChain hooks (DRY)
- <DeliveryAddressSheet>: replays the chain, adopts a deliverable verdict
without reload, keeps open with a message on a refusal
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… surfaces - <AddressFirstForm> now consumes usePlacesAutocomplete + useAddressQuoteChain (behaviour-preserving: A.1 address → deliverable → Wallet card → /menu intact) - <DeliveryModeProvider> consumes the extracted reducer + exposes adoptVerdict (persist + dispatch ADOPT_VERDICT) - <DeliveryModeToggle> takes tenantId, routes a disabled Livraison tap via decideDisabledDeliveryTap to the closed sheet or the address sheet - menu-view / panier-body / app/page wrap the toggle in <ServiceStatusProvider> and mount <ClosedRestoSheet>; home uses <AddressFirstHome> wrapper Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related UX overhauls in the PWA Client (
apps/web), one PR.Feature A — Closed-restaurant UX, detected at LOAD (Uber-Eats parity)
Previously a customer had to enter an address before discovering the resto was closed (the
hors_horaireverdict only came after the quote chain). The opening hours are known at page load, so we surface a « Resto fermé » bottom sheet immediately and bypass the address-first form when closed — a delivery quote is pointless when closed.readServiceStatus(newpublicTenantQueryinlib/delivery/quote.ts) returns{ isOpen, windows }, reading only through the sanctionedlistTenantServiceWindowsseam (ADR 0010).readServiceOpenleft intact.lib/availability/decide-next-opening.ts:clientIsOpenNow,decideNextOpening(next window-start strictly after now — later today / tomorrow / later this week / wrap to next week / no windows → null, DST-correct Europe/Paris) and the FR label (Réouverture aujourd'hui à 18h00/demain à 11h30/lundi à 11h30/Horaires non communiqués). TDD'd (18 cases)./,<AddressFirstHome>overlays the dismissable closed sheet; « Voir la carte » routes to/menufor read-only browse. Ordering/checkout stays blocked by the existingisOpenNow/hors_horairegate (safety net untouched).<ServiceStatusProvider>combines the Convex reactive subscription onreadServiceStatus(an admin editing hours propagates live) with a ~30s client interval re-evaluatingclientIsOpenNowagainst the wall clock — because a natural 22:00 closing crosses the clock with no data change, so reactivity alone would never push it. On flip open→closed the sheet appears mid-browse; on closed→open it dismisses.Feature B — Reusable delivery-address sheet on a disabled « Livraison » tap
A disabled Livraison button is no longer a dead tap.
decideDisabledDeliveryTap(pure predicate): closed → defer to the closed sheet; verdict null → address sheet PROMPT mode (« Renseigne ton adresse »);hors_zone/surge→ address sheet EDIT mode, pre-filled (« Modifie ton adresse »);hors_horaireverdict → defer to closed UX.<DeliveryAddressSheet>replays the address→quote chain. On adeliverableverdict it dispatches the newADOPT_VERDICTreducer action (enable + select Livraison, persist to localStorage) and closes — no page reload. On a refusal it shows the reason inside the sheet and stays open to try another address.useAddressQuoteChain+usePlacesAutocomplete, consumed by BOTH<AddressFirstForm>and the sheet. The Google Places loader is still brought in via a DYNAMICimport()inside auseEffect(guardrailgooglemaps-loader-ssr-bug— never a static top-level import).<AddressFirstForm>refactor is behaviour-preserving: the validated A.1 flow (address → deliverable → palier 1 Wallet card → /menu) is unchanged.Tests
TDD:
test:→feat:→refactor:. Pure functions, theADOPT_VERDICTtransition, the disabled-tap predicate, and the backend query contract + tenant isolation are all unit-pinned. Full suites green locally: web 373, backend 1665;typecheck+lintclean (0 errors). No new npm deps; no shared barrel edits.Manual checks
/→ bottom sheet « Resto fermé · réouverture à X » SANS saisir d'adresse ; « Voir la carte » → menu parcourable./menuonglet frais → taper « Livraison » → sheet adresse (prompt) → adresse livrable → « Livraison » s'active sans reload.hors_zone→ taper « Livraison » → sheet pré-rempli (edit) pour modifier l'adresse.🤖 Generated with Claude Code