@akinon/pz-theme 2.0.167-rc.0 → 2.0.167
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 +7 -53
- package/package.json +4 -4
- package/src/__tests__/editor-locale-bridge.test.tsx +1 -32
- package/src/blocks/__tests__/order-list-empty-state.test.tsx +195 -0
- package/src/blocks/__tests__/text-block.test.tsx +6 -34
- package/src/blocks/order-detail-block.tsx +95 -113
- package/src/blocks/order-list-block.tsx +77 -3
- package/src/blocks/text-block.tsx +2 -6
- package/src/custom/README.md +219 -100
- package/src/custom/__tests__/use-theme-setting.test.tsx +0 -97
- package/src/custom/collect.ts +9 -4
- package/src/custom/property-types.ts +15 -15
- package/src/custom/use-theme-setting.ts +5 -21
- package/src/editor-locale-bridge.tsx +11 -39
- package/src/theme-placeholder-wrapper.tsx +0 -3
- package/src/blocks/__tests__/order-line-actions.test.tsx +0 -54
- package/src/blocks/order-line-actions.ts +0 -45
- package/src/utils/__tests__/sanitize-rich-text.test.ts +0 -80
- package/src/utils/sanitize-rich-text.ts +0 -215
package/CHANGELOG.md
CHANGED
|
@@ -1,58 +1,12 @@
|
|
|
1
1
|
# @akinon/pz-theme
|
|
2
2
|
|
|
3
|
-
## 2.0.167
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- 17a598dae: ZERO-5296: report the storefront default language to the editor
|
|
11
|
-
- 3b05a407d: ZERO-5232: allow richtext settings on custom blocks
|
|
12
|
-
- Updated dependencies [86187b1c2]
|
|
13
|
-
- Updated dependencies [08390a87]
|
|
14
|
-
- Updated dependencies [0cf9ea239]
|
|
15
|
-
- Updated dependencies [324f97d55]
|
|
16
|
-
- Updated dependencies [4c3465ae]
|
|
17
|
-
- Updated dependencies [51ea06888]
|
|
18
|
-
- Updated dependencies
|
|
19
|
-
- Updated dependencies [549da545]
|
|
20
|
-
- Updated dependencies [2fea41430]
|
|
21
|
-
- Updated dependencies [b55acb768]
|
|
22
|
-
- Updated dependencies [760258c1c]
|
|
23
|
-
- Updated dependencies [143be2b9d]
|
|
24
|
-
- Updated dependencies [7889b08fe]
|
|
25
|
-
- Updated dependencies [9f8cd3bc5]
|
|
26
|
-
- Updated dependencies [d635d345]
|
|
27
|
-
- Updated dependencies [bfafa3f49]
|
|
28
|
-
- Updated dependencies [d860c917]
|
|
29
|
-
- Updated dependencies [ac6cada1c]
|
|
30
|
-
- Updated dependencies [e90c0e02]
|
|
31
|
-
- Updated dependencies [dc7f9ca5]
|
|
32
|
-
- Updated dependencies [57d7eb305]
|
|
33
|
-
- Updated dependencies [0145dc3f]
|
|
34
|
-
- Updated dependencies [d99a6a7d5]
|
|
35
|
-
- Updated dependencies [92086b29]
|
|
36
|
-
- Updated dependencies [227b7408]
|
|
37
|
-
- Updated dependencies [9db81a714]
|
|
38
|
-
- Updated dependencies [e3c2bab2]
|
|
39
|
-
- Updated dependencies [591e345e1]
|
|
40
|
-
- Updated dependencies [4de5303c5]
|
|
41
|
-
- Updated dependencies [95b139dc1]
|
|
42
|
-
- Updated dependencies [1d00f2d06]
|
|
43
|
-
- Updated dependencies [4ac7b2a1e]
|
|
44
|
-
- Updated dependencies [4998a9631]
|
|
45
|
-
- Updated dependencies [804d2bd6c]
|
|
46
|
-
- Updated dependencies [3909d3224]
|
|
47
|
-
- Updated dependencies [6a3d8a631]
|
|
48
|
-
- Updated dependencies [e18836b20]
|
|
49
|
-
- Updated dependencies [b1111d7b1]
|
|
50
|
-
- Updated dependencies [1595d458e]
|
|
51
|
-
- Updated dependencies [dca128112]
|
|
52
|
-
- Updated dependencies [9a7b0937]
|
|
53
|
-
- Updated dependencies [8853822c]
|
|
54
|
-
- @akinon/next@2.0.167-rc.0
|
|
55
|
-
- @akinon/theme-contract@2.0.167-rc.0
|
|
3
|
+
## 2.0.167
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e0fc41c3: ZERO-5511: Native empty state for the order-list block. A child group marked with role `order-list-empty` is drawn in place of the whole block (title included) once a signed-in shopper turns out to have no orders, so the heading, the explanation, the call-to-action links and product suggestions are authored in the panel instead of being switched on with custom CSS. `previewEmptyState` draws it on the canvas (editor only). The empty message (`emptyLabel`) and the `data-order-list-state="empty"` wrapper stay as they were, and the storefront now shows them for an empty list even when a row layout is set.
|
|
8
|
+
- @akinon/next@2.0.167
|
|
9
|
+
- @akinon/theme-contract@2.0.167
|
|
56
10
|
|
|
57
11
|
## 2.0.166
|
|
58
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.167
|
|
4
|
+
"version": "2.0.167",
|
|
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.167
|
|
11
|
+
"@akinon/next": "2.0.167",
|
|
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.167
|
|
16
|
+
"@akinon/theme-contract": "2.0.167",
|
|
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.167
|
|
24
|
+
"@akinon/next": "2.0.167",
|
|
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",
|
|
@@ -28,7 +28,6 @@ const SETTINGS_LOCALES = [
|
|
|
28
28
|
const mockLocalization = {
|
|
29
29
|
locale: 'tr',
|
|
30
30
|
locales: SETTINGS_LOCALES as Array<Record<string, unknown>>,
|
|
31
|
-
defaultLocaleValue: 'tr',
|
|
32
31
|
setLocale: jest.fn()
|
|
33
32
|
};
|
|
34
33
|
|
|
@@ -107,7 +106,6 @@ beforeEach(() => {
|
|
|
107
106
|
postMessage.mockClear();
|
|
108
107
|
mockLocalization.locale = 'tr';
|
|
109
108
|
mockLocalization.locales = SETTINGS_LOCALES;
|
|
110
|
-
mockLocalization.defaultLocaleValue = 'tr';
|
|
111
109
|
mockLocalization.setLocale = jest.fn();
|
|
112
110
|
delete (window as { __iframeReadySent?: boolean }).__iframeReadySent;
|
|
113
111
|
});
|
|
@@ -146,8 +144,7 @@ describe('inside the editor', () => {
|
|
|
146
144
|
{ value: 'en', label: 'English' },
|
|
147
145
|
{ value: 'tr', label: 'Türkçe' },
|
|
148
146
|
{ value: 'de', label: 'de' }
|
|
149
|
-
]
|
|
150
|
-
defaultLocale: 'tr'
|
|
147
|
+
]
|
|
151
148
|
}
|
|
152
149
|
})
|
|
153
150
|
]);
|
|
@@ -157,34 +154,6 @@ describe('inside the editor', () => {
|
|
|
157
154
|
expect(targetOrigin).toBe(EDITOR_ORIGIN);
|
|
158
155
|
});
|
|
159
156
|
|
|
160
|
-
it('posts the languages before the document reports ready', () => {
|
|
161
|
-
// The editor holds every per-language write until IFRAME_READY and takes
|
|
162
|
-
// "ready without languages" to mean they are never coming, so the order
|
|
163
|
-
// of the two frames is part of the contract.
|
|
164
|
-
render(placeholder('header'));
|
|
165
|
-
|
|
166
|
-
const types = postMessage.mock.calls.map(([message]) => message?.type);
|
|
167
|
-
expect(types).toContain('LOCALE_UPDATE');
|
|
168
|
-
expect(types).toContain('IFRAME_READY');
|
|
169
|
-
expect(types.indexOf('LOCALE_UPDATE')).toBeLessThan(
|
|
170
|
-
types.indexOf('IFRAME_READY')
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("posts the brand's default language, whichever it is", () => {
|
|
175
|
-
// The editor's per-language text controls take the plain-string form to
|
|
176
|
-
// mean this language; left to guess, the editor assumed English.
|
|
177
|
-
mockLocalization.defaultLocaleValue = 'de';
|
|
178
|
-
|
|
179
|
-
render(<EditorLocaleBridge />);
|
|
180
|
-
|
|
181
|
-
expect(localeFrames()).toEqual([
|
|
182
|
-
expect.objectContaining({
|
|
183
|
-
data: expect.objectContaining({ locale: 'tr', defaultLocale: 'de' })
|
|
184
|
-
})
|
|
185
|
-
]);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
157
|
it("follows the editor's switch exactly once, with several placeholders mounted", () => {
|
|
189
158
|
render(
|
|
190
159
|
<>
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import OrderListBlock from '../order-list-block';
|
|
4
|
+
|
|
5
|
+
const state: {
|
|
6
|
+
status: string;
|
|
7
|
+
query: { data?: unknown; isLoading: boolean };
|
|
8
|
+
hasTemplate: boolean;
|
|
9
|
+
} = {
|
|
10
|
+
status: 'authenticated',
|
|
11
|
+
query: { data: { count: 0, results: [] }, isLoading: false },
|
|
12
|
+
hasTemplate: false
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
jest.mock('../item-template', () => ({
|
|
16
|
+
__esModule: true,
|
|
17
|
+
hasItemTemplate: () => state.hasTemplate,
|
|
18
|
+
// Like the real list on the storefront: nothing to repeat, nothing drawn.
|
|
19
|
+
ItemTemplateList: ({ items }: { items: unknown[] }) =>
|
|
20
|
+
items.length > 0 ? <li data-testid="template-row" /> : null
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock('../../theme-block', () => ({
|
|
24
|
+
__esModule: true,
|
|
25
|
+
default: ({ block }: { block: { id: string } }) => (
|
|
26
|
+
<div data-testid="theme-block" data-block-id={block.id} />
|
|
27
|
+
)
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
jest.mock('next-auth/react', () => ({
|
|
31
|
+
useSession: () => ({ data: null, status: state.status })
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
jest.mock('@akinon/next/hooks', () => ({
|
|
35
|
+
__esModule: true,
|
|
36
|
+
useLocalization: () => ({ locale: 'tr', currency: 'try' })
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
jest.mock('@akinon/next/data/client/account', () => ({
|
|
40
|
+
__esModule: true,
|
|
41
|
+
useGetOrdersQuery: () => state.query
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const ORDER = {
|
|
45
|
+
id: 42,
|
|
46
|
+
number: 'ORD-42',
|
|
47
|
+
amount: '100.00',
|
|
48
|
+
created_date: '2026-09-01T10:00:00Z',
|
|
49
|
+
status: { value: '550', label: 'Delivered' },
|
|
50
|
+
orderitem_set: []
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const emptyLayout = (overrides: Record<string, unknown> = {}) => ({
|
|
54
|
+
id: 'empty-layout',
|
|
55
|
+
type: 'group',
|
|
56
|
+
properties: { role: { desktop: 'order-list-empty' } },
|
|
57
|
+
styles: {},
|
|
58
|
+
blocks: [],
|
|
59
|
+
...overrides
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const block = (
|
|
63
|
+
properties: Record<string, unknown> = {},
|
|
64
|
+
blocks: unknown[] = [emptyLayout()]
|
|
65
|
+
) =>
|
|
66
|
+
({
|
|
67
|
+
id: 'order-list-1',
|
|
68
|
+
type: 'order-list',
|
|
69
|
+
properties: {
|
|
70
|
+
showTitle: { desktop: true },
|
|
71
|
+
title: { desktop: 'My orders' },
|
|
72
|
+
...properties
|
|
73
|
+
},
|
|
74
|
+
styles: {},
|
|
75
|
+
blocks
|
|
76
|
+
} as never);
|
|
77
|
+
|
|
78
|
+
const renderBlock = (
|
|
79
|
+
props: Record<string, unknown> = {},
|
|
80
|
+
isDesigner = false,
|
|
81
|
+
blocks?: unknown[]
|
|
82
|
+
) =>
|
|
83
|
+
render(
|
|
84
|
+
<OrderListBlock
|
|
85
|
+
block={block(props, blocks)}
|
|
86
|
+
placeholderId="p1"
|
|
87
|
+
sectionId="s1"
|
|
88
|
+
isDesigner={isDesigner}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
state.status = 'authenticated';
|
|
94
|
+
state.query = { data: { count: 0, results: [] }, isLoading: false };
|
|
95
|
+
state.hasTemplate = false;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('order list empty state', () => {
|
|
99
|
+
it('draws the empty-orders layout in place of the block for a shopper without orders', () => {
|
|
100
|
+
const { container } = renderBlock();
|
|
101
|
+
|
|
102
|
+
expect(screen.getByTestId('theme-block')).toHaveAttribute(
|
|
103
|
+
'data-block-id',
|
|
104
|
+
'empty-layout'
|
|
105
|
+
);
|
|
106
|
+
expect(
|
|
107
|
+
container.querySelector('[data-order-list-state="empty"]')
|
|
108
|
+
).not.toBeNull();
|
|
109
|
+
// The layout carries its own heading; the block title gives way.
|
|
110
|
+
expect(container.textContent).not.toContain('My orders');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('keeps the list when the shopper has orders', () => {
|
|
114
|
+
state.query = { data: { count: 1, results: [ORDER] }, isLoading: false };
|
|
115
|
+
const { container } = renderBlock();
|
|
116
|
+
|
|
117
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
118
|
+
expect(container.querySelector('[data-order-list-item]')).not.toBeNull();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('claims nothing while the orders are loading or for a guest', () => {
|
|
122
|
+
state.query = { data: undefined, isLoading: true };
|
|
123
|
+
renderBlock();
|
|
124
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
125
|
+
|
|
126
|
+
state.status = 'unauthenticated';
|
|
127
|
+
state.query = { data: undefined, isLoading: false };
|
|
128
|
+
renderBlock();
|
|
129
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('leaves a hidden layout off the storefront and falls back to the empty message', () => {
|
|
133
|
+
const { container } = renderBlock(
|
|
134
|
+
{ emptyLabel: { desktop: 'No orders yet' } },
|
|
135
|
+
false,
|
|
136
|
+
[emptyLayout({ hidden: true })]
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
140
|
+
expect(
|
|
141
|
+
container.querySelector('[data-order-list-state="empty"]')?.textContent
|
|
142
|
+
).toBe('No orders yet');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('shows the empty message on the storefront even when a row layout is set', () => {
|
|
146
|
+
state.hasTemplate = true;
|
|
147
|
+
const { container } = renderBlock(
|
|
148
|
+
{ emptyLabel: { desktop: 'No orders yet' } },
|
|
149
|
+
false,
|
|
150
|
+
[]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
expect(container.querySelector('[data-order-list]')?.tagName).toBe('DIV');
|
|
154
|
+
expect(
|
|
155
|
+
container.querySelector('[data-order-list-state="empty"]')?.textContent
|
|
156
|
+
).toBe('No orders yet');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('draws the empty-orders layout on the storefront even when a row layout is set', () => {
|
|
160
|
+
state.hasTemplate = true;
|
|
161
|
+
renderBlock();
|
|
162
|
+
|
|
163
|
+
expect(screen.getByTestId('theme-block')).toHaveAttribute(
|
|
164
|
+
'data-block-id',
|
|
165
|
+
'empty-layout'
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('previews the layout on the canvas only when asked, even with orders', () => {
|
|
170
|
+
state.query = { data: { count: 1, results: [ORDER] }, isLoading: false };
|
|
171
|
+
|
|
172
|
+
renderBlock({}, true);
|
|
173
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
174
|
+
|
|
175
|
+
renderBlock({ previewEmptyState: { desktop: true } }, true);
|
|
176
|
+
expect(screen.getByTestId('theme-block')).toHaveAttribute(
|
|
177
|
+
'data-block-id',
|
|
178
|
+
'empty-layout'
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('never lets the preview toggle empty a live list', () => {
|
|
183
|
+
state.query = { data: { count: 1, results: [ORDER] }, isLoading: false };
|
|
184
|
+
renderBlock({ previewEmptyState: { desktop: true } }, false);
|
|
185
|
+
|
|
186
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('keeps sample rows on the canvas when a row layout is there to design', () => {
|
|
190
|
+
state.hasTemplate = true;
|
|
191
|
+
renderBlock({}, true);
|
|
192
|
+
|
|
193
|
+
expect(screen.queryByTestId('theme-block')).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -2,31 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import TextBlock from '../text-block';
|
|
4
4
|
|
|
5
|
-
// The
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
const mockLocalization = { locale: 'en-us', defaultLocaleValue: 'en-us' };
|
|
9
|
-
|
|
10
|
-
jest.mock('@akinon/next/hooks', () => ({
|
|
11
|
-
__esModule: true,
|
|
12
|
-
useLocalization: () => ({
|
|
13
|
-
t: (path: string) => path,
|
|
14
|
-
setLocale: () => {},
|
|
15
|
-
setCurrency: () => {},
|
|
16
|
-
locale: mockLocalization.locale,
|
|
17
|
-
currency: 'USD',
|
|
18
|
-
locales: [{ value: 'en-us' }, { value: 'tr-tr' }],
|
|
19
|
-
currencies: ['USD'],
|
|
20
|
-
defaultLocaleValue: mockLocalization.defaultLocaleValue,
|
|
21
|
-
defaultCurrencyCode: 'USD',
|
|
22
|
-
localeUrlStrategy: 'hide-default-locale'
|
|
23
|
-
})
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
mockLocalization.locale = 'en-us';
|
|
28
|
-
mockLocalization.defaultLocaleValue = 'en-us';
|
|
29
|
-
});
|
|
5
|
+
// The mocked useLocalization (jest/__mocks__/akinon-next-hooks.js) returns
|
|
6
|
+
// locale: 'en-us'. process.env.NEXT_PUBLIC_DEFAULT_LOCALE is unset in the
|
|
7
|
+
// jest env, so the component's defaultLocale falls back to 'en'.
|
|
30
8
|
|
|
31
9
|
const renderBlock = (value: unknown, styles?: Record<string, unknown>) => {
|
|
32
10
|
const block: any = { id: 'b1', type: 'text', value, styles };
|
|
@@ -56,16 +34,10 @@ describe('TextBlock value-shape resolution', () => {
|
|
|
56
34
|
);
|
|
57
35
|
});
|
|
58
36
|
|
|
59
|
-
it(
|
|
60
|
-
|
|
61
|
-
// the block used to show: its default was read from an env variable
|
|
62
|
-
// that falls back to English, not from the storefront (ZERO-5296).
|
|
63
|
-
mockLocalization.locale = 'de-de';
|
|
64
|
-
mockLocalization.defaultLocaleValue = 'tr-tr';
|
|
65
|
-
|
|
66
|
-
const { container } = renderBlock({ en: 'EN', 'tr-tr': 'TR' });
|
|
37
|
+
it('falls back to defaultLocale ("en") when active locale is absent', () => {
|
|
38
|
+
const { container } = renderBlock({ en: 'EN', fr: 'FR' });
|
|
67
39
|
expect((container.querySelector('div') as HTMLDivElement).innerHTML).toBe(
|
|
68
|
-
'
|
|
40
|
+
'EN'
|
|
69
41
|
);
|
|
70
42
|
});
|
|
71
43
|
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
ITEM_TEMPLATE_ROLE,
|
|
14
14
|
ItemTemplateList
|
|
15
15
|
} from './item-template';
|
|
16
|
-
import { renderOrderLineActions } from './order-line-actions';
|
|
17
16
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
18
17
|
import type { Block } from '../theme-block';
|
|
19
18
|
import {
|
|
@@ -30,7 +29,7 @@ import {
|
|
|
30
29
|
useOrderActionRules
|
|
31
30
|
} from '../utils/order-action-rules';
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
interface OrderItemLike {
|
|
34
33
|
id?: number;
|
|
35
34
|
price?: string;
|
|
36
35
|
quantity?: string | number;
|
|
@@ -52,7 +51,7 @@ type OrderItemLike = {
|
|
|
52
51
|
absolute_url?: string;
|
|
53
52
|
attributes?: Record<string, unknown>;
|
|
54
53
|
} | null;
|
|
55
|
-
}
|
|
54
|
+
}
|
|
56
55
|
|
|
57
56
|
interface AddressLike {
|
|
58
57
|
first_name?: string;
|
|
@@ -431,7 +430,7 @@ const formatAddress = (address: AddressLike | null | undefined): string[] => {
|
|
|
431
430
|
* currencyText, currencyPosition.
|
|
432
431
|
* Parts: detail, header, number, date, header-total, status,
|
|
433
432
|
* invoice-link, cancel-link, return-link, list, item, image, name, quantity,
|
|
434
|
-
* color, size, item-status, item-tracking, item-return-code,
|
|
433
|
+
* color, size, item-status, item-tracking, item-return-code,
|
|
435
434
|
* item-return-method, item-return-carrier, item-pickup-date, price, shipment,
|
|
436
435
|
* delivery, tracking, addresses, address, address-label, totals, total-row,
|
|
437
436
|
* total-label, total-value, grand-total, payment-info, payment-info-label.
|
|
@@ -1132,21 +1131,6 @@ const OrderDetailBlock = ({
|
|
|
1132
1131
|
showPickupDate && returnDetails.pickupDate
|
|
1133
1132
|
? formatPickupDate(returnDetails.pickupDate, locale)
|
|
1134
1133
|
: '';
|
|
1135
|
-
const lineActions = renderOrderLineActions({
|
|
1136
|
-
order,
|
|
1137
|
-
line,
|
|
1138
|
-
isDesigner
|
|
1139
|
-
});
|
|
1140
|
-
const showLineMeta =
|
|
1141
|
-
showShipmentStatus &&
|
|
1142
|
-
Boolean(
|
|
1143
|
-
item.status?.label ||
|
|
1144
|
-
trackingValue ||
|
|
1145
|
-
returnCodeValue ||
|
|
1146
|
-
returnMethodValue ||
|
|
1147
|
-
returnCarrierValue ||
|
|
1148
|
-
pickupDateValue
|
|
1149
|
-
);
|
|
1150
1134
|
|
|
1151
1135
|
return (
|
|
1152
1136
|
<li
|
|
@@ -1262,100 +1246,98 @@ const OrderDetailBlock = ({
|
|
|
1262
1246
|
</div>
|
|
1263
1247
|
</div>
|
|
1264
1248
|
|
|
1265
|
-
{
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
</div>
|
|
1358
|
-
)}
|
|
1249
|
+
{showShipmentStatus &&
|
|
1250
|
+
(item.status?.label ||
|
|
1251
|
+
trackingValue ||
|
|
1252
|
+
returnCodeValue ||
|
|
1253
|
+
returnMethodValue ||
|
|
1254
|
+
returnCarrierValue ||
|
|
1255
|
+
pickupDateValue) && (
|
|
1256
|
+
<div
|
|
1257
|
+
style={{
|
|
1258
|
+
display: 'flex',
|
|
1259
|
+
flexDirection: 'column',
|
|
1260
|
+
alignItems: isMobile ? 'flex-start' : 'flex-end',
|
|
1261
|
+
textAlign: isMobile ? 'left' : 'right',
|
|
1262
|
+
gap: '4px',
|
|
1263
|
+
flex: isMobile ? '1 1 100%' : '0 0 auto'
|
|
1264
|
+
}}
|
|
1265
|
+
>
|
|
1266
|
+
{item.status?.label && (
|
|
1267
|
+
<span
|
|
1268
|
+
data-order-detail-item-status=""
|
|
1269
|
+
data-order-status-value={item.status.value}
|
|
1270
|
+
{...partAttrs('item-status')}
|
|
1271
|
+
style={{ display: 'inline-block' }}
|
|
1272
|
+
>
|
|
1273
|
+
{item.status.label}
|
|
1274
|
+
</span>
|
|
1275
|
+
)}
|
|
1276
|
+
{/* Label and value in their own spans, so a
|
|
1277
|
+
composition can colour them apart. */}
|
|
1278
|
+
{returnMethodValue && (
|
|
1279
|
+
<span
|
|
1280
|
+
data-order-detail-item-return-method=""
|
|
1281
|
+
{...partAttrs('item-return-method')}
|
|
1282
|
+
style={{ opacity: 0.8 }}
|
|
1283
|
+
>
|
|
1284
|
+
<span data-label="">{returnMethodLabel}</span>{' '}
|
|
1285
|
+
<span data-value="">{returnMethodValue}</span>
|
|
1286
|
+
</span>
|
|
1287
|
+
)}
|
|
1288
|
+
{returnCarrierValue && (
|
|
1289
|
+
<span
|
|
1290
|
+
data-order-detail-item-return-carrier=""
|
|
1291
|
+
{...partAttrs('item-return-carrier')}
|
|
1292
|
+
style={{ opacity: 0.8 }}
|
|
1293
|
+
>
|
|
1294
|
+
<span data-label="">{returnCarrierLabel}</span>{' '}
|
|
1295
|
+
<span data-value="">{returnCarrierValue}</span>
|
|
1296
|
+
</span>
|
|
1297
|
+
)}
|
|
1298
|
+
{pickupDateValue && (
|
|
1299
|
+
<span
|
|
1300
|
+
data-order-detail-item-pickup-date=""
|
|
1301
|
+
{...partAttrs('item-pickup-date')}
|
|
1302
|
+
style={{ opacity: 0.8 }}
|
|
1303
|
+
>
|
|
1304
|
+
<span data-label="">{pickupDateLabel}</span>{' '}
|
|
1305
|
+
<span data-value="">{pickupDateValue}</span>
|
|
1306
|
+
</span>
|
|
1307
|
+
)}
|
|
1308
|
+
{trackingValue && (
|
|
1309
|
+
<span
|
|
1310
|
+
data-order-detail-item-tracking=""
|
|
1311
|
+
{...partAttrs('item-tracking')}
|
|
1312
|
+
style={{ opacity: 0.8 }}
|
|
1313
|
+
>
|
|
1314
|
+
{lineTrackingLabel}{' '}
|
|
1315
|
+
{item.tracking_url ? (
|
|
1316
|
+
<a
|
|
1317
|
+
href={isDesigner ? '#' : item.tracking_url}
|
|
1318
|
+
onClick={guard}
|
|
1319
|
+
target={isDesigner ? undefined : '_blank'}
|
|
1320
|
+
rel="noreferrer"
|
|
1321
|
+
style={{ color: 'inherit' }}
|
|
1322
|
+
>
|
|
1323
|
+
{item.tracking_number || item.tracking_url}
|
|
1324
|
+
</a>
|
|
1325
|
+
) : (
|
|
1326
|
+
trackingValue
|
|
1327
|
+
)}
|
|
1328
|
+
</span>
|
|
1329
|
+
)}
|
|
1330
|
+
{returnCodeValue && (
|
|
1331
|
+
<span
|
|
1332
|
+
data-order-detail-item-return-code=""
|
|
1333
|
+
{...partAttrs('item-return-code')}
|
|
1334
|
+
style={{ opacity: 0.8 }}
|
|
1335
|
+
>
|
|
1336
|
+
{returnCodeLabel} {returnCodeValue}
|
|
1337
|
+
</span>
|
|
1338
|
+
)}
|
|
1339
|
+
</div>
|
|
1340
|
+
)}
|
|
1359
1341
|
</div>
|
|
1360
1342
|
</li>
|
|
1361
1343
|
);
|