@akinon/projectzero 2.0.0-beta.19 → 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 (223) hide show
  1. package/CHANGELOG.md +9 -7
  2. package/app-template/CHANGELOG.md +251 -204
  3. package/app-template/akinon.json +1 -1
  4. package/app-template/package.json +28 -28
  5. package/app-template/public/amex.svg +12 -0
  6. package/app-template/public/apple-pay.svg +16 -0
  7. package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
  8. package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
  9. package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
  10. package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
  11. package/app-template/public/google-pay.svg +16 -0
  12. package/app-template/public/locales/en/account.json +6 -3
  13. package/app-template/public/locales/en/auth.json +6 -7
  14. package/app-template/public/locales/en/basket.json +6 -6
  15. package/app-template/public/locales/en/blog.json +7 -0
  16. package/app-template/public/locales/en/category.json +3 -1
  17. package/app-template/public/locales/en/checkout.json +5 -4
  18. package/app-template/public/locales/en/common.json +11 -2
  19. package/app-template/public/locales/en/forgot_password.json +6 -7
  20. package/app-template/public/locales/en/product.json +4 -3
  21. package/app-template/public/locales/tr/account.json +6 -3
  22. package/app-template/public/locales/tr/auth.json +16 -17
  23. package/app-template/public/locales/tr/basket.json +4 -4
  24. package/app-template/public/locales/tr/blog.json +7 -0
  25. package/app-template/public/locales/tr/category.json +3 -1
  26. package/app-template/public/locales/tr/checkout.json +39 -38
  27. package/app-template/public/locales/tr/common.json +10 -1
  28. package/app-template/public/locales/tr/forgot_password.json +12 -13
  29. package/app-template/public/locales/tr/product.json +1 -0
  30. package/app-template/public/logo.svg +3 -27
  31. package/app-template/public/mastercard.svg +14 -0
  32. package/app-template/public/promotion-banner.jpg +0 -0
  33. package/app-template/public/shop-pay.svg +12 -0
  34. package/app-template/public/visa.svg +12 -0
  35. package/app-template/src/app/[commerce]/[locale]/[currency]/blog/[slug]/page.tsx +118 -0
  36. package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +15 -0
  37. package/app-template/src/app/api/theme-settings/route.ts +12 -0
  38. package/app-template/src/assets/fonts/pz-icon.css +211 -49
  39. package/app-template/src/assets/fonts/pz-icon.eot +0 -0
  40. package/app-template/src/assets/fonts/pz-icon.html +486 -0
  41. package/app-template/src/assets/fonts/pz-icon.scss +373 -49
  42. package/app-template/src/assets/fonts/pz-icon.svg +215 -53
  43. package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
  44. package/app-template/src/assets/fonts/pz-icon.woff +0 -0
  45. package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
  46. package/app-template/src/assets/globals.scss +4 -0
  47. package/app-template/src/assets/icons/arrow-right.svg +3 -0
  48. package/app-template/src/assets/icons/cart.svg +4 -12
  49. package/app-template/src/assets/icons/check.svg +2 -18
  50. package/app-template/src/assets/icons/chevron-down.svg +2 -7
  51. package/app-template/src/assets/icons/delete.svg +3 -0
  52. package/app-template/src/assets/icons/facebook.svg +2 -8
  53. package/app-template/src/assets/icons/fav-off.svg +5 -0
  54. package/app-template/src/assets/icons/fav-on.svg +5 -0
  55. package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
  56. package/app-template/src/assets/icons/heart.svg +3 -0
  57. package/app-template/src/assets/icons/instagram.svg +2 -13
  58. package/app-template/src/assets/icons/materials.svg +3 -0
  59. package/app-template/src/assets/icons/person.svg +4 -0
  60. package/app-template/src/assets/icons/pinterest.svg +5 -11
  61. package/app-template/src/assets/icons/ruler.svg +3 -0
  62. package/app-template/src/assets/icons/search.svg +8 -11
  63. package/app-template/src/assets/icons/share.svg +2 -9
  64. package/app-template/src/assets/icons/snapchat.svg +3 -0
  65. package/app-template/src/assets/icons/tiktok.svg +3 -0
  66. package/app-template/src/assets/icons/tumblr.svg +6 -0
  67. package/app-template/src/assets/icons/twitter.svg +2 -10
  68. package/app-template/src/assets/icons/vimeo.svg +3 -0
  69. package/app-template/src/assets/icons/youtube.svg +3 -0
  70. package/app-template/src/assets/icons/zoom.svg +8 -0
  71. package/app-template/src/components/accordion.tsx +33 -11
  72. package/app-template/src/components/action-tooltip.tsx +160 -0
  73. package/app-template/src/components/currency-select.tsx +149 -4
  74. package/app-template/src/components/icon.tsx +5 -6
  75. package/app-template/src/components/index.ts +4 -1
  76. package/app-template/src/components/language-select.tsx +88 -2
  77. package/app-template/src/components/pagination.tsx +132 -20
  78. package/app-template/src/components/quantity-input.tsx +63 -0
  79. package/app-template/src/components/quantity-selector.tsx +203 -0
  80. package/app-template/src/components/route-handler.tsx +50 -0
  81. package/app-template/src/components/select.tsx +89 -69
  82. package/app-template/src/components/types/index.ts +26 -0
  83. package/app-template/src/components/widget-content.tsx +323 -0
  84. package/app-template/src/data/server/theme.ts +70 -0
  85. package/app-template/src/hooks/use-fav-button.tsx +5 -2
  86. package/app-template/src/hooks/use-product-cart.ts +11 -8
  87. package/app-template/src/hooks/use-theme-settings.ts +42 -0
  88. package/app-template/src/lib/fonts.ts +149 -0
  89. package/app-template/src/settings.js +2 -2
  90. package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
  91. package/app-template/src/types/widget.ts +169 -0
  92. package/app-template/src/utils/formatDate.ts +48 -0
  93. package/app-template/src/utils/styles.ts +71 -0
  94. package/app-template/src/views/account/contact-form.tsx +147 -130
  95. package/app-template/src/views/basket/basket-item.tsx +691 -107
  96. package/app-template/src/views/basket/basket-summary-context.tsx +560 -0
  97. package/app-template/src/views/basket/designer-context.tsx +617 -0
  98. package/app-template/src/views/basket/index.ts +2 -0
  99. package/app-template/src/views/basket/summary.tsx +496 -75
  100. package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +190 -0
  101. package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +286 -0
  102. package/app-template/src/views/breadcrumb/constants.ts +15 -0
  103. package/app-template/src/views/breadcrumb/index.tsx +127 -0
  104. package/app-template/src/views/breadcrumb.tsx +13 -38
  105. package/app-template/src/views/category/category-banner.tsx +4 -23
  106. package/app-template/src/views/category/category-header.tsx +289 -66
  107. package/app-template/src/views/category/category-info.tsx +173 -24
  108. package/app-template/src/views/category/filters/filter-item.tsx +138 -42
  109. package/app-template/src/views/category/filters/index.tsx +208 -48
  110. package/app-template/src/views/category/layout.tsx +7 -4
  111. package/app-template/src/views/category/native-widget-context.tsx +257 -0
  112. package/app-template/src/views/category/product-list-registrar.tsx +665 -0
  113. package/app-template/src/views/checkout/auth.tsx +64 -40
  114. package/app-template/src/views/checkout/checkout-address-registrar.tsx +254 -0
  115. package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +183 -0
  116. package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +259 -0
  117. package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +253 -0
  118. package/app-template/src/views/checkout/checkout-summary-registrar.tsx +183 -0
  119. package/app-template/src/views/checkout/constants.ts +5 -0
  120. package/app-template/src/views/checkout/index.tsx +5 -0
  121. package/app-template/src/views/checkout/layout/header.tsx +9 -5
  122. package/app-template/src/views/checkout/steps/payment/index.tsx +5 -2
  123. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +72 -1
  124. package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +15 -0
  125. package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +18 -0
  126. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +171 -40
  127. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +74 -12
  128. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +128 -45
  129. package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +232 -27
  130. package/app-template/src/views/checkout/summary.tsx +303 -29
  131. package/app-template/src/views/footer/footer-app-banner-context.tsx +326 -0
  132. package/app-template/src/views/footer/footer-bottom-context.tsx +215 -0
  133. package/app-template/src/views/footer/footer-bottom-wrapper.tsx +74 -0
  134. package/app-template/src/views/footer/footer-layout-constants.ts +35 -0
  135. package/app-template/src/views/footer/footer-layout-registrar.tsx +342 -0
  136. package/app-template/src/views/footer/footer-layout-switcher.tsx +110 -0
  137. package/app-template/src/views/footer/footer-menu-context.tsx +211 -0
  138. package/app-template/src/views/footer/footer-native-widgets.tsx +60 -0
  139. package/app-template/src/views/footer/footer-social-context.tsx +254 -0
  140. package/app-template/src/views/footer/footer-subscription-context.tsx +210 -0
  141. package/app-template/src/views/footer/footer-utils.ts +43 -0
  142. package/app-template/src/views/footer/footer-value-props-context.tsx +326 -0
  143. package/app-template/src/views/footer/logo-settings.ts +183 -0
  144. package/app-template/src/views/footer/native-widget-config.ts +262 -0
  145. package/app-template/src/views/footer/subscription-settings.ts +122 -0
  146. package/app-template/src/views/footer/use-footer-logo.ts +162 -0
  147. package/app-template/src/views/footer.tsx +415 -13
  148. package/app-template/src/views/guest-login/index.tsx +62 -58
  149. package/app-template/src/views/header/action-menu.tsx +277 -45
  150. package/app-template/src/views/header/band.tsx +6 -21
  151. package/app-template/src/views/header/designer-context.tsx +261 -0
  152. package/app-template/src/views/header/header-announcement-registrar.tsx +267 -0
  153. package/app-template/src/views/header/header-client-wrapper.tsx +496 -0
  154. package/app-template/src/views/header/header-content.tsx +1026 -0
  155. package/app-template/src/views/header/header-currency-registrar.tsx +348 -0
  156. package/app-template/src/views/header/header-icons-context.tsx +262 -0
  157. package/app-template/src/views/header/header-language-registrar.tsx +348 -0
  158. package/app-template/src/views/header/header-layout-context.tsx +143 -0
  159. package/app-template/src/views/header/header-layout-registrar.tsx +658 -0
  160. package/app-template/src/views/header/header-logo-context.tsx +228 -0
  161. package/app-template/src/views/header/header-logo.tsx +118 -0
  162. package/app-template/src/views/header/header-mini-basket-context.tsx +524 -0
  163. package/app-template/src/views/header/header-search-registrar.tsx +511 -0
  164. package/app-template/src/views/header/header-text-slider-registrar.tsx +382 -0
  165. package/app-template/src/views/header/index.tsx +109 -47
  166. package/app-template/src/views/header/inline-search.tsx +262 -0
  167. package/app-template/src/views/header/mini-basket.tsx +819 -44
  168. package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
  169. package/app-template/src/views/header/mobile-menu.tsx +12 -0
  170. package/app-template/src/views/header/navbar-menu-context.tsx +219 -0
  171. package/app-template/src/views/header/navbar.tsx +178 -111
  172. package/app-template/src/views/header/search/index.tsx +71 -32
  173. package/app-template/src/views/header/search/results.tsx +127 -65
  174. package/app-template/src/views/header/search/search-input.tsx +61 -0
  175. package/app-template/src/views/header/server-settings-parser.ts +1105 -0
  176. package/app-template/src/views/header/use-header-icons.ts +241 -0
  177. package/app-template/src/views/header/use-header-logo.ts +213 -0
  178. package/app-template/src/views/header/use-navbar-menu.ts +179 -0
  179. package/app-template/src/views/login/index.tsx +54 -46
  180. package/app-template/src/views/product/accordion-section.tsx +61 -0
  181. package/app-template/src/views/product/accordion-wrapper.tsx +135 -43
  182. package/app-template/src/views/product/custom-button-group.tsx +69 -0
  183. package/app-template/src/views/product/favorites-button-section.tsx +69 -0
  184. package/app-template/src/views/product/find-in-store-section.tsx +60 -0
  185. package/app-template/src/views/product/index.ts +1 -0
  186. package/app-template/src/views/product/layout.tsx +6 -5
  187. package/app-template/src/views/product/misc-buttons.tsx +339 -25
  188. package/app-template/src/views/product/price-wrapper.tsx +3 -29
  189. package/app-template/src/views/product/product-actions.tsx +137 -8
  190. package/app-template/src/views/product/product-info-section.tsx +140 -0
  191. package/app-template/src/views/product/product-info.tsx +69 -31
  192. package/app-template/src/views/product/product-share.tsx +13 -8
  193. package/app-template/src/views/product/product-variants.tsx +2 -2
  194. package/app-template/src/views/product/quantity-section.tsx +73 -0
  195. package/app-template/src/views/product/sale-tag.tsx +10 -0
  196. package/app-template/src/views/product/share-section.tsx +357 -0
  197. package/app-template/src/views/product/slider.tsx +117 -79
  198. package/app-template/src/views/product/variant.tsx +69 -41
  199. package/app-template/src/views/product/variants-section.tsx +126 -0
  200. package/app-template/src/views/product-detail/constants.ts +272 -0
  201. package/app-template/src/views/product-detail/index.ts +10 -0
  202. package/app-template/src/views/product-detail/product-detail-registrar.tsx +616 -0
  203. package/app-template/src/views/product-item/index.tsx +119 -46
  204. package/app-template/src/views/register/index.tsx +14 -25
  205. package/app-template/src/views/share/index.tsx +9 -6
  206. package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
  207. package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
  208. package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
  209. package/app-template/src/widgets/footer-app-banner.tsx +444 -0
  210. package/app-template/src/widgets/footer-bottom.tsx +127 -0
  211. package/app-template/src/widgets/footer-menu-compact.tsx +238 -0
  212. package/app-template/src/widgets/footer-menu-two.tsx +298 -0
  213. package/app-template/src/widgets/footer-social-client.tsx +251 -0
  214. package/app-template/src/widgets/footer-social.tsx +47 -16
  215. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
  216. package/app-template/src/widgets/footer-subscription/index.tsx +183 -17
  217. package/app-template/src/widgets/footer-value-props.tsx +201 -0
  218. package/app-template/src/widgets/index.ts +7 -0
  219. package/app-template/src/widgets/schemas/about-us.json +46 -0
  220. package/app-template/src/widgets/schemas/blog-list.json +37 -0
  221. package/app-template/src/widgets/schemas/blog.json +29 -0
  222. package/app-template/tailwind.config.js +18 -2
  223. package/package.json +1 -1
