@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.
@@ -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: packageLabel, numberLabel, dateLabel, headerTotalLabel,
252
- * showInvoiceLink, invoiceLinkLabel, showCancelLink, cancelLinkLabel,
253
- * cancelHrefBase, showShipmentStatus, showItems, showImage, imageWidth,
254
- * imageHeight, showQuantity, quantityLabel, quantityPrefix, showColor,
255
- * colorLabel, colorAttributeKey, showSize, sizeLabel, sizeAttributeKey,
256
- * showShippingAddress, shippingAddressLabel, showBillingAddress,
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
- (key) => (params as Record<string, unknown>)?.[key],
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 liveOrder = (data ?? null) as OrderLike | null;
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
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
575
- {packageLabel && (
576
- <span
577
- data-order-detail-package=""
578
- {...partAttrs('package-label')}
579
- style={{ opacity: 0.7 }}
580
- >
581
- {packageLabel}
582
- </span>
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-number=""
586
- {...partAttrs('number')}
587
- style={{ fontWeight: 600 }}
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
- {numberLabel ? `${numberLabel} ` : ''}
590
- {order.number || orderKey}
291
+ {order.status.label}
591
292
  </span>
592
- </div>
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
- // Status, currency and thumbnails are identical across the lines of
692
- // a group, so the first line represents the whole row.
693
- const item = line.item;
694
- const name = line.name;
695
- const href = line.href;
696
- const lineCurrency = item.price_currency?.value || currency;
697
- const colorValue = showColor
698
- ? resolveAttribute(item.product?.attributes, colorAttributeKey)
699
- : '';
700
- const sizeValue = showSize
701
- ? resolveAttribute(item.product?.attributes, sizeAttributeKey)
702
- : '';
703
-
704
- return (
705
- <li
706
- key={line.key}
707
- data-order-detail-item=""
708
- {...partAttrs('item')}
709
- style={{
710
- display: 'flex',
711
- alignItems: 'flex-start',
712
- gap: '12px',
713
- paddingBottom: '16px',
714
- borderBottom:
715
- '1px solid var(--order-detail-border, #e5e7eb)'
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
- {/* eslint-disable-next-line @next/next/no-img-element */}
727
- <img
728
- src={item.product.image}
729
- alt={name}
730
- width={imageWidth}
731
- height={imageHeight}
732
- {...partAttrs('image')}
733
- style={{ objectFit: 'cover', display: 'block' }}
734
- />
735
- </NextLink>
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
- <div style={{ flex: '1 1 auto', minWidth: 0 }}>
739
- <NextLink
740
- href={isDesigner ? '#' : href || '#'}
741
- onClick={guard}
742
- data-order-detail-item-name=""
743
- {...partAttrs('name')}
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
- {name}
747
- </NextLink>
748
-
749
- {showColor && colorValue && (
750
- <div
751
- data-order-detail-item-color=""
752
- {...partAttrs('color')}
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
- {formatMoney(line.total, {
799
- locale,
800
- currency: lineCurrency
801
- })}
802
- </span>
803
- </li>
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
- {showPieceCount &&
878
- totalRow(
879
- `${groupedItems.reduce(
880
- (sum, line) => sum + Number(line.quantity || 0),
881
- 0
882
- )} ${pieceCountLabel}`,
883
- pieceSubtotal,
884
- 'data-order-detail-piece-count'
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
- Number(order.shipping_amount || 0) > 0
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>