@akinon/next 2.0.0-beta.20 → 2.0.0-beta.21
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 +23 -0
- package/api/auth.ts +292 -60
- package/bin/pz-install-plugins.js +1 -1
- package/package.json +3 -3
- package/types/index.ts +19 -6
- package/types/next-auth.d.ts +1 -1
- package/with-pz-config.js +8 -1
- package/components/theme-editor/blocks/accordion-block.tsx +0 -136
- package/components/theme-editor/blocks/block-renderer-registry.tsx +0 -77
- package/components/theme-editor/blocks/button-block.tsx +0 -593
- package/components/theme-editor/blocks/counter-block.tsx +0 -348
- package/components/theme-editor/blocks/divider-block.tsx +0 -20
- package/components/theme-editor/blocks/embed-block.tsx +0 -208
- package/components/theme-editor/blocks/group-block.tsx +0 -116
- package/components/theme-editor/blocks/hotspot-block.tsx +0 -147
- package/components/theme-editor/blocks/icon-block.tsx +0 -230
- package/components/theme-editor/blocks/image-block.tsx +0 -137
- package/components/theme-editor/blocks/image-gallery-block.tsx +0 -269
- package/components/theme-editor/blocks/input-block.tsx +0 -123
- package/components/theme-editor/blocks/link-block.tsx +0 -216
- package/components/theme-editor/blocks/lottie-block.tsx +0 -325
- package/components/theme-editor/blocks/map-block.tsx +0 -89
- package/components/theme-editor/blocks/slider-block.tsx +0 -595
- package/components/theme-editor/blocks/tab-block.tsx +0 -10
- package/components/theme-editor/blocks/text-block.tsx +0 -52
- package/components/theme-editor/blocks/video-block.tsx +0 -122
- package/components/theme-editor/components/action-toolbar.tsx +0 -305
- package/components/theme-editor/components/designer-overlay.tsx +0 -74
- package/components/theme-editor/components/with-designer-features.tsx +0 -142
- package/components/theme-editor/dynamic-font-loader.tsx +0 -79
- package/components/theme-editor/hooks/use-designer-features.tsx +0 -100
- package/components/theme-editor/hooks/use-external-designer.tsx +0 -95
- package/components/theme-editor/hooks/use-native-widget-data.ts +0 -188
- package/components/theme-editor/hooks/use-visibility-context.ts +0 -27
- package/components/theme-editor/placeholder-registry.ts +0 -31
- package/components/theme-editor/sections/before-after-section.tsx +0 -245
- package/components/theme-editor/sections/contact-form-section.tsx +0 -563
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +0 -433
- package/components/theme-editor/sections/coupon-banner-section.tsx +0 -710
- package/components/theme-editor/sections/divider-section.tsx +0 -62
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +0 -507
- package/components/theme-editor/sections/find-in-store-section.tsx +0 -1995
- package/components/theme-editor/sections/hover-showcase-section.tsx +0 -326
- package/components/theme-editor/sections/image-hotspot-section.tsx +0 -142
- package/components/theme-editor/sections/installment-options-section.tsx +0 -1065
- package/components/theme-editor/sections/notification-banner-section.tsx +0 -173
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +0 -1379
- package/components/theme-editor/sections/posts-slider-section.tsx +0 -472
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +0 -663
- package/components/theme-editor/sections/section-renderer-registry.tsx +0 -89
- package/components/theme-editor/sections/section-wrapper.tsx +0 -135
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +0 -586
- package/components/theme-editor/sections/stats-counter-section.tsx +0 -486
- package/components/theme-editor/sections/tabs-section.tsx +0 -578
- package/components/theme-editor/theme-block.tsx +0 -102
- package/components/theme-editor/theme-placeholder-client.tsx +0 -218
- package/components/theme-editor/theme-placeholder-wrapper.tsx +0 -732
- package/components/theme-editor/theme-placeholder.tsx +0 -288
- package/components/theme-editor/theme-section.tsx +0 -1224
- package/components/theme-editor/theme-settings-context.tsx +0 -13
- package/components/theme-editor/utils/index.ts +0 -792
- package/components/theme-editor/utils/iterator-utils.ts +0 -234
- package/components/theme-editor/utils/publish-window.ts +0 -86
- package/components/theme-editor/utils/visibility-rules.ts +0 -188
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { getResponsiveValue, resolveThemeCssVariables } from '../utils';
|
|
5
|
-
import { Section } from '../theme-section';
|
|
6
|
-
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
7
|
-
|
|
8
|
-
interface DividerSectionProps {
|
|
9
|
-
section: Section;
|
|
10
|
-
currentBreakpoint?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default function DividerSection({
|
|
14
|
-
section,
|
|
15
|
-
currentBreakpoint = 'desktop'
|
|
16
|
-
}: DividerSectionProps) {
|
|
17
|
-
const themeSettings = useThemeSettingsContext();
|
|
18
|
-
|
|
19
|
-
const containerStyles = {
|
|
20
|
-
paddingTop: getResponsiveValue(
|
|
21
|
-
section.styles?.['padding-top'],
|
|
22
|
-
currentBreakpoint
|
|
23
|
-
),
|
|
24
|
-
paddingRight: getResponsiveValue(
|
|
25
|
-
section.styles?.['padding-right'],
|
|
26
|
-
currentBreakpoint
|
|
27
|
-
),
|
|
28
|
-
paddingBottom: getResponsiveValue(
|
|
29
|
-
section.styles?.['padding-bottom'],
|
|
30
|
-
currentBreakpoint
|
|
31
|
-
),
|
|
32
|
-
paddingLeft: getResponsiveValue(
|
|
33
|
-
section.styles?.['padding-left'],
|
|
34
|
-
currentBreakpoint
|
|
35
|
-
),
|
|
36
|
-
position: 'relative'
|
|
37
|
-
} as React.CSSProperties;
|
|
38
|
-
|
|
39
|
-
const spanStyles = {
|
|
40
|
-
display: 'block',
|
|
41
|
-
height: getResponsiveValue(
|
|
42
|
-
section.styles?.height,
|
|
43
|
-
currentBreakpoint,
|
|
44
|
-
'1px'
|
|
45
|
-
),
|
|
46
|
-
backgroundColor: resolveThemeCssVariables(
|
|
47
|
-
getResponsiveValue(
|
|
48
|
-
section.styles?.['background-color'],
|
|
49
|
-
currentBreakpoint,
|
|
50
|
-
'#e0e0e0'
|
|
51
|
-
) as string,
|
|
52
|
-
themeSettings
|
|
53
|
-
),
|
|
54
|
-
width: getResponsiveValue(section.styles?.width, currentBreakpoint, '100%')
|
|
55
|
-
} as React.CSSProperties;
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div style={containerStyles}>
|
|
59
|
-
<span style={spanStyles} />
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useMemo } from 'react';
|
|
4
|
-
import { useGetProductByPkQuery } from '@akinon/next/data/client/product';
|
|
5
|
-
|
|
6
|
-
import ThemeBlock, { Block } from '../theme-block';
|
|
7
|
-
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
8
|
-
import { Section } from '../theme-section';
|
|
9
|
-
import { getCSSStyles, getResponsiveValue } from '../utils';
|
|
10
|
-
|
|
11
|
-
interface FeaturedProductSpotlightSectionProps {
|
|
12
|
-
section: Section;
|
|
13
|
-
currentBreakpoint?: string;
|
|
14
|
-
placeholderId?: string;
|
|
15
|
-
isDesigner?: boolean;
|
|
16
|
-
selectedBlockId?: string | null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const parseBoolean = (value: unknown, fallback: boolean): boolean => {
|
|
20
|
-
if (typeof value === 'boolean') return value;
|
|
21
|
-
if (typeof value === 'string') {
|
|
22
|
-
const normalized = value.trim().toLowerCase();
|
|
23
|
-
if (normalized === 'true') return true;
|
|
24
|
-
if (normalized === 'false') return false;
|
|
25
|
-
}
|
|
26
|
-
return fallback;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const resolvePath = (
|
|
30
|
-
source: Record<string, unknown> | undefined,
|
|
31
|
-
path: string
|
|
32
|
-
): unknown => {
|
|
33
|
-
if (!source || !path) return undefined;
|
|
34
|
-
|
|
35
|
-
const normalizedPath = path.replace(/^item\./, '');
|
|
36
|
-
const pathParts = normalizedPath.split('.');
|
|
37
|
-
let value: unknown = source;
|
|
38
|
-
|
|
39
|
-
for (const part of pathParts) {
|
|
40
|
-
const arrayMatch = part.match(/^(.+)\[(\d+)\]$/);
|
|
41
|
-
if (arrayMatch) {
|
|
42
|
-
const [, arrayKey, indexText] = arrayMatch;
|
|
43
|
-
const arrayValue = (value as Record<string, unknown>)?.[arrayKey];
|
|
44
|
-
value = Array.isArray(arrayValue)
|
|
45
|
-
? arrayValue[Number(indexText)]
|
|
46
|
-
: undefined;
|
|
47
|
-
} else {
|
|
48
|
-
value = (value as Record<string, unknown>)?.[part];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (value === undefined || value === null) {
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return value;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const parsePriceValue = (value: unknown): number | null => {
|
|
60
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (typeof value !== 'string') return null;
|
|
65
|
-
|
|
66
|
-
const cleaned = value.trim().replace(/[^\d,.-]/g, '');
|
|
67
|
-
if (!cleaned) return null;
|
|
68
|
-
|
|
69
|
-
let normalized = cleaned;
|
|
70
|
-
const hasComma = normalized.includes(',');
|
|
71
|
-
const hasDot = normalized.includes('.');
|
|
72
|
-
|
|
73
|
-
if (hasComma && hasDot) {
|
|
74
|
-
const lastComma = normalized.lastIndexOf(',');
|
|
75
|
-
const lastDot = normalized.lastIndexOf('.');
|
|
76
|
-
normalized =
|
|
77
|
-
lastComma > lastDot
|
|
78
|
-
? normalized.replace(/\./g, '').replace(',', '.')
|
|
79
|
-
: normalized.replace(/,/g, '');
|
|
80
|
-
} else if (hasComma) {
|
|
81
|
-
const unsigned = normalized.replace(/^-/, '');
|
|
82
|
-
const isThousandsPattern = /^\d{1,3}(,\d{3})+$/.test(unsigned);
|
|
83
|
-
normalized = isThousandsPattern
|
|
84
|
-
? normalized.replace(/,/g, '')
|
|
85
|
-
: normalized.replace(/,/g, '.');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const parsed = Number(normalized);
|
|
89
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const parsePositiveInt = (value: unknown): number | null => {
|
|
93
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
94
|
-
return value;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (typeof value === 'string') {
|
|
98
|
-
const trimmed = value.trim();
|
|
99
|
-
if (!/^\d+$/.test(trimmed)) return null;
|
|
100
|
-
const parsed = Number.parseInt(trimmed, 10);
|
|
101
|
-
return parsed > 0 ? parsed : null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const normalizeCurrencyLabel = (currency: unknown): string => {
|
|
108
|
-
const raw = String(currency || '').trim().toUpperCase();
|
|
109
|
-
if (!raw) return 'TL';
|
|
110
|
-
|
|
111
|
-
const map: Record<string, string> = {
|
|
112
|
-
TRY: 'TL',
|
|
113
|
-
TL: 'TL',
|
|
114
|
-
USD: 'USD',
|
|
115
|
-
EUR: 'EUR',
|
|
116
|
-
GBP: 'GBP'
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
return map[raw] || raw;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const formatPriceWithCurrency = (
|
|
123
|
-
value: unknown,
|
|
124
|
-
currency: unknown
|
|
125
|
-
): string | null => {
|
|
126
|
-
if (value == null || value === '') return null;
|
|
127
|
-
|
|
128
|
-
const price = parsePriceValue(value);
|
|
129
|
-
if (price === null) {
|
|
130
|
-
const text = String(value).trim();
|
|
131
|
-
return text || null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const hasDecimals = Math.abs(price % 1) > 0.00001;
|
|
135
|
-
const formatted = price.toLocaleString('tr-TR', {
|
|
136
|
-
minimumFractionDigits: hasDecimals ? 2 : 0,
|
|
137
|
-
maximumFractionDigits: 2
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return `${formatted} ${normalizeCurrencyLabel(currency)}`;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const getCollectionProducts = (
|
|
144
|
-
section: Section,
|
|
145
|
-
isDesigner: boolean
|
|
146
|
-
): Record<string, unknown>[] => {
|
|
147
|
-
const collectionDetails = section.dataSource?.details?.collection;
|
|
148
|
-
const staticData = section.dataSource?.details?.static?.data;
|
|
149
|
-
const isEditorMode =
|
|
150
|
-
typeof window !== 'undefined' && isDesigner && window.parent !== window;
|
|
151
|
-
|
|
152
|
-
const collectionPayload = isEditorMode
|
|
153
|
-
? collectionDetails?.products || collectionDetails?.data
|
|
154
|
-
: collectionDetails?.data || collectionDetails?.products;
|
|
155
|
-
|
|
156
|
-
if (Array.isArray(collectionPayload)) {
|
|
157
|
-
return collectionPayload as Record<string, unknown>[];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (Array.isArray((collectionPayload as Record<string, unknown>)?.products)) {
|
|
161
|
-
return (collectionPayload as Record<string, unknown>).products as Record<
|
|
162
|
-
string,
|
|
163
|
-
unknown
|
|
164
|
-
>[];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (Array.isArray((collectionPayload as Record<string, unknown>)?.items)) {
|
|
168
|
-
return (collectionPayload as Record<string, unknown>).items as Record<
|
|
169
|
-
string,
|
|
170
|
-
unknown
|
|
171
|
-
>[];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (Array.isArray(staticData)) {
|
|
175
|
-
return staticData as Record<string, unknown>[];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return [];
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const getDiscountBadgeText = (
|
|
182
|
-
currentPrice: number | null,
|
|
183
|
-
retailPrice: number | null
|
|
184
|
-
): string | null => {
|
|
185
|
-
if (
|
|
186
|
-
currentPrice === null ||
|
|
187
|
-
retailPrice === null ||
|
|
188
|
-
retailPrice <= currentPrice ||
|
|
189
|
-
retailPrice <= 0
|
|
190
|
-
) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const percentage = Math.round(((retailPrice - currentPrice) / retailPrice) * 100);
|
|
195
|
-
if (percentage <= 0) return null;
|
|
196
|
-
return `Save ${percentage}%`;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export default function FeaturedProductSpotlightSection({
|
|
200
|
-
section,
|
|
201
|
-
currentBreakpoint = 'desktop',
|
|
202
|
-
placeholderId = '',
|
|
203
|
-
isDesigner = false,
|
|
204
|
-
selectedBlockId = null
|
|
205
|
-
}: FeaturedProductSpotlightSectionProps) {
|
|
206
|
-
const themeSettings = useThemeSettingsContext();
|
|
207
|
-
|
|
208
|
-
const maxWidth = getResponsiveValue(
|
|
209
|
-
section.styles?.['max-width'],
|
|
210
|
-
currentBreakpoint,
|
|
211
|
-
'normal'
|
|
212
|
-
);
|
|
213
|
-
const maxWidthClass =
|
|
214
|
-
maxWidth === 'narrow'
|
|
215
|
-
? 'max-w-4xl'
|
|
216
|
-
: maxWidth === 'normal'
|
|
217
|
-
? 'max-w-7xl'
|
|
218
|
-
: maxWidth === 'full'
|
|
219
|
-
? 'w-full'
|
|
220
|
-
: '';
|
|
221
|
-
const hasMaxWidth = maxWidth !== 'none' && maxWidth !== 'full';
|
|
222
|
-
|
|
223
|
-
const filteredStyles = Object.fromEntries(
|
|
224
|
-
Object.entries(section.styles || {}).filter(([key]) => key !== 'max-width')
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const sectionStyles = useMemo(() => {
|
|
228
|
-
const baseStyles = getCSSStyles(
|
|
229
|
-
filteredStyles,
|
|
230
|
-
themeSettings,
|
|
231
|
-
currentBreakpoint
|
|
232
|
-
);
|
|
233
|
-
const reverseLayout = parseBoolean(
|
|
234
|
-
getResponsiveValue(section.properties?.reverse, currentBreakpoint, false),
|
|
235
|
-
false
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
if (!reverseLayout) {
|
|
239
|
-
return baseStyles;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
...baseStyles,
|
|
244
|
-
flexDirection: currentBreakpoint === 'mobile' ? 'column-reverse' : 'row-reverse'
|
|
245
|
-
};
|
|
246
|
-
}, [
|
|
247
|
-
currentBreakpoint,
|
|
248
|
-
filteredStyles,
|
|
249
|
-
section.properties?.reverse,
|
|
250
|
-
themeSettings
|
|
251
|
-
]);
|
|
252
|
-
|
|
253
|
-
const products = useMemo(
|
|
254
|
-
() => getCollectionProducts(section, isDesigner),
|
|
255
|
-
[section, isDesigner]
|
|
256
|
-
);
|
|
257
|
-
const fallbackProductPk = parsePositiveInt(
|
|
258
|
-
getResponsiveValue(section.properties?.['product-pk'], currentBreakpoint, '')
|
|
259
|
-
);
|
|
260
|
-
const { data: fallbackProductResponse } = useGetProductByPkQuery(
|
|
261
|
-
fallbackProductPk as number,
|
|
262
|
-
{
|
|
263
|
-
skip: products.length > 0 || !fallbackProductPk
|
|
264
|
-
}
|
|
265
|
-
);
|
|
266
|
-
const product =
|
|
267
|
-
products[0] ||
|
|
268
|
-
((fallbackProductResponse?.product as Record<string, unknown> | undefined) ??
|
|
269
|
-
undefined);
|
|
270
|
-
|
|
271
|
-
const activePrice =
|
|
272
|
-
((product?.active_price as Record<string, unknown> | undefined) || {});
|
|
273
|
-
const currentPriceRaw = activePrice?.price ?? product?.price;
|
|
274
|
-
const retailPriceRaw = activePrice?.retail_price ?? product?.retail_price;
|
|
275
|
-
const currency =
|
|
276
|
-
activePrice?.currency_type ?? product?.currency_type ?? product?.currency;
|
|
277
|
-
const currentPrice = parsePriceValue(currentPriceRaw);
|
|
278
|
-
const retailPrice = parsePriceValue(retailPriceRaw);
|
|
279
|
-
|
|
280
|
-
const currentPriceText = formatPriceWithCurrency(currentPriceRaw, currency);
|
|
281
|
-
const retailPriceText = formatPriceWithCurrency(retailPriceRaw, currency);
|
|
282
|
-
const discountBadgeText = getDiscountBadgeText(currentPrice, retailPrice);
|
|
283
|
-
const hasRetailPrice =
|
|
284
|
-
Boolean(retailPriceText) &&
|
|
285
|
-
retailPrice !== null &&
|
|
286
|
-
currentPrice !== null &&
|
|
287
|
-
retailPrice > currentPrice;
|
|
288
|
-
|
|
289
|
-
const showBadge = parseBoolean(
|
|
290
|
-
getResponsiveValue(section.properties?.['show-badge'], currentBreakpoint, true),
|
|
291
|
-
true
|
|
292
|
-
);
|
|
293
|
-
const showOldPrice = parseBoolean(
|
|
294
|
-
getResponsiveValue(
|
|
295
|
-
section.properties?.['show-old-price'],
|
|
296
|
-
currentBreakpoint,
|
|
297
|
-
true
|
|
298
|
-
),
|
|
299
|
-
true
|
|
300
|
-
);
|
|
301
|
-
const showThumbnails = parseBoolean(
|
|
302
|
-
getResponsiveValue(
|
|
303
|
-
section.properties?.['show-thumbnails'],
|
|
304
|
-
currentBreakpoint,
|
|
305
|
-
true
|
|
306
|
-
),
|
|
307
|
-
true
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
const normalizedBlocks = useMemo(() => {
|
|
311
|
-
const cloneWithProduct = (block: Block): Block | null => {
|
|
312
|
-
const normalizedLabel = String(block.label || '').toLowerCase();
|
|
313
|
-
|
|
314
|
-
if (normalizedLabel === 'thumbnail row' && !showThumbnails) {
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
normalizedLabel === 'discount badge' &&
|
|
320
|
-
(!showBadge || !discountBadgeText)
|
|
321
|
-
) {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (
|
|
326
|
-
normalizedLabel === 'old price' &&
|
|
327
|
-
(!showOldPrice || !hasRetailPrice)
|
|
328
|
-
) {
|
|
329
|
-
return null;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const nextBlock: Block = {
|
|
333
|
-
...block,
|
|
334
|
-
properties: block.properties ? { ...block.properties } : block.properties,
|
|
335
|
-
styles: block.styles
|
|
336
|
-
? JSON.parse(JSON.stringify(block.styles))
|
|
337
|
-
: block.styles,
|
|
338
|
-
blocks: undefined
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
if (normalizedLabel === 'discount badge' && discountBadgeText) {
|
|
342
|
-
nextBlock.value = discountBadgeText;
|
|
343
|
-
} else if (normalizedLabel === 'current price' && currentPriceText) {
|
|
344
|
-
nextBlock.value = currentPriceText;
|
|
345
|
-
} else if (normalizedLabel === 'old price' && retailPriceText) {
|
|
346
|
-
nextBlock.value = retailPriceText;
|
|
347
|
-
} else if (normalizedLabel === 'primary cta' && product?.absolute_url) {
|
|
348
|
-
nextBlock.properties = {
|
|
349
|
-
...(nextBlock.properties || {}),
|
|
350
|
-
url: product.absolute_url
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const bindingPath = nextBlock.properties?.dataBinding;
|
|
355
|
-
if (bindingPath && product) {
|
|
356
|
-
const boundValue = resolvePath(product, String(bindingPath));
|
|
357
|
-
|
|
358
|
-
if (boundValue !== undefined) {
|
|
359
|
-
if (nextBlock.type === 'button') {
|
|
360
|
-
nextBlock.properties = {
|
|
361
|
-
...(nextBlock.properties || {}),
|
|
362
|
-
url: boundValue
|
|
363
|
-
};
|
|
364
|
-
} else if (
|
|
365
|
-
nextBlock.type === 'text' &&
|
|
366
|
-
normalizedLabel !== 'current price' &&
|
|
367
|
-
normalizedLabel !== 'old price'
|
|
368
|
-
) {
|
|
369
|
-
nextBlock.value = boundValue;
|
|
370
|
-
} else if (nextBlock.type === 'image') {
|
|
371
|
-
nextBlock.value = boundValue;
|
|
372
|
-
}
|
|
373
|
-
} else if (
|
|
374
|
-
nextBlock.type === 'image' &&
|
|
375
|
-
normalizedLabel.includes('thumbnail')
|
|
376
|
-
) {
|
|
377
|
-
const fallbackImage = resolvePath(product, 'productimage_set[0].image');
|
|
378
|
-
if (fallbackImage !== undefined) {
|
|
379
|
-
nextBlock.value = fallbackImage;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (block.blocks?.length) {
|
|
385
|
-
nextBlock.blocks = block.blocks
|
|
386
|
-
.map(cloneWithProduct)
|
|
387
|
-
.filter(Boolean) as Block[];
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return nextBlock;
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
return (section.blocks || [])
|
|
394
|
-
.sort((a, b) => (a.order || 0) - (b.order || 0))
|
|
395
|
-
.map(cloneWithProduct)
|
|
396
|
-
.filter(Boolean) as Block[];
|
|
397
|
-
}, [
|
|
398
|
-
currentPriceText,
|
|
399
|
-
discountBadgeText,
|
|
400
|
-
hasRetailPrice,
|
|
401
|
-
product,
|
|
402
|
-
retailPriceText,
|
|
403
|
-
section.blocks,
|
|
404
|
-
showBadge,
|
|
405
|
-
showOldPrice,
|
|
406
|
-
showThumbnails
|
|
407
|
-
]);
|
|
408
|
-
|
|
409
|
-
const renderBlock = (block: Block) => (
|
|
410
|
-
<ThemeBlock
|
|
411
|
-
key={block.id}
|
|
412
|
-
block={block}
|
|
413
|
-
placeholderId={placeholderId}
|
|
414
|
-
sectionId={section.id}
|
|
415
|
-
isDesigner={isDesigner}
|
|
416
|
-
isSelected={selectedBlockId === block.id}
|
|
417
|
-
selectedBlockId={selectedBlockId}
|
|
418
|
-
currentBreakpoint={currentBreakpoint}
|
|
419
|
-
onMoveUp={() => {
|
|
420
|
-
if (window.parent) {
|
|
421
|
-
window.parent.postMessage(
|
|
422
|
-
{
|
|
423
|
-
type: 'MOVE_BLOCK_UP',
|
|
424
|
-
data: { placeholderId, sectionId: section.id, blockId: block.id }
|
|
425
|
-
},
|
|
426
|
-
'*'
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}}
|
|
430
|
-
onMoveDown={() => {
|
|
431
|
-
if (window.parent) {
|
|
432
|
-
window.parent.postMessage(
|
|
433
|
-
{
|
|
434
|
-
type: 'MOVE_BLOCK_DOWN',
|
|
435
|
-
data: { placeholderId, sectionId: section.id, blockId: block.id }
|
|
436
|
-
},
|
|
437
|
-
'*'
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
}}
|
|
441
|
-
onDuplicate={() => {
|
|
442
|
-
if (window.parent) {
|
|
443
|
-
window.parent.postMessage(
|
|
444
|
-
{
|
|
445
|
-
type: 'DUPLICATE_BLOCK',
|
|
446
|
-
data: { placeholderId, sectionId: section.id, blockId: block.id }
|
|
447
|
-
},
|
|
448
|
-
'*'
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
}}
|
|
452
|
-
onToggleVisibility={() => {
|
|
453
|
-
if (window.parent) {
|
|
454
|
-
window.parent.postMessage(
|
|
455
|
-
{
|
|
456
|
-
type: 'TOGGLE_BLOCK_VISIBILITY',
|
|
457
|
-
data: { placeholderId, sectionId: section.id, blockId: block.id }
|
|
458
|
-
},
|
|
459
|
-
'*'
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
}}
|
|
463
|
-
onDelete={() => {
|
|
464
|
-
if (window.parent) {
|
|
465
|
-
window.parent.postMessage(
|
|
466
|
-
{
|
|
467
|
-
type: 'DELETE_BLOCK',
|
|
468
|
-
data: { placeholderId, sectionId: section.id, blockId: block.id }
|
|
469
|
-
},
|
|
470
|
-
'*'
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
}}
|
|
474
|
-
onRename={(newLabel) => {
|
|
475
|
-
if (window.parent) {
|
|
476
|
-
window.parent.postMessage(
|
|
477
|
-
{
|
|
478
|
-
type: 'RENAME_BLOCK',
|
|
479
|
-
data: {
|
|
480
|
-
placeholderId,
|
|
481
|
-
sectionId: section.id,
|
|
482
|
-
blockId: block.id,
|
|
483
|
-
label: newLabel
|
|
484
|
-
}
|
|
485
|
-
},
|
|
486
|
-
'*'
|
|
487
|
-
);
|
|
488
|
-
}
|
|
489
|
-
}}
|
|
490
|
-
/>
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
return (
|
|
494
|
-
<div
|
|
495
|
-
className={
|
|
496
|
-
hasMaxWidth
|
|
497
|
-
? `mx-auto ${maxWidthClass}`
|
|
498
|
-
: maxWidthClass || undefined
|
|
499
|
-
}
|
|
500
|
-
style={sectionStyles}
|
|
501
|
-
>
|
|
502
|
-
{normalizedBlocks
|
|
503
|
-
.filter((block) => (isDesigner ? true : !block.hidden))
|
|
504
|
-
.map(renderBlock)}
|
|
505
|
-
</div>
|
|
506
|
-
);
|
|
507
|
-
}
|