@akinon/next 2.0.0-beta.2 → 2.0.0-beta.20

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.
Files changed (189) hide show
  1. package/.eslintrc.js +12 -0
  2. package/CHANGELOG.md +377 -7
  3. package/__tests__/next-config.test.ts +83 -0
  4. package/__tests__/tsconfig.json +23 -0
  5. package/api/auth.ts +133 -44
  6. package/api/barcode-search.ts +59 -0
  7. package/api/cache.ts +41 -5
  8. package/api/client.ts +21 -4
  9. package/api/form.ts +85 -0
  10. package/api/image-proxy.ts +75 -0
  11. package/api/product-categories.ts +53 -0
  12. package/api/similar-product-list.ts +63 -0
  13. package/api/similar-products.ts +111 -0
  14. package/api/virtual-try-on.ts +382 -0
  15. package/assets/styles/index.scss +84 -0
  16. package/babel.config.js +6 -0
  17. package/bin/pz-generate-routes.js +115 -0
  18. package/bin/pz-prebuild.js +1 -0
  19. package/bin/pz-predev.js +1 -0
  20. package/bin/pz-run-tests.js +99 -0
  21. package/bin/run-prebuild-tests.js +46 -0
  22. package/components/accordion.tsx +20 -5
  23. package/components/button.tsx +51 -36
  24. package/components/client-root.tsx +138 -2
  25. package/components/file-input.tsx +65 -3
  26. package/components/index.ts +1 -0
  27. package/components/input.tsx +1 -1
  28. package/components/link.tsx +46 -16
  29. package/components/logger-popup.tsx +213 -0
  30. package/components/modal.tsx +32 -16
  31. package/components/plugin-module.tsx +62 -3
  32. package/components/price.tsx +2 -2
  33. package/components/select.tsx +1 -1
  34. package/components/selected-payment-option-view.tsx +21 -0
  35. package/components/theme-editor/blocks/accordion-block.tsx +136 -0
  36. package/components/theme-editor/blocks/block-renderer-registry.tsx +77 -0
  37. package/components/theme-editor/blocks/button-block.tsx +593 -0
  38. package/components/theme-editor/blocks/counter-block.tsx +348 -0
  39. package/components/theme-editor/blocks/divider-block.tsx +20 -0
  40. package/components/theme-editor/blocks/embed-block.tsx +208 -0
  41. package/components/theme-editor/blocks/group-block.tsx +116 -0
  42. package/components/theme-editor/blocks/hotspot-block.tsx +147 -0
  43. package/components/theme-editor/blocks/icon-block.tsx +230 -0
  44. package/components/theme-editor/blocks/image-block.tsx +137 -0
  45. package/components/theme-editor/blocks/image-gallery-block.tsx +269 -0
  46. package/components/theme-editor/blocks/input-block.tsx +123 -0
  47. package/components/theme-editor/blocks/link-block.tsx +216 -0
  48. package/components/theme-editor/blocks/lottie-block.tsx +325 -0
  49. package/components/theme-editor/blocks/map-block.tsx +89 -0
  50. package/components/theme-editor/blocks/slider-block.tsx +595 -0
  51. package/components/theme-editor/blocks/tab-block.tsx +10 -0
  52. package/components/theme-editor/blocks/text-block.tsx +52 -0
  53. package/components/theme-editor/blocks/video-block.tsx +122 -0
  54. package/components/theme-editor/components/action-toolbar.tsx +305 -0
  55. package/components/theme-editor/components/designer-overlay.tsx +74 -0
  56. package/components/theme-editor/components/with-designer-features.tsx +142 -0
  57. package/components/theme-editor/dynamic-font-loader.tsx +79 -0
  58. package/components/theme-editor/hooks/use-designer-features.tsx +100 -0
  59. package/components/theme-editor/hooks/use-external-designer.tsx +95 -0
  60. package/components/theme-editor/hooks/use-native-widget-data.ts +188 -0
  61. package/components/theme-editor/hooks/use-visibility-context.ts +27 -0
  62. package/components/theme-editor/placeholder-registry.ts +31 -0
  63. package/components/theme-editor/sections/before-after-section.tsx +245 -0
  64. package/components/theme-editor/sections/contact-form-section.tsx +563 -0
  65. package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +433 -0
  66. package/components/theme-editor/sections/coupon-banner-section.tsx +710 -0
  67. package/components/theme-editor/sections/divider-section.tsx +62 -0
  68. package/components/theme-editor/sections/featured-product-spotlight-section.tsx +507 -0
  69. package/components/theme-editor/sections/find-in-store-section.tsx +1995 -0
  70. package/components/theme-editor/sections/hover-showcase-section.tsx +326 -0
  71. package/components/theme-editor/sections/image-hotspot-section.tsx +142 -0
  72. package/components/theme-editor/sections/installment-options-section.tsx +1065 -0
  73. package/components/theme-editor/sections/notification-banner-section.tsx +173 -0
  74. package/components/theme-editor/sections/order-tracking-lookup-section.tsx +1379 -0
  75. package/components/theme-editor/sections/posts-slider-section.tsx +472 -0
  76. package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +663 -0
  77. package/components/theme-editor/sections/section-renderer-registry.tsx +89 -0
  78. package/components/theme-editor/sections/section-wrapper.tsx +135 -0
  79. package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +586 -0
  80. package/components/theme-editor/sections/stats-counter-section.tsx +486 -0
  81. package/components/theme-editor/sections/tabs-section.tsx +578 -0
  82. package/components/theme-editor/theme-block.tsx +102 -0
  83. package/components/theme-editor/theme-placeholder-client.tsx +218 -0
  84. package/components/theme-editor/theme-placeholder-wrapper.tsx +732 -0
  85. package/components/theme-editor/theme-placeholder.tsx +288 -0
  86. package/components/theme-editor/theme-section.tsx +1224 -0
  87. package/components/theme-editor/theme-settings-context.tsx +13 -0
  88. package/components/theme-editor/utils/index.ts +792 -0
  89. package/components/theme-editor/utils/iterator-utils.ts +234 -0
  90. package/components/theme-editor/utils/publish-window.ts +86 -0
  91. package/components/theme-editor/utils/visibility-rules.ts +188 -0
  92. package/data/client/account.ts +17 -2
  93. package/data/client/api.ts +2 -0
  94. package/data/client/basket.ts +66 -5
  95. package/data/client/checkout.ts +391 -99
  96. package/data/client/misc.ts +38 -2
  97. package/data/client/product.ts +19 -2
  98. package/data/client/user.ts +16 -8
  99. package/data/server/category.ts +11 -9
  100. package/data/server/flatpage.ts +11 -4
  101. package/data/server/form.ts +15 -4
  102. package/data/server/landingpage.ts +11 -4
  103. package/data/server/list.ts +5 -4
  104. package/data/server/menu.ts +11 -3
  105. package/data/server/product.ts +111 -55
  106. package/data/server/seo.ts +14 -4
  107. package/data/server/special-page.ts +5 -4
  108. package/data/server/widget.ts +90 -5
  109. package/data/urls.ts +16 -5
  110. package/hocs/client/with-segment-defaults.tsx +2 -2
  111. package/hocs/server/with-segment-defaults.tsx +65 -20
  112. package/hooks/index.ts +4 -0
  113. package/hooks/use-localization.ts +24 -10
  114. package/hooks/use-logger-context.tsx +114 -0
  115. package/hooks/use-logger.ts +92 -0
  116. package/hooks/use-loyalty-availability.ts +21 -0
  117. package/hooks/use-payment-options.ts +2 -1
  118. package/hooks/use-pz-params.ts +37 -0
  119. package/hooks/use-router.ts +51 -14
  120. package/hooks/use-sentry-uncaught-errors.ts +24 -0
  121. package/instrumentation/index.ts +10 -1
  122. package/instrumentation/node.ts +2 -20
  123. package/jest.config.js +25 -0
  124. package/lib/cache-handler.mjs +534 -16
  125. package/lib/cache.ts +272 -37
  126. package/localization/index.ts +2 -1
  127. package/localization/provider.tsx +2 -5
  128. package/middlewares/bfcache-headers.ts +18 -0
  129. package/middlewares/checkout-provider.ts +1 -1
  130. package/middlewares/complete-gpay.ts +32 -26
  131. package/middlewares/complete-masterpass.ts +33 -26
  132. package/middlewares/complete-wallet.ts +182 -0
  133. package/middlewares/default.ts +360 -215
  134. package/middlewares/index.ts +10 -2
  135. package/middlewares/locale.ts +34 -11
  136. package/middlewares/masterpass-rest-callback.ts +230 -0
  137. package/middlewares/oauth-login.ts +200 -57
  138. package/middlewares/pretty-url.ts +21 -8
  139. package/middlewares/redirection-payment.ts +32 -26
  140. package/middlewares/saved-card-redirection.ts +33 -26
  141. package/middlewares/three-d-redirection.ts +32 -26
  142. package/middlewares/url-redirection.ts +11 -1
  143. package/middlewares/wallet-complete-redirection.ts +206 -0
  144. package/package.json +25 -10
  145. package/plugins.d.ts +19 -4
  146. package/plugins.js +10 -1
  147. package/redux/actions.ts +47 -0
  148. package/redux/middlewares/checkout.ts +63 -138
  149. package/redux/middlewares/index.ts +14 -10
  150. package/redux/middlewares/pre-order/address.ts +7 -2
  151. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
  152. package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
  153. package/redux/middlewares/pre-order/delivery-option.ts +7 -1
  154. package/redux/middlewares/pre-order/index.ts +16 -10
  155. package/redux/middlewares/pre-order/installment-option.ts +8 -1
  156. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  157. package/redux/middlewares/pre-order/payment-option.ts +7 -1
  158. package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
  159. package/redux/middlewares/pre-order/redirection.ts +8 -2
  160. package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
  161. package/redux/middlewares/pre-order/shipping-option.ts +7 -1
  162. package/redux/middlewares/pre-order/shipping-step.ts +5 -1
  163. package/redux/reducers/checkout.ts +23 -3
  164. package/redux/reducers/index.ts +11 -3
  165. package/redux/reducers/root.ts +7 -2
  166. package/redux/reducers/widget.ts +80 -0
  167. package/sentry/index.ts +69 -13
  168. package/tailwind/content.js +16 -0
  169. package/types/commerce/account.ts +5 -1
  170. package/types/commerce/checkout.ts +35 -1
  171. package/types/commerce/widget.ts +33 -0
  172. package/types/index.ts +101 -6
  173. package/types/next-auth.d.ts +2 -2
  174. package/types/widget.ts +80 -0
  175. package/utils/app-fetch.ts +7 -2
  176. package/utils/generate-commerce-search-params.ts +3 -2
  177. package/utils/get-checkout-path.ts +3 -0
  178. package/utils/get-root-hostname.ts +28 -0
  179. package/utils/index.ts +64 -10
  180. package/utils/localization.ts +4 -0
  181. package/utils/mobile-3d-iframe.ts +8 -2
  182. package/utils/override-middleware.ts +7 -12
  183. package/utils/pz-segments.ts +92 -0
  184. package/utils/redirect-ignore.ts +35 -0
  185. package/utils/redirect.ts +9 -3
  186. package/utils/redirection-iframe.ts +8 -2
  187. package/utils/widget-styles.ts +107 -0
  188. package/views/error-page.tsx +93 -0
  189. package/with-pz-config.js +13 -6
@@ -0,0 +1,288 @@
1
+ import 'server-only';
2
+
3
+ import { getCollectionWidgetData, getWidgetData, getWidgetSchemaData } from '@akinon/next/data/server';
4
+ import ThemePlaceholderWrapper from './theme-placeholder-wrapper';
5
+ import { Section } from './theme-section';
6
+ import { generateThemeCSS } from './utils';
7
+
8
+ type ThemePlaceholderData = {
9
+ theme_editor_placeholder: Array<{
10
+ section_slug: string;
11
+ order: number;
12
+ }>;
13
+ };
14
+
15
+ interface ThemePlaceholderProps {
16
+ slug: string;
17
+ }
18
+
19
+ export default async function ThemePlaceholder({
20
+ slug
21
+ }: ThemePlaceholderProps) {
22
+ try {
23
+ let dataSources: any[] = [];
24
+ let themeSettings: Record<string, unknown> | null = null;
25
+ try {
26
+ const themeConfigData = await getWidgetData<any>({
27
+ slug: 'theme-config'
28
+ });
29
+
30
+ if (themeConfigData?.attributes?.theme_dataSources) {
31
+ try {
32
+ const raw = themeConfigData.attributes.theme_dataSources;
33
+ // Handle both plain string/array and { value: "..." } attribute format
34
+ const resolved =
35
+ typeof raw === 'string'
36
+ ? raw
37
+ : typeof raw === 'object' && raw !== null && !Array.isArray(raw)
38
+ ? (raw as { value?: any }).value ?? raw
39
+ : raw;
40
+
41
+ if (typeof resolved === 'string') {
42
+ dataSources = JSON.parse(resolved);
43
+ } else if (Array.isArray(resolved)) {
44
+ dataSources = resolved;
45
+ }
46
+ } catch (e) {
47
+ console.error('Failed to parse theme_dataSources:', e);
48
+ }
49
+ }
50
+
51
+ if (themeConfigData?.attributes?.theme_settings) {
52
+ try {
53
+ const rawSettings = themeConfigData.attributes.theme_settings;
54
+ const resolvedValue =
55
+ typeof rawSettings === 'string'
56
+ ? rawSettings
57
+ : typeof rawSettings === 'object' && rawSettings !== null
58
+ ? (rawSettings as { value?: string }).value ?? rawSettings
59
+ : null;
60
+
61
+ if (resolvedValue) {
62
+ if (typeof resolvedValue === 'string') {
63
+ themeSettings = JSON.parse(resolvedValue);
64
+ } else if (typeof resolvedValue === 'object') {
65
+ themeSettings = resolvedValue as Record<string, unknown>;
66
+ }
67
+ }
68
+ } catch (e) {
69
+ console.error('Failed to parse theme_settings:', e);
70
+ }
71
+ }
72
+ } catch (configError) {}
73
+
74
+ const data = await getWidgetData<ThemePlaceholderData>({
75
+ slug
76
+ });
77
+
78
+ const sections: Section[] = [];
79
+
80
+ if (data?.attributes?.theme_editor_placeholder) {
81
+ let placeholderData = data.attributes.theme_editor_placeholder;
82
+
83
+ if (
84
+ typeof placeholderData === 'object' &&
85
+ placeholderData !== null &&
86
+ 'value' in placeholderData
87
+ ) {
88
+ placeholderData = (
89
+ placeholderData as {
90
+ value: { section_slug: string; order: number }[];
91
+ }
92
+ ).value;
93
+ }
94
+
95
+ if (typeof placeholderData === 'string') {
96
+ try {
97
+ placeholderData = JSON.parse(placeholderData);
98
+ } catch {
99
+ placeholderData = [];
100
+ }
101
+ }
102
+
103
+ if (!Array.isArray(placeholderData)) {
104
+ placeholderData = [];
105
+ }
106
+
107
+ const sectionPromises = placeholderData.map(async (item: any) => {
108
+ try {
109
+ const sectionSlug = item.value?.section_slug || item.section_slug;
110
+ const sectionOrder = item.value?.order ?? item.order ?? 0;
111
+
112
+ if (!sectionSlug) {
113
+ return null;
114
+ }
115
+
116
+ const [regularSectionData, sectionSchema] = await Promise.all([
117
+ getWidgetData<any>({
118
+ slug: sectionSlug
119
+ }),
120
+ getWidgetSchemaData<any>({
121
+ widgetSlug: sectionSlug
122
+ })
123
+ ]);
124
+
125
+ let sectionData = regularSectionData;
126
+ if (!sectionData) {
127
+ try {
128
+ sectionData = await getCollectionWidgetData<any>({
129
+ slug: sectionSlug
130
+ });
131
+ } catch {
132
+ // Not a collection widget either; sectionData stays null
133
+ }
134
+ }
135
+
136
+ const sectionSchemaEntry = sectionSchema?.schema?.[sectionSlug];
137
+ const sectionMetadata = sectionSchemaEntry?.metadata || {};
138
+
139
+ const getBlockValue = (blockId: string): unknown => {
140
+ const attrData = sectionData?.attributes?.[blockId];
141
+ if (attrData === undefined || attrData === null) return undefined;
142
+
143
+ if (typeof attrData === 'string') {
144
+ try {
145
+ return JSON.parse(attrData);
146
+ } catch {
147
+ return attrData;
148
+ }
149
+ } else if (typeof attrData === 'object' && 'value' in attrData) {
150
+ return attrData.value;
151
+ }
152
+ return attrData;
153
+ };
154
+
155
+ const reconstructBlock = (blockSchema: any): any => {
156
+ return {
157
+ id: blockSchema.id,
158
+ type: blockSchema.type || 'text',
159
+ label: blockSchema.label || blockSchema.id,
160
+ order: blockSchema.order || 0,
161
+ isIterator: blockSchema.isIterator,
162
+ iteratorDataPath: blockSchema.iteratorDataPath,
163
+ value: getBlockValue(blockSchema.id),
164
+ styles: blockSchema.styles || {},
165
+ properties: blockSchema.properties || {},
166
+ hidden: blockSchema.hidden || false,
167
+ blocks: (blockSchema.blocks || []).map((b: any) =>
168
+ reconstructBlock(b)
169
+ )
170
+ };
171
+ };
172
+
173
+ const blocks: any[] = [];
174
+ if (sectionMetadata.blocks && Array.isArray(sectionMetadata.blocks)) {
175
+ sectionMetadata.blocks.forEach((blockSchema: any) => {
176
+ blocks.push(reconstructBlock(blockSchema));
177
+ });
178
+ }
179
+
180
+ blocks.sort((a, b) => a.order - b.order);
181
+
182
+ // dataSourceId may be stored in schema metadata OR in widget data attributes
183
+ // (theme editor saves it as a widget attribute when user selects a data source)
184
+ const dataSourceId =
185
+ sectionMetadata.dataSourceId ||
186
+ (getBlockValue('dataSourceId') as string | undefined) ||
187
+ (getBlockValue('selectedDataSourceId') as string | undefined);
188
+
189
+ const dataSource = dataSourceId
190
+ ? dataSources.find((ds) => ds.id === dataSourceId)
191
+ : undefined;
192
+
193
+ let dataSourceWithData = dataSource;
194
+ if (dataSource && dataSource.details) {
195
+ // Only fetch collection data if we have a valid widget slug
196
+ // (not the section's own slug which contains config, not products)
197
+ const collectionSlug = dataSource.details.collection?.slug;
198
+ if (collectionSlug) {
199
+ try {
200
+ let collectionData = await getWidgetData<{
201
+ [key: string]: unknown;
202
+ }>({
203
+ slug: collectionSlug
204
+ });
205
+
206
+ if (!collectionData) {
207
+ collectionData = await getCollectionWidgetData<{
208
+ [key: string]: unknown;
209
+ }>({
210
+ slug: collectionSlug
211
+ });
212
+ }
213
+
214
+ if (collectionData) {
215
+ dataSourceWithData = {
216
+ ...dataSource,
217
+ details: {
218
+ ...dataSource.details,
219
+ collection: {
220
+ ...dataSource.details.collection,
221
+ data: collectionData
222
+ }
223
+ }
224
+ };
225
+ }
226
+ } catch (error) {
227
+ console.error(
228
+ `Error fetching widget data for section ${sectionSlug}:`,
229
+ error
230
+ );
231
+ }
232
+ }
233
+ // If no collection slug, the saved products from editor
234
+ // (details.collection.products) will be used as fallback
235
+ }
236
+
237
+ const section: Section = {
238
+ id: sectionSlug,
239
+ type: sectionMetadata.type || 'default',
240
+ name: sectionMetadata.name || sectionSlug,
241
+ label: sectionMetadata.label || sectionSlug,
242
+ properties: sectionMetadata.properties || {},
243
+ styles: sectionMetadata.styles || {},
244
+ blocks,
245
+ order: sectionOrder,
246
+ hidden: sectionMetadata.hidden || false,
247
+ dataSourceId,
248
+ dataSource: dataSourceWithData
249
+ };
250
+
251
+ return section;
252
+ } catch (error) {
253
+ const errorSlug =
254
+ item.value?.section_slug || item.section_slug || 'unknown';
255
+ console.error(
256
+ `Error parsing section metadata for ${errorSlug}:`,
257
+ error
258
+ );
259
+ return null;
260
+ }
261
+ });
262
+
263
+ const resolvedSections = await Promise.all(sectionPromises);
264
+ sections.push(
265
+ ...resolvedSections.filter((s): s is Section => s !== null)
266
+ );
267
+ }
268
+
269
+ const themeCSS = generateThemeCSS(sections);
270
+
271
+ return (
272
+ <>
273
+ {themeCSS && <style dangerouslySetInnerHTML={{ __html: themeCSS }} />}
274
+ <ThemePlaceholderWrapper
275
+ slug={slug}
276
+ initialSections={sections}
277
+ initialPlaceholderId={data?.slug?.toString() || ''}
278
+ isDesignMode={false}
279
+ dataSources={dataSources}
280
+ initialThemeSettings={themeSettings}
281
+ />
282
+ </>
283
+ );
284
+ } catch (error) {
285
+ console.error(`Error fetching theme placeholder data for ${slug}:`, error);
286
+ return null;
287
+ }
288
+ }