@standhigher/besttrack-page-extension 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # @standhigher/besttrack-page-extension
2
2
 
3
3
  V0.7.1 adds the besttrack.branded built-in template. Its announcement, order
4
- query, order items, recommendations, quick links and Blog blocks use the same
5
- host-injected tracking query contract as Ready-to-go. Logo and content are
4
+ query, order items, recommendations, quick links and Blog blocks use the
5
+ shared, host-injected `TrackingPageQuery` contract. The
6
+ `ReadyToGoTrackingQuery` name remains a compatible alias. Logo and content are
6
7
  JSON-only block props; brand colour, font and radius use the template Theme
7
8
  Tokens and may be overridden through PageDocument.theme.
8
9
 
@@ -12,3 +13,22 @@ the Shopify Track Page layout (query card, five-step progress, shipping
12
13
  timeline, package contents and product cards) using inline styles and `--pb-*`
13
14
  Theme Tokens. Live tracking is injected by the host Runtime; this package never
14
15
  stores credentials or network endpoints in a `PageDocument`.
16
+
17
+ V0.7.2 Sales uses the shared, transient `TrackingPageQuery` contract exported
18
+ by this package. The external Go Consumer Runtime API owns live-query
19
+ authorization and transport; Sales only receives its display-safe result and
20
+ never falls back to mock data after a live failure.
21
+
22
+ Sales keeps the stable `besttrack.sales` v1 template and seven block IDs.
23
+ Its host must inject the query from an authorized Consumer Runtime flow; this
24
+ package provides neither a Go client nor a BFF. The editor validates
25
+ merchant-authored text, preview-only tracking placeholders and collection
26
+ links, while the storefront renders only site-relative or HTTPS resource
27
+ links. See `docs/integration/sales-template.md` and
28
+ `docs/integration/consumer-runtime-api.md` for the host contract.
29
+
30
+ New Sales pages use the `hero` visual Variant: a token-driven announcement,
31
+ merchant HTTPS hero image and tracking-only query card. Existing `commerce`
32
+ Variants and published v1 documents stay valid. The browser does not offer an
33
+ order-number mode because the shared Consumer Runtime API currently accepts
34
+ only a tracking number.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,14 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { PageBuilderExtension, TemplateDefinition } from '@standhigher/puck-page-builder/runtime';
4
4
 
