changes - UI Functionality
Some checks failed
CI / Lint, Unit & Integration Tests (push) Has been cancelled
Some checks failed
CI / Lint, Unit & Integration Tests (push) Has been cancelled
This commit is contained in:
parent
8650b1cb0c
commit
808a3b7001
45
HANDOVER_STATUS_SUMMARY.md
Normal file
45
HANDOVER_STATUS_SUMMARY.md
Normal file
@ -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.
|
||||||
65
app/routes/apps.scheduling.locations.tsx
Normal file
65
app/routes/apps.scheduling.locations.tsx
Normal file
@ -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 });
|
||||||
|
};
|
||||||
@ -7,7 +7,11 @@
|
|||||||
"typecheck": "tsc --noEmit -p tsconfig.json"
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@shopify/ui-extensions": "2025.10.x",
|
"@shopify/ui-extensions": "^2025.7.3",
|
||||||
"preact": "^10.10.x"
|
"@shopify/ui-extensions-react": "^2025.7.3",
|
||||||
|
"react": "^18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^7.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,43 @@
|
|||||||
import "@shopify/ui-extensions/preact";
|
import {
|
||||||
import { render } from "preact";
|
reactExtension,
|
||||||
import { useEffect, useMemo, useState } from "preact/hooks";
|
useTranslate,
|
||||||
import { METHODS, authedFetch, slotTimeLabel, writeAttribute, readAttribute } from "./lib.js";
|
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 () => {
|
export default reactExtension("purchase.checkout.block.render", () => <Extension />);
|
||||||
render(<Extension />, document.body);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
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 [method, setMethod] = useState(initialMethod);
|
||||||
const [postalCode, setPostalCode] = useState("");
|
const [postalCode, setPostalCode] = useState("");
|
||||||
const [availability, setAvailability] = useState(null);
|
const [availability, setAvailability] = useState(/** @type {any} */ (null));
|
||||||
const [date, setDate] = useState("");
|
const [date, setDate] = useState("");
|
||||||
const [status, setStatus] = useState("idle"); // idle|loading|error
|
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 needsPostal = method === "LOCAL_DELIVERY";
|
||||||
const cartToken = shopify.checkoutToken?.value || `checkout-${Date.now()}`;
|
|
||||||
|
|
||||||
async function loadAvailability() {
|
async function loadAvailability() {
|
||||||
setStatus("loading");
|
setStatus("loading");
|
||||||
@ -32,11 +46,13 @@ function Extension() {
|
|||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({ method, days: "14" });
|
const params = new URLSearchParams({ method, days: "14" });
|
||||||
if (needsPostal && postalCode) params.set("postalCode", postalCode);
|
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();
|
const body = await res.json();
|
||||||
if (!res.ok || !body.locationId) {
|
if (!res.ok || !body.locationId) {
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
setAvailability({ error: body.error || shopify.i18n.translate("out_of_area"), dates: {} });
|
setAvailability({ error: body.error || translate("out_of_area"), dates: {} });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAvailability(body);
|
setAvailability(body);
|
||||||
@ -51,13 +67,29 @@ function Extension() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [method]);
|
}, [method]);
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
const dates = useMemo(() => Object.keys(availability?.dates ?? {}).sort(), [availability]);
|
const dates = useMemo(() => Object.keys(availability?.dates ?? {}).sort(), [availability]);
|
||||||
|
/** @type {any[]} */
|
||||||
const slots = date ? availability?.dates?.[date] ?? [] : [];
|
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) {
|
async function selectSlot(slot) {
|
||||||
setStatus("loading");
|
setStatus("loading");
|
||||||
try {
|
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",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
intent: "create",
|
intent: "create",
|
||||||
@ -65,7 +97,7 @@ function Extension() {
|
|||||||
method,
|
method,
|
||||||
date,
|
date,
|
||||||
startMin: slot.startMin,
|
startMin: slot.startMin,
|
||||||
cartToken,
|
cartToken: checkoutToken,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const hold = await holdRes.json();
|
const hold = await holdRes.json();
|
||||||
@ -79,13 +111,13 @@ function Extension() {
|
|||||||
const methodMeta = METHODS.find((m) => m.value === method);
|
const methodMeta = METHODS.find((m) => m.value === method);
|
||||||
const display = `${date}, ${slotTimeLabel(slot)}`;
|
const display = `${date}, ${slotTimeLabel(slot)}`;
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
writeAttribute(shopify, methodMeta.attrLabel, display),
|
writeAttribute(methodMeta?.attrLabel || "Delivery date", display),
|
||||||
writeAttribute(shopify, "dd_method", method),
|
writeAttribute("dd_method", method),
|
||||||
writeAttribute(shopify, "dd_date", date),
|
writeAttribute("dd_date", date),
|
||||||
writeAttribute(shopify, "dd_start_min", slot.startMin),
|
writeAttribute("dd_start_min", slot.startMin),
|
||||||
writeAttribute(shopify, "dd_end_min", slot.endMin),
|
writeAttribute("dd_end_min", slot.endMin),
|
||||||
writeAttribute(shopify, "dd_location_id", availability.locationId),
|
writeAttribute("dd_location_id", availability.locationId),
|
||||||
availability.zoneId ? writeAttribute(shopify, "dd_zone_id", availability.zoneId) : Promise.resolve(),
|
availability.zoneId ? writeAttribute("dd_zone_id", availability.zoneId) : Promise.resolve(),
|
||||||
]);
|
]);
|
||||||
setConfirmed(display);
|
setConfirmed(display);
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
@ -96,78 +128,78 @@ function Extension() {
|
|||||||
|
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
return (
|
return (
|
||||||
<s-stack gap="base">
|
<BlockStack spacing="base">
|
||||||
<s-text>
|
<Text>
|
||||||
{shopify.i18n.translate("confirmed")} {confirmed}
|
{translate("confirmed")} {confirmed}
|
||||||
</s-text>
|
</Text>
|
||||||
<s-button onClick={() => setConfirmed(null)}>
|
<Button onPress={() => setConfirmed(null)}>
|
||||||
{shopify.i18n.translate("change")}
|
{translate("change")}
|
||||||
</s-button>
|
</Button>
|
||||||
</s-stack>
|
</BlockStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<s-stack gap="base">
|
<BlockStack spacing="base">
|
||||||
<s-heading>{shopify.i18n.translate("picker_heading")}</s-heading>
|
<Heading>{translate("picker_heading")}</Heading>
|
||||||
|
|
||||||
<s-choice-list
|
<ChoiceList
|
||||||
name="dd-method"
|
name="dd-method"
|
||||||
values={[method]}
|
value={method}
|
||||||
onChange={(e) => setMethod(e.currentTarget.values[0])}
|
onChange={(/** @type {string} */ val) => setMethod(val)}
|
||||||
>
|
>
|
||||||
{METHODS.map((m) => (
|
{METHODS.map((m) => (
|
||||||
<s-choice key={m.value} value={m.value}>
|
<Choice key={m.value} id={m.value}>
|
||||||
{shopify.i18n.translate(m.labelKey)}
|
{translate(m.labelKey)}
|
||||||
</s-choice>
|
</Choice>
|
||||||
))}
|
))}
|
||||||
</s-choice-list>
|
</ChoiceList>
|
||||||
|
|
||||||
{needsPostal && (
|
{needsPostal && (
|
||||||
<s-stack gap="base">
|
<BlockStack spacing="base">
|
||||||
<s-text-field
|
<TextField
|
||||||
label={shopify.i18n.translate("postal_code")}
|
label={translate("postal_code")}
|
||||||
value={postalCode}
|
value={postalCode}
|
||||||
onChange={(e) => setPostalCode(e.currentTarget.value)}
|
onChange={(/** @type {string} */ val) => setPostalCode(val)}
|
||||||
/>
|
/>
|
||||||
<s-button onClick={loadAvailability}>
|
<Button onPress={loadAvailability}>
|
||||||
{shopify.i18n.translate("postal_code_submit")}
|
{translate("postal_code_submit")}
|
||||||
</s-button>
|
</Button>
|
||||||
</s-stack>
|
</BlockStack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status === "loading" && <s-text>{shopify.i18n.translate("loading")}</s-text>}
|
{status === "loading" && <Text>{translate("loading")}</Text>}
|
||||||
{status === "error" && <s-banner tone="critical">{shopify.i18n.translate("error")}</s-banner>}
|
{status === "error" && <Banner status="critical">{translate("error")}</Banner>}
|
||||||
{availability?.error && <s-banner tone="warning">{availability.error}</s-banner>}
|
{availability?.error && <Banner status="warning">{availability.error}</Banner>}
|
||||||
|
|
||||||
{dates.length > 0 && (
|
{dates.length > 0 && (
|
||||||
<s-choice-list name="dd-date" values={date ? [date] : []} onChange={(e) => setDate(e.currentTarget.values[0])}>
|
<ChoiceList name="dd-date" value={date} onChange={(/** @type {string} */ val) => setDate(val)}>
|
||||||
{dates.map((d) => (
|
{dates.map((d) => (
|
||||||
<s-choice key={d} value={d}>
|
<Choice key={d} id={d}>
|
||||||
{d}
|
{d}
|
||||||
</s-choice>
|
</Choice>
|
||||||
))}
|
))}
|
||||||
</s-choice-list>
|
</ChoiceList>
|
||||||
)}
|
)}
|
||||||
{availability && !availability.error && dates.length === 0 && status === "idle" && (
|
{availability && !availability.error && dates.length === 0 && status === "idle" && (
|
||||||
<s-text>{shopify.i18n.translate("no_dates")}</s-text>
|
<Text>{translate("no_dates")}</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{date && slots.length > 0 && (
|
{date && slots.length > 0 && (
|
||||||
<s-choice-list
|
<ChoiceList
|
||||||
name="dd-slot"
|
name="dd-slot"
|
||||||
onChange={(e) => {
|
onChange={(/** @type {string} */ val) => {
|
||||||
const slot = slots.find((s) => String(s.startMin) === e.currentTarget.values[0]);
|
const slot = slots.find((/** @type {any} */ s) => String(s.startMin) === val);
|
||||||
if (slot) selectSlot(slot);
|
if (slot) selectSlot(slot);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{slots.map((slot) => (
|
{slots.map((slot) => (
|
||||||
<s-choice key={slot.startMin} value={String(slot.startMin)}>
|
<Choice key={slot.startMin} id={String(slot.startMin)}>
|
||||||
{slotTimeLabel(slot)}
|
{slotTimeLabel(slot)}
|
||||||
</s-choice>
|
</Choice>
|
||||||
))}
|
))}
|
||||||
</s-choice-list>
|
</ChoiceList>
|
||||||
)}
|
)}
|
||||||
</s-stack>
|
</BlockStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,18 @@
|
|||||||
import "@shopify/ui-extensions/preact";
|
import { reactExtension, BlockStack, Heading, Text } from "@shopify/ui-extensions-react/checkout";
|
||||||
import { render } from "preact";
|
import { useConfirmationText } from "./lib.js";
|
||||||
import { confirmationText } from "./lib.js";
|
import { useTranslate } from "@shopify/ui-extensions-react/checkout";
|
||||||
|
|
||||||
export default async () => {
|
// @ts-ignore - customer account targets might not be in the checkout UI extensions type definition
|
||||||
render(<Extension />, document.body);
|
export default reactExtension("customer-account.order-status.block.render", () => <Extension />);
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
function Extension() {
|
||||||
const text = confirmationText(shopify);
|
const text = useConfirmationText();
|
||||||
|
const translate = useTranslate();
|
||||||
if (!text) return null;
|
if (!text) return null;
|
||||||
return (
|
return (
|
||||||
<s-stack gap="base">
|
<BlockStack spacing="base">
|
||||||
<s-heading>{shopify.i18n.translate("name")}</s-heading>
|
<Heading>{translate("name")}</Heading>
|
||||||
<s-text>{text}</s-text>
|
<Text>{text}</Text>
|
||||||
</s-stack>
|
</BlockStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
import "@shopify/ui-extensions/preact";
|
import { reactExtension, BlockStack, Heading, Text } from "@shopify/ui-extensions-react/checkout";
|
||||||
import { render } from "preact";
|
import { useConfirmationText } from "./lib.js";
|
||||||
import { confirmationText } from "./lib.js";
|
import { useTranslate } from "@shopify/ui-extensions-react/checkout";
|
||||||
|
|
||||||
export default async () => {
|
export default reactExtension("purchase.thank-you.block.render", () => <Extension />);
|
||||||
render(<Extension />, document.body);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
function Extension() {
|
||||||
const text = confirmationText(shopify);
|
const text = useConfirmationText();
|
||||||
|
const translate = useTranslate();
|
||||||
if (!text) return null;
|
if (!text) return null;
|
||||||
return (
|
return (
|
||||||
<s-stack gap="base">
|
<BlockStack spacing="base">
|
||||||
<s-heading>{shopify.i18n.translate("name")}</s-heading>
|
<Heading>{translate("name")}</Heading>
|
||||||
<s-text>{text}</s-text>
|
<Text>{text}</Text>
|
||||||
</s-stack>
|
</BlockStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,4 @@
|
|||||||
// Shared helpers for the checkout-datetime extension modules.
|
import { useTranslate, useAttributes } from "@shopify/ui-extensions-react/checkout";
|
||||||
//
|
|
||||||
// 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).
|
|
||||||
|
|
||||||
export const METHODS = [
|
export const METHODS = [
|
||||||
{ value: "PICKUP", labelKey: "method_pickup", attrLabel: "Pickup date" },
|
{ value: "PICKUP", labelKey: "method_pickup", attrLabel: "Pickup date" },
|
||||||
@ -13,6 +6,7 @@ export const METHODS = [
|
|||||||
{ value: "SHIPPING", labelKey: "method_shipping", attrLabel: "Shipping date" },
|
{ value: "SHIPPING", labelKey: "method_shipping", attrLabel: "Shipping date" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** @param {number} minutes */
|
||||||
export function minutesToDisplayTime(minutes) {
|
export function minutesToDisplayTime(minutes) {
|
||||||
const h24 = Math.floor(minutes / 60);
|
const h24 = Math.floor(minutes / 60);
|
||||||
const m = minutes % 60;
|
const m = minutes % 60;
|
||||||
@ -21,6 +15,7 @@ export function minutesToDisplayTime(minutes) {
|
|||||||
return `${h12}:${String(m).padStart(2, "0")} ${period}`;
|
return `${h12}:${String(m).padStart(2, "0")} ${period}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {any} slot */
|
||||||
export function slotTimeLabel(slot) {
|
export function slotTimeLabel(slot) {
|
||||||
if (slot.arrivalRangeStart && slot.arrivalRangeEnd) {
|
if (slot.arrivalRangeStart && slot.arrivalRangeEnd) {
|
||||||
return `Arrives ${slot.arrivalRangeStart.slice(0, 10)}–${slot.arrivalRangeEnd.slice(0, 10)}`;
|
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)}`;
|
return `${minutesToDisplayTime(slot.startMin)}–${minutesToDisplayTime(slot.endMin)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Trims a trailing slash so `${base}/checkout/...` never doubles up. */
|
/** @param {any} settings */
|
||||||
export function backendBase(shopify) {
|
export function backendBase(settings) {
|
||||||
const fromSettings = shopify.settings?.value?.app_url;
|
const fromSettings = settings?.app_url;
|
||||||
return typeof fromSettings === "string" && fromSettings ? fromSettings.replace(/\/+$/, "") : "";
|
return typeof fromSettings === "string" && fromSettings ? fromSettings.replace(/\/+$/, "") : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function authedFetch(shopify, path, options = {}) {
|
/**
|
||||||
const base = backendBase(shopify);
|
* @param {string} base
|
||||||
const token = await shopify.sessionToken.get();
|
* @param {string} token
|
||||||
|
* @param {string} path
|
||||||
|
* @param {any} options
|
||||||
|
*/
|
||||||
|
export async function authedFetch(base, token, path, options = {}) {
|
||||||
return fetch(`${base}${path}`, {
|
return fetch(`${base}${path}`, {
|
||||||
...options,
|
...options,
|
||||||
headers: { ...(options.headers || {}), Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
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) {
|
* @param {any[] | undefined} attributes
|
||||||
const attrs = shopify.attributes?.value ?? [];
|
* @param {string} key
|
||||||
return attrs.find((a) => a.key === key)?.value;
|
*/
|
||||||
}
|
export function readAttribute(attributes, key) {
|
||||||
|
return (attributes ?? []).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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {Record<string, string>} */
|
||||||
const CONFIRMATION_LABEL = {
|
const CONFIRMATION_LABEL = {
|
||||||
PICKUP: "confirmation_pickup",
|
PICKUP: "confirmation_pickup",
|
||||||
LOCAL_DELIVERY: "confirmation_delivery",
|
LOCAL_DELIVERY: "confirmation_delivery",
|
||||||
SHIPPING: "confirmation_shipping",
|
SHIPPING: "confirmation_shipping",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Shared "Pickup — Fri, Aug 25, 9:00 AM–12:00 PM" line for the TY / order-status blocks. */
|
export function useConfirmationText() {
|
||||||
export function confirmationText(shopify) {
|
const translate = useTranslate();
|
||||||
const method = readAttribute(shopify, "dd_method");
|
const attributes = useAttributes();
|
||||||
const date = readAttribute(shopify, "dd_date");
|
|
||||||
const startMin = readAttribute(shopify, "dd_start_min");
|
const method = readAttribute(attributes, "dd_method");
|
||||||
const endMin = readAttribute(shopify, "dd_end_min");
|
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;
|
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 =
|
const time =
|
||||||
startMin && endMin ? `, ${minutesToDisplayTime(Number(startMin))}–${minutesToDisplayTime(Number(endMin))}` : "";
|
startMin && endMin ? `, ${minutesToDisplayTime(Number(startMin))}–${minutesToDisplayTime(Number(endMin))}` : "";
|
||||||
return `${label}: ${date}${time}`;
|
return `${label}: ${date}${time}`;
|
||||||
|
|||||||
@ -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": {
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"checkJs": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "preact",
|
"allowSyntheticDefaultImports": true,
|
||||||
"target": "ES2020",
|
|
||||||
"checkJs": false,
|
|
||||||
"allowJs": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"noEmit": true,
|
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "shopify.d.ts"],
|
"include": ["src/**/*", "shopify.d.ts"],
|
||||||
|
|||||||
731
package-lock.json
generated
731
package-lock.json
generated
@ -58,32 +58,82 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@preact/signals": "^2.3.x",
|
"@shopify/ui-extensions": "^2025.7.3",
|
||||||
"@shopify/ui-extensions": "2026.7.x",
|
"@shopify/ui-extensions-react": "^2025.7.3",
|
||||||
"preact": "^10.10.x"
|
"react": "^18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^7.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extensions/checkout-datetime/node_modules/@shopify/ui-extensions": {
|
"extensions/checkout-datetime/node_modules/@shopify/ui-extensions": {
|
||||||
"version": "2026.7.0",
|
"version": "2025.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/@shopify/ui-extensions/-/ui-extensions-2026.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@shopify/ui-extensions/-/ui-extensions-2025.7.3.tgz",
|
||||||
"integrity": "sha512-xqcExD7d5yAmTZMd+W8yB5sfB587UKobY/fPeONImZLITsEPRbUYfH9MPUHoGJ3uL+5oBYy9ZMZofysQ3vP7eQ==",
|
"integrity": "sha512-XqTEzLnU0YOD7s9vd2EEBdXuqxL6H2VMBkwXuHNHdohwMtN/50S5/OsKRrEsE97D3/+/TcEEhQywocA8z4IvSw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"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": {
|
"peerDependencies": {
|
||||||
"@preact/signals": "*",
|
"@shopify/ui-extensions": "2025.7.3",
|
||||||
"preact": "*"
|
"react": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@preact/signals": {
|
"@shopify/ui-extensions": {
|
||||||
"optional": true
|
"optional": false
|
||||||
},
|
},
|
||||||
"preact": {
|
"react": {
|
||||||
"optional": true
|
"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": {
|
"extensions/delivery-customization": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "UNLICENSED",
|
"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": {
|
"extensions/pos-datetime": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
@ -4900,6 +5196,62 @@
|
|||||||
"web-streams-polyfill": "^3.1.1"
|
"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": {
|
"node_modules/@repeaterjs/repeater": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.1.0.tgz",
|
||||||
@ -6037,6 +6389,15 @@
|
|||||||
"@types/react": "^18.0.0"
|
"@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": {
|
"node_modules/@types/react-transition-group": {
|
||||||
"version": "4.4.12",
|
"version": "4.4.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
"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"
|
"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": {
|
"node_modules/@ungap/structured-clone": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
|
||||||
@ -15222,6 +15923,10 @@
|
|||||||
"node": ">= 14.16"
|
"node": ">= 14.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/payment-customization": {
|
||||||
|
"resolved": "extensions/payment-customization",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/peek-stream": {
|
"node_modules/peek-stream": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz",
|
||||||
|
|||||||
@ -37,6 +37,8 @@ interface RateDto {
|
|||||||
interface AvailabilityResponse {
|
interface AvailabilityResponse {
|
||||||
locationId: string | null;
|
locationId: string | null;
|
||||||
locationName?: string;
|
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;
|
locationLat?: number | null;
|
||||||
locationLng?: number | null;
|
locationLng?: number | null;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
@ -500,6 +502,10 @@ class DateTimeWidget {
|
|||||||
return;
|
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) {
|
if (method.value === "PICKUP" && this.config.googleMapsApiKey) {
|
||||||
this.showPickupMap();
|
this.showPickupMap();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user