@@ -0,0 +1,262 @@
1
+ export const FOOTER_PLACEHOLDER_ID = 'footer';
2
+ export const FOOTER_MENU_SECTION_ID = 'footer-menu';
3
+ export const FOOTER_SUBSCRIPTION_SECTION_ID = 'footer-subscription';
4
+ export const FOOTER_SOCIAL_SECTION_ID = 'footer-social';
5
+ export const FOOTER_BOTTOM_SECTION_ID = 'footer-bottom';
6
+ export const FOOTER_APP_BANNER_SECTION_ID = 'footer-app-banner';
7
+ export const FOOTER_VALUE_PROPS_SECTION_ID = 'footer-value-props';
8
+
9
+ export const FOOTER_MENU_WIDGET_SLUG = 'footer-menu-settings-2';
10
+ export const FOOTER_SUBSCRIPTION_WIDGET_SLUG = 'footer-subscription-settings-2';
11
+ export const FOOTER_SOCIAL_WIDGET_SLUG = 'footer-social-settings-2';
12
+ export const FOOTER_BOTTOM_WIDGET_SLUG = 'footer-bottom-settings-2';
13
+ export const FOOTER_APP_BANNER_WIDGET_SLUG = 'footer-app-banner-settings-2';
14
+ export const FOOTER_VALUE_PROPS_WIDGET_SLUG = 'footer-value-props-settings-2';
15
+
16
+ export const FOOTER_MENU_LOGO_BLOCK_ID = 'footer-menu-logo';
17
+ export const FOOTER_MENU_HEADING_BLOCK_ID = 'footer-menu-heading';
18
+ export const FOOTER_MENU_LINK_BLOCK_ID = 'footer-menu-link';
19
+ export const FOOTER_SUBSCRIPTION_TITLE_BLOCK_ID = 'footer-subscription-title';
20
+ export const FOOTER_SUBSCRIPTION_DESCRIPTION_BLOCK_ID =
21
+ 'footer-subscription-description';
22
+ export const FOOTER_SUBSCRIPTION_FORM_BLOCK_ID = 'footer-subscription-form';
23
+ export const FOOTER_SOCIAL_ICONS_BLOCK_ID = 'footer-social-icons';
24
+ export const FOOTER_BOTTOM_LABELS_BLOCK_ID = 'footer-bottom-labels';
25
+ export const FOOTER_BOTTOM_SELECTORS_BLOCK_ID = 'footer-bottom-selectors';
26
+ export const FOOTER_BOTTOM_PAYMENTS_BLOCK_ID = 'footer-bottom-payment-icons';
27
+
28
+ export const FOOTER_APP_BANNER_IMAGE_BLOCK_ID = 'footer-app-banner-image';
29
+ export const FOOTER_APP_BANNER_TITLE_BLOCK_ID = 'footer-app-banner-title';
30
+ export const FOOTER_APP_BANNER_DESCRIPTION_BLOCK_ID =
31
+ 'footer-app-banner-description';
32
+ export const FOOTER_APP_BANNER_BUTTONS_BLOCK_ID = 'footer-app-banner-buttons';
33
+ export const FOOTER_APP_BANNER_APPSTORE_BLOCK_ID = 'footer-app-banner-appstore';
34
+ export const FOOTER_APP_BANNER_PLAYSTORE_BLOCK_ID =
35
+ 'footer-app-banner-playstore';
36
+
37
+ export const FOOTER_VALUE_PROPS_LIST_BLOCK_ID = 'footer-value-props-list';
38
+
39
+ export interface FooterNativeWidgetBlockConfig {
40
+ id: string;
41
+ type: string;
42
+ label: string;
43
+ blocks?: FooterNativeWidgetBlockConfig[];
44
+ }
45
+
46
+ export interface FooterNativeWidgetSectionConfig {
47
+ placeholderId: string;
48
+ sectionId: string;
49
+ label: string;
50
+ widgetSlug: string;
51
+ blocks: FooterNativeWidgetBlockConfig[];
52
+ }
53
+
54
+ export interface FooterNativeWidgetBlock extends FooterNativeWidgetBlockConfig {
55
+ styles?: Record<string, unknown>;
56
+ properties?: Record<string, unknown>;
57
+ value?: unknown;
58
+ blocks?: FooterNativeWidgetBlock[];
59
+ }
60
+
61
+ export interface FooterNativeWidgetSection
62
+ extends FooterNativeWidgetSectionConfig {
63
+ blocks: FooterNativeWidgetBlock[];
64
+ sectionProperties?: Record<string, unknown>;
65
+ sectionStyles?: Record<string, unknown>;
66
+ }
67
+
68
+ export const FOOTER_NATIVE_SECTIONS: FooterNativeWidgetSectionConfig[] = [
69
+ {
70
+ placeholderId: FOOTER_PLACEHOLDER_ID,
71
+ sectionId: FOOTER_MENU_SECTION_ID,
72
+ label: 'Footer Menu',
73
+ widgetSlug: FOOTER_MENU_WIDGET_SLUG,
74
+ blocks: [
75
+ {
76
+ id: 'footer-menu-heading',
77
+ type: 'text',
78
+ label: 'Menu Headings'
79
+ },
80
+ {
81
+ id: 'footer-menu-link',
82
+ type: 'text',
83
+ label: 'Menu Links'
84
+ },
85
+ {
86
+ id: FOOTER_MENU_LOGO_BLOCK_ID,
87
+ type: 'image',
88
+ label: 'Footer Logo'
89
+ }
90
+ ]
91
+ },
92
+ {
93
+ placeholderId: FOOTER_PLACEHOLDER_ID,
94
+ sectionId: FOOTER_SUBSCRIPTION_SECTION_ID,
95
+ label: 'Footer Subscription',
96
+ widgetSlug: FOOTER_SUBSCRIPTION_WIDGET_SLUG,
97
+ blocks: [
98
+ {
99
+ id: FOOTER_SUBSCRIPTION_TITLE_BLOCK_ID,
100
+ type: 'text',
101
+ label: 'Subscription Title'
102
+ },
103
+ {
104
+ id: FOOTER_SUBSCRIPTION_DESCRIPTION_BLOCK_ID,
105
+ type: 'text',
106
+ label: 'Subscription Description'
107
+ },
108
+ {
109
+ id: FOOTER_SUBSCRIPTION_FORM_BLOCK_ID,
110
+ type: 'group',
111
+ label: 'Subscription Form'
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ placeholderId: FOOTER_PLACEHOLDER_ID,
117
+ sectionId: FOOTER_SOCIAL_SECTION_ID,
118
+ label: 'Footer Social Icons',
119
+ widgetSlug: FOOTER_SOCIAL_WIDGET_SLUG,
120
+ blocks: [
121
+ {
122
+ id: FOOTER_SOCIAL_ICONS_BLOCK_ID,
123
+ type: 'social-icons-list',
124
+ label: 'Social Icons'
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ placeholderId: FOOTER_PLACEHOLDER_ID,
130
+ sectionId: FOOTER_BOTTOM_SECTION_ID,
131
+ label: 'Footer Bottom',
132
+ widgetSlug: FOOTER_BOTTOM_WIDGET_SLUG,
133
+ blocks: [
134
+ {
135
+ id: FOOTER_BOTTOM_PAYMENTS_BLOCK_ID,
136
+ type: 'icon-group',
137
+ label: 'Payment Icons'
138
+ }
139
+ ]
140
+ },
141
+ {
142
+ placeholderId: FOOTER_PLACEHOLDER_ID,
143
+ sectionId: FOOTER_APP_BANNER_SECTION_ID,
144
+ label: 'App Banner',
145
+ widgetSlug: FOOTER_APP_BANNER_WIDGET_SLUG,
146
+ blocks: [
147
+ {
148
+ id: FOOTER_APP_BANNER_IMAGE_BLOCK_ID,
149
+ type: 'image',
150
+ label: 'App Image'
151
+ },
152
+ {
153
+ id: FOOTER_APP_BANNER_TITLE_BLOCK_ID,
154
+ type: 'text',
155
+ label: 'Banner Title'
156
+ },
157
+ {
158
+ id: FOOTER_APP_BANNER_DESCRIPTION_BLOCK_ID,
159
+ type: 'text',
160
+ label: 'Banner Description'
161
+ },
162
+ {
163
+ id: FOOTER_APP_BANNER_BUTTONS_BLOCK_ID,
164
+ type: 'group',
165
+ label: 'Store Buttons',
166
+ blocks: [
167
+ {
168
+ id: FOOTER_APP_BANNER_APPSTORE_BLOCK_ID,
169
+ type: 'image',
170
+ label: 'App Store Button'
171
+ },
172
+ {
173
+ id: FOOTER_APP_BANNER_PLAYSTORE_BLOCK_ID,
174
+ type: 'image',
175
+ label: 'Google Play Button'
176
+ }
177
+ ]
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ placeholderId: FOOTER_PLACEHOLDER_ID,
183
+ sectionId: FOOTER_VALUE_PROPS_SECTION_ID,
184
+ label: 'Value Propositions',
185
+ widgetSlug: FOOTER_VALUE_PROPS_WIDGET_SLUG,
186
+ blocks: [
187
+ {
188
+ id: FOOTER_VALUE_PROPS_LIST_BLOCK_ID,
189
+ type: 'value-props-list',
190
+ label: 'Value Props'
191
+ }
192
+ ]
193
+ }
194
+ ];
195
+
196
+ type WidgetAttributes = Record<string, unknown>;
197
+
198
+ type WidgetData = {
199
+ attributes?: WidgetAttributes;
200
+ } | null;
201
+
202
+ type ParsedBlockPayload = {
203
+ styles?: Record<string, unknown>;
204
+ properties?: Record<string, unknown>;
205
+ value?: unknown;
206
+ [key: string]: unknown;
207
+ };
208
+
209
+ export function parseWidgetBlockAttributes(
210
+ widgetData: WidgetData,
211
+ blockId: string
212
+ ): Pick<FooterNativeWidgetBlock, 'styles' | 'properties' | 'value'> {
213
+ const attributes = widgetData?.attributes;
214
+ if (!attributes || !(blockId in attributes)) {
215
+ return {};
216
+ }
217
+
218
+ const attrData = attributes[blockId];
219
+ let parsed: unknown = attrData;
220
+
221
+ if (typeof attrData === 'string') {
222
+ try {
223
+ parsed = JSON.parse(attrData);
224
+ } catch {
225
+ return { value: attrData };
226
+ }
227
+ } else if (
228
+ typeof attrData === 'object' &&
229
+ attrData !== null &&
230
+ 'value' in (attrData as Record<string, unknown>)
231
+ ) {
232
+ const value = (attrData as { value?: unknown }).value;
233
+ if (typeof value === 'string') {
234
+ try {
235
+ parsed = JSON.parse(value);
236
+ } catch {
237
+ return { value };
238
+ }
239
+ } else {
240
+ parsed = value;
241
+ }
242
+ }
243
+
244
+ if (typeof parsed !== 'object' || parsed === null) {
245
+ if (typeof parsed === 'string') {
246
+ return { value: parsed };
247
+ }
248
+ return {};
249
+ }
250
+
251
+ const payload = parsed as ParsedBlockPayload;
252
+ const styles =
253
+ payload.styles ||
254
+ // Legacy format where style props live at root
255
+ (!payload.properties && !('value' in payload) ? payload : undefined);
256
+
257
+ return {
258
+ styles,
259
+ properties: payload.properties,
260
+ value: payload.value
261
+ };
262
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ FOOTER_SUBSCRIPTION_SECTION_ID,
3
+ FOOTER_SUBSCRIPTION_TITLE_BLOCK_ID,
4
+ FOOTER_SUBSCRIPTION_DESCRIPTION_BLOCK_ID,
5
+ FOOTER_SUBSCRIPTION_FORM_BLOCK_ID,
6
+ FOOTER_SUBSCRIPTION_WIDGET_SLUG,
7
+ type FooterNativeWidgetSection,
8
+ type FooterNativeWidgetBlock
9
+ } from './native-widget-config';
10
+
11
+ export type FooterSubscriptionBlockSnapshot = Array<FooterNativeWidgetBlock>;
12
+
13
+ export const FOOTER_SUBSCRIPTION_DEFAULT_CONTENT = {
14
+ title: 'Keep up with our latest news',
15
+ description:
16
+ 'Subscribe to our newsletter to hear about new arrivals before anyone else.'
17
+ } as const;
18
+
19
+ export const FOOTER_SUBSCRIPTION_BLOCKS = {
20
+ TITLE: {
21
+ id: FOOTER_SUBSCRIPTION_TITLE_BLOCK_ID,
22
+ type: 'text',
23
+ label: 'Subscription Title'
24
+ },
25
+ DESCRIPTION: {
26
+ id: FOOTER_SUBSCRIPTION_DESCRIPTION_BLOCK_ID,
27
+ type: 'text',
28
+ label: 'Subscription Description'
29
+ },
30
+ FORM: {
31
+ id: FOOTER_SUBSCRIPTION_FORM_BLOCK_ID,
32
+ type: 'group',
33
+ label: 'Subscription Form'
34
+ }
35
+ } as const;
36
+
37
+ const FOOTER_SUBSCRIPTION_BLOCK_LIST = Object.values(
38
+ FOOTER_SUBSCRIPTION_BLOCKS
39
+ );
40
+
41
+ const FOOTER_SUBSCRIPTION_BLOCK_ID_SET = new Set<string>(
42
+ FOOTER_SUBSCRIPTION_BLOCK_LIST.map((block) => block.id)
43
+ );
44
+
45
+ const createTextValue = (text?: string) => {
46
+ if (!text) {
47
+ return undefined;
48
+ }
49
+
50
+ return text;
51
+ };
52
+
53
+ export function getFooterSubscriptionInitialBlocks(
54
+ sections: FooterNativeWidgetSection[]
55
+ ): FooterSubscriptionBlockSnapshot {
56
+ const section = sections.find(
57
+ (item) => item.sectionId === FOOTER_SUBSCRIPTION_SECTION_ID
58
+ );
59
+
60
+ if (!section) {
61
+ return [];
62
+ }
63
+
64
+ return section.blocks.map((block) => ({
65
+ id: block.id,
66
+ type: block.type,
67
+ label: block.label,
68
+ styles: block.styles,
69
+ properties: block.properties,
70
+ value: block.value
71
+ }));
72
+ }
73
+
74
+ type FooterSubscriptionContent = {
75
+ title?: string;
76
+ description?: string;
77
+ };
78
+
79
+ export function mergeFooterSubscriptionContent(
80
+ blocks: FooterSubscriptionBlockSnapshot,
81
+ content: FooterSubscriptionContent
82
+ ): FooterSubscriptionBlockSnapshot {
83
+ const blockMap = new Map<string, FooterNativeWidgetBlock>();
84
+ blocks.forEach((block) => {
85
+ blockMap.set(block.id, { ...block });
86
+ });
87
+
88
+ const normalizedBlocks = FOOTER_SUBSCRIPTION_BLOCK_LIST.map((config) => {
89
+ const existing = blockMap.get(config.id) ?? {
90
+ id: config.id,
91
+ type: config.type,
92
+ label: config.label
93
+ };
94
+
95
+ if (existing.value == null) {
96
+ const fallbackText =
97
+ config.id === FOOTER_SUBSCRIPTION_TITLE_BLOCK_ID
98
+ ? content.title
99
+ : config.id === FOOTER_SUBSCRIPTION_DESCRIPTION_BLOCK_ID
100
+ ? content.description
101
+ : undefined;
102
+
103
+ const fallbackValue = createTextValue(fallbackText);
104
+ if (fallbackValue) {
105
+ return {
106
+ ...existing,
107
+ value: fallbackValue
108
+ };
109
+ }
110
+ }
111
+
112
+ return existing;
113
+ });
114
+
115
+ const extraBlocks = blocks.filter(
116
+ (block) => !FOOTER_SUBSCRIPTION_BLOCK_ID_SET.has(block.id)
117
+ );
118
+
119
+ return [...normalizedBlocks, ...extraBlocks];
120
+ }
121
+
122
+ export { FOOTER_SUBSCRIPTION_WIDGET_SLUG };
@@ -0,0 +1,162 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
5
+ import { useGetWidgetQuery } from '@akinon/next/data/client/misc';
6
+ import {
7
+ defaultFooterLogoSettings,
8
+ FooterLogoSettings,
9
+ parseFooterLogoBlockPayload,
10
+ FOOTER_LOGO_PLACEHOLDER_ID,
11
+ FOOTER_LOGO_SECTION_ID,
12
+ FOOTER_LOGO_BLOCK_ID,
13
+ FOOTER_LOGO_WIDGET_SLUG
14
+ } from './logo-settings';
15
+
16
+ const THEME_CONFIG_SLUG = 'theme-config';
17
+
18
+ const BLOCK_META = [
19
+ { id: FOOTER_LOGO_BLOCK_ID, type: 'image', label: 'Footer Logo' }
20
+ ];
21
+
22
+ type ThemeSettings = {
23
+ logo?: string;
24
+ };
25
+
26
+ const parseThemeConfigLogo = (
27
+ themeSettings?: ThemeSettings
28
+ ): Partial<FooterLogoSettings> => {
29
+ if (!themeSettings?.logo) {
30
+ return {};
31
+ }
32
+ return { src: themeSettings.logo };
33
+ };
34
+
35
+ export function useFooterLogoSettings(
36
+ initialSettings?: FooterLogoSettings
37
+ ): FooterLogoSettings {
38
+ const isDesignerRef = useRef(false);
39
+ const [isDesignerChecked, setIsDesignerChecked] = useState(false);
40
+
41
+ useEffect(() => {
42
+ if (typeof window === 'undefined') return;
43
+ isDesignerRef.current = window.self !== window.top;
44
+ setIsDesignerChecked(true);
45
+ }, []);
46
+
47
+ const isDesigner = isDesignerRef.current;
48
+
49
+ const { data: themeConfigData } = useGetWidgetQuery(THEME_CONFIG_SLUG, {
50
+ skip: !isDesignerChecked || isDesigner
51
+ });
52
+
53
+ const widgetData = useNativeWidgetData({
54
+ widgetSlug: FOOTER_LOGO_WIDGET_SLUG,
55
+ sectionId: FOOTER_LOGO_SECTION_ID,
56
+ skip: !isDesignerChecked || isDesigner,
57
+ blockMeta: BLOCK_META
58
+ });
59
+
60
+ const [settings, setSettings] = useState<FooterLogoSettings>(
61
+ initialSettings || defaultFooterLogoSettings
62
+ );
63
+ const hasInitialSettings = !!initialSettings;
64
+
65
+ useEffect(() => {
66
+ if (!isDesignerChecked) return;
67
+ if (isDesigner) return;
68
+ if (hasInitialSettings) return;
69
+ if (widgetData.isLoading && !themeConfigData) return;
70
+
71
+ let nextSettings: FooterLogoSettings = { ...defaultFooterLogoSettings };
72
+
73
+ const themeSettingsAttr = themeConfigData?.attributes?.theme_settings;
74
+ if (themeSettingsAttr) {
75
+ try {
76
+ let parsedSettings: ThemeSettings | undefined;
77
+
78
+ if (typeof themeSettingsAttr === 'string') {
79
+ parsedSettings = JSON.parse(themeSettingsAttr) as ThemeSettings;
80
+ } else if (
81
+ typeof themeSettingsAttr === 'object' &&
82
+ 'value' in themeSettingsAttr
83
+ ) {
84
+ const rawValue = themeSettingsAttr.value;
85
+ if (typeof rawValue === 'string') {
86
+ parsedSettings = JSON.parse(rawValue) as ThemeSettings;
87
+ } else if (rawValue && typeof rawValue === 'object') {
88
+ parsedSettings = rawValue as ThemeSettings;
89
+ }
90
+ } else if (typeof themeSettingsAttr === 'object') {
91
+ parsedSettings = themeSettingsAttr as ThemeSettings;
92
+ }
93
+
94
+ if (parsedSettings) {
95
+ nextSettings = {
96
+ ...nextSettings,
97
+ ...parseThemeConfigLogo(parsedSettings)
98
+ };
99
+ }
100
+ } catch {
101
+ // ignore
102
+ }
103
+ }
104
+
105
+ const block = widgetData.getBlock(FOOTER_LOGO_BLOCK_ID);
106
+ if (block) {
107
+ const overrides = parseFooterLogoBlockPayload(block);
108
+ nextSettings = {
109
+ ...nextSettings,
110
+ ...overrides,
111
+ src: overrides.src ?? nextSettings.src
112
+ };
113
+ }
114
+
115
+ setSettings(nextSettings);
116
+ }, [
117
+ widgetData,
118
+ themeConfigData,
119
+ hasInitialSettings,
120
+ isDesigner,
121
+ isDesignerChecked
122
+ ]);
123
+
124
+ useEffect(() => {
125
+ const handleMessage = (event: MessageEvent) => {
126
+ const { type, data } = event.data || {};
127
+
128
+ if (
129
+ (type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
130
+ data?.theme?.placeholders
131
+ ) {
132
+ const placeholder = data.theme.placeholders.find(
133
+ (p: { slug: string }) => p.slug === FOOTER_LOGO_PLACEHOLDER_ID
134
+ );
135
+ const section = placeholder?.sections?.find(
136
+ (s: { id: string }) => s.id === FOOTER_LOGO_SECTION_ID
137
+ );
138
+ const block = section?.blocks?.find(
139
+ (b: { id: string }) => b.id === FOOTER_LOGO_BLOCK_ID
140
+ );
141
+
142
+ if (block) {
143
+ const overrides = parseFooterLogoBlockPayload(block);
144
+ if (Object.keys(overrides).length > 0) {
145
+ setSettings((prev) => ({
146
+ ...prev,
147
+ ...overrides,
148
+ src: overrides.src ?? prev.src
149
+ }));
150
+ }
151
+ }
152
+ }
153
+ };
154
+
155
+ window.addEventListener('message', handleMessage);
156
+ return () => window.removeEventListener('message', handleMessage);
157
+ }, []);
158
+
159
+ return settings;
160
+ }
161
+
162
+ export type { FooterLogoSettings };