5
- type ReadyToGoOrderItem = {
5
+ /**
6
+ * Display-safe, transient data returned by a Consumer Runtime query.
7
+ *
8
+ * These types intentionally describe the boundary between a storefront host
9
+ * and the template. They are not PageDocument props and must never be saved
10
+ * with a draft or published document.
11
+ */
12
+ type TrackingPageOrderItem = {
6
13
  id: string;
7
14
  title: string;
8
15
  quantity: number;
@@ -10,7 +17,7 @@ type ReadyToGoOrderItem = {
10
17
  description?: string;
11
18
  href?: string;
12
19
  };
13
- type ReadyToGoRecommendation = {
20
+ type TrackingPageRecommendation = {
14
21
  id: string;
15
22
  title: string;
16
23
  description: string;
@@ -18,21 +25,21 @@ type ReadyToGoRecommendation = {
18
25
  href?: string;
19
26
  price?: string;
20
27
  };
21
- type ReadyToGoTrackingStep = {
28
+ type TrackingPageTrackingStep = {
22
29
  id: string;
23
30
  label: string;
24
31
  state: "complete" | "current" | "upcoming";
25
32
  date?: string;
26
33
  icon?: "check" | "bag" | "truck" | "box";
27
34
  };
28
- type ReadyToGoTrackingEvent = {
35
+ type TrackingPageTrackingEvent = {
29
36
  id: string;
30
37
  title: string;
31
38
  at?: string;
32
39
  detail?: string;
33
40
  state?: "complete" | "current" | "upcoming";
34
41
  };
35
- type ReadyToGoShipment = {
42
+ type TrackingPageShipment = {
36
43
  id: string;
37
44
  label: string;
38
45
  trackingNumber?: string;
@@ -42,12 +49,14 @@ type ReadyToGoShipment = {
42
49
  updatedAt?: string;
43
50
  deliveryAddress?: string;
44
51
  estimatedDelivery?: string;
45
- progress?: ReadyToGoTrackingStep[];
46
- events?: ReadyToGoTrackingEvent[];
47
- orderItems?: ReadyToGoOrderItem[];
48
- recommendations?: ReadyToGoRecommendation[];
52
+ progress?: TrackingPageTrackingStep[];
53
+ events?: TrackingPageTrackingEvent[];
54
+ orderItems?: TrackingPageOrderItem[];
55
+ recommendations?: TrackingPageRecommendation[];
49
56
  };
50
- type ReadyToGoTrackingResult = {
57
+ type TrackingPageQueryResult = {
58
+ /** `empty` is a successful query with no customer-visible tracking result. */
59
+ outcome?: "found" | "empty";
51
60
  trackingNumber: string;
52
61
  status: string;
53
62
  carrier?: string;
@@ -55,13 +64,22 @@ type ReadyToGoTrackingResult = {
55
64
  updatedAt?: string;
56
65
  deliveryAddress?: string;
57
66
  estimatedDelivery?: string;
58
- progress?: ReadyToGoTrackingStep[];
59
- events?: ReadyToGoTrackingEvent[];
60
- orderItems?: ReadyToGoOrderItem[];
61
- recommendations?: ReadyToGoRecommendation[];
62
- shipments?: ReadyToGoShipment[];
67
+ progress?: TrackingPageTrackingStep[];
68
+ events?: TrackingPageTrackingEvent[];
69
+ orderItems?: TrackingPageOrderItem[];
70
+ recommendations?: TrackingPageRecommendation[];
71
+ shipments?: TrackingPageShipment[];
63
72
  };
64
- type ReadyToGoTrackingQuery = (trackingNumber: string) => Promise<ReadyToGoTrackingResult>;
73
+ /** The UI receives an injected function; it never knows a Go endpoint or credential. */
74
+ type TrackingPageQuery = (trackingNumber: string) => Promise<TrackingPageQueryResult>;
75
+ type TrackingPageRuntimePhase = "idle" | "loading" | "success" | "empty" | "error";
76
+ declare function isEmptyTrackingPageResult(result: TrackingPageQueryResult): boolean;
77
+
78
+ type ReadyToGoTrackingStep = TrackingPageTrackingStep;
79
+ type ReadyToGoTrackingEvent = TrackingPageTrackingEvent;
80
+ type ReadyToGoShipment = TrackingPageShipment;
81
+ type ReadyToGoTrackingResult = TrackingPageQueryResult;
82
+ type ReadyToGoTrackingQuery = TrackingPageQuery;
65
83
  type ReadyToGoRuntimeState = {
66
84
  phase: "idle" | "loading" | "success" | "error";
67
85
  result?: ReadyToGoTrackingResult;
@@ -79,31 +97,32 @@ declare const bestTrackPageExtension: PageBuilderExtension;
79
97
  declare function createBrandedTemplate(): TemplateDefinition;
80
98
  declare const bestTrackBrandedExtension: PageBuilderExtension;
81
99
 
100
+ /** Branded uses the shared, display-safe Consumer Runtime result without persisting it. */
82
101
  type BrandedRuntimeState = {
83
- phase: "idle" | "loading" | "success" | "error";
84
- result?: ReadyToGoTrackingResult;
102
+ phase: TrackingPageRuntimePhase;
103
+ result?: TrackingPageQueryResult;
85
104
  error?: string;
86
105
  };
87
106
  type BrandedRuntimeProviderProps = {
88
107
  children: ReactNode;
89
- queryTracking: ReadyToGoTrackingQuery;
108
+ queryTracking: TrackingPageQuery;
90
109
  };
91
110
  declare function BrandedRuntimeProvider({ children, queryTracking }: BrandedRuntimeProviderProps): react.JSX.Element;
92
111
 
112
+ /** Stable Sales v1 document and block IDs are intentionally retained for published-page compatibility. */
93
113
  declare function createSalesTemplate(): TemplateDefinition;
94
114
  declare const bestTrackSalesExtension: PageBuilderExtension;
95
115
 
96
- type SalesPhase = "idle" | "loading" | "success" | "error";
116
+ /** Transient, consumer-safe state. The host error is deliberately never retained for display. */
97
117
  type SalesRuntimeState = {
98
- phase: SalesPhase;
99
- result?: ReadyToGoTrackingResult;
100
- error?: string;
118
+ phase: TrackingPageRuntimePhase;
119
+ result?: TrackingPageQueryResult;
101
120
  };
102
121
  type SalesRuntimeProviderProps = {
103
122
  children: ReactNode;
104
- queryTracking: ReadyToGoTrackingQuery;
123
+ queryTracking: TrackingPageQuery;
105
124
  };
106
125
  /** The host injects a validated query; Sales never calls a DataSource itself. */
107
126
  declare function SalesRuntimeProvider({ children, queryTracking }: SalesRuntimeProviderProps): react.JSX.Element;
108
127
 
109
- export { BrandedRuntimeProvider, type BrandedRuntimeProviderProps, type BrandedRuntimeState, ReadyToGoRuntimeProvider, type ReadyToGoRuntimeProviderProps, type ReadyToGoRuntimeState, type ReadyToGoShipment, type ReadyToGoTrackingEvent, type ReadyToGoTrackingQuery, type ReadyToGoTrackingResult, type ReadyToGoTrackingStep, SalesRuntimeProvider, type SalesRuntimeProviderProps, type SalesRuntimeState, bestTrackBrandedExtension, bestTrackPageExtension, bestTrackSalesExtension, createBrandedTemplate, createReadyToGoTemplate, createSalesTemplate };
128
+ export { BrandedRuntimeProvider, type BrandedRuntimeProviderProps, type BrandedRuntimeState, ReadyToGoRuntimeProvider, type ReadyToGoRuntimeProviderProps, type ReadyToGoRuntimeState, type ReadyToGoShipment, type ReadyToGoTrackingEvent, type ReadyToGoTrackingQuery, type ReadyToGoTrackingResult, type ReadyToGoTrackingStep, SalesRuntimeProvider, type SalesRuntimeProviderProps, type SalesRuntimeState, type TrackingPageOrderItem, type TrackingPageQuery, type TrackingPageQueryResult, type TrackingPageRecommendation, type TrackingPageRuntimePhase, type TrackingPageShipment, type TrackingPageTrackingEvent, type TrackingPageTrackingStep, bestTrackBrandedExtension, bestTrackPageExtension, bestTrackSalesExtension, createBrandedTemplate, createReadyToGoTemplate, createSalesTemplate, isEmptyTrackingPageResult };
package/dist/index.js CHANGED
@@ -345,7 +345,7 @@ function ReadyToGoQueryEditor(block) {
345
345
  /* @__PURE__ */ jsx2("p", { style: { marginTop: "auto", paddingTop: 28, textAlign: "center", fontSize: 12, lineHeight: "17px", fontStyle: "italic", color: "#b8b8b8" }, children: "Powered by BestTrack" })
346
346
  ] }) });
347
347
  }
348
- function ReadyToGoProgressEditor(_block) {
348
+ function ReadyToGoProgressEditor() {
349
349
  return /* @__PURE__ */ jsx2("section", { "aria-label": "Ready-to-go progress editor", style: { ...pageFont, background: "var(--pb-color-background, #fff)", color: "var(--pb-color-text, #0f172a)", borderBottom: "1px solid #f1f5f9" }, children: /* @__PURE__ */ jsx2("div", { style: { ...contentWidth, textAlign: "center", width: "min(1248px, 100%)" }, children: /* @__PURE__ */ jsx2(ProgressResult, { showEstimatedDelivery: false, result: previewReadyToGoTracking() }) }) });
350
350
  }
351
351
  function ReadyToGoDeliveryEditor(block) {
@@ -577,7 +577,14 @@ var bestTrackPageExtension = Object.freeze({
577
577
  import { createPageDocument as createPageDocument2 } from "@standhigher/puck-page-builder/runtime";
578
578
 
579
579
  // src/branded.tsx
580
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useMemo as useMemo2, useState as useState3 } from "react";
580
+ import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
581
+
582
+ // src/tracking-page-runtime.ts
583
+ function isEmptyTrackingPageResult(result) {
584
+ return result.outcome === "empty";
585
+ }
586
+
587
+ // src/branded.tsx
581
588
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
582
589
  var initialRuntime2 = {
583
590
  phase: "idle",
@@ -598,17 +605,22 @@ var cardStyle = { background: "#fff", color: "#0a0a0a", border: "1px solid #e7e7
598
605
  function BrandedRuntimeProvider({ children, queryTracking }) {
599
606
  const [state, setState] = useState3({ phase: "idle" });
600
607
  const [selectedShipmentId, setSelectedShipmentId] = useState3(null);
608
+ const requestId = useRef(0);
601
609
  const query = useCallback2(async (trackingNumber) => {
610
+ const currentRequestId = ++requestId.current;
602
611
  setState({ phase: "loading" });
603
612
  try {
604
613
  const result = await queryTracking(trackingNumber);
614
+ if (currentRequestId !== requestId.current) return;
605
615
  setSelectedShipmentId(result.shipments?.[0]?.id ?? null);
606
- setState({ phase: "success", result });
607
- } catch (error) {
608
- setState({ phase: "error", error: error instanceof Error ? error.message : "tracking-query-failed" });
616
+ setState({ phase: isEmptyTrackingPageResult(result) ? "empty" : "success", result });
617
+ } catch {
618
+ if (currentRequestId !== requestId.current) return;
619
+ setState({ phase: "error", error: "tracking-query-failed" });
609
620
  }
610
621
  }, [queryTracking]);
611
622
  const reset = useCallback2(() => {
623
+ requestId.current += 1;
612
624
  setSelectedShipmentId(null);
613
625
  setState({ phase: "idle" });
614
626
  }, []);
@@ -617,7 +629,9 @@ function BrandedRuntimeProvider({ children, queryTracking }) {
617
629
  const shipment = state.result.shipments?.find((item) => item.id === selectedShipmentId);
618
630
  return shipment ? { ...state.result, ...shipment } : state.result;
619
631
  }, [selectedShipmentId, state.result]);
620
- const selectShipment = useCallback2((id) => setSelectedShipmentId(id), []);
632
+ const selectShipment = useCallback2((id) => {
633
+ if (state.result?.shipments?.some((shipment) => shipment.id === id)) setSelectedShipmentId(id);
634
+ }, [state.result]);
621
635
  const value = useMemo2(
622
636
  () => ({ ...state, displayedResult, selectedShipmentId, query, reset, selectShipment }),
623
637
  [displayedResult, query, reset, selectedShipmentId, selectShipment, state]
@@ -632,7 +646,7 @@ function text3(props, key, fallback) {
632
646
  }
633
647
  function safeHref2(value) {
634
648
  if (typeof value !== "string") return "#";
635
- if (value.startsWith("/")) return value;
649
+ if (value.startsWith("/") && !value.startsWith("//")) return value;
636
650
  try {
637
651
  const url = new URL(value);
638
652
  return url.protocol === "https:" || url.protocol === "http:" ? url.toString() : "#";
@@ -663,7 +677,7 @@ function ProductImage2({ src, alt }) {
663
677
  function ShipmentSwitcher({ shipmentLabels: configuredLabels }) {
664
678
  const runtime = useBrandedRuntime();
665
679
  const configuredShipments = shipmentLabels(configuredLabels);
666
- const shipments = runtime.result?.shipments?.length ? runtime.result.shipments : configuredShipments;
680
+ const shipments = runtime.phase === "idle" || runtime.phase === "loading" ? configuredShipments : runtime.result?.shipments ?? [];
667
681
  if (!shipments.length) return null;
668
682
  return /* @__PURE__ */ jsx3("div", { "aria-label": "Shipment switcher", style: { ...contentWidth2, minHeight: 56, display: "flex", alignItems: "center", gap: 8, overflowX: "auto", whiteSpace: "nowrap" }, children: shipments.map((shipment, index) => {
669
683
  const selected = (runtime.selectedShipmentId ?? shipments[0]?.id) === shipment.id;
@@ -693,7 +707,8 @@ function QueryHero(props) {
693
707
  /* @__PURE__ */ jsx3("input", { id: "branded-tracking-number", "aria-label": mode === "order" ? "Order number" : "Tracking number", value: trackingNumber, onChange: (event) => setTrackingNumber(event.target.value), required: true, minLength: 4, maxLength: 64, placeholder: mode === "order" ? "Enter your order number" : "Enter your tracking number", style: { width: "100%", height: 48, padding: "0 16px", boxSizing: "border-box", border: "1px solid #e2e2e2", borderRadius: 10, font: "inherit" } }),
694
708
  /* @__PURE__ */ jsx3("button", { type: "submit", disabled: runtime.phase === "loading", style: { width: "100%", minHeight: 48, marginTop: 24, border: 0, borderRadius: 10, background: runtime.phase === "loading" ? "#6b6b6b" : "#000", color: "#fff", font: "inherit", cursor: runtime.phase === "loading" ? "wait" : "pointer" }, children: runtime.phase === "loading" ? "Tracking\u2026" : text3(props, "submitLabel", "Track") })
695
709
  ] }),
696
- runtime.phase === "error" ? /* @__PURE__ */ jsx3("p", { role: "alert", style: { marginBottom: 0, color: "#b42318" }, children: runtime.error }) : null,
710
+ runtime.phase === "empty" ? /* @__PURE__ */ jsx3("p", { role: "status", style: { marginBottom: 0, color: "#6b6b6b" }, children: "We couldn\u2019t find an order for that number." }) : null,
711
+ runtime.phase === "error" ? /* @__PURE__ */ jsx3("p", { role: "alert", style: { marginBottom: 0, color: "#b42318" }, children: "We couldn\u2019t retrieve this order right now. Please try again later." }) : null,
697
712
  /* @__PURE__ */ jsx3("small", { style: { display: "block", marginTop: 10, color: "#8a8a8a", fontSize: 8, textAlign: "right" }, children: "Powered by BestTrack" })
698
713
  ] })
699
714
  ] });
@@ -873,13 +888,14 @@ function BrandedOrderItemsBlock(props) {
873
888
  ] }) });
874
889
  }
875
890
  function RecommendationCard({ item }) {
891
+ const href = safeHref2(item.href);
876
892
  return /* @__PURE__ */ jsxs3("article", { style: { overflow: "hidden", borderRadius: 6, background: "#fff", boxShadow: "0 1px 2px rgb(0 0 0 / 8%)" }, children: [
877
893
  /* @__PURE__ */ jsx3(ProductImage2, { src: item.imageUrl, alt: item.title }),
878
894
  /* @__PURE__ */ jsxs3("div", { style: { padding: 14 }, children: [
879
895
  /* @__PURE__ */ jsx3("strong", { children: item.title }),
880
896
  item.price ? /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0", fontWeight: 700 }, children: item.price }) : null,
881
897
  /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0", color: "#6b6b6b", fontSize: 13 }, children: item.description }),
882
- /* @__PURE__ */ jsx3("a", { href: safeHref2(item.href), style: { color: "#0a0a0a", fontSize: 13, fontWeight: 700 }, children: "View product" })
898
+ href === "#" ? /* @__PURE__ */ jsx3("span", { style: { color: "#6b6b6b", fontSize: 13, fontWeight: 700 }, children: "View product" }) : /* @__PURE__ */ jsx3("a", { href, style: { color: "#0a0a0a", fontSize: 13, fontWeight: 700 }, children: "View product" })
883
899
  ] })
884
900
  ] });
885
901
  }
@@ -887,10 +903,10 @@ function BrandedRecommendationsBlock(props) {
887
903
  const runtime = useBrandedRuntime();
888
904
  const recommendations = runtime.displayedResult?.recommendations ?? [];
889
905
  const title = text3(props, "heading", "You might also like");
890
- if (runtime.phase === "success" && !recommendations.length && text3(props, "hideWhenEmpty", "false") === "true") return null;
906
+ if ((runtime.phase === "success" || runtime.phase === "empty") && !recommendations.length && text3(props, "hideWhenEmpty", "false") === "true") return null;
891
907
  return /* @__PURE__ */ jsx3("section", { "aria-label": title, style: { background: "#fffdf0", color: "#0a0a0a", fontFamily: "var(--pb-font-family)", padding: "clamp(28px, 4vw, 48px) 0" }, children: /* @__PURE__ */ jsxs3("div", { style: contentWidth2, children: [
892
908
  /* @__PURE__ */ jsx3("h2", { style: { margin: "0 0 24px", textAlign: "center", fontSize: 20 }, children: title }),
893
- runtime.phase === "loading" ? /* @__PURE__ */ jsx3("div", { "aria-label": "Loading recommendations", style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 16 }, children: [0, 1, 2].map((item) => /* @__PURE__ */ jsx3("span", { style: { display: "block", height: 220, borderRadius: 6, background: "#e7e7e7" } }, item)) }) : runtime.phase === "success" && recommendations.length ? /* @__PURE__ */ jsx3("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 16 }, children: recommendations.map((item) => /* @__PURE__ */ jsx3(RecommendationCard, { item }, item.id)) }) : /* @__PURE__ */ jsx3("p", { style: { margin: 0, textAlign: "center", color: runtime.phase === "error" ? "#b42318" : "#6b6b6b" }, children: runtime.phase === "error" ? "Recommendations are temporarily unavailable." : "Recommendations will appear with your order." })
909
+ runtime.phase === "loading" ? /* @__PURE__ */ jsx3("div", { "aria-label": "Loading recommendations", style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 16 }, children: [0, 1, 2].map((item) => /* @__PURE__ */ jsx3("span", { style: { display: "block", height: 220, borderRadius: 6, background: "#e7e7e7" } }, item)) }) : runtime.phase === "success" && recommendations.length ? /* @__PURE__ */ jsx3("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 16 }, children: recommendations.map((item) => /* @__PURE__ */ jsx3(RecommendationCard, { item }, item.id)) }) : /* @__PURE__ */ jsx3("p", { style: { margin: 0, textAlign: "center", color: runtime.phase === "error" ? "#b42318" : "#6b6b6b" }, children: runtime.phase === "error" ? "Recommendations are temporarily unavailable." : runtime.phase === "empty" ? "No recommendations are available for that number." : "Recommendations will appear with your order." })
894
910
  ] }) });
895
911
  }
896
912
  function BrandedQuickLinksBlock(props) {
@@ -936,22 +952,30 @@ var bestTrackBrandedExtension = Object.freeze({ name: "besttrack.branded", versi
936
952
  import { createPageDocument as createPageDocument3 } from "@standhigher/puck-page-builder/runtime";
937
953
 
938
954
  // src/sales.tsx
939
- import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useMemo as useMemo3, useState as useState4 } from "react";
940
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
955
+ import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useId, useMemo as useMemo3, useRef as useRef2, useState as useState4 } from "react";
956
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
941
957
  var initialRuntime3 = { phase: "idle", async query() {
942
958
  return void 0;
943
959
  } };
944
960
  var SalesRuntimeContext = createContext3(initialRuntime3);
945
- var cardStyle2 = { border: "1px solid var(--pb-color-border)", borderRadius: "var(--pb-radius)", padding: "var(--pb-spacing)", background: "var(--pb-color-surface)", color: "var(--pb-color-text)", fontFamily: "var(--pb-font-family)" };
946
- var gridStyle = { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: "var(--pb-spacing)" };
961
+ var contentWidth3 = { boxSizing: "border-box", width: "min(1120px, calc(100% - 32px))", margin: "0 auto" };
962
+ var sectionStyle = { ...contentWidth3, marginTop: "clamp(32px, 6vw, 72px)", color: "var(--pb-color-text)", fontFamily: "var(--pb-font-family)" };
963
+ var panelStyle = { boxSizing: "border-box", border: "1px solid var(--pb-color-border)", borderRadius: "var(--pb-radius)", padding: "clamp(20px, 3vw, 32px)", background: "var(--pb-color-surface)", color: "var(--pb-color-text)", overflowWrap: "anywhere" };
964
+ var gridStyle = { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 220px), 1fr))", gap: "clamp(16px, 2vw, 24px)" };
965
+ var trackingNumberPattern = /^[A-Za-z0-9-]{4,64}$/;
947
966
  function SalesRuntimeProvider({ children, queryTracking }) {
948
967
  const [state, setState] = useState4({ phase: "idle" });
968
+ const requestId = useRef2(0);
949
969
  const query = useCallback3(async (trackingNumber) => {
970
+ const currentRequestId = ++requestId.current;
950
971
  setState({ phase: "loading" });
951
972
  try {
952
- setState({ phase: "success", result: await queryTracking(trackingNumber) });
953
- } catch (error) {
954
- setState({ phase: "error", error: error instanceof Error ? error.message : "tracking-query-failed" });
973
+ const result = await queryTracking(trackingNumber);
974
+ if (currentRequestId !== requestId.current) return;
975
+ setState({ phase: isEmptyTrackingPageResult(result) ? "empty" : "success", result });
976
+ } catch {
977
+ if (currentRequestId !== requestId.current) return;
978
+ setState({ phase: "error" });
955
979
  }
956
980
  }, [queryTracking]);
957
981
  const value = useMemo3(() => ({ ...state, query }), [query, state]);
@@ -961,115 +985,314 @@ function useSalesRuntime() {
961
985
  return useContext3(SalesRuntimeContext);
962
986
  }
963
987
  function text5(props, key, fallback) {
964
- return typeof props[key] === "string" ? props[key] : fallback;
988
+ const value = props[key];
989
+ return typeof value === "string" && value.trim() ? value.trim() : fallback;
990
+ }
991
+ function hasControlCharacter(value) {
992
+ return [...value].some((character) => character.charCodeAt(0) < 32);
965
993
  }
966
994
  function safeHref3(value) {
967
- if (typeof value !== "string") return "#";
968
- if (value.startsWith("/")) return value;
995
+ if (typeof value !== "string") return void 0;
996
+ if (value.startsWith("/") && !value.startsWith("//") && !value.startsWith("/\\") && !value.includes("\\") && !hasControlCharacter(value)) return value;
969
997
  try {
970
998
  const url = new URL(value);
971
- return url.protocol === "https:" || url.protocol === "http:" ? url.toString() : "#";
999
+ return url.protocol === "https:" ? url.toString() : void 0;
972
1000
  } catch {
973
- return "#";
1001
+ return void 0;
974
1002
  }
975
1003
  }
976
- function Section({ title, children }) {
977
- return /* @__PURE__ */ jsxs4("section", { "aria-label": title, style: cardStyle2, children: [
978
- /* @__PURE__ */ jsx4("h2", { style: { marginTop: 0 }, children: title }),
979
- children
980
- ] });
1004
+ function safeImageUrl3(value) {
1005
+ if (typeof value !== "string") return void 0;
1006
+ try {
1007
+ const url = new URL(value);
1008
+ return url.protocol === "https:" ? url.toString() : void 0;
1009
+ } catch {
1010
+ return void 0;
1011
+ }
981
1012
  }
982
1013
  function resourceState(value) {
983
1014
  return typeof value !== "string" ? "invalid" : value.trim() ? "ready" : "empty";
984
1015
  }
1016
+ function Status({ children, alert = false }) {
1017
+ return /* @__PURE__ */ jsx4("p", { role: alert ? "alert" : "status", "aria-live": alert ? "assertive" : "polite", style: { margin: "16px 0 0", color: alert ? "#a61b1b" : "var(--pb-color-muted)" }, children });
1018
+ }
1019
+ function Section({ title, children, busy = false }) {
1020
+ return /* @__PURE__ */ jsx4("section", { "aria-label": title, "aria-busy": busy || void 0, style: sectionStyle, children: /* @__PURE__ */ jsxs4("div", { style: panelStyle, children: [
1021
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px", fontSize: "clamp(24px, 3vw, 34px)", lineHeight: 1.15 }, children: title }),
1022
+ children
1023
+ ] }) });
1024
+ }
1025
+ function ProductImage3({ src, alt }) {
1026
+ const [failed, setFailed] = useState4(false);
1027
+ const safeSrc = safeImageUrl3(src);
1028
+ if (!safeSrc || failed) return /* @__PURE__ */ jsx4("span", { "aria-label": `${alt} image unavailable`, role: "img", style: { display: "grid", placeItems: "center", width: 76, height: 76, flex: "0 0 auto", border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)", color: "var(--pb-color-muted)", fontWeight: 700 }, children: alt.slice(0, 1).toUpperCase() });
1029
+ return /* @__PURE__ */ jsx4("img", { src: safeSrc, alt, onError: () => setFailed(true), style: { width: 76, height: 76, flex: "0 0 auto", borderRadius: "calc(var(--pb-radius) / 1.5)", objectFit: "cover", background: "var(--pb-color-background)" } });
1030
+ }
1031
+ function HeroAsset({ src }) {
1032
+ const [failed, setFailed] = useState4(false);
1033
+ const safeSrc = safeImageUrl3(src);
1034
+ if (!safeSrc || failed) return null;
1035
+ return /* @__PURE__ */ jsx4("img", { "data-sales-hero-image": true, src: safeSrc, alt: "", "aria-hidden": "true", onError: () => setFailed(true), style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", opacity: 0.62 } });
1036
+ }
985
1037
  function SalesTextField({ value, onChange }) {
986
1038
  return /* @__PURE__ */ jsx4("input", { "aria-label": "Sales text", value: typeof value === "string" ? value : "", onChange: (event) => onChange(event.target.value) });
987
1039
  }
1040
+ var editorSurfaceStyle = { boxSizing: "border-box", border: "1px solid #dfddd7", borderRadius: 10, padding: 24, background: "#ffffff", color: "#0a0a0a", fontFamily: "Arial, Helvetica, sans-serif" };
1041
+ var editorCardStyle = { boxSizing: "border-box", padding: 18, border: "1px solid #dfddd7", borderRadius: 7, background: "#f7f5f0" };
1042
+ function editorText(block, name, fallback) {
1043
+ return text5(block, name, fallback);
1044
+ }
1045
+ function SalesInlineText({ block, name, fallback, style }) {
1046
+ const value = editorText(block, name, fallback);
1047
+ if (!block.selected) return /* @__PURE__ */ jsx4("span", { "data-sales-editor-field": name, style, children: value });
1048
+ return /* @__PURE__ */ jsx4("input", { "aria-label": `Canvas ${name}`, "data-sales-editor-field": name, value, onMouseDown: (event) => event.stopPropagation(), onClick: (event) => event.stopPropagation(), onChange: (event) => block.onPropsChange({ [name]: event.currentTarget.value }), style: { boxSizing: "border-box", width: "100%", border: "1px dashed currentColor", borderRadius: 3, padding: "2px 5px", background: "transparent", color: "inherit", font: "inherit", fontWeight: "inherit", lineHeight: "inherit", textAlign: "inherit", ...style } });
1049
+ }
1050
+ function SalesEditorSurface({ block, children }) {
1051
+ return /* @__PURE__ */ jsx4("section", { "aria-label": "Sales editor preview", style: { ...editorSurfaceStyle, outline: block.selected ? "2px solid #2563eb" : void 0, outlineOffset: -2 }, children });
1052
+ }
1053
+ function SalesAnnouncementEditor(block) {
1054
+ return /* @__PURE__ */ jsx4("section", { "aria-label": "Sales announcement editor", style: { display: "grid", minHeight: 38, placeItems: "center", boxSizing: "border-box", padding: "10px 16px", borderBottom: "1px solid #dfddd7", background: "#f7f5f0", color: "#0a0a0a", fontFamily: "Arial, Helvetica, sans-serif", fontSize: 13, fontWeight: 700, textAlign: "center" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "message", fallback: "Free delivery on orders over $50" }) });
1055
+ }
1056
+ function SalesQueryEditor(block) {
1057
+ const imageUrl = safeImageUrl3(block.heroImageUrl);
1058
+ const trackingNumber = editorText(block, "defaultTrackingNumber", "BT-2048-DEMO");
1059
+ return /* @__PURE__ */ jsxs4("section", { "aria-label": "Sales Hero query editor", style: { position: "relative", display: "grid", minHeight: "clamp(460px, 52vw, 620px)", placeItems: "center", boxSizing: "border-box", overflow: "hidden", padding: "clamp(28px, 6vw, 72px) 16px", background: "#0a0a0a", fontFamily: "Arial, Helvetica, sans-serif" }, children: [
1060
+ imageUrl ? /* @__PURE__ */ jsx4("img", { src: imageUrl, alt: "", "aria-hidden": "true", style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", opacity: 0.62 } }) : null,
1061
+ /* @__PURE__ */ jsx4("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, background: "rgb(0 0 0 / 42%)" } }),
1062
+ /* @__PURE__ */ jsxs4("div", { style: { position: "relative", zIndex: 1, boxSizing: "border-box", width: "min(560px, 100%)", padding: "clamp(28px, 5vw, 48px)", borderRadius: 10, background: "#ffffff", color: "#0a0a0a", boxShadow: "0 20px 56px rgb(0 0 0 / 28%)" }, children: [
1063
+ /* @__PURE__ */ jsx4("h1", { style: { margin: "0 0 28px", fontSize: "clamp(32px, 5vw, 48px)", fontWeight: 800, letterSpacing: "-0.035em", lineHeight: 1.02, textAlign: "center" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Track your order" }) }),
1064
+ /* @__PURE__ */ jsx4("input", { "aria-label": "Canvas default tracking number", readOnly: !block.selected, value: trackingNumber, onMouseDown: (event) => block.selected && event.stopPropagation(), onClick: (event) => block.selected && event.stopPropagation(), onChange: (event) => block.onPropsChange({ defaultTrackingNumber: event.currentTarget.value }), style: { boxSizing: "border-box", width: "100%", minHeight: 58, padding: "12px 16px", border: "1px solid #dfddd7", borderRadius: 8, background: "#ffffff", color: "#0a0a0a", font: "inherit", fontSize: 17 } }),
1065
+ /* @__PURE__ */ jsx4("div", { style: { display: "grid", minHeight: 58, placeItems: "center", marginTop: 14, padding: "12px 18px", borderRadius: 8, background: "#0a0a0a", color: "#ffffff", fontSize: 16, fontWeight: 700 }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "submitLabel", fallback: "Track order" }) }),
1066
+ /* @__PURE__ */ jsx4("small", { style: { display: "block", marginTop: 14, color: "#6b6b6b", fontSize: 11, textAlign: "right" }, children: "Powered by BestTrack" })
1067
+ ] })
1068
+ ] });
1069
+ }
1070
+ function SalesOrderItemsEditor(block) {
1071
+ return /* @__PURE__ */ jsxs4(SalesEditorSurface, { block, children: [
1072
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Items in your order" }) }),
1073
+ /* @__PURE__ */ jsxs4("article", { style: { ...editorCardStyle, display: "flex", alignItems: "center", gap: 14 }, children: [
1074
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { width: 68, height: 68, borderRadius: 7, background: "#dfddd7" } }),
1075
+ /* @__PURE__ */ jsxs4("span", { children: [
1076
+ /* @__PURE__ */ jsx4("strong", { children: "Order item" }),
1077
+ /* @__PURE__ */ jsx4("br", {}),
1078
+ /* @__PURE__ */ jsx4("small", { style: { color: "#6b6b6b" }, children: "Items appear after a successful tracking query." })
1079
+ ] })
1080
+ ] })
1081
+ ] });
1082
+ }
1083
+ function SalesOtherTrackingEditor(block) {
1084
+ return /* @__PURE__ */ jsxs4(SalesEditorSurface, { block, children: [
1085
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Other shipments" }) }),
1086
+ /* @__PURE__ */ jsx4("p", { style: { margin: 0, color: "#6b6b6b" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "emptyMessage", fallback: "No other shipments are linked to this order." }) })
1087
+ ] });
1088
+ }
1089
+ function SalesServiceCardsEditor(block) {
1090
+ return /* @__PURE__ */ jsxs4(SalesEditorSurface, { block, children: [
1091
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Shop with confidence" }) }),
1092
+ /* @__PURE__ */ jsxs4("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 180px), 1fr))", gap: 14 }, children: [
1093
+ /* @__PURE__ */ jsxs4("article", { style: editorCardStyle, children: [
1094
+ /* @__PURE__ */ jsx4("strong", { children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "firstTitle", fallback: "Easy returns" }) }),
1095
+ /* @__PURE__ */ jsx4("p", { style: { marginBottom: 0, color: "#6b6b6b" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "firstDescription", fallback: "Simple support when plans change." }) })
1096
+ ] }),
1097
+ /* @__PURE__ */ jsxs4("article", { style: editorCardStyle, children: [
1098
+ /* @__PURE__ */ jsx4("strong", { children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "secondTitle", fallback: "Secure delivery" }) }),
1099
+ /* @__PURE__ */ jsx4("p", { style: { marginBottom: 0, color: "#6b6b6b" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "secondDescription", fallback: "Follow every milestone in one place." }) })
1100
+ ] })
1101
+ ] })
1102
+ ] });
1103
+ }
1104
+ function SalesProductCategoriesEditor(block) {
1105
+ return /* @__PURE__ */ jsxs4(SalesEditorSurface, { block, children: [
1106
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Shop by category" }) }),
1107
+ /* @__PURE__ */ jsxs4("div", { style: { ...editorCardStyle, display: "flex", justifyContent: "space-between", fontWeight: 700 }, children: [
1108
+ /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "collectionLabel", fallback: "Featured collection" }),
1109
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2192" })
1110
+ ] })
1111
+ ] });
1112
+ }
1113
+ function SalesRecommendationsEditor(block) {
1114
+ return /* @__PURE__ */ jsxs4(SalesEditorSurface, { block, children: [
1115
+ /* @__PURE__ */ jsx4("h2", { style: { margin: "0 0 20px" }, children: /* @__PURE__ */ jsx4(SalesInlineText, { block, name: "heading", fallback: "Complete your order" }) }),
1116
+ /* @__PURE__ */ jsx4("div", { style: { ...editorCardStyle, color: "#6b6b6b" }, children: "Recommended products appear after a successful tracking query." })
1117
+ ] });
1118
+ }
988
1119
  function SalesAnnouncementBlock(props) {
989
- return /* @__PURE__ */ jsx4("section", { "aria-label": "Sales announcement", style: { ...cardStyle2, background: "var(--pb-color-primary)", color: "white", textAlign: "center" }, children: /* @__PURE__ */ jsx4("strong", { children: text5(props, "message", "Free delivery on orders over $50") }) });
1120
+ return /* @__PURE__ */ jsx4("section", { "aria-label": "Sales announcement", "data-sales-announcement": true, style: { display: "grid", minHeight: 38, placeItems: "center", boxSizing: "border-box", padding: "10px 16px", borderBottom: "1px solid var(--pb-color-border)", background: "var(--pb-color-background)", color: "var(--pb-color-text)", fontFamily: "var(--pb-font-family)", fontSize: 13, textAlign: "center" }, children: /* @__PURE__ */ jsx4("strong", { children: text5(props, "message", "Free delivery on orders over $50") }) });
990
1121
  }
991
1122
  function SalesQueryBlock(props) {
992
1123
  const runtime = useSalesRuntime();
1124
+ const inputId = useId();
1125
+ const errorId = useId();
993
1126
  const [trackingNumber, setTrackingNumber] = useState4(text5(props, "defaultTrackingNumber", "BT-2048-DEMO"));
1127
+ const [inputError, setInputError] = useState4(null);
994
1128
  const submit = (event) => {
995
1129
  event.preventDefault();
996
- void runtime.query(trackingNumber);
1130
+ const normalizedTrackingNumber = trackingNumber.trim();
1131
+ if (!trackingNumberPattern.test(normalizedTrackingNumber)) {
1132
+ setInputError("Enter a tracking number using 4\u201364 letters, numbers, or hyphens.");
1133
+ return;
1134
+ }
1135
+ setInputError(null);
1136
+ void runtime.query(normalizedTrackingNumber);
997
1137
  };
998
- return /* @__PURE__ */ jsxs4("section", { "aria-label": "Sales tracking query", style: { ...cardStyle2, boxShadow: "0 12px 32px rgb(0 0 0 / 8%)" }, children: [
999
- /* @__PURE__ */ jsx4("h1", { style: { marginTop: 0 }, children: text5(props, "heading", "Track an order") }),
1000
- /* @__PURE__ */ jsxs4("form", { onSubmit: submit, style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
1001
- /* @__PURE__ */ jsx4("input", { "aria-label": "Sales tracking number", value: trackingNumber, onChange: (event) => setTrackingNumber(event.target.value), minLength: 4, maxLength: 64, required: true, style: { flex: "1 1 220px", padding: 12, borderRadius: "var(--pb-radius)", border: "1px solid var(--pb-color-border)" } }),
1002
- /* @__PURE__ */ jsx4("button", { type: "submit", disabled: runtime.phase === "loading", style: { padding: "12px 18px", border: 0, borderRadius: "var(--pb-radius)", color: "white", background: "var(--pb-color-primary)" }, children: runtime.phase === "loading" ? "Checking\u2026" : text5(props, "submitLabel", "Track order") })
1003
- ] }),
1004
- runtime.phase === "error" ? /* @__PURE__ */ jsxs4("p", { role: "alert", children: [
1005
- "We could not retrieve this order: ",
1006
- runtime.error
1007
- ] }) : null
1138
+ const description = inputError ?? (runtime.phase === "empty" ? "We couldn\u2019t find an order for that number." : runtime.phase === "error" ? "We couldn\u2019t retrieve this order right now. Please try again later." : void 0);
1139
+ return /* @__PURE__ */ jsxs4("section", { "aria-label": "Sales tracking query", "aria-busy": runtime.phase === "loading" || void 0, "data-sales-hero": true, style: { position: "relative", display: "grid", minHeight: "clamp(460px, 52vw, 620px)", placeItems: "center", boxSizing: "border-box", overflow: "hidden", padding: "clamp(28px, 6vw, 72px) 16px", background: "var(--pb-color-text)", color: "var(--pb-color-surface)", fontFamily: "var(--pb-font-family)" }, children: [
1140
+ /* @__PURE__ */ jsx4(HeroAsset, { src: props.heroImageUrl }),
1141
+ /* @__PURE__ */ jsx4("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, background: "rgb(0 0 0 / 42%)" } }),
1142
+ /* @__PURE__ */ jsxs4("div", { "data-sales-query-card": true, style: { position: "relative", zIndex: 1, boxSizing: "border-box", width: "min(560px, 100%)", padding: "clamp(28px, 5vw, 48px)", borderRadius: "var(--pb-radius)", background: "var(--pb-color-surface)", color: "var(--pb-color-text)", boxShadow: "0 20px 56px rgb(0 0 0 / 28%)" }, children: [
1143
+ /* @__PURE__ */ jsx4("h1", { style: { margin: "0 0 28px", color: "var(--pb-color-text)", fontSize: "clamp(32px, 5vw, 48px)", fontWeight: 800, letterSpacing: "-0.035em", lineHeight: 1.02, textAlign: "center" }, children: text5(props, "heading", "Track an order") }),
1144
+ /* @__PURE__ */ jsxs4("form", { noValidate: true, onSubmit: submit, style: { display: "grid", gap: 14 }, children: [
1145
+ /* @__PURE__ */ jsx4("label", { htmlFor: inputId, style: { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", border: 0 }, children: "Tracking number" }),
1146
+ /* @__PURE__ */ jsx4("input", { id: inputId, "aria-describedby": description ? errorId : void 0, "aria-invalid": Boolean(inputError) || void 0, "aria-label": "Sales tracking number", value: trackingNumber, onChange: (event) => {
1147
+ setTrackingNumber(event.target.value);
1148
+ if (inputError) setInputError(null);
1149
+ }, inputMode: "text", autoComplete: "off", pattern: "[A-Za-z0-9-]{4,64}", minLength: 4, maxLength: 64, required: true, placeholder: "Enter your tracking number", style: { boxSizing: "border-box", width: "100%", minHeight: 58, padding: "12px 16px", border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.25)", background: "var(--pb-color-surface)", color: "var(--pb-color-text)", font: "inherit", fontSize: 17 } }),
1150
+ /* @__PURE__ */ jsx4("button", { type: "submit", disabled: runtime.phase === "loading", style: { width: "100%", minHeight: 58, padding: "12px 18px", border: 0, borderRadius: "calc(var(--pb-radius) / 1.25)", background: "var(--pb-color-text)", color: "var(--pb-color-surface)", font: "inherit", fontSize: 16, fontWeight: 700, cursor: runtime.phase === "loading" ? "wait" : "pointer" }, children: runtime.phase === "loading" ? "Checking\u2026" : text5(props, "submitLabel", "Track order") })
1151
+ ] }),
1152
+ runtime.phase === "loading" ? /* @__PURE__ */ jsx4(Status, { children: "Checking your order\u2026" }) : null,
1153
+ inputError ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", children: inputError }) : null,
1154
+ !inputError && runtime.phase === "empty" ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "status", children: "We couldn\u2019t find an order for that number." }) : null,
1155
+ !inputError && runtime.phase === "error" ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", children: "We couldn\u2019t retrieve this order right now. Please try again later." }) : null,
1156
+ /* @__PURE__ */ jsx4("small", { style: { display: "block", marginTop: 14, color: "var(--pb-color-muted)", fontSize: 11, textAlign: "right" }, children: "Powered by BestTrack" })
1157
+ ] })
1008
1158
  ] });
1009
1159
  }
1010
1160
  function SalesOrderItemsBlock(props) {
1011
1161
  const runtime = useSalesRuntime();
1012
1162
  const title = text5(props, "heading", "Items in your order");
1013
1163
  const items = runtime.result?.orderItems ?? [];
1014
- return /* @__PURE__ */ jsx4(Section, { title, children: runtime.phase === "error" ? /* @__PURE__ */ jsx4("p", { role: "alert", children: "Order items are temporarily unavailable." }) : runtime.phase === "success" && items.length ? /* @__PURE__ */ jsx4("ul", { style: { marginBottom: 0, paddingLeft: 20 }, children: items.map((item) => /* @__PURE__ */ jsxs4("li", { children: [
1015
- /* @__PURE__ */ jsx4("strong", { children: item.title }),
1016
- " \xB7 Qty ",
1017
- item.quantity
1018
- ] }, item.id)) }) : /* @__PURE__ */ jsx4("p", { children: runtime.phase === "loading" ? "Loading order items\u2026" : "Order items appear after a successful query." }) });
1164
+ return /* @__PURE__ */ jsx4(Section, { title, busy: runtime.phase === "loading", children: runtime.phase === "error" ? /* @__PURE__ */ jsx4(Status, { alert: true, children: "Order items are temporarily unavailable." }) : runtime.phase === "empty" ? /* @__PURE__ */ jsx4(Status, { children: "No order items are available for that number." }) : runtime.phase === "success" && items.length ? /* @__PURE__ */ jsx4("ul", { style: { display: "grid", gap: 12, margin: 0, padding: 0, listStyle: "none" }, children: items.map((item) => {
1165
+ const href = safeHref3(item.href);
1166
+ const itemTitle = text5(item, "title", "Order item");
1167
+ const itemContent = href ? /* @__PURE__ */ jsx4("a", { href, style: { color: "inherit" }, children: itemTitle }) : itemTitle;
1168
+ return /* @__PURE__ */ jsxs4("li", { style: { display: "flex", alignItems: "center", gap: 16, minWidth: 0, padding: 14, border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)" }, children: [
1169
+ /* @__PURE__ */ jsx4(ProductImage3, { src: item.imageUrl, alt: itemTitle }),
1170
+ /* @__PURE__ */ jsxs4("span", { style: { minWidth: 0 }, children: [
1171
+ /* @__PURE__ */ jsx4("strong", { children: itemContent }),
1172
+ /* @__PURE__ */ jsx4("br", {}),
1173
+ /* @__PURE__ */ jsxs4("small", { style: { color: "var(--pb-color-muted)" }, children: [
1174
+ "Qty ",
1175
+ Number.isFinite(item.quantity) && item.quantity > 0 ? item.quantity : 1
1176
+ ] }),
1177
+ item.description ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
1178
+ /* @__PURE__ */ jsx4("br", {}),
1179
+ /* @__PURE__ */ jsx4("small", { style: { color: "var(--pb-color-muted)" }, children: item.description })
1180
+ ] }) : null
1181
+ ] })
1182
+ ] }, item.id);
1183
+ }) }) : /* @__PURE__ */ jsx4(Status, { children: runtime.phase === "loading" ? "Loading order items\u2026" : "Order items appear after a successful query." }) });
1019
1184
  }
1020
1185
  function SalesOtherTrackingBlock(props) {
1021
1186
  const runtime = useSalesRuntime();
1022
1187
  const title = text5(props, "heading", "Other shipments");
1023
- return /* @__PURE__ */ jsx4(Section, { title, children: runtime.phase === "error" ? /* @__PURE__ */ jsx4("p", { role: "alert", children: "Additional shipments are unavailable." }) : runtime.phase === "success" ? /* @__PURE__ */ jsx4("p", { children: text5(props, "emptyMessage", "No other shipments are linked to this order.") }) : /* @__PURE__ */ jsx4("p", { children: "Other tracking numbers appear with your order result." }) });
1188
+ const shipments = (runtime.result?.shipments ?? []).filter((shipment) => shipment.trackingNumber !== runtime.result?.trackingNumber);
1189
+ return /* @__PURE__ */ jsx4(Section, { title, busy: runtime.phase === "loading", children: runtime.phase === "error" ? /* @__PURE__ */ jsx4(Status, { alert: true, children: "Additional shipments are unavailable." }) : runtime.phase === "empty" ? /* @__PURE__ */ jsx4(Status, { children: "No additional shipments are available for that number." }) : runtime.phase === "success" && shipments.length ? /* @__PURE__ */ jsx4("ul", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 220px), 1fr))", gap: 12, margin: 0, padding: 0, listStyle: "none" }, children: shipments.map((shipment) => /* @__PURE__ */ jsxs4("li", { style: { padding: 16, border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)" }, children: [
1190
+ /* @__PURE__ */ jsx4("strong", { children: text5(shipment, "label", "Shipment") }),
1191
+ shipment.status ? /* @__PURE__ */ jsx4("p", { style: { margin: "8px 0 0", color: "var(--pb-color-muted)" }, children: shipment.status }) : null,
1192
+ shipment.trackingNumber ? /* @__PURE__ */ jsx4("small", { style: { display: "block", marginTop: 8, color: "var(--pb-color-muted)" }, children: shipment.trackingNumber }) : null
1193
+ ] }, shipment.id)) }) : runtime.phase === "success" ? /* @__PURE__ */ jsx4(Status, { children: text5(props, "emptyMessage", "No other shipments are linked to this order.") }) : /* @__PURE__ */ jsx4(Status, { children: runtime.phase === "loading" ? "Loading other shipments\u2026" : "Other tracking numbers appear with your order result." }) });
1024
1194
  }
1025
1195
  function SalesServiceCardsBlock(props) {
1026
1196
  const title = text5(props, "heading", "Shop with confidence");
1027
1197
  return /* @__PURE__ */ jsx4(Section, { title, children: /* @__PURE__ */ jsxs4("div", { style: gridStyle, children: [
1028
- /* @__PURE__ */ jsxs4("article", { children: [
1198
+ /* @__PURE__ */ jsxs4("article", { style: { padding: 20, border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)" }, children: [
1029
1199
  /* @__PURE__ */ jsx4("strong", { children: text5(props, "firstTitle", "Easy returns") }),
1030
- /* @__PURE__ */ jsx4("p", { children: text5(props, "firstDescription", "Simple support when plans change.") })
1200
+ /* @__PURE__ */ jsx4("p", { style: { marginBottom: 0, color: "var(--pb-color-muted)" }, children: text5(props, "firstDescription", "Simple support when plans change.") })
1031
1201
  ] }),
1032
- /* @__PURE__ */ jsxs4("article", { children: [
1202
+ /* @__PURE__ */ jsxs4("article", { style: { padding: 20, border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)" }, children: [
1033
1203
  /* @__PURE__ */ jsx4("strong", { children: text5(props, "secondTitle", "Secure delivery") }),
1034
- /* @__PURE__ */ jsx4("p", { children: text5(props, "secondDescription", "Follow every milestone in one place.") })
1204
+ /* @__PURE__ */ jsx4("p", { style: { marginBottom: 0, color: "var(--pb-color-muted)" }, children: text5(props, "secondDescription", "Follow every milestone in one place.") })
1035
1205
  ] })
1036
1206
  ] }) });
1037
1207
  }
1038
1208
  function SalesProductCategoriesBlock(props) {
1039
1209
  const title = text5(props, "heading", "Shop by category");
1040
- const collectionId = text5(props, "collectionId", "");
1041
- const state = resourceState(collectionId);
1042
- return /* @__PURE__ */ jsx4(Section, { title, children: state === "invalid" ? /* @__PURE__ */ jsx4("p", { role: "alert", children: "Collection reference is invalid." }) : state === "empty" ? /* @__PURE__ */ jsx4("p", { children: "No collection selected. Choose a collection through the authorized resource BFF." }) : /* @__PURE__ */ jsxs4("div", { style: gridStyle, children: [
1043
- /* @__PURE__ */ jsx4("a", { href: safeHref3(props.collectionHref), style: { color: "var(--pb-color-primary)" }, children: text5(props, "collectionLabel", "Featured collection") }),
1044
- /* @__PURE__ */ jsxs4("small", { children: [
1045
- "Collection reference: ",
1046
- collectionId
1047
- ] })
1048
- ] }) });
1210
+ const state = resourceState(props.collectionId);
1211
+ const href = safeHref3(props.collectionHref);
1212
+ return /* @__PURE__ */ jsx4(Section, { title, children: state === "invalid" ? /* @__PURE__ */ jsx4(Status, { alert: true, children: "Collection reference is invalid." }) : state === "empty" ? /* @__PURE__ */ jsx4(Status, { children: "No collection selected. Choose a collection through an authorized resource integration." }) : href ? /* @__PURE__ */ jsxs4("a", { href, style: { display: "flex", alignItems: "center", justifyContent: "space-between", minHeight: 74, boxSizing: "border-box", padding: "14px 18px", border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)", color: "var(--pb-color-text)", fontWeight: 700, textDecoration: "none" }, children: [
1213
+ /* @__PURE__ */ jsx4("span", { children: text5(props, "collectionLabel", "Featured collection") }),
1214
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2192" })
1215
+ ] }) : /* @__PURE__ */ jsx4(Status, { alert: true, children: "Collection link is unavailable." }) });
1049
1216
  }
1050
1217
  function SalesRecommendationsBlock(props) {
1051
1218
  const runtime = useSalesRuntime();
1052
1219
  const title = text5(props, "heading", "Complete your order");
1053
1220
  const recommendations = runtime.result?.recommendations ?? [];
1054
- return /* @__PURE__ */ jsx4(Section, { title, children: runtime.phase === "error" ? /* @__PURE__ */ jsx4("p", { role: "alert", children: "Recommendations are temporarily unavailable." }) : runtime.phase === "success" && recommendations.length ? /* @__PURE__ */ jsx4("div", { style: gridStyle, children: recommendations.map((item) => /* @__PURE__ */ jsxs4("article", { style: { borderTop: "3px solid var(--pb-color-primary)", paddingTop: 8 }, children: [
1055
- /* @__PURE__ */ jsx4("strong", { children: item.title }),
1056
- /* @__PURE__ */ jsx4("p", { children: item.description })
1057
- ] }, item.id)) }) : /* @__PURE__ */ jsx4("p", { children: runtime.phase === "success" ? "No recommendations are available for this order." : "Recommended products appear with your order result." }) });
1221
+ return /* @__PURE__ */ jsx4(Section, { title, busy: runtime.phase === "loading", children: runtime.phase === "error" ? /* @__PURE__ */ jsx4(Status, { alert: true, children: "Recommendations are temporarily unavailable." }) : runtime.phase === "empty" ? /* @__PURE__ */ jsx4(Status, { children: "No recommendations are available for that number." }) : runtime.phase === "success" && recommendations.length ? /* @__PURE__ */ jsx4("div", { style: gridStyle, children: recommendations.map((item) => {
1222
+ const href = safeHref3(item.href);
1223
+ const itemTitle = text5(item, "title", "Recommended product");
1224
+ return /* @__PURE__ */ jsxs4("article", { style: { display: "grid", gap: 14, minWidth: 0, padding: 18, border: "1px solid var(--pb-color-border)", borderRadius: "calc(var(--pb-radius) / 1.5)", background: "var(--pb-color-background)" }, children: [
1225
+ /* @__PURE__ */ jsx4(ProductImage3, { src: item.imageUrl, alt: itemTitle }),
1226
+ /* @__PURE__ */ jsxs4("div", { children: [
1227
+ /* @__PURE__ */ jsx4("strong", { children: href ? /* @__PURE__ */ jsx4("a", { href, style: { color: "inherit" }, children: itemTitle }) : itemTitle }),
1228
+ item.price ? /* @__PURE__ */ jsx4("small", { style: { display: "block", marginTop: 5, color: "var(--pb-color-muted)" }, children: item.price }) : null,
1229
+ item.description ? /* @__PURE__ */ jsx4("p", { style: { marginBottom: 0, color: "var(--pb-color-muted)" }, children: item.description }) : null
1230
+ ] })
1231
+ ] }, item.id);
1232
+ }) }) : /* @__PURE__ */ jsx4(Status, { children: runtime.phase === "success" ? "No recommendations are available for this order." : runtime.phase === "loading" ? "Loading recommendations\u2026" : "Recommended products appear with your order result." }) });
1058
1233
  }
1059
1234
 
1060
1235
  // src/sales-definition.ts
1061
- var text6 = (label, options = {}) => ({ field: "besttrack.sales.text", label, control: "text", ...options });
1236
+ var trackingNumberPattern2 = /^[A-Za-z0-9-]{4,64}$/;
1237
+ var text6 = (label, options = {}) => ({ field: "besttrack.sales.text", label, control: "text", required: true, ...options });
1238
+ var shortText = (label, description) => text6(label, { description, group: "Content" });
1239
+ var longText = (label, description) => text6(label, { control: "textarea", description, group: "Content" });
1240
+ var link = (label, description) => text6(label, { control: "url", description: description ?? "Use a site-relative path or HTTPS URL.", group: "Links" });
1241
+ function safeLink(value) {
1242
+ if (typeof value !== "string") return false;
1243
+ if (value.startsWith("/") && !value.startsWith("//") && !value.startsWith("/\\") && !value.includes("\\") && !hasControlCharacter2(value)) return true;
1244
+ try {
1245
+ return new URL(value).protocol === "https:";
1246
+ } catch {
1247
+ return false;
1248
+ }
1249
+ }
1250
+ function hasControlCharacter2(value) {
1251
+ return [...value].some((character) => character.charCodeAt(0) < 32);
1252
+ }
1253
+ function requiredString(props, key, issues, maxLength = 160) {
1254
+ const value = props[key];
1255
+ if (typeof value !== "string" || !value.trim()) issues.push({ path: `props.${key}`, message: "A non-empty value is required." });
1256
+ else if (value.length > maxLength) issues.push({ path: `props.${key}`, message: `Must be at most ${maxLength} characters.` });
1257
+ }
1258
+ function textProps(keys) {
1259
+ return (props) => {
1260
+ const issues = [];
1261
+ keys.forEach(([key, maxLength]) => requiredString(props, key, issues, maxLength));
1262
+ return issues;
1263
+ };
1264
+ }
1265
+ function validateQuery(props) {
1266
+ const issues = textProps([["heading", 120], ["submitLabel", 80]])(props);
1267
+ const trackingNumber = props.defaultTrackingNumber;
1268
+ if (typeof trackingNumber !== "string" || !trackingNumberPattern2.test(trackingNumber)) issues.push({ path: "props.defaultTrackingNumber", message: "Use 4\u201364 letters, numbers, or hyphens. Use a non-sensitive preview placeholder only." });
1269
+ if (props.heroImageUrl !== void 0 && !safeImage(props.heroImageUrl)) issues.push({ path: "props.heroImageUrl", message: "Use an HTTPS URL for the merchant-configured hero image." });
1270
+ return issues;
1271
+ }
1272
+ function safeImage(value) {
1273
+ if (typeof value !== "string") return false;
1274
+ try {
1275
+ return new URL(value).protocol === "https:";
1276
+ } catch {
1277
+ return false;
1278
+ }
1279
+ }
1280
+ function validateCategories(props) {
1281
+ const issues = textProps([["heading", 120], ["collectionId", 256], ["collectionLabel", 120]])(props);
1282
+ if (!safeLink(props.collectionHref)) issues.push({ path: "props.collectionHref", message: "Use a site-relative path or HTTPS URL." });
1283
+ return issues;
1284
+ }
1062
1285
  var salesBlocks = [
1063
- { type: "besttrack.sales.announcement", version: 1, label: "Announcement", category: "BestTrack Sales", targets: ["web"], defaultProps: { message: "Free delivery on orders over $50" }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }, { id: "minimal", label: "Minimal", theme: { "color.primary": "#202223" } }], fields: { message: text6("Announcement", { control: "textarea" }) }, render: { web: SalesAnnouncementBlock } },
1064
- { type: "besttrack.sales.query", version: 1, label: "Order query", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Track an order", submitLabel: "Track order", defaultTrackingNumber: "BT-2048-DEMO" }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }, { id: "compact", label: "Compact", theme: { spacing: "12px" } }], fields: { heading: text6("Heading"), submitLabel: text6("Button label"), defaultTrackingNumber: text6("Default tracking number") }, render: { web: SalesQueryBlock } },
1065
- { type: "besttrack.sales.order-items", version: 1, label: "Order items", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Items in your order" }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }], fields: { heading: text6("Heading") }, render: { web: SalesOrderItemsBlock } },
1066
- { type: "besttrack.sales.other-tracking", version: 1, label: "Other tracking", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Other shipments", emptyMessage: "No other shipments are linked to this order." }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }], fields: { heading: text6("Heading"), emptyMessage: text6("Empty message", { control: "textarea" }) }, render: { web: SalesOtherTrackingBlock } },
1067
- { type: "besttrack.sales.service-cards", version: 1, label: "Service cards", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Shop with confidence", firstTitle: "Easy returns", firstDescription: "Simple support when plans change.", secondTitle: "Secure delivery", secondDescription: "Follow every milestone in one place." }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }], fields: { heading: text6("Heading"), firstTitle: text6("First card title"), firstDescription: text6("First card description", { control: "textarea" }), secondTitle: text6("Second card title"), secondDescription: text6("Second card description", { control: "textarea" }) }, render: { web: SalesServiceCardsBlock } },
1068
- { type: "besttrack.sales.product-categories", version: 1, label: "Product categories", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Shop by category", collectionId: "gid://shopify/Collection/featured", collectionLabel: "Featured collection", collectionHref: "/collections/featured" }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }, { id: "grid", label: "Grid", theme: { "color.surface": "#f8fafc" } }], fields: { heading: text6("Heading"), collectionId: text6("Collection ID"), collectionLabel: text6("Collection label"), collectionHref: text6("Collection URL", { control: "url" }) }, render: { web: SalesProductCategoriesBlock } },
1069
- { type: "besttrack.sales.recommendations", version: 1, label: "Recommended products", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Complete your order" }, defaultVariant: "commerce", variants: [{ id: "commerce", label: "Commerce" }, { id: "grid", label: "Product grid", theme: { "color.surface": "#f8fafc" } }], fields: { heading: text6("Heading") }, render: { web: SalesRecommendationsBlock } }
1286
+ { type: "besttrack.sales.announcement", version: 1, label: "Announcement", category: "BestTrack Sales", targets: ["web"], defaultProps: { message: "Free delivery on orders over $50" }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }, { id: "minimal", label: "Minimal", theme: { "color.primary": "#202223" } }], fields: { message: longText("Announcement", "Short, customer-facing promotion copy.") }, validate: textProps([["message", 280]]), render: { web: SalesAnnouncementBlock, editor: SalesAnnouncementEditor } },
1287
+ { type: "besttrack.sales.query", version: 1, label: "Order query", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Track your order", submitLabel: "Track order", defaultTrackingNumber: "BT-2048-DEMO", heroImageUrl: "https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?auto=format&fit=crop&w=1800&q=85" }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }, { id: "compact", label: "Compact", theme: { spacing: "12px" } }], fields: { heading: shortText("Heading"), submitLabel: shortText("Button label"), defaultTrackingNumber: text6("Default tracking number", { group: "Tracking settings", description: "Preview-only placeholder; never save a customer's tracking number." }), heroImageUrl: text6("Hero image URL", { control: "url", group: "Hero asset", description: "Merchant-configured HTTPS image. It is rendered as an image element, never as CSS." }) }, validate: validateQuery, render: { web: SalesQueryBlock, editor: SalesQueryEditor } },
1288
+ { type: "besttrack.sales.order-items", version: 1, label: "Order items", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Items in your order" }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }], fields: { heading: shortText("Heading") }, validate: textProps([["heading", 120]]), render: { web: SalesOrderItemsBlock, editor: SalesOrderItemsEditor } },
1289
+ { type: "besttrack.sales.other-tracking", version: 1, label: "Other tracking", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Other shipments", emptyMessage: "No other shipments are linked to this order." }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }], fields: { heading: shortText("Heading"), emptyMessage: longText("Empty message") }, validate: textProps([["heading", 120], ["emptyMessage", 280]]), render: { web: SalesOtherTrackingBlock, editor: SalesOtherTrackingEditor } },
1290
+ { type: "besttrack.sales.service-cards", version: 1, label: "Service cards", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Shop with confidence", firstTitle: "Easy returns", firstDescription: "Simple support when plans change.", secondTitle: "Secure delivery", secondDescription: "Follow every milestone in one place." }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }], fields: { heading: shortText("Heading"), firstTitle: shortText("First card title"), firstDescription: longText("First card description"), secondTitle: shortText("Second card title"), secondDescription: longText("Second card description") }, validate: textProps([["heading", 120], ["firstTitle", 120], ["firstDescription", 280], ["secondTitle", 120], ["secondDescription", 280]]), render: { web: SalesServiceCardsBlock, editor: SalesServiceCardsEditor } },
1291
+ { type: "besttrack.sales.product-categories", version: 1, label: "Product categories", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Shop by category", collectionId: "gid://shopify/Collection/featured", collectionLabel: "Featured collection", collectionHref: "/collections/featured" }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }, { id: "grid", label: "Grid", theme: { "color.surface": "#f8fafc" } }], fields: { heading: shortText("Heading"), collectionId: text6("Collection ID", { group: "Resource", description: "JSON-only reference selected by an authorized integration." }), collectionLabel: shortText("Collection label"), collectionHref: link("Collection URL") }, validate: validateCategories, render: { web: SalesProductCategoriesBlock, editor: SalesProductCategoriesEditor } },
1292
+ { type: "besttrack.sales.recommendations", version: 1, label: "Recommended products", category: "BestTrack Sales", targets: ["web"], defaultProps: { heading: "Complete your order" }, defaultVariant: "hero", variants: [{ id: "hero", label: "Sales Hero" }, { id: "commerce", label: "Commerce" }, { id: "grid", label: "Product grid", theme: { "color.surface": "#f8fafc" } }], fields: { heading: shortText("Heading") }, validate: textProps([["heading", 120]]), render: { web: SalesRecommendationsBlock, editor: SalesRecommendationsEditor } }
1070
1293
  ];
1071
1294
  function createSalesTemplate() {
1072
- return { id: "besttrack.sales", version: 1, name: "Sales", target: "web", source: "built-in", requiredBlocks: salesBlocks.map((block) => block.type), theme: { "color.primary": "#dc2626", "color.background": "#fffaf5", "color.surface": "#ffffff", "color.text": "#1c1917", "color.border": "#fed7aa", "font.family": "system-ui, sans-serif", radius: "10px", spacing: "20px" }, create: () => createPageDocument3({ pageId: "besttrack-sales", target: "web", templateId: "besttrack.sales", templateVersion: 1, settings: { locale: "en", seoTitle: "Shop and track your order" }, blocks: salesBlocks.map((block, index) => ({ id: "sales-" + (index + 1), type: block.type, version: block.version, props: block.defaultProps, variant: block.defaultVariant, style: {} })) }) };
1295
+ return { id: "besttrack.sales", version: 1, name: "Sales", target: "web", source: "built-in", requiredBlocks: salesBlocks.map((block) => block.type), theme: { "color.primary": "#000000", "color.background": "#f7f5f0", "color.surface": "#ffffff", "color.text": "#0a0a0a", "color.muted": "#6b6b6b", "color.border": "#dfddd7", "font.family": "Arial, Helvetica, sans-serif", radius: "10px", spacing: "24px" }, create: () => createPageDocument3({ pageId: "besttrack-sales", target: "web", templateId: "besttrack.sales", templateVersion: 1, settings: { locale: "en", seoTitle: "Shop and track your order" }, blocks: salesBlocks.map((block, index) => ({ id: "sales-" + (index + 1), type: block.type, version: block.version, props: block.defaultProps, variant: block.defaultVariant, style: {} })) }) };
1073
1296
  }
1074
1297
  var bestTrackSalesExtension = Object.freeze({ name: "besttrack.sales", version: "0.7.2", fields: [{ type: "besttrack.sales.text", component: SalesTextField }], blocks: salesBlocks, templates: [createSalesTemplate()] });
1075
1298
  export {
@@ -1081,5 +1304,6 @@ export {
1081
1304
  bestTrackSalesExtension,
1082
1305
  createBrandedTemplate,
1083
1306
  createReadyToGoTemplate,
1084
- createSalesTemplate
1307
+ createSalesTemplate,
1308
+ isEmptyTrackingPageResult
1085
1309
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standhigher/besttrack-page-extension",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "BestTrack business extension entry point for @standhigher/puck-page-builder.",
5
5
  "type": "module",
6
6
  "files": [
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@standhigher/puck-page-builder": "0.8.1"
19
+ "@standhigher/puck-page-builder": "0.9.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "tsup": "^8.5.1",