metatrondelivery/app/lib/cart-rule-params.ts
MOHAN 03574a4914 feat: product rules, driving-distance zones, and shipping date ranges
Closes remaining DS-parity gaps from the feature audit:

- ProductRule model (product/collection/vendor/type/tag scoping) with
  real server-side enforcement in hold-request.server.ts, plus shaped
  availability in availability-request.server.ts. Covers per-product
  prep time, cart-content-based slot blocking, and product-restricted
  locations in one mechanism. New /app/rules admin page (Growth+).
- Driving-distance delivery zones via Google's Distance Matrix API,
  cached like existing geocoding results.
- SHIPPING-only estimated arrival range (transitMinDays/transitMaxDays
  on SlotTemplate) — widget shows "Arrives Thu-Sat" instead of a
  meaningless ship-out time slot; carried through to the order
  metafield write-back.

Storefront widget and POS extension now send cart contents (vendor/
type from cart.js, product ids for Admin-API-resolved collection/tag
rules) to both availability and hold endpoints.

checkout-datetime remains excluded from this deploy pending Shopify's
Network Access approval (unrelated to this work) — re-add from
../checkout-datetime-disabled and redeploy once granted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 00:28:45 +05:30

35 lines
1.2 KiB
TypeScript

// Shared parsing for the ProductRule-scoping params every scheduling route
// (storefront + POS, availability + hold) accepts identically — kept here so
// the four call sites don't each re-implement the same defensive JSON parsing.
export interface CartLineParam {
vendor?: string;
productType?: string;
}
/** `cartLines` is a JSON-encoded array; malformed/missing input degrades to "no cart info" rather than erroring the request. */
export function parseCartLinesParam(raw: string | null | undefined): CartLineParam[] {
if (!raw) return [];
try {
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed
.filter((entry): entry is Record<string, unknown> => typeof entry === "object" && entry !== null)
.map((entry) => ({
vendor: typeof entry.vendor === "string" ? entry.vendor : undefined,
productType: typeof entry.productType === "string" ? entry.productType : undefined,
}));
} catch {
return [];
}
}
/** `productIds` is a comma-separated list of Shopify Product GIDs. */
export function parseProductIdsParam(raw: string | null | undefined): string[] {
if (!raw) return [];
return raw
.split(",")
.map((id) => id.trim())
.filter(Boolean);
}