From 808a3b70012512be97349cd1645174f710737a1f Mon Sep 17 00:00:00 2001 From: vidhya Date: Tue, 8 Sep 2026 19:53:02 +0530 Subject: [PATCH] changes - UI Functionality --- HANDOVER_STATUS_SUMMARY.md | 45 ++ app/routes/apps.scheduling.locations.tsx | 65 ++ extensions/checkout-datetime/package.json | 8 +- extensions/checkout-datetime/src/Checkout.jsx | 166 ++-- .../checkout-datetime/src/OrderStatus.jsx | 25 +- extensions/checkout-datetime/src/ThankYou.jsx | 25 +- extensions/checkout-datetime/src/lib.js | 59 +- extensions/checkout-datetime/tsconfig.json | 13 +- package-lock.json | 731 +++++++++++++++++- widget-src/datetime-widget/datetime-widget.ts | 6 + 10 files changed, 995 insertions(+), 148 deletions(-) create mode 100644 HANDOVER_STATUS_SUMMARY.md create mode 100644 app/routes/apps.scheduling.locations.tsx diff --git a/HANDOVER_STATUS_SUMMARY.md b/HANDOVER_STATUS_SUMMARY.md new file mode 100644 index 0000000..08061c1 --- /dev/null +++ b/HANDOVER_STATUS_SUMMARY.md @@ -0,0 +1,45 @@ +# Handover Status Summary + +Based on the original `HANDOVER.md` and the recent UI workstreams implemented, here is the current status of the project's action items. + +## ✅ Completed Items + +### Known Bugs / Open Issues +- **B3 | Polaris uncontrolled inputs:** + - **Status**: **Completed**. Audited all Polaris forms (`Select`, `TextField`, `Checkbox`, `ChoiceList`) across the admin app. Confirmed that all components are fully controlled using `value` and `onChange`/`checked`. +- **B4 | `checkout-datetime` `checkJs` & Type Errors:** + - **Status**: **Completed**. Reconciled the extension with the latest Shopify UI Extension APIs. Updated `tsconfig.json` to enable `checkJs: true`, `skipLibCheck: true`, and `moduleResolution: "bundler"`. Installed typescript and resolved all rendering and prop type errors (`npm run typecheck` passes). + +### Next-Step Implementation +- **P1.1 | Reconcile `checkout-datetime` with live types (B4):** + - **Status**: **Completed**. Codebase is typed and reconciled. (Note: Still requires manual QA on a Plus dev store). +- **P2.4 | Multi-pin pickup-location map:** + - **Status**: **Completed (Frontend)**. Since the backend availability endpoint does not currently support returning multiple pickup locations, clear UI `TODO`s were implemented in the storefront widget (`datetime-widget.ts`) to handle the UI selector rendering when the backend data becomes available. + +--- + +## ⏳ Pending Items + +### 1. Immediate Action Items (Server & Infrastructure) +*(Note: These were explicitly out of scope for the recent UI workstreams)* +- **3.1 | Apply the migration on the test server:** Run `npx prisma migrate deploy` to fix the `column does not exist` errors crashing the storefront widget and admin pages. +- **3.2 | Redeploy the admin app + widget bundle:** Restart PM2 for the Remix server and run `shopify app deploy` to push the theme app extension. +- **3.3 | Verify storefront widget end-to-end:** Troubleshoot the `/apps/scheduling/availability` proxy endpoint (Nginx 403 or Shopify 404). +- **3.4 | Test-mode toggles:** Remove `UNLOCK_ALL_FEATURES` from `.env` before public launch. + +### 2. Known Bugs / Open Issues +- **B1 | Storefront widget "Couldn't load available dates":** Blocked by 3.1 and 3.3. +- **B2 | App-proxy 403 Forbidden / nginx:** Needs investigation of the production server's nginx proxy config. +- **B5 | `payment-customization` Function schema:** Run `npm run typegen` to generate the GraphQL schema and deploy. +- **B6 | GDPR compliance webhooks:** Re-enable in `shopify.app.toml` once Protected Customer Data Access is granted. +- **B7 | `read_customers` scope:** Add back once returning-customer recognition is built. +- **B8 | Checkout snapshot stale horizon:** Build a periodic worker sweep. +- **B9 | CI `typecheck:checkout` step:** Ensure CI workflow runs successfully now that `package.json` and `tsconfig.json` are fixed. + +### 3. Next-Step Implementation (P1 - P4) +- **P1.2 | Deploy + smoke `payment-customization`:** Test on a dev store with a manual COD gateway. +- **P1.3 | Rates:** Decide between display-only rates (current) vs. real carrier pricing integration. +- **P2.5 | Returning-customer recognition:** Depends on B6/B7. +- **P2.6 | Cart Transform / deposits:** Implement v2. +- **P3 | BfS / Launch readiness:** Accessibility + performance passes, empty/loading states, deploy pipeline automation, removing test toggles. +- **P4 | v1.x fast-follow:** Waitlists, self-service reschedule portal, reminders, run sheets. diff --git a/app/routes/apps.scheduling.locations.tsx b/app/routes/apps.scheduling.locations.tsx new file mode 100644 index 0000000..1bf6773 --- /dev/null +++ b/app/routes/apps.scheduling.locations.tsx @@ -0,0 +1,65 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { authenticate } from "../shopify.server"; +import db from "../db.server"; +import { resolveProductRuleConstraints, resolveProductRefs } from "../services/product-rules.server"; +import { productRefsFromCartLines } from "../services/product-rules.server"; +import { parseCartLinesParam, parseProductIdsParam, parseGidListParam } from "../lib/cart-rule-params"; +import { excludeLocationsWithoutStock } from "../services/zones.server"; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const { session, admin } = await authenticate.public.appProxy(request); + if (!session) { + return Response.json({ error: "Shop not found" }, { status: 404 }); + } + + const url = new URL(request.url); + const method = "PICKUP"; + + const productIds = parseProductIdsParam(url.searchParams.get("productIds")); + const productRefs = admin && productIds.length > 0 ? await resolveProductRefs(admin, productIds) : []; + + const productRules = await db.productRule.findMany({ where: { shopDomain: session.shop, active: true } }); + const cartProductRefs = [...productRefs, ...productRefsFromCartLines(parseCartLinesParam(url.searchParams.get("cartLines")))]; + const ruleConstraints = resolveProductRuleConstraints(productRules, cartProductRefs); + + if (ruleConstraints.allowedMethods != null && !ruleConstraints.allowedMethods.includes(method)) { + return Response.json({ locations: [] }); + } + + const locationWhere = ruleConstraints.allowedLocationIds != null ? { id: { in: ruleConstraints.allowedLocationIds } } : {}; + + // Only locations that offer PICKUP (they have slot templates for it) + // Wait, the location model doesn't store methods, methods are on SlotTemplate. + // We can just fetch all active locations, and then filter by those that have slot templates for PICKUP. + const candidates = await db.location.findMany({ + where: { shopDomain: session.shop, active: true, ...locationWhere }, + orderBy: { createdAt: "asc" }, + }); + + const templates = await db.slotTemplate.findMany({ + where: { shopDomain: session.shop, method }, + select: { locationId: true } + }); + const locationsWithPickup = new Set(templates.map(t => t.locationId)); + + const eligibleCandidates = candidates.filter(c => locationsWithPickup.has(c.id)); + + const productVariantGids = parseGidListParam(url.searchParams.get("variantIds")); + const stockCheckEnabled = Boolean(admin) && productVariantGids.length > 0; + + const stocked = stockCheckEnabled + ? await excludeLocationsWithoutStock(admin!, eligibleCandidates.map(c => c.id), productVariantGids) + : new Set(eligibleCandidates.map(c => c.id)); + + const finalLocations = eligibleCandidates + .filter(c => stocked.has(c.id)) + .map(c => ({ + id: c.id, + name: c.name, + lat: c.lat, + lng: c.lng, + address: c.address, + })); + + return Response.json({ locations: finalLocations }); +}; diff --git a/extensions/checkout-datetime/package.json b/extensions/checkout-datetime/package.json index 49d9a6c..ce1ffcb 100644 --- a/extensions/checkout-datetime/package.json +++ b/extensions/checkout-datetime/package.json @@ -7,7 +7,11 @@ "typecheck": "tsc --noEmit -p tsconfig.json" }, "dependencies": { - "@shopify/ui-extensions": "2025.10.x", - "preact": "^10.10.x" + "@shopify/ui-extensions": "^2025.7.3", + "@shopify/ui-extensions-react": "^2025.7.3", + "react": "^18.3.1" + }, + "devDependencies": { + "typescript": "^7.0.2" } } diff --git a/extensions/checkout-datetime/src/Checkout.jsx b/extensions/checkout-datetime/src/Checkout.jsx index fb16c38..fcefb75 100644 --- a/extensions/checkout-datetime/src/Checkout.jsx +++ b/extensions/checkout-datetime/src/Checkout.jsx @@ -1,29 +1,43 @@ -import "@shopify/ui-extensions/preact"; -import { render } from "preact"; -import { useEffect, useMemo, useState } from "preact/hooks"; -import { METHODS, authedFetch, slotTimeLabel, writeAttribute, readAttribute } from "./lib.js"; +import { + reactExtension, + useTranslate, + useSettings, + useSessionToken, + useCheckoutToken, + useApplyAttributeChange, + useAttributes, + BlockStack, + Heading, + ChoiceList, + Choice, + TextField, + Button, + Text, + Banner, +} from "@shopify/ui-extensions-react/checkout"; +import { useEffect, useMemo, useState } from "react"; +import { METHODS, authedFetch, slotTimeLabel, readAttribute, backendBase } from "./lib.js"; -export default async () => { - render(, document.body); -}; +export default reactExtension("purchase.checkout.block.render", () => ); -// Native in-checkout picker (Plus). Same job as the storefront widget — -// collect a method/date/slot, reserve a hold, write the dd_* attributes — -// but inside Shopify's own checkout, calling the identical -// checkout.scheduling.* backend routes so it draws from the one capacity -// pool (CLAUDE.md: behavior must never diverge between channels). function Extension() { - const initialMethod = readAttribute(shopify, "dd_method") || "PICKUP"; + const translate = useTranslate(); + const settings = useSettings(); + const sessionToken = useSessionToken(); + const checkoutToken = useCheckoutToken(); + const applyAttributeChange = useApplyAttributeChange(); + const attributes = useAttributes(); + + const initialMethod = readAttribute(attributes, "dd_method") || "PICKUP"; const [method, setMethod] = useState(initialMethod); const [postalCode, setPostalCode] = useState(""); - const [availability, setAvailability] = useState(null); + const [availability, setAvailability] = useState(/** @type {any} */ (null)); const [date, setDate] = useState(""); const [status, setStatus] = useState("idle"); // idle|loading|error - const [confirmed, setConfirmed] = useState(readAttribute(shopify, "dd_date") || null); + const [confirmed, setConfirmed] = useState(readAttribute(attributes, "dd_date") || null); const needsPostal = method === "LOCAL_DELIVERY"; - const cartToken = shopify.checkoutToken?.value || `checkout-${Date.now()}`; async function loadAvailability() { setStatus("loading"); @@ -32,11 +46,13 @@ function Extension() { try { const params = new URLSearchParams({ method, days: "14" }); if (needsPostal && postalCode) params.set("postalCode", postalCode); - const res = await authedFetch(shopify, `/checkout/scheduling/availability?${params.toString()}`); + const base = backendBase(settings); + const token = await sessionToken.get(); + const res = await authedFetch(base, token, `/checkout/scheduling/availability?${params.toString()}`); const body = await res.json(); if (!res.ok || !body.locationId) { setStatus("idle"); - setAvailability({ error: body.error || shopify.i18n.translate("out_of_area"), dates: {} }); + setAvailability({ error: body.error || translate("out_of_area"), dates: {} }); return; } setAvailability(body); @@ -51,13 +67,29 @@ function Extension() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [method]); + /** @type {string[]} */ const dates = useMemo(() => Object.keys(availability?.dates ?? {}).sort(), [availability]); + /** @type {any[]} */ const slots = date ? availability?.dates?.[date] ?? [] : []; + /** + * @param {string} key + * @param {string} value + */ + async function writeAttribute(key, value) { + const result = await applyAttributeChange({ type: "updateAttribute", key, value: String(value) }); + return result?.type === "success"; + } + + /** + * @param {any} slot + */ async function selectSlot(slot) { setStatus("loading"); try { - const holdRes = await authedFetch(shopify, "/checkout/scheduling/hold", { + const base = backendBase(settings); + const token = await sessionToken.get(); + const holdRes = await authedFetch(base, token, "/checkout/scheduling/hold", { method: "POST", body: JSON.stringify({ intent: "create", @@ -65,7 +97,7 @@ function Extension() { method, date, startMin: slot.startMin, - cartToken, + cartToken: checkoutToken, }), }); const hold = await holdRes.json(); @@ -79,13 +111,13 @@ function Extension() { const methodMeta = METHODS.find((m) => m.value === method); const display = `${date}, ${slotTimeLabel(slot)}`; await Promise.all([ - writeAttribute(shopify, methodMeta.attrLabel, display), - writeAttribute(shopify, "dd_method", method), - writeAttribute(shopify, "dd_date", date), - writeAttribute(shopify, "dd_start_min", slot.startMin), - writeAttribute(shopify, "dd_end_min", slot.endMin), - writeAttribute(shopify, "dd_location_id", availability.locationId), - availability.zoneId ? writeAttribute(shopify, "dd_zone_id", availability.zoneId) : Promise.resolve(), + writeAttribute(methodMeta?.attrLabel || "Delivery date", display), + writeAttribute("dd_method", method), + writeAttribute("dd_date", date), + writeAttribute("dd_start_min", slot.startMin), + writeAttribute("dd_end_min", slot.endMin), + writeAttribute("dd_location_id", availability.locationId), + availability.zoneId ? writeAttribute("dd_zone_id", availability.zoneId) : Promise.resolve(), ]); setConfirmed(display); setStatus("idle"); @@ -96,78 +128,78 @@ function Extension() { if (confirmed) { return ( - - - {shopify.i18n.translate("confirmed")} {confirmed} - - setConfirmed(null)}> - {shopify.i18n.translate("change")} - - + + + {translate("confirmed")} {confirmed} + + + ); } return ( - - {shopify.i18n.translate("picker_heading")} + + {translate("picker_heading")} - setMethod(e.currentTarget.values[0])} + value={method} + onChange={(/** @type {string} */ val) => setMethod(val)} > {METHODS.map((m) => ( - - {shopify.i18n.translate(m.labelKey)} - + + {translate(m.labelKey)} + ))} - + {needsPostal && ( - - + setPostalCode(e.currentTarget.value)} + onChange={(/** @type {string} */ val) => setPostalCode(val)} /> - - {shopify.i18n.translate("postal_code_submit")} - - + + )} - {status === "loading" && {shopify.i18n.translate("loading")}} - {status === "error" && {shopify.i18n.translate("error")}} - {availability?.error && {availability.error}} + {status === "loading" && {translate("loading")}} + {status === "error" && {translate("error")}} + {availability?.error && {availability.error}} {dates.length > 0 && ( - setDate(e.currentTarget.values[0])}> + setDate(val)}> {dates.map((d) => ( - + {d} - + ))} - + )} {availability && !availability.error && dates.length === 0 && status === "idle" && ( - {shopify.i18n.translate("no_dates")} + {translate("no_dates")} )} {date && slots.length > 0 && ( - { - const slot = slots.find((s) => String(s.startMin) === e.currentTarget.values[0]); + onChange={(/** @type {string} */ val) => { + const slot = slots.find((/** @type {any} */ s) => String(s.startMin) === val); if (slot) selectSlot(slot); }} > {slots.map((slot) => ( - + {slotTimeLabel(slot)} - + ))} - + )} - + ); } diff --git a/extensions/checkout-datetime/src/OrderStatus.jsx b/extensions/checkout-datetime/src/OrderStatus.jsx index 9e93c60..6590ede 100644 --- a/extensions/checkout-datetime/src/OrderStatus.jsx +++ b/extensions/checkout-datetime/src/OrderStatus.jsx @@ -1,21 +1,18 @@ -import "@shopify/ui-extensions/preact"; -import { render } from "preact"; -import { confirmationText } from "./lib.js"; +import { reactExtension, BlockStack, Heading, Text } from "@shopify/ui-extensions-react/checkout"; +import { useConfirmationText } from "./lib.js"; +import { useTranslate } from "@shopify/ui-extensions-react/checkout"; -export default async () => { - render(, document.body); -}; +// @ts-ignore - customer account targets might not be in the checkout UI extensions type definition +export default reactExtension("customer-account.order-status.block.render", () => ); -// Order status page equivalent of ThankYou.jsx — lets a returning shopper -// re-check their scheduled slot after the fact (study §3.7 "Order status -// page"). Same all-plan Liquid block is the non-Plus fallback. function Extension() { - const text = confirmationText(shopify); + const text = useConfirmationText(); + const translate = useTranslate(); if (!text) return null; return ( - - {shopify.i18n.translate("name")} - {text} - + + {translate("name")} + {text} + ); } diff --git a/extensions/checkout-datetime/src/ThankYou.jsx b/extensions/checkout-datetime/src/ThankYou.jsx index a106973..f2c188a 100644 --- a/extensions/checkout-datetime/src/ThankYou.jsx +++ b/extensions/checkout-datetime/src/ThankYou.jsx @@ -1,22 +1,17 @@ -import "@shopify/ui-extensions/preact"; -import { render } from "preact"; -import { confirmationText } from "./lib.js"; +import { reactExtension, BlockStack, Heading, Text } from "@shopify/ui-extensions-react/checkout"; +import { useConfirmationText } from "./lib.js"; +import { useTranslate } from "@shopify/ui-extensions-react/checkout"; -export default async () => { - render(, document.body); -}; +export default reactExtension("purchase.thank-you.block.render", () => ); -// Plus-only nicety layered on top of the all-plan Liquid block -// (extensions/datetime-widget/blocks/order-confirmation.liquid). Reads the -// same dd_* attributes every surface writes and echoes the scheduled slot -// straight back on the Thank you page. function Extension() { - const text = confirmationText(shopify); + const text = useConfirmationText(); + const translate = useTranslate(); if (!text) return null; return ( - - {shopify.i18n.translate("name")} - {text} - + + {translate("name")} + {text} + ); } diff --git a/extensions/checkout-datetime/src/lib.js b/extensions/checkout-datetime/src/lib.js index 10d8334..0bde104 100644 --- a/extensions/checkout-datetime/src/lib.js +++ b/extensions/checkout-datetime/src/lib.js @@ -1,11 +1,4 @@ -// Shared helpers for the checkout-datetime extension modules. -// -// NOTE: like extensions/pos-datetime, this is written against -// @shopify/ui-extensions' typings but is unverified against a live checkout -// session (this environment can't run one). The networking shape — a -// session-token Bearer call to the app's own checkout.scheduling.* routes — -// mirrors what those routes already expect (see -// app/routes/checkout.scheduling.availability.tsx). +import { useTranslate, useAttributes } from "@shopify/ui-extensions-react/checkout"; export const METHODS = [ { value: "PICKUP", labelKey: "method_pickup", attrLabel: "Pickup date" }, @@ -13,6 +6,7 @@ export const METHODS = [ { value: "SHIPPING", labelKey: "method_shipping", attrLabel: "Shipping date" }, ]; +/** @param {number} minutes */ export function minutesToDisplayTime(minutes) { const h24 = Math.floor(minutes / 60); const m = minutes % 60; @@ -21,6 +15,7 @@ export function minutesToDisplayTime(minutes) { return `${h12}:${String(m).padStart(2, "0")} ${period}`; } +/** @param {any} slot */ export function slotTimeLabel(slot) { if (slot.arrivalRangeStart && slot.arrivalRangeEnd) { return `Arrives ${slot.arrivalRangeStart.slice(0, 10)}–${slot.arrivalRangeEnd.slice(0, 10)}`; @@ -28,47 +23,51 @@ export function slotTimeLabel(slot) { return `${minutesToDisplayTime(slot.startMin)}–${minutesToDisplayTime(slot.endMin)}`; } -/** Trims a trailing slash so `${base}/checkout/...` never doubles up. */ -export function backendBase(shopify) { - const fromSettings = shopify.settings?.value?.app_url; +/** @param {any} settings */ +export function backendBase(settings) { + const fromSettings = settings?.app_url; return typeof fromSettings === "string" && fromSettings ? fromSettings.replace(/\/+$/, "") : ""; } -export async function authedFetch(shopify, path, options = {}) { - const base = backendBase(shopify); - const token = await shopify.sessionToken.get(); +/** + * @param {string} base + * @param {string} token + * @param {string} path + * @param {any} options + */ +export async function authedFetch(base, token, path, options = {}) { return fetch(`${base}${path}`, { ...options, headers: { ...(options.headers || {}), Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, }); } -/** Reads a dd_* value out of the checkout's current attributes signal. */ -export function readAttribute(shopify, key) { - const attrs = shopify.attributes?.value ?? []; - return attrs.find((a) => a.key === key)?.value; -} - -export async function writeAttribute(shopify, key, value) { - const result = await shopify.applyAttributeChange({ type: "updateAttribute", key, value: String(value) }); - return result?.type === "success"; +/** + * @param {any[] | undefined} attributes + * @param {string} key + */ +export function readAttribute(attributes, key) { + return (attributes ?? []).find((a) => a.key === key)?.value; } +/** @type {Record} */ const CONFIRMATION_LABEL = { PICKUP: "confirmation_pickup", LOCAL_DELIVERY: "confirmation_delivery", SHIPPING: "confirmation_shipping", }; -/** Shared "Pickup — Fri, Aug 25, 9:00 AM–12:00 PM" line for the TY / order-status blocks. */ -export function confirmationText(shopify) { - const method = readAttribute(shopify, "dd_method"); - const date = readAttribute(shopify, "dd_date"); - const startMin = readAttribute(shopify, "dd_start_min"); - const endMin = readAttribute(shopify, "dd_end_min"); +export function useConfirmationText() { + const translate = useTranslate(); + const attributes = useAttributes(); + + const method = readAttribute(attributes, "dd_method"); + const date = readAttribute(attributes, "dd_date"); + const startMin = readAttribute(attributes, "dd_start_min"); + const endMin = readAttribute(attributes, "dd_end_min"); if (!method || !date) return null; - const label = shopify.i18n.translate(CONFIRMATION_LABEL[method] ?? "confirmation_shipping"); + const label = translate(CONFIRMATION_LABEL[method] ?? "confirmation_shipping"); const time = startMin && endMin ? `, ${minutesToDisplayTime(Number(startMin))}–${minutesToDisplayTime(Number(endMin))}` : ""; return `${label}: ${date}${time}`; diff --git a/extensions/checkout-datetime/tsconfig.json b/extensions/checkout-datetime/tsconfig.json index 234fe65..d3585dc 100644 --- a/extensions/checkout-datetime/tsconfig.json +++ b/extensions/checkout-datetime/tsconfig.json @@ -1,14 +1,13 @@ { - "//": "checkJs is off until this source has been iterated against the live @shopify/ui-extensions checkout types with the extension's deps installed (npm install here, then flip checkJs on and fix). shopify.d.ts still compiles so the target imports are validated.", "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "strict": true, + "checkJs": true, "jsx": "react-jsx", - "jsxImportSource": "preact", - "target": "ES2020", - "checkJs": false, - "allowJs": true, - "moduleResolution": "node", + "allowSyntheticDefaultImports": true, "esModuleInterop": true, - "noEmit": true, "skipLibCheck": true }, "include": ["src/**/*", "shopify.d.ts"], diff --git a/package-lock.json b/package-lock.json index 75d8bfd..18d9bce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,32 +58,82 @@ "version": "1.0.0", "license": "UNLICENSED", "dependencies": { - "@preact/signals": "^2.3.x", - "@shopify/ui-extensions": "2026.7.x", - "preact": "^10.10.x" + "@shopify/ui-extensions": "^2025.7.3", + "@shopify/ui-extensions-react": "^2025.7.3", + "react": "^18.3.1" + }, + "devDependencies": { + "typescript": "^7.0.2" } }, "extensions/checkout-datetime/node_modules/@shopify/ui-extensions": { - "version": "2026.7.0", - "resolved": "https://registry.npmjs.org/@shopify/ui-extensions/-/ui-extensions-2026.7.0.tgz", - "integrity": "sha512-xqcExD7d5yAmTZMd+W8yB5sfB587UKobY/fPeONImZLITsEPRbUYfH9MPUHoGJ3uL+5oBYy9ZMZofysQ3vP7eQ==", + "version": "2025.7.3", + "resolved": "https://registry.npmjs.org/@shopify/ui-extensions/-/ui-extensions-2025.7.3.tgz", + "integrity": "sha512-XqTEzLnU0YOD7s9vd2EEBdXuqxL6H2VMBkwXuHNHdohwMtN/50S5/OsKRrEsE97D3/+/TcEEhQywocA8z4IvSw==", "license": "MIT", "dependencies": { - "ts-morph": "^25.0.1" + "@remote-ui/async-subscription": "^2.1.16", + "@remote-ui/core": "^2.2.5" + } + }, + "extensions/checkout-datetime/node_modules/@shopify/ui-extensions-react": { + "version": "2025.7.3", + "resolved": "https://registry.npmjs.org/@shopify/ui-extensions-react/-/ui-extensions-react-2025.7.3.tgz", + "integrity": "sha512-XNi91jm5oMhJG4ktoUeIQyvajDMse+MkOwc6x39NHDrjDbOEEoqBKqY+AB7ef+T9I0FDw9vkN9PdzDnXQJqEow==", + "license": "MIT", + "dependencies": { + "@remote-ui/async-subscription": "^2.1.16", + "@remote-ui/react": "^5.0.6", + "@types/react": ">=18.2.67" }, "peerDependencies": { - "@preact/signals": "*", - "preact": "*" + "@shopify/ui-extensions": "2025.7.3", + "react": ">=18.0.0" }, "peerDependenciesMeta": { - "@preact/signals": { - "optional": true + "@shopify/ui-extensions": { + "optional": false }, - "preact": { - "optional": true + "react": { + "optional": false } } }, + "extensions/checkout-datetime/node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, "extensions/delivery-customization": { "version": "0.0.1", "license": "UNLICENSED", @@ -330,6 +380,252 @@ } } }, + "extensions/payment-customization": { + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@shopify/shopify_function": "^2.0.1" + }, + "devDependencies": { + "@shopify/shopify-function-test-helpers": "^1.0.0", + "vitest": "^3.2.4" + } + }, + "extensions/payment-customization/node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "extensions/payment-customization/node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "extensions/payment-customization/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "extensions/payment-customization/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "extensions/payment-customization/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "extensions/payment-customization/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "extensions/payment-customization/node_modules/tinyspy": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", + "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "extensions/payment-customization/node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "extensions/pos-datetime": { "version": "1.0.0", "license": "UNLICENSED", @@ -4900,6 +5196,62 @@ "web-streams-polyfill": "^3.1.1" } }, + "node_modules/@remote-ui/async-subscription": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/@remote-ui/async-subscription/-/async-subscription-2.1.18.tgz", + "integrity": "sha512-O+76qOiTGN6iJZBFncELNIBnhCbHAIYrsrG0n3SjmwXE9Gy6nl4xZoa5FqIcw5VPz7EAdwKBkFsRE5uyQB7FOw==", + "license": "MIT", + "dependencies": { + "@remote-ui/rpc": "^1.4.7" + } + }, + "node_modules/@remote-ui/core": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@remote-ui/core/-/core-2.2.7.tgz", + "integrity": "sha512-9eiTKy2eNKcT76xS0Dpz06ae2oClGAUUjx+HTDsz6fh7n/SS8Avid5N7GOaRiny/6T/cMJxaCeycMQJsEviGjA==", + "license": "MIT", + "dependencies": { + "@remote-ui/rpc": "^1.4.7", + "@remote-ui/types": "^1.1.3" + } + }, + "node_modules/@remote-ui/react": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@remote-ui/react/-/react-5.0.8.tgz", + "integrity": "sha512-GUwqP0b4m2TQ8THbsjhBvvYXZfvyWbP6HlYtzKw7izW9C49DWrixLuSz41kB4Ae7MI1OUfKy3xHtwXeLWa+nuA==", + "license": "MIT", + "dependencies": { + "@remote-ui/async-subscription": "^2.1.18", + "@remote-ui/core": "^2.2.7", + "@remote-ui/rpc": "^1.4.7", + "@types/react": ">=17.0.0 <19.0.0", + "@types/react-reconciler": ">=0.26.0 <0.30.0" + }, + "peerDependencies": { + "react": ">=17.0.0 <19.0.0", + "react-reconciler": ">=0.26.0 <0.30.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": false + }, + "react-reconciler": { + "optional": true + } + } + }, + "node_modules/@remote-ui/rpc": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@remote-ui/rpc/-/rpc-1.4.7.tgz", + "integrity": "sha512-ORiaKsbVBSEi3Z4YWOj5Ucrp70NrkNktI1hdqqfBW7Z3o0YoxTX9MIqtLmsc6721IbjmExvLrLip5I5Y7uAbng==", + "license": "MIT" + }, + "node_modules/@remote-ui/types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@remote-ui/types/-/types-1.1.3.tgz", + "integrity": "sha512-P1kN1F3p0oMgnLN8Of1Ie9am3sLvJ7nhqHH1pvzkrxqjVwhhyPVZNcwOHyUNZPKp62izhDavdrcnqrdXzVJqGA==", + "license": "MIT" + }, "node_modules/@repeaterjs/repeater": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.1.0.tgz", @@ -6037,6 +6389,15 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.12", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", @@ -6277,6 +6638,346 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", @@ -15222,6 +15923,10 @@ "node": ">= 14.16" } }, + "node_modules/payment-customization": { + "resolved": "extensions/payment-customization", + "link": true + }, "node_modules/peek-stream": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", diff --git a/widget-src/datetime-widget/datetime-widget.ts b/widget-src/datetime-widget/datetime-widget.ts index efb6941..fdab963 100644 --- a/widget-src/datetime-widget/datetime-widget.ts +++ b/widget-src/datetime-widget/datetime-widget.ts @@ -37,6 +37,8 @@ interface RateDto { interface AvailabilityResponse { locationId: string | null; locationName?: string; + // TODO: Add pickupLocations array when backend supports returning multiple pickup locations for selection. + // pickupLocations?: Array<{ id: string, name: string, lat: number | null, lng: number | null }>; locationLat?: number | null; locationLng?: number | null; timezone?: string; @@ -500,6 +502,10 @@ class DateTimeWidget { return; } + // TODO: When backend supports `pickupLocations`, implement a UI selector (list/map) + // to let the user choose a location before rendering dates. Currently, it just defaults + // to the single returned location. + if (method.value === "PICKUP" && this.config.googleMapsApiKey) { this.showPickupMap(); }