Skip to content

Latest commit

 

History

History
192 lines (150 loc) · 13.3 KB

File metadata and controls

192 lines (150 loc) · 13.3 KB

AGENTS.md — delivery-promise-components

Repository Purpose

vtex.delivery-promise-components is a VTEX IO Store Framework app exporting blocks that let shoppers set a postal code, choose delivery vs. pickup, and pick a pickup point — gated to stores using the Delivery Promise platform service (closed beta).

Blocks: shopper-location-setter, shipping-method-selector, pickup-point-selector, availability-badges. Extra React exports for host apps: PickupModalPresentational, pickupSearchClient, pickupInPointPreference.

Sources of Truth

File What it defines
manifest.json App identity (vtex.delivery-promise-components@1.1.1), builders (react, messages, docs, store), platform deps, registries (smartcheckout)
store/interfaces.json Block interfaces, component bindings, content schemas (props)
react/client.ts All HTTP calls to platform APIs — read first when changing data flow
react/utils/cookie.ts shipping_info cookie + __RUNTIME__.segmentToken reading; helpers for country, orderform, facets
react/constants.ts SHIPPING_INFO_COOKIE, DEFAULT_TRADE_POLICY = '1', Pixel event ids, error codes
react/context/DeliveryPromiseContext.tsx Context shape (State), action union, providers, hook exports
react/context/useDeliveryPromise.ts All business logic — action handler, side effects, session orchestration
react/context/DeliveryPromiseProviderCore.tsx Mounts UnavailableItemsModal once, wires state/dispatch contexts
react/pickupInPointPreference.ts localStorage pickup preference (key shared with vtex.search-result PLP)
react/modules/suppressAutoGeolocationSession.ts sessionStorage flag controlling auto-geolocation after CLEAR_ZIPCODE
react/modules/pixelHelper.ts OrderForm → Pixel cart-item mapper
react/typings/ Platform typings — regenerated by Toolbelt; do not hand-edit
messages/en.json Reference locale; all other locales must mirror keys (intl-equalizer)
docs/README.md User-facing block reference (props, CSS handles)
README.md Contributor guide (run, test, publish)

Architecture

DeliveryPromiseProvider (single instance, mounted via theme StoreWrapper) wires the context, mounts UnavailableItemsModal once, and contains all blocks. Blocks register themselves on mount (uiRegistry tracks { required } per block type). All mutations go through useDeliveryPromise() which holds the only side-effect logic and is the sole caller of react/client.ts. Each block must be a singleton per type — the registry has no ref counting; the last mount wins if duplicates exist.

External APIs (all fetch, no GraphQL)

All HTTP traffic lives in react/client.ts, plus one Nominatim call in react/components/ShopperLocationDetectorButton.tsx.

Checkout

  • GET /api/checkout/pub/postal-code/{country}/{zipCode}?an={account}getAddress. Resolves city + geoCoordinates; called before nearly every other Delivery Promise call.
  • POST /api/checkout/pub/orderForm/{orderFormId}/attachments/shippingDataupdateOrderForm / clearOrderFormShipping.
  • GET /api/checkout/pub/orderForm/{orderFormId}getCartProducts. Cart used by the availability gate. orderFormId from localStorage.orderform.

VTEX Sessions

  • POST /api/sessionsupdateSession / clearShippingSession. Writes public.facets.value as a ;-joined string, e.g. zip-code=12345;country=BRA;coordinates=-23,-46;shipping=delivery;pickupPoint=abc.

Intelligent Search

  • GET /api/intelligent-search/v0/pickup-point-availability/trade-policy/{salesChannel}?zip-code=...&an=...&country=...getPickups. Called with credentials: 'omit'.
  • GET /api/io/_v/api/intelligent-search/catalog_count?zip-code=...&coordinates=...getCatalogCount. If total === 0 the postal code is rejected (PRODUCTS_NOT_FOUND_ERROR_CODE).

Delivery Promise BFF (vtex.delivery-promises-bff)

All return { unavailableItemIds }. Optional ?sc={salesChannel} is appended when defined.

  • POST /api/delivery-promises-bff/availability/deliveryorpickup?an={account} — default cart-availability check (header / ShopperLocationSetter flow).
  • POST /api/delivery-promises-bff/availability/delivery?an={account} — PLP postal facet flow + SELECT_DELIVERY_SHIPPING_OPTION.
  • POST /api/delivery-promises-bff/availability/pickupid?an={account}&pickupId={id}UPDATE_PICKUP.

