@akinon/pz-theme 2.0.35-rc.0 → 2.0.35

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 (39) hide show
  1. package/CHANGELOG.md +12 -28
  2. package/package.json +4 -3
  3. package/src/__tests__/theme-contract.test.ts +130 -0
  4. package/src/blocks/__tests__/text-block.test.tsx +8 -1
  5. package/src/blocks/__tests__/video-block.test.tsx +14 -7
  6. package/src/blocks/counter-block.tsx +44 -56
  7. package/src/blocks/group-block.tsx +23 -22
  8. package/src/blocks/hotspot-block.tsx +171 -80
  9. package/src/blocks/input-block.tsx +15 -17
  10. package/src/blocks/link-block.tsx +18 -21
  11. package/src/blocks/slider-block.tsx +15 -12
  12. package/src/components/with-designer-features.tsx +2 -0
  13. package/src/editor-bridge.ts +160 -0
  14. package/src/hooks/use-designer-features.tsx +30 -37
  15. package/src/placeholder-registry.ts +5 -7
  16. package/src/sections/__tests__/countdown-campaign-banner-section.test.tsx +8 -4
  17. package/src/sections/__tests__/coupon-banner-section.test.tsx +4 -2
  18. package/src/sections/before-after-section.tsx +189 -134
  19. package/src/sections/contact-form-section.tsx +185 -60
  20. package/src/sections/countdown-campaign-banner-section.tsx +73 -27
  21. package/src/sections/coupon-banner-section.tsx +152 -48
  22. package/src/sections/featured-product-spotlight-section.tsx +67 -66
  23. package/src/sections/find-in-store-section.tsx +37 -25
  24. package/src/sections/hover-showcase-section.tsx +221 -89
  25. package/src/sections/image-hotspot-section.tsx +208 -94
  26. package/src/sections/installment-options-section.tsx +268 -259
  27. package/src/sections/notification-banner-section.tsx +111 -25
  28. package/src/sections/order-tracking-lookup-section.tsx +272 -247
  29. package/src/sections/posts-slider-section.tsx +17 -16
  30. package/src/sections/pre-order-launch-banner-section.tsx +31 -50
  31. package/src/sections/section-wrapper.tsx +9 -10
  32. package/src/sections/shipping-threshold-progress-section.tsx +72 -29
  33. package/src/sections/stats-counter-section.tsx +68 -30
  34. package/src/sections/tabs-section.tsx +124 -147
  35. package/src/theme-placeholder-client.tsx +11 -14
  36. package/src/theme-placeholder-wrapper.tsx +42 -51
  37. package/src/theme-section.tsx +27 -34
  38. package/src/utils/__tests__/transform-styles.test.ts +97 -0
  39. package/src/utils/index.ts +9 -0
@@ -22,6 +22,8 @@ import {
22
22
  } from '@akinon/next/data/client/account';
23
23
  import { ContactFormType } from '@akinon/next/types';
24
24
 
