@akinon/pz-theme 2.0.145-beta.0 → 2.0.145
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 +4 -4
- package/package.json +4 -4
- package/src/blocks/__tests__/account-money-format.test.tsx +129 -0
- package/src/blocks/__tests__/money-style.test.ts +53 -0
- package/src/blocks/checkout-summary-block.tsx +13 -3
- package/src/blocks/coupon-list-block.tsx +9 -2
- package/src/blocks/coupon-wallet-block.tsx +9 -2
- package/src/blocks/favourites-grid-block.tsx +6 -2
- package/src/blocks/money-style.ts +56 -0
- package/src/blocks/order-detail-block.tsx +18 -5
- package/src/blocks/order-list-block.tsx +12 -5
- package/src/blocks/order-success-block.tsx +3 -1
- package/src/blocks/order-tracking-block.tsx +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @akinon/pz-theme
|
|
2
2
|
|
|
3
|
-
## 2.0.145
|
|
3
|
+
## 2.0.145
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- @akinon/next@2.0.145
|
|
9
|
-
- @akinon/theme-contract@2.0.145
|
|
7
|
+
- 44d0c72: ZERO-5180: Let the order, coupon, favourites and checkout summary blocks take the money format controls the price and basket blocks have
|
|
8
|
+
- @akinon/next@2.0.145
|
|
9
|
+
- @akinon/theme-contract@2.0.145
|
|
10
10
|
|
|
11
11
|
## 2.0.144
|
|
12
12
|
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/pz-theme",
|
|
3
3
|
"description": "Theme package for Project Zero Next — ThemePlaceholder system and theme editor infrastructure",
|
|
4
|
-
"version": "2.0.145
|
|
4
|
+
"version": "2.0.145",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "jest"
|
|
9
9
|
},
|
|
10
10
|
"peerDependencies": {
|
|
11
|
-
"@akinon/next": "2.0.145
|
|
11
|
+
"@akinon/next": "2.0.145",
|
|
12
12
|
"react": "^18.0.0 || ^19.0.0",
|
|
13
13
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@akinon/theme-contract": "2.0.145
|
|
16
|
+
"@akinon/theme-contract": "2.0.145",
|
|
17
17
|
"clsx": "2.1.1",
|
|
18
18
|
"lottie-web": "^5.13.0",
|
|
19
19
|
"react-multi-carousel": "2.8.4",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"zod": "4.4.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@akinon/next": "2.0.145
|
|
24
|
+
"@akinon/next": "2.0.145",
|
|
25
25
|
"@testing-library/jest-dom": "5.16.4",
|
|
26
26
|
"@testing-library/react": "16.3.2",
|
|
27
27
|
"@testing-library/user-event": "14.1.1",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import OrderListBlock from '../order-list-block';
|
|
4
|
+
import FavouritesGridBlock from '../favourites-grid-block';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The account blocks print money through the same four controls the price
|
|
8
|
+
* and basket blocks carry, so a brand that drops ",00" from whole amounts
|
|
9
|
+
* ("Only when needed") sees it dropped on the order list and the favourites
|
|
10
|
+
* too — and a block nobody has set keeps the theme's original format.
|
|
11
|
+
*/
|
|
12
|
+
const tlAuto = {
|
|
13
|
+
decimals: 'auto',
|
|
14
|
+
currencyDisplay: 'custom',
|
|
15
|
+
currencyText: 'TL',
|
|
16
|
+
currencyPosition: 'after'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
jest.mock('next-auth/react', () => ({
|
|
20
|
+
useSession: () => ({ data: null, status: 'authenticated' })
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock('@akinon/next/hooks', () => ({
|
|
24
|
+
__esModule: true,
|
|
25
|
+
useLocalization: () => ({ locale: 'tr', currency: 'try' })
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
jest.mock('@akinon/next/redux/hooks', () => ({
|
|
29
|
+
__esModule: true,
|
|
30
|
+
useAppSelector: () => undefined,
|
|
31
|
+
useAppDispatch: () => jest.fn()
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// Every `@akinon/next/data/client/*` subpath maps to the same mock file, so
|
|
35
|
+
// one factory serves the orders, wishlist, product and basket clients alike.
|
|
36
|
+
jest.mock('@akinon/next/data/client/account', () => ({
|
|
37
|
+
__esModule: true,
|
|
38
|
+
useGetOrdersQuery: () => ({
|
|
39
|
+
data: {
|
|
40
|
+
count: 2,
|
|
41
|
+
results: [
|
|
42
|
+
{ id: 1, number: 'G-1', amount: '9999.00' },
|
|
43
|
+
{ id: 2, number: 'G-2', amount: '9999.50' }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
isLoading: false
|
|
47
|
+
}),
|
|
48
|
+
useGetFavoritesQuery: () => ({
|
|
49
|
+
data: {
|
|
50
|
+
results: [
|
|
51
|
+
{
|
|
52
|
+
pk: 11,
|
|
53
|
+
product: {
|
|
54
|
+
pk: 101,
|
|
55
|
+
name: 'Gömlek',
|
|
56
|
+
absolute_url: '/gomlek/',
|
|
57
|
+
price: '3999.90',
|
|
58
|
+
retail_price: '4999.00',
|
|
59
|
+
currency_type: 'try'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
isLoading: false
|
|
65
|
+
}),
|
|
66
|
+
useRemoveFavoriteMutation: () => [jest.fn(), { isLoading: false }],
|
|
67
|
+
useAddProductMutation: () => [jest.fn(), { isLoading: false }],
|
|
68
|
+
basketApi: { util: { invalidateTags: jest.fn() } }
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
const blockOf = (type: string, properties: Record<string, unknown>) => ({
|
|
72
|
+
id: `${type}-1`,
|
|
73
|
+
type,
|
|
74
|
+
properties,
|
|
75
|
+
styles: {},
|
|
76
|
+
blocks: []
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const texts = (root: ParentNode, part: string) =>
|
|
80
|
+
Array.from(root.querySelectorAll(`[data-part="${part}"]`)).map(
|
|
81
|
+
(node) => node.textContent
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
describe('order-list money format', () => {
|
|
85
|
+
const renderList = (properties: Record<string, unknown>) =>
|
|
86
|
+
render(
|
|
87
|
+
<OrderListBlock
|
|
88
|
+
block={blockOf('order-list', properties)}
|
|
89
|
+
placeholderId="p1"
|
|
90
|
+
sectionId="s1"
|
|
91
|
+
/>
|
|
92
|
+
).container;
|
|
93
|
+
|
|
94
|
+
it('drops ",00" from a whole total and keeps any other fraction', () => {
|
|
95
|
+
expect(texts(renderList(tlAuto), 'total')).toEqual([
|
|
96
|
+
'9.999 TL',
|
|
97
|
+
'9.999,50 TL'
|
|
98
|
+
]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('keeps the original format on a block nobody has set', () => {
|
|
102
|
+
expect(texts(renderList({}), 'total')).toEqual(['₺9.999,00', '₺9.999,50']);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('favourites-grid money format', () => {
|
|
107
|
+
const renderGrid = (properties: Record<string, unknown>) =>
|
|
108
|
+
render(
|
|
109
|
+
<FavouritesGridBlock
|
|
110
|
+
block={blockOf('favourites-grid', properties)}
|
|
111
|
+
placeholderId="p1"
|
|
112
|
+
sectionId="s1"
|
|
113
|
+
/>
|
|
114
|
+
).container;
|
|
115
|
+
|
|
116
|
+
it('formats the price and the struck retail price with the block format', () => {
|
|
117
|
+
const container = renderGrid(tlAuto);
|
|
118
|
+
|
|
119
|
+
expect(texts(container, 'retail-price')).toEqual(['4.999 TL']);
|
|
120
|
+
expect(container.textContent).toContain('3.999,90 TL');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('keeps the original format on a block nobody has set', () => {
|
|
124
|
+
const container = renderGrid({});
|
|
125
|
+
|
|
126
|
+
expect(texts(container, 'retail-price')).toEqual(['₺4.999,00']);
|
|
127
|
+
expect(container.textContent).toContain('₺3.999,90');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readMoneyStyle } from '../money-style';
|
|
2
|
+
|
|
3
|
+
describe('readMoneyStyle', () => {
|
|
4
|
+
it('resolves unset properties to the format the theme always printed', () => {
|
|
5
|
+
expect(readMoneyStyle(undefined, 'desktop')).toEqual({
|
|
6
|
+
decimals: 'always',
|
|
7
|
+
currencyDisplay: '',
|
|
8
|
+
currencyText: '',
|
|
9
|
+
currencyPosition: ''
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('reads the four money controls as the editor writes them', () => {
|
|
14
|
+
expect(
|
|
15
|
+
readMoneyStyle(
|
|
16
|
+
{
|
|
17
|
+
decimals: 'auto',
|
|
18
|
+
currencyDisplay: 'custom',
|
|
19
|
+
currencyText: 'TL',
|
|
20
|
+
currencyPosition: 'after'
|
|
21
|
+
},
|
|
22
|
+
'desktop'
|
|
23
|
+
)
|
|
24
|
+
).toEqual({
|
|
25
|
+
decimals: 'auto',
|
|
26
|
+
currencyDisplay: 'custom',
|
|
27
|
+
currencyText: 'TL',
|
|
28
|
+
currencyPosition: 'after'
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('reads a per-breakpoint value for the breakpoint being drawn', () => {
|
|
33
|
+
const properties = { decimals: { desktop: 'always', mobile: 'never' } };
|
|
34
|
+
|
|
35
|
+
expect(readMoneyStyle(properties, 'desktop').decimals).toBe('always');
|
|
36
|
+
expect(readMoneyStyle(properties, 'mobile').decimals).toBe('never');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('falls back on a value no control offers', () => {
|
|
40
|
+
const style = readMoneyStyle(
|
|
41
|
+
{
|
|
42
|
+
decimals: 'sometimes',
|
|
43
|
+
currencyDisplay: 'emoji',
|
|
44
|
+
currencyPosition: 'x'
|
|
45
|
+
},
|
|
46
|
+
'desktop'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(style.decimals).toBe('always');
|
|
50
|
+
expect(style.currencyDisplay).toBe('');
|
|
51
|
+
expect(style.currencyPosition).toBe('');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -7,6 +7,7 @@ import { useLocalization } from '@akinon/next/hooks';
|
|
|
7
7
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
8
8
|
import { useThemePageContext } from '../theme-page-context';
|
|
9
9
|
import { formatMoney, parseAmount } from './commerce-format';
|
|
10
|
+
import { readMoneyStyle } from './money-style';
|
|
10
11
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
11
12
|
import { getLineImage, readToggle } from './product-block-utils';
|
|
12
13
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -97,6 +98,7 @@ const CheckoutSummaryBlock = ({
|
|
|
97
98
|
currentBreakpoint = 'desktop',
|
|
98
99
|
isDesigner = false
|
|
99
100
|
}: BlockRendererProps) => {
|
|
101
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
100
102
|
const prop = (key: string, fallback: string) =>
|
|
101
103
|
String(
|
|
102
104
|
getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
|
|
@@ -208,7 +210,7 @@ const CheckoutSummaryBlock = ({
|
|
|
208
210
|
'';
|
|
209
211
|
|
|
210
212
|
const money = (value?: string | number) =>
|
|
211
|
-
formatMoney(value, { locale, currency });
|
|
213
|
+
formatMoney(value, { locale, currency, ...moneyStyle });
|
|
212
214
|
|
|
213
215
|
const hasBasket = Boolean(liveBasket || contextBasket);
|
|
214
216
|
|
|
@@ -257,7 +259,11 @@ const CheckoutSummaryBlock = ({
|
|
|
257
259
|
name: line.name,
|
|
258
260
|
image: line.image,
|
|
259
261
|
quantity: line.quantity,
|
|
260
|
-
total: formatMoney(line.total, {
|
|
262
|
+
total: formatMoney(line.total, {
|
|
263
|
+
locale,
|
|
264
|
+
currency: line.currency,
|
|
265
|
+
...moneyStyle
|
|
266
|
+
}),
|
|
261
267
|
currency: line.currency
|
|
262
268
|
}));
|
|
263
269
|
|
|
@@ -396,7 +402,11 @@ const CheckoutSummaryBlock = ({
|
|
|
396
402
|
{...partAttrs('line-total')}
|
|
397
403
|
style={{ flex: '0 0 auto' }}
|
|
398
404
|
>
|
|
399
|
-
{formatMoney(line.total, {
|
|
405
|
+
{formatMoney(line.total, {
|
|
406
|
+
locale,
|
|
407
|
+
currency: line.currency,
|
|
408
|
+
...moneyStyle
|
|
409
|
+
})}
|
|
400
410
|
</span>
|
|
401
411
|
</li>
|
|
402
412
|
))}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { useLocalization } from '@akinon/next/hooks';
|
|
10
10
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
11
11
|
import { formatDate, formatMoney } from './commerce-format';
|
|
12
|
+
import { readMoneyStyle } from './money-style';
|
|
12
13
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
13
14
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
14
15
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|
|
@@ -46,7 +47,8 @@ const readCoupons = (payload: unknown): CouponLike[] => {
|
|
|
46
47
|
* expired ones are listed too, which fields the card carries, the card columns
|
|
47
48
|
* and the styling.
|
|
48
49
|
* Properties: showExpired, showLabel, showCode, showValue, showValidity, limit,
|
|
49
|
-
* columns, percentagePrefix, validitySeparator, emptyLabel
|
|
50
|
+
* columns, percentagePrefix, validitySeparator, emptyLabel, decimals,
|
|
51
|
+
* currencyDisplay, currencyText, currencyPosition.
|
|
50
52
|
* Parts: list, item (disabled on an expired coupon), label, code, value,
|
|
51
53
|
* validity, empty.
|
|
52
54
|
*/
|
|
@@ -58,6 +60,7 @@ const CouponListBlock = ({
|
|
|
58
60
|
currentBreakpoint = 'desktop',
|
|
59
61
|
isDesigner = false
|
|
60
62
|
}: BlockRendererProps) => {
|
|
63
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
61
64
|
const showExpired = readToggle(
|
|
62
65
|
block.properties?.showExpired,
|
|
63
66
|
currentBreakpoint,
|
|
@@ -130,7 +133,11 @@ const CouponListBlock = ({
|
|
|
130
133
|
coupon.start_datetime || coupon.created_date,
|
|
131
134
|
locale
|
|
132
135
|
);
|
|
133
|
-
const amount = formatMoney(coupon.amount, {
|
|
136
|
+
const amount = formatMoney(coupon.amount, {
|
|
137
|
+
locale,
|
|
138
|
+
currency,
|
|
139
|
+
...moneyStyle
|
|
140
|
+
});
|
|
134
141
|
const percentage =
|
|
135
142
|
coupon.percentage !== undefined &&
|
|
136
143
|
coupon.percentage !== null &&
|
|
@@ -10,6 +10,7 @@ import { useLocalization } from '@akinon/next/hooks';
|
|
|
10
10
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
11
11
|
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
12
12
|
import { formatDate, formatMoney } from './commerce-format';
|
|
13
|
+
import { readMoneyStyle } from './money-style';
|
|
13
14
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
14
15
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|
|
15
16
|
|
|
@@ -61,7 +62,8 @@ const readCoupons = (payload: unknown): CouponLike[] => {
|
|
|
61
62
|
* from the editor, not a per-block property.
|
|
62
63
|
* Properties: activeTitle, showActive, activeEmptyLabel, usedTitle, showUsed,
|
|
63
64
|
* usedEmptyLabel, limit, showDate, showCode, showAmount, percentagePrefix,
|
|
64
|
-
* termsLinkLabel, termsHref, termsLinkTarget
|
|
65
|
+
* termsLinkLabel, termsHref, termsLinkTarget, decimals, currencyDisplay,
|
|
66
|
+
* currencyText, currencyPosition.
|
|
65
67
|
* Parts: wrapper, column (active/used via data-coupon-wallet-column),
|
|
66
68
|
* column-title, list, item (state: disabled, for a used/redeemed row), date,
|
|
67
69
|
* title, code, amount, terms-link, empty.
|
|
@@ -71,6 +73,7 @@ const CouponWalletBlock = ({
|
|
|
71
73
|
currentBreakpoint = 'desktop',
|
|
72
74
|
isDesigner = false
|
|
73
75
|
}: BlockRendererProps) => {
|
|
76
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
74
77
|
const showActive = readToggle(
|
|
75
78
|
block.properties?.showActive,
|
|
76
79
|
currentBreakpoint,
|
|
@@ -138,7 +141,11 @@ const CouponWalletBlock = ({
|
|
|
138
141
|
|
|
139
142
|
/** The one value a row actually prices at, driven by `benefit_type`. */
|
|
140
143
|
const describeValue = (coupon: CouponLike): string => {
|
|
141
|
-
const amount = formatMoney(coupon.amount, {
|
|
144
|
+
const amount = formatMoney(coupon.amount, {
|
|
145
|
+
locale,
|
|
146
|
+
currency,
|
|
147
|
+
...moneyStyle
|
|
148
|
+
});
|
|
142
149
|
const percentage =
|
|
143
150
|
coupon.percentage !== undefined &&
|
|
144
151
|
coupon.percentage !== null &&
|
|
@@ -12,6 +12,7 @@ import { basketApi } from '@akinon/next/data/client/basket';
|
|
|
12
12
|
import { useAppDispatch } from '@akinon/next/redux/hooks';
|
|
13
13
|
import { useLocalization } from '@akinon/next/hooks';
|
|
14
14
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
15
|
+
import { readMoneyStyle } from './money-style';
|
|
15
16
|
import {
|
|
16
17
|
MOBILE_MEDIA_QUERY_COMPACT,
|
|
17
18
|
TABLET_MEDIA_QUERY_COMPACT
|
|
@@ -87,7 +88,8 @@ const isInStock = (product: FavouriteProductLike): boolean => {
|
|
|
87
88
|
* every caption and the styling.
|
|
88
89
|
* Properties: columnsDesktop, columnsMobile, limit, showRemove, removeIcon,
|
|
89
90
|
* showAddToCart, showPrice, showRetailPrice, imageRatio, nameLines, addLabel,
|
|
90
|
-
* outOfStockLabel, removeLabel, emptyLabel
|
|
91
|
+
* outOfStockLabel, removeLabel, emptyLabel, decimals, currencyDisplay,
|
|
92
|
+
* currencyText, currencyPosition.
|
|
91
93
|
* Parts: list, item, link, image, name, price, retail-price, add, remove,
|
|
92
94
|
* empty.
|
|
93
95
|
*/
|
|
@@ -99,6 +101,7 @@ const FavouritesGridBlock = ({
|
|
|
99
101
|
currentBreakpoint = 'desktop',
|
|
100
102
|
isDesigner = false
|
|
101
103
|
}: BlockRendererProps) => {
|
|
104
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
102
105
|
const columnsDesktop = clampColumns(
|
|
103
106
|
getResponsiveValue(block.properties?.columnsDesktop, currentBreakpoint, 4),
|
|
104
107
|
4,
|
|
@@ -249,7 +252,8 @@ const FavouritesGridBlock = ({
|
|
|
249
252
|
locale,
|
|
250
253
|
// The normalizer prefers the product's own currency_type; the active
|
|
251
254
|
// currency is only the fallback for payloads that omit it.
|
|
252
|
-
currency: localeCurrency
|
|
255
|
+
currency: localeCurrency,
|
|
256
|
+
money: moneyStyle
|
|
253
257
|
});
|
|
254
258
|
const href = normalized?.url && normalized.url !== '#'
|
|
255
259
|
? normalized.url
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getResponsiveValue } from '../utils';
|
|
2
|
+
import type {
|
|
3
|
+
MoneyCurrencyDisplay,
|
|
4
|
+
MoneyCurrencyPosition,
|
|
5
|
+
MoneyDecimals,
|
|
6
|
+
MoneyStyleOptions
|
|
7
|
+
} from './commerce-format';
|
|
8
|
+
|
|
9
|
+
const DECIMALS: MoneyDecimals[] = ['always', 'auto', 'never'];
|
|
10
|
+
const CURRENCY_DISPLAYS: MoneyCurrencyDisplay[] = [
|
|
11
|
+
'',
|
|
12
|
+
'symbol',
|
|
13
|
+
'code',
|
|
14
|
+
'custom',
|
|
15
|
+
'none'
|
|
16
|
+
];
|
|
17
|
+
const CURRENCY_POSITIONS: MoneyCurrencyPosition[] = ['', 'before', 'after'];
|
|
18
|
+
|
|
19
|
+
const readOption = <T extends string>(
|
|
20
|
+
value: unknown,
|
|
21
|
+
breakpoint: string,
|
|
22
|
+
allowed: T[],
|
|
23
|
+
fallback: T
|
|
24
|
+
): T => {
|
|
25
|
+
const raw = String(getResponsiveValue(value, breakpoint, fallback));
|
|
26
|
+
return (allowed as string[]).includes(raw) ? (raw as T) : fallback;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The money format a block's editor properties author — `decimals`,
|
|
31
|
+
* `currencyDisplay`, `currencyText` and `currencyPosition`, the four the
|
|
32
|
+
* editor's money controls write. Unset properties resolve to the format the
|
|
33
|
+
* theme always printed ("₺1.599,00"), so a published composition does not
|
|
34
|
+
* move until somebody picks a format for it.
|
|
35
|
+
*/
|
|
36
|
+
export const readMoneyStyle = (
|
|
37
|
+
properties: Record<string, unknown> | undefined,
|
|
38
|
+
breakpoint: string
|
|
39
|
+
): MoneyStyleOptions => ({
|
|
40
|
+
decimals: readOption(properties?.decimals, breakpoint, DECIMALS, 'always'),
|
|
41
|
+
currencyDisplay: readOption(
|
|
42
|
+
properties?.currencyDisplay,
|
|
43
|
+
breakpoint,
|
|
44
|
+
CURRENCY_DISPLAYS,
|
|
45
|
+
''
|
|
46
|
+
),
|
|
47
|
+
currencyText: String(
|
|
48
|
+
getResponsiveValue(properties?.currencyText, breakpoint, '')
|
|
49
|
+
),
|
|
50
|
+
currencyPosition: readOption(
|
|
51
|
+
properties?.currencyPosition,
|
|
52
|
+
breakpoint,
|
|
53
|
+
CURRENCY_POSITIONS,
|
|
54
|
+
''
|
|
55
|
+
)
|
|
56
|
+
});
|
|
@@ -7,6 +7,7 @@ import { useGetOrderQuery } from '@akinon/next/data/client/account';
|
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
8
8
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
9
9
|
import { formatDate, formatMoney } from './commerce-format';
|
|
10
|
+
import { readMoneyStyle } from './money-style';
|
|
10
11
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
11
12
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
12
13
|
import {
|
|
@@ -283,7 +284,8 @@ const formatAddress = (address: AddressLike | null | undefined): string[] => {
|
|
|
283
284
|
* pieceCountLabel, showDiscountItems, showInstallmentInfo,
|
|
284
285
|
* installmentInfoLabel, subtotalLabel, discountLabel, shippingLabel,
|
|
285
286
|
* freeShippingLabel, totalLabel, trackingLabel,
|
|
286
|
-
* showPaymentInfo, paymentInfoLabel
|
|
287
|
+
* showPaymentInfo, paymentInfoLabel, decimals, currencyDisplay,
|
|
288
|
+
* currencyText, currencyPosition.
|
|
287
289
|
* Parts: detail, header, number, date, header-total, status,
|
|
288
290
|
* invoice-link, cancel-link, list, item, image, name, quantity, color, size,
|
|
289
291
|
* item-status, item-tracking, item-return-code, price, shipment,
|
|
@@ -298,6 +300,7 @@ const OrderDetailBlock = ({
|
|
|
298
300
|
currentBreakpoint = 'desktop',
|
|
299
301
|
isDesigner = false
|
|
300
302
|
}: BlockRendererProps) => {
|
|
303
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
301
304
|
const showShipmentStatus = readToggle(
|
|
302
305
|
block.properties?.showShipmentStatus,
|
|
303
306
|
currentBreakpoint,
|
|
@@ -480,7 +483,8 @@ const OrderDetailBlock = ({
|
|
|
480
483
|
// The order carries the currency it was placed in; the storefront currency is
|
|
481
484
|
// only the fallback for payloads that omit it.
|
|
482
485
|
const currency = order.currency?.value || localeCurrency;
|
|
483
|
-
const money = (value: unknown) =>
|
|
486
|
+
const money = (value: unknown) =>
|
|
487
|
+
formatMoney(value, { locale, currency, ...moneyStyle });
|
|
484
488
|
|
|
485
489
|
// Navigating inside the designer iframe would drop the author out of the page
|
|
486
490
|
// they are editing, so every link is inert there.
|
|
@@ -512,8 +516,16 @@ const OrderDetailBlock = ({
|
|
|
512
516
|
url: line.href,
|
|
513
517
|
href: line.href,
|
|
514
518
|
quantity: line.quantity,
|
|
515
|
-
price: formatMoney(line.price, {
|
|
516
|
-
|
|
519
|
+
price: formatMoney(line.price, {
|
|
520
|
+
locale,
|
|
521
|
+
currency: lineCurrency,
|
|
522
|
+
...moneyStyle
|
|
523
|
+
}),
|
|
524
|
+
total: formatMoney(line.total, {
|
|
525
|
+
locale,
|
|
526
|
+
currency: lineCurrency,
|
|
527
|
+
...moneyStyle
|
|
528
|
+
}),
|
|
517
529
|
status: line.item.status?.label || '',
|
|
518
530
|
statusValue: line.item.status?.value || ''
|
|
519
531
|
};
|
|
@@ -855,7 +867,8 @@ const OrderDetailBlock = ({
|
|
|
855
867
|
printed before. */}
|
|
856
868
|
{formatMoney(line.total, {
|
|
857
869
|
locale,
|
|
858
|
-
currency: lineCurrency
|
|
870
|
+
currency: lineCurrency,
|
|
871
|
+
...moneyStyle
|
|
859
872
|
})}
|
|
860
873
|
</div>
|
|
861
874
|
</div>
|
|
@@ -7,6 +7,7 @@ import { useGetOrdersQuery } from '@akinon/next/data/client/account';
|
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
8
8
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
9
9
|
import { formatDate, formatMoney } from './commerce-format';
|
|
10
|
+
import { readMoneyStyle } from './money-style';
|
|
10
11
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
11
12
|
import { readToggle } from './product-block-utils';
|
|
12
13
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -54,7 +55,8 @@ const sortOrders = (orders: OrderLike[], sort: string): OrderLike[] => {
|
|
|
54
55
|
* Properties: limit, orderHrefBase, sort, showDate, showStatus, showAmount,
|
|
55
56
|
* showItemImages, imageWidth, imageHeight, imageGap, imageAlign, maxImages,
|
|
56
57
|
* numberLabel, dateLabel, totalLabel, detailLinkLabel, showTitle, title,
|
|
57
|
-
* showPagination, perPage, emptyLabel
|
|
58
|
+
* showPagination, perPage, emptyLabel, decimals, currencyDisplay,
|
|
59
|
+
* currencyText, currencyPosition.
|
|
58
60
|
* Parts: title, list, item, images, image-overflow, link, date, total,
|
|
59
61
|
* detail-link, status, pagination, empty.
|
|
60
62
|
*/
|
|
@@ -66,6 +68,7 @@ const OrderListBlock = ({
|
|
|
66
68
|
currentBreakpoint = 'desktop',
|
|
67
69
|
isDesigner = false
|
|
68
70
|
}: BlockRendererProps) => {
|
|
71
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
69
72
|
const rawLimit = Number(
|
|
70
73
|
getResponsiveValue(block.properties?.limit, currentBreakpoint, 5)
|
|
71
74
|
);
|
|
@@ -222,11 +225,13 @@ const OrderListBlock = ({
|
|
|
222
225
|
date: formatDate(order.created_date, locale),
|
|
223
226
|
total: formatMoney(order.amount, {
|
|
224
227
|
locale,
|
|
225
|
-
currency: order.currency?.value || localeCurrency
|
|
228
|
+
currency: order.currency?.value || localeCurrency,
|
|
229
|
+
...moneyStyle
|
|
226
230
|
}),
|
|
227
231
|
price: formatMoney(order.amount, {
|
|
228
232
|
locale,
|
|
229
|
-
currency: order.currency?.value || localeCurrency
|
|
233
|
+
currency: order.currency?.value || localeCurrency,
|
|
234
|
+
...moneyStyle
|
|
230
235
|
}),
|
|
231
236
|
status: order.status?.label || '',
|
|
232
237
|
statusValue: order.status?.value || '',
|
|
@@ -520,7 +525,8 @@ const OrderListBlock = ({
|
|
|
520
525
|
totalLabel,
|
|
521
526
|
formatMoney(order.amount, {
|
|
522
527
|
locale,
|
|
523
|
-
currency: order.currency?.value || localeCurrency
|
|
528
|
+
currency: order.currency?.value || localeCurrency,
|
|
529
|
+
...moneyStyle
|
|
524
530
|
}),
|
|
525
531
|
'total',
|
|
526
532
|
'data-order-list-total-inline'
|
|
@@ -562,7 +568,8 @@ const OrderListBlock = ({
|
|
|
562
568
|
storefront currency is only the fallback. */}
|
|
563
569
|
{formatMoney(order.amount, {
|
|
564
570
|
locale,
|
|
565
|
-
currency: order.currency?.value || localeCurrency
|
|
571
|
+
currency: order.currency?.value || localeCurrency,
|
|
572
|
+
...moneyStyle
|
|
566
573
|
})}
|
|
567
574
|
</span>
|
|
568
575
|
)}
|
|
@@ -8,6 +8,7 @@ import { useLocalization } from '@akinon/next/hooks';
|
|
|
8
8
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
9
9
|
import { useThemePageContext } from '../theme-page-context';
|
|
10
10
|
import { formatDate, formatMoney, parseAmount } from './commerce-format';
|
|
11
|
+
import { readMoneyStyle } from './money-style';
|
|
11
12
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
12
13
|
import { groupOrderItems, readToggle } from './product-block-utils';
|
|
13
14
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -177,6 +178,7 @@ const OrderSuccessBlock = ({
|
|
|
177
178
|
currentBreakpoint = 'desktop',
|
|
178
179
|
isDesigner = false
|
|
179
180
|
}: BlockRendererProps) => {
|
|
181
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
180
182
|
const prop = (key: string, fallback: string) =>
|
|
181
183
|
String(
|
|
182
184
|
getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
|
|
@@ -276,7 +278,7 @@ const OrderSuccessBlock = ({
|
|
|
276
278
|
// is only the fallback.
|
|
277
279
|
const currency = order.currency?.value || localeCurrency || '';
|
|
278
280
|
const money = (value?: string | number) =>
|
|
279
|
-
formatMoney(value, { locale, currency });
|
|
281
|
+
formatMoney(value, { locale, currency, ...moneyStyle });
|
|
280
282
|
|
|
281
283
|
const items = Array.isArray(order.orderitem_set) ? order.orderitem_set : [];
|
|
282
284
|
|
|
@@ -5,6 +5,7 @@ import { buildClientRequestUrl, getCookie } from '@akinon/next/utils';
|
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
7
7
|
import { formatDate, formatMoney } from './commerce-format';
|
|
8
|
+
import { readMoneyStyle } from './money-style';
|
|
8
9
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
9
10
|
import { readToggle } from './product-block-utils';
|
|
10
11
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -71,6 +72,7 @@ const OrderTrackingBlock = ({
|
|
|
71
72
|
currentBreakpoint = 'desktop',
|
|
72
73
|
isDesigner = false
|
|
73
74
|
}: BlockRendererProps) => {
|
|
75
|
+
const moneyStyle = readMoneyStyle(block.properties, currentBreakpoint);
|
|
74
76
|
const prop = (key: string, fallback: string) =>
|
|
75
77
|
String(
|
|
76
78
|
getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
|
|
@@ -245,7 +247,7 @@ const OrderTrackingBlock = ({
|
|
|
245
247
|
resultRows.push({
|
|
246
248
|
key: 'total',
|
|
247
249
|
label: totalLabel,
|
|
248
|
-
value: formatMoney(order?.amount, { locale, currency }),
|
|
250
|
+
value: formatMoney(order?.amount, { locale, currency, ...moneyStyle }),
|
|
249
251
|
type: 'total'
|
|
250
252
|
});
|
|
251
253
|
}
|
|
@@ -257,7 +259,7 @@ const OrderTrackingBlock = ({
|
|
|
257
259
|
name: item.product?.name || '',
|
|
258
260
|
image: item.product?.image || '',
|
|
259
261
|
status: item.status?.label || '',
|
|
260
|
-
price: formatMoney(item.price, { locale, currency })
|
|
262
|
+
price: formatMoney(item.price, { locale, currency, ...moneyStyle })
|
|
261
263
|
}));
|
|
262
264
|
|
|
263
265
|
const customRows = hasItemTemplate(block, currentBreakpoint) ? (
|
|
@@ -492,7 +494,11 @@ const OrderTrackingBlock = ({
|
|
|
492
494
|
{...partAttrs('line-total')}
|
|
493
495
|
style={{ flex: '0 0 auto' }}
|
|
494
496
|
>
|
|
495
|
-
{formatMoney(item.price, {
|
|
497
|
+
{formatMoney(item.price, {
|
|
498
|
+
locale,
|
|
499
|
+
currency,
|
|
500
|
+
...moneyStyle
|
|
501
|
+
})}
|
|
496
502
|
</span>
|
|
497
503
|
</li>
|
|
498
504
|
))}
|