Body shape:

{ "location": { "zipCode": "...", "coordinate": { "longitude": ..., "latitude": ... }, "country": "..." },
  "items": [ { "itemId": "<sku>", "productId": "<catalog>" } ] }

External (non-VTEX) — Nominatim

  • GET https://nominatim.openstreetmap.org/reverse?format=json&lat=...&lon=... — only from ShopperLocationDetectorButton. Public unauthenticated endpoint subject to OSM usage policy.

Cookies, Session, and Storage

This app keeps shopper location state in four places, deliberately, because the storefront, Intelligent Search, and Checkout each read from different sources.

1. shipping_info cookie (custom — owned by this app)

  • Constant: SHIPPING_INFO_COOKIE = 'shipping_info' (react/constants.ts).
  • Written by setCookie in client.ts:updateSession, cleared in clearShippingSession. 30-min expiry, path=/.
  • Stores the same facets value as the session, with ; replaced by : (cookies disallow ;): zip-code=12345:country=BRA:coordinates=-23,-46:shipping=delivery:pickupPoint=abc.
  • Read first by getFacetsData (react/utils/cookie.ts), before falling back to the segment token. Code comment: "__RUNTIME__.segmentToken is not reliable for the facets. It might not be updated."

2. VTEX Segment token (__RUNTIME__.segmentToken)

  • Read-only fallback; produced by the platform render runtime (base64-encoded JSON).
  • Decoded by getCountryCode (returns countryCode) and as a fallback inside getFacetsData (extracts facets).
  • Why not the source of truth: segment token can lag; the custom cookie is updated synchronously by updateSession so reads see new values immediately.

3. VTEX Session (/api/sessions, vtex.session-client)

  • Authoritative storefront session.
  • Read via useRenderSession() to get session.namespaces.store.channel.value (sales channel / trade policy; falls back to DEFAULT_TRADE_POLICY = '1').
  • Written via POST /api/sessions to set public.facets.value. Propagates to Intelligent Search and other storefront services.

4. Browser storage

  • localStorage.orderform — read by getOrderFormId to talk to Checkout.
  • localStorage['vtex.search.pickupInPoint'] — pickup preference shared with vtex.search-result PLP. Shape: { id, friendlyName, address, postalCode }. Invalidated when stored postalCode ≠ current. Helpers: pickupInPointPreference.{readStoredPickupPreference,persistPickupPreference}.
  • sessionStorage['vtex:delivery-promise:suppressAutoGeolocation'] — set on CLEAR_ZIPCODE, cleared on successful submitZipcode. Prevents re-prompting geolocation after the user explicitly cleared their location.

Reducer Actions

Defined in DeliveryPromiseContext.tsx, handled in useDeliveryPromise.ts.

Action Effect
UPDATE_ZIPCODE Cart-availability check (BFF delivery or deliveryorpickup per args.cartAvailability), then submitZipcode. May location.reload() (deferred when shipping-method-selector is mounted as required).
UPDATE_PICKUP BFF pickupid check, then selectPickup → updates session + reloads. Persists pickup preference.
SELECT_DELIVERY_SHIPPING_OPTION BFF delivery check, then writes shipping=delivery to session + reloads.
RESET_FULFILLMENT_METHOD Clears shipping/pickupPoint from session + reloads.
CLEAR_ZIPCODE Clears Checkout shippingData, session facets, shipping_info cookie; sets suppressAutoGeolocation; reloads.
REGISTER_*_BLOCK / UNREGISTER_*_BLOCK Track { required } flag in uiRegistry.{shopperLocation,shippingMethod}.
REQUEST_OPEN_SHIPPING_METHOD_MODAL Increments shippingMethodModalRequestId so a sibling shipping-method-selector opens its modal.
ABORT_UNAVAILABLE_ITEMS_ACTION Closes UnavailableItemsModal without removing items.
CONTINUE_UNAVAILABLE_ITEMS_ACTION Removes unavailable items via vtex.order-items (fires removeFromCart Pixel event), then runs the deferred action.

