@akinon/pz-theme 2.0.65-beta.0 → 2.0.65-rc.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/CHANGELOG.md +31 -7
- package/package.json +4 -4
- package/src/blocks/order-detail-block.tsx +124 -600
- package/src/blocks/order-list-block.tsx +101 -477
|
@@ -30,26 +30,10 @@ interface OrderItemLike {
|
|
|
30
30
|
name?: string;
|
|
31
31
|
image?: string;
|
|
32
32
|
absolute_url?: string;
|
|
33
|
-
attributes?: Record<string, unknown>;
|
|
34
33
|
} | null;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
interface AddressLike {
|
|
38
|
-
first_name?: string;
|
|
39
|
-
last_name?: string;
|
|
40
|
-
line?: string;
|
|
41
|
-
township?: { name?: string } | null;
|
|
42
|
-
district?: { name?: string } | null;
|
|
43
|
-
city?: { name?: string } | null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface DiscountItemLike {
|
|
47
|
-
name?: string;
|
|
48
|
-
amount?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
36
|
interface OrderLike {
|
|
52
|
-
id?: number;
|
|
53
37
|
number?: string;
|
|
54
38
|
created_date?: string;
|
|
55
39
|
amount?: string;
|
|
@@ -61,14 +45,6 @@ interface OrderLike {
|
|
|
61
45
|
status?: { value?: string; label?: string };
|
|
62
46
|
currency?: { value?: string; label?: string };
|
|
63
47
|
orderitem_set?: OrderItemLike[];
|
|
64
|
-
discountitem_set?: DiscountItemLike[];
|
|
65
|
-
is_cancellable?: boolean;
|
|
66
|
-
e_archive_url?: string | null;
|
|
67
|
-
shipping_address?: AddressLike | null;
|
|
68
|
-
billing_address?: AddressLike | null;
|
|
69
|
-
bank?: { name?: string } | null;
|
|
70
|
-
payment_option?: { name?: string } | null;
|
|
71
|
-
installment_count?: number;
|
|
72
48
|
}
|
|
73
49
|
|
|
74
50
|
/**
|
|
@@ -112,135 +88,6 @@ const readSize = (value: unknown, fallback: number): number => {
|
|
|
112
88
|
return Number.isFinite(numeric) && numeric > 0 ? numeric : fallback;
|
|
113
89
|
};
|
|
114
90
|
|
|
115
|
-
/**
|
|
116
|
-
* pz-dynamic-widget's page picker points the designer at a fixed, almost
|
|
117
|
-
* certainly non-existent order id (`PREVIEW_ORDER_PK`) so the route mounts
|
|
118
|
-
* without depending on the preview session having a real order — deliberate,
|
|
119
|
-
* per that file's own comment. The trade-off lands here: without a sample
|
|
120
|
-
* object, every section below (header, lines, addresses, totals, payment)
|
|
121
|
-
* would sit behind one "order not found" box, unstylable. This mirrors
|
|
122
|
-
* `DESIGNER_SAMPLE_PRODUCT`'s role for item-template rows, just for the
|
|
123
|
-
* whole order. Never reached outside `isDesigner`, so real shoppers never
|
|
124
|
-
* see it. The color/size attribute keys are duplicated under a few common
|
|
125
|
-
* names so a brand's configured key is more likely to resolve to something
|
|
126
|
-
* out of the box.
|
|
127
|
-
*/
|
|
128
|
-
const DESIGNER_SAMPLE_ORDER: OrderLike = {
|
|
129
|
-
// Never 0 — `order.id ? ... : ''` further down would read a real falsy
|
|
130
|
-
// zero as "no id" and hide the cancellation link.
|
|
131
|
-
id: 999999,
|
|
132
|
-
number: '20920397829',
|
|
133
|
-
created_date: '2026-01-15T10:00:00Z',
|
|
134
|
-
amount: '4099.20',
|
|
135
|
-
amount_without_discount: '4599.00',
|
|
136
|
-
discount_amount: '3497.00',
|
|
137
|
-
shipping_amount: '0.00',
|
|
138
|
-
status: { value: '10', label: 'Approved' },
|
|
139
|
-
currency: { value: 'try', label: 'TRY' },
|
|
140
|
-
is_cancellable: true,
|
|
141
|
-
e_archive_url: '#',
|
|
142
|
-
installment_count: 3,
|
|
143
|
-
orderitem_set: [
|
|
144
|
-
{
|
|
145
|
-
id: 1,
|
|
146
|
-
price: '2299.50',
|
|
147
|
-
quantity: 1,
|
|
148
|
-
total_amount: '2299.50',
|
|
149
|
-
status: { value: '10', label: 'Approved' },
|
|
150
|
-
price_currency: { value: 'try', label: 'TRY' },
|
|
151
|
-
product: {
|
|
152
|
-
pk: 1,
|
|
153
|
-
name: 'Sample Product',
|
|
154
|
-
image: 'https://placehold.co/220x300',
|
|
155
|
-
absolute_url: '#',
|
|
156
|
-
attributes: {
|
|
157
|
-
color: 'Navy',
|
|
158
|
-
size: 'M',
|
|
159
|
-
filterable_color: 'Navy',
|
|
160
|
-
integration_renk_detay: 'Navy',
|
|
161
|
-
filtrenebilir_beden: 'M'
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
id: 2,
|
|
167
|
-
price: '2299.50',
|
|
168
|
-
quantity: 1,
|
|
169
|
-
total_amount: '2299.50',
|
|
170
|
-
status: { value: '10', label: 'Approved' },
|
|
171
|
-
price_currency: { value: 'try', label: 'TRY' },
|
|
172
|
-
product: {
|
|
173
|
-
pk: 2,
|
|
174
|
-
name: 'Sample Product 2',
|
|
175
|
-
image: 'https://placehold.co/220x300',
|
|
176
|
-
absolute_url: '#',
|
|
177
|
-
attributes: {
|
|
178
|
-
color: 'Black',
|
|
179
|
-
size: 'L',
|
|
180
|
-
filterable_color: 'Black',
|
|
181
|
-
integration_renk_detay: 'Black',
|
|
182
|
-
filtrenebilir_beden: 'L'
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
],
|
|
187
|
-
discountitem_set: [
|
|
188
|
-
{ name: 'Basket discount', amount: '-2499.00' },
|
|
189
|
-
{ name: 'Promo code', amount: '-998.00' }
|
|
190
|
-
],
|
|
191
|
-
shipping_address: {
|
|
192
|
-
first_name: 'Jane',
|
|
193
|
-
last_name: 'Doe',
|
|
194
|
-
line: '123 Sample Street',
|
|
195
|
-
district: { name: 'Downtown' },
|
|
196
|
-
township: { name: 'Central' },
|
|
197
|
-
city: { name: 'Istanbul' }
|
|
198
|
-
},
|
|
199
|
-
billing_address: {
|
|
200
|
-
first_name: 'Jane',
|
|
201
|
-
last_name: 'Doe',
|
|
202
|
-
line: '123 Sample Street',
|
|
203
|
-
district: { name: 'Downtown' },
|
|
204
|
-
township: { name: 'Central' },
|
|
205
|
-
city: { name: 'Istanbul' }
|
|
206
|
-
},
|
|
207
|
-
bank: { name: 'Sample Bank' },
|
|
208
|
-
payment_option: { name: 'Credit Card' }
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Attribute names are brand-specific (a raw commerce feed, not a fixed
|
|
213
|
-
* schema), so color/size read from a designer-configured key rather than a
|
|
214
|
-
* hardcoded one — the alternative is a block that only ever works for the
|
|
215
|
-
* one brand whose attribute names happened to match.
|
|
216
|
-
*/
|
|
217
|
-
const resolveAttribute = (
|
|
218
|
-
attributes: Record<string, unknown> | undefined | null,
|
|
219
|
-
key: string
|
|
220
|
-
): string => {
|
|
221
|
-
if (!key || !attributes) return '';
|
|
222
|
-
const value = attributes[key];
|
|
223
|
-
|
|
224
|
-
return typeof value === 'string' ? value : '';
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const formatAddress = (address: AddressLike | null | undefined): string[] => {
|
|
228
|
-
if (!address) return [];
|
|
229
|
-
const name = [address.first_name, address.last_name]
|
|
230
|
-
.filter(Boolean)
|
|
231
|
-
.join(' ');
|
|
232
|
-
const locality = [
|
|
233
|
-
address.district?.name,
|
|
234
|
-
address.township?.name,
|
|
235
|
-
address.city?.name
|
|
236
|
-
]
|
|
237
|
-
.filter(Boolean)
|
|
238
|
-
.join(' / ');
|
|
239
|
-
const lineWithLocality = [address.line, locality].filter(Boolean).join(' ');
|
|
240
|
-
|
|
241
|
-
return [name, lineWithLocality].filter(Boolean);
|
|
242
|
-
};
|
|
243
|
-
|
|
244
91
|
/**
|
|
245
92
|
* Functional account block: one order's lines, totals, status and shipment
|
|
246
93
|
* info. Order CONTENT stays commerce-driven (account order service) and the
|
|
@@ -248,22 +95,12 @@ const formatAddress = (address: AddressLike | null | undefined): string[] => {
|
|
|
248
95
|
* every caption and the styling.
|
|
249
96
|
* A designer can mark one child as the row layout for the order lines; without
|
|
250
97
|
* one the built-in line renders.
|
|
251
|
-
* Properties:
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* billingAddressLabel, showTotals, showPieceCount, pieceCountLabel,
|
|
258
|
-
* showDiscountItems, showInstallmentInfo, installmentInfoLabel,
|
|
259
|
-
* subtotalLabel, discountLabel, shippingLabel, freeShippingLabel, totalLabel,
|
|
260
|
-
* trackingLabel,
|
|
261
|
-
* showPaymentInfo, paymentInfoLabel.
|
|
262
|
-
* Parts: detail, header, package-label, number, date, header-total, status,
|
|
263
|
-
* invoice-link, cancel-link, list, item, image, name, quantity, color, size,
|
|
264
|
-
* item-status, price, shipment, delivery, tracking, addresses, address,
|
|
265
|
-
* address-label, totals, total-row, total-label, total-value, grand-total,
|
|
266
|
-
* payment-info, payment-info-label.
|
|
98
|
+
* Properties: showShipmentStatus, showItems, showImage, imageWidth,
|
|
99
|
+
* imageHeight, showQuantity, showTotals, quantityPrefix, subtotalLabel,
|
|
100
|
+
* discountLabel, shippingLabel, totalLabel, trackingLabel.
|
|
101
|
+
* Parts: detail, header, number, date, status, list, item, image, name,
|
|
102
|
+
* quantity, item-status, price, shipment, delivery, tracking, totals,
|
|
103
|
+
* total-row, total-label, total-value, grand-total.
|
|
267
104
|
*/
|
|
268
105
|
const OrderDetailBlock = ({
|
|
269
106
|
block,
|
|
@@ -311,110 +148,25 @@ const OrderDetailBlock = ({
|
|
|
311
148
|
getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
|
|
312
149
|
);
|
|
313
150
|
const quantityPrefix = text('quantityPrefix', 'x');
|
|
314
|
-
const quantityLabel = text('quantityLabel', '');
|
|
315
151
|
const subtotalLabel = text('subtotalLabel', 'Subtotal');
|
|
316
152
|
const discountLabel = text('discountLabel', 'Discount');
|
|
317
153
|
const shippingLabel = text('shippingLabel', 'Shipping');
|
|
318
|
-
const freeShippingLabel = text('freeShippingLabel', 'Free');
|
|
319
154
|
const totalLabel = text('totalLabel', 'Total');
|
|
320
155
|
const trackingLabel = text('trackingLabel', 'Track shipment');
|
|
321
156
|
|
|
322
|
-
// Header: empty keeps the original bare header (no package tag, no
|
|
323
|
-
// separate labelled total line, no invoice/cancel links) — every one of
|
|
324
|
-
// these is an opt-in addition, same as the order-list block's labels.
|
|
325
|
-
const packageLabel = text('packageLabel', '');
|
|
326
|
-
const numberLabel = text('numberLabel', '');
|
|
327
|
-
const dateLabel = text('dateLabel', '');
|
|
328
|
-
const headerTotalLabel = text('headerTotalLabel', '');
|
|
329
|
-
const showInvoiceLink = readToggle(
|
|
330
|
-
block.properties?.showInvoiceLink,
|
|
331
|
-
currentBreakpoint,
|
|
332
|
-
false
|
|
333
|
-
);
|
|
334
|
-
const invoiceLinkLabel = text('invoiceLinkLabel', 'Invoice');
|
|
335
|
-
const showCancelLink = readToggle(
|
|
336
|
-
block.properties?.showCancelLink,
|
|
337
|
-
currentBreakpoint,
|
|
338
|
-
false
|
|
339
|
-
);
|
|
340
|
-
const cancelLinkLabel = text('cancelLinkLabel', 'Easy cancellation');
|
|
341
|
-
const cancelHrefBase = text('cancelHrefBase', '/account/orders');
|
|
342
|
-
|
|
343
|
-
const showColor = readToggle(
|
|
344
|
-
block.properties?.showColor,
|
|
345
|
-
currentBreakpoint,
|
|
346
|
-
false
|
|
347
|
-
);
|
|
348
|
-
const colorLabel = text('colorLabel', 'Color:');
|
|
349
|
-
const colorAttributeKey = text('colorAttributeKey', '');
|
|
350
|
-
const showSize = readToggle(
|
|
351
|
-
block.properties?.showSize,
|
|
352
|
-
currentBreakpoint,
|
|
353
|
-
false
|
|
354
|
-
);
|
|
355
|
-
const sizeLabel = text('sizeLabel', 'Size:');
|
|
356
|
-
const sizeAttributeKey = text('sizeAttributeKey', '');
|
|
357
|
-
|
|
358
|
-
const showShippingAddress = readToggle(
|
|
359
|
-
block.properties?.showShippingAddress,
|
|
360
|
-
currentBreakpoint,
|
|
361
|
-
false
|
|
362
|
-
);
|
|
363
|
-
const shippingAddressLabel = text('shippingAddressLabel', 'Delivery address');
|
|
364
|
-
const showBillingAddress = readToggle(
|
|
365
|
-
block.properties?.showBillingAddress,
|
|
366
|
-
currentBreakpoint,
|
|
367
|
-
false
|
|
368
|
-
);
|
|
369
|
-
const billingAddressLabel = text('billingAddressLabel', 'Billing address');
|
|
370
|
-
|
|
371
|
-
const showPieceCount = readToggle(
|
|
372
|
-
block.properties?.showPieceCount,
|
|
373
|
-
currentBreakpoint,
|
|
374
|
-
false
|
|
375
|
-
);
|
|
376
|
-
const pieceCountLabel = text('pieceCountLabel', 'pieces');
|
|
377
|
-
const showDiscountItems = readToggle(
|
|
378
|
-
block.properties?.showDiscountItems,
|
|
379
|
-
currentBreakpoint,
|
|
380
|
-
false
|
|
381
|
-
);
|
|
382
|
-
const showInstallmentInfo = readToggle(
|
|
383
|
-
block.properties?.showInstallmentInfo,
|
|
384
|
-
currentBreakpoint,
|
|
385
|
-
false
|
|
386
|
-
);
|
|
387
|
-
const installmentInfoLabel = text('installmentInfoLabel', 'Installments');
|
|
388
|
-
|
|
389
|
-
const showPaymentInfo = readToggle(
|
|
390
|
-
block.properties?.showPaymentInfo,
|
|
391
|
-
currentBreakpoint,
|
|
392
|
-
false
|
|
393
|
-
);
|
|
394
|
-
const paymentInfoLabel = text('paymentInfoLabel', 'Payment information');
|
|
395
|
-
|
|
396
|
-
const isMobile = currentBreakpoint === 'mobile';
|
|
397
|
-
|
|
398
157
|
const { locale, currency: localeCurrency } = useLocalization();
|
|
399
158
|
const params = useParams();
|
|
400
159
|
const searchParams = useSearchParams();
|
|
401
160
|
|
|
402
161
|
const orderKey =
|
|
403
|
-
firstString(
|
|
404
|
-
|
|
405
|
-
ORDER_PARAM_KEYS
|
|
406
|
-
) || firstString((key) => searchParams?.get(key), ORDER_PARAM_KEYS);
|
|
162
|
+
firstString((key) => (params as Record<string, unknown>)?.[key], ORDER_PARAM_KEYS) ||
|
|
163
|
+
firstString((key) => searchParams?.get(key), ORDER_PARAM_KEYS);
|
|
407
164
|
|
|
408
165
|
// Not gated on the session: brands expose the same endpoint for anonymous
|
|
409
166
|
// order tracking. Without a key there is nothing to ask for at all.
|
|
410
167
|
const { data, isLoading } = useGetOrderQuery(orderKey, { skip: !orderKey });
|
|
411
168
|
|
|
412
|
-
const
|
|
413
|
-
// See DESIGNER_SAMPLE_ORDER's own comment: the preview route's order id is
|
|
414
|
-
// fake by design, so a designer gets a fully populated sample instead of
|
|
415
|
-
// the section collapsing to one "not found" box the moment it's empty.
|
|
416
|
-
const order: OrderLike | null =
|
|
417
|
-
liveOrder ?? (isDesigner && !isLoading ? DESIGNER_SAMPLE_ORDER : null);
|
|
169
|
+
const order = (data ?? null) as OrderLike | null;
|
|
418
170
|
const items = order?.orderitem_set ?? [];
|
|
419
171
|
// pz writes one order line per unit; the same shared grouping the thank-you
|
|
420
172
|
// page uses keeps a three-unit order from listing as three "x1" rows here.
|
|
@@ -428,6 +180,8 @@ const OrderDetailBlock = ({
|
|
|
428
180
|
label={
|
|
429
181
|
isLoading
|
|
430
182
|
? 'Order detail (loading)'
|
|
183
|
+
: orderKey
|
|
184
|
+
? 'Order detail (order not found)'
|
|
431
185
|
: 'Order detail (no order in URL)'
|
|
432
186
|
}
|
|
433
187
|
attribute="data-order-detail"
|
|
@@ -497,59 +251,6 @@ const OrderDetailBlock = ({
|
|
|
497
251
|
</div>
|
|
498
252
|
) : null;
|
|
499
253
|
|
|
500
|
-
// Matches the storefront's own account-orders page: the amount before
|
|
501
|
-
// shipping is what "N pieces" prices against, not the grand total.
|
|
502
|
-
const pieceSubtotal = money(
|
|
503
|
-
String(
|
|
504
|
-
Number(order.amount_without_discount || 0) -
|
|
505
|
-
Number(order.shipping_amount || 0)
|
|
506
|
-
)
|
|
507
|
-
);
|
|
508
|
-
const cancelHref = order.id
|
|
509
|
-
? `${cancelHrefBase.replace(/\/$/, '')}/${order.id}/cancellation`
|
|
510
|
-
: '';
|
|
511
|
-
const installmentCount = Math.max(0, Number(order.installment_count) || 0);
|
|
512
|
-
const perInstallment =
|
|
513
|
-
installmentCount > 1
|
|
514
|
-
? money(String(Number(order.amount || 0) / installmentCount))
|
|
515
|
-
: '';
|
|
516
|
-
|
|
517
|
-
const addressBlock = (
|
|
518
|
-
address: AddressLike | null | undefined,
|
|
519
|
-
label: string,
|
|
520
|
-
part: string
|
|
521
|
-
) => {
|
|
522
|
-
const lines = formatAddress(address);
|
|
523
|
-
if (lines.length === 0) return null;
|
|
524
|
-
|
|
525
|
-
return (
|
|
526
|
-
<div
|
|
527
|
-
{...partAttrs('address')}
|
|
528
|
-
data-order-detail-address={part}
|
|
529
|
-
style={{ flex: isMobile ? '1 1 auto' : '0 1 50%', minWidth: 0 }}
|
|
530
|
-
>
|
|
531
|
-
<div
|
|
532
|
-
{...partAttrs('address-label')}
|
|
533
|
-
style={{ fontWeight: 600, marginBottom: '8px' }}
|
|
534
|
-
>
|
|
535
|
-
{label}
|
|
536
|
-
</div>
|
|
537
|
-
<div
|
|
538
|
-
style={{
|
|
539
|
-
display: 'flex',
|
|
540
|
-
flexDirection: 'column',
|
|
541
|
-
gap: '2px',
|
|
542
|
-
opacity: 0.8
|
|
543
|
-
}}
|
|
544
|
-
>
|
|
545
|
-
{lines.map((line, index) => (
|
|
546
|
-
<span key={index}>{line}</span>
|
|
547
|
-
))}
|
|
548
|
-
</div>
|
|
549
|
-
</div>
|
|
550
|
-
);
|
|
551
|
-
};
|
|
552
|
-
|
|
553
254
|
return (
|
|
554
255
|
<div
|
|
555
256
|
data-order-detail=""
|
|
@@ -563,114 +264,33 @@ const OrderDetailBlock = ({
|
|
|
563
264
|
<header
|
|
564
265
|
data-order-detail-header=""
|
|
565
266
|
{...partAttrs('header')}
|
|
566
|
-
style={{
|
|
567
|
-
display: 'flex',
|
|
568
|
-
flexDirection: isMobile ? 'column' : 'row',
|
|
569
|
-
alignItems: isMobile ? 'stretch' : 'flex-start',
|
|
570
|
-
justifyContent: 'space-between',
|
|
571
|
-
gap: isMobile ? '12px' : '24px'
|
|
572
|
-
}}
|
|
267
|
+
style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}
|
|
573
268
|
>
|
|
574
|
-
<
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)}
|
|
269
|
+
<span
|
|
270
|
+
data-order-detail-number=""
|
|
271
|
+
{...partAttrs('number')}
|
|
272
|
+
style={{ fontWeight: 600 }}
|
|
273
|
+
>
|
|
274
|
+
{order.number || orderKey}
|
|
275
|
+
</span>
|
|
276
|
+
<span
|
|
277
|
+
data-order-detail-date=""
|
|
278
|
+
{...partAttrs('date')}
|
|
279
|
+
style={{ opacity: 0.8 }}
|
|
280
|
+
>
|
|
281
|
+
{formatDate(order.created_date, locale)}
|
|
282
|
+
</span>
|
|
283
|
+
{order.status?.label && (
|
|
584
284
|
<span
|
|
585
|
-
data-order-detail-
|
|
586
|
-
|
|
587
|
-
|
|
285
|
+
data-order-detail-status=""
|
|
286
|
+
// The raw status code is published so scoped custom CSS can paint
|
|
287
|
+
// one status differently from the rest.
|
|
288
|
+
data-order-status-value={order.status.value}
|
|
289
|
+
{...partAttrs('status')}
|
|
588
290
|
>
|
|
589
|
-
{
|
|
590
|
-
{order.number || orderKey}
|
|
291
|
+
{order.status.label}
|
|
591
292
|
</span>
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
<div
|
|
595
|
-
style={{
|
|
596
|
-
display: 'flex',
|
|
597
|
-
flexDirection: isMobile ? 'column' : 'row',
|
|
598
|
-
alignItems: isMobile ? 'stretch' : 'flex-start',
|
|
599
|
-
gap: isMobile ? '8px' : '24px'
|
|
600
|
-
}}
|
|
601
|
-
>
|
|
602
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
|
603
|
-
<span data-order-detail-date="" {...partAttrs('date')}>
|
|
604
|
-
{dateLabel ? `${dateLabel} ` : ''}
|
|
605
|
-
{formatDate(order.created_date, locale)}
|
|
606
|
-
</span>
|
|
607
|
-
{headerTotalLabel && (
|
|
608
|
-
<span
|
|
609
|
-
data-order-detail-header-total=""
|
|
610
|
-
{...partAttrs('header-total')}
|
|
611
|
-
>
|
|
612
|
-
{headerTotalLabel} {money(order.amount)}
|
|
613
|
-
</span>
|
|
614
|
-
)}
|
|
615
|
-
{(showInvoiceLink || showCancelLink) && (
|
|
616
|
-
<div
|
|
617
|
-
style={{
|
|
618
|
-
display: 'flex',
|
|
619
|
-
gap: '16px',
|
|
620
|
-
marginTop: '4px'
|
|
621
|
-
}}
|
|
622
|
-
>
|
|
623
|
-
{showInvoiceLink && order.e_archive_url && (
|
|
624
|
-
<a
|
|
625
|
-
href={isDesigner ? '#' : order.e_archive_url}
|
|
626
|
-
onClick={guard}
|
|
627
|
-
target={isDesigner ? undefined : '_blank'}
|
|
628
|
-
rel="noreferrer"
|
|
629
|
-
data-order-detail-invoice-link=""
|
|
630
|
-
{...partAttrs('invoice-link')}
|
|
631
|
-
style={{
|
|
632
|
-
color: 'inherit',
|
|
633
|
-
textDecoration: 'none',
|
|
634
|
-
whiteSpace: 'nowrap'
|
|
635
|
-
}}
|
|
636
|
-
>
|
|
637
|
-
→ {invoiceLinkLabel}
|
|
638
|
-
</a>
|
|
639
|
-
)}
|
|
640
|
-
{showCancelLink && order.is_cancellable && cancelHref && (
|
|
641
|
-
<NextLink
|
|
642
|
-
href={isDesigner ? '#' : cancelHref}
|
|
643
|
-
onClick={guard}
|
|
644
|
-
data-order-detail-cancel-link=""
|
|
645
|
-
{...partAttrs('cancel-link')}
|
|
646
|
-
style={{
|
|
647
|
-
color: 'inherit',
|
|
648
|
-
textDecoration: 'none',
|
|
649
|
-
whiteSpace: 'nowrap'
|
|
650
|
-
}}
|
|
651
|
-
>
|
|
652
|
-
→ {cancelLinkLabel}
|
|
653
|
-
</NextLink>
|
|
654
|
-
)}
|
|
655
|
-
</div>
|
|
656
|
-
)}
|
|
657
|
-
</div>
|
|
658
|
-
|
|
659
|
-
{order.status?.label && (
|
|
660
|
-
<span
|
|
661
|
-
data-order-detail-status=""
|
|
662
|
-
// The raw status code is published so scoped custom CSS can paint
|
|
663
|
-
// one status differently from the rest.
|
|
664
|
-
data-order-status-value={order.status.value}
|
|
665
|
-
{...partAttrs('status')}
|
|
666
|
-
// No background/padding/radius here: the Status style group owns
|
|
667
|
-
// that, same reasoning as the order-list block's status pill.
|
|
668
|
-
style={{ display: 'inline-block', flex: '0 0 auto' }}
|
|
669
|
-
>
|
|
670
|
-
{order.status.label}
|
|
671
|
-
</span>
|
|
672
|
-
)}
|
|
673
|
-
</div>
|
|
293
|
+
)}
|
|
674
294
|
</header>
|
|
675
295
|
|
|
676
296
|
{showItems && (groupedItems.length > 0 || customLines) && (
|
|
@@ -688,121 +308,92 @@ const OrderDetailBlock = ({
|
|
|
688
308
|
>
|
|
689
309
|
{customLines ??
|
|
690
310
|
groupedItems.map((line) => {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
{showImage && item.product?.image && (
|
|
719
|
-
<NextLink
|
|
720
|
-
href={isDesigner ? '#' : href || '#'}
|
|
721
|
-
onClick={guard}
|
|
722
|
-
style={{ flex: '0 0 auto', display: 'block' }}
|
|
723
|
-
>
|
|
724
|
-
{/* Plain img: order line thumbnails come from arbitrary
|
|
311
|
+
// Status, currency and thumbnails are identical across the lines of
|
|
312
|
+
// a group, so the first line represents the whole row.
|
|
313
|
+
const item = line.item;
|
|
314
|
+
const name = line.name;
|
|
315
|
+
const href = line.href;
|
|
316
|
+
const lineCurrency = item.price_currency?.value || currency;
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<li
|
|
320
|
+
key={line.key}
|
|
321
|
+
data-order-detail-item=""
|
|
322
|
+
{...partAttrs('item')}
|
|
323
|
+
style={{
|
|
324
|
+
display: 'flex',
|
|
325
|
+
alignItems: 'flex-start',
|
|
326
|
+
gap: '12px',
|
|
327
|
+
paddingBottom: '16px',
|
|
328
|
+
borderBottom: '1px solid var(--order-detail-border, #e5e7eb)'
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
{showImage && item.product?.image && (
|
|
332
|
+
<NextLink
|
|
333
|
+
href={isDesigner ? '#' : href || '#'}
|
|
334
|
+
onClick={guard}
|
|
335
|
+
style={{ flex: '0 0 auto', display: 'block' }}
|
|
336
|
+
>
|
|
337
|
+
{/* Plain img: order line thumbnails come from arbitrary
|
|
725
338
|
commerce CDNs that the brand next.config may not allow. */}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
339
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
340
|
+
<img
|
|
341
|
+
src={item.product.image}
|
|
342
|
+
alt={name}
|
|
343
|
+
width={imageWidth}
|
|
344
|
+
height={imageHeight}
|
|
345
|
+
{...partAttrs('image')}
|
|
346
|
+
style={{ objectFit: 'cover', display: 'block' }}
|
|
347
|
+
/>
|
|
348
|
+
</NextLink>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
<div style={{ flex: '1 1 auto', minWidth: 0 }}>
|
|
352
|
+
<NextLink
|
|
353
|
+
href={isDesigner ? '#' : href || '#'}
|
|
354
|
+
onClick={guard}
|
|
355
|
+
data-order-detail-item-name=""
|
|
356
|
+
{...partAttrs('name')}
|
|
357
|
+
style={{ color: 'inherit', textDecoration: 'none' }}
|
|
358
|
+
>
|
|
359
|
+
{name}
|
|
360
|
+
</NextLink>
|
|
361
|
+
|
|
362
|
+
{showQuantity && (
|
|
363
|
+
<div
|
|
364
|
+
data-order-detail-item-quantity=""
|
|
365
|
+
{...partAttrs('quantity')}
|
|
366
|
+
style={{ marginTop: '4px', opacity: 0.8 }}
|
|
367
|
+
>
|
|
368
|
+
{`${quantityPrefix}${line.quantity}`}
|
|
369
|
+
</div>
|
|
736
370
|
)}
|
|
737
371
|
|
|
738
|
-
|
|
739
|
-
<
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
{
|
|
744
|
-
style={{ textDecoration: 'none' }}
|
|
372
|
+
{showShipmentStatus && item.status?.label && (
|
|
373
|
+
<div
|
|
374
|
+
data-order-detail-item-status=""
|
|
375
|
+
data-order-status-value={item.status.value}
|
|
376
|
+
{...partAttrs('item-status')}
|
|
377
|
+
style={{ marginTop: '4px', opacity: 0.8 }}
|
|
745
378
|
>
|
|
746
|
-
{
|
|
747
|
-
</
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
style={{ marginTop: '4px' }}
|
|
754
|
-
>
|
|
755
|
-
{colorLabel} {colorValue}
|
|
756
|
-
</div>
|
|
757
|
-
)}
|
|
758
|
-
|
|
759
|
-
{showSize && sizeValue && (
|
|
760
|
-
<div
|
|
761
|
-
data-order-detail-item-size=""
|
|
762
|
-
{...partAttrs('size')}
|
|
763
|
-
style={{ marginTop: '4px' }}
|
|
764
|
-
>
|
|
765
|
-
{sizeLabel} {sizeValue}
|
|
766
|
-
</div>
|
|
767
|
-
)}
|
|
768
|
-
|
|
769
|
-
{showQuantity && (
|
|
770
|
-
<div
|
|
771
|
-
data-order-detail-item-quantity=""
|
|
772
|
-
{...partAttrs('quantity')}
|
|
773
|
-
style={{ marginTop: '4px', opacity: 0.8 }}
|
|
774
|
-
>
|
|
775
|
-
{quantityLabel
|
|
776
|
-
? `${quantityLabel} ${line.quantity}`
|
|
777
|
-
: `${quantityPrefix}${line.quantity}`}
|
|
778
|
-
</div>
|
|
779
|
-
)}
|
|
780
|
-
|
|
781
|
-
{showShipmentStatus && item.status?.label && (
|
|
782
|
-
<div
|
|
783
|
-
data-order-detail-item-status=""
|
|
784
|
-
data-order-status-value={item.status.value}
|
|
785
|
-
{...partAttrs('item-status')}
|
|
786
|
-
style={{ marginTop: '4px', opacity: 0.8 }}
|
|
787
|
-
>
|
|
788
|
-
{item.status.label}
|
|
789
|
-
</div>
|
|
790
|
-
)}
|
|
791
|
-
</div>
|
|
792
|
-
|
|
793
|
-
<span data-order-detail-item-price="" {...partAttrs('price')}>
|
|
794
|
-
{/* The row now stands for every unit of the product, so the
|
|
379
|
+
{item.status.label}
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<span data-order-detail-item-price="" {...partAttrs('price')}>
|
|
385
|
+
{/* The row now stands for every unit of the product, so the
|
|
795
386
|
group's summed line total is what it must price — the
|
|
796
387
|
single line's total_amount would understate an "x3" row.
|
|
797
388
|
For an ungrouped row this is the value printed before. */}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
389
|
+
{formatMoney(line.total, {
|
|
390
|
+
locale,
|
|
391
|
+
currency: lineCurrency
|
|
392
|
+
})}
|
|
393
|
+
</span>
|
|
394
|
+
</li>
|
|
395
|
+
);
|
|
396
|
+
})}
|
|
806
397
|
</ul>
|
|
807
398
|
)}
|
|
808
399
|
|
|
@@ -847,100 +438,33 @@ const OrderDetailBlock = ({
|
|
|
847
438
|
</section>
|
|
848
439
|
)}
|
|
849
440
|
|
|
850
|
-
{(showShippingAddress || showBillingAddress) && (
|
|
851
|
-
<section
|
|
852
|
-
data-order-detail-addresses=""
|
|
853
|
-
{...partAttrs('addresses')}
|
|
854
|
-
style={{
|
|
855
|
-
display: 'flex',
|
|
856
|
-
flexDirection: isMobile ? 'column' : 'row',
|
|
857
|
-
gap: isMobile ? '16px' : '40px'
|
|
858
|
-
}}
|
|
859
|
-
>
|
|
860
|
-
{showShippingAddress &&
|
|
861
|
-
addressBlock(
|
|
862
|
-
order.shipping_address,
|
|
863
|
-
shippingAddressLabel,
|
|
864
|
-
'shipping'
|
|
865
|
-
)}
|
|
866
|
-
{showBillingAddress &&
|
|
867
|
-
addressBlock(order.billing_address, billingAddressLabel, 'billing')}
|
|
868
|
-
</section>
|
|
869
|
-
)}
|
|
870
|
-
|
|
871
441
|
{showTotals && (
|
|
872
442
|
<section
|
|
873
443
|
data-order-detail-totals=""
|
|
874
444
|
{...partAttrs('totals')}
|
|
875
445
|
style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}
|
|
876
446
|
>
|
|
877
|
-
{
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
447
|
+
{totalRow(
|
|
448
|
+
subtotalLabel,
|
|
449
|
+
money(order.amount_without_discount),
|
|
450
|
+
'data-order-detail-subtotal'
|
|
451
|
+
)}
|
|
452
|
+
{totalRow(
|
|
453
|
+
discountLabel,
|
|
454
|
+
money(order.discount_amount),
|
|
455
|
+
'data-order-detail-discount'
|
|
456
|
+
)}
|
|
886
457
|
{totalRow(
|
|
887
458
|
shippingLabel,
|
|
888
|
-
|
|
889
|
-
? money(order.shipping_amount)
|
|
890
|
-
: freeShippingLabel,
|
|
459
|
+
money(order.shipping_amount),
|
|
891
460
|
'data-order-detail-shipping'
|
|
892
461
|
)}
|
|
893
|
-
{showDiscountItems
|
|
894
|
-
? (order.discountitem_set || []).map((discount, index) =>
|
|
895
|
-
totalRow(
|
|
896
|
-
discount.name || discountLabel,
|
|
897
|
-
money(discount.amount),
|
|
898
|
-
`data-order-detail-discount-${index}`
|
|
899
|
-
)
|
|
900
|
-
)
|
|
901
|
-
: totalRow(
|
|
902
|
-
discountLabel,
|
|
903
|
-
money(order.discount_amount),
|
|
904
|
-
'data-order-detail-discount'
|
|
905
|
-
)}
|
|
906
462
|
{totalRow(
|
|
907
463
|
totalLabel,
|
|
908
464
|
money(order.amount),
|
|
909
465
|
'data-order-detail-total',
|
|
910
466
|
'grand-total'
|
|
911
467
|
)}
|
|
912
|
-
{showInstallmentInfo &&
|
|
913
|
-
installmentCount > 1 &&
|
|
914
|
-
totalRow(
|
|
915
|
-
installmentInfoLabel,
|
|
916
|
-
`${installmentCount} x ${perInstallment}`,
|
|
917
|
-
'data-order-detail-installments'
|
|
918
|
-
)}
|
|
919
|
-
</section>
|
|
920
|
-
)}
|
|
921
|
-
|
|
922
|
-
{showPaymentInfo && (order.bank?.name || order.payment_option?.name) && (
|
|
923
|
-
<section
|
|
924
|
-
data-order-detail-payment=""
|
|
925
|
-
{...partAttrs('payment-info')}
|
|
926
|
-
style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}
|
|
927
|
-
>
|
|
928
|
-
<div
|
|
929
|
-
{...partAttrs('payment-info-label')}
|
|
930
|
-
style={{ fontWeight: 600, marginBottom: '4px' }}
|
|
931
|
-
>
|
|
932
|
-
{paymentInfoLabel}
|
|
933
|
-
</div>
|
|
934
|
-
{formatAddress(order.billing_address)
|
|
935
|
-
.slice(0, 1)
|
|
936
|
-
.map((line, index) => (
|
|
937
|
-
<span key={index}>{line}</span>
|
|
938
|
-
))}
|
|
939
|
-
<span style={{ opacity: 0.8 }}>
|
|
940
|
-
{[order.bank?.name, order.payment_option?.name]
|
|
941
|
-
.filter(Boolean)
|
|
942
|
-
.join(' — ')}
|
|
943
|
-
</span>
|
|
944
468
|
</section>
|
|
945
469
|
)}
|
|
946
470
|
</div>
|