@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,79 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+
5
+ interface DynamicFontLoaderProps {
6
+ fontFamily?: string;
7
+ fontWeight?: string;
8
+ fontSubsets?: string;
9
+ }
10
+
11
+ /**
12
+ * Client-side component to dynamically load Google Fonts
13
+ * Used for theme editor buttons that use custom fonts
14
+ */
15
+ export function DynamicFontLoader({
16
+ fontFamily,
17
+ fontWeight,
18
+ fontSubsets
19
+ }: DynamicFontLoaderProps) {
20
+ useEffect(() => {
21
+ if (!fontFamily || fontFamily === 'inherit') return;
22
+
23
+ // Clean font family name (remove quotes and extra spaces)
24
+ const cleanFontFamily = fontFamily.replace(/['"]/g, '').trim();
25
+
26
+ // Skip if it's a system font
27
+ const systemFonts = [
28
+ 'Arial',
29
+ 'Helvetica',
30
+ 'Times New Roman',
31
+ 'Georgia',
32
+ 'Courier New',
33
+ 'Verdana'
34
+ ];
35
+ if (systemFonts.includes(cleanFontFamily)) return;
36
+
37
+ // Build Google Fonts URL
38
+ const params = new URLSearchParams();
39
+
40
+ // Font family with weights
41
+ const weights = fontWeight || '100;300;400;500;600;700;800;900';
42
+ const fontParam = `${cleanFontFamily.replace(/\s+/g, '+')}:wght@${weights}`;
43
+ params.append('family', fontParam);
44
+
45
+ // Subsets
46
+ if (fontSubsets) {
47
+ params.append('subset', fontSubsets);
48
+ }
49
+
50
+ // Display swap for better performance
51
+ params.append('display', 'swap');
52
+
53
+ const fontUrl = `https://fonts.googleapis.com/css2?${params.toString()}`;
54
+
55
+ // Check if link already exists
56
+ const existingLink = document.querySelector(`link[href="${fontUrl}"]`);
57
+ if (existingLink) return;
58
+
59
+ // Create and append link element
60
+ const link = document.createElement('link');
61
+ link.href = fontUrl;
62
+ link.rel = 'stylesheet';
63
+ link.id = `google-font-${cleanFontFamily
64
+ .replace(/\s+/g, '-')
65
+ .toLowerCase()}`;
66
+
67
+ document.head.appendChild(link);
68
+
69
+ // Cleanup on unmount
70
+ return () => {
71
+ const linkToRemove = document.getElementById(link.id);
72
+ if (linkToRemove) {
73
+ document.head.removeChild(linkToRemove);
74
+ }
75
+ };
76
+ }, [fontFamily, fontWeight, fontSubsets]);
77
+
78
+ return null;
79
+ }
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ import { useCallback } from 'react';
4
+
5
+ interface BlockInfo {
6
+ id: string;
7
+ type: string;
8
+ label: string;
9
+ }
10
+
11
+ interface UseDesignerFeaturesProps {
12
+ blockId: string;
13
+ placeholderId: string;
14
+ sectionId: string;
15
+ isDesigner: boolean;
16
+ blockInfo?: BlockInfo;
17
+ }
18
+
19
+ export function useDesignerFeatures({
20
+ blockId,
21
+ placeholderId,
22
+ sectionId,
23
+ isDesigner,
24
+ blockInfo
25
+ }: UseDesignerFeaturesProps) {
26
+ const handleClick = useCallback(
27
+ (e: React.MouseEvent) => {
28
+ if (isDesigner) {
29
+ e.preventDefault();
30
+ e.stopPropagation();
31
+
32
+ if (window.parent) {
33
+ window.parent.postMessage(
34
+ {
35
+ type: 'SELECT_BLOCK',
36
+ data: {
37
+ placeholderId,
38
+ sectionId,
39
+ blockId,
40
+ blockType: blockInfo?.type,
41
+ blockLabel: blockInfo?.label
42
+ }
43
+ },
44
+ '*'
45
+ );
46
+ }
47
+ }
48
+ },
49
+ [isDesigner, blockId, placeholderId, sectionId, blockInfo]
50
+ );
51
+
52
+ const createActionHandler = useCallback(
53
+ (actionType: string) => () => {
54
+ if (window.parent) {
55
+ window.parent.postMessage(
56
+ {
57
+ type: actionType,
58
+ data: {
59
+ placeholderId,
60
+ sectionId,
61
+ blockId
62
+ }
63
+ },
64
+ '*'
65
+ );
66
+ }
67
+ },
68
+ [blockId, placeholderId, sectionId]
69
+ );
70
+
71
+ const handleRename = useCallback(
72
+ (newLabel: string) => {
73
+ if (window.parent) {
74
+ window.parent.postMessage(
75
+ {
76
+ type: 'RENAME_BLOCK',
77
+ data: {
78
+ placeholderId,
79
+ sectionId,
80
+ blockId,
81
+ label: newLabel
82
+ }
83
+ },
84
+ '*'
85
+ );
86
+ }
87
+ },
88
+ [blockId, placeholderId, sectionId]
89
+ );
90
+
91
+ return {
92
+ handleClick,
93
+ onMoveUp: createActionHandler('MOVE_BLOCK_UP'),
94
+ onMoveDown: createActionHandler('MOVE_BLOCK_DOWN'),
95
+ onDuplicate: createActionHandler('DUPLICATE_BLOCK'),
96
+ onToggleVisibility: createActionHandler('TOGGLE_BLOCK_VISIBILITY'),
97
+ onDelete: createActionHandler('DELETE_BLOCK'),
98
+ onRename: handleRename
99
+ };
100
+ }
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import {
5
+ registerPlaceholder,
6
+ unregisterPlaceholder
7
+ } from '../placeholder-registry';
8
+
9
+ interface UseExternalDesignerOptions {
10
+ placeholderId: string;
11
+ }
12
+
13
+ interface ExternalDesignerState {
14
+ isDesigner: boolean;
15
+ selectedSectionId: string | null;
16
+ selectedBlockId: string | null;
17
+ }
18
+
19
+ export function useExternalDesigner({
20
+ placeholderId
21
+ }: UseExternalDesignerOptions): ExternalDesignerState {
22
+ const [isDesigner, setIsDesigner] = useState(false);
23
+ const [selectedSectionId, setSelectedSectionId] = useState<string | null>(
24
+ null
25
+ );
26
+ const [selectedBlockId, setSelectedBlockId] = useState<string | null>(null);
27
+
28
+ useEffect(() => {
29
+ if (typeof window === 'undefined') {
30
+ return;
31
+ }
32
+
33
+ const isInIframe = window.self !== window.top;
34
+
35
+ // Set isDesigner immediately if in iframe (don't wait for SET_THEME_EDITOR_COOKIE)
36
+ if (isInIframe) {
37
+ setIsDesigner(true);
38
+ }
39
+
40
+ if (isInIframe && window.parent) {
41
+ // Only send IFRAME_READY once per page session to prevent re-initialization
42
+ if (
43
+ !(window as Window & { __iframeReadySent?: boolean }).__iframeReadySent
44
+ ) {
45
+ window.parent.postMessage(
46
+ {
47
+ type: 'IFRAME_READY'
48
+ },
49
+ '*'
50
+ );
51
+ (window as Window & { __iframeReadySent?: boolean }).__iframeReadySent =
52
+ true;
53
+ }
54
+
55
+ registerPlaceholder(placeholderId);
56
+ }
57
+
58
+ const handleMessage = (event: MessageEvent) => {
59
+ const { type, data } = event.data || {};
60
+
61
+ // Also handle SET_THEME_EDITOR_COOKIE for backwards compatibility
62
+ if (type === 'SET_THEME_EDITOR_COOKIE' && data) {
63
+ setIsDesigner(true);
64
+ }
65
+
66
+ if (type === 'SELECT_SECTION' && data?.placeholderId === placeholderId) {
67
+ setSelectedSectionId(data.sectionId ?? null);
68
+ setSelectedBlockId(null);
69
+ }
70
+
71
+ if (type === 'SELECT_BLOCK' && data?.placeholderId === placeholderId) {
72
+ setSelectedSectionId(data.sectionId ?? null);
73
+ setSelectedBlockId(data.blockId ?? null);
74
+ }
75
+
76
+ if (type === 'CLEAR_SELECTION') {
77
+ setSelectedSectionId(null);
78
+ setSelectedBlockId(null);
79
+ }
80
+ };
81
+
82
+ window.addEventListener('message', handleMessage);
83
+
84
+ return () => {
85
+ window.removeEventListener('message', handleMessage);
86
+ unregisterPlaceholder(placeholderId);
87
+ };
88
+ }, [placeholderId]);
89
+
90
+ return {
91
+ isDesigner,
92
+ selectedSectionId,
93
+ selectedBlockId
94
+ };
95
+ }
@@ -0,0 +1,188 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState, useCallback, useRef } from 'react';
4
+ import {
5
+ useGetWidgetQuery,
6
+ useGetWidgetSchemaQuery
7
+ } from '../../../data/client/misc';
8
+
9
+ export interface NativeWidgetBlock {
10
+ id: string;
11
+ type?: string;
12
+ label?: string;
13
+ styles?: Record<string, unknown>;
14
+ properties?: Record<string, unknown>;
15
+ value?: unknown;
16
+ blocks?: NativeWidgetBlock[];
17
+ }
18
+
19
+ export interface NativeWidgetData {
20
+ blocks: Map<string, NativeWidgetBlock>;
21
+ sectionStyles: Record<string, unknown>;
22
+ sectionProperties: Record<string, unknown>;
23
+ isLoading: boolean;
24
+ getBlock: (blockId: string) => NativeWidgetBlock | undefined;
25
+ getSectionStyle: (key: string) => unknown;
26
+ getSectionProperty: (key: string) => unknown;
27
+ }
28
+
29
+ interface UseNativeWidgetDataOptions {
30
+ widgetSlug: string;
31
+ sectionId: string;
32
+ skip?: boolean;
33
+ blockMeta?: Array<{ id: string; type: string; label: string }>;
34
+ }
35
+
36
+ function parseAttributeValue(attrData: unknown): unknown {
37
+ if (attrData === undefined || attrData === null) return undefined;
38
+
39
+ if (typeof attrData === 'string') {
40
+ try {
41
+ return JSON.parse(attrData);
42
+ } catch {
43
+ return attrData;
44
+ }
45
+ }
46
+
47
+ if (typeof attrData === 'object' && attrData !== null && 'value' in attrData) {
48
+ const value = (attrData as { value?: unknown }).value;
49
+ if (typeof value === 'string') {
50
+ try {
51
+ return JSON.parse(value);
52
+ } catch {
53
+ return value;
54
+ }
55
+ }
56
+ return value;
57
+ }
58
+
59
+ return attrData;
60
+ }
61
+
62
+ function extractResponsiveValue(value: unknown): unknown {
63
+ if (typeof value === 'object' && value !== null && 'desktop' in value) {
64
+ return (value as Record<string, unknown>).desktop;
65
+ }
66
+ return value;
67
+ }
68
+
69
+ export function useNativeWidgetData({
70
+ widgetSlug,
71
+ sectionId,
72
+ skip = false,
73
+ blockMeta = []
74
+ }: UseNativeWidgetDataOptions): NativeWidgetData {
75
+ const [blockMap, setBlockMap] = useState<Map<string, NativeWidgetBlock>>(
76
+ () => new Map()
77
+ );
78
+ const [sectionStyles, setSectionStyles] = useState<Record<string, unknown>>({});
79
+ const [sectionProperties, setSectionProperties] = useState<Record<string, unknown>>({});
80
+ const blockMapRef = useRef(blockMap);
81
+
82
+ const { data: widgetData, isLoading: isWidgetLoading } = useGetWidgetQuery(
83
+ widgetSlug,
84
+ { skip }
85
+ );
86
+
87
+ const { data: schemaData, isLoading: isSchemaLoading } = useGetWidgetSchemaQuery(
88
+ widgetSlug,
89
+ { skip }
90
+ );
91
+
92
+ useEffect(() => {
93
+ if (skip) return;
94
+
95
+ const schema = schemaData?.schema;
96
+ const attributes = widgetData?.attributes;
97
+
98
+ const sectionMeta = schema?.[sectionId]?.metadata;
99
+ if (sectionMeta) {
100
+ if (sectionMeta.styles) {
101
+ setSectionStyles(sectionMeta.styles);
102
+ }
103
+ if (sectionMeta.properties) {
104
+ setSectionProperties(sectionMeta.properties);
105
+ }
106
+ }
107
+
108
+ const newBlockMap = new Map<string, NativeWidgetBlock>();
109
+
110
+ blockMeta.forEach((meta) => {
111
+ newBlockMap.set(meta.id, {
112
+ id: meta.id,
113
+ type: meta.type,
114
+ label: meta.label
115
+ });
116
+ });
117
+
118
+ if (schema) {
119
+ Object.entries(schema).forEach(([blockId, blockSchema]: [string, any]) => {
120
+ if (blockId === sectionId) return;
121
+
122
+ const metadata = blockSchema?.metadata || {};
123
+ const existing = newBlockMap.get(blockId);
124
+ const value = attributes ? parseAttributeValue(attributes[blockId]) : undefined;
125
+
126
+ newBlockMap.set(blockId, {
127
+ ...existing,
128
+ id: blockId,
129
+ type: metadata.type || existing?.type,
130
+ label: blockSchema.label || existing?.label,
131
+ styles: metadata.styles || {},
132
+ properties: metadata.properties || {},
133
+ value
134
+ });
135
+ });
136
+ } else if (attributes) {
137
+ Object.keys(attributes).forEach((blockId) => {
138
+ const existing = newBlockMap.get(blockId);
139
+ const parsed = parseAttributeValue(attributes[blockId]);
140
+
141
+ if (typeof parsed === 'object' && parsed !== null) {
142
+ const payload = parsed as Record<string, unknown>;
143
+ newBlockMap.set(blockId, {
144
+ ...existing,
145
+ id: blockId,
146
+ styles: (payload.styles as Record<string, unknown>) || existing?.styles,
147
+ properties: (payload.properties as Record<string, unknown>) || existing?.properties,
148
+ value: payload.value !== undefined ? payload.value : existing?.value
149
+ });
150
+ } else {
151
+ newBlockMap.set(blockId, {
152
+ ...existing,
153
+ id: blockId,
154
+ value: parsed
155
+ });
156
+ }
157
+ });
158
+ }
159
+
160
+ setBlockMap(newBlockMap);
161
+ blockMapRef.current = newBlockMap;
162
+ }, [skip, widgetData, schemaData, sectionId, blockMeta]);
163
+
164
+ const getBlock = useCallback(
165
+ (blockId: string) => blockMapRef.current.get(blockId),
166
+ []
167
+ );
168
+
169
+ const getSectionStyle = useCallback(
170
+ (key: string) => extractResponsiveValue(sectionStyles[key]),
171
+ [sectionStyles]
172
+ );
173
+
174
+ const getSectionProperty = useCallback(
175
+ (key: string) => extractResponsiveValue(sectionProperties[key]),
176
+ [sectionProperties]
177
+ );
178
+
179
+ return {
180
+ blocks: blockMap,
181
+ sectionStyles,
182
+ sectionProperties,
183
+ isLoading: isWidgetLoading || isSchemaLoading,
184
+ getBlock,
185
+ getSectionStyle,
186
+ getSectionProperty
187
+ };
188
+ }
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ import { usePathname, useSearchParams } from 'next/navigation';
4
+ import { useSession } from 'next-auth/react';
5
+
6
+ import { useLocalization } from '../../../hooks/use-localization';
7
+
8
+ export const useVisibilityContext = (currentBreakpoint = 'desktop') => {
9
+ const pathname = usePathname();
10
+ const searchParams = useSearchParams();
11
+ const { status } = useSession();
12
+ const { locale, currency } = useLocalization();
13
+
14
+ return {
15
+ authState:
16
+ status === 'authenticated'
17
+ ? ('authenticated' as const)
18
+ : status === 'loading'
19
+ ? ('loading' as const)
20
+ : ('guest' as const),
21
+ breakpoint: currentBreakpoint,
22
+ locale: String(locale || ''),
23
+ currency: String(currency || ''),
24
+ pathname: String(pathname || ''),
25
+ searchParams
26
+ };
27
+ };
@@ -0,0 +1,31 @@
1
+ const placeholderRegistry = new Set<string>();
2
+ let registryTimeout: NodeJS.Timeout | null = null;
3
+
4
+ export const registerPlaceholder = (slug: string) => {
5
+ placeholderRegistry.add(slug);
6
+
7
+ if (registryTimeout) {
8
+ clearTimeout(registryTimeout);
9
+ }
10
+
11
+ registryTimeout = setTimeout(() => {
12
+ if (
13
+ typeof window !== 'undefined' &&
14
+ window.parent &&
15
+ window.self !== window.top
16
+ ) {
17
+ const availablePlaceholders = Array.from(placeholderRegistry);
18
+ window.parent.postMessage(
19
+ {
20
+ type: 'PLACEHOLDERS_AVAILABLE',
21
+ data: { placeholders: availablePlaceholders }
22
+ },
23
+ '*'
24
+ );
25
+ }
26
+ }, 100);
27
+ };
28
+
29
+ export const unregisterPlaceholder = (slug: string) => {
30
+ placeholderRegistry.delete(slug);
31
+ };