astryx-dracula 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BRAND.md +30 -18
- package/README.md +17 -12
- 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 +66 -33
- package/templates/ai-chat.tsx +36 -19
- package/templates/blank.tsx +4 -3
- package/templates/centered-hero.tsx +14 -125
- package/templates/classic-gallery.tsx +20 -66
- package/templates/contact-form.tsx +59 -39
- package/templates/dashboard-portfolio.tsx +143 -226
- package/templates/dashboard.tsx +122 -122
- package/templates/detail-page.tsx +95 -91
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +83 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +29 -19
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +52 -74
- package/templates/editor.tsx +112 -84
- package/templates/file-explorer.tsx +131 -84
- package/templates/form-two-column.tsx +61 -125
- package/templates/gallery-hero.tsx +15 -171
- package/templates/ide.tsx +22 -20
- package/templates/incident-console.tsx +104 -70
- package/templates/kanban-board.tsx +99 -42
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +37 -42
- package/templates/login-card.tsx +58 -87
- package/templates/login-split.tsx +88 -126
- package/templates/login-sso.tsx +105 -64
- package/templates/login.tsx +107 -53
- package/templates/messaging-shell.tsx +171 -144
- package/templates/mixed-gallery.tsx +29 -130
- package/templates/payment-form.tsx +368 -290
- package/templates/product-detail.tsx +39 -58
- package/templates/product-gallery.tsx +30 -41
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +135 -282
- package/templates/settings-sidebar.tsx +178 -268
- package/templates/settings.tsx +41 -14
- package/templates/shell-nav.tsx +31 -24
- package/templates/shell-side-nav.tsx +29 -20
- package/templates/shell-top-nav.tsx +80 -44
- package/templates/side-gallery.tsx +38 -94
- package/templates/table-grouped.tsx +62 -34
- package/templates/table-page-chart.tsx +23 -302
- package/templates/table-page-heatmap-status.tsx +156 -92
- package/templates/table-page-shoe-store-heatmap.tsx +18 -293
- package/templates/table-page.tsx +23 -7
- package/templates/table.tsx +23 -8
- 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 +299 -237
- package/theme.css +76 -14
- package/tokens.css +425 -30
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > LC[p=0] > Ctr >
|
|
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"))))
|
|
4
4
|
|
|
5
5
|
import {useState, type CSSProperties} from 'react';
|
|
6
6
|
import {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@astryxdesign/core/Layout';
|
|
14
14
|
import {Grid} from '@astryxdesign/core/Grid';
|
|
15
15
|
import {Button} from '@astryxdesign/core/Button';
|
|
16
|
-
import {Text} from '@astryxdesign/core/Text';
|
|
16
|
+
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
17
17
|
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
18
18
|
import {Selector} from '@astryxdesign/core/Selector';
|
|
19
19
|
import {CheckboxInput} from '@astryxdesign/core/CheckboxInput';
|
|
@@ -23,13 +23,14 @@ import {TextArea} from '@astryxdesign/core/TextArea';
|
|
|
23
23
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
24
24
|
import {Banner} from '@astryxdesign/core/Banner';
|
|
25
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';
|
|
26
28
|
import {Collapsible} from '@astryxdesign/core/Collapsible';
|
|
27
|
-
import {
|
|
29
|
+
import {StatusDot} from '@astryxdesign/core/StatusDot';
|
|
28
30
|
import {NumberInput} from '@astryxdesign/core/NumberInput';
|
|
29
31
|
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
30
32
|
import {Section} from '@astryxdesign/core/Section';
|
|
31
33
|
import {Center} from '@astryxdesign/core/Center';
|
|
32
|
-
import {Thumbnail} from '@astryxdesign/core/Thumbnail';
|
|
33
34
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
34
35
|
import {ShieldCheck, Lock, CircleCheck, Truck} from 'lucide-react';
|
|
35
36
|
|
|
@@ -104,18 +105,14 @@ const US_STATES = [
|
|
|
104
105
|
'Wyoming',
|
|
105
106
|
];
|
|
106
107
|
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
'2':
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
'3': {
|
|
117
|
-
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',
|
|
118
|
-
},
|
|
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)',
|
|
119
116
|
};
|
|
120
117
|
|
|
121
118
|
const ORDER_ITEMS = [
|
|
@@ -167,19 +164,15 @@ const summarySticky: CSSProperties = {
|
|
|
167
164
|
};
|
|
168
165
|
// On mobile the summary moves above the form.
|
|
169
166
|
const summaryMobileOrder: CSSProperties = {order: -1};
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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,
|
|
174
175
|
};
|
|
175
|
-
// Google Pay button: widget-surface token stands in for the official dark
|
|
176
|
-
// button background (no raw hex on brand surfaces).
|
|
177
|
-
const gpayButton: CSSProperties = {
|
|
178
|
-
backgroundColor: 'var(--color-widget-background)',
|
|
179
|
-
borderColor: 'var(--color-widget-background)',
|
|
180
|
-
};
|
|
181
|
-
// Brand logos inside the express-checkout buttons.
|
|
182
|
-
const brandLogo: CSSProperties = {height: 'var(--spacing-5)', width: 'auto'};
|
|
183
176
|
// Accepted card-network marks (Visa/Mastercard/Amex), shared style.
|
|
184
177
|
const cardLogo: CSSProperties = {
|
|
185
178
|
height: 'var(--spacing-7)',
|
|
@@ -187,12 +180,231 @@ const cardLogo: CSSProperties = {
|
|
|
187
180
|
borderRadius: 'var(--radius-element)',
|
|
188
181
|
borderWidth: 'var(--border-width)',
|
|
189
182
|
borderStyle: 'solid',
|
|
190
|
-
borderColor: 'var(--color-
|
|
183
|
+
borderColor: 'var(--color-separator)',
|
|
191
184
|
backgroundColor: 'var(--color-background-surface)',
|
|
192
185
|
};
|
|
193
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
|
+
|
|
194
397
|
export default function PaymentForm() {
|
|
195
|
-
|
|
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)');
|
|
196
408
|
const isStacked = useMediaQuery('(max-width: 1024px)');
|
|
197
409
|
const [firstName, setFirstName] = useState('');
|
|
198
410
|
const [lastName, setLastName] = useState('');
|
|
@@ -294,9 +506,9 @@ export default function PaymentForm() {
|
|
|
294
506
|
{/* Page header */}
|
|
295
507
|
<VStack gap={6}>
|
|
296
508
|
<VStack gap={2}>
|
|
297
|
-
<
|
|
509
|
+
<Heading level={1} type="display-2">
|
|
298
510
|
Payment Request
|
|
299
|
-
</
|
|
511
|
+
</Heading>
|
|
300
512
|
<Text type="body" color="secondary">
|
|
301
513
|
Review your order and complete your purchase. All
|
|
302
514
|
transactions are sealed with 256-bit SSL encryption.
|
|
@@ -316,13 +528,11 @@ export default function PaymentForm() {
|
|
|
316
528
|
{/* Sign in */}
|
|
317
529
|
<VStack gap={1}>
|
|
318
530
|
<HStack gap={2} hAlign="between" vAlign="center">
|
|
319
|
-
<
|
|
320
|
-
Sign in to check out
|
|
321
|
-
</Text>
|
|
531
|
+
<Heading level={2}>Sign in to check out</Heading>
|
|
322
532
|
<Button
|
|
323
|
-
label="Sign
|
|
533
|
+
label="Sign in"
|
|
324
534
|
variant="secondary"
|
|
325
|
-
|
|
535
|
+
|
|
326
536
|
onClick={() => {}}
|
|
327
537
|
/>
|
|
328
538
|
</HStack>
|
|
@@ -334,13 +544,12 @@ export default function PaymentForm() {
|
|
|
334
544
|
|
|
335
545
|
{/* Contact Information */}
|
|
336
546
|
<VStack gap={3}>
|
|
337
|
-
<
|
|
338
|
-
Contact Information
|
|
339
|
-
</Text>
|
|
547
|
+
<Heading level={2}>Contact information</Heading>
|
|
340
548
|
<TextInput
|
|
341
549
|
size="lg"
|
|
342
550
|
label="Email"
|
|
343
|
-
|
|
551
|
+
isRequired
|
|
552
|
+
placeholder="you@castle.dracula"
|
|
344
553
|
value={email}
|
|
345
554
|
onChange={setEmail}
|
|
346
555
|
status={
|
|
@@ -358,14 +567,13 @@ export default function PaymentForm() {
|
|
|
358
567
|
|
|
359
568
|
{/* Shipping Information */}
|
|
360
569
|
<VStack gap={3}>
|
|
361
|
-
<
|
|
362
|
-
Shipping Information
|
|
363
|
-
</Text>
|
|
570
|
+
<Heading level={2}>Shipping information</Heading>
|
|
364
571
|
<Grid columns={isMobile ? 1 : 2} gap={3}>
|
|
365
572
|
<TextInput
|
|
366
573
|
size="lg"
|
|
367
574
|
label="First Name"
|
|
368
|
-
|
|
575
|
+
isRequired
|
|
576
|
+
placeholder="Vlad"
|
|
369
577
|
value={firstName}
|
|
370
578
|
onChange={setFirstName}
|
|
371
579
|
status={
|
|
@@ -377,7 +585,8 @@ export default function PaymentForm() {
|
|
|
377
585
|
<TextInput
|
|
378
586
|
size="lg"
|
|
379
587
|
label="Last Name"
|
|
380
|
-
|
|
588
|
+
isRequired
|
|
589
|
+
placeholder="Dracul"
|
|
381
590
|
value={lastName}
|
|
382
591
|
onChange={setLastName}
|
|
383
592
|
status={
|
|
@@ -390,7 +599,8 @@ export default function PaymentForm() {
|
|
|
390
599
|
<TextInput
|
|
391
600
|
size="lg"
|
|
392
601
|
label="Address"
|
|
393
|
-
|
|
602
|
+
isRequired
|
|
603
|
+
placeholder="666 Castle Way"
|
|
394
604
|
value={address}
|
|
395
605
|
onChange={setAddress}
|
|
396
606
|
status={
|
|
@@ -403,7 +613,8 @@ export default function PaymentForm() {
|
|
|
403
613
|
<TextInput
|
|
404
614
|
size="lg"
|
|
405
615
|
label="City"
|
|
406
|
-
|
|
616
|
+
isRequired
|
|
617
|
+
placeholder="Transylvania"
|
|
407
618
|
value={city}
|
|
408
619
|
onChange={setCity}
|
|
409
620
|
status={
|
|
@@ -415,7 +626,8 @@ export default function PaymentForm() {
|
|
|
415
626
|
<TextInput
|
|
416
627
|
size="lg"
|
|
417
628
|
label="ZIP Code"
|
|
418
|
-
|
|
629
|
+
isRequired
|
|
630
|
+
placeholder="40000"
|
|
419
631
|
value={zip}
|
|
420
632
|
onChange={setZip}
|
|
421
633
|
status={
|
|
@@ -428,6 +640,7 @@ export default function PaymentForm() {
|
|
|
428
640
|
<Selector
|
|
429
641
|
size="lg"
|
|
430
642
|
label="State"
|
|
643
|
+
isRequired
|
|
431
644
|
placeholder="Select state"
|
|
432
645
|
options={US_STATES}
|
|
433
646
|
value={state}
|
|
@@ -441,6 +654,7 @@ export default function PaymentForm() {
|
|
|
441
654
|
<TextInput
|
|
442
655
|
size="lg"
|
|
443
656
|
label="Phone Number"
|
|
657
|
+
isRequired
|
|
444
658
|
placeholder="+1 (555) 123-4567"
|
|
445
659
|
value={phone}
|
|
446
660
|
onChange={setPhone}
|
|
@@ -461,9 +675,7 @@ export default function PaymentForm() {
|
|
|
461
675
|
{/* Delivery */}
|
|
462
676
|
<VStack gap={3}>
|
|
463
677
|
<VStack gap={1}>
|
|
464
|
-
<
|
|
465
|
-
Delivery
|
|
466
|
-
</Text>
|
|
678
|
+
<Heading level={2}>Delivery</Heading>
|
|
467
679
|
<Text type="body" color="secondary">
|
|
468
680
|
Please allow 1–3 business days processing time
|
|
469
681
|
before your order ships.
|
|
@@ -479,7 +691,7 @@ export default function PaymentForm() {
|
|
|
479
691
|
endContent={
|
|
480
692
|
<Text
|
|
481
693
|
type="body"
|
|
482
|
-
weight="
|
|
694
|
+
weight="semibold"
|
|
483
695
|
hasTabularNumbers>
|
|
484
696
|
$4.95
|
|
485
697
|
</Text>
|
|
@@ -491,7 +703,7 @@ export default function PaymentForm() {
|
|
|
491
703
|
endContent={
|
|
492
704
|
<Text
|
|
493
705
|
type="body"
|
|
494
|
-
weight="
|
|
706
|
+
weight="semibold"
|
|
495
707
|
hasTabularNumbers>
|
|
496
708
|
$9.95
|
|
497
709
|
</Text>
|
|
@@ -503,9 +715,7 @@ export default function PaymentForm() {
|
|
|
503
715
|
{/* Payment Method */}
|
|
504
716
|
<VStack gap={3}>
|
|
505
717
|
<VStack gap={1}>
|
|
506
|
-
<
|
|
507
|
-
Payment Method
|
|
508
|
-
</Text>
|
|
718
|
+
<Heading level={2}>Payment method</Heading>
|
|
509
719
|
<Text type="body" color="secondary">
|
|
510
720
|
All transactions are secure and encrypted.
|
|
511
721
|
</Text>
|
|
@@ -517,68 +727,95 @@ export default function PaymentForm() {
|
|
|
517
727
|
{/* PayPal */}
|
|
518
728
|
<Button
|
|
519
729
|
label="PayPal"
|
|
520
|
-
variant="
|
|
730
|
+
variant="secondary"
|
|
521
731
|
size="lg"
|
|
522
732
|
onClick={() => {}}
|
|
523
|
-
|
|
524
|
-
<img
|
|
525
|
-
src="https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png"
|
|
526
|
-
alt="PayPal"
|
|
527
|
-
style={brandLogo}
|
|
528
|
-
/>
|
|
529
|
-
</Button>
|
|
733
|
+
/>
|
|
530
734
|
{/* Google Pay */}
|
|
531
735
|
<Button
|
|
532
736
|
label="Google Pay"
|
|
533
|
-
variant="
|
|
737
|
+
variant="secondary"
|
|
534
738
|
size="lg"
|
|
535
739
|
onClick={() => {}}
|
|
536
|
-
|
|
537
|
-
<img
|
|
538
|
-
src="https://pay.google.com/about/static_kcs/images/logos/google-pay-logo.svg"
|
|
539
|
-
alt="Google Pay"
|
|
540
|
-
style={brandLogo}
|
|
541
|
-
/>
|
|
542
|
-
</Button>
|
|
740
|
+
/>
|
|
543
741
|
</Grid>
|
|
544
742
|
</VStack>
|
|
545
743
|
|
|
546
|
-
|
|
547
|
-
<HStack gap={3} vAlign="center">
|
|
548
|
-
<StackItem size="fill">
|
|
549
|
-
<Divider />
|
|
550
|
-
</StackItem>
|
|
551
|
-
<Text type="supporting" color="secondary">
|
|
552
|
-
OR
|
|
553
|
-
</Text>
|
|
554
|
-
<StackItem size="fill">
|
|
555
|
-
<Divider />
|
|
556
|
-
</StackItem>
|
|
557
|
-
</HStack>
|
|
744
|
+
<Divider label="Or pay with card" />
|
|
558
745
|
|
|
559
746
|
{/* Credit card fields */}
|
|
560
747
|
<VStack gap={3}>
|
|
561
|
-
{/* Card type icons
|
|
748
|
+
{/* Card type icons — inline Dracula marks on
|
|
749
|
+
brand tokens; no vendor hotlinks. */}
|
|
562
750
|
<HStack gap={1.5} vAlign="center">
|
|
563
|
-
<
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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>
|
|
578
814
|
</HStack>
|
|
579
815
|
<TextInput
|
|
580
816
|
size="lg"
|
|
581
817
|
label="Card Number"
|
|
818
|
+
isRequired
|
|
582
819
|
placeholder="1234 5678 9012 3456"
|
|
583
820
|
value={cardNumber}
|
|
584
821
|
onChange={setCardNumber}
|
|
@@ -592,6 +829,7 @@ export default function PaymentForm() {
|
|
|
592
829
|
<Selector
|
|
593
830
|
size="lg"
|
|
594
831
|
label="Expiry Month"
|
|
832
|
+
isRequired
|
|
595
833
|
placeholder="MM"
|
|
596
834
|
options={MONTHS}
|
|
597
835
|
value={expiry}
|
|
@@ -605,6 +843,7 @@ export default function PaymentForm() {
|
|
|
605
843
|
<Selector
|
|
606
844
|
size="lg"
|
|
607
845
|
label="Expiry Year"
|
|
846
|
+
isRequired
|
|
608
847
|
placeholder="YY"
|
|
609
848
|
options={YEARS}
|
|
610
849
|
value={expYear}
|
|
@@ -618,6 +857,7 @@ export default function PaymentForm() {
|
|
|
618
857
|
<TextInput
|
|
619
858
|
size="lg"
|
|
620
859
|
label="CVC"
|
|
860
|
+
isRequired
|
|
621
861
|
placeholder="123"
|
|
622
862
|
value={cvc}
|
|
623
863
|
onChange={setCvc}
|
|
@@ -632,7 +872,8 @@ export default function PaymentForm() {
|
|
|
632
872
|
<TextInput
|
|
633
873
|
size="lg"
|
|
634
874
|
label="Name on Card"
|
|
635
|
-
|
|
875
|
+
isRequired
|
|
876
|
+
placeholder="Vlad Dracul"
|
|
636
877
|
value={cardName}
|
|
637
878
|
onChange={setCardName}
|
|
638
879
|
status={
|
|
@@ -651,7 +892,8 @@ export default function PaymentForm() {
|
|
|
651
892
|
<TextInput
|
|
652
893
|
size="lg"
|
|
653
894
|
label="Address"
|
|
654
|
-
|
|
895
|
+
isRequired
|
|
896
|
+
placeholder="666 Castle Way"
|
|
655
897
|
value={billingAddress}
|
|
656
898
|
onChange={setBillingAddress}
|
|
657
899
|
status={
|
|
@@ -667,7 +909,8 @@ export default function PaymentForm() {
|
|
|
667
909
|
<TextInput
|
|
668
910
|
size="lg"
|
|
669
911
|
label="City"
|
|
670
|
-
|
|
912
|
+
isRequired
|
|
913
|
+
placeholder="Transylvania"
|
|
671
914
|
value={billingCity}
|
|
672
915
|
onChange={setBillingCity}
|
|
673
916
|
status={
|
|
@@ -682,7 +925,8 @@ export default function PaymentForm() {
|
|
|
682
925
|
<TextInput
|
|
683
926
|
size="lg"
|
|
684
927
|
label="ZIP Code"
|
|
685
|
-
|
|
928
|
+
isRequired
|
|
929
|
+
placeholder="40000"
|
|
686
930
|
value={billingZip}
|
|
687
931
|
onChange={setBillingZip}
|
|
688
932
|
status={
|
|
@@ -698,6 +942,7 @@ export default function PaymentForm() {
|
|
|
698
942
|
<Selector
|
|
699
943
|
size="lg"
|
|
700
944
|
label="State"
|
|
945
|
+
isRequired
|
|
701
946
|
placeholder="Select state"
|
|
702
947
|
options={US_STATES}
|
|
703
948
|
value={billingState}
|
|
@@ -718,9 +963,7 @@ export default function PaymentForm() {
|
|
|
718
963
|
|
|
719
964
|
{/* Promo Code */}
|
|
720
965
|
<VStack gap={3}>
|
|
721
|
-
<
|
|
722
|
-
Promo Code
|
|
723
|
-
</Text>
|
|
966
|
+
<Heading level={2}>Promo code</Heading>
|
|
724
967
|
<HStack gap={2} vAlign="end">
|
|
725
968
|
<StackItem size="fill">
|
|
726
969
|
<TextInput
|
|
@@ -743,9 +986,7 @@ export default function PaymentForm() {
|
|
|
743
986
|
|
|
744
987
|
{/* Gift Options */}
|
|
745
988
|
<VStack gap={3}>
|
|
746
|
-
<
|
|
747
|
-
Gift Options
|
|
748
|
-
</Text>
|
|
989
|
+
<Heading level={2}>Gift options</Heading>
|
|
749
990
|
<CheckboxInput
|
|
750
991
|
label="Add a gift message"
|
|
751
992
|
value={addGiftMessage}
|
|
@@ -757,23 +998,20 @@ export default function PaymentForm() {
|
|
|
757
998
|
<TextInput
|
|
758
999
|
size="lg"
|
|
759
1000
|
label="To"
|
|
760
|
-
|
|
761
|
-
placeholder="To"
|
|
1001
|
+
placeholder="Recipient name"
|
|
762
1002
|
value={giftTo}
|
|
763
1003
|
onChange={setGiftTo}
|
|
764
1004
|
/>
|
|
765
1005
|
<TextInput
|
|
766
1006
|
size="lg"
|
|
767
1007
|
label="From"
|
|
768
|
-
|
|
769
|
-
placeholder="From"
|
|
1008
|
+
placeholder="Your name"
|
|
770
1009
|
value={giftFrom}
|
|
771
1010
|
onChange={setGiftFrom}
|
|
772
1011
|
/>
|
|
773
1012
|
</Grid>
|
|
774
1013
|
<TextArea
|
|
775
1014
|
label="Gift message"
|
|
776
|
-
isLabelHidden
|
|
777
1015
|
placeholder="Write something here"
|
|
778
1016
|
value={giftMessage}
|
|
779
1017
|
onChange={setGiftMessage}
|
|
@@ -784,48 +1022,17 @@ export default function PaymentForm() {
|
|
|
784
1022
|
|
|
785
1023
|
{/* Trust bar + CTAs + policy links */}
|
|
786
1024
|
<VStack gap={4}>
|
|
787
|
-
<
|
|
788
|
-
<HStack gap={1} vAlign="center">
|
|
789
|
-
<Icon
|
|
790
|
-
icon={ShieldCheck}
|
|
791
|
-
size="sm"
|
|
792
|
-
color="secondary"
|
|
793
|
-
/>
|
|
794
|
-
<Text type="supporting" color="secondary">
|
|
795
|
-
Secure Payment
|
|
796
|
-
</Text>
|
|
797
|
-
</HStack>
|
|
798
|
-
<HStack gap={1} vAlign="center">
|
|
799
|
-
<Icon
|
|
800
|
-
icon={Lock}
|
|
801
|
-
size="sm"
|
|
802
|
-
color="secondary"
|
|
803
|
-
/>
|
|
804
|
-
<Text type="supporting" color="secondary">
|
|
805
|
-
SSL Encrypted
|
|
806
|
-
</Text>
|
|
807
|
-
</HStack>
|
|
808
|
-
<HStack gap={1} vAlign="center">
|
|
809
|
-
<Icon
|
|
810
|
-
icon={CircleCheck}
|
|
811
|
-
size="sm"
|
|
812
|
-
color="secondary"
|
|
813
|
-
/>
|
|
814
|
-
<Text type="supporting" color="secondary">
|
|
815
|
-
Free Returns
|
|
816
|
-
</Text>
|
|
817
|
-
</HStack>
|
|
818
|
-
</HStack>
|
|
1025
|
+
<TrustBar />
|
|
819
1026
|
<VStack gap={2}>
|
|
820
1027
|
<Button
|
|
821
|
-
label="Place
|
|
1028
|
+
label="Place order"
|
|
822
1029
|
variant="primary"
|
|
823
1030
|
size="lg"
|
|
824
1031
|
style={fullWidth}
|
|
825
1032
|
onClick={() => setSubmitted(true)}
|
|
826
1033
|
/>
|
|
827
1034
|
<Button
|
|
828
|
-
label="Continue
|
|
1035
|
+
label="Continue shopping"
|
|
829
1036
|
variant="secondary"
|
|
830
1037
|
size="lg"
|
|
831
1038
|
style={fullWidth}
|
|
@@ -833,20 +1040,7 @@ export default function PaymentForm() {
|
|
|
833
1040
|
/>
|
|
834
1041
|
</VStack>
|
|
835
1042
|
<Divider />
|
|
836
|
-
<
|
|
837
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
838
|
-
Refund policy
|
|
839
|
-
</Link>
|
|
840
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
841
|
-
Privacy policy
|
|
842
|
-
</Link>
|
|
843
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
844
|
-
Terms of service
|
|
845
|
-
</Link>
|
|
846
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
847
|
-
Cancellations
|
|
848
|
-
</Link>
|
|
849
|
-
</HStack>
|
|
1043
|
+
<PolicyLinks />
|
|
850
1044
|
</VStack>
|
|
851
1045
|
</VStack>
|
|
852
1046
|
</StackItem>
|
|
@@ -854,132 +1048,16 @@ export default function PaymentForm() {
|
|
|
854
1048
|
<StackItem
|
|
855
1049
|
size="fill"
|
|
856
1050
|
style={isStacked ? summaryMobileOrder : summarySticky}>
|
|
857
|
-
<
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
<HStack gap={3} vAlign="start">
|
|
868
|
-
<Thumbnail
|
|
869
|
-
src={ITEM_IMAGES[item.id].src}
|
|
870
|
-
alt={item.name}
|
|
871
|
-
/>
|
|
872
|
-
<StackItem size="fill">
|
|
873
|
-
<VStack gap={1}>
|
|
874
|
-
<HStack
|
|
875
|
-
gap={2}
|
|
876
|
-
hAlign="between"
|
|
877
|
-
vAlign="start">
|
|
878
|
-
<HStack gap={2} vAlign="center" wrap="wrap">
|
|
879
|
-
<Text type="body" weight="medium">
|
|
880
|
-
{item.name}
|
|
881
|
-
</Text>
|
|
882
|
-
{item.limited && (
|
|
883
|
-
<Badge
|
|
884
|
-
variant="yellow"
|
|
885
|
-
label="LIMITED EDITION"
|
|
886
|
-
/>
|
|
887
|
-
)}
|
|
888
|
-
</HStack>
|
|
889
|
-
<Text
|
|
890
|
-
type="body"
|
|
891
|
-
weight="bold"
|
|
892
|
-
hasTabularNumbers>
|
|
893
|
-
{fmt(item.price)}
|
|
894
|
-
</Text>
|
|
895
|
-
</HStack>
|
|
896
|
-
<Text type="supporting" color="secondary">
|
|
897
|
-
{item.variant}
|
|
898
|
-
</Text>
|
|
899
|
-
<HStack gap={2} vAlign="end" wrap="wrap">
|
|
900
|
-
<NumberInput
|
|
901
|
-
label="Qty"
|
|
902
|
-
value={
|
|
903
|
-
quantities[item.id] ?? item.qty
|
|
904
|
-
}
|
|
905
|
-
onChange={v =>
|
|
906
|
-
setQuantities(q => ({
|
|
907
|
-
...q,
|
|
908
|
-
[item.id]: v,
|
|
909
|
-
}))
|
|
910
|
-
}
|
|
911
|
-
min={1}
|
|
912
|
-
max={10}
|
|
913
|
-
isIntegerOnly
|
|
914
|
-
/>
|
|
915
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
916
|
-
Remove
|
|
917
|
-
</Link>
|
|
918
|
-
<Link href="#/templates/payment-form" type="supporting">
|
|
919
|
-
Save
|
|
920
|
-
</Link>
|
|
921
|
-
</HStack>
|
|
922
|
-
</VStack>
|
|
923
|
-
</StackItem>
|
|
924
|
-
</HStack>
|
|
925
|
-
<Divider />
|
|
926
|
-
</VStack>
|
|
927
|
-
))}
|
|
928
|
-
|
|
929
|
-
{/* Order total subsection */}
|
|
930
|
-
<VStack gap={3}>
|
|
931
|
-
<Text type="large" weight="bold">
|
|
932
|
-
Order Total
|
|
933
|
-
</Text>
|
|
934
|
-
<VStack gap={2}>
|
|
935
|
-
<HStack hAlign="between" vAlign="center">
|
|
936
|
-
<Text type="body" color="secondary">
|
|
937
|
-
Subtotal
|
|
938
|
-
</Text>
|
|
939
|
-
<Text type="body" hasTabularNumbers>
|
|
940
|
-
{fmt(subtotal)}
|
|
941
|
-
</Text>
|
|
942
|
-
</HStack>
|
|
943
|
-
<HStack hAlign="between" vAlign="center">
|
|
944
|
-
<Text type="body" color="secondary">
|
|
945
|
-
Shipping
|
|
946
|
-
</Text>
|
|
947
|
-
<Text type="body" hasTabularNumbers>
|
|
948
|
-
{fmt(shipping)}
|
|
949
|
-
</Text>
|
|
950
|
-
</HStack>
|
|
951
|
-
<HStack hAlign="between" vAlign="center">
|
|
952
|
-
<Text type="body" color="secondary">
|
|
953
|
-
Tax
|
|
954
|
-
</Text>
|
|
955
|
-
<Text type="body" hasTabularNumbers>
|
|
956
|
-
{fmt(tax)}
|
|
957
|
-
</Text>
|
|
958
|
-
</HStack>
|
|
959
|
-
</VStack>
|
|
960
|
-
<Divider />
|
|
961
|
-
<HStack hAlign="between" vAlign="center">
|
|
962
|
-
<Text type="large" weight="bold">
|
|
963
|
-
Total
|
|
964
|
-
</Text>
|
|
965
|
-
<Text
|
|
966
|
-
type="large"
|
|
967
|
-
weight="bold"
|
|
968
|
-
hasTabularNumbers>
|
|
969
|
-
{fmt(total)}
|
|
970
|
-
</Text>
|
|
971
|
-
</HStack>
|
|
972
|
-
<Banner
|
|
973
|
-
status="info"
|
|
974
|
-
icon={<Icon icon={Truck} size="sm" />}
|
|
975
|
-
title="Free shipping on orders over $300"
|
|
976
|
-
/>
|
|
977
|
-
</VStack>
|
|
978
|
-
</VStack>
|
|
979
|
-
</Collapsible>
|
|
980
|
-
</VStack>
|
|
981
|
-
{/* end outer card VStack gap={4} */}
|
|
982
|
-
</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
|
+
/>
|
|
983
1061
|
</StackItem>
|
|
984
1062
|
</Stack>
|
|
985
1063
|
</VStack>
|