astryx-dracula 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BRAND.md +30 -18
- package/README.md +22 -11
- package/USAGE.md +29 -10
- package/astryx-dracula.js +19 -13
- package/astryx-dracula.variants.d.ts +15 -0
- package/package.json +59 -17
- package/shared/auth-chrome-config.ts +18 -0
- package/shared/auth-chrome.tsx +25 -0
- package/shared/auth-copy.ts +22 -0
- package/shared/chaptered-doc-config.ts +92 -0
- package/shared/chaptered-doc.tsx +332 -0
- package/shared/chart-hues.ts +27 -0
- package/shared/chart-labels.tsx +44 -0
- package/shared/chart-legend.tsx +100 -0
- package/shared/chart-panel-style.ts +13 -0
- package/shared/data-bar.tsx +160 -0
- package/shared/gallery-image.ts +20 -0
- package/shared/login-demo.ts +7 -0
- package/shared/metric-delta.tsx +36 -0
- package/shared/order-desk.tsx +273 -0
- package/shared/revenue-chart.tsx +174 -0
- package/shared/scene-castle.tsx +322 -0
- package/shared/scene-frame.tsx +88 -0
- package/shared/scene-hues.ts +49 -0
- package/shared/scene-tile.tsx +128 -0
- package/shared/settings-data.ts +125 -0
- package/shared/settings-rows.tsx +179 -0
- package/shared/sparkline.tsx +94 -0
- package/shared/sso-icons.tsx +30 -0
- package/templates/ai-chat-landing.tsx +67 -32
- package/templates/ai-chat.tsx +41 -20
- package/templates/blank.tsx +5 -2
- package/templates/centered-hero.tsx +15 -124
- package/templates/classic-gallery.tsx +21 -65
- package/templates/contact-form.tsx +62 -40
- package/templates/dashboard-portfolio.tsx +146 -227
- package/templates/dashboard.tsx +123 -121
- package/templates/detail-page.tsx +101 -95
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +85 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +33 -21
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +54 -74
- package/templates/editor.tsx +119 -89
- package/templates/file-explorer.tsx +133 -84
- package/templates/form-two-column.tsx +62 -124
- package/templates/gallery-hero.tsx +17 -171
- package/templates/ide.tsx +27 -21
- package/templates/incident-console.tsx +105 -69
- package/templates/kanban-board.tsx +100 -41
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +38 -41
- package/templates/login-card.tsx +59 -86
- package/templates/login-split.tsx +88 -128
- package/templates/login-sso.tsx +107 -68
- package/templates/login.tsx +108 -52
- package/templates/messaging-shell.tsx +172 -143
- package/templates/mixed-gallery.tsx +31 -130
- package/templates/payment-form.tsx +369 -289
- package/templates/product-detail.tsx +40 -57
- package/templates/product-gallery.tsx +31 -40
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +145 -286
- package/templates/settings-sidebar.tsx +179 -267
- package/templates/settings.tsx +42 -13
- package/templates/shell-nav.tsx +32 -23
- package/templates/shell-side-nav.tsx +30 -19
- package/templates/shell-top-nav.tsx +81 -43
- package/templates/side-gallery.tsx +39 -93
- package/templates/table-grouped.tsx +63 -33
- package/templates/table-page-chart.tsx +24 -297
- package/templates/table-page-heatmap-status.tsx +157 -91
- package/templates/table-page-shoe-store-heatmap.tsx +19 -288
- package/templates/table-page.tsx +24 -6
- package/templates/table.tsx +24 -7
- package/templates/tech-report.template.mjs +6 -0
- package/templates/tech-report.tsx +1486 -0
- package/templates/theme-showcase.template.mjs +1 -1
- package/templates/theme-showcase.tsx +313 -249
- package/theme.css +76 -14
- package/tokens.css +425 -30
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
+
// L > LC[p=0] > Ctr > S.transparent[p=6 mw=1100] > V[g=5] > (V[g=6] > (V[g=2] > (Hd"Payment Request"[level=1 t=display-2] + Tx"Review your order"[t=body]) + D) + H[g=8] > (SI > V[g=8] > (V[g=1] > (H[j=between] > (Hd"Sign in to check out"[level=2] + B"Sign in") + Tx"Sign in to track your order"[t=body]) + V[g=3] > (Hd"Contact information"[level=2] + TI"Email" + CB"Email offers") + V[g=3] > (Hd"Shipping information"[level=2] + G[c=2 g=3] > (TI"First Name" + TI"Last Name") + TI"Address" + G[c=2 g=3] > (TI"City" + TI"ZIP Code") + SE"State" + TI"Phone Number" + CB"Save information") + V[g=3] > (V[g=1] > (Hd"Delivery"[level=2] + Tx"Processing time"[t=body]) + RL"Delivery method" > RLI*2) + V[g=3] > (V[g=1] > (Hd"Payment method"[level=2] + Tx"Encrypted"[t=body]) + G[c=2 g=3] > (B"PayPal" + B"Google Pay") + D"Or pay with card" + TI"Card Number" + G[c=3 g=3] > (SE"Expiry Month" + SE"Expiry Year" + TI"CVC") + TI"Name on Card" + CB"Billing address") + V[g=3] > (Hd"Promo code"[level=2] + H[g=2] > (TI"Promo code" + B"Apply")) + V[g=3] > (Hd"Gift options"[level=2] + CB"Gift message" + TA"Gift message") + V[g=4] > (H[g=5] > (H[g=1] > (Ic + Tx))*3 + V[g=2] > (B"Place order"[primary] + B"Continue shopping") + D + H[g=4] > (Lk"Refund policy" + Lk"Privacy policy" + Lk"Terms" + Lk"Cancellations"))) + SI > C[p=4] > Col"Order Summary" > V[g=4] > (V[g=3] > V[g=3] > (H[g=3] > (Tmb + V[g=1] > (Tx"Obsidian Ritual Chalice" + Tx"Hand-carved"[t=supporting])) + D)*3 + V[g=3] > (Hd"Order Total"[level=3] + H[j=between] > (Tx"Subtotal" + Tx"$91.20") + D + H[j=between] > (Tx"Total"[t=large] + Tx"$91.20"[t=large]) + Bn"Free shipping over $300"))))
|
|
2
4
|
|
|
3
5
|
import {useState, type CSSProperties} from 'react';
|
|
4
6
|
import {
|
|
@@ -11,7 +13,7 @@ import {
|
|
|
11
13
|
} from '@astryxdesign/core/Layout';
|
|
12
14
|
import {Grid} from '@astryxdesign/core/Grid';
|
|
13
15
|
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
-
import {Text} from '@astryxdesign/core/Text';
|
|
16
|
+
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
15
17
|
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
16
18
|
import {Selector} from '@astryxdesign/core/Selector';
|
|
17
19
|
import {CheckboxInput} from '@astryxdesign/core/CheckboxInput';
|
|
@@ -21,13 +23,14 @@ import {TextArea} from '@astryxdesign/core/TextArea';
|
|
|
21
23
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
22
24
|
import {Banner} from '@astryxdesign/core/Banner';
|
|
23
25
|
import {Card} from '@astryxdesign/core/Card';
|
|
26
|
+
import {type SceneHue} from 'astryx-dracula/shared/scene-hues';
|
|
27
|
+
import {SceneTile} from 'astryx-dracula/shared/scene-tile';
|
|
24
28
|
import {Collapsible} from '@astryxdesign/core/Collapsible';
|
|
25
|
-
import {
|
|
29
|
+
import {StatusDot} from '@astryxdesign/core/StatusDot';
|
|
26
30
|
import {NumberInput} from '@astryxdesign/core/NumberInput';
|
|
27
31
|
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
28
32
|
import {Section} from '@astryxdesign/core/Section';
|
|
29
33
|
import {Center} from '@astryxdesign/core/Center';
|
|
30
|
-
import {Thumbnail} from '@astryxdesign/core/Thumbnail';
|
|
31
34
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
32
35
|
import {ShieldCheck, Lock, CircleCheck, Truck} from 'lucide-react';
|
|
33
36
|
|
|
@@ -102,18 +105,14 @@ const US_STATES = [
|
|
|
102
105
|
'Wyoming',
|
|
103
106
|
];
|
|
104
107
|
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
'2':
|
|
112
|
-
|
|
113
|
-
},
|
|
114
|
-
'3': {
|
|
115
|
-
src: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20400%20300%22%20preserveAspectRatio%3D%22xMidYMid%20slice%22%3E%3Crect%20width%3D%22400%22%20height%3D%22300%22%20fill%3D%22%2321222C%22%2F%3E%3Cg%20transform%3D%22translate%28200%20150%29%22%20fill%3D%22none%22%20stroke%3D%22%236272A4%22%20stroke-width%3D%225%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%22-44%22%20y%3D%22-44%22%20width%3D%2288%22%20height%3D%2288%22%20rx%3D%2216%22%2F%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%22-18%22%20r%3D%222.5%22%20fill%3D%22%236272A4%22%20stroke%3D%22none%22%2F%3E%3Cpath%20d%3D%22M-34%2030%20L-8%200%20L10%2018%20L20%208%20L34%2024%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E',
|
|
116
|
-
},
|
|
108
|
+
// One Dracula accent per line item, from the fixed categorical vocabulary.
|
|
109
|
+
// Art lives in shared/scene-tile (lg fork: centered glyph on a flat field), so
|
|
110
|
+
// this map is typed `SceneHue`. `Record<string, string>` let any caller slip
|
|
111
|
+
// purple through to a scene; the union is what stops the next one.
|
|
112
|
+
const ITEM_HUES: Record<string, SceneHue> = {
|
|
113
|
+
'1': 'var(--dracula-cyan)',
|
|
114
|
+
'2': 'var(--dracula-pink)',
|
|
115
|
+
'3': 'var(--dracula-yellow)',
|
|
117
116
|
};
|
|
118
117
|
|
|
119
118
|
const ORDER_ITEMS = [
|
|
@@ -165,19 +164,15 @@ const summarySticky: CSSProperties = {
|
|
|
165
164
|
};
|
|
166
165
|
// On mobile the summary moves above the form.
|
|
167
166
|
const summaryMobileOrder: CSSProperties = {order: -1};
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
// Line-item photo: fixed square frame; art lives in shared/scene-tile
|
|
168
|
+
// (lg fork). Astryx has no Image primitive (#2582), so the tile is inline
|
|
169
|
+
// SVG on brand tokens instead of a data-URI bitmap with baked-in hex.
|
|
170
|
+
const itemPhotoFrame: CSSProperties = {
|
|
171
|
+
width: 'var(--spacing-10)',
|
|
172
|
+
height: 'var(--spacing-10)',
|
|
173
|
+
overflow: 'clip',
|
|
174
|
+
flexShrink: 0,
|
|
172
175
|
};
|
|
173
|
-
// Google Pay button: widget-surface token stands in for the official dark
|
|
174
|
-
// button background (no raw hex on brand surfaces).
|
|
175
|
-
const gpayButton: CSSProperties = {
|
|
176
|
-
backgroundColor: 'var(--color-widget-background)',
|
|
177
|
-
borderColor: 'var(--color-widget-background)',
|
|
178
|
-
};
|
|
179
|
-
// Brand logos inside the express-checkout buttons.
|
|
180
|
-
const brandLogo: CSSProperties = {height: 'var(--spacing-5)', width: 'auto'};
|
|
181
176
|
// Accepted card-network marks (Visa/Mastercard/Amex), shared style.
|
|
182
177
|
const cardLogo: CSSProperties = {
|
|
183
178
|
height: 'var(--spacing-7)',
|
|
@@ -185,12 +180,231 @@ const cardLogo: CSSProperties = {
|
|
|
185
180
|
borderRadius: 'var(--radius-element)',
|
|
186
181
|
borderWidth: 'var(--border-width)',
|
|
187
182
|
borderStyle: 'solid',
|
|
188
|
-
borderColor: 'var(--color-
|
|
183
|
+
borderColor: 'var(--color-separator)',
|
|
189
184
|
backgroundColor: 'var(--color-background-surface)',
|
|
190
185
|
};
|
|
191
186
|
|
|
187
|
+
function OrderItemPhoto({item}: {item: (typeof ORDER_ITEMS)[number]}) {
|
|
188
|
+
return (
|
|
189
|
+
<Card padding={0} style={itemPhotoFrame}>
|
|
190
|
+
<SceneTile label={item.name} hue={ITEM_HUES[item.id]} size="lg" />
|
|
191
|
+
</Card>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function OrderLineItem({
|
|
196
|
+
item,
|
|
197
|
+
qty,
|
|
198
|
+
onChangeQty,
|
|
199
|
+
}: {
|
|
200
|
+
item: (typeof ORDER_ITEMS)[number];
|
|
201
|
+
qty: number;
|
|
202
|
+
onChangeQty: (v: number) => void;
|
|
203
|
+
}) {
|
|
204
|
+
return (
|
|
205
|
+
<VStack gap={3}>
|
|
206
|
+
<HStack gap={3} vAlign="start">
|
|
207
|
+
<OrderItemPhoto item={item} />
|
|
208
|
+
<StackItem size="fill">
|
|
209
|
+
<VStack gap={1}>
|
|
210
|
+
<HStack gap={2} hAlign="between" vAlign="start">
|
|
211
|
+
<HStack gap={2} vAlign="center" wrap="wrap">
|
|
212
|
+
<Text type="body" weight="semibold">
|
|
213
|
+
{item.name}
|
|
214
|
+
</Text>
|
|
215
|
+
{item.limited && (
|
|
216
|
+
<HStack gap={1} vAlign="center">
|
|
217
|
+
<StatusDot variant="warning" label="Limited edition" />
|
|
218
|
+
<Text type="supporting" color="secondary">
|
|
219
|
+
Limited edition
|
|
220
|
+
</Text>
|
|
221
|
+
</HStack>
|
|
222
|
+
)}
|
|
223
|
+
</HStack>
|
|
224
|
+
<Text type="body" hasTabularNumbers>
|
|
225
|
+
{fmt(item.price)}
|
|
226
|
+
</Text>
|
|
227
|
+
</HStack>
|
|
228
|
+
<Text type="supporting" color="secondary">
|
|
229
|
+
{item.variant}
|
|
230
|
+
</Text>
|
|
231
|
+
<HStack gap={2} vAlign="end" wrap="wrap">
|
|
232
|
+
<NumberInput
|
|
233
|
+
label="Qty"
|
|
234
|
+
value={qty}
|
|
235
|
+
onChange={onChangeQty}
|
|
236
|
+
min={1}
|
|
237
|
+
max={10}
|
|
238
|
+
isIntegerOnly
|
|
239
|
+
/>
|
|
240
|
+
<Link href={SELF_HREF} type="supporting">
|
|
241
|
+
Remove
|
|
242
|
+
</Link>
|
|
243
|
+
<Link href={SELF_HREF} type="supporting">
|
|
244
|
+
Save
|
|
245
|
+
</Link>
|
|
246
|
+
</HStack>
|
|
247
|
+
</VStack>
|
|
248
|
+
</StackItem>
|
|
249
|
+
</HStack>
|
|
250
|
+
<Divider />
|
|
251
|
+
</VStack>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function OrderSummaryCard({
|
|
256
|
+
quantities,
|
|
257
|
+
onChangeQty,
|
|
258
|
+
subtotal,
|
|
259
|
+
shipping,
|
|
260
|
+
tax,
|
|
261
|
+
total,
|
|
262
|
+
}: {
|
|
263
|
+
quantities: Record<string, number>;
|
|
264
|
+
onChangeQty: (id: string, v: number) => void;
|
|
265
|
+
subtotal: number;
|
|
266
|
+
shipping: number;
|
|
267
|
+
tax: number;
|
|
268
|
+
total: number;
|
|
269
|
+
}) {
|
|
270
|
+
return (
|
|
271
|
+
<Card padding={4}>
|
|
272
|
+
<VStack gap={4}>
|
|
273
|
+
{/* Accordion header — clickable on mobile only */}
|
|
274
|
+
<Collapsible trigger="Order Summary" defaultIsOpen={true}>
|
|
275
|
+
<VStack gap={4} style={summaryContent}>
|
|
276
|
+
{ORDER_ITEMS.map(item => (
|
|
277
|
+
<OrderLineItem
|
|
278
|
+
key={item.id}
|
|
279
|
+
item={item}
|
|
280
|
+
qty={quantities[item.id] ?? item.qty}
|
|
281
|
+
onChangeQty={v => onChangeQty(item.id, v)}
|
|
282
|
+
/>
|
|
283
|
+
))}
|
|
284
|
+
<OrderTotalSection
|
|
285
|
+
subtotal={subtotal}
|
|
286
|
+
shipping={shipping}
|
|
287
|
+
tax={tax}
|
|
288
|
+
total={total}
|
|
289
|
+
/>
|
|
290
|
+
</VStack>
|
|
291
|
+
</Collapsible>
|
|
292
|
+
</VStack>
|
|
293
|
+
</Card>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function OrderTotalSection({
|
|
298
|
+
subtotal,
|
|
299
|
+
shipping,
|
|
300
|
+
tax,
|
|
301
|
+
total,
|
|
302
|
+
}: {
|
|
303
|
+
subtotal: number;
|
|
304
|
+
shipping: number;
|
|
305
|
+
tax: number;
|
|
306
|
+
total: number;
|
|
307
|
+
}) {
|
|
308
|
+
return (
|
|
309
|
+
<VStack gap={3}>
|
|
310
|
+
<Heading level={3}>Order Total</Heading>
|
|
311
|
+
<VStack gap={2}>
|
|
312
|
+
<HStack hAlign="between" vAlign="center">
|
|
313
|
+
<Text type="body" color="secondary">
|
|
314
|
+
Subtotal
|
|
315
|
+
</Text>
|
|
316
|
+
<Text type="body" hasTabularNumbers>
|
|
317
|
+
{fmt(subtotal)}
|
|
318
|
+
</Text>
|
|
319
|
+
</HStack>
|
|
320
|
+
<HStack hAlign="between" vAlign="center">
|
|
321
|
+
<Text type="body" color="secondary">
|
|
322
|
+
Shipping
|
|
323
|
+
</Text>
|
|
324
|
+
<Text type="body" hasTabularNumbers>
|
|
325
|
+
{fmt(shipping)}
|
|
326
|
+
</Text>
|
|
327
|
+
</HStack>
|
|
328
|
+
<HStack hAlign="between" vAlign="center">
|
|
329
|
+
<Text type="body" color="secondary">
|
|
330
|
+
Tax
|
|
331
|
+
</Text>
|
|
332
|
+
<Text type="body" hasTabularNumbers>
|
|
333
|
+
{fmt(tax)}
|
|
334
|
+
</Text>
|
|
335
|
+
</HStack>
|
|
336
|
+
</VStack>
|
|
337
|
+
<Divider />
|
|
338
|
+
<HStack hAlign="between" vAlign="center">
|
|
339
|
+
<Text type="large">
|
|
340
|
+
Total
|
|
341
|
+
</Text>
|
|
342
|
+
<Text type="large" hasTabularNumbers>
|
|
343
|
+
{fmt(total)}
|
|
344
|
+
</Text>
|
|
345
|
+
</HStack>
|
|
346
|
+
<Banner
|
|
347
|
+
status="info"
|
|
348
|
+
icon={<Icon icon={Truck} size="sm" />}
|
|
349
|
+
title="Free shipping on orders over $300"
|
|
350
|
+
/>
|
|
351
|
+
</VStack>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const TRUST_ITEMS: Array<{icon: typeof ShieldCheck; label: string}> = [
|
|
356
|
+
{icon: ShieldCheck, label: 'Secure Payment'},
|
|
357
|
+
{icon: Lock, label: 'SSL Encrypted'},
|
|
358
|
+
{icon: CircleCheck, label: 'Free Returns'},
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const SELF_HREF = '#/templates/payment-form';
|
|
362
|
+
|
|
363
|
+
const POLICY_LINKS = [
|
|
364
|
+
'Refund policy',
|
|
365
|
+
'Privacy policy',
|
|
366
|
+
'Terms of service',
|
|
367
|
+
'Cancellations',
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
function TrustBar() {
|
|
371
|
+
return (
|
|
372
|
+
<HStack gap={5} hAlign="center" wrap="wrap">
|
|
373
|
+
{TRUST_ITEMS.map(item => (
|
|
374
|
+
<HStack key={item.label} gap={1} vAlign="center">
|
|
375
|
+
<Icon icon={item.icon} size="sm" color="secondary" />
|
|
376
|
+
<Text type="supporting" color="secondary">
|
|
377
|
+
{item.label}
|
|
378
|
+
</Text>
|
|
379
|
+
</HStack>
|
|
380
|
+
))}
|
|
381
|
+
</HStack>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function PolicyLinks() {
|
|
386
|
+
return (
|
|
387
|
+
<HStack gap={4} vAlign="center" wrap="wrap">
|
|
388
|
+
{POLICY_LINKS.map(label => (
|
|
389
|
+
<Link key={label} href={SELF_HREF} type="supporting">
|
|
390
|
+
{label}
|
|
391
|
+
</Link>
|
|
392
|
+
))}
|
|
393
|
+
</HStack>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
192
397
|
export default function PaymentForm() {
|
|
193
|
-
|
|
398
|
+
// 768, not 767: eight sibling templates gate the same collapse at exactly
|
|
399
|
+
// 768 (`documentation-design.tsx:568`, `documentation-technical.tsx:61`,
|
|
400
|
+
// `editor.tsx:640`, `file-explorer.tsx:375`, `ide.tsx:244`,
|
|
401
|
+
// `settings-sidebar.tsx:127`, `settings.tsx:79`, `messaging-shell.tsx:393`),
|
|
402
|
+
// and this file's own second query below is 1024. At 767 this was the only
|
|
403
|
+
// off-by-one in the set, so at exactly a 768px viewport `isMobile` was
|
|
404
|
+
// false while `isStacked` was true: the outer Stack had already gone
|
|
405
|
+
// vertical (`:514`) and all six `columns={isMobile ? 1 : N}` grids below
|
|
406
|
+
// stayed wide inside it, including the 3-column expiry row at `:821`.
|
|
407
|
+
const isMobile = useMediaQuery('(max-width: 768px)');
|
|
194
408
|
const isStacked = useMediaQuery('(max-width: 1024px)');
|
|
195
409
|
const [firstName, setFirstName] = useState('');
|
|
196
410
|
const [lastName, setLastName] = useState('');
|
|
@@ -292,9 +506,9 @@ export default function PaymentForm() {
|
|
|
292
506
|
{/* Page header */}
|
|
293
507
|
<VStack gap={6}>
|
|
294
508
|
<VStack gap={2}>
|
|
295
|
-
<
|
|
509
|
+
<Heading level={1} type="display-2">
|
|
296
510
|
Payment Request
|
|
297
|
-
</
|
|
511
|
+
</Heading>
|
|
298
512
|
<Text type="body" color="secondary">
|
|
299
513
|
Review your order and complete your purchase. All
|
|
300
514
|
transactions are sealed with 256-bit SSL encryption.
|
|
@@ -314,13 +528,11 @@ export default function PaymentForm() {
|
|
|
314
528
|
{/* Sign in */}
|
|
315
529
|
<VStack gap={1}>
|
|
316
530
|
<HStack gap={2} hAlign="between" vAlign="center">
|
|
317
|
-
<
|
|
318
|
-
Sign in to check out
|
|
319
|
-
</Text>
|
|
531
|
+
<Heading level={2}>Sign in to check out</Heading>
|
|
320
532
|
<Button
|
|
321
|
-
label="Sign
|
|
533
|
+
label="Sign in"
|
|
322
534
|
variant="secondary"
|
|
323
|
-
|
|
535
|
+
|
|
324
536
|
onClick={() => {}}
|
|
325
537
|
/>
|
|
326
538
|
</HStack>
|
|
@@ -332,13 +544,12 @@ export default function PaymentForm() {
|
|
|
332
544
|
|
|
333
545
|
{/* Contact Information */}
|
|
334
546
|
<VStack gap={3}>
|
|
335
|
-
<
|
|
336
|
-
Contact Information
|
|
337
|
-
</Text>
|
|
547
|
+
<Heading level={2}>Contact information</Heading>
|
|
338
548
|
<TextInput
|
|
339
549
|
size="lg"
|
|
340
550
|
label="Email"
|
|
341
|
-
|
|
551
|
+
isRequired
|
|
552
|
+
placeholder="you@castle.dracula"
|
|
342
553
|
value={email}
|
|
343
554
|
onChange={setEmail}
|
|
344
555
|
status={
|
|
@@ -356,14 +567,13 @@ export default function PaymentForm() {
|
|
|
356
567
|
|
|
357
568
|
{/* Shipping Information */}
|
|
358
569
|
<VStack gap={3}>
|
|
359
|
-
<
|
|
360
|
-
Shipping Information
|
|
361
|
-
</Text>
|
|
570
|
+
<Heading level={2}>Shipping information</Heading>
|
|
362
571
|
<Grid columns={isMobile ? 1 : 2} gap={3}>
|
|
363
572
|
<TextInput
|
|
364
573
|
size="lg"
|
|
365
574
|
label="First Name"
|
|
366
|
-
|
|
575
|
+
isRequired
|
|
576
|
+
placeholder="Vlad"
|
|
367
577
|
value={firstName}
|
|
368
578
|
onChange={setFirstName}
|
|
369
579
|
status={
|
|
@@ -375,7 +585,8 @@ export default function PaymentForm() {
|
|
|
375
585
|
<TextInput
|
|
376
586
|
size="lg"
|
|
377
587
|
label="Last Name"
|
|
378
|
-
|
|
588
|
+
isRequired
|
|
589
|
+
placeholder="Dracul"
|
|
379
590
|
value={lastName}
|
|
380
591
|
onChange={setLastName}
|
|
381
592
|
status={
|
|
@@ -388,7 +599,8 @@ export default function PaymentForm() {
|
|
|
388
599
|
<TextInput
|
|
389
600
|
size="lg"
|
|
390
601
|
label="Address"
|
|
391
|
-
|
|
602
|
+
isRequired
|
|
603
|
+
placeholder="666 Castle Way"
|
|
392
604
|
value={address}
|
|
393
605
|
onChange={setAddress}
|
|
394
606
|
status={
|
|
@@ -401,7 +613,8 @@ export default function PaymentForm() {
|
|
|
401
613
|
<TextInput
|
|
402
614
|
size="lg"
|
|
403
615
|
label="City"
|
|
404
|
-
|
|
616
|
+
isRequired
|
|
617
|
+
placeholder="Transylvania"
|
|
405
618
|
value={city}
|
|
406
619
|
onChange={setCity}
|
|
407
620
|
status={
|
|
@@ -413,7 +626,8 @@ export default function PaymentForm() {
|
|
|
413
626
|
<TextInput
|
|
414
627
|
size="lg"
|
|
415
628
|
label="ZIP Code"
|
|
416
|
-
|
|
629
|
+
isRequired
|
|
630
|
+
placeholder="40000"
|
|
417
631
|
value={zip}
|
|
418
632
|
onChange={setZip}
|
|
419
633
|
status={
|
|
@@ -426,6 +640,7 @@ export default function PaymentForm() {
|
|
|
426
640
|
<Selector
|
|
427
641
|
size="lg"
|
|
428
642
|
label="State"
|
|
643
|
+
isRequired
|
|
429
644
|
placeholder="Select state"
|
|
430
645
|
options={US_STATES}
|
|
431
646
|
value={state}
|
|
@@ -439,6 +654,7 @@ export default function PaymentForm() {
|
|
|
439
654
|
<TextInput
|
|
440
655
|
size="lg"
|
|
441
656
|
label="Phone Number"
|
|
657
|
+
isRequired
|
|
442
658
|
placeholder="+1 (555) 123-4567"
|
|
443
659
|
value={phone}
|
|
444
660
|
onChange={setPhone}
|
|
@@ -459,9 +675,7 @@ export default function PaymentForm() {
|
|
|
459
675
|
{/* Delivery */}
|
|
460
676
|
<VStack gap={3}>
|
|
461
677
|
<VStack gap={1}>
|
|
462
|
-
<
|
|
463
|
-
Delivery
|
|
464
|
-
</Text>
|
|
678
|
+
<Heading level={2}>Delivery</Heading>
|
|
465
679
|
<Text type="body" color="secondary">
|
|
466
680
|
Please allow 1–3 business days processing time
|
|
467
681
|
before your order ships.
|
|
@@ -477,7 +691,7 @@ export default function PaymentForm() {
|
|
|
477
691
|
endContent={
|
|
478
692
|
<Text
|
|
479
693
|
type="body"
|
|
480
|
-
weight="
|
|
694
|
+
weight="semibold"
|
|
481
695
|
hasTabularNumbers>
|
|
482
696
|
$4.95
|
|
483
697
|
</Text>
|
|
@@ -489,7 +703,7 @@ export default function PaymentForm() {
|
|
|
489
703
|
endContent={
|
|
490
704
|
<Text
|
|
491
705
|
type="body"
|
|
492
|
-
weight="
|
|
706
|
+
weight="semibold"
|
|
493
707
|
hasTabularNumbers>
|
|
494
708
|
$9.95
|
|
495
709
|
</Text>
|
|
@@ -501,9 +715,7 @@ export default function PaymentForm() {
|
|
|
501
715
|
{/* Payment Method */}
|
|
502
716
|
<VStack gap={3}>
|
|
503
717
|
<VStack gap={1}>
|
|
504
|
-
<
|
|
505
|
-
Payment Method
|
|
506
|
-
</Text>
|
|
718
|
+
<Heading level={2}>Payment method</Heading>
|
|
507
719
|
<Text type="body" color="secondary">
|
|
508
720
|
All transactions are secure and encrypted.
|
|
509
721
|
</Text>
|
|
@@ -515,68 +727,95 @@ export default function PaymentForm() {
|
|
|
515
727
|
{/* PayPal */}
|
|
516
728
|
<Button
|
|
517
729
|
label="PayPal"
|
|
518
|
-
variant="
|
|
730
|
+
variant="secondary"
|
|
519
731
|
size="lg"
|
|
520
732
|
onClick={() => {}}
|
|
521
|
-
|
|
522
|
-
<img
|
|
523
|
-
src="https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png"
|
|
524
|
-
alt="PayPal"
|
|
525
|
-
style={brandLogo}
|
|
526
|
-
/>
|
|
527
|
-
</Button>
|
|
733
|
+
/>
|
|
528
734
|
{/* Google Pay */}
|
|
529
735
|
<Button
|
|
530
736
|
label="Google Pay"
|
|
531
|
-
variant="
|
|
737
|
+
variant="secondary"
|
|
532
738
|
size="lg"
|
|
533
739
|
onClick={() => {}}
|
|
534
|
-
|
|
535
|
-
<img
|
|
536
|
-
src="https://pay.google.com/about/static_kcs/images/logos/google-pay-logo.svg"
|
|
537
|
-
alt="Google Pay"
|
|
538
|
-
style={brandLogo}
|
|
539
|
-
/>
|
|
540
|
-
</Button>
|
|
740
|
+
/>
|
|
541
741
|
</Grid>
|
|
542
742
|
</VStack>
|
|
543
743
|
|
|
544
|
-
|
|
545
|
-
<HStack gap={3} vAlign="center">
|
|
546
|
-
<StackItem size="fill">
|
|
547
|
-
<Divider />
|
|
548
|
-
</StackItem>
|
|
549
|
-
<Text type="supporting" color="secondary">
|
|
550
|
-
OR
|
|
551
|
-
</Text>
|
|
552
|
-
<StackItem size="fill">
|
|
553
|
-
<Divider />
|
|
554
|
-
</StackItem>
|
|
555
|
-
</HStack>
|
|
744
|
+
<Divider label="Or pay with card" />
|
|
556
745
|
|
|
557
746
|
{/* Credit card fields */}
|
|
558
747
|
<VStack gap={3}>
|
|
559
|
-
{/* Card type icons
|
|
748
|
+
{/* Card type icons — inline Dracula marks on
|
|
749
|
+
brand tokens; no vendor hotlinks. */}
|
|
560
750
|
<HStack gap={1.5} vAlign="center">
|
|
561
|
-
<
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
751
|
+
<svg
|
|
752
|
+
viewBox="0 0 48 32"
|
|
753
|
+
role="img"
|
|
754
|
+
aria-label="Visa"
|
|
755
|
+
style={cardLogo}>
|
|
756
|
+
<rect
|
|
757
|
+
width="48"
|
|
758
|
+
height="32"
|
|
759
|
+
fill="var(--color-background-surface)"
|
|
760
|
+
/>
|
|
761
|
+
<text
|
|
762
|
+
x="24"
|
|
763
|
+
y="21"
|
|
764
|
+
textAnchor="middle"
|
|
765
|
+
fontSize="10"
|
|
766
|
+
fontStyle="italic"
|
|
767
|
+
fill="var(--dracula-comment)">
|
|
768
|
+
VISA
|
|
769
|
+
</text>
|
|
770
|
+
</svg>
|
|
771
|
+
<svg
|
|
772
|
+
viewBox="0 0 48 32"
|
|
773
|
+
role="img"
|
|
774
|
+
aria-label="Mastercard"
|
|
775
|
+
style={cardLogo}>
|
|
776
|
+
<rect
|
|
777
|
+
width="48"
|
|
778
|
+
height="32"
|
|
779
|
+
fill="var(--color-background-surface)"
|
|
780
|
+
/>
|
|
781
|
+
<circle
|
|
782
|
+
cx="19"
|
|
783
|
+
cy="16"
|
|
784
|
+
r="8"
|
|
785
|
+
fill="var(--dracula-red)"
|
|
786
|
+
/>
|
|
787
|
+
<circle
|
|
788
|
+
cx="29"
|
|
789
|
+
cy="16"
|
|
790
|
+
r="8"
|
|
791
|
+
fill="var(--dracula-orange)"
|
|
792
|
+
opacity={0.85}
|
|
793
|
+
/>
|
|
794
|
+
</svg>
|
|
795
|
+
<svg
|
|
796
|
+
viewBox="0 0 48 32"
|
|
797
|
+
role="img"
|
|
798
|
+
aria-label="Amex"
|
|
799
|
+
style={cardLogo}>
|
|
800
|
+
<rect
|
|
801
|
+
width="48"
|
|
802
|
+
height="32"
|
|
803
|
+
fill="var(--color-background-surface)"
|
|
804
|
+
/>
|
|
805
|
+
<text
|
|
806
|
+
x="24"
|
|
807
|
+
y="21"
|
|
808
|
+
textAnchor="middle"
|
|
809
|
+
fontSize="10"
|
|
810
|
+
fill="var(--dracula-cyan)">
|
|
811
|
+
AMEX
|
|
812
|
+
</text>
|
|
813
|
+
</svg>
|
|
576
814
|
</HStack>
|
|
577
815
|
<TextInput
|
|
578
816
|
size="lg"
|
|
579
817
|
label="Card Number"
|
|
818
|
+
isRequired
|
|
580
819
|
placeholder="1234 5678 9012 3456"
|
|
581
820
|
value={cardNumber}
|
|
582
821
|
onChange={setCardNumber}
|
|
@@ -590,6 +829,7 @@ export default function PaymentForm() {
|
|
|
590
829
|
<Selector
|
|
591
830
|
size="lg"
|
|
592
831
|
label="Expiry Month"
|
|
832
|
+
isRequired
|
|
593
833
|
placeholder="MM"
|
|
594
834
|
options={MONTHS}
|
|
595
835
|
value={expiry}
|
|
@@ -603,6 +843,7 @@ export default function PaymentForm() {
|
|
|
603
843
|
<Selector
|
|
604
844
|
size="lg"
|
|
605
845
|
label="Expiry Year"
|
|
846
|
+
isRequired
|
|
606
847
|
placeholder="YY"
|
|
607
848
|
options={YEARS}
|
|
608
849
|
value={expYear}
|
|
@@ -616,6 +857,7 @@ export default function PaymentForm() {
|
|
|
616
857
|
<TextInput
|
|
617
858
|
size="lg"
|
|
618
859
|
label="CVC"
|
|
860
|
+
isRequired
|
|
619
861
|
placeholder="123"
|
|
620
862
|
value={cvc}
|
|
621
863
|
onChange={setCvc}
|
|
@@ -630,7 +872,8 @@ export default function PaymentForm() {
|
|
|
630
872
|
<TextInput
|
|
631
873
|
size="lg"
|
|
632
874
|
label="Name on Card"
|
|
633
|
-
|
|
875
|
+
isRequired
|
|
876
|
+
placeholder="Vlad Dracul"
|
|
634
877
|
value={cardName}
|
|
635
878
|
onChange={setCardName}
|
|
636
879
|
status={
|
|
@@ -649,7 +892,8 @@ export default function PaymentForm() {
|
|
|
649
892
|
<TextInput
|
|
650
893
|
size="lg"
|
|
651
894
|
label="Address"
|
|
652
|
-
|
|
895
|
+
isRequired
|
|
896
|
+
placeholder="666 Castle Way"
|
|
653
897
|
value={billingAddress}
|
|
654
898
|
onChange={setBillingAddress}
|
|
655
899
|
status={
|
|
@@ -665,7 +909,8 @@ export default function PaymentForm() {
|
|
|
665
909
|
<TextInput
|
|
666
910
|
size="lg"
|
|
667
911
|
label="City"
|
|
668
|
-
|
|
912
|
+
isRequired
|
|
913
|
+
placeholder="Transylvania"
|
|
669
914
|
value={billingCity}
|
|
670
915
|
onChange={setBillingCity}
|
|
671
916
|
status={
|
|
@@ -680,7 +925,8 @@ export default function PaymentForm() {
|
|
|
680
925
|
<TextInput
|
|
681
926
|
size="lg"
|
|
682
927
|
label="ZIP Code"
|
|
683
|
-
|
|
928
|
+
isRequired
|
|
929
|
+
placeholder="40000"
|
|
684
930
|
value={billingZip}
|
|
685
931
|
onChange={setBillingZip}
|
|
686
932
|
status={
|
|
@@ -696,6 +942,7 @@ export default function PaymentForm() {
|
|
|
696
942
|
<Selector
|
|
697
943
|
size="lg"
|
|
698
944
|
label="State"
|
|
945
|
+
isRequired
|
|
699
946
|
placeholder="Select state"
|
|
700
947
|
options={US_STATES}
|
|
701
948
|
value={billingState}
|
|
@@ -716,9 +963,7 @@ export default function PaymentForm() {
|
|
|
716
963
|
|
|
717
964
|
{/* Promo Code */}
|
|
718
965
|
<VStack gap={3}>
|
|
719
|
-
<
|
|
720
|
-
Promo Code
|
|
721
|
-
</Text>
|
|
966
|
+
<Heading level={2}>Promo code</Heading>
|
|
722
967
|
<HStack gap={2} vAlign="end">
|
|
723
968
|
<StackItem size="fill">
|
|
724
969
|
<TextInput
|
|
@@ -741,9 +986,7 @@ export default function PaymentForm() {
|
|
|
741
986
|
|
|
742
987
|
{/* Gift Options */}
|
|
743
988
|
<VStack gap={3}>
|
|
744
|
-
<
|
|
745
|
-
Gift Options
|
|
746
|
-
</Text>
|
|
989
|
+
<Heading level={2}>Gift options</Heading>
|
|
747
990
|
<CheckboxInput
|
|
748
991
|
label="Add a gift message"
|
|
749
992
|
value={addGiftMessage}
|
|
@@ -755,23 +998,20 @@ export default function PaymentForm() {
|
|
|
755
998
|
<TextInput
|
|
756
999
|
size="lg"
|
|
757
1000
|
label="To"
|
|
758
|
-
|
|
759
|
-
placeholder="To"
|
|
1001
|
+
placeholder="Recipient name"
|
|
760
1002
|
value={giftTo}
|
|
761
1003
|
onChange={setGiftTo}
|
|
762
1004
|
/>
|
|
763
1005
|
<TextInput
|
|
764
1006
|
size="lg"
|
|
765
1007
|
label="From"
|
|
766
|
-
|
|
767
|
-
placeholder="From"
|
|
1008
|
+
placeholder="Your name"
|
|
768
1009
|
value={giftFrom}
|
|
769
1010
|
onChange={setGiftFrom}
|
|
770
1011
|
/>
|
|
771
1012
|
</Grid>
|
|
772
1013
|
<TextArea
|
|
773
1014
|
label="Gift message"
|
|
774
|
-
isLabelHidden
|
|
775
1015
|
placeholder="Write something here"
|
|
776
1016
|
value={giftMessage}
|
|
777
1017
|
onChange={setGiftMessage}
|
|
@@ -782,48 +1022,17 @@ export default function PaymentForm() {
|
|
|
782
1022
|
|
|
783
1023
|
{/* Trust bar + CTAs + policy links */}
|
|
784
1024
|
<VStack gap={4}>
|
|
785
|
-
<
|
|
786
|
-
<HStack gap={1} vAlign="center">
|
|
787
|
-
<Icon
|
|
788
|
-
icon={ShieldCheck}
|
|
789
|
-
size="sm"
|
|
790
|
-
color="secondary"
|
|
791
|
-
/>
|
|
792
|
-
<Text type="supporting" color="secondary">
|
|
793
|
-
Secure Payment
|
|
794
|
-
</Text>
|
|
795
|
-
</HStack>
|
|
796
|
-
<HStack gap={1} vAlign="center">
|
|
797
|
-
<Icon
|
|
798
|
-
icon={Lock}
|
|
799
|
-
size="sm"
|
|
800
|
-
color="secondary"
|
|
801
|
-
/>
|
|
802
|
-
<Text type="supporting" color="secondary">
|
|
803
|
-
SSL Encrypted
|
|
804
|
-
</Text>
|
|
805
|
-
</HStack>
|
|
806
|
-
<HStack gap={1} vAlign="center">
|
|
807
|
-
<Icon
|
|
808
|
-
icon={CircleCheck}
|
|
809
|
-
size="sm"
|
|
810
|
-
color="secondary"
|
|
811
|
-
/>
|
|
812
|
-
<Text type="supporting" color="secondary">
|
|
813
|
-
Free Returns
|
|
814
|
-
</Text>
|
|
815
|
-
</HStack>
|
|
816
|
-
</HStack>
|
|
1025
|
+
<TrustBar />
|
|
817
1026
|
<VStack gap={2}>
|
|
818
1027
|
<Button
|
|
819
|
-
label="Place
|
|
1028
|
+
label="Place order"
|
|
820
1029
|
variant="primary"
|
|
821
1030
|
size="lg"
|
|
822
1031
|
style={fullWidth}
|
|
823
1032
|
onClick={() => setSubmitted(true)}
|
|
824
1033
|
/>
|
|
825
1034
|
<Button
|
|
826
|
-
label="Continue
|
|
1035
|
+
label="Continue shopping"
|
|
827
1036
|
variant="secondary"
|
|
828
1037
|
size="lg"
|
|
829
1038
|
style={fullWidth}
|
|
@@ -831,20 +1040,7 @@ export default function PaymentForm() {
|
|
|
831
1040
|
/>
|
|
832
1041
|
</VStack>
|
|
833
1042
|
<Divider />
|
|
834
|
-
<
|
|
835
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
836
|
-
Refund policy
|
|
837
|
-
</Link>
|
|
838
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
839
|
-
Privacy policy
|
|
840
|
-
</Link>
|
|
841
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
842
|
-
Terms of service
|
|
843
|
-
</Link>
|
|
844
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
845
|
-
Cancellations
|
|
846
|
-
</Link>
|
|
847
|
-
</HStack>
|
|
1043
|
+
<PolicyLinks />
|
|
848
1044
|
</VStack>
|
|
849
1045
|
</VStack>
|
|
850
1046
|
</StackItem>
|
|
@@ -852,132 +1048,16 @@ export default function PaymentForm() {
|
|
|
852
1048
|
<StackItem
|
|
853
1049
|
size="fill"
|
|
854
1050
|
style={isStacked ? summaryMobileOrder : summarySticky}>
|
|
855
|
-
<
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
<HStack gap={3} vAlign="start">
|
|
866
|
-
<Thumbnail
|
|
867
|
-
src={ITEM_IMAGES[item.id].src}
|
|
868
|
-
alt={item.name}
|
|
869
|
-
/>
|
|
870
|
-
<StackItem size="fill">
|
|
871
|
-
<VStack gap={1}>
|
|
872
|
-
<HStack
|
|
873
|
-
gap={2}
|
|
874
|
-
hAlign="between"
|
|
875
|
-
vAlign="start">
|
|
876
|
-
<HStack gap={2} vAlign="center" wrap="wrap">
|
|
877
|
-
<Text type="body" weight="medium">
|
|
878
|
-
{item.name}
|
|
879
|
-
</Text>
|
|
880
|
-
{item.limited && (
|
|
881
|
-
<Badge
|
|
882
|
-
variant="yellow"
|
|
883
|
-
label="LIMITED EDITION"
|
|
884
|
-
/>
|
|
885
|
-
)}
|
|
886
|
-
</HStack>
|
|
887
|
-
<Text
|
|
888
|
-
type="body"
|
|
889
|
-
weight="bold"
|
|
890
|
-
hasTabularNumbers>
|
|
891
|
-
{fmt(item.price)}
|
|
892
|
-
</Text>
|
|
893
|
-
</HStack>
|
|
894
|
-
<Text type="supporting" color="secondary">
|
|
895
|
-
{item.variant}
|
|
896
|
-
</Text>
|
|
897
|
-
<HStack gap={2} vAlign="end" wrap="wrap">
|
|
898
|
-
<NumberInput
|
|
899
|
-
label="Qty"
|
|
900
|
-
value={
|
|
901
|
-
quantities[item.id] ?? item.qty
|
|
902
|
-
}
|
|
903
|
-
onChange={v =>
|
|
904
|
-
setQuantities(q => ({
|
|
905
|
-
...q,
|
|
906
|
-
[item.id]: v,
|
|
907
|
-
}))
|
|
908
|
-
}
|
|
909
|
-
min={1}
|
|
910
|
-
max={10}
|
|
911
|
-
isIntegerOnly
|
|
912
|
-
/>
|
|
913
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
914
|
-
Remove
|
|
915
|
-
</Link>
|
|
916
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
917
|
-
Save
|
|
918
|
-
</Link>
|
|
919
|
-
</HStack>
|
|
920
|
-
</VStack>
|
|
921
|
-
</StackItem>
|
|
922
|
-
</HStack>
|
|
923
|
-
<Divider />
|
|
924
|
-
</VStack>
|
|
925
|
-
))}
|
|
926
|
-
|
|
927
|
-
{/* Order total subsection */}
|
|
928
|
-
<VStack gap={3}>
|
|
929
|
-
<Text type="large" weight="bold">
|
|
930
|
-
Order Total
|
|
931
|
-
</Text>
|
|
932
|
-
<VStack gap={2}>
|
|
933
|
-
<HStack hAlign="between" vAlign="center">
|
|
934
|
-
<Text type="body" color="secondary">
|
|
935
|
-
Subtotal
|
|
936
|
-
</Text>
|
|
937
|
-
<Text type="body" hasTabularNumbers>
|
|
938
|
-
{fmt(subtotal)}
|
|
939
|
-
</Text>
|
|
940
|
-
</HStack>
|
|
941
|
-
<HStack hAlign="between" vAlign="center">
|
|
942
|
-
<Text type="body" color="secondary">
|
|
943
|
-
Shipping
|
|
944
|
-
</Text>
|
|
945
|
-
<Text type="body" hasTabularNumbers>
|
|
946
|
-
{fmt(shipping)}
|
|
947
|
-
</Text>
|
|
948
|
-
</HStack>
|
|
949
|
-
<HStack hAlign="between" vAlign="center">
|
|
950
|
-
<Text type="body" color="secondary">
|
|
951
|
-
Tax
|
|
952
|
-
</Text>
|
|
953
|
-
<Text type="body" hasTabularNumbers>
|
|
954
|
-
{fmt(tax)}
|
|
955
|
-
</Text>
|
|
956
|
-
</HStack>
|
|
957
|
-
</VStack>
|
|
958
|
-
<Divider />
|
|
959
|
-
<HStack hAlign="between" vAlign="center">
|
|
960
|
-
<Text type="large" weight="bold">
|
|
961
|
-
Total
|
|
962
|
-
</Text>
|
|
963
|
-
<Text
|
|
964
|
-
type="large"
|
|
965
|
-
weight="bold"
|
|
966
|
-
hasTabularNumbers>
|
|
967
|
-
{fmt(total)}
|
|
968
|
-
</Text>
|
|
969
|
-
</HStack>
|
|
970
|
-
<Banner
|
|
971
|
-
status="info"
|
|
972
|
-
icon={<Icon icon={Truck} size="sm" />}
|
|
973
|
-
title="Free shipping on orders over $300"
|
|
974
|
-
/>
|
|
975
|
-
</VStack>
|
|
976
|
-
</VStack>
|
|
977
|
-
</Collapsible>
|
|
978
|
-
</VStack>
|
|
979
|
-
{/* end outer card VStack gap={4} */}
|
|
980
|
-
</Card>
|
|
1051
|
+
<OrderSummaryCard
|
|
1052
|
+
quantities={quantities}
|
|
1053
|
+
onChangeQty={(id, v) =>
|
|
1054
|
+
setQuantities(q => ({...q, [id]: v}))
|
|
1055
|
+
}
|
|
1056
|
+
subtotal={subtotal}
|
|
1057
|
+
shipping={shipping}
|
|
1058
|
+
tax={tax}
|
|
1059
|
+
total={total}
|
|
1060
|
+
/>
|
|
981
1061
|
</StackItem>
|
|
982
1062
|
</Stack>
|
|
983
1063
|
</VStack>
|