Reload semantics

  • submitZipcode(reload) computes effectiveReload = reload && !shippingMethodRequired.
  • If shipping-method-selector is mounted with required: true, reload is deferred so the method modal can open first.
  • When both location and method are required, the method modal is not auto-requested at submit time (the location flow handles it afterwards) so modals don't stack.

Platform Dependencies (manifest.json)

Dep Use
vtex.styleguide UI primitives (Modal, Button, Input).
vtex.css-handles Stable theme-customization class anchors.
vtex.render-runtime useRuntime() (account), useSSR(), __RUNTIME__.segmentToken.
vtex.pixel-manager Fires removeFromCart; subscribes to item-added-to-cart-shipping-modal.
vtex.device-detector Mobile/desktop UI adaptation.
vtex.address-form Postal code input pieces.
vtex.product-summary-context AvailabilityBadges reads product.deliveryPromisesBadges.
vtex.order-items addItems/removeItem for the unavailable-items flow.
vtex.session-client useRenderSession() for sales channel + session loading state.

Verified Commands

make dev          # yarn install (root + react) + vtex setup
make lint         # yarn lint (eslint, no --fix)
make test         # cd react && yarn test (vtex-test-tools)
make coverage     # cd react && yarn test --coverage  (60% threshold in react/jest.config.js)
make check        # lint + test (pre-PR gate)
make link         # vtex link (uses active VTEX account/workspace)
make run          # alias for make link

Root yarn scripts: yarn lint, yarn format, yarn lint:locales (intl-equalizer), yarn test.

Expected Skills

  • vtex-io-cli · vtex-io-app-structure · vtex-io-react-apps · vtex-store-framework-navigator
  • specification + implementing (vtex-agent-skills) for the SDD Lite workflow

Expected MCPs

  • GitHub MCP — cross-repo references, issues, PRs (declared in .mcp.json).
  • Atlassian MCP — Jira/Confluence context when tracking against the roadmap.

Multi-repo Specs

Part of the is-io-specs multi-repo workspace alongside vtex.intelligent-search-api, vtex.search-graphql, vtex.search-resolver, vtex.search-result, and vtex.search-session. SpecKit artifacts (constitution.md, scope_of_work/, specs/<feature>/) live at the parent aggregator:

is-io-specs/
├── .specify/memory/constitution.md     # team contract across all 6 apps
├── docs/scope_of_work/                  # per-feature inputs to /speckit.specify
└── specs/<feature>/                     # spec.md, plan.md, tasks.md, analysis.md

This repo intentionally has no local .specify/. Because this is a public repo, multi-repo storage is required (see the Multi-repo spec-kit extension).

Autonomy Limits

Toolbelt and platform

  • Never run vtex link, vtex publish, vtex deploy, workspace promotion, or any account/workspace-changing Toolbelt command without explicit confirmation.
  • Ask before running vtex setup --tooling — it can overwrite react/.eslintrc and root Prettier/ESLint config; this repo has custom rules (eslint-config-vtex, eslint-config-vtex-react).
  • Never modify manifest.json version directly — version bumps go through vtex release <patch|minor|major> stable (Danger CI in .github/workflows/danger.yml).
  • Never modify react/typings/ — regenerated by vtex setup --typings --ignore-linked.
  • Ask before adding/removing builders in manifest.json — affects what BuilderHub validates and packages.

Code constraints

  • Single instance per block type. No ref counting; do not introduce multi-mount logic without a context redesign.
  • Do not bypass the cart-availability gate in useDeliveryPromise. It prevents shoppers from silently losing cart items when location/method changes.
  • Do not change the shipping_info cookie format without coordinated changes to getFacetsData — Intelligent Search relies on the same facets value the session has, parsed from the cookie when the segment token lags.
  • Sales channel must come from session.namespaces.store.channel.value, falling back to DEFAULT_TRADE_POLICY = '1'. Do not hard-code.
  • Do not call new external (non-VTEX) endpoints without review. The only one today is Nominatim from the geolocation button; adding more changes the privacy/legal surface.

Ownership and i18n

  • docs/ is owned by @vtex-apps/technical-writers; messages/ by @vtex-apps/localization (see .github/CODEOWNERS). Coordinate cross-folder changes accordingly.
  • messages/en.json is the reference locale. New keys must be added in all locales — yarn lint:locales (and the lint-staged hook on messages/*.json) enforces this.
  • Do not commit secrets, account names, workspace names, or store-specific data. All per-store context lives in the platform.