@nok-integration/storefront-react 0.6.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/dist/index.d.ts +3530 -0
- package/dist/index.mjs +7629 -0
- package/dist/standalone/storefront.mjs +7629 -0
- package/dist/standalone/styles.css +2277 -0
- package/dist/styles.css +2277 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3530 @@
|
|
|
1
|
+
import { AnchorHTMLAttributes } from 'react';
|
|
2
|
+
import { ButtonHTMLAttributes } from 'react';
|
|
3
|
+
import { ComponentPropsWithoutRef } from 'react';
|
|
4
|
+
import { CSSProperties } from 'react';
|
|
5
|
+
import { ForwardRefExoticComponent } from 'react';
|
|
6
|
+
import { JSX } from 'react';
|
|
7
|
+
import { ReactNode } from 'react';
|
|
8
|
+
import { RefAttributes } from 'react';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
/** `POST /v1/cart/items` — add an item. */
|
|
12
|
+
declare const AddCartItemRequest: z.ZodObject<{
|
|
13
|
+
sku: z.ZodString;
|
|
14
|
+
qty: z.ZodNumber;
|
|
15
|
+
}, "strict", z.ZodTypeAny, {
|
|
16
|
+
sku: string;
|
|
17
|
+
qty: number;
|
|
18
|
+
}, {
|
|
19
|
+
sku: string;
|
|
20
|
+
qty: number;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
declare type AddCartItemRequest = z.infer<typeof AddCartItemRequest>;
|
|
24
|
+
|
|
25
|
+
/** The `error` member — a machine `code`, a human `message`, and optional `details`. */
|
|
26
|
+
declare const ApiErrorBody: z.ZodObject<{
|
|
27
|
+
code: z.ZodEnum<["invalid_request", "unauthorized", "out_of_stock", "cart_locked", "sku_not_found", "category_not_found", "amount_mismatch", "duplicate_order", "order_not_found", "order_not_cancellable", "forbidden", "invalid_transition", "rate_limited", "server_error"]>;
|
|
28
|
+
message: z.ZodString;
|
|
29
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
30
|
+
}, "strip", z.ZodTypeAny, {
|
|
31
|
+
code: "invalid_request" | "unauthorized" | "out_of_stock" | "cart_locked" | "sku_not_found" | "category_not_found" | "amount_mismatch" | "duplicate_order" | "order_not_found" | "order_not_cancellable" | "forbidden" | "invalid_transition" | "rate_limited" | "server_error";
|
|
32
|
+
message: string;
|
|
33
|
+
details?: Record<string, unknown> | undefined;
|
|
34
|
+
}, {
|
|
35
|
+
code: "invalid_request" | "unauthorized" | "out_of_stock" | "cart_locked" | "sku_not_found" | "category_not_found" | "amount_mismatch" | "duplicate_order" | "order_not_found" | "order_not_cancellable" | "forbidden" | "invalid_transition" | "rate_limited" | "server_error";
|
|
36
|
+
message: string;
|
|
37
|
+
details?: Record<string, unknown> | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
declare type ApiErrorBody = z.infer<typeof ApiErrorBody>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Narrow an unknown thrown value to the typed API error, if it is one.
|
|
44
|
+
*
|
|
45
|
+
* **Structural, not `instanceof`.** The identity check looks obviously correct and is
|
|
46
|
+
* wrong across a bundle boundary: the standalone distribution embeds its own copy of the
|
|
47
|
+
* client, so an error thrown inside the SDK is not an `instanceof` the `DaznApiError`
|
|
48
|
+
* a host imported separately — and vice versa. Every branch silently falls through to
|
|
49
|
+
* "unknown error", which is the worst possible failure for error handling because it
|
|
50
|
+
* still *works*, just always wrongly.
|
|
51
|
+
*
|
|
52
|
+
* Found by consuming the built package instead of the source. Against source there is
|
|
53
|
+
* one class and `instanceof` passes.
|
|
54
|
+
*/
|
|
55
|
+
export declare function asApiError(err: unknown): DaznApiError | undefined;
|
|
56
|
+
|
|
57
|
+
/** Small status/label pill. */
|
|
58
|
+
export declare function Badge({ tone, children }: BadgeProps): JSX.Element;
|
|
59
|
+
|
|
60
|
+
export declare interface BadgeProps {
|
|
61
|
+
tone?: BadgeTone;
|
|
62
|
+
children: ReactNode;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export declare type BadgeTone = 'neutral' | 'success' | 'critical' | 'promo';
|
|
66
|
+
|
|
67
|
+
export declare function BottomSheet({ open, onClose, title, children, footer }: BottomSheetProps): JSX.Element | null;
|
|
68
|
+
|
|
69
|
+
export declare interface BottomSheetProps {
|
|
70
|
+
open: boolean;
|
|
71
|
+
onClose: () => void;
|
|
72
|
+
title: string;
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
/** Optional sticky footer (e.g. a StickyCtaBar). */
|
|
75
|
+
footer?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Primary/secondary/ghost button. ≥44px tap target, visible focus, disabled and
|
|
80
|
+
* loading states. Loading sets `aria-busy` and blocks clicks without collapsing size.
|
|
81
|
+
*/
|
|
82
|
+
export declare const Button: ForwardRefExoticComponent<ButtonProps & RefAttributes<HTMLButtonElement>>;
|
|
83
|
+
|
|
84
|
+
export declare interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
85
|
+
variant?: ButtonVariant;
|
|
86
|
+
/** Shows a spinner and marks the control busy; also disables interaction. */
|
|
87
|
+
loading?: boolean;
|
|
88
|
+
fullWidth?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export declare type ButtonVariant = 'primary' | 'secondary' | 'ghost';
|
|
92
|
+
|
|
93
|
+
/** A line in the cart. `line_total` = `unit_price` × `qty`, in the cart currency. */
|
|
94
|
+
declare const CartItem: z.ZodObject<{
|
|
95
|
+
sku: z.ZodString;
|
|
96
|
+
qty: z.ZodNumber;
|
|
97
|
+
unit_price: z.ZodObject<{
|
|
98
|
+
amount: z.ZodNumber;
|
|
99
|
+
currency: z.ZodString;
|
|
100
|
+
}, "strict", z.ZodTypeAny, {
|
|
101
|
+
amount: number;
|
|
102
|
+
currency: string;
|
|
103
|
+
}, {
|
|
104
|
+
amount: number;
|
|
105
|
+
currency: string;
|
|
106
|
+
}>;
|
|
107
|
+
line_total: z.ZodObject<{
|
|
108
|
+
amount: z.ZodNumber;
|
|
109
|
+
currency: z.ZodString;
|
|
110
|
+
}, "strict", z.ZodTypeAny, {
|
|
111
|
+
amount: number;
|
|
112
|
+
currency: string;
|
|
113
|
+
}, {
|
|
114
|
+
amount: number;
|
|
115
|
+
currency: string;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Product title for this line, e.g. "Signature Hoodie".
|
|
119
|
+
*
|
|
120
|
+
* **Display only**, and the reason it exists: without it a cart can only render the
|
|
121
|
+
* `sku`, and a SKU is an internal identifier, not something to show a fan. A consumer
|
|
122
|
+
* must not parse it, match on it, or treat it as stable — it is whatever the catalogue
|
|
123
|
+
* says today, and it is localised with the rest of the catalogue.
|
|
124
|
+
*
|
|
125
|
+
* Optional because it is projected from the catalogue read model, which is a cache: a
|
|
126
|
+
* SKU that has been de-listed since it entered the cart still has a line, and a
|
|
127
|
+
* fabricated title would be worse than none. Absent = the consumer falls back to
|
|
128
|
+
* whatever it can show, and the line still prices and checks out correctly.
|
|
129
|
+
*/
|
|
130
|
+
title: z.ZodOptional<z.ZodString>;
|
|
131
|
+
/**
|
|
132
|
+
* Line thumbnail — the variant image where the variant has one, otherwise the
|
|
133
|
+
* product's first media entry. Absent = render a neutral placeholder; never
|
|
134
|
+
* substitute another product's image.
|
|
135
|
+
*/
|
|
136
|
+
image: z.ZodOptional<z.ZodString>;
|
|
137
|
+
/**
|
|
138
|
+
* The variant's defining attributes, e.g. `{ size: 'M', colour: 'Black' }`. Mirrors
|
|
139
|
+
* {@link ProductVariant.attributes}, so a consumer reads one shape everywhere.
|
|
140
|
+
*
|
|
141
|
+
* Sent as the raw map rather than a pre-composed "M · Black" string on purpose. The
|
|
142
|
+
* separator, the order and the casing are presentation, and the engine has no business
|
|
143
|
+
* choosing them for a host that may render size and colour as separate chips. Two
|
|
144
|
+
* lines differing only by size are otherwise indistinguishable in the cart, which is
|
|
145
|
+
* the bug this field exists to fix.
|
|
146
|
+
*/
|
|
147
|
+
attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
148
|
+
/**
|
|
149
|
+
* Optional stock ceiling for this line, so the quantity stepper can cap the increment
|
|
150
|
+
* and tell the fan *why* instead of silently refusing. Mirrors
|
|
151
|
+
* {@link ProductVariant.available}.
|
|
152
|
+
*
|
|
153
|
+
* **Display only, and a hint.** The projection is a cache and the authoritative check
|
|
154
|
+
* runs at checkout (DAZN's, under ADR-0008) — a consumer must not treat this as a
|
|
155
|
+
* guarantee that N are purchasable. Absent = impose no cap client-side; the engine
|
|
156
|
+
* still rejects an over-quantity update with `out_of_stock`, which carries the real
|
|
157
|
+
* `available` figure in its error details.
|
|
158
|
+
*/
|
|
159
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
160
|
+
}, "strip", z.ZodTypeAny, {
|
|
161
|
+
sku: string;
|
|
162
|
+
qty: number;
|
|
163
|
+
unit_price: {
|
|
164
|
+
amount: number;
|
|
165
|
+
currency: string;
|
|
166
|
+
};
|
|
167
|
+
line_total: {
|
|
168
|
+
amount: number;
|
|
169
|
+
currency: string;
|
|
170
|
+
};
|
|
171
|
+
title?: string | undefined;
|
|
172
|
+
image?: string | undefined;
|
|
173
|
+
attributes?: Record<string, string> | undefined;
|
|
174
|
+
available?: number | undefined;
|
|
175
|
+
}, {
|
|
176
|
+
sku: string;
|
|
177
|
+
qty: number;
|
|
178
|
+
unit_price: {
|
|
179
|
+
amount: number;
|
|
180
|
+
currency: string;
|
|
181
|
+
};
|
|
182
|
+
line_total: {
|
|
183
|
+
amount: number;
|
|
184
|
+
currency: string;
|
|
185
|
+
};
|
|
186
|
+
title?: string | undefined;
|
|
187
|
+
image?: string | undefined;
|
|
188
|
+
attributes?: Record<string, string> | undefined;
|
|
189
|
+
available?: number | undefined;
|
|
190
|
+
}>;
|
|
191
|
+
|
|
192
|
+
declare type CartItem = z.infer<typeof CartItem>;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A single cart line: media, title, variant, unit price, qty stepper, and line total.
|
|
196
|
+
*
|
|
197
|
+
* Title, image and variant come from the line itself (contracts 2.15.0). Before that the
|
|
198
|
+
* wire carried only a SKU, so this rendered an internal code against an empty grey box —
|
|
199
|
+
* and two sizes of one product were indistinguishable.
|
|
200
|
+
*
|
|
201
|
+
* All three stay optional, because the catalogue projection is a cache: a SKU de-listed
|
|
202
|
+
* after it entered the cart still has a line. It degrades rather than breaks — the SKU
|
|
203
|
+
* stands in for a missing title, and a missing image leaves the placeholder — so the fan
|
|
204
|
+
* can always still read the price and remove the line.
|
|
205
|
+
*/
|
|
206
|
+
export declare function CartLineItem({ item, title, image, maxQty, onQtyChange, onRemove, pending, }: CartLineItemProps): JSX.Element;
|
|
207
|
+
|
|
208
|
+
export declare interface CartLineItemProps {
|
|
209
|
+
item: CartItem;
|
|
210
|
+
/**
|
|
211
|
+
* Override the line's own title/image (contracts 2.15.0). Escape hatches for a host
|
|
212
|
+
* with richer catalogue data than the cart carries — not the normal path, and not
|
|
213
|
+
* required for the line to render properly.
|
|
214
|
+
*/
|
|
215
|
+
title?: string;
|
|
216
|
+
image?: string;
|
|
217
|
+
maxQty?: number;
|
|
218
|
+
onQtyChange: (qty: number) => void;
|
|
219
|
+
onRemove: () => void;
|
|
220
|
+
pending?: boolean;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
declare interface CartSheetApi {
|
|
224
|
+
open: () => void;
|
|
225
|
+
close: () => void;
|
|
226
|
+
isOpen: boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Presents the cart as a bottom sheet over the current screen, so adding to
|
|
231
|
+
* the cart never costs the fan their place in the catalogue.
|
|
232
|
+
*
|
|
233
|
+
* The `/cart` route still renders the same {@link CartView} — this is a second
|
|
234
|
+
* presentation of one component, not a second cart. Both read the session cart from the
|
|
235
|
+
* engine, so they cannot disagree.
|
|
236
|
+
*/
|
|
237
|
+
declare function CartSheetProvider({ children }: {
|
|
238
|
+
children: ReactNode;
|
|
239
|
+
}): JSX.Element;
|
|
240
|
+
export { CartSheetProvider as CartSheet }
|
|
241
|
+
export { CartSheetProvider }
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Opens the cart as a bottom sheet.
|
|
245
|
+
*
|
|
246
|
+
* Falls back to a link to `/cart` when no {@link CartSheetProvider} is above it, so the
|
|
247
|
+
* cart is always reachable — including with JS unavailable, where the anchor still
|
|
248
|
+
* navigates. The route renders the same CartView, so the fallback is the same cart, not
|
|
249
|
+
* a degraded one.
|
|
250
|
+
*/
|
|
251
|
+
export declare function CartTrigger({ className, children, ...rest }: CartTriggerProps): JSX.Element;
|
|
252
|
+
|
|
253
|
+
export declare interface CartTriggerProps {
|
|
254
|
+
className?: string;
|
|
255
|
+
children: ReactNode;
|
|
256
|
+
'aria-label'?: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The CTA needs no prop to work in both presentations: StickyCtaBar is
|
|
261
|
+
* `position: sticky; bottom: 0`, so on the page it pins to the viewport and inside the
|
|
262
|
+
* sheet it pins to the bottom of the sheet's scrolling body. Same component, same
|
|
263
|
+
* safe-area handling, no branch.
|
|
264
|
+
*/
|
|
265
|
+
export declare function CartView(props?: CartViewProps): JSX.Element;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 4.4 Cart. Loads the session cart, supports optimistic + idempotent qty/remove edits
|
|
269
|
+
* (reverting on error), and hands off to checkout.
|
|
270
|
+
*
|
|
271
|
+
* There is no `withSession` wrapper here any more. Re-authentication moved down into the
|
|
272
|
+
* SDK's transport, which asks the host's `getToken()` once on a `401` and retries — so
|
|
273
|
+
* every component gets that behaviour without remembering to opt in, and a component
|
|
274
|
+
* that forgets can no longer quietly lose it.
|
|
275
|
+
*/
|
|
276
|
+
export declare interface CartViewProps {
|
|
277
|
+
/**
|
|
278
|
+
* Dismiss the containing sheet. Supplied when the cart is presented as a
|
|
279
|
+
* BottomSheet; omitted on the `/cart` route, where there is nothing to dismiss.
|
|
280
|
+
*/
|
|
281
|
+
onContinueShopping?: () => void;
|
|
282
|
+
/**
|
|
283
|
+
* Label for the primary CTA. Default `Proceed to checkout`.
|
|
284
|
+
*
|
|
285
|
+
* DAZN's checkout is the next step and DAZN name it; a host whose own wording is
|
|
286
|
+
* "Continue" or "Go to checkout" should not have to accept ours. The locked-cart label
|
|
287
|
+
* is deliberately NOT configurable — it states a condition rather than an action, and a
|
|
288
|
+
* host renaming it could imply the button still works.
|
|
289
|
+
*/
|
|
290
|
+
checkoutLabel?: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Query params for the single-product, recommendations and category-page reads — market
|
|
295
|
+
* context only. A separate alias (rather than reusing {@link MarketQuery} directly) so
|
|
296
|
+
* these endpoints can gain their own params later without touching the shared object.
|
|
297
|
+
*/
|
|
298
|
+
declare const CatalogReadQuery: z.ZodObject<{
|
|
299
|
+
country: z.ZodOptional<z.ZodString>;
|
|
300
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
301
|
+
}, "strip", z.ZodTypeAny, {
|
|
302
|
+
country?: string | undefined;
|
|
303
|
+
lang?: string | undefined;
|
|
304
|
+
}, {
|
|
305
|
+
country?: string | undefined;
|
|
306
|
+
lang?: string | undefined;
|
|
307
|
+
}>;
|
|
308
|
+
|
|
309
|
+
declare type CatalogReadQuery = z.infer<typeof CatalogReadQuery>;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 4.2 Category (PCP) — the composed screen, given a page that is already fetched.
|
|
313
|
+
*
|
|
314
|
+
* This exists for the same reason `ProductDetail` does: the presentation belongs to the
|
|
315
|
+
* package, so that the fetching wrapper around it — `CategoryView` here, an RSC route in
|
|
316
|
+
* our own app — is only ever fetching. It was previously written out twice, once in each,
|
|
317
|
+
* and the two drifted: the SDK copy rendered a bare `div`/`section`/`ul` tree with none of
|
|
318
|
+
* the page layout, so a host mounting `CategoryView` got correctly styled blocks stacked
|
|
319
|
+
* with no column spacing, no page background, and a full-bleed one-column "grid". The
|
|
320
|
+
* blocks looked right individually, which is what made it read as a missing stylesheet
|
|
321
|
+
* rather than a missing wrapper.
|
|
322
|
+
*
|
|
323
|
+
* Not a client component: it renders no interactivity of its own, so it stays usable
|
|
324
|
+
* directly from a server component. The pieces that do need the client carry their own
|
|
325
|
+
* `'use client'`.
|
|
326
|
+
*/
|
|
327
|
+
export declare function CategoryContent({ page }: CategoryContentProps): JSX.Element;
|
|
328
|
+
|
|
329
|
+
export declare interface CategoryContentProps {
|
|
330
|
+
/** An already-fetched category page. */
|
|
331
|
+
page: CategoryPage;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* `GET /v1/catalog/categories/{code}/page` — the CMS-authored content for a category
|
|
336
|
+
* landing page (PCP). A page is a hero plus an **ordered list of typed blocks**, so
|
|
337
|
+
* merchandisers can compose editorial + product sections freely. Product blocks embed
|
|
338
|
+
* resolved {@link Product}s so the storefront renders without a second round-trip.
|
|
339
|
+
*/
|
|
340
|
+
declare const CategoryHero: z.ZodObject<{
|
|
341
|
+
title: z.ZodString;
|
|
342
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
343
|
+
image: z.ZodOptional<z.ZodString>;
|
|
344
|
+
}, "strip", z.ZodTypeAny, {
|
|
345
|
+
title: string;
|
|
346
|
+
image?: string | undefined;
|
|
347
|
+
subtitle?: string | undefined;
|
|
348
|
+
}, {
|
|
349
|
+
title: string;
|
|
350
|
+
image?: string | undefined;
|
|
351
|
+
subtitle?: string | undefined;
|
|
352
|
+
}>;
|
|
353
|
+
|
|
354
|
+
declare type CategoryHero = z.infer<typeof CategoryHero>;
|
|
355
|
+
|
|
356
|
+
declare const CategoryPage: z.ZodObject<{
|
|
357
|
+
code: z.ZodString;
|
|
358
|
+
hero: z.ZodObject<{
|
|
359
|
+
title: z.ZodString;
|
|
360
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
361
|
+
image: z.ZodOptional<z.ZodString>;
|
|
362
|
+
}, "strip", z.ZodTypeAny, {
|
|
363
|
+
title: string;
|
|
364
|
+
image?: string | undefined;
|
|
365
|
+
subtitle?: string | undefined;
|
|
366
|
+
}, {
|
|
367
|
+
title: string;
|
|
368
|
+
image?: string | undefined;
|
|
369
|
+
subtitle?: string | undefined;
|
|
370
|
+
}>;
|
|
371
|
+
blocks: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
372
|
+
kind: z.ZodLiteral<"rail">;
|
|
373
|
+
title: z.ZodString;
|
|
374
|
+
products: z.ZodArray<z.ZodObject<{
|
|
375
|
+
sku: z.ZodString;
|
|
376
|
+
title: z.ZodString;
|
|
377
|
+
category: z.ZodString;
|
|
378
|
+
price: z.ZodObject<{
|
|
379
|
+
amount: z.ZodNumber;
|
|
380
|
+
currency: z.ZodString;
|
|
381
|
+
}, "strict", z.ZodTypeAny, {
|
|
382
|
+
amount: number;
|
|
383
|
+
currency: string;
|
|
384
|
+
}, {
|
|
385
|
+
amount: number;
|
|
386
|
+
currency: string;
|
|
387
|
+
}>;
|
|
388
|
+
compare_at: z.ZodOptional<z.ZodObject<{
|
|
389
|
+
amount: z.ZodNumber;
|
|
390
|
+
currency: z.ZodString;
|
|
391
|
+
}, "strict", z.ZodTypeAny, {
|
|
392
|
+
amount: number;
|
|
393
|
+
currency: string;
|
|
394
|
+
}, {
|
|
395
|
+
amount: number;
|
|
396
|
+
currency: string;
|
|
397
|
+
}>>;
|
|
398
|
+
media: z.ZodArray<z.ZodString, "many">;
|
|
399
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
400
|
+
sku: z.ZodString;
|
|
401
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
402
|
+
in_stock: z.ZodBoolean;
|
|
403
|
+
image: z.ZodOptional<z.ZodString>;
|
|
404
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
405
|
+
}, "strip", z.ZodTypeAny, {
|
|
406
|
+
sku: string;
|
|
407
|
+
attributes: Record<string, string>;
|
|
408
|
+
in_stock: boolean;
|
|
409
|
+
image?: string | undefined;
|
|
410
|
+
available?: number | undefined;
|
|
411
|
+
}, {
|
|
412
|
+
sku: string;
|
|
413
|
+
attributes: Record<string, string>;
|
|
414
|
+
in_stock: boolean;
|
|
415
|
+
image?: string | undefined;
|
|
416
|
+
available?: number | undefined;
|
|
417
|
+
}>, "many">;
|
|
418
|
+
description: z.ZodOptional<z.ZodString>;
|
|
419
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
420
|
+
promo: z.ZodOptional<z.ZodObject<{
|
|
421
|
+
message: z.ZodString;
|
|
422
|
+
ends_at: z.ZodOptional<z.ZodString>;
|
|
423
|
+
}, "strip", z.ZodTypeAny, {
|
|
424
|
+
message: string;
|
|
425
|
+
ends_at?: string | undefined;
|
|
426
|
+
}, {
|
|
427
|
+
message: string;
|
|
428
|
+
ends_at?: string | undefined;
|
|
429
|
+
}>>;
|
|
430
|
+
size_guide: z.ZodOptional<z.ZodObject<{
|
|
431
|
+
title: z.ZodString;
|
|
432
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
433
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>;
|
|
434
|
+
notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
435
|
+
}, "strip", z.ZodTypeAny, {
|
|
436
|
+
title: string;
|
|
437
|
+
columns?: string[] | undefined;
|
|
438
|
+
rows?: string[][] | undefined;
|
|
439
|
+
notes?: string[] | undefined;
|
|
440
|
+
}, {
|
|
441
|
+
title: string;
|
|
442
|
+
columns?: string[] | undefined;
|
|
443
|
+
rows?: string[][] | undefined;
|
|
444
|
+
notes?: string[] | undefined;
|
|
445
|
+
}>>;
|
|
446
|
+
}, "strip", z.ZodTypeAny, {
|
|
447
|
+
sku: string;
|
|
448
|
+
title: string;
|
|
449
|
+
category: string;
|
|
450
|
+
price: {
|
|
451
|
+
amount: number;
|
|
452
|
+
currency: string;
|
|
453
|
+
};
|
|
454
|
+
media: string[];
|
|
455
|
+
variants: {
|
|
456
|
+
sku: string;
|
|
457
|
+
attributes: Record<string, string>;
|
|
458
|
+
in_stock: boolean;
|
|
459
|
+
image?: string | undefined;
|
|
460
|
+
available?: number | undefined;
|
|
461
|
+
}[];
|
|
462
|
+
compare_at?: {
|
|
463
|
+
amount: number;
|
|
464
|
+
currency: string;
|
|
465
|
+
} | undefined;
|
|
466
|
+
description?: string | undefined;
|
|
467
|
+
badges?: string[] | undefined;
|
|
468
|
+
promo?: {
|
|
469
|
+
message: string;
|
|
470
|
+
ends_at?: string | undefined;
|
|
471
|
+
} | undefined;
|
|
472
|
+
size_guide?: {
|
|
473
|
+
title: string;
|
|
474
|
+
columns?: string[] | undefined;
|
|
475
|
+
rows?: string[][] | undefined;
|
|
476
|
+
notes?: string[] | undefined;
|
|
477
|
+
} | undefined;
|
|
478
|
+
}, {
|
|
479
|
+
sku: string;
|
|
480
|
+
title: string;
|
|
481
|
+
category: string;
|
|
482
|
+
price: {
|
|
483
|
+
amount: number;
|
|
484
|
+
currency: string;
|
|
485
|
+
};
|
|
486
|
+
media: string[];
|
|
487
|
+
variants: {
|
|
488
|
+
sku: string;
|
|
489
|
+
attributes: Record<string, string>;
|
|
490
|
+
in_stock: boolean;
|
|
491
|
+
image?: string | undefined;
|
|
492
|
+
available?: number | undefined;
|
|
493
|
+
}[];
|
|
494
|
+
compare_at?: {
|
|
495
|
+
amount: number;
|
|
496
|
+
currency: string;
|
|
497
|
+
} | undefined;
|
|
498
|
+
description?: string | undefined;
|
|
499
|
+
badges?: string[] | undefined;
|
|
500
|
+
promo?: {
|
|
501
|
+
message: string;
|
|
502
|
+
ends_at?: string | undefined;
|
|
503
|
+
} | undefined;
|
|
504
|
+
size_guide?: {
|
|
505
|
+
title: string;
|
|
506
|
+
columns?: string[] | undefined;
|
|
507
|
+
rows?: string[][] | undefined;
|
|
508
|
+
notes?: string[] | undefined;
|
|
509
|
+
} | undefined;
|
|
510
|
+
}>, "many">;
|
|
511
|
+
}, "strip", z.ZodTypeAny, {
|
|
512
|
+
title: string;
|
|
513
|
+
kind: "rail";
|
|
514
|
+
products: {
|
|
515
|
+
sku: string;
|
|
516
|
+
title: string;
|
|
517
|
+
category: string;
|
|
518
|
+
price: {
|
|
519
|
+
amount: number;
|
|
520
|
+
currency: string;
|
|
521
|
+
};
|
|
522
|
+
media: string[];
|
|
523
|
+
variants: {
|
|
524
|
+
sku: string;
|
|
525
|
+
attributes: Record<string, string>;
|
|
526
|
+
in_stock: boolean;
|
|
527
|
+
image?: string | undefined;
|
|
528
|
+
available?: number | undefined;
|
|
529
|
+
}[];
|
|
530
|
+
compare_at?: {
|
|
531
|
+
amount: number;
|
|
532
|
+
currency: string;
|
|
533
|
+
} | undefined;
|
|
534
|
+
description?: string | undefined;
|
|
535
|
+
badges?: string[] | undefined;
|
|
536
|
+
promo?: {
|
|
537
|
+
message: string;
|
|
538
|
+
ends_at?: string | undefined;
|
|
539
|
+
} | undefined;
|
|
540
|
+
size_guide?: {
|
|
541
|
+
title: string;
|
|
542
|
+
columns?: string[] | undefined;
|
|
543
|
+
rows?: string[][] | undefined;
|
|
544
|
+
notes?: string[] | undefined;
|
|
545
|
+
} | undefined;
|
|
546
|
+
}[];
|
|
547
|
+
}, {
|
|
548
|
+
title: string;
|
|
549
|
+
kind: "rail";
|
|
550
|
+
products: {
|
|
551
|
+
sku: string;
|
|
552
|
+
title: string;
|
|
553
|
+
category: string;
|
|
554
|
+
price: {
|
|
555
|
+
amount: number;
|
|
556
|
+
currency: string;
|
|
557
|
+
};
|
|
558
|
+
media: string[];
|
|
559
|
+
variants: {
|
|
560
|
+
sku: string;
|
|
561
|
+
attributes: Record<string, string>;
|
|
562
|
+
in_stock: boolean;
|
|
563
|
+
image?: string | undefined;
|
|
564
|
+
available?: number | undefined;
|
|
565
|
+
}[];
|
|
566
|
+
compare_at?: {
|
|
567
|
+
amount: number;
|
|
568
|
+
currency: string;
|
|
569
|
+
} | undefined;
|
|
570
|
+
description?: string | undefined;
|
|
571
|
+
badges?: string[] | undefined;
|
|
572
|
+
promo?: {
|
|
573
|
+
message: string;
|
|
574
|
+
ends_at?: string | undefined;
|
|
575
|
+
} | undefined;
|
|
576
|
+
size_guide?: {
|
|
577
|
+
title: string;
|
|
578
|
+
columns?: string[] | undefined;
|
|
579
|
+
rows?: string[][] | undefined;
|
|
580
|
+
notes?: string[] | undefined;
|
|
581
|
+
} | undefined;
|
|
582
|
+
}[];
|
|
583
|
+
}>, z.ZodObject<{
|
|
584
|
+
kind: z.ZodLiteral<"lifestyle_text">;
|
|
585
|
+
text: z.ZodString;
|
|
586
|
+
align: z.ZodDefault<z.ZodEnum<["center", "left"]>>;
|
|
587
|
+
}, "strip", z.ZodTypeAny, {
|
|
588
|
+
kind: "lifestyle_text";
|
|
589
|
+
text: string;
|
|
590
|
+
align: "center" | "left";
|
|
591
|
+
}, {
|
|
592
|
+
kind: "lifestyle_text";
|
|
593
|
+
text: string;
|
|
594
|
+
align?: "center" | "left" | undefined;
|
|
595
|
+
}>, z.ZodObject<{
|
|
596
|
+
kind: z.ZodLiteral<"featured">;
|
|
597
|
+
product: z.ZodObject<{
|
|
598
|
+
sku: z.ZodString;
|
|
599
|
+
title: z.ZodString;
|
|
600
|
+
category: z.ZodString;
|
|
601
|
+
price: z.ZodObject<{
|
|
602
|
+
amount: z.ZodNumber;
|
|
603
|
+
currency: z.ZodString;
|
|
604
|
+
}, "strict", z.ZodTypeAny, {
|
|
605
|
+
amount: number;
|
|
606
|
+
currency: string;
|
|
607
|
+
}, {
|
|
608
|
+
amount: number;
|
|
609
|
+
currency: string;
|
|
610
|
+
}>;
|
|
611
|
+
compare_at: z.ZodOptional<z.ZodObject<{
|
|
612
|
+
amount: z.ZodNumber;
|
|
613
|
+
currency: z.ZodString;
|
|
614
|
+
}, "strict", z.ZodTypeAny, {
|
|
615
|
+
amount: number;
|
|
616
|
+
currency: string;
|
|
617
|
+
}, {
|
|
618
|
+
amount: number;
|
|
619
|
+
currency: string;
|
|
620
|
+
}>>;
|
|
621
|
+
media: z.ZodArray<z.ZodString, "many">;
|
|
622
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
623
|
+
sku: z.ZodString;
|
|
624
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
625
|
+
in_stock: z.ZodBoolean;
|
|
626
|
+
image: z.ZodOptional<z.ZodString>;
|
|
627
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
628
|
+
}, "strip", z.ZodTypeAny, {
|
|
629
|
+
sku: string;
|
|
630
|
+
attributes: Record<string, string>;
|
|
631
|
+
in_stock: boolean;
|
|
632
|
+
image?: string | undefined;
|
|
633
|
+
available?: number | undefined;
|
|
634
|
+
}, {
|
|
635
|
+
sku: string;
|
|
636
|
+
attributes: Record<string, string>;
|
|
637
|
+
in_stock: boolean;
|
|
638
|
+
image?: string | undefined;
|
|
639
|
+
available?: number | undefined;
|
|
640
|
+
}>, "many">;
|
|
641
|
+
description: z.ZodOptional<z.ZodString>;
|
|
642
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
643
|
+
promo: z.ZodOptional<z.ZodObject<{
|
|
644
|
+
message: z.ZodString;
|
|
645
|
+
ends_at: z.ZodOptional<z.ZodString>;
|
|
646
|
+
}, "strip", z.ZodTypeAny, {
|
|
647
|
+
message: string;
|
|
648
|
+
ends_at?: string | undefined;
|
|
649
|
+
}, {
|
|
650
|
+
message: string;
|
|
651
|
+
ends_at?: string | undefined;
|
|
652
|
+
}>>;
|
|
653
|
+
size_guide: z.ZodOptional<z.ZodObject<{
|
|
654
|
+
title: z.ZodString;
|
|
655
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
656
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>;
|
|
657
|
+
notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
658
|
+
}, "strip", z.ZodTypeAny, {
|
|
659
|
+
title: string;
|
|
660
|
+
columns?: string[] | undefined;
|
|
661
|
+
rows?: string[][] | undefined;
|
|
662
|
+
notes?: string[] | undefined;
|
|
663
|
+
}, {
|
|
664
|
+
title: string;
|
|
665
|
+
columns?: string[] | undefined;
|
|
666
|
+
rows?: string[][] | undefined;
|
|
667
|
+
notes?: string[] | undefined;
|
|
668
|
+
}>>;
|
|
669
|
+
}, "strip", z.ZodTypeAny, {
|
|
670
|
+
sku: string;
|
|
671
|
+
title: string;
|
|
672
|
+
category: string;
|
|
673
|
+
price: {
|
|
674
|
+
amount: number;
|
|
675
|
+
currency: string;
|
|
676
|
+
};
|
|
677
|
+
media: string[];
|
|
678
|
+
variants: {
|
|
679
|
+
sku: string;
|
|
680
|
+
attributes: Record<string, string>;
|
|
681
|
+
in_stock: boolean;
|
|
682
|
+
image?: string | undefined;
|
|
683
|
+
available?: number | undefined;
|
|
684
|
+
}[];
|
|
685
|
+
compare_at?: {
|
|
686
|
+
amount: number;
|
|
687
|
+
currency: string;
|
|
688
|
+
} | undefined;
|
|
689
|
+
description?: string | undefined;
|
|
690
|
+
badges?: string[] | undefined;
|
|
691
|
+
promo?: {
|
|
692
|
+
message: string;
|
|
693
|
+
ends_at?: string | undefined;
|
|
694
|
+
} | undefined;
|
|
695
|
+
size_guide?: {
|
|
696
|
+
title: string;
|
|
697
|
+
columns?: string[] | undefined;
|
|
698
|
+
rows?: string[][] | undefined;
|
|
699
|
+
notes?: string[] | undefined;
|
|
700
|
+
} | undefined;
|
|
701
|
+
}, {
|
|
702
|
+
sku: string;
|
|
703
|
+
title: string;
|
|
704
|
+
category: string;
|
|
705
|
+
price: {
|
|
706
|
+
amount: number;
|
|
707
|
+
currency: string;
|
|
708
|
+
};
|
|
709
|
+
media: string[];
|
|
710
|
+
variants: {
|
|
711
|
+
sku: string;
|
|
712
|
+
attributes: Record<string, string>;
|
|
713
|
+
in_stock: boolean;
|
|
714
|
+
image?: string | undefined;
|
|
715
|
+
available?: number | undefined;
|
|
716
|
+
}[];
|
|
717
|
+
compare_at?: {
|
|
718
|
+
amount: number;
|
|
719
|
+
currency: string;
|
|
720
|
+
} | undefined;
|
|
721
|
+
description?: string | undefined;
|
|
722
|
+
badges?: string[] | undefined;
|
|
723
|
+
promo?: {
|
|
724
|
+
message: string;
|
|
725
|
+
ends_at?: string | undefined;
|
|
726
|
+
} | undefined;
|
|
727
|
+
size_guide?: {
|
|
728
|
+
title: string;
|
|
729
|
+
columns?: string[] | undefined;
|
|
730
|
+
rows?: string[][] | undefined;
|
|
731
|
+
notes?: string[] | undefined;
|
|
732
|
+
} | undefined;
|
|
733
|
+
}>;
|
|
734
|
+
}, "strip", z.ZodTypeAny, {
|
|
735
|
+
kind: "featured";
|
|
736
|
+
product: {
|
|
737
|
+
sku: string;
|
|
738
|
+
title: string;
|
|
739
|
+
category: string;
|
|
740
|
+
price: {
|
|
741
|
+
amount: number;
|
|
742
|
+
currency: string;
|
|
743
|
+
};
|
|
744
|
+
media: string[];
|
|
745
|
+
variants: {
|
|
746
|
+
sku: string;
|
|
747
|
+
attributes: Record<string, string>;
|
|
748
|
+
in_stock: boolean;
|
|
749
|
+
image?: string | undefined;
|
|
750
|
+
available?: number | undefined;
|
|
751
|
+
}[];
|
|
752
|
+
compare_at?: {
|
|
753
|
+
amount: number;
|
|
754
|
+
currency: string;
|
|
755
|
+
} | undefined;
|
|
756
|
+
description?: string | undefined;
|
|
757
|
+
badges?: string[] | undefined;
|
|
758
|
+
promo?: {
|
|
759
|
+
message: string;
|
|
760
|
+
ends_at?: string | undefined;
|
|
761
|
+
} | undefined;
|
|
762
|
+
size_guide?: {
|
|
763
|
+
title: string;
|
|
764
|
+
columns?: string[] | undefined;
|
|
765
|
+
rows?: string[][] | undefined;
|
|
766
|
+
notes?: string[] | undefined;
|
|
767
|
+
} | undefined;
|
|
768
|
+
};
|
|
769
|
+
}, {
|
|
770
|
+
kind: "featured";
|
|
771
|
+
product: {
|
|
772
|
+
sku: string;
|
|
773
|
+
title: string;
|
|
774
|
+
category: string;
|
|
775
|
+
price: {
|
|
776
|
+
amount: number;
|
|
777
|
+
currency: string;
|
|
778
|
+
};
|
|
779
|
+
media: string[];
|
|
780
|
+
variants: {
|
|
781
|
+
sku: string;
|
|
782
|
+
attributes: Record<string, string>;
|
|
783
|
+
in_stock: boolean;
|
|
784
|
+
image?: string | undefined;
|
|
785
|
+
available?: number | undefined;
|
|
786
|
+
}[];
|
|
787
|
+
compare_at?: {
|
|
788
|
+
amount: number;
|
|
789
|
+
currency: string;
|
|
790
|
+
} | undefined;
|
|
791
|
+
description?: string | undefined;
|
|
792
|
+
badges?: string[] | undefined;
|
|
793
|
+
promo?: {
|
|
794
|
+
message: string;
|
|
795
|
+
ends_at?: string | undefined;
|
|
796
|
+
} | undefined;
|
|
797
|
+
size_guide?: {
|
|
798
|
+
title: string;
|
|
799
|
+
columns?: string[] | undefined;
|
|
800
|
+
rows?: string[][] | undefined;
|
|
801
|
+
notes?: string[] | undefined;
|
|
802
|
+
} | undefined;
|
|
803
|
+
};
|
|
804
|
+
}>, z.ZodObject<{
|
|
805
|
+
kind: z.ZodLiteral<"lifestyle_image">;
|
|
806
|
+
images: z.ZodArray<z.ZodString, "many">;
|
|
807
|
+
}, "strip", z.ZodTypeAny, {
|
|
808
|
+
kind: "lifestyle_image";
|
|
809
|
+
images: string[];
|
|
810
|
+
}, {
|
|
811
|
+
kind: "lifestyle_image";
|
|
812
|
+
images: string[];
|
|
813
|
+
}>, z.ZodObject<{
|
|
814
|
+
kind: z.ZodLiteral<"grid">;
|
|
815
|
+
title: z.ZodString;
|
|
816
|
+
products: z.ZodArray<z.ZodObject<{
|
|
817
|
+
sku: z.ZodString;
|
|
818
|
+
title: z.ZodString;
|
|
819
|
+
category: z.ZodString;
|
|
820
|
+
price: z.ZodObject<{
|
|
821
|
+
amount: z.ZodNumber;
|
|
822
|
+
currency: z.ZodString;
|
|
823
|
+
}, "strict", z.ZodTypeAny, {
|
|
824
|
+
amount: number;
|
|
825
|
+
currency: string;
|
|
826
|
+
}, {
|
|
827
|
+
amount: number;
|
|
828
|
+
currency: string;
|
|
829
|
+
}>;
|
|
830
|
+
compare_at: z.ZodOptional<z.ZodObject<{
|
|
831
|
+
amount: z.ZodNumber;
|
|
832
|
+
currency: z.ZodString;
|
|
833
|
+
}, "strict", z.ZodTypeAny, {
|
|
834
|
+
amount: number;
|
|
835
|
+
currency: string;
|
|
836
|
+
}, {
|
|
837
|
+
amount: number;
|
|
838
|
+
currency: string;
|
|
839
|
+
}>>;
|
|
840
|
+
media: z.ZodArray<z.ZodString, "many">;
|
|
841
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
842
|
+
sku: z.ZodString;
|
|
843
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
844
|
+
in_stock: z.ZodBoolean;
|
|
845
|
+
image: z.ZodOptional<z.ZodString>;
|
|
846
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
847
|
+
}, "strip", z.ZodTypeAny, {
|
|
848
|
+
sku: string;
|
|
849
|
+
attributes: Record<string, string>;
|
|
850
|
+
in_stock: boolean;
|
|
851
|
+
image?: string | undefined;
|
|
852
|
+
available?: number | undefined;
|
|
853
|
+
}, {
|
|
854
|
+
sku: string;
|
|
855
|
+
attributes: Record<string, string>;
|
|
856
|
+
in_stock: boolean;
|
|
857
|
+
image?: string | undefined;
|
|
858
|
+
available?: number | undefined;
|
|
859
|
+
}>, "many">;
|
|
860
|
+
description: z.ZodOptional<z.ZodString>;
|
|
861
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
862
|
+
promo: z.ZodOptional<z.ZodObject<{
|
|
863
|
+
message: z.ZodString;
|
|
864
|
+
ends_at: z.ZodOptional<z.ZodString>;
|
|
865
|
+
}, "strip", z.ZodTypeAny, {
|
|
866
|
+
message: string;
|
|
867
|
+
ends_at?: string | undefined;
|
|
868
|
+
}, {
|
|
869
|
+
message: string;
|
|
870
|
+
ends_at?: string | undefined;
|
|
871
|
+
}>>;
|
|
872
|
+
size_guide: z.ZodOptional<z.ZodObject<{
|
|
873
|
+
title: z.ZodString;
|
|
874
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
875
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>;
|
|
876
|
+
notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
877
|
+
}, "strip", z.ZodTypeAny, {
|
|
878
|
+
title: string;
|
|
879
|
+
columns?: string[] | undefined;
|
|
880
|
+
rows?: string[][] | undefined;
|
|
881
|
+
notes?: string[] | undefined;
|
|
882
|
+
}, {
|
|
883
|
+
title: string;
|
|
884
|
+
columns?: string[] | undefined;
|
|
885
|
+
rows?: string[][] | undefined;
|
|
886
|
+
notes?: string[] | undefined;
|
|
887
|
+
}>>;
|
|
888
|
+
}, "strip", z.ZodTypeAny, {
|
|
889
|
+
sku: string;
|
|
890
|
+
title: string;
|
|
891
|
+
category: string;
|
|
892
|
+
price: {
|
|
893
|
+
amount: number;
|
|
894
|
+
currency: string;
|
|
895
|
+
};
|
|
896
|
+
media: string[];
|
|
897
|
+
variants: {
|
|
898
|
+
sku: string;
|
|
899
|
+
attributes: Record<string, string>;
|
|
900
|
+
in_stock: boolean;
|
|
901
|
+
image?: string | undefined;
|
|
902
|
+
available?: number | undefined;
|
|
903
|
+
}[];
|
|
904
|
+
compare_at?: {
|
|
905
|
+
amount: number;
|
|
906
|
+
currency: string;
|
|
907
|
+
} | undefined;
|
|
908
|
+
description?: string | undefined;
|
|
909
|
+
badges?: string[] | undefined;
|
|
910
|
+
promo?: {
|
|
911
|
+
message: string;
|
|
912
|
+
ends_at?: string | undefined;
|
|
913
|
+
} | undefined;
|
|
914
|
+
size_guide?: {
|
|
915
|
+
title: string;
|
|
916
|
+
columns?: string[] | undefined;
|
|
917
|
+
rows?: string[][] | undefined;
|
|
918
|
+
notes?: string[] | undefined;
|
|
919
|
+
} | undefined;
|
|
920
|
+
}, {
|
|
921
|
+
sku: string;
|
|
922
|
+
title: string;
|
|
923
|
+
category: string;
|
|
924
|
+
price: {
|
|
925
|
+
amount: number;
|
|
926
|
+
currency: string;
|
|
927
|
+
};
|
|
928
|
+
media: string[];
|
|
929
|
+
variants: {
|
|
930
|
+
sku: string;
|
|
931
|
+
attributes: Record<string, string>;
|
|
932
|
+
in_stock: boolean;
|
|
933
|
+
image?: string | undefined;
|
|
934
|
+
available?: number | undefined;
|
|
935
|
+
}[];
|
|
936
|
+
compare_at?: {
|
|
937
|
+
amount: number;
|
|
938
|
+
currency: string;
|
|
939
|
+
} | undefined;
|
|
940
|
+
description?: string | undefined;
|
|
941
|
+
badges?: string[] | undefined;
|
|
942
|
+
promo?: {
|
|
943
|
+
message: string;
|
|
944
|
+
ends_at?: string | undefined;
|
|
945
|
+
} | undefined;
|
|
946
|
+
size_guide?: {
|
|
947
|
+
title: string;
|
|
948
|
+
columns?: string[] | undefined;
|
|
949
|
+
rows?: string[][] | undefined;
|
|
950
|
+
notes?: string[] | undefined;
|
|
951
|
+
} | undefined;
|
|
952
|
+
}>, "many">;
|
|
953
|
+
}, "strip", z.ZodTypeAny, {
|
|
954
|
+
title: string;
|
|
955
|
+
kind: "grid";
|
|
956
|
+
products: {
|
|
957
|
+
sku: string;
|
|
958
|
+
title: string;
|
|
959
|
+
category: string;
|
|
960
|
+
price: {
|
|
961
|
+
amount: number;
|
|
962
|
+
currency: string;
|
|
963
|
+
};
|
|
964
|
+
media: string[];
|
|
965
|
+
variants: {
|
|
966
|
+
sku: string;
|
|
967
|
+
attributes: Record<string, string>;
|
|
968
|
+
in_stock: boolean;
|
|
969
|
+
image?: string | undefined;
|
|
970
|
+
available?: number | undefined;
|
|
971
|
+
}[];
|
|
972
|
+
compare_at?: {
|
|
973
|
+
amount: number;
|
|
974
|
+
currency: string;
|
|
975
|
+
} | undefined;
|
|
976
|
+
description?: string | undefined;
|
|
977
|
+
badges?: string[] | undefined;
|
|
978
|
+
promo?: {
|
|
979
|
+
message: string;
|
|
980
|
+
ends_at?: string | undefined;
|
|
981
|
+
} | undefined;
|
|
982
|
+
size_guide?: {
|
|
983
|
+
title: string;
|
|
984
|
+
columns?: string[] | undefined;
|
|
985
|
+
rows?: string[][] | undefined;
|
|
986
|
+
notes?: string[] | undefined;
|
|
987
|
+
} | undefined;
|
|
988
|
+
}[];
|
|
989
|
+
}, {
|
|
990
|
+
title: string;
|
|
991
|
+
kind: "grid";
|
|
992
|
+
products: {
|
|
993
|
+
sku: string;
|
|
994
|
+
title: string;
|
|
995
|
+
category: string;
|
|
996
|
+
price: {
|
|
997
|
+
amount: number;
|
|
998
|
+
currency: string;
|
|
999
|
+
};
|
|
1000
|
+
media: string[];
|
|
1001
|
+
variants: {
|
|
1002
|
+
sku: string;
|
|
1003
|
+
attributes: Record<string, string>;
|
|
1004
|
+
in_stock: boolean;
|
|
1005
|
+
image?: string | undefined;
|
|
1006
|
+
available?: number | undefined;
|
|
1007
|
+
}[];
|
|
1008
|
+
compare_at?: {
|
|
1009
|
+
amount: number;
|
|
1010
|
+
currency: string;
|
|
1011
|
+
} | undefined;
|
|
1012
|
+
description?: string | undefined;
|
|
1013
|
+
badges?: string[] | undefined;
|
|
1014
|
+
promo?: {
|
|
1015
|
+
message: string;
|
|
1016
|
+
ends_at?: string | undefined;
|
|
1017
|
+
} | undefined;
|
|
1018
|
+
size_guide?: {
|
|
1019
|
+
title: string;
|
|
1020
|
+
columns?: string[] | undefined;
|
|
1021
|
+
rows?: string[][] | undefined;
|
|
1022
|
+
notes?: string[] | undefined;
|
|
1023
|
+
} | undefined;
|
|
1024
|
+
}[];
|
|
1025
|
+
}>, z.ZodObject<{
|
|
1026
|
+
kind: z.ZodLiteral<"review">;
|
|
1027
|
+
quotes: z.ZodArray<z.ZodObject<{
|
|
1028
|
+
quote: z.ZodString;
|
|
1029
|
+
author: z.ZodString;
|
|
1030
|
+
}, "strip", z.ZodTypeAny, {
|
|
1031
|
+
quote: string;
|
|
1032
|
+
author: string;
|
|
1033
|
+
}, {
|
|
1034
|
+
quote: string;
|
|
1035
|
+
author: string;
|
|
1036
|
+
}>, "many">;
|
|
1037
|
+
}, "strip", z.ZodTypeAny, {
|
|
1038
|
+
kind: "review";
|
|
1039
|
+
quotes: {
|
|
1040
|
+
quote: string;
|
|
1041
|
+
author: string;
|
|
1042
|
+
}[];
|
|
1043
|
+
}, {
|
|
1044
|
+
kind: "review";
|
|
1045
|
+
quotes: {
|
|
1046
|
+
quote: string;
|
|
1047
|
+
author: string;
|
|
1048
|
+
}[];
|
|
1049
|
+
}>, z.ZodObject<{
|
|
1050
|
+
kind: z.ZodLiteral<"why_us">;
|
|
1051
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1052
|
+
icon: z.ZodOptional<z.ZodEnum<["payment", "delivery", "returns"]>>;
|
|
1053
|
+
title: z.ZodString;
|
|
1054
|
+
body: z.ZodString;
|
|
1055
|
+
}, "strip", z.ZodTypeAny, {
|
|
1056
|
+
title: string;
|
|
1057
|
+
body: string;
|
|
1058
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1059
|
+
}, {
|
|
1060
|
+
title: string;
|
|
1061
|
+
body: string;
|
|
1062
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1063
|
+
}>, "many">;
|
|
1064
|
+
}, "strip", z.ZodTypeAny, {
|
|
1065
|
+
items: {
|
|
1066
|
+
title: string;
|
|
1067
|
+
body: string;
|
|
1068
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1069
|
+
}[];
|
|
1070
|
+
kind: "why_us";
|
|
1071
|
+
}, {
|
|
1072
|
+
items: {
|
|
1073
|
+
title: string;
|
|
1074
|
+
body: string;
|
|
1075
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1076
|
+
}[];
|
|
1077
|
+
kind: "why_us";
|
|
1078
|
+
}>, z.ZodObject<{
|
|
1079
|
+
kind: z.ZodLiteral<"explore">;
|
|
1080
|
+
title: z.ZodString;
|
|
1081
|
+
links: z.ZodArray<z.ZodObject<{
|
|
1082
|
+
label: z.ZodString;
|
|
1083
|
+
code: z.ZodString;
|
|
1084
|
+
}, "strip", z.ZodTypeAny, {
|
|
1085
|
+
code: string;
|
|
1086
|
+
label: string;
|
|
1087
|
+
}, {
|
|
1088
|
+
code: string;
|
|
1089
|
+
label: string;
|
|
1090
|
+
}>, "many">;
|
|
1091
|
+
}, "strip", z.ZodTypeAny, {
|
|
1092
|
+
title: string;
|
|
1093
|
+
kind: "explore";
|
|
1094
|
+
links: {
|
|
1095
|
+
code: string;
|
|
1096
|
+
label: string;
|
|
1097
|
+
}[];
|
|
1098
|
+
}, {
|
|
1099
|
+
title: string;
|
|
1100
|
+
kind: "explore";
|
|
1101
|
+
links: {
|
|
1102
|
+
code: string;
|
|
1103
|
+
label: string;
|
|
1104
|
+
}[];
|
|
1105
|
+
}>]>, "many">;
|
|
1106
|
+
}, "strip", z.ZodTypeAny, {
|
|
1107
|
+
code: string;
|
|
1108
|
+
hero: {
|
|
1109
|
+
title: string;
|
|
1110
|
+
image?: string | undefined;
|
|
1111
|
+
subtitle?: string | undefined;
|
|
1112
|
+
};
|
|
1113
|
+
blocks: ({
|
|
1114
|
+
title: string;
|
|
1115
|
+
kind: "rail";
|
|
1116
|
+
products: {
|
|
1117
|
+
sku: string;
|
|
1118
|
+
title: string;
|
|
1119
|
+
category: string;
|
|
1120
|
+
price: {
|
|
1121
|
+
amount: number;
|
|
1122
|
+
currency: string;
|
|
1123
|
+
};
|
|
1124
|
+
media: string[];
|
|
1125
|
+
variants: {
|
|
1126
|
+
sku: string;
|
|
1127
|
+
attributes: Record<string, string>;
|
|
1128
|
+
in_stock: boolean;
|
|
1129
|
+
image?: string | undefined;
|
|
1130
|
+
available?: number | undefined;
|
|
1131
|
+
}[];
|
|
1132
|
+
compare_at?: {
|
|
1133
|
+
amount: number;
|
|
1134
|
+
currency: string;
|
|
1135
|
+
} | undefined;
|
|
1136
|
+
description?: string | undefined;
|
|
1137
|
+
badges?: string[] | undefined;
|
|
1138
|
+
promo?: {
|
|
1139
|
+
message: string;
|
|
1140
|
+
ends_at?: string | undefined;
|
|
1141
|
+
} | undefined;
|
|
1142
|
+
size_guide?: {
|
|
1143
|
+
title: string;
|
|
1144
|
+
columns?: string[] | undefined;
|
|
1145
|
+
rows?: string[][] | undefined;
|
|
1146
|
+
notes?: string[] | undefined;
|
|
1147
|
+
} | undefined;
|
|
1148
|
+
}[];
|
|
1149
|
+
} | {
|
|
1150
|
+
kind: "lifestyle_text";
|
|
1151
|
+
text: string;
|
|
1152
|
+
align: "center" | "left";
|
|
1153
|
+
} | {
|
|
1154
|
+
kind: "featured";
|
|
1155
|
+
product: {
|
|
1156
|
+
sku: string;
|
|
1157
|
+
title: string;
|
|
1158
|
+
category: string;
|
|
1159
|
+
price: {
|
|
1160
|
+
amount: number;
|
|
1161
|
+
currency: string;
|
|
1162
|
+
};
|
|
1163
|
+
media: string[];
|
|
1164
|
+
variants: {
|
|
1165
|
+
sku: string;
|
|
1166
|
+
attributes: Record<string, string>;
|
|
1167
|
+
in_stock: boolean;
|
|
1168
|
+
image?: string | undefined;
|
|
1169
|
+
available?: number | undefined;
|
|
1170
|
+
}[];
|
|
1171
|
+
compare_at?: {
|
|
1172
|
+
amount: number;
|
|
1173
|
+
currency: string;
|
|
1174
|
+
} | undefined;
|
|
1175
|
+
description?: string | undefined;
|
|
1176
|
+
badges?: string[] | undefined;
|
|
1177
|
+
promo?: {
|
|
1178
|
+
message: string;
|
|
1179
|
+
ends_at?: string | undefined;
|
|
1180
|
+
} | undefined;
|
|
1181
|
+
size_guide?: {
|
|
1182
|
+
title: string;
|
|
1183
|
+
columns?: string[] | undefined;
|
|
1184
|
+
rows?: string[][] | undefined;
|
|
1185
|
+
notes?: string[] | undefined;
|
|
1186
|
+
} | undefined;
|
|
1187
|
+
};
|
|
1188
|
+
} | {
|
|
1189
|
+
kind: "lifestyle_image";
|
|
1190
|
+
images: string[];
|
|
1191
|
+
} | {
|
|
1192
|
+
title: string;
|
|
1193
|
+
kind: "grid";
|
|
1194
|
+
products: {
|
|
1195
|
+
sku: string;
|
|
1196
|
+
title: string;
|
|
1197
|
+
category: string;
|
|
1198
|
+
price: {
|
|
1199
|
+
amount: number;
|
|
1200
|
+
currency: string;
|
|
1201
|
+
};
|
|
1202
|
+
media: string[];
|
|
1203
|
+
variants: {
|
|
1204
|
+
sku: string;
|
|
1205
|
+
attributes: Record<string, string>;
|
|
1206
|
+
in_stock: boolean;
|
|
1207
|
+
image?: string | undefined;
|
|
1208
|
+
available?: number | undefined;
|
|
1209
|
+
}[];
|
|
1210
|
+
compare_at?: {
|
|
1211
|
+
amount: number;
|
|
1212
|
+
currency: string;
|
|
1213
|
+
} | undefined;
|
|
1214
|
+
description?: string | undefined;
|
|
1215
|
+
badges?: string[] | undefined;
|
|
1216
|
+
promo?: {
|
|
1217
|
+
message: string;
|
|
1218
|
+
ends_at?: string | undefined;
|
|
1219
|
+
} | undefined;
|
|
1220
|
+
size_guide?: {
|
|
1221
|
+
title: string;
|
|
1222
|
+
columns?: string[] | undefined;
|
|
1223
|
+
rows?: string[][] | undefined;
|
|
1224
|
+
notes?: string[] | undefined;
|
|
1225
|
+
} | undefined;
|
|
1226
|
+
}[];
|
|
1227
|
+
} | {
|
|
1228
|
+
kind: "review";
|
|
1229
|
+
quotes: {
|
|
1230
|
+
quote: string;
|
|
1231
|
+
author: string;
|
|
1232
|
+
}[];
|
|
1233
|
+
} | {
|
|
1234
|
+
items: {
|
|
1235
|
+
title: string;
|
|
1236
|
+
body: string;
|
|
1237
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1238
|
+
}[];
|
|
1239
|
+
kind: "why_us";
|
|
1240
|
+
} | {
|
|
1241
|
+
title: string;
|
|
1242
|
+
kind: "explore";
|
|
1243
|
+
links: {
|
|
1244
|
+
code: string;
|
|
1245
|
+
label: string;
|
|
1246
|
+
}[];
|
|
1247
|
+
})[];
|
|
1248
|
+
}, {
|
|
1249
|
+
code: string;
|
|
1250
|
+
hero: {
|
|
1251
|
+
title: string;
|
|
1252
|
+
image?: string | undefined;
|
|
1253
|
+
subtitle?: string | undefined;
|
|
1254
|
+
};
|
|
1255
|
+
blocks: ({
|
|
1256
|
+
title: string;
|
|
1257
|
+
kind: "rail";
|
|
1258
|
+
products: {
|
|
1259
|
+
sku: string;
|
|
1260
|
+
title: string;
|
|
1261
|
+
category: string;
|
|
1262
|
+
price: {
|
|
1263
|
+
amount: number;
|
|
1264
|
+
currency: string;
|
|
1265
|
+
};
|
|
1266
|
+
media: string[];
|
|
1267
|
+
variants: {
|
|
1268
|
+
sku: string;
|
|
1269
|
+
attributes: Record<string, string>;
|
|
1270
|
+
in_stock: boolean;
|
|
1271
|
+
image?: string | undefined;
|
|
1272
|
+
available?: number | undefined;
|
|
1273
|
+
}[];
|
|
1274
|
+
compare_at?: {
|
|
1275
|
+
amount: number;
|
|
1276
|
+
currency: string;
|
|
1277
|
+
} | undefined;
|
|
1278
|
+
description?: string | undefined;
|
|
1279
|
+
badges?: string[] | undefined;
|
|
1280
|
+
promo?: {
|
|
1281
|
+
message: string;
|
|
1282
|
+
ends_at?: string | undefined;
|
|
1283
|
+
} | undefined;
|
|
1284
|
+
size_guide?: {
|
|
1285
|
+
title: string;
|
|
1286
|
+
columns?: string[] | undefined;
|
|
1287
|
+
rows?: string[][] | undefined;
|
|
1288
|
+
notes?: string[] | undefined;
|
|
1289
|
+
} | undefined;
|
|
1290
|
+
}[];
|
|
1291
|
+
} | {
|
|
1292
|
+
kind: "lifestyle_text";
|
|
1293
|
+
text: string;
|
|
1294
|
+
align?: "center" | "left" | undefined;
|
|
1295
|
+
} | {
|
|
1296
|
+
kind: "featured";
|
|
1297
|
+
product: {
|
|
1298
|
+
sku: string;
|
|
1299
|
+
title: string;
|
|
1300
|
+
category: string;
|
|
1301
|
+
price: {
|
|
1302
|
+
amount: number;
|
|
1303
|
+
currency: string;
|
|
1304
|
+
};
|
|
1305
|
+
media: string[];
|
|
1306
|
+
variants: {
|
|
1307
|
+
sku: string;
|
|
1308
|
+
attributes: Record<string, string>;
|
|
1309
|
+
in_stock: boolean;
|
|
1310
|
+
image?: string | undefined;
|
|
1311
|
+
available?: number | undefined;
|
|
1312
|
+
}[];
|
|
1313
|
+
compare_at?: {
|
|
1314
|
+
amount: number;
|
|
1315
|
+
currency: string;
|
|
1316
|
+
} | undefined;
|
|
1317
|
+
description?: string | undefined;
|
|
1318
|
+
badges?: string[] | undefined;
|
|
1319
|
+
promo?: {
|
|
1320
|
+
message: string;
|
|
1321
|
+
ends_at?: string | undefined;
|
|
1322
|
+
} | undefined;
|
|
1323
|
+
size_guide?: {
|
|
1324
|
+
title: string;
|
|
1325
|
+
columns?: string[] | undefined;
|
|
1326
|
+
rows?: string[][] | undefined;
|
|
1327
|
+
notes?: string[] | undefined;
|
|
1328
|
+
} | undefined;
|
|
1329
|
+
};
|
|
1330
|
+
} | {
|
|
1331
|
+
kind: "lifestyle_image";
|
|
1332
|
+
images: string[];
|
|
1333
|
+
} | {
|
|
1334
|
+
title: string;
|
|
1335
|
+
kind: "grid";
|
|
1336
|
+
products: {
|
|
1337
|
+
sku: string;
|
|
1338
|
+
title: string;
|
|
1339
|
+
category: string;
|
|
1340
|
+
price: {
|
|
1341
|
+
amount: number;
|
|
1342
|
+
currency: string;
|
|
1343
|
+
};
|
|
1344
|
+
media: string[];
|
|
1345
|
+
variants: {
|
|
1346
|
+
sku: string;
|
|
1347
|
+
attributes: Record<string, string>;
|
|
1348
|
+
in_stock: boolean;
|
|
1349
|
+
image?: string | undefined;
|
|
1350
|
+
available?: number | undefined;
|
|
1351
|
+
}[];
|
|
1352
|
+
compare_at?: {
|
|
1353
|
+
amount: number;
|
|
1354
|
+
currency: string;
|
|
1355
|
+
} | undefined;
|
|
1356
|
+
description?: string | undefined;
|
|
1357
|
+
badges?: string[] | undefined;
|
|
1358
|
+
promo?: {
|
|
1359
|
+
message: string;
|
|
1360
|
+
ends_at?: string | undefined;
|
|
1361
|
+
} | undefined;
|
|
1362
|
+
size_guide?: {
|
|
1363
|
+
title: string;
|
|
1364
|
+
columns?: string[] | undefined;
|
|
1365
|
+
rows?: string[][] | undefined;
|
|
1366
|
+
notes?: string[] | undefined;
|
|
1367
|
+
} | undefined;
|
|
1368
|
+
}[];
|
|
1369
|
+
} | {
|
|
1370
|
+
kind: "review";
|
|
1371
|
+
quotes: {
|
|
1372
|
+
quote: string;
|
|
1373
|
+
author: string;
|
|
1374
|
+
}[];
|
|
1375
|
+
} | {
|
|
1376
|
+
items: {
|
|
1377
|
+
title: string;
|
|
1378
|
+
body: string;
|
|
1379
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1380
|
+
}[];
|
|
1381
|
+
kind: "why_us";
|
|
1382
|
+
} | {
|
|
1383
|
+
title: string;
|
|
1384
|
+
kind: "explore";
|
|
1385
|
+
links: {
|
|
1386
|
+
code: string;
|
|
1387
|
+
label: string;
|
|
1388
|
+
}[];
|
|
1389
|
+
})[];
|
|
1390
|
+
}>;
|
|
1391
|
+
|
|
1392
|
+
declare type CategoryPage = z.infer<typeof CategoryPage>;
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* A whole category screen, data fetching included — a **level 1 view**.
|
|
1396
|
+
*
|
|
1397
|
+
* This is the smallest integration surface a host can take: give it a code, it renders,
|
|
1398
|
+
* and it owns its own loading, empty and error states. The screen itself is
|
|
1399
|
+
* {@link CategoryContent}; everything it is built from (`RailCarousel`, `ProductTile`, …)
|
|
1400
|
+
* stays exported for a host that would rather compose and fetch it themselves.
|
|
1401
|
+
*
|
|
1402
|
+
* Fetching happens on mount, client-side, through the provider's client — so it inherits
|
|
1403
|
+
* the host's session and market without being told about either.
|
|
1404
|
+
*/
|
|
1405
|
+
export declare function CategoryView({ code, initialData, onNotFound }: CategoryViewProps): JSX.Element;
|
|
1406
|
+
|
|
1407
|
+
export declare interface CategoryViewProps {
|
|
1408
|
+
/** Category code — the leaf, e.g. `hoodies`. */
|
|
1409
|
+
code: string;
|
|
1410
|
+
/**
|
|
1411
|
+
* An already-fetched page, from a host that server-rendered this screen. Painted
|
|
1412
|
+
* synchronously on the first frame — no skeleton, no waterfall — and revalidated in the
|
|
1413
|
+
* background, because a server-rendered page can be arbitrarily old by the time it is
|
|
1414
|
+
* interactive and price and availability are the two things worst served by being stale.
|
|
1415
|
+
*/
|
|
1416
|
+
initialData?: CategoryPage;
|
|
1417
|
+
/**
|
|
1418
|
+
* Replaces the built-in empty state when the category does not exist, so a host can
|
|
1419
|
+
* render their own 404 in their own chrome. Only for `category_not_found`: a genuine
|
|
1420
|
+
* fault still renders the recoverable error state, because inviting a retry is right
|
|
1421
|
+
* there and wrong for a category that is gone.
|
|
1422
|
+
*/
|
|
1423
|
+
onNotFound?: () => ReactNode;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
declare interface ClientOptions {
|
|
1427
|
+
/** The `/v1` base URL, e.g. `https://storefront.example.com/v1`. */
|
|
1428
|
+
baseUrl: string;
|
|
1429
|
+
/** Override the fetch implementation (defaults to the global `fetch`). */
|
|
1430
|
+
fetch?: typeof fetch;
|
|
1431
|
+
/** Supplies the session bearer token, if not relying solely on the session cookie. */
|
|
1432
|
+
getToken?: () => string | undefined | Promise<string | undefined>;
|
|
1433
|
+
/** Extra headers applied to every request. */
|
|
1434
|
+
defaultHeaders?: Record<string, string>;
|
|
1435
|
+
/** Credentials mode (browser); defaults to `include` so the session cookie is sent. */
|
|
1436
|
+
credentials?: RequestCredentials;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/** Extract the API error `code` from a thrown value, if present. */
|
|
1440
|
+
export declare function codeOf(err: unknown): ErrorCode | undefined;
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* PDP colour selector (Figma `mobile/pdp/color`): a "Colour: <name>" header and a row of
|
|
1444
|
+
* 79×105 image tiles. The selected tile gets a dark border; sold-out colours are disabled
|
|
1445
|
+
* with an overlay. A real radiogroup for keyboard + screen-reader support.
|
|
1446
|
+
*/
|
|
1447
|
+
export declare function ColorSelector({ colours, selected, onSelect }: ColorSelectorProps): JSX.Element;
|
|
1448
|
+
|
|
1449
|
+
export declare interface ColorSelectorProps {
|
|
1450
|
+
colours: ColourOption[];
|
|
1451
|
+
/** Selected colour value, or null when none is chosen yet. */
|
|
1452
|
+
selected: string | null;
|
|
1453
|
+
onSelect: (value: string) => void;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/** The attribute key a product uses for colour, if any (`colour` or `color`). */
|
|
1457
|
+
export declare function colourKeyOf(variants: ProductVariant[]): string | null;
|
|
1458
|
+
|
|
1459
|
+
export declare interface ColourOption {
|
|
1460
|
+
value: string;
|
|
1461
|
+
available: boolean;
|
|
1462
|
+
image?: string;
|
|
1463
|
+
/** Remaining stock for this colour (summed across in-stock sizes); drives "N left". */
|
|
1464
|
+
remaining?: number;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Distinct colours with a representative image and remaining stock. `available` and
|
|
1469
|
+
* `remaining` are filtered to the given size when one is selected.
|
|
1470
|
+
*/
|
|
1471
|
+
export declare function colourOptions(variants: ProductVariant[], size?: string | null): ColourOption[];
|
|
1472
|
+
|
|
1473
|
+
/**
|
|
1474
|
+
* A typed, schema-validating client for the `/v1` API. Every response is parsed with
|
|
1475
|
+
* its contract schema; a non-2xx response throws a {@link DaznApiError}.
|
|
1476
|
+
*/
|
|
1477
|
+
declare function createClient(options: ClientOptions): {
|
|
1478
|
+
catalog: {
|
|
1479
|
+
listProducts: (query?: ListProductsQuery) => Promise<{
|
|
1480
|
+
data: {
|
|
1481
|
+
sku: string;
|
|
1482
|
+
title: string;
|
|
1483
|
+
category: string;
|
|
1484
|
+
price: {
|
|
1485
|
+
amount: number;
|
|
1486
|
+
currency: string;
|
|
1487
|
+
};
|
|
1488
|
+
media: string[];
|
|
1489
|
+
variants: {
|
|
1490
|
+
sku: string;
|
|
1491
|
+
attributes: Record<string, string>;
|
|
1492
|
+
in_stock: boolean;
|
|
1493
|
+
image?: string | undefined;
|
|
1494
|
+
available?: number | undefined;
|
|
1495
|
+
}[];
|
|
1496
|
+
compare_at?: {
|
|
1497
|
+
amount: number;
|
|
1498
|
+
currency: string;
|
|
1499
|
+
} | undefined;
|
|
1500
|
+
description?: string | undefined;
|
|
1501
|
+
badges?: string[] | undefined;
|
|
1502
|
+
promo?: {
|
|
1503
|
+
message: string;
|
|
1504
|
+
ends_at?: string | undefined;
|
|
1505
|
+
} | undefined;
|
|
1506
|
+
size_guide?: {
|
|
1507
|
+
title: string;
|
|
1508
|
+
columns?: string[] | undefined;
|
|
1509
|
+
rows?: string[][] | undefined;
|
|
1510
|
+
notes?: string[] | undefined;
|
|
1511
|
+
} | undefined;
|
|
1512
|
+
}[];
|
|
1513
|
+
page: {
|
|
1514
|
+
cursor: string | null;
|
|
1515
|
+
has_more: boolean;
|
|
1516
|
+
};
|
|
1517
|
+
}>;
|
|
1518
|
+
getProduct: (sku: string, query?: CatalogReadQuery) => Promise<{
|
|
1519
|
+
sku: string;
|
|
1520
|
+
title: string;
|
|
1521
|
+
category: string;
|
|
1522
|
+
price: {
|
|
1523
|
+
amount: number;
|
|
1524
|
+
currency: string;
|
|
1525
|
+
};
|
|
1526
|
+
media: string[];
|
|
1527
|
+
variants: {
|
|
1528
|
+
sku: string;
|
|
1529
|
+
attributes: Record<string, string>;
|
|
1530
|
+
in_stock: boolean;
|
|
1531
|
+
image?: string | undefined;
|
|
1532
|
+
available?: number | undefined;
|
|
1533
|
+
}[];
|
|
1534
|
+
compare_at?: {
|
|
1535
|
+
amount: number;
|
|
1536
|
+
currency: string;
|
|
1537
|
+
} | undefined;
|
|
1538
|
+
description?: string | undefined;
|
|
1539
|
+
badges?: string[] | undefined;
|
|
1540
|
+
promo?: {
|
|
1541
|
+
message: string;
|
|
1542
|
+
ends_at?: string | undefined;
|
|
1543
|
+
} | undefined;
|
|
1544
|
+
size_guide?: {
|
|
1545
|
+
title: string;
|
|
1546
|
+
columns?: string[] | undefined;
|
|
1547
|
+
rows?: string[][] | undefined;
|
|
1548
|
+
notes?: string[] | undefined;
|
|
1549
|
+
} | undefined;
|
|
1550
|
+
}>;
|
|
1551
|
+
getRecommendations: (sku: string, query?: CatalogReadQuery) => Promise<{
|
|
1552
|
+
data: {
|
|
1553
|
+
sku: string;
|
|
1554
|
+
title: string;
|
|
1555
|
+
category: string;
|
|
1556
|
+
price: {
|
|
1557
|
+
amount: number;
|
|
1558
|
+
currency: string;
|
|
1559
|
+
};
|
|
1560
|
+
media: string[];
|
|
1561
|
+
variants: {
|
|
1562
|
+
sku: string;
|
|
1563
|
+
attributes: Record<string, string>;
|
|
1564
|
+
in_stock: boolean;
|
|
1565
|
+
image?: string | undefined;
|
|
1566
|
+
available?: number | undefined;
|
|
1567
|
+
}[];
|
|
1568
|
+
compare_at?: {
|
|
1569
|
+
amount: number;
|
|
1570
|
+
currency: string;
|
|
1571
|
+
} | undefined;
|
|
1572
|
+
description?: string | undefined;
|
|
1573
|
+
badges?: string[] | undefined;
|
|
1574
|
+
promo?: {
|
|
1575
|
+
message: string;
|
|
1576
|
+
ends_at?: string | undefined;
|
|
1577
|
+
} | undefined;
|
|
1578
|
+
size_guide?: {
|
|
1579
|
+
title: string;
|
|
1580
|
+
columns?: string[] | undefined;
|
|
1581
|
+
rows?: string[][] | undefined;
|
|
1582
|
+
notes?: string[] | undefined;
|
|
1583
|
+
} | undefined;
|
|
1584
|
+
}[];
|
|
1585
|
+
}>;
|
|
1586
|
+
/**
|
|
1587
|
+
* The code travels as a QUERY parameter, not a path segment.
|
|
1588
|
+
*
|
|
1589
|
+
* Category codes contain a slash, which in a path segment must be sent as `%2F` —
|
|
1590
|
+
* and Apache rejects that by default (`AllowEncodedSlashes Off`), returning its own
|
|
1591
|
+
* HTML 404 before the request reaches the API at all. Every real category code has
|
|
1592
|
+
* a slash in it, so the path form served none of them.
|
|
1593
|
+
*
|
|
1594
|
+
* The engine still exposes the path form for slash-free codes; this client does not
|
|
1595
|
+
* use it, because a query parameter works regardless of how any proxy in the chain
|
|
1596
|
+
* is configured.
|
|
1597
|
+
*/
|
|
1598
|
+
getCategoryPage: (code: string, query?: CatalogReadQuery) => Promise<{
|
|
1599
|
+
code: string;
|
|
1600
|
+
hero: {
|
|
1601
|
+
title: string;
|
|
1602
|
+
image?: string | undefined;
|
|
1603
|
+
subtitle?: string | undefined;
|
|
1604
|
+
};
|
|
1605
|
+
blocks: ({
|
|
1606
|
+
title: string;
|
|
1607
|
+
kind: "rail";
|
|
1608
|
+
products: {
|
|
1609
|
+
sku: string;
|
|
1610
|
+
title: string;
|
|
1611
|
+
category: string;
|
|
1612
|
+
price: {
|
|
1613
|
+
amount: number;
|
|
1614
|
+
currency: string;
|
|
1615
|
+
};
|
|
1616
|
+
media: string[];
|
|
1617
|
+
variants: {
|
|
1618
|
+
sku: string;
|
|
1619
|
+
attributes: Record<string, string>;
|
|
1620
|
+
in_stock: boolean;
|
|
1621
|
+
image?: string | undefined;
|
|
1622
|
+
available?: number | undefined;
|
|
1623
|
+
}[];
|
|
1624
|
+
compare_at?: {
|
|
1625
|
+
amount: number;
|
|
1626
|
+
currency: string;
|
|
1627
|
+
} | undefined;
|
|
1628
|
+
description?: string | undefined;
|
|
1629
|
+
badges?: string[] | undefined;
|
|
1630
|
+
promo?: {
|
|
1631
|
+
message: string;
|
|
1632
|
+
ends_at?: string | undefined;
|
|
1633
|
+
} | undefined;
|
|
1634
|
+
size_guide?: {
|
|
1635
|
+
title: string;
|
|
1636
|
+
columns?: string[] | undefined;
|
|
1637
|
+
rows?: string[][] | undefined;
|
|
1638
|
+
notes?: string[] | undefined;
|
|
1639
|
+
} | undefined;
|
|
1640
|
+
}[];
|
|
1641
|
+
} | {
|
|
1642
|
+
kind: "lifestyle_text";
|
|
1643
|
+
text: string;
|
|
1644
|
+
align?: "center" | "left" | undefined;
|
|
1645
|
+
} | {
|
|
1646
|
+
kind: "featured";
|
|
1647
|
+
product: {
|
|
1648
|
+
sku: string;
|
|
1649
|
+
title: string;
|
|
1650
|
+
category: string;
|
|
1651
|
+
price: {
|
|
1652
|
+
amount: number;
|
|
1653
|
+
currency: string;
|
|
1654
|
+
};
|
|
1655
|
+
media: string[];
|
|
1656
|
+
variants: {
|
|
1657
|
+
sku: string;
|
|
1658
|
+
attributes: Record<string, string>;
|
|
1659
|
+
in_stock: boolean;
|
|
1660
|
+
image?: string | undefined;
|
|
1661
|
+
available?: number | undefined;
|
|
1662
|
+
}[];
|
|
1663
|
+
compare_at?: {
|
|
1664
|
+
amount: number;
|
|
1665
|
+
currency: string;
|
|
1666
|
+
} | undefined;
|
|
1667
|
+
description?: string | undefined;
|
|
1668
|
+
badges?: string[] | undefined;
|
|
1669
|
+
promo?: {
|
|
1670
|
+
message: string;
|
|
1671
|
+
ends_at?: string | undefined;
|
|
1672
|
+
} | undefined;
|
|
1673
|
+
size_guide?: {
|
|
1674
|
+
title: string;
|
|
1675
|
+
columns?: string[] | undefined;
|
|
1676
|
+
rows?: string[][] | undefined;
|
|
1677
|
+
notes?: string[] | undefined;
|
|
1678
|
+
} | undefined;
|
|
1679
|
+
};
|
|
1680
|
+
} | {
|
|
1681
|
+
kind: "lifestyle_image";
|
|
1682
|
+
images: string[];
|
|
1683
|
+
} | {
|
|
1684
|
+
title: string;
|
|
1685
|
+
kind: "grid";
|
|
1686
|
+
products: {
|
|
1687
|
+
sku: string;
|
|
1688
|
+
title: string;
|
|
1689
|
+
category: string;
|
|
1690
|
+
price: {
|
|
1691
|
+
amount: number;
|
|
1692
|
+
currency: string;
|
|
1693
|
+
};
|
|
1694
|
+
media: string[];
|
|
1695
|
+
variants: {
|
|
1696
|
+
sku: string;
|
|
1697
|
+
attributes: Record<string, string>;
|
|
1698
|
+
in_stock: boolean;
|
|
1699
|
+
image?: string | undefined;
|
|
1700
|
+
available?: number | undefined;
|
|
1701
|
+
}[];
|
|
1702
|
+
compare_at?: {
|
|
1703
|
+
amount: number;
|
|
1704
|
+
currency: string;
|
|
1705
|
+
} | undefined;
|
|
1706
|
+
description?: string | undefined;
|
|
1707
|
+
badges?: string[] | undefined;
|
|
1708
|
+
promo?: {
|
|
1709
|
+
message: string;
|
|
1710
|
+
ends_at?: string | undefined;
|
|
1711
|
+
} | undefined;
|
|
1712
|
+
size_guide?: {
|
|
1713
|
+
title: string;
|
|
1714
|
+
columns?: string[] | undefined;
|
|
1715
|
+
rows?: string[][] | undefined;
|
|
1716
|
+
notes?: string[] | undefined;
|
|
1717
|
+
} | undefined;
|
|
1718
|
+
}[];
|
|
1719
|
+
} | {
|
|
1720
|
+
kind: "review";
|
|
1721
|
+
quotes: {
|
|
1722
|
+
quote: string;
|
|
1723
|
+
author: string;
|
|
1724
|
+
}[];
|
|
1725
|
+
} | {
|
|
1726
|
+
kind: "why_us";
|
|
1727
|
+
items: {
|
|
1728
|
+
title: string;
|
|
1729
|
+
body: string;
|
|
1730
|
+
icon?: "payment" | "delivery" | "returns" | undefined;
|
|
1731
|
+
}[];
|
|
1732
|
+
} | {
|
|
1733
|
+
title: string;
|
|
1734
|
+
kind: "explore";
|
|
1735
|
+
links: {
|
|
1736
|
+
code: string;
|
|
1737
|
+
label: string;
|
|
1738
|
+
}[];
|
|
1739
|
+
})[];
|
|
1740
|
+
}>;
|
|
1741
|
+
};
|
|
1742
|
+
inventory: {
|
|
1743
|
+
get: (sku: string) => Promise<{
|
|
1744
|
+
sku: string;
|
|
1745
|
+
in_stock: boolean;
|
|
1746
|
+
available: number;
|
|
1747
|
+
}>;
|
|
1748
|
+
};
|
|
1749
|
+
cart: {
|
|
1750
|
+
create: () => Promise<{
|
|
1751
|
+
currency: string;
|
|
1752
|
+
items: {
|
|
1753
|
+
sku: string;
|
|
1754
|
+
qty: number;
|
|
1755
|
+
unit_price: {
|
|
1756
|
+
amount: number;
|
|
1757
|
+
currency: string;
|
|
1758
|
+
};
|
|
1759
|
+
line_total: {
|
|
1760
|
+
amount: number;
|
|
1761
|
+
currency: string;
|
|
1762
|
+
};
|
|
1763
|
+
attributes?: Record<string, string> | undefined;
|
|
1764
|
+
image?: string | undefined;
|
|
1765
|
+
available?: number | undefined;
|
|
1766
|
+
title?: string | undefined;
|
|
1767
|
+
}[];
|
|
1768
|
+
cart_id: string;
|
|
1769
|
+
totals: {
|
|
1770
|
+
subtotal: {
|
|
1771
|
+
amount: number;
|
|
1772
|
+
currency: string;
|
|
1773
|
+
};
|
|
1774
|
+
shipping: {
|
|
1775
|
+
amount: number;
|
|
1776
|
+
currency: string;
|
|
1777
|
+
};
|
|
1778
|
+
tax: {
|
|
1779
|
+
amount: number;
|
|
1780
|
+
currency: string;
|
|
1781
|
+
};
|
|
1782
|
+
total: {
|
|
1783
|
+
amount: number;
|
|
1784
|
+
currency: string;
|
|
1785
|
+
};
|
|
1786
|
+
};
|
|
1787
|
+
locked?: {
|
|
1788
|
+
since: string;
|
|
1789
|
+
reason: "checkout_handoff";
|
|
1790
|
+
expires_at?: string | undefined;
|
|
1791
|
+
} | undefined;
|
|
1792
|
+
}>;
|
|
1793
|
+
get: () => Promise<{
|
|
1794
|
+
currency: string;
|
|
1795
|
+
items: {
|
|
1796
|
+
sku: string;
|
|
1797
|
+
qty: number;
|
|
1798
|
+
unit_price: {
|
|
1799
|
+
amount: number;
|
|
1800
|
+
currency: string;
|
|
1801
|
+
};
|
|
1802
|
+
line_total: {
|
|
1803
|
+
amount: number;
|
|
1804
|
+
currency: string;
|
|
1805
|
+
};
|
|
1806
|
+
attributes?: Record<string, string> | undefined;
|
|
1807
|
+
image?: string | undefined;
|
|
1808
|
+
available?: number | undefined;
|
|
1809
|
+
title?: string | undefined;
|
|
1810
|
+
}[];
|
|
1811
|
+
cart_id: string;
|
|
1812
|
+
totals: {
|
|
1813
|
+
subtotal: {
|
|
1814
|
+
amount: number;
|
|
1815
|
+
currency: string;
|
|
1816
|
+
};
|
|
1817
|
+
shipping: {
|
|
1818
|
+
amount: number;
|
|
1819
|
+
currency: string;
|
|
1820
|
+
};
|
|
1821
|
+
tax: {
|
|
1822
|
+
amount: number;
|
|
1823
|
+
currency: string;
|
|
1824
|
+
};
|
|
1825
|
+
total: {
|
|
1826
|
+
amount: number;
|
|
1827
|
+
currency: string;
|
|
1828
|
+
};
|
|
1829
|
+
};
|
|
1830
|
+
locked?: {
|
|
1831
|
+
since: string;
|
|
1832
|
+
reason: "checkout_handoff";
|
|
1833
|
+
expires_at?: string | undefined;
|
|
1834
|
+
} | undefined;
|
|
1835
|
+
}>;
|
|
1836
|
+
addItem: (body: AddCartItemRequest) => Promise<{
|
|
1837
|
+
currency: string;
|
|
1838
|
+
items: {
|
|
1839
|
+
sku: string;
|
|
1840
|
+
qty: number;
|
|
1841
|
+
unit_price: {
|
|
1842
|
+
amount: number;
|
|
1843
|
+
currency: string;
|
|
1844
|
+
};
|
|
1845
|
+
line_total: {
|
|
1846
|
+
amount: number;
|
|
1847
|
+
currency: string;
|
|
1848
|
+
};
|
|
1849
|
+
attributes?: Record<string, string> | undefined;
|
|
1850
|
+
image?: string | undefined;
|
|
1851
|
+
available?: number | undefined;
|
|
1852
|
+
title?: string | undefined;
|
|
1853
|
+
}[];
|
|
1854
|
+
cart_id: string;
|
|
1855
|
+
totals: {
|
|
1856
|
+
subtotal: {
|
|
1857
|
+
amount: number;
|
|
1858
|
+
currency: string;
|
|
1859
|
+
};
|
|
1860
|
+
shipping: {
|
|
1861
|
+
amount: number;
|
|
1862
|
+
currency: string;
|
|
1863
|
+
};
|
|
1864
|
+
tax: {
|
|
1865
|
+
amount: number;
|
|
1866
|
+
currency: string;
|
|
1867
|
+
};
|
|
1868
|
+
total: {
|
|
1869
|
+
amount: number;
|
|
1870
|
+
currency: string;
|
|
1871
|
+
};
|
|
1872
|
+
};
|
|
1873
|
+
locked?: {
|
|
1874
|
+
since: string;
|
|
1875
|
+
reason: "checkout_handoff";
|
|
1876
|
+
expires_at?: string | undefined;
|
|
1877
|
+
} | undefined;
|
|
1878
|
+
}>;
|
|
1879
|
+
updateItem: (sku: string, body: UpdateCartItemRequest) => Promise<{
|
|
1880
|
+
currency: string;
|
|
1881
|
+
items: {
|
|
1882
|
+
sku: string;
|
|
1883
|
+
qty: number;
|
|
1884
|
+
unit_price: {
|
|
1885
|
+
amount: number;
|
|
1886
|
+
currency: string;
|
|
1887
|
+
};
|
|
1888
|
+
line_total: {
|
|
1889
|
+
amount: number;
|
|
1890
|
+
currency: string;
|
|
1891
|
+
};
|
|
1892
|
+
attributes?: Record<string, string> | undefined;
|
|
1893
|
+
image?: string | undefined;
|
|
1894
|
+
available?: number | undefined;
|
|
1895
|
+
title?: string | undefined;
|
|
1896
|
+
}[];
|
|
1897
|
+
cart_id: string;
|
|
1898
|
+
totals: {
|
|
1899
|
+
subtotal: {
|
|
1900
|
+
amount: number;
|
|
1901
|
+
currency: string;
|
|
1902
|
+
};
|
|
1903
|
+
shipping: {
|
|
1904
|
+
amount: number;
|
|
1905
|
+
currency: string;
|
|
1906
|
+
};
|
|
1907
|
+
tax: {
|
|
1908
|
+
amount: number;
|
|
1909
|
+
currency: string;
|
|
1910
|
+
};
|
|
1911
|
+
total: {
|
|
1912
|
+
amount: number;
|
|
1913
|
+
currency: string;
|
|
1914
|
+
};
|
|
1915
|
+
};
|
|
1916
|
+
locked?: {
|
|
1917
|
+
since: string;
|
|
1918
|
+
reason: "checkout_handoff";
|
|
1919
|
+
expires_at?: string | undefined;
|
|
1920
|
+
} | undefined;
|
|
1921
|
+
}>;
|
|
1922
|
+
removeItem: (sku: string) => Promise<{
|
|
1923
|
+
currency: string;
|
|
1924
|
+
items: {
|
|
1925
|
+
sku: string;
|
|
1926
|
+
qty: number;
|
|
1927
|
+
unit_price: {
|
|
1928
|
+
amount: number;
|
|
1929
|
+
currency: string;
|
|
1930
|
+
};
|
|
1931
|
+
line_total: {
|
|
1932
|
+
amount: number;
|
|
1933
|
+
currency: string;
|
|
1934
|
+
};
|
|
1935
|
+
attributes?: Record<string, string> | undefined;
|
|
1936
|
+
image?: string | undefined;
|
|
1937
|
+
available?: number | undefined;
|
|
1938
|
+
title?: string | undefined;
|
|
1939
|
+
}[];
|
|
1940
|
+
cart_id: string;
|
|
1941
|
+
totals: {
|
|
1942
|
+
subtotal: {
|
|
1943
|
+
amount: number;
|
|
1944
|
+
currency: string;
|
|
1945
|
+
};
|
|
1946
|
+
shipping: {
|
|
1947
|
+
amount: number;
|
|
1948
|
+
currency: string;
|
|
1949
|
+
};
|
|
1950
|
+
tax: {
|
|
1951
|
+
amount: number;
|
|
1952
|
+
currency: string;
|
|
1953
|
+
};
|
|
1954
|
+
total: {
|
|
1955
|
+
amount: number;
|
|
1956
|
+
currency: string;
|
|
1957
|
+
};
|
|
1958
|
+
};
|
|
1959
|
+
locked?: {
|
|
1960
|
+
since: string;
|
|
1961
|
+
reason: "checkout_handoff";
|
|
1962
|
+
expires_at?: string | undefined;
|
|
1963
|
+
} | undefined;
|
|
1964
|
+
}>;
|
|
1965
|
+
/**
|
|
1966
|
+
* Hand the cart to DAZN checkout and get the URL to send the fan to.
|
|
1967
|
+
*
|
|
1968
|
+
* The URL is opaque — redirect to it, never parse it. This is the last call in our
|
|
1969
|
+
* journey; payment, order and confirmation all happen on DAZN.
|
|
1970
|
+
*/
|
|
1971
|
+
handoff: () => Promise<{
|
|
1972
|
+
checkout_url: string;
|
|
1973
|
+
}>;
|
|
1974
|
+
};
|
|
1975
|
+
auth: {
|
|
1976
|
+
/** Exchange a single-use DAZN identity JWT for a session (sets the cookie). */
|
|
1977
|
+
exchangeSession: (body: SessionExchangeRequest) => Promise<{
|
|
1978
|
+
user_id: string;
|
|
1979
|
+
}>;
|
|
1980
|
+
/**
|
|
1981
|
+
* Redeem the `session` + `code` a DAZN redirect arrives with (sets the cookie).
|
|
1982
|
+
* The entry path from the DAZN app; see `ShopSessionRequest`.
|
|
1983
|
+
*/
|
|
1984
|
+
redeemShopSession: (body: ShopSessionRequest) => Promise<{
|
|
1985
|
+
expires_at: number;
|
|
1986
|
+
shop_session_id: string;
|
|
1987
|
+
session_token: string;
|
|
1988
|
+
email?: string | undefined;
|
|
1989
|
+
product?: {
|
|
1990
|
+
title: string;
|
|
1991
|
+
id: string;
|
|
1992
|
+
vendor_id: string;
|
|
1993
|
+
} | null | undefined;
|
|
1994
|
+
}>;
|
|
1995
|
+
};
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1998
|
+
/**
|
|
1999
|
+
* The session seam, implemented once at the transport level.
|
|
2000
|
+
*
|
|
2001
|
+
* Retry-on-401 lives here rather than wrapped around each API method for a reason: the
|
|
2002
|
+
* typed client is a nested object of namespaces, so wrapping the surface means a proxy
|
|
2003
|
+
* or ~20 hand-written wrappers, and either would drift the first time a method is added.
|
|
2004
|
+
* A `fetch` is one function and every request already passes through it.
|
|
2005
|
+
*
|
|
2006
|
+
* **The retry budget is exactly one.** A host whose `getToken` cannot recover — expired
|
|
2007
|
+
* refresh, revoked session, signed-out user — would otherwise have every request spin
|
|
2008
|
+
* against its identity service. One retry, then we stop and say so.
|
|
2009
|
+
*/
|
|
2010
|
+
export declare function createShopFetch(opts: {
|
|
2011
|
+
getToken: GetToken;
|
|
2012
|
+
onEvent?: OnEvent;
|
|
2013
|
+
fetchImpl?: typeof fetch;
|
|
2014
|
+
/** Needed only to detect the silent-anonymous-session trap below. */
|
|
2015
|
+
sessionTransport?: 'bearer' | 'cookie';
|
|
2016
|
+
}): typeof fetch;
|
|
2017
|
+
|
|
2018
|
+
/** Thrown on any non-2xx `/v1` response, carrying the standard error object. */
|
|
2019
|
+
declare class DaznApiError extends Error {
|
|
2020
|
+
readonly status: number;
|
|
2021
|
+
readonly code?: ErrorCode_2;
|
|
2022
|
+
readonly requestId?: string;
|
|
2023
|
+
readonly body?: ApiErrorBody;
|
|
2024
|
+
constructor(status: number, parsed?: {
|
|
2025
|
+
error: ApiErrorBody;
|
|
2026
|
+
request_id: string;
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
declare type DaznClient = ReturnType<typeof createClient>;
|
|
2031
|
+
|
|
2032
|
+
export declare interface DaznShopConfig {
|
|
2033
|
+
apiBaseUrl: string;
|
|
2034
|
+
getToken: GetToken;
|
|
2035
|
+
/**
|
|
2036
|
+
* How the session travels to the commerce API. Default `'bearer'`.
|
|
2037
|
+
*
|
|
2038
|
+
* - `'bearer'` — the token from {@link GetToken} goes in `Authorization`, and cookies
|
|
2039
|
+
* are **not** sent. Correct when mounted in a host's page: the SDK is cross-origin
|
|
2040
|
+
* with the API, holds no credential of its own, and a cookie would be neither sent
|
|
2041
|
+
* nor wanted.
|
|
2042
|
+
* - `'cookie'` — the engine's httpOnly session cookie is sent instead. Correct for a
|
|
2043
|
+
* first-party web view that authenticates by cookie and has no token for JS to hold.
|
|
2044
|
+
*
|
|
2045
|
+
* Getting this wrong does not fail loudly. Requests simply arrive unauthenticated, and
|
|
2046
|
+
* an environment that issues anonymous sessions will hand out a **new** one per call —
|
|
2047
|
+
* so an item is added to one cart and read back from another, and the basket looks
|
|
2048
|
+
* empty rather than broken. That is exactly how this was found.
|
|
2049
|
+
*/
|
|
2050
|
+
sessionTransport?: 'bearer' | 'cookie';
|
|
2051
|
+
/**
|
|
2052
|
+
* Origin (or path prefix) the SDK's own static assets are served from — the design
|
|
2053
|
+
* imagery the shop-entry screen renders. Default: none, i.e. paths are left as written.
|
|
2054
|
+
*
|
|
2055
|
+
* These are **root-relative** in the source (`/figma/home/hero.jpg`), which is correct
|
|
2056
|
+
* in our own app and wrong everywhere else: a browser resolves them against the page's
|
|
2057
|
+
* origin, and mounted in a host's page that is the **host's** origin. The host does not
|
|
2058
|
+
* serve our imagery, so every one of them 404s and the entry screen renders with broken
|
|
2059
|
+
* images while the rest of the SDK works perfectly — which is exactly how it presents.
|
|
2060
|
+
*
|
|
2061
|
+
* Only root-relative paths are rebased. Absolute and protocol-relative URLs are left
|
|
2062
|
+
* untouched, because catalogue media arrives from the API already absolute and must not
|
|
2063
|
+
* be rewritten.
|
|
2064
|
+
*/
|
|
2065
|
+
assetBaseUrl?: string;
|
|
2066
|
+
market?: Market;
|
|
2067
|
+
navigate?: Navigate;
|
|
2068
|
+
/**
|
|
2069
|
+
* @deprecated No longer affects how anything renders, and safe to remove.
|
|
2070
|
+
*
|
|
2071
|
+
* The storefront has one design per screen, not two: the shop entry and category pages
|
|
2072
|
+
* are drawn dark, the PDP and cart light. There is no light version of the dark hero to
|
|
2073
|
+
* switch to, so this never selected between two designs — it repainted the tokens under
|
|
2074
|
+
* a screen built for the other palette, which is how `theme="light"` produced near-black
|
|
2075
|
+
* button text on a dark photograph.
|
|
2076
|
+
*
|
|
2077
|
+
* Still accepted so that passing it does not break a build, and still readable from
|
|
2078
|
+
* context by a host that wants to know what was asked for. It decides nothing.
|
|
2079
|
+
*/
|
|
2080
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
2081
|
+
notifications?: 'internal' | 'host';
|
|
2082
|
+
onEvent?: OnEvent;
|
|
2083
|
+
locale?: string;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
/**
|
|
2087
|
+
* Mount once, high in the host tree. Owns configuration, the API client, the session
|
|
2088
|
+
* seam and event fan-out, so two components rendering the same cart cannot disagree.
|
|
2089
|
+
*
|
|
2090
|
+
* Nesting providers is supported but each gets an independent client — don't, unless
|
|
2091
|
+
* that is what you mean.
|
|
2092
|
+
*/
|
|
2093
|
+
export declare function DaznShopProvider({ children, ...config }: DaznShopConfig & {
|
|
2094
|
+
children: ReactNode;
|
|
2095
|
+
}): JSX.Element;
|
|
2096
|
+
|
|
2097
|
+
/** A row this surface cannot put a number against yet. */
|
|
2098
|
+
export declare type DeferredLine = 'shipping' | 'tax';
|
|
2099
|
+
|
|
2100
|
+
/**
|
|
2101
|
+
* A whole-number discount percentage derived from two server-provided Money values.
|
|
2102
|
+
* This is presentational rounding of values the server already computed — it does NOT
|
|
2103
|
+
* price anything. Returns undefined when there is no genuine discount.
|
|
2104
|
+
*/
|
|
2105
|
+
export declare function discountPct(price: Money, compareAt?: Money): number | undefined;
|
|
2106
|
+
|
|
2107
|
+
/** Distinct values for an attribute key, in first-seen order. */
|
|
2108
|
+
export declare function distinctValues(variants: ProductVariant[], key: string): string[];
|
|
2109
|
+
|
|
2110
|
+
/** Neutral empty state with a title, optional body, and optional action. */
|
|
2111
|
+
export declare function EmptyState({ title, body, action, icon }: EmptyStateProps): JSX.Element;
|
|
2112
|
+
|
|
2113
|
+
export declare interface EmptyStateProps {
|
|
2114
|
+
title: string;
|
|
2115
|
+
body?: string;
|
|
2116
|
+
/** Optional call-to-action (e.g. a Button or Link). */
|
|
2117
|
+
action?: ReactNode;
|
|
2118
|
+
icon?: ReactNode;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
/**
|
|
2122
|
+
* The stable error `code` enum. Clients branch on `code`, never on `message`.
|
|
2123
|
+
* Additive-only: a new code is a minor bump consumers must tolerate.
|
|
2124
|
+
*
|
|
2125
|
+
* | code | HTTP | when |
|
|
2126
|
+
* | ------------------ | ---- | --------------------------------------------- |
|
|
2127
|
+
* | invalid_request | 400 | malformed body or missing field |
|
|
2128
|
+
* | unauthorized | 401 | missing/invalid session or credentials |
|
|
2129
|
+
* | out_of_stock | 409 | requested qty exceeds available stock |
|
|
2130
|
+
* | cart_locked | 409 | cart handed to checkout; mutations refused |
|
|
2131
|
+
* | sku_not_found | 404 | a referenced SKU/product does not exist |
|
|
2132
|
+
* | category_not_found | 404 | a referenced category code does not exist |
|
|
2133
|
+
* | amount_mismatch | 422 | totals do not match the authorised amount |
|
|
2134
|
+
* | duplicate_order | 200 | idempotent replay — existing order returned |
|
|
2135
|
+
* | forbidden | 403 | authenticated, but the role may not do this |
|
|
2136
|
+
* | invalid_transition | 409 | fulfilment move not allowed from this status |
|
|
2137
|
+
* | rate_limited | 429 | too many requests |
|
|
2138
|
+
* | server_error | 500 | unexpected error (safe to retry idempotent) |
|
|
2139
|
+
*/
|
|
2140
|
+
declare const ErrorCode: z.ZodEnum<["invalid_request", "unauthorized", "out_of_stock", "cart_locked", "sku_not_found", "category_not_found", "amount_mismatch", "duplicate_order", "order_not_found", "order_not_cancellable", "forbidden", "invalid_transition", "rate_limited", "server_error"]>;
|
|
2141
|
+
|
|
2142
|
+
declare type ErrorCode = z.infer<typeof ErrorCode>;
|
|
2143
|
+
|
|
2144
|
+
/**
|
|
2145
|
+
* The stable error `code` enum. Clients branch on `code`, never on `message`.
|
|
2146
|
+
* Additive-only: a new code is a minor bump consumers must tolerate.
|
|
2147
|
+
*
|
|
2148
|
+
* | code | HTTP | when |
|
|
2149
|
+
* | ------------------ | ---- | --------------------------------------------- |
|
|
2150
|
+
* | invalid_request | 400 | malformed body or missing field |
|
|
2151
|
+
* | unauthorized | 401 | missing/invalid session or credentials |
|
|
2152
|
+
* | out_of_stock | 409 | requested qty exceeds available stock |
|
|
2153
|
+
* | cart_locked | 409 | cart handed to checkout; mutations refused |
|
|
2154
|
+
* | sku_not_found | 404 | a referenced SKU/product does not exist |
|
|
2155
|
+
* | category_not_found | 404 | a referenced category code does not exist |
|
|
2156
|
+
* | amount_mismatch | 422 | totals do not match the authorised amount |
|
|
2157
|
+
* | duplicate_order | 200 | idempotent replay — existing order returned |
|
|
2158
|
+
* | forbidden | 403 | authenticated, but the role may not do this |
|
|
2159
|
+
* | invalid_transition | 409 | fulfilment move not allowed from this status |
|
|
2160
|
+
* | rate_limited | 429 | too many requests |
|
|
2161
|
+
* | server_error | 500 | unexpected error (safe to retry idempotent) |
|
|
2162
|
+
*/
|
|
2163
|
+
declare const ErrorCode_2: z.ZodEnum<["invalid_request", "unauthorized", "out_of_stock", "cart_locked", "sku_not_found", "category_not_found", "amount_mismatch", "duplicate_order", "order_not_found", "order_not_cancellable", "forbidden", "invalid_transition", "rate_limited", "server_error"]>;
|
|
2164
|
+
|
|
2165
|
+
declare type ErrorCode_2 = z.infer<typeof ErrorCode_2>;
|
|
2166
|
+
|
|
2167
|
+
/**
|
|
2168
|
+
* Error state whose copy is derived from the API error `code`. The retry affordance is
|
|
2169
|
+
* shown only for retriable codes (or when the caller forces `onRetry`).
|
|
2170
|
+
*/
|
|
2171
|
+
export declare function ErrorState({ code, title, body, onRetry, retryLabel }: ErrorStateProps): JSX.Element;
|
|
2172
|
+
|
|
2173
|
+
export declare interface ErrorStateProps {
|
|
2174
|
+
/** API error code — copy is mapped from this, never from a raw message. */
|
|
2175
|
+
code?: ErrorCode;
|
|
2176
|
+
/** Override the mapped title/body if a screen needs bespoke copy. */
|
|
2177
|
+
title?: string;
|
|
2178
|
+
body?: string;
|
|
2179
|
+
/** Retry handler; only rendered when the error is retriable. */
|
|
2180
|
+
onRetry?: () => void;
|
|
2181
|
+
retryLabel?: string;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
/**
|
|
2185
|
+
* UI treatment for each API error `code`. The storefront ALWAYS branches on the
|
|
2186
|
+
* machine `code` (never the human message). Copy is display-only; the engine remains
|
|
2187
|
+
* authoritative for what actually happened.
|
|
2188
|
+
*/
|
|
2189
|
+
export declare interface ErrorTreatment {
|
|
2190
|
+
title: string;
|
|
2191
|
+
body: string;
|
|
2192
|
+
/** May the user safely retry the same action? */
|
|
2193
|
+
retriable: boolean;
|
|
2194
|
+
/** Should this trigger the silent DAZN re-handshake instead of a visible error? */
|
|
2195
|
+
reauth?: boolean;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
declare interface ExploreLink {
|
|
2199
|
+
label: string;
|
|
2200
|
+
code: string;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
/** "Shop more" — links to sibling categories. */
|
|
2204
|
+
export declare function ExploreList({ title, links }: {
|
|
2205
|
+
title: string;
|
|
2206
|
+
links: ExploreLink[];
|
|
2207
|
+
}): JSX.Element;
|
|
2208
|
+
|
|
2209
|
+
/** Horizontal chip filter bar for the PLP. Chips are toggle buttons. */
|
|
2210
|
+
export declare function FilterBar({ options, active, onToggle, onClear }: FilterBarProps): JSX.Element;
|
|
2211
|
+
|
|
2212
|
+
export declare interface FilterBarProps {
|
|
2213
|
+
options: FilterOption[];
|
|
2214
|
+
/** Currently active filter values. */
|
|
2215
|
+
active: string[];
|
|
2216
|
+
onToggle: (value: string) => void;
|
|
2217
|
+
onClear?: () => void;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
export declare interface FilterOption {
|
|
2221
|
+
value: string;
|
|
2222
|
+
label: string;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
/**
|
|
2226
|
+
* Format a {@link Money} for display in the given locale. Divides minor units by the
|
|
2227
|
+
* currency exponent purely for presentation.
|
|
2228
|
+
*/
|
|
2229
|
+
export declare function formatMoney(money: Money, locale?: string): string;
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* Supplies the session bearer token. Called before each authenticated request and again,
|
|
2233
|
+
* once, after a `401`.
|
|
2234
|
+
*
|
|
2235
|
+
* Returning `undefined` or an empty string is legal and means "no session yet":
|
|
2236
|
+
* catalogue browsing works without one, and cart operations raise `session_required`.
|
|
2237
|
+
* The SDK never persists what this returns and never holds a refresh credential — that
|
|
2238
|
+
* stays with the host, which is the whole point of it being a callback.
|
|
2239
|
+
*/
|
|
2240
|
+
export declare type GetToken = () => string | undefined | Promise<string | undefined>;
|
|
2241
|
+
|
|
2242
|
+
declare function HomeShowcase(): JSX.Element;
|
|
2243
|
+
export { HomeShowcase }
|
|
2244
|
+
export { HomeShowcase as ShopHome }
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* 4.5 Identity gate. **DAZN owns identity; the storefront runs no login**, so this asks
|
|
2248
|
+
* the host to take over rather than doing anything itself.
|
|
2249
|
+
*
|
|
2250
|
+
* In the app this called a `window.DAZNBridge` method. That was right for a web view and
|
|
2251
|
+
* wrong for a mounted component: reaching for a global the host may not have installed
|
|
2252
|
+
* is a hidden coupling, and a component that silently does nothing when the global is
|
|
2253
|
+
* absent is worse than one that says so. It now raises `session_required` — the same
|
|
2254
|
+
* request, made through the contract the host already handles.
|
|
2255
|
+
*
|
|
2256
|
+
* The cart is session-scoped server-side and survives the round trip; we never drop it.
|
|
2257
|
+
*/
|
|
2258
|
+
export declare function IdentityGate({ returnTo }: IdentityGateProps): JSX.Element;
|
|
2259
|
+
|
|
2260
|
+
export declare interface IdentityGateProps {
|
|
2261
|
+
/** Where the host should return the fan after its sign-in flow. */
|
|
2262
|
+
returnTo: string;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
/**
|
|
2266
|
+
* `GET /v1/inventory/{sku}` — read-model availability. Authoritative stock is Odoo;
|
|
2267
|
+
* a live check is also run at checkout.
|
|
2268
|
+
*/
|
|
2269
|
+
declare const Inventory: z.ZodObject<{
|
|
2270
|
+
sku: z.ZodString;
|
|
2271
|
+
available: z.ZodNumber;
|
|
2272
|
+
in_stock: z.ZodBoolean;
|
|
2273
|
+
}, "strip", z.ZodTypeAny, {
|
|
2274
|
+
sku: string;
|
|
2275
|
+
available: number;
|
|
2276
|
+
in_stock: boolean;
|
|
2277
|
+
}, {
|
|
2278
|
+
sku: string;
|
|
2279
|
+
available: number;
|
|
2280
|
+
in_stock: boolean;
|
|
2281
|
+
}>;
|
|
2282
|
+
|
|
2283
|
+
declare type Inventory = z.infer<typeof Inventory>;
|
|
2284
|
+
|
|
2285
|
+
/** A duplicate order (200 replay) is a SUCCESS, never an error to surface. */
|
|
2286
|
+
export declare function isDuplicateOrder(err: unknown): boolean;
|
|
2287
|
+
|
|
2288
|
+
/** A 401 anywhere in the flow means the session lapsed — re-handshake, keep the cart. */
|
|
2289
|
+
export declare function isUnauthorized(err: unknown): boolean;
|
|
2290
|
+
|
|
2291
|
+
/** Full-width lifestyle image (rounded), inset from the page edges. */
|
|
2292
|
+
export declare function LifestyleImage({ images }: {
|
|
2293
|
+
images: string[];
|
|
2294
|
+
}): JSX.Element | null;
|
|
2295
|
+
|
|
2296
|
+
/** Editorial text band between PCP sections. */
|
|
2297
|
+
export declare function LifestyleText({ text, align }: LifestyleTextProps): JSX.Element;
|
|
2298
|
+
|
|
2299
|
+
export declare interface LifestyleTextProps {
|
|
2300
|
+
text: string;
|
|
2301
|
+
align?: 'center' | 'left';
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
/**
|
|
2305
|
+
* Query params for the product list. Extends {@link MarketQuery}, so `country` and
|
|
2306
|
+
* `lang` are accepted here as on every other catalogue read.
|
|
2307
|
+
*/
|
|
2308
|
+
declare const ListProductsQuery: z.ZodObject<{
|
|
2309
|
+
country: z.ZodOptional<z.ZodString>;
|
|
2310
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
2311
|
+
} & {
|
|
2312
|
+
category: z.ZodOptional<z.ZodString>;
|
|
2313
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
2314
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
2315
|
+
}, "strip", z.ZodTypeAny, {
|
|
2316
|
+
country?: string | undefined;
|
|
2317
|
+
lang?: string | undefined;
|
|
2318
|
+
cursor?: string | undefined;
|
|
2319
|
+
limit?: number | undefined;
|
|
2320
|
+
category?: string | undefined;
|
|
2321
|
+
}, {
|
|
2322
|
+
country?: string | undefined;
|
|
2323
|
+
lang?: string | undefined;
|
|
2324
|
+
cursor?: string | undefined;
|
|
2325
|
+
limit?: number | undefined;
|
|
2326
|
+
category?: string | undefined;
|
|
2327
|
+
}>;
|
|
2328
|
+
|
|
2329
|
+
declare type ListProductsQuery = z.infer<typeof ListProductsQuery>;
|
|
2330
|
+
|
|
2331
|
+
export declare interface Market {
|
|
2332
|
+
/** ISO 3166-1 alpha-2. Selects pricing, currency and merchandising. */
|
|
2333
|
+
country: string;
|
|
2334
|
+
/** IETF language tag. Display copy only — deliberately independent of `country`. */
|
|
2335
|
+
lang: string;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
/**
|
|
2339
|
+
* Swipeable image gallery with dot indicators. Reduced-motion aware (smooth-scroll is
|
|
2340
|
+
* disabled globally under prefers-reduced-motion). Keyboard: dots are real buttons.
|
|
2341
|
+
*/
|
|
2342
|
+
export declare function MediaGallery({ media, alt }: MediaGalleryProps): JSX.Element;
|
|
2343
|
+
|
|
2344
|
+
export declare interface MediaGalleryProps {
|
|
2345
|
+
media: string[];
|
|
2346
|
+
alt: string;
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
export declare function minorUnitExponent(currency: string): number;
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* Money is **always** integer minor units + an ISO-4217 currency.
|
|
2353
|
+
* `{ amount: 2500, currency: 'GBP' }` = £25.00. Never a float, never a bare number.
|
|
2354
|
+
*
|
|
2355
|
+
* Strict: the shape is fixed, so unknown keys are rejected everywhere Money appears.
|
|
2356
|
+
*/
|
|
2357
|
+
declare const Money: z.ZodObject<{
|
|
2358
|
+
amount: z.ZodNumber;
|
|
2359
|
+
currency: z.ZodString;
|
|
2360
|
+
}, "strict", z.ZodTypeAny, {
|
|
2361
|
+
amount: number;
|
|
2362
|
+
currency: string;
|
|
2363
|
+
}, {
|
|
2364
|
+
amount: number;
|
|
2365
|
+
currency: string;
|
|
2366
|
+
}>;
|
|
2367
|
+
|
|
2368
|
+
declare type Money = z.infer<typeof Money>;
|
|
2369
|
+
|
|
2370
|
+
/** Host router adapter. Omit it and navigable elements only raise `navigate` events. */
|
|
2371
|
+
export declare type Navigate = (to: ShopRoute) => void;
|
|
2372
|
+
|
|
2373
|
+
/**
|
|
2374
|
+
* Events are informational. **No event is a control point** — a handler cannot cancel
|
|
2375
|
+
* one, and the SDK does not await it. Gate an action in host UI before mounting the
|
|
2376
|
+
* component instead.
|
|
2377
|
+
*/
|
|
2378
|
+
export declare type OnEvent = (event: ShopEvent) => void;
|
|
2379
|
+
|
|
2380
|
+
/**
|
|
2381
|
+
* Fulfilment order status. Lives here (not in a synchronous order contract) because the
|
|
2382
|
+
* engine no longer owns orders (ADR-0008) — order/fulfilment status reaches us only as
|
|
2383
|
+
* async events (Odoo → engine → consumers). DAZN is authoritative for completed orders.
|
|
2384
|
+
*/
|
|
2385
|
+
declare const OrderStatus: z.ZodEnum<["confirmed", "packed", "shipped", "delivered", "cancelled", "refunded"]>;
|
|
2386
|
+
|
|
2387
|
+
declare type OrderStatus = z.infer<typeof OrderStatus>;
|
|
2388
|
+
|
|
2389
|
+
/**
|
|
2390
|
+
* The pricing breakdown. DISPLAY ONLY — every value is server-authoritative; the UI
|
|
2391
|
+
* never sums or computes anything here.
|
|
2392
|
+
*/
|
|
2393
|
+
export declare function OrderSummary({ pricing, deferred }: OrderSummaryProps): JSX.Element;
|
|
2394
|
+
|
|
2395
|
+
export declare interface OrderSummaryProps {
|
|
2396
|
+
pricing: Pricing;
|
|
2397
|
+
/**
|
|
2398
|
+
* Rows whose value is **not ours to state**, rendered as "Calculated at checkout"
|
|
2399
|
+
* instead of the figure in `pricing`.
|
|
2400
|
+
*
|
|
2401
|
+
* This exists because a zero is ambiguous and the ambiguity is dangerous. `Pricing`
|
|
2402
|
+
* requires all four values, so the engine sends `0` for shipping and tax it has not
|
|
2403
|
+
* computed — and rendering that gives the fan a confident "Tax £0.00" and a total that
|
|
2404
|
+
* changes at checkout. DAZN owns checkout and therefore owns delivery and tax
|
|
2405
|
+
* (ADR-0008), so in the cart both are genuinely unknown rather than genuinely free.
|
|
2406
|
+
*
|
|
2407
|
+
* When anything is deferred the grand total is withheld too: a total that omits
|
|
2408
|
+
* delivery is not a total. Subtotal carries the emphasis instead.
|
|
2409
|
+
*/
|
|
2410
|
+
deferred?: DeferredLine[];
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
/** PCP hero — category title + subtitle over a full-bleed lifestyle image (dark). */
|
|
2414
|
+
export declare function PcpHero({ hero }: {
|
|
2415
|
+
hero: CategoryHero;
|
|
2416
|
+
}): JSX.Element;
|
|
2417
|
+
|
|
2418
|
+
/**
|
|
2419
|
+
* Row of merchandising badges shown above the PDP title (mobile/badge/top-pick — light
|
|
2420
|
+
* surface, dark text). Renders nothing when the catalogue provides no flags, so the
|
|
2421
|
+
* layout collapses cleanly rather than showing an empty row.
|
|
2422
|
+
*/
|
|
2423
|
+
export declare function PdpBadges({ badges }: PdpBadgesProps): JSX.Element | null;
|
|
2424
|
+
|
|
2425
|
+
export declare interface PdpBadgesProps {
|
|
2426
|
+
/** Merchandising flags (Product.badges), e.g. ["Top pick", "99% Happy Customers"]. */
|
|
2427
|
+
badges: string[];
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
/**
|
|
2431
|
+
* Displays a price with an optional discount. DISPLAY ONLY — it never computes totals;
|
|
2432
|
+
* any percentage shown is a presentational rounding of values the server already
|
|
2433
|
+
* provided. Matches the DAZN design: discounts render as the green sale badge (PDP) or a
|
|
2434
|
+
* struck-through was-price (cards) — never as red discount text.
|
|
2435
|
+
*/
|
|
2436
|
+
export declare function PriceBlock({ price, compareAt, discountPct, size, variant, align, }: PriceBlockProps): JSX.Element;
|
|
2437
|
+
|
|
2438
|
+
export declare interface PriceBlockProps {
|
|
2439
|
+
/** Current server-authoritative price. */
|
|
2440
|
+
price: Money;
|
|
2441
|
+
/** Optional strike-through "was" price (server-provided). */
|
|
2442
|
+
compareAt?: Money;
|
|
2443
|
+
/** Optional explicit discount %. If omitted, derived from price/compareAt. */
|
|
2444
|
+
discountPct?: number;
|
|
2445
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2446
|
+
/**
|
|
2447
|
+
* How a discount is shown:
|
|
2448
|
+
* - `strikethrough` (default, product cards): current price + struck was-price.
|
|
2449
|
+
* - `badge` (PDP): current price + struck was-price + a green `-NN%` sale badge.
|
|
2450
|
+
*/
|
|
2451
|
+
variant?: 'strikethrough' | 'badge';
|
|
2452
|
+
/** Cross-axis alignment: `baseline` (default), `center`, or `end` (cards). */
|
|
2453
|
+
align?: 'baseline' | 'center' | 'end';
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
/**
|
|
2457
|
+
* The four-line pricing breakdown shared by the cart (`totals`) and the fan-facing
|
|
2458
|
+
* order (`pricing`). Every line is Money in the same currency.
|
|
2459
|
+
*/
|
|
2460
|
+
declare const Pricing: z.ZodObject<{
|
|
2461
|
+
subtotal: z.ZodObject<{
|
|
2462
|
+
amount: z.ZodNumber;
|
|
2463
|
+
currency: z.ZodString;
|
|
2464
|
+
}, "strict", z.ZodTypeAny, {
|
|
2465
|
+
amount: number;
|
|
2466
|
+
currency: string;
|
|
2467
|
+
}, {
|
|
2468
|
+
amount: number;
|
|
2469
|
+
currency: string;
|
|
2470
|
+
}>;
|
|
2471
|
+
shipping: z.ZodObject<{
|
|
2472
|
+
amount: z.ZodNumber;
|
|
2473
|
+
currency: z.ZodString;
|
|
2474
|
+
}, "strict", z.ZodTypeAny, {
|
|
2475
|
+
amount: number;
|
|
2476
|
+
currency: string;
|
|
2477
|
+
}, {
|
|
2478
|
+
amount: number;
|
|
2479
|
+
currency: string;
|
|
2480
|
+
}>;
|
|
2481
|
+
tax: z.ZodObject<{
|
|
2482
|
+
amount: z.ZodNumber;
|
|
2483
|
+
currency: z.ZodString;
|
|
2484
|
+
}, "strict", z.ZodTypeAny, {
|
|
2485
|
+
amount: number;
|
|
2486
|
+
currency: string;
|
|
2487
|
+
}, {
|
|
2488
|
+
amount: number;
|
|
2489
|
+
currency: string;
|
|
2490
|
+
}>;
|
|
2491
|
+
total: z.ZodObject<{
|
|
2492
|
+
amount: z.ZodNumber;
|
|
2493
|
+
currency: z.ZodString;
|
|
2494
|
+
}, "strict", z.ZodTypeAny, {
|
|
2495
|
+
amount: number;
|
|
2496
|
+
currency: string;
|
|
2497
|
+
}, {
|
|
2498
|
+
amount: number;
|
|
2499
|
+
currency: string;
|
|
2500
|
+
}>;
|
|
2501
|
+
}, "strip", z.ZodTypeAny, {
|
|
2502
|
+
subtotal: {
|
|
2503
|
+
amount: number;
|
|
2504
|
+
currency: string;
|
|
2505
|
+
};
|
|
2506
|
+
shipping: {
|
|
2507
|
+
amount: number;
|
|
2508
|
+
currency: string;
|
|
2509
|
+
};
|
|
2510
|
+
tax: {
|
|
2511
|
+
amount: number;
|
|
2512
|
+
currency: string;
|
|
2513
|
+
};
|
|
2514
|
+
total: {
|
|
2515
|
+
amount: number;
|
|
2516
|
+
currency: string;
|
|
2517
|
+
};
|
|
2518
|
+
}, {
|
|
2519
|
+
subtotal: {
|
|
2520
|
+
amount: number;
|
|
2521
|
+
currency: string;
|
|
2522
|
+
};
|
|
2523
|
+
shipping: {
|
|
2524
|
+
amount: number;
|
|
2525
|
+
currency: string;
|
|
2526
|
+
};
|
|
2527
|
+
tax: {
|
|
2528
|
+
amount: number;
|
|
2529
|
+
currency: string;
|
|
2530
|
+
};
|
|
2531
|
+
total: {
|
|
2532
|
+
amount: number;
|
|
2533
|
+
currency: string;
|
|
2534
|
+
};
|
|
2535
|
+
}>;
|
|
2536
|
+
|
|
2537
|
+
declare type Pricing = z.infer<typeof Pricing>;
|
|
2538
|
+
|
|
2539
|
+
/** A catalog product, served from the engine's cached read model (synced from Odoo). */
|
|
2540
|
+
declare const Product: z.ZodObject<{
|
|
2541
|
+
sku: z.ZodString;
|
|
2542
|
+
title: z.ZodString;
|
|
2543
|
+
category: z.ZodString;
|
|
2544
|
+
price: z.ZodObject<{
|
|
2545
|
+
amount: z.ZodNumber;
|
|
2546
|
+
currency: z.ZodString;
|
|
2547
|
+
}, "strict", z.ZodTypeAny, {
|
|
2548
|
+
amount: number;
|
|
2549
|
+
currency: string;
|
|
2550
|
+
}, {
|
|
2551
|
+
amount: number;
|
|
2552
|
+
currency: string;
|
|
2553
|
+
}>;
|
|
2554
|
+
/**
|
|
2555
|
+
* Optional strike-through "was" price for a sale. Additive/optional: the engine only
|
|
2556
|
+
* populates it once the pricing source is agreed (A2 — pricing contract). Display
|
|
2557
|
+
* only; never authoritative. Absent = not on sale.
|
|
2558
|
+
*/
|
|
2559
|
+
compare_at: z.ZodOptional<z.ZodObject<{
|
|
2560
|
+
amount: z.ZodNumber;
|
|
2561
|
+
currency: z.ZodString;
|
|
2562
|
+
}, "strict", z.ZodTypeAny, {
|
|
2563
|
+
amount: number;
|
|
2564
|
+
currency: string;
|
|
2565
|
+
}, {
|
|
2566
|
+
amount: number;
|
|
2567
|
+
currency: string;
|
|
2568
|
+
}>>;
|
|
2569
|
+
media: z.ZodArray<z.ZodString, "many">;
|
|
2570
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
2571
|
+
sku: z.ZodString;
|
|
2572
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
2573
|
+
in_stock: z.ZodBoolean;
|
|
2574
|
+
/**
|
|
2575
|
+
* Optional per-variant image — e.g. the colour swatch/tile shown in the PDP colour
|
|
2576
|
+
* selector. Absent = fall back to the product's `media`.
|
|
2577
|
+
*/
|
|
2578
|
+
image: z.ZodOptional<z.ZodString>;
|
|
2579
|
+
/**
|
|
2580
|
+
* Optional per-variant stock count, used to show "N left" low-stock badges. Display
|
|
2581
|
+
* only — the authoritative check runs at checkout. Absent = show no count.
|
|
2582
|
+
*/
|
|
2583
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
2584
|
+
}, "strip", z.ZodTypeAny, {
|
|
2585
|
+
sku: string;
|
|
2586
|
+
attributes: Record<string, string>;
|
|
2587
|
+
in_stock: boolean;
|
|
2588
|
+
image?: string | undefined;
|
|
2589
|
+
available?: number | undefined;
|
|
2590
|
+
}, {
|
|
2591
|
+
sku: string;
|
|
2592
|
+
attributes: Record<string, string>;
|
|
2593
|
+
in_stock: boolean;
|
|
2594
|
+
image?: string | undefined;
|
|
2595
|
+
available?: number | undefined;
|
|
2596
|
+
}>, "many">;
|
|
2597
|
+
/** Optional long-form product description (PDP details accordion). Absent = no body. */
|
|
2598
|
+
description: z.ZodOptional<z.ZodString>;
|
|
2599
|
+
/**
|
|
2600
|
+
* Optional merchandising flags shown as badges on the PDP/cards (e.g. "Top pick").
|
|
2601
|
+
* Free-form labels sourced from the catalogue; absent/empty = no badges.
|
|
2602
|
+
*/
|
|
2603
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
2604
|
+
/** Optional PDP promo nudge (display only — the offer is applied at DAZN checkout). */
|
|
2605
|
+
promo: z.ZodOptional<z.ZodObject<{
|
|
2606
|
+
message: z.ZodString;
|
|
2607
|
+
/** ISO-8601 end time that drives the countdown; absent = show the message, no timer. */
|
|
2608
|
+
ends_at: z.ZodOptional<z.ZodString>;
|
|
2609
|
+
}, "strip", z.ZodTypeAny, {
|
|
2610
|
+
message: string;
|
|
2611
|
+
ends_at?: string | undefined;
|
|
2612
|
+
}, {
|
|
2613
|
+
message: string;
|
|
2614
|
+
ends_at?: string | undefined;
|
|
2615
|
+
}>>;
|
|
2616
|
+
/**
|
|
2617
|
+
* Optional category-wide sizing help for the PDP "Size guide" overlay. Absent = the
|
|
2618
|
+
* storefront hides the control rather than opening an empty overlay.
|
|
2619
|
+
*/
|
|
2620
|
+
size_guide: z.ZodOptional<z.ZodObject<{
|
|
2621
|
+
title: z.ZodString;
|
|
2622
|
+
/** Column headings for `rows`, e.g. `['Size', 'Chest (cm)', 'Length (cm)']`. */
|
|
2623
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
2624
|
+
/** One row per size; each row's length matches `columns`. */
|
|
2625
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>;
|
|
2626
|
+
/** Free-text fit guidance, e.g. "Relaxed, slightly oversized fit". */
|
|
2627
|
+
notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
2628
|
+
}, "strip", z.ZodTypeAny, {
|
|
2629
|
+
title: string;
|
|
2630
|
+
columns?: string[] | undefined;
|
|
2631
|
+
rows?: string[][] | undefined;
|
|
2632
|
+
notes?: string[] | undefined;
|
|
2633
|
+
}, {
|
|
2634
|
+
title: string;
|
|
2635
|
+
columns?: string[] | undefined;
|
|
2636
|
+
rows?: string[][] | undefined;
|
|
2637
|
+
notes?: string[] | undefined;
|
|
2638
|
+
}>>;
|
|
2639
|
+
}, "strip", z.ZodTypeAny, {
|
|
2640
|
+
sku: string;
|
|
2641
|
+
title: string;
|
|
2642
|
+
category: string;
|
|
2643
|
+
price: {
|
|
2644
|
+
amount: number;
|
|
2645
|
+
currency: string;
|
|
2646
|
+
};
|
|
2647
|
+
media: string[];
|
|
2648
|
+
variants: {
|
|
2649
|
+
sku: string;
|
|
2650
|
+
attributes: Record<string, string>;
|
|
2651
|
+
in_stock: boolean;
|
|
2652
|
+
image?: string | undefined;
|
|
2653
|
+
available?: number | undefined;
|
|
2654
|
+
}[];
|
|
2655
|
+
compare_at?: {
|
|
2656
|
+
amount: number;
|
|
2657
|
+
currency: string;
|
|
2658
|
+
} | undefined;
|
|
2659
|
+
description?: string | undefined;
|
|
2660
|
+
badges?: string[] | undefined;
|
|
2661
|
+
promo?: {
|
|
2662
|
+
message: string;
|
|
2663
|
+
ends_at?: string | undefined;
|
|
2664
|
+
} | undefined;
|
|
2665
|
+
size_guide?: {
|
|
2666
|
+
title: string;
|
|
2667
|
+
columns?: string[] | undefined;
|
|
2668
|
+
rows?: string[][] | undefined;
|
|
2669
|
+
notes?: string[] | undefined;
|
|
2670
|
+
} | undefined;
|
|
2671
|
+
}, {
|
|
2672
|
+
sku: string;
|
|
2673
|
+
title: string;
|
|
2674
|
+
category: string;
|
|
2675
|
+
price: {
|
|
2676
|
+
amount: number;
|
|
2677
|
+
currency: string;
|
|
2678
|
+
};
|
|
2679
|
+
media: string[];
|
|
2680
|
+
variants: {
|
|
2681
|
+
sku: string;
|
|
2682
|
+
attributes: Record<string, string>;
|
|
2683
|
+
in_stock: boolean;
|
|
2684
|
+
image?: string | undefined;
|
|
2685
|
+
available?: number | undefined;
|
|
2686
|
+
}[];
|
|
2687
|
+
compare_at?: {
|
|
2688
|
+
amount: number;
|
|
2689
|
+
currency: string;
|
|
2690
|
+
} | undefined;
|
|
2691
|
+
description?: string | undefined;
|
|
2692
|
+
badges?: string[] | undefined;
|
|
2693
|
+
promo?: {
|
|
2694
|
+
message: string;
|
|
2695
|
+
ends_at?: string | undefined;
|
|
2696
|
+
} | undefined;
|
|
2697
|
+
size_guide?: {
|
|
2698
|
+
title: string;
|
|
2699
|
+
columns?: string[] | undefined;
|
|
2700
|
+
rows?: string[][] | undefined;
|
|
2701
|
+
notes?: string[] | undefined;
|
|
2702
|
+
} | undefined;
|
|
2703
|
+
}>;
|
|
2704
|
+
|
|
2705
|
+
declare type Product = z.infer<typeof Product>;
|
|
2706
|
+
|
|
2707
|
+
/**
|
|
2708
|
+
* 4.3 PDP interactive surface, aligned to the DAZN mobile PDP design. Sections render
|
|
2709
|
+
* from server-authoritative contract data: badges, title + price, promo nudge, description
|
|
2710
|
+
* accordion, size + colour selectors, and static shipping/returns. Size and colour are
|
|
2711
|
+
* independent dimensions; the SKU is resolved from both. A required selection must be made
|
|
2712
|
+
* before add-to-cart — tapping without one scrolls to the first missing selector (and flips
|
|
2713
|
+
* the size error). Add-to-cart calls the engine through the session so a 401 silently
|
|
2714
|
+
* re-handshakes.
|
|
2715
|
+
*/
|
|
2716
|
+
export declare function ProductDetail({ product, inventory, recommendations }: ProductDetailProps): JSX.Element;
|
|
2717
|
+
|
|
2718
|
+
export declare interface ProductDetailProps {
|
|
2719
|
+
product: Product;
|
|
2720
|
+
inventory: Inventory | null;
|
|
2721
|
+
/** "You may like it" rail — related products from the engine. Empty = rail hidden. */
|
|
2722
|
+
recommendations?: Product[];
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
/**
|
|
2726
|
+
* PDP "Product details" accordion (mobile/pdp/details). Collapsed clamps the body to
|
|
2727
|
+
* three lines with a more/less toggle. Only renders when the catalogue provides a
|
|
2728
|
+
* description — no placeholder copy is invented.
|
|
2729
|
+
*/
|
|
2730
|
+
export declare function ProductDetails({ description }: ProductDetailsProps): JSX.Element | null;
|
|
2731
|
+
|
|
2732
|
+
export declare interface ProductDetailsProps {
|
|
2733
|
+
/** Long-form description (Product.description). The block is not rendered when empty. */
|
|
2734
|
+
description: string;
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
/**
|
|
2738
|
+
* PLP grid with cursor-based infinite scroll and a chip FilterBar.
|
|
2739
|
+
*
|
|
2740
|
+
* `initialProducts` is the host's first page — server-rendered where the host can, or
|
|
2741
|
+
* simply fetched before mount. Subsequent pages come from the provider's client, so the
|
|
2742
|
+
* grid inherits the host's session and market without being told about either.
|
|
2743
|
+
*
|
|
2744
|
+
* Filtering is a client-side view over what has loaded; it deliberately does not refetch,
|
|
2745
|
+
* because a filter that resets an infinite scroll loses the fan's place.
|
|
2746
|
+
*/
|
|
2747
|
+
export declare function ProductGrid({ category, initialProducts, initialCursor, filters, }: ProductGridProps): JSX.Element;
|
|
2748
|
+
|
|
2749
|
+
export declare interface ProductGridProps {
|
|
2750
|
+
category: string;
|
|
2751
|
+
initialProducts: Product[];
|
|
2752
|
+
initialCursor: string | null;
|
|
2753
|
+
filters?: FilterOption[];
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
/**
|
|
2757
|
+
* Optional PDP promo nudge (Figma `mobile/pdp/promo`). DISPLAY ONLY — the `message` and
|
|
2758
|
+
* the `ends_at` countdown are presentational; the actual offer is applied by DAZN at
|
|
2759
|
+
* checkout. The storefront never computes a discount from this.
|
|
2760
|
+
*/
|
|
2761
|
+
declare const ProductPromo: z.ZodObject<{
|
|
2762
|
+
message: z.ZodString;
|
|
2763
|
+
/** ISO-8601 end time that drives the countdown; absent = show the message, no timer. */
|
|
2764
|
+
ends_at: z.ZodOptional<z.ZodString>;
|
|
2765
|
+
}, "strip", z.ZodTypeAny, {
|
|
2766
|
+
message: string;
|
|
2767
|
+
ends_at?: string | undefined;
|
|
2768
|
+
}, {
|
|
2769
|
+
message: string;
|
|
2770
|
+
ends_at?: string | undefined;
|
|
2771
|
+
}>;
|
|
2772
|
+
|
|
2773
|
+
declare type ProductPromo = z.infer<typeof ProductPromo>;
|
|
2774
|
+
|
|
2775
|
+
/**
|
|
2776
|
+
* Product card (Figma `mobile/product-card`) — the shared rail/grid card. Media is a
|
|
2777
|
+
* 165:220 image (radius-6) with a TOP PICK flag top-left and a green sale badge
|
|
2778
|
+
* bottom-left; below sit the title (2-line clamp) and price with a struck was-price.
|
|
2779
|
+
*/
|
|
2780
|
+
export declare function ProductTile({ product, priority }: ProductTileProps): JSX.Element;
|
|
2781
|
+
|
|
2782
|
+
export declare interface ProductTileProps {
|
|
2783
|
+
product: Product;
|
|
2784
|
+
/** Priority-load the image (above-the-fold tiles). */
|
|
2785
|
+
priority?: boolean;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
/** `GET /v1/catalog/products` — a single purchasable variant of a product. */
|
|
2789
|
+
declare const ProductVariant: z.ZodObject<{
|
|
2790
|
+
sku: z.ZodString;
|
|
2791
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
2792
|
+
in_stock: z.ZodBoolean;
|
|
2793
|
+
/**
|
|
2794
|
+
* Optional per-variant image — e.g. the colour swatch/tile shown in the PDP colour
|
|
2795
|
+
* selector. Absent = fall back to the product's `media`.
|
|
2796
|
+
*/
|
|
2797
|
+
image: z.ZodOptional<z.ZodString>;
|
|
2798
|
+
/**
|
|
2799
|
+
* Optional per-variant stock count, used to show "N left" low-stock badges. Display
|
|
2800
|
+
* only — the authoritative check runs at checkout. Absent = show no count.
|
|
2801
|
+
*/
|
|
2802
|
+
available: z.ZodOptional<z.ZodNumber>;
|
|
2803
|
+
}, "strip", z.ZodTypeAny, {
|
|
2804
|
+
sku: string;
|
|
2805
|
+
attributes: Record<string, string>;
|
|
2806
|
+
in_stock: boolean;
|
|
2807
|
+
image?: string | undefined;
|
|
2808
|
+
available?: number | undefined;
|
|
2809
|
+
}, {
|
|
2810
|
+
sku: string;
|
|
2811
|
+
attributes: Record<string, string>;
|
|
2812
|
+
in_stock: boolean;
|
|
2813
|
+
image?: string | undefined;
|
|
2814
|
+
available?: number | undefined;
|
|
2815
|
+
}>;
|
|
2816
|
+
|
|
2817
|
+
declare type ProductVariant = z.infer<typeof ProductVariant>;
|
|
2818
|
+
|
|
2819
|
+
/**
|
|
2820
|
+
* A whole product-detail screen, data fetching included — a **level 1 view**.
|
|
2821
|
+
*
|
|
2822
|
+
* Fetches the product, then enriches it with inventory and recommendations. Those two are
|
|
2823
|
+
* deliberately best-effort: the PDP renders perfectly well without either, so a failure in
|
|
2824
|
+
* one must not fault the screen. `Promise.allSettled` rather than `all` is what makes that
|
|
2825
|
+
* true — with `all`, a recommendations outage would blank a page the fan could otherwise
|
|
2826
|
+
* have bought from.
|
|
2827
|
+
*
|
|
2828
|
+
* `sku_not_found` is separated from the generic error path because it is not a fault: the
|
|
2829
|
+
* product is gone, and "this is no longer available" is the honest thing to say. An error
|
|
2830
|
+
* card inviting a retry would be a lie.
|
|
2831
|
+
*/
|
|
2832
|
+
export declare function ProductView({ sku, initialData, showRecommendations, onNotFound, }: ProductViewProps): JSX.Element;
|
|
2833
|
+
|
|
2834
|
+
export declare interface ProductViewProps {
|
|
2835
|
+
/** The product's SKU. */
|
|
2836
|
+
sku: string;
|
|
2837
|
+
/**
|
|
2838
|
+
* An already-fetched product, from a host that server-rendered this screen. Painted on
|
|
2839
|
+
* the first frame and revalidated in the background.
|
|
2840
|
+
*
|
|
2841
|
+
* `inventory` is optional even here: it is the one field guaranteed to be stale by the
|
|
2842
|
+
* time a server-rendered page is interactive, so a host is not obliged to have fetched
|
|
2843
|
+
* it, and the revalidation replaces it either way.
|
|
2844
|
+
*/
|
|
2845
|
+
initialData?: {
|
|
2846
|
+
product: Product;
|
|
2847
|
+
inventory?: Inventory | null;
|
|
2848
|
+
};
|
|
2849
|
+
/** The "you may like it" rail. Default `true`. */
|
|
2850
|
+
showRecommendations?: boolean;
|
|
2851
|
+
/**
|
|
2852
|
+
* Replaces the built-in empty state when the SKU does not exist. Only for
|
|
2853
|
+
* `sku_not_found` — a genuine fault still renders the recoverable error state.
|
|
2854
|
+
*/
|
|
2855
|
+
onNotFound?: () => ReactNode;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
/**
|
|
2859
|
+
* PDP promo banner (Figma `mobile/pdp/promo`). DISPLAY ONLY — the message and countdown
|
|
2860
|
+
* are a merchandising nudge; the actual offer is applied by DAZN at checkout. The timer
|
|
2861
|
+
* is mounted client-side (starts null to match SSR, so there is no hydration mismatch)
|
|
2862
|
+
* and hides itself once elapsed or when the promo has no `ends_at`.
|
|
2863
|
+
*/
|
|
2864
|
+
export declare function PromoBanner({ promo }: {
|
|
2865
|
+
promo: ProductPromo;
|
|
2866
|
+
}): JSX.Element;
|
|
2867
|
+
|
|
2868
|
+
/**
|
|
2869
|
+
* Quantity stepper. At qty=1 the decrement turns into a delete affordance; the
|
|
2870
|
+
* increment is capped at max stock. Values are never used to compute money.
|
|
2871
|
+
*
|
|
2872
|
+
* When the cap is reached the reason is stated inline rather than the control simply
|
|
2873
|
+
* going dead — a disabled `+` with no explanation reads as a bug, and the fan cannot
|
|
2874
|
+
* tell "we have no more" from "this is broken".
|
|
2875
|
+
*/
|
|
2876
|
+
export declare function QtyStepper({ qty, max, onChange, onRemove, disabled }: QtyStepperProps): JSX.Element;
|
|
2877
|
+
|
|
2878
|
+
export declare interface QtyStepperProps {
|
|
2879
|
+
qty: number;
|
|
2880
|
+
/** Max selectable quantity (capped at available stock). */
|
|
2881
|
+
max?: number;
|
|
2882
|
+
onChange: (qty: number) => void;
|
|
2883
|
+
/** Called when decrementing from 1 (the decrement becomes a delete). */
|
|
2884
|
+
onRemove?: () => void;
|
|
2885
|
+
disabled?: boolean;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
/**
|
|
2889
|
+
* Horizontally scrollable product rail. Renders NOTHING when there are no products
|
|
2890
|
+
* (empty rails are hidden entirely, per spec).
|
|
2891
|
+
*/
|
|
2892
|
+
export declare function RailCarousel({ title, products, seeAllTo, inset }: RailCarouselProps): JSX.Element | null;
|
|
2893
|
+
|
|
2894
|
+
export declare interface RailCarouselProps {
|
|
2895
|
+
title: string;
|
|
2896
|
+
products: Product[];
|
|
2897
|
+
/**
|
|
2898
|
+
* "See all" destination, as a storefront route.
|
|
2899
|
+
*
|
|
2900
|
+
* Was a raw `href` on a plain anchor. In a mounted component that hard-navigates the
|
|
2901
|
+
* HOST's page out from under the fan — the exact thing the navigation seam exists to
|
|
2902
|
+
* prevent — so it now goes through {@link ShopLink} and the host's router.
|
|
2903
|
+
*/
|
|
2904
|
+
seeAllTo?: ShopRoute;
|
|
2905
|
+
/**
|
|
2906
|
+
* Inset variant: the rail lives inside an already-padded column (the PDP content
|
|
2907
|
+
* block) rather than bleeding edge-to-edge like the home rails, so it adds no side
|
|
2908
|
+
* padding of its own and uses fixed-width cards.
|
|
2909
|
+
*/
|
|
2910
|
+
inset?: boolean;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
/**
|
|
2914
|
+
* Resolve an asset path against the host-configured asset base.
|
|
2915
|
+
*
|
|
2916
|
+
* Mounted in DAZN's page, a root-relative `src` resolves against DAZN's origin — so the
|
|
2917
|
+
* SDK's own design imagery is requested from a server that has never heard of it. This
|
|
2918
|
+
* is the one rewrite that fixes it, and it is deliberately narrow.
|
|
2919
|
+
*
|
|
2920
|
+
* Left alone, always:
|
|
2921
|
+
* - **absolute URLs** (`https://…`, `data:`, `blob:`) — catalogue media arrives from the
|
|
2922
|
+
* API already absolute, and rewriting it would break the images that currently work.
|
|
2923
|
+
* - **protocol-relative** (`//cdn…`) — already origin-bearing.
|
|
2924
|
+
* - **relative** (`./x`, `x.jpg`) — resolves against the document, which is the host's
|
|
2925
|
+
* business, not ours.
|
|
2926
|
+
*
|
|
2927
|
+
* With no base configured this is the identity function, so our own app — which serves
|
|
2928
|
+
* these paths itself — needs no configuration and keeps working unchanged.
|
|
2929
|
+
*/
|
|
2930
|
+
export declare function resolveAssetUrl(src: string, base?: string): string;
|
|
2931
|
+
|
|
2932
|
+
/**
|
|
2933
|
+
* Resolve the variant SKU for the selected size/colour, considering only the dimensions
|
|
2934
|
+
* the product actually has. Returns null until every present dimension is chosen.
|
|
2935
|
+
*/
|
|
2936
|
+
export declare function resolveSku(variants: ProductVariant[], size: string | null, colour: string | null): string | null;
|
|
2937
|
+
|
|
2938
|
+
declare interface Review {
|
|
2939
|
+
quote: string;
|
|
2940
|
+
author: string;
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
/** Testimonial carousel — swipeable light cards with dot pagination. */
|
|
2944
|
+
export declare function ReviewCarousel({ quotes }: {
|
|
2945
|
+
quotes: Review[];
|
|
2946
|
+
}): JSX.Element;
|
|
2947
|
+
|
|
2948
|
+
/**
|
|
2949
|
+
* The single green sale/discount badge (Figma `mobile/badge/sale`): success-soft
|
|
2950
|
+
* surface, success text, a discount-tag icon and `-NN%`. One look everywhere — the PDP
|
|
2951
|
+
* price row and the product-card image overlay both use it.
|
|
2952
|
+
*/
|
|
2953
|
+
export declare function SaleBadge({ pct }: SaleBadgeProps): JSX.Element;
|
|
2954
|
+
|
|
2955
|
+
export declare interface SaleBadgeProps {
|
|
2956
|
+
/** Whole-number discount percentage, e.g. 30 → "-30%". */
|
|
2957
|
+
pct: number;
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
/**
|
|
2961
|
+
* What a whole screen looks like while its data is in flight.
|
|
2962
|
+
*
|
|
2963
|
+
* This replaces `<Skeleton />` on its own, which was not a loading state at all: with no
|
|
2964
|
+
* width, height or content, a `display: block` span computes to **zero pixels tall**. The
|
|
2965
|
+
* views returned it, it painted nothing, and whatever the host had behind us showed
|
|
2966
|
+
* through — which in DAZN's dark app is a black screen between tapping a link and the
|
|
2967
|
+
* product arriving. It looked like a navigation bug and was a CSS one.
|
|
2968
|
+
*
|
|
2969
|
+
* Two things follow from that and are the whole point of this component. It has real
|
|
2970
|
+
* dimensions, so there is something on screen for the entire wait. And it is a
|
|
2971
|
+
* `ShopSurface`, so the wait is painted in the palette of the screen you are waiting for
|
|
2972
|
+
* — a light PDP that resolves out of a light wait, rather than out of the host's black.
|
|
2973
|
+
*
|
|
2974
|
+
* The blocks are laid out roughly where the real content lands, so the screen settles into
|
|
2975
|
+
* place instead of rearranging itself once the data is in.
|
|
2976
|
+
*/
|
|
2977
|
+
export declare function ScreenSkeleton({ tone, shape }: ScreenSkeletonProps): JSX.Element;
|
|
2978
|
+
|
|
2979
|
+
export declare interface ScreenSkeletonProps {
|
|
2980
|
+
/** The palette of the screen being waited for, so the wait is not a different colour. */
|
|
2981
|
+
tone: ShopTone;
|
|
2982
|
+
/** `media` leads with an image block (PDP, category). `list` is rows (cart, search). */
|
|
2983
|
+
shape?: 'media' | 'list';
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
/** Request: the single-use DAZN identity JWT to exchange. */
|
|
2987
|
+
declare const SessionExchangeRequest: z.ZodObject<{
|
|
2988
|
+
token: z.ZodString;
|
|
2989
|
+
}, "strict", z.ZodTypeAny, {
|
|
2990
|
+
token: string;
|
|
2991
|
+
}, {
|
|
2992
|
+
token: string;
|
|
2993
|
+
}>;
|
|
2994
|
+
|
|
2995
|
+
declare type SessionExchangeRequest = z.infer<typeof SessionExchangeRequest>;
|
|
2996
|
+
|
|
2997
|
+
/**
|
|
2998
|
+
* PDP shipping & returns block (mobile/pdp/shipping-info). Static informational copy —
|
|
2999
|
+
* no free-shipping threshold (per design). The shopper picks the delivery tier at
|
|
3000
|
+
* DAZN's checkout; this only signals that the options exist. Returns window is fixed.
|
|
3001
|
+
*/
|
|
3002
|
+
export declare function ShippingInfo(): JSX.Element;
|
|
3003
|
+
|
|
3004
|
+
declare interface ShopContextValue {
|
|
3005
|
+
api: DaznClient;
|
|
3006
|
+
market?: Market;
|
|
3007
|
+
theme: 'light' | 'dark' | 'auto';
|
|
3008
|
+
notifications: 'internal' | 'host';
|
|
3009
|
+
locale?: string;
|
|
3010
|
+
assetBaseUrl?: string;
|
|
3011
|
+
emit: OnEvent;
|
|
3012
|
+
/** Listen to the same event stream the host sees. Returns an unsubscribe. */
|
|
3013
|
+
subscribe: (listener: OnEvent) => () => void;
|
|
3014
|
+
navigate: (to: ShopRoute) => void;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
export declare type ShopEvent = {
|
|
3018
|
+
type: 'ready';
|
|
3019
|
+
} | {
|
|
3020
|
+
type: 'navigate';
|
|
3021
|
+
to: ShopRoute;
|
|
3022
|
+
} | {
|
|
3023
|
+
type: 'product_viewed';
|
|
3024
|
+
sku: string;
|
|
3025
|
+
} | {
|
|
3026
|
+
type: 'cart_updated';
|
|
3027
|
+
itemCount: number;
|
|
3028
|
+
subtotal: Money;
|
|
3029
|
+
} | {
|
|
3030
|
+
type: 'add_to_cart';
|
|
3031
|
+
sku: string;
|
|
3032
|
+
qty: number;
|
|
3033
|
+
} | {
|
|
3034
|
+
type: 'remove_from_cart';
|
|
3035
|
+
sku: string;
|
|
3036
|
+
qty: number;
|
|
3037
|
+
} | {
|
|
3038
|
+
type: 'checkout_handoff';
|
|
3039
|
+
cartId: string;
|
|
3040
|
+
url?: string;
|
|
3041
|
+
} | {
|
|
3042
|
+
type: 'session_required';
|
|
3043
|
+
action: string;
|
|
3044
|
+
} | {
|
|
3045
|
+
type: 'session_expired';
|
|
3046
|
+
} | {
|
|
3047
|
+
type: 'notification';
|
|
3048
|
+
tone: 'neutral' | 'success' | 'critical';
|
|
3049
|
+
message: string;
|
|
3050
|
+
} | {
|
|
3051
|
+
type: 'error';
|
|
3052
|
+
code: ErrorCode | undefined;
|
|
3053
|
+
scope: string;
|
|
3054
|
+
};
|
|
3055
|
+
|
|
3056
|
+
/**
|
|
3057
|
+
* The 56px shop header from Figma `mobile/header` (context=shop): back chevron on the
|
|
3058
|
+
* left, cart and overflow on the right.
|
|
3059
|
+
*
|
|
3060
|
+
* **Optional, and off by default in every view.** In the real embed the DAZN app provides
|
|
3061
|
+
* this chrome — it is their back button and their menu — which is why nothing in the SDK
|
|
3062
|
+
* renders it for you. It is exported because DAZN's team asked for it after finding the
|
|
3063
|
+
* bar they see in our own storefront was not available to them: rebuilding it is a waste
|
|
3064
|
+
* if the design is ours, and a mismatch if only half of it is.
|
|
3065
|
+
*
|
|
3066
|
+
* The cart count is real, never fabricated: `useCartCount` returns `null` until it knows,
|
|
3067
|
+
* and the badge stays hidden for both `null` and `0` — an empty cart wearing a "0" is
|
|
3068
|
+
* noise, and a guessed number is worse.
|
|
3069
|
+
*
|
|
3070
|
+
* Mount it inside `DaznShopProvider`, and inside `CartSheet` if `showCart` is on, since
|
|
3071
|
+
* the trigger opens that sheet.
|
|
3072
|
+
*/
|
|
3073
|
+
export declare function ShopHeader({ onBack, onMenu, showCart, backLabel, menuLabel, className, }: ShopHeaderProps): JSX.Element;
|
|
3074
|
+
|
|
3075
|
+
export declare interface ShopHeaderProps {
|
|
3076
|
+
/**
|
|
3077
|
+
* Back control. Omit it and the slot stays empty but reserved — a host whose own
|
|
3078
|
+
* navigation already provides a back affordance should not get a second one.
|
|
3079
|
+
*/
|
|
3080
|
+
onBack?: () => void;
|
|
3081
|
+
/** Overflow menu. Omitted by default: a control that opens nothing is worse than none. */
|
|
3082
|
+
onMenu?: () => void;
|
|
3083
|
+
/** The cart icon and its live count. */
|
|
3084
|
+
showCart?: boolean;
|
|
3085
|
+
/** Accessible name for the back control, for a host that is not in English. */
|
|
3086
|
+
backLabel?: string;
|
|
3087
|
+
menuLabel?: string;
|
|
3088
|
+
className?: string;
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
/**
|
|
3092
|
+
* The replacement for `next/image` inside the SDK.
|
|
3093
|
+
*
|
|
3094
|
+
* `next/image` cannot come with us: it needs the Next runtime and its optimiser
|
|
3095
|
+
* endpoint, and the SDK runs in DAZN's bundle where neither exists. So this is a plain
|
|
3096
|
+
* `<img>` with the platform features that actually carry the weight — native lazy
|
|
3097
|
+
* loading, `decoding="async"`, `sizes`, and `fetchpriority` for the LCP image.
|
|
3098
|
+
*
|
|
3099
|
+
* **What is genuinely lost is on-the-fly resizing and format negotiation** (AVIF/WebP
|
|
3100
|
+
* per browser). That is not something a component can polyfill — it is a serving
|
|
3101
|
+
* concern. Whoever serves the catalogue media should emit `srcset`-ready derivatives
|
|
3102
|
+
* and modern formats; until then, this renders the source as given rather than
|
|
3103
|
+
* pretending to optimise it.
|
|
3104
|
+
*
|
|
3105
|
+
* The one rewrite it does perform is rebasing **root-relative** paths onto the host's
|
|
3106
|
+
* `assetBaseUrl` — see {@link resolveAssetUrl}. Without it the SDK's own design imagery
|
|
3107
|
+
* is requested from the host's origin, which does not serve it.
|
|
3108
|
+
*/
|
|
3109
|
+
export declare function ShopImage({ src, alt, fill, width, height, sizes, className, style, priority, }: ShopImageProps): JSX.Element;
|
|
3110
|
+
|
|
3111
|
+
export declare interface ShopImageProps {
|
|
3112
|
+
src: string;
|
|
3113
|
+
alt: string;
|
|
3114
|
+
/**
|
|
3115
|
+
* Fill the positioned ancestor, as `next/image`'s `fill` does. The parent must be
|
|
3116
|
+
* `position: relative` (or absolute/fixed) — same requirement, same reason.
|
|
3117
|
+
*/
|
|
3118
|
+
fill?: boolean;
|
|
3119
|
+
width?: number;
|
|
3120
|
+
height?: number;
|
|
3121
|
+
sizes?: string;
|
|
3122
|
+
className?: string;
|
|
3123
|
+
style?: CSSProperties;
|
|
3124
|
+
/**
|
|
3125
|
+
* Above-the-fold. Sets `fetchpriority="high"` and opts out of lazy loading.
|
|
3126
|
+
*
|
|
3127
|
+
* Named `priority` to match the prop the components already pass, so porting a
|
|
3128
|
+
* component is an import change rather than a rename at every call site.
|
|
3129
|
+
*/
|
|
3130
|
+
priority?: boolean;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
/**
|
|
3134
|
+
* The replacement for `next/link` inside the SDK. **We never touch the address bar** —
|
|
3135
|
+
* this calls the host's `navigate` and lets the host decide what a route change means.
|
|
3136
|
+
*
|
|
3137
|
+
* It still renders a real `<a href>` rather than a `<div onClick>`, because that is what
|
|
3138
|
+
* gives keyboard activation, focus order, a middle-click that opens a new tab, and a
|
|
3139
|
+
* screen-reader announcement of "link" — none of which a click handler provides. The
|
|
3140
|
+
* default is then prevented so the host router, not the browser, performs the
|
|
3141
|
+
* navigation.
|
|
3142
|
+
*
|
|
3143
|
+
* Modified clicks (⌘/ctrl/shift/alt, or a non-primary button) are deliberately left
|
|
3144
|
+
* alone: a fan holding ⌘ is asking the *browser* for a new tab, and intercepting that
|
|
3145
|
+
* is the single most irritating thing a client-side router can do.
|
|
3146
|
+
*/
|
|
3147
|
+
export declare function ShopLink({ to, children, ...rest }: ShopLinkProps): JSX.Element;
|
|
3148
|
+
|
|
3149
|
+
export declare interface ShopLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'onClick'> {
|
|
3150
|
+
to: ShopRoute;
|
|
3151
|
+
children: ReactNode;
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
/**
|
|
3155
|
+
* The public surface DAZN programs against. Everything here is a **contract**, not an
|
|
3156
|
+
* implementation detail — see the versioning policy in the component SDK specification.
|
|
3157
|
+
*/
|
|
3158
|
+
/** A storefront-relative identifier. NOT a URL we expect to exist on the host. */
|
|
3159
|
+
export declare type ShopRoute = '/' | `/category/${string}` | `/product/${string}` | '/cart';
|
|
3160
|
+
|
|
3161
|
+
/**
|
|
3162
|
+
* Request: the shopping session id and the one-time code, exactly as they arrive on the
|
|
3163
|
+
* redirect URL's `session` and `code` query parameters.
|
|
3164
|
+
*
|
|
3165
|
+
* Named for what DAZN's redirect calls them rather than for what `validate-session` calls
|
|
3166
|
+
* them — DAZN's own two endpoints disagree (`session` on the way in, `sessionId` on the
|
|
3167
|
+
* way out), and the storefront only ever sees the redirect.
|
|
3168
|
+
*/
|
|
3169
|
+
declare const ShopSessionRequest: z.ZodObject<{
|
|
3170
|
+
session: z.ZodString;
|
|
3171
|
+
code: z.ZodString;
|
|
3172
|
+
}, "strict", z.ZodTypeAny, {
|
|
3173
|
+
code: string;
|
|
3174
|
+
session: string;
|
|
3175
|
+
}, {
|
|
3176
|
+
code: string;
|
|
3177
|
+
session: string;
|
|
3178
|
+
}>;
|
|
3179
|
+
|
|
3180
|
+
declare type ShopSessionRequest = z.infer<typeof ShopSessionRequest>;
|
|
3181
|
+
|
|
3182
|
+
/**
|
|
3183
|
+
* The root element of a storefront screen: base typography, element defaults, and a
|
|
3184
|
+
* palette.
|
|
3185
|
+
*
|
|
3186
|
+
* Both halves of this were previously the host's job by accident. The typography lived in
|
|
3187
|
+
* our Next app's `globals.css` on `<body>`, and the dark palette was switched on by a
|
|
3188
|
+
* route-watching `ThemeController` that set `data-theme` on `<html>`. Neither travelled
|
|
3189
|
+
* with the components into this package, so a host mounting `<CategoryView>` got a screen
|
|
3190
|
+
* designed dark rendered on a light palette in the host's own body font — every component
|
|
3191
|
+
* individually correct, the screen as a whole wrong.
|
|
3192
|
+
*
|
|
3193
|
+
* **`tone` is the only input, and the provider's `theme` no longer overrides it.**
|
|
3194
|
+
*
|
|
3195
|
+
* It used to. The reasoning was that a host wanting one palette throughout should be able
|
|
3196
|
+
* to say so — but the storefront has no light and dark of the same screen to choose
|
|
3197
|
+
* between. Each screen is drawn once, in one palette: the shop entry and category pages
|
|
3198
|
+
* are dark, the PDP and cart are light. There is no light version of the dark hero, so
|
|
3199
|
+
* `theme="light"` did not render one; it repainted the tokens underneath a screen whose
|
|
3200
|
+
* artwork, photography and contrast were all built for the other palette.
|
|
3201
|
+
*
|
|
3202
|
+
* DAZN set `theme="light"` and got a dark hero photograph with near-black button text on
|
|
3203
|
+
* it — a screen that exists in no design, produced by an option that sounded reasonable.
|
|
3204
|
+
* The prop is still accepted so that setting it does not break a host's build, and it
|
|
3205
|
+
* still reaches the context for hosts reading it, but it no longer decides what a screen
|
|
3206
|
+
* looks like. Black screens stay black; light screens stay light.
|
|
3207
|
+
*
|
|
3208
|
+
* `data-shop-theme` is a real attribute rather than a hashed module class so the token
|
|
3209
|
+
* remap can live once in `theme.css` beside the values it overrides, and so a host can
|
|
3210
|
+
* scope our palette onto a subtree of their own if they ever need to.
|
|
3211
|
+
*
|
|
3212
|
+
* **Anything that paints its own background must be one of these, not merely look like
|
|
3213
|
+
* one.** Setting `background: var(--color-surface-default)` on an element without also
|
|
3214
|
+
* setting `color` leaves the text inheriting from the host's `<body>` — so a light sheet
|
|
3215
|
+
* in a dark host app renders white text on white. That is what happened to the cart
|
|
3216
|
+
* bottom sheet, and it was invisible in our own app because our `<body>` happened to
|
|
3217
|
+
* carry a matching colour. Hence the forwarded props and ref: a surface has to be usable
|
|
3218
|
+
* **as** a dialog or a layout element, or callers will reach for the CSS shortcut again.
|
|
3219
|
+
*/
|
|
3220
|
+
export declare const ShopSurface: ForwardRefExoticComponent<ShopSurfaceProps & RefAttributes<HTMLDivElement>>;
|
|
3221
|
+
|
|
3222
|
+
export declare interface ShopSurfaceProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
3223
|
+
/**
|
|
3224
|
+
* The palette this screen is **designed** in — dark for the shop entry and category,
|
|
3225
|
+
* light for the PDP and cart. This is the whole answer: nothing overrides it.
|
|
3226
|
+
*/
|
|
3227
|
+
tone: ShopTone;
|
|
3228
|
+
/**
|
|
3229
|
+
* Paint the full height of the viewport, not just the height of the content.
|
|
3230
|
+
*
|
|
3231
|
+
* On for a screen, off for anything mounted inside one. A screen that stops at its
|
|
3232
|
+
* content leaves the host's own background showing below it, and the host's background
|
|
3233
|
+
* is not ours to predict — DAZN's app is dark, so a short light cart rendered as a white
|
|
3234
|
+
* card floating on black.
|
|
3235
|
+
*/
|
|
3236
|
+
fill?: boolean;
|
|
3237
|
+
children: ReactNode;
|
|
3238
|
+
className?: string;
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
export declare type ShopTone = 'dark' | 'light';
|
|
3242
|
+
|
|
3243
|
+
/**
|
|
3244
|
+
* Category-wide sizing help, shown in the PDP "Size guide" overlay.
|
|
3245
|
+
*
|
|
3246
|
+
* `rows` is a measurement table (one row per size). It is **optional and may be empty**:
|
|
3247
|
+
* DAZN has not supplied a measurement chart, and garment measurements are the kind of
|
|
3248
|
+
* value that must never be guessed — a fabricated chest measurement sends a fan the
|
|
3249
|
+
* wrong size. A guide with `notes` but no `rows` is valid and renders as fit guidance
|
|
3250
|
+
* alone; a consumer must not synthesise the missing numbers.
|
|
3251
|
+
*
|
|
3252
|
+
* The array fields are `.optional()` rather than `.default([])` deliberately. A zod
|
|
3253
|
+
* default makes the schema's input and output types diverge, and this package ships two
|
|
3254
|
+
* declaration copies (`dist/` for the root export, `client/` for the typed client) — so
|
|
3255
|
+
* a divergent field makes the two `Product` types structurally unequal and every
|
|
3256
|
+
* consumer that mixes the entry points fails to compile.
|
|
3257
|
+
*/
|
|
3258
|
+
declare const SizeGuide: z.ZodObject<{
|
|
3259
|
+
title: z.ZodString;
|
|
3260
|
+
/** Column headings for `rows`, e.g. `['Size', 'Chest (cm)', 'Length (cm)']`. */
|
|
3261
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
3262
|
+
/** One row per size; each row's length matches `columns`. */
|
|
3263
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>;
|
|
3264
|
+
/** Free-text fit guidance, e.g. "Relaxed, slightly oversized fit". */
|
|
3265
|
+
notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
3266
|
+
}, "strip", z.ZodTypeAny, {
|
|
3267
|
+
title: string;
|
|
3268
|
+
columns?: string[] | undefined;
|
|
3269
|
+
rows?: string[][] | undefined;
|
|
3270
|
+
notes?: string[] | undefined;
|
|
3271
|
+
}, {
|
|
3272
|
+
title: string;
|
|
3273
|
+
columns?: string[] | undefined;
|
|
3274
|
+
rows?: string[][] | undefined;
|
|
3275
|
+
notes?: string[] | undefined;
|
|
3276
|
+
}>;
|
|
3277
|
+
|
|
3278
|
+
declare type SizeGuide = z.infer<typeof SizeGuide>;
|
|
3279
|
+
|
|
3280
|
+
/**
|
|
3281
|
+
* The category-wide size-guide overlay (28 Jul kickoff: "a category-wide size guide
|
|
3282
|
+
* overlay pop-up"), presented as a bottom sheet to match the other PDP overlays.
|
|
3283
|
+
*
|
|
3284
|
+
* Renders whatever the guide actually carries. DAZN has supplied **no measurement
|
|
3285
|
+
* chart**, so today every guide has `notes` and no `rows`, and the table simply does not
|
|
3286
|
+
* appear — the component must never fabricate measurements to fill it, because a wrong
|
|
3287
|
+
* chest figure sends a fan the wrong size. When the real chart arrives the table renders
|
|
3288
|
+
* with no code change.
|
|
3289
|
+
*/
|
|
3290
|
+
export declare function SizeGuideSheet({ guide, onClose }: {
|
|
3291
|
+
guide: SizeGuide;
|
|
3292
|
+
onClose: () => void;
|
|
3293
|
+
}): JSX.Element;
|
|
3294
|
+
|
|
3295
|
+
export declare interface SizeOption {
|
|
3296
|
+
value: string;
|
|
3297
|
+
available: boolean;
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
/**
|
|
3301
|
+
* Distinct sizes. `available` is true when a variant of that size is in stock — filtered
|
|
3302
|
+
* to the given colour when one is selected (so picking a colour disables its unavailable
|
|
3303
|
+
* sizes).
|
|
3304
|
+
*/
|
|
3305
|
+
export declare function sizeOptions(variants: ProductVariant[], colour?: string | null): SizeOption[];
|
|
3306
|
+
|
|
3307
|
+
/**
|
|
3308
|
+
* PDP size selector — square chips (per the DAZN mobile/pdp/size component). A selection
|
|
3309
|
+
* is REQUIRED before add-to-cart; out-of-stock sizes are disabled. On Add without a
|
|
3310
|
+
* choice the parent flips `error` and the message + scroll target appear (buttons stay
|
|
3311
|
+
* enabled per the design note). Chips are a real radiogroup for keyboard + SR support.
|
|
3312
|
+
*/
|
|
3313
|
+
export declare function SizeSelector({ sizes, selected, onSelect, error, guide, }: SizeSelectorProps): JSX.Element;
|
|
3314
|
+
|
|
3315
|
+
export declare interface SizeSelectorProps {
|
|
3316
|
+
sizes: SizeOption[];
|
|
3317
|
+
/** Selected size value, or null when none is chosen yet. */
|
|
3318
|
+
selected: string | null;
|
|
3319
|
+
onSelect: (value: string) => void;
|
|
3320
|
+
/** Show the "select a size" error (set after Add-to-cart is tapped with no selection). */
|
|
3321
|
+
error?: boolean;
|
|
3322
|
+
/**
|
|
3323
|
+
* Category-wide sizing help. Absent = the control is not rendered at all, rather than
|
|
3324
|
+
* opening an empty overlay — until DAZN supplies a measurement chart most categories
|
|
3325
|
+
* have no guide, and a dead "Size guide" button is worse than none.
|
|
3326
|
+
*/
|
|
3327
|
+
guide?: SizeGuide;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
/**
|
|
3331
|
+
* Content placeholder for loading states. Prefer skeletons over full-page spinners.
|
|
3332
|
+
* Marked aria-hidden and respects reduced motion (shimmer disabled globally).
|
|
3333
|
+
*/
|
|
3334
|
+
export declare function Skeleton({ width, height, radius, className, circle }: SkeletonProps): JSX.Element;
|
|
3335
|
+
|
|
3336
|
+
export declare interface SkeletonProps {
|
|
3337
|
+
width?: number | string;
|
|
3338
|
+
height?: number | string;
|
|
3339
|
+
radius?: number | string;
|
|
3340
|
+
className?: string;
|
|
3341
|
+
/** Render as a circle (avatars/thumbnails). */
|
|
3342
|
+
circle?: boolean;
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
/** Order-status pill mapping the OrderStatus enum to a success/neutral/critical tone. */
|
|
3346
|
+
export declare function StatusBadge({ status }: StatusBadgeProps): JSX.Element;
|
|
3347
|
+
|
|
3348
|
+
export declare interface StatusBadgeProps {
|
|
3349
|
+
status: OrderStatus;
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
/** Bottom-pinned CTA bar, safe-area aware. Holds one or two primary actions. */
|
|
3353
|
+
export declare function StickyCtaBar({ children, info }: StickyCtaBarProps): JSX.Element;
|
|
3354
|
+
|
|
3355
|
+
export declare interface StickyCtaBarProps {
|
|
3356
|
+
children: ReactNode;
|
|
3357
|
+
/** Optional content shown above the actions (e.g. a total). */
|
|
3358
|
+
info?: ReactNode;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
export declare function swatchColour(name: string): string;
|
|
3362
|
+
|
|
3363
|
+
/** An optional inline action, e.g. the undo offered after removing a cart line. */
|
|
3364
|
+
export declare interface ToastAction {
|
|
3365
|
+
label: string;
|
|
3366
|
+
onAction: () => void;
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
declare interface ToastApi {
|
|
3370
|
+
show: (message: string, tone?: ToastTone, action?: ToastAction) => void;
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
export declare function ToastProvider({ children }: {
|
|
3374
|
+
children: ReactNode;
|
|
3375
|
+
}): JSX.Element;
|
|
3376
|
+
|
|
3377
|
+
export declare type ToastTone = 'neutral' | 'success' | 'critical';
|
|
3378
|
+
|
|
3379
|
+
/** Resolve UI copy + behaviour for an API error code (unknown → generic retriable). */
|
|
3380
|
+
export declare function treatmentFor(code: ErrorCode | undefined): ErrorTreatment;
|
|
3381
|
+
|
|
3382
|
+
/** `PATCH /v1/cart/items/{sku}` — change the quantity. */
|
|
3383
|
+
declare const UpdateCartItemRequest: z.ZodObject<{
|
|
3384
|
+
qty: z.ZodNumber;
|
|
3385
|
+
}, "strict", z.ZodTypeAny, {
|
|
3386
|
+
qty: number;
|
|
3387
|
+
}, {
|
|
3388
|
+
qty: number;
|
|
3389
|
+
}>;
|
|
3390
|
+
|
|
3391
|
+
declare type UpdateCartItemRequest = z.infer<typeof UpdateCartItemRequest>;
|
|
3392
|
+
|
|
3393
|
+
/**
|
|
3394
|
+
* Resolve an SDK asset path against the host's configured base. Identity outside a
|
|
3395
|
+
* provider, and identity when no base is configured — so our own app, which serves these
|
|
3396
|
+
* paths itself, needs no configuration.
|
|
3397
|
+
*/
|
|
3398
|
+
export declare function useAssetUrl(): (src: string) => string;
|
|
3399
|
+
|
|
3400
|
+
/**
|
|
3401
|
+
* How many units are in the cart, for a header badge.
|
|
3402
|
+
*
|
|
3403
|
+
* `null` means **not known** — no provider, no session yet, or the read failed — and is
|
|
3404
|
+
* deliberately distinct from `0`. A badge must render nothing for `null` and nothing for
|
|
3405
|
+
* `0`, but a caller that wants to say "your cart is empty" needs to tell the two apart,
|
|
3406
|
+
* and a hook that collapses them takes that away.
|
|
3407
|
+
*
|
|
3408
|
+
* Reads once on mount, then follows the `cart_updated` event rather than re-fetching. The
|
|
3409
|
+
* event carries the authoritative count from the mutation's own response, so the badge is
|
|
3410
|
+
* exact rather than optimistic, and adding from the PDP updates a badge in the header
|
|
3411
|
+
* without either component knowing the other exists.
|
|
3412
|
+
*
|
|
3413
|
+
* Counts **units, not lines** — three of one tee reads as 3. That is the convention the
|
|
3414
|
+
* `cart_updated` event already uses, and the two must not disagree.
|
|
3415
|
+
*/
|
|
3416
|
+
export declare function useCartCount(): number | null;
|
|
3417
|
+
|
|
3418
|
+
/**
|
|
3419
|
+
* Access the cart sheet. Returns `null` outside the provider rather than throwing, so a
|
|
3420
|
+
* component can offer "open the sheet" where it is available and fall back to the
|
|
3421
|
+
* `/cart` route where it is not — a header rendered in a test harness without the
|
|
3422
|
+
* provider should not crash the harness.
|
|
3423
|
+
*/
|
|
3424
|
+
export declare function useCartSheet(): CartSheetApi | null;
|
|
3425
|
+
|
|
3426
|
+
/**
|
|
3427
|
+
* Format server-authoritative {@link Money} for display. Formatting ONLY — never
|
|
3428
|
+
* computes totals, tax or discounts, and never does float arithmetic on money.
|
|
3429
|
+
*
|
|
3430
|
+
* The locale resolution order changed when this moved into the SDK, and the order
|
|
3431
|
+
* matters: **provider `locale` → provider `market.lang` → browser locale**.
|
|
3432
|
+
*
|
|
3433
|
+
* The app version read `navigator.language` alone. Mounted in a host page that is
|
|
3434
|
+
* already rendering a chosen market, the browser locale is the *wrong* answer — a fan
|
|
3435
|
+
* in Spain with an English-language device would see Spanish-market prices formatted to
|
|
3436
|
+
* en-GB conventions. The host tells us the market, so the host's answer wins.
|
|
3437
|
+
*
|
|
3438
|
+
* Works without a provider, falling back to the browser locale exactly as before.
|
|
3439
|
+
* Formatting a price needs no API client, and requiring one would make every component
|
|
3440
|
+
* that shows a price untestable in isolation.
|
|
3441
|
+
*/
|
|
3442
|
+
export declare function useMoney(defaultLocale?: string): {
|
|
3443
|
+
format: (money: Money, localeOverride?: string) => string;
|
|
3444
|
+
};
|
|
3445
|
+
|
|
3446
|
+
/**
|
|
3447
|
+
* Navigate to a storefront route, or `null` outside a provider.
|
|
3448
|
+
*
|
|
3449
|
+
* `null` rather than a throw or a no-op: it lets {@link ShopLink} tell the difference
|
|
3450
|
+
* between "the host wants to handle this" and "nobody is listening", and fall back to
|
|
3451
|
+
* being an ordinary link in the second case. A no-op would instead swallow the click and
|
|
3452
|
+
* leave a link that looks fine and does nothing.
|
|
3453
|
+
*/
|
|
3454
|
+
export declare function useNavigate(): ((to: ShopRoute) => void) | null;
|
|
3455
|
+
|
|
3456
|
+
/** The typed, schema-validating commerce client. Requires a provider. */
|
|
3457
|
+
export declare function useShopApi(): DaznClient;
|
|
3458
|
+
|
|
3459
|
+
/** Raise an event to the host. Silently dropped outside a provider. */
|
|
3460
|
+
export declare function useShopEvent(): OnEvent;
|
|
3461
|
+
|
|
3462
|
+
/**
|
|
3463
|
+
* Read the shop context, or `null` outside a provider.
|
|
3464
|
+
*
|
|
3465
|
+
* Not every capability needs a provider, and pretending otherwise makes the components
|
|
3466
|
+
* harder to test and harder to adopt piecemeal. Formatting a price and rendering a link
|
|
3467
|
+
* are both meaningful on their own; reaching the API is not. So the strictness is per
|
|
3468
|
+
* capability, below, rather than blanket.
|
|
3469
|
+
*/
|
|
3470
|
+
export declare function useShopOptional(): ShopContextValue | null;
|
|
3471
|
+
|
|
3472
|
+
/**
|
|
3473
|
+
* Access the toast API. Always returns something usable.
|
|
3474
|
+
*
|
|
3475
|
+
* This used to throw outside `<ToastProvider>`, and that was wrong on the project's own
|
|
3476
|
+
* principle that strictness is **per capability**: `useShopApi` throws because reaching
|
|
3477
|
+
* the API genuinely cannot degrade, but a toast can. Worse, it made the specification's
|
|
3478
|
+
* opening example — one `<DaznShopProvider>` with `<CartView>` beneath it — crash the
|
|
3479
|
+
* entire tree on mount, because `CartView` raises toasts and nothing had mounted a
|
|
3480
|
+
* provider. With no error boundary above it, a host following our own documentation got
|
|
3481
|
+
* a blank page.
|
|
3482
|
+
*
|
|
3483
|
+
* Without a `<ToastProvider>` the message still goes somewhere useful: it is emitted to
|
|
3484
|
+
* the host as a `notification` event if there is a shop provider to emit through, which
|
|
3485
|
+
* is exactly what `notifications: 'host'` does deliberately. Failing that it is dropped,
|
|
3486
|
+
* because a missing toast is a cosmetic loss and taking the storefront down over one is
|
|
3487
|
+
* not a trade worth making.
|
|
3488
|
+
*/
|
|
3489
|
+
export declare function useToast(): ToastApi;
|
|
3490
|
+
|
|
3491
|
+
/**
|
|
3492
|
+
* A one-line label for a chosen variant, e.g. `Black · M`.
|
|
3493
|
+
*
|
|
3494
|
+
* Composed here rather than server-side because the separator and the order are
|
|
3495
|
+
* presentation. Colour leads and size follows, matching how the PDP asks for them;
|
|
3496
|
+
* anything else the catalogue carries is appended in its own order rather than dropped,
|
|
3497
|
+
* so a product with a third dimension is not silently flattened.
|
|
3498
|
+
*
|
|
3499
|
+
* Returns `undefined` for an absent or empty map, so a caller can skip the element
|
|
3500
|
+
* entirely instead of rendering a blank line.
|
|
3501
|
+
*/
|
|
3502
|
+
export declare function variantLabel(attributes?: Record<string, string>): string | undefined;
|
|
3503
|
+
|
|
3504
|
+
/**
|
|
3505
|
+
* Variant picker. A selection is REQUIRED before add-to-cart; out-of-stock options are
|
|
3506
|
+
* disabled and cannot be chosen. When nothing is selected the parent blocks the CTA.
|
|
3507
|
+
*/
|
|
3508
|
+
export declare function VariantSelector({ variants, selectedSku, onSelect, labelAttribute, }: VariantSelectorProps): JSX.Element;
|
|
3509
|
+
|
|
3510
|
+
export declare interface VariantSelectorProps {
|
|
3511
|
+
variants: ProductVariant[];
|
|
3512
|
+
/** SKU of the selected variant, or null when none is chosen yet. */
|
|
3513
|
+
selectedSku: string | null;
|
|
3514
|
+
onSelect: (sku: string) => void;
|
|
3515
|
+
/** Attribute key to render as the option label (e.g. "size"). */
|
|
3516
|
+
labelAttribute?: string;
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
/** "Why shop with us" reassurance rows. */
|
|
3520
|
+
export declare function WhyUs({ items }: {
|
|
3521
|
+
items: WhyUsItem[];
|
|
3522
|
+
}): JSX.Element;
|
|
3523
|
+
|
|
3524
|
+
declare interface WhyUsItem {
|
|
3525
|
+
icon?: 'payment' | 'delivery' | 'returns';
|
|
3526
|
+
title: string;
|
|
3527
|
+
body: string;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
export { }
|