25
+ import { postToEditor } from '../editor-bridge';
26
+ import { PreviewToEditorMessageType } from '@akinon/theme-contract';
25
27
  interface ContactFormSectionProps {
26
28
  section: Section;
27
29
  currentBreakpoint?: string;
@@ -71,7 +73,9 @@ const readProperty = (
71
73
  if (typeof raw === 'object') {
72
74
  const responsive = raw as Record<string, unknown>;
73
75
  const picked =
74
- responsive[breakpoint] ?? responsive.desktop ?? Object.values(responsive)[0];
76
+ responsive[breakpoint] ??
77
+ responsive.desktop ??
78
+ Object.values(responsive)[0];
75
79
  return picked == null ? fallback : String(picked);
76
80
  }
77
81
  return String(raw);
@@ -100,7 +104,15 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
100
104
 
101
105
  // Build filtered subjects
102
106
  const filteredSubjects: { label: string; value: string }[] = [
103
- { label: readProperty(section.properties, 'subject-placeholder', 'Konu seçiniz', currentBreakpoint), value: '' }
107
+ {
108
+ label: readProperty(
109
+ section.properties,
110
+ 'subject-placeholder',
111
+ 'Konu seçiniz',
112
+ currentBreakpoint
113
+ ),
114
+ value: ''
115
+ }
104
116
  ];
105
117
 
106
118
  if (subjectsSuccess && contactSubjects) {
@@ -124,21 +136,60 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
124
136
  reset,
125
137
  handleSubmit,
126
138
  formState: { errors }
127
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
140
  } = useForm<ContactFormType & Record<string, any>>({
129
141
  resolver: yupResolver(contactFormSchema) as any
130
142
  });
131
143
 
132
144
  // Read section properties
133
145
  const props = section.properties || {};
134
- const fullNamePlaceholder = readProperty(props, 'full-name-placeholder', 'Adınız Soyadınız', currentBreakpoint);
135
- const emailPlaceholder = readProperty(props, 'email-placeholder', 'E-posta adresiniz', currentBreakpoint);
136
- const phonePlaceholder = readProperty(props, 'phone-placeholder', 'Telefon numaranız', currentBreakpoint);
137
- const messagePlaceholder = readProperty(props, 'message-placeholder', 'Mesajınızı yazın...', currentBreakpoint);
138
- const buttonText = readProperty(props, 'button-text', 'Gönder', currentBreakpoint);
139
- const successMessage = readProperty(props, 'success-message', 'Mesajınız başarıyla gönderildi!', currentBreakpoint);
140
- const errorMessageText = readProperty(props, 'error-message', 'Bir hata oluştu. Lütfen tekrar deneyin.', currentBreakpoint);
141
- const showFileUploadRaw = getResponsiveValue(props['show-file-upload'], currentBreakpoint, true);
146
+ const fullNamePlaceholder = readProperty(
147
+ props,
148
+ 'full-name-placeholder',
149
+ 'Adınız Soyadınız',
150
+ currentBreakpoint
151
+ );
152
+ const emailPlaceholder = readProperty(
153
+ props,
154
+ 'email-placeholder',
155
+ 'E-posta adresiniz',
156
+ currentBreakpoint
157
+ );
158
+ const phonePlaceholder = readProperty(
159
+ props,
160
+ 'phone-placeholder',
161
+ 'Telefon numaranız',
162
+ currentBreakpoint
163
+ );
164
+ const messagePlaceholder = readProperty(
165
+ props,
166
+ 'message-placeholder',
167
+ 'Mesajınızı yazın...',
168
+ currentBreakpoint
169
+ );
170
+ const buttonText = readProperty(
171
+ props,
172
+ 'button-text',
173
+ 'Gönder',
174
+ currentBreakpoint
175
+ );
176
+ const successMessage = readProperty(
177
+ props,
178
+ 'success-message',
179
+ 'Mesajınız başarıyla gönderildi!',
180
+ currentBreakpoint
181
+ );
182
+ const errorMessageText = readProperty(
183
+ props,
184
+ 'error-message',
185
+ 'Bir hata oluştu. Lütfen tekrar deneyin.',
186
+ currentBreakpoint
187
+ );
188
+ const showFileUploadRaw = getResponsiveValue(
189
+ props['show-file-upload'],
190
+ currentBreakpoint,
191
+ true
192
+ );
142
193
  const showFileUpload =
143
194
  showFileUploadRaw === false || showFileUploadRaw === 'false' ? false : true;
144
195
  // Read section styles
@@ -155,43 +206,99 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
155
206
  : '';
156
207
  const hasMaxWidth = maxWidth !== 'none' && maxWidth !== 'full';
157
208
 
158
- const formGap = getResponsiveValue(section.styles?.['form-gap'], currentBreakpoint, 16);
209
+ const formGap = getResponsiveValue(
210
+ section.styles?.['form-gap'],
211
+ currentBreakpoint,
212
+ 16
213
+ );
159
214
 
160
215
  const inputBg = resolveThemeCssVariables(
161
- String(getResponsiveValue(section.styles?.['input-background-color'], currentBreakpoint, '#ffffff')),
216
+ String(
217
+ getResponsiveValue(
218
+ section.styles?.['input-background-color'],
219
+ currentBreakpoint,
220
+ '#ffffff'
221
+ )
222
+ ),
162
223
  themeSettings
163
224
  );
164
225
  const inputTextColor = resolveThemeCssVariables(
165
- String(getResponsiveValue(section.styles?.['input-text-color'], currentBreakpoint, '#0f172a')),
226
+ String(
227
+ getResponsiveValue(
228
+ section.styles?.['input-text-color'],
229
+ currentBreakpoint,
230
+ '#0f172a'
231
+ )
232
+ ),
166
233
  themeSettings
167
234
  );
168
235
  const inputBorderColor = resolveThemeCssVariables(
169
- String(getResponsiveValue(section.styles?.['input-border-color'], currentBreakpoint, '#cbd5e1')),
236
+ String(
237
+ getResponsiveValue(
238
+ section.styles?.['input-border-color'],
239
+ currentBreakpoint,
240
+ '#cbd5e1'
241
+ )
242
+ ),
170
243
  themeSettings
171
244
  );
172
245
  const inputBorderRadius = String(
173
- getResponsiveValue(section.styles?.['input-border-radius'], currentBreakpoint, '8px')
246
+ getResponsiveValue(
247
+ section.styles?.['input-border-radius'],
248
+ currentBreakpoint,
249
+ '8px'
250
+ )
174
251
  );
175
252
  const inputPadding = String(
176
- getResponsiveValue(section.styles?.['input-padding'], currentBreakpoint, '14px 16px')
253
+ getResponsiveValue(
254
+ section.styles?.['input-padding'],
255
+ currentBreakpoint,
256
+ '14px 16px'
257
+ )
177
258
  );
178
259
  const buttonBg = resolveThemeCssVariables(
179
- String(getResponsiveValue(section.styles?.['button-background-color'], currentBreakpoint, '#0f172a')),
260
+ String(
261
+ getResponsiveValue(
262
+ section.styles?.['button-background-color'],
263
+ currentBreakpoint,
264
+ '#0f172a'
265
+ )
266
+ ),
180
267
  themeSettings
181
268
  );
182
269
  const buttonTextColor = resolveThemeCssVariables(
183
- String(getResponsiveValue(section.styles?.['button-text-color'], currentBreakpoint, '#ffffff')),
270
+ String(
271
+ getResponsiveValue(
272
+ section.styles?.['button-text-color'],
273
+ currentBreakpoint,
274
+ '#ffffff'
275
+ )
276
+ ),
184
277
  themeSettings
185
278
  );
186
279
  const buttonHoverBg = resolveThemeCssVariables(
187
- String(getResponsiveValue(section.styles?.['button-hover-background-color'], currentBreakpoint, '#1e293b')),
280
+ String(
281
+ getResponsiveValue(
282
+ section.styles?.['button-hover-background-color'],
283
+ currentBreakpoint,
284
+ '#1e293b'
285
+ )
286
+ ),
188
287
  themeSettings
189
288
  );
190
289
  const buttonBorderRadius = String(
191
- getResponsiveValue(section.styles?.['button-border-radius'], currentBreakpoint, '8px')
290
+ getResponsiveValue(
291
+ section.styles?.['button-border-radius'],
292
+ currentBreakpoint,
293
+ '8px'
294
+ )
192
295
  );
193
296
  const buttonPadding = String(
194
- getResponsiveValue(section.styles?.['button-padding'], currentBreakpoint, '14px 32px')
297
+ getResponsiveValue(
298
+ section.styles?.['button-padding'],
299
+ currentBreakpoint,
300
+ '14px 32px'
301
+ )
195
302
  );
196
303
 
197
304
  const filteredStyles = Object.fromEntries(
@@ -214,7 +321,11 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
214
321
  )
215
322
  );
216
323
 
217
- const sectionStyles = getCSSStyles(filteredStyles, themeSettings, currentBreakpoint);
324
+ const sectionStyles = getCSSStyles(
325
+ filteredStyles,
326
+ themeSettings,
327
+ currentBreakpoint
328
+ );
218
329
 
219
330
  const inputStyle: React.CSSProperties = {
220
331
  backgroundColor: inputBg,
@@ -257,20 +368,21 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
257
368
  .forEach((b) => textBlocks.push(b));
258
369
  }
259
370
 
260
- const postBlockAction = (type: string, blockId: string, label?: string) => {
371
+ const postBlockAction = (
372
+ type: PreviewToEditorMessageType,
373
+ blockId: string,
374
+ label?: string
375
+ ) => {
261
376
  if (!window.parent) return;
262
- window.parent.postMessage(
263
- {
264
- type,
265
- data: {
266
- placeholderId,
267
- sectionId: section.id,
268
- blockId,
269
- ...(label ? { label } : {})
270
- }
271
- },
272
- '*'
273
- );
377
+ postToEditor({
378
+ type,
379
+ data: {
380
+ placeholderId,
381
+ sectionId: section.id,
382
+ blockId,
383
+ ...(label ? { label } : {})
384
+ }
385
+ });
274
386
  };
275
387
 
276
388
  const renderBlock = (block: Block) => (
@@ -286,9 +398,13 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
286
398
  onMoveUp={() => postBlockAction('MOVE_BLOCK_UP', block.id)}
287
399
  onMoveDown={() => postBlockAction('MOVE_BLOCK_DOWN', block.id)}
288
400
  onDuplicate={() => postBlockAction('DUPLICATE_BLOCK', block.id)}
289
- onToggleVisibility={() => postBlockAction('TOGGLE_BLOCK_VISIBILITY', block.id)}
401
+ onToggleVisibility={() =>
402
+ postBlockAction('TOGGLE_BLOCK_VISIBILITY', block.id)
403
+ }
290
404
  onDelete={() => postBlockAction('DELETE_BLOCK', block.id)}
291
- onRename={(newLabel) => postBlockAction('RENAME_BLOCK', block.id, newLabel)}
405
+ onRename={(newLabel) =>
406
+ postBlockAction('RENAME_BLOCK', block.id, newLabel)
407
+ }
292
408
  />
293
409
  );
294
410
 
@@ -320,10 +436,9 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
320
436
  .catch((err) => {
321
437
  let msg = errorMessageText;
322
438
  if (err?.data && typeof err.data === 'object') {
323
- const entries = Object.entries(err.data)
324
- .map(([, msgs]) =>
325
- Array.isArray(msgs) ? msgs.join(', ') : String(msgs)
326
- );
439
+ const entries = Object.entries(err.data).map(([, msgs]) =>
440
+ Array.isArray(msgs) ? msgs.join(', ') : String(msgs)
441
+ );
327
442
  if (entries.length > 0) msg = entries.join(' ');
328
443
  }
329
444
  setFeedbackModal({ open: true, title: 'Hata', message: msg });
@@ -342,7 +457,6 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
342
457
  marginTop: '4px'
343
458
  };
344
459
 
345
-
346
460
  return (
347
461
  <>
348
462
  <div
@@ -371,7 +485,11 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
371
485
  {/* Form */}
372
486
  <form
373
487
  onSubmit={handleSubmit(onSubmit)}
374
- style={{ display: 'flex', flexDirection: 'column', gap: `${formGap}px` }}
488
+ style={{
489
+ display: 'flex',
490
+ flexDirection: 'column',
491
+ gap: `${formGap}px`
492
+ }}
375
493
  >
376
494
  {/* Name + Email row */}
377
495
  <div
@@ -430,7 +548,13 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
430
548
  {/* Subject — select from API */}
431
549
  <div style={{ position: 'relative' }}>
432
550
  {subjectsLoading && (
433
- <div style={{ padding: '10px', fontSize: '13px', color: '#94a3b8' }}>
551
+ <div
552
+ style={{
553
+ padding: '10px',
554
+ fontSize: '13px',
555
+ color: '#94a3b8'
556
+ }}
557
+ >
434
558
  Konular yükleniyor...
435
559
  </div>
436
560
  )}
@@ -508,8 +632,9 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
508
632
  }}
509
633
  onMouseEnter={(e) => {
510
634
  if (!isSubmitting) {
511
- (e.currentTarget as HTMLButtonElement).style.backgroundColor =
512
- buttonHoverBg;
635
+ (
636
+ e.currentTarget as HTMLButtonElement
637
+ ).style.backgroundColor = buttonHoverBg;
513
638
  }
514
639
  }}
515
640
  onMouseLeave={(e) => {
@@ -523,19 +648,21 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
523
648
 
524
649
  {/* Server-side errors */}
525
650
  {submitError && (
526
- <div style={{ color: '#ef4444', fontSize: '13px', marginTop: '4px' }}>
651
+ <div
652
+ style={{ color: '#ef4444', fontSize: '13px', marginTop: '4px' }}
653
+ >
527
654
  {'data' in submitError &&
528
655
  submitError.data &&
529
656
  typeof submitError.data === 'object'
530
- ? Object.entries(submitError.data as Record<string, unknown>).map(
531
- ([field, messages]) => (
532
- <div key={field}>
533
- {Array.isArray(messages)
534
- ? messages.join(', ')
535
- : String(messages)}
536
- </div>
537
- )
538
- )
657
+ ? Object.entries(
658
+ submitError.data as Record<string, unknown>
659
+ ).map(([field, messages]) => (
660
+ <div key={field}>
661
+ {Array.isArray(messages)
662
+ ? messages.join(', ')
663
+ : String(messages)}
664
+ </div>
665
+ ))
539
666
  : errorMessageText}
540
667
  </div>
541
668
  )}
@@ -546,9 +673,7 @@ const ContactFormSection: React.FC<ContactFormSectionProps> = ({
546
673
  <Modal
547
674
  portalId={`contact-form-feedback-${section.id}`}
548
675
  open={feedbackModal.open}
549
- setOpen={(open) =>
550
- setFeedbackModal((prev) => ({ ...prev, open }))
551
- }
676
+ setOpen={(open) => setFeedbackModal((prev) => ({ ...prev, open }))}
552
677
  title={feedbackModal.title}
553
678
  showCloseButton={true}
554
679
  className="w-[92vw] max-w-[420px] rounded"
@@ -14,6 +14,8 @@ import {
14
14
  } from '../utils';
15
15
  import { Section } from '../theme-section';
16
16
 
17
+ import { postToEditor } from '../editor-bridge';
18
+ import { PreviewToEditorMessageType } from '@akinon/theme-contract';
17
19
  interface CountdownCampaignBannerSectionProps {
18
20
  section: Section;
19
21
  currentBreakpoint?: string;
@@ -87,7 +89,8 @@ const parseNumber = (value: unknown, fallback: number): number => {
87
89
  const clampNumber = (value: number, min: number, max: number) =>
88
90
  Math.min(Math.max(value, min), max);
89
91
 
90
- const formatTwoDigits = (value: number) => String(Math.max(0, value)).padStart(2, '0');
92
+ const formatTwoDigits = (value: number) =>
93
+ String(Math.max(0, value)).padStart(2, '0');
91
94
 
92
95
  const parseBoolean = (value: unknown, fallback: boolean): boolean => {
93
96
  if (typeof value === 'boolean') return value;
@@ -166,7 +169,9 @@ const getRemainingTime = (
166
169
  };
167
170
  };
168
171
 
169
- const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionProps> = ({
172
+ const CountdownCampaignBannerSection: React.FC<
173
+ CountdownCampaignBannerSectionProps
174
+ > = ({
170
175
  section,
171
176
  currentBreakpoint = 'desktop',
172
177
  placeholderId = '',
@@ -203,7 +208,11 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
203
208
  const endDate = useMemo(() => parseEndDate(endDateRaw), [endDateRaw]);
204
209
 
205
210
  const showSeconds = parseBoolean(
206
- getResponsiveValue(section.properties?.['show-seconds'], currentBreakpoint, true),
211
+ getResponsiveValue(
212
+ section.properties?.['show-seconds'],
213
+ currentBreakpoint,
214
+ true
215
+ ),
207
216
  true
208
217
  );
209
218
 
@@ -256,7 +265,11 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
256
265
  )
257
266
  );
258
267
 
259
- const sectionStyles = getCSSStyles(filteredStyles, themeSettings, currentBreakpoint);
268
+ const sectionStyles = getCSSStyles(
269
+ filteredStyles,
270
+ themeSettings,
271
+ currentBreakpoint
272
+ );
260
273
  const backgroundImageSrc = useMemo(() => {
261
274
  if (!backgroundBlock) return '';
262
275
  const raw = pickImageSource(backgroundBlock.value, currentBreakpoint);
@@ -266,14 +279,22 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
266
279
 
267
280
  const overlayColor = resolveThemeCssVariables(
268
281
  String(
269
- getResponsiveValue(section.styles?.['overlay-color'], currentBreakpoint, '#0f172a')
282
+ getResponsiveValue(
283
+ section.styles?.['overlay-color'],
284
+ currentBreakpoint,
285
+ '#0f172a'
286
+ )
270
287
  ),
271
288
  themeSettings
272
289
  );
273
290
  const overlayOpacity =
274
291
  clampNumber(
275
292
  parseNumber(
276
- getResponsiveValue(section.styles?.['overlay-opacity'], currentBreakpoint, 45),
293
+ getResponsiveValue(
294
+ section.styles?.['overlay-opacity'],
295
+ currentBreakpoint,
296
+ 45
297
+ ),
277
298
  45
278
299
  ),
279
300
  0,
@@ -293,39 +314,55 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
293
314
  );
294
315
  const timerTextColor = resolveThemeCssVariables(
295
316
  String(
296
- getResponsiveValue(section.styles?.['timer-text-color'], currentBreakpoint, '#0f172a')
317
+ getResponsiveValue(
318
+ section.styles?.['timer-text-color'],
319
+ currentBreakpoint,
320
+ '#0f172a'
321
+ )
297
322
  ),
298
323
  themeSettings
299
324
  );
300
325
  const timerLabelColor = resolveThemeCssVariables(
301
326
  String(
302
- getResponsiveValue(section.styles?.['timer-label-color'], currentBreakpoint, '#64748b')
327
+ getResponsiveValue(
328
+ section.styles?.['timer-label-color'],
329
+ currentBreakpoint,
330
+ '#64748b'
331
+ )
303
332
  ),
304
333
  themeSettings
305
334
  );
306
335
  const timerBorderRadius = String(
307
- getResponsiveValue(section.styles?.['timer-border-radius'], currentBreakpoint, '10px')
336
+ getResponsiveValue(
337
+ section.styles?.['timer-border-radius'],
338
+ currentBreakpoint,
339
+ '10px'
340
+ )
308
341
  );
309
342
  const timerGap = clampNumber(
310
- parseNumber(getResponsiveValue(section.styles?.['timer-gap'], currentBreakpoint, 10), 10),
343
+ parseNumber(
344
+ getResponsiveValue(section.styles?.['timer-gap'], currentBreakpoint, 10),
345
+ 10
346
+ ),
311
347
  0,
312
348
  32
313
349
  );
314
350
 
315
- const postBlockAction = (type: string, blockId: string, label?: string) => {
351
+ const postBlockAction = (
352
+ type: PreviewToEditorMessageType,
353
+ blockId: string,
354
+ label?: string
355
+ ) => {
316
356
  if (!window.parent) return;
317
- window.parent.postMessage(
318
- {
319
- type,
320
- data: {
321
- placeholderId,
322
- sectionId: section.id,
323
- blockId,
324
- ...(label ? { label } : {})
325
- }
326
- },
327
- '*'
328
- );
357
+ postToEditor({
358
+ type,
359
+ data: {
360
+ placeholderId,
361
+ sectionId: section.id,
362
+ blockId,
363
+ ...(label ? { label } : {})
364
+ }
365
+ });
329
366
  };
330
367
 
331
368
  const renderBlock = (block: Block) => (
@@ -341,9 +378,13 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
341
378
  onMoveUp={() => postBlockAction('MOVE_BLOCK_UP', block.id)}
342
379
  onMoveDown={() => postBlockAction('MOVE_BLOCK_DOWN', block.id)}
343
380
  onDuplicate={() => postBlockAction('DUPLICATE_BLOCK', block.id)}
344
- onToggleVisibility={() => postBlockAction('TOGGLE_BLOCK_VISIBILITY', block.id)}
381
+ onToggleVisibility={() =>
382
+ postBlockAction('TOGGLE_BLOCK_VISIBILITY', block.id)
383
+ }
345
384
  onDelete={() => postBlockAction('DELETE_BLOCK', block.id)}
346
- onRename={(newLabel) => postBlockAction('RENAME_BLOCK', block.id, newLabel)}
385
+ onRename={(newLabel) =>
386
+ postBlockAction('RENAME_BLOCK', block.id, newLabel)
387
+ }
347
388
  />
348
389
  );
349
390
 
@@ -372,7 +413,9 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
372
413
  style={{ ...sectionStyles, position: 'relative' }}
373
414
  >
374
415
  {backgroundBlock && backgroundImageSrc ? (
375
- <div className="absolute inset-0 z-0">{renderBlock(backgroundBlock)}</div>
416
+ <div className="absolute inset-0 z-0">
417
+ {renderBlock(backgroundBlock)}
418
+ </div>
376
419
  ) : (
377
420
  <div className="absolute inset-0 z-0 bg-slate-600" />
378
421
  )}
@@ -397,7 +440,10 @@ const CountdownCampaignBannerSection: React.FC<CountdownCampaignBannerSectionPro
397
440
  {expiredMessage}
398
441
  </div>
399
442
  ) : (
400
- <div className="flex flex-wrap items-stretch" style={{ gap: `${timerGap}px` }}>
443
+ <div
444
+ className="flex flex-wrap items-stretch"
445
+ style={{ gap: `${timerGap}px` }}
446
+ >
401
447
  {timerUnits.map((unit) => (
402
448
  <div
403
449
  key={unit.key}