@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
@@ -1,21 +1,423 @@
1
1
  import 'server-only';
2
2
 
3
- import FooterCopyright from '@theme/widgets/footer-copyright';
4
- import FooterInfo from '@theme/widgets/footer-info';
5
- import FooterMenu from '@theme/widgets/footer-menu';
3
+ import FooterBottom from '@theme/widgets/footer-bottom';
6
4
  import FooterSocial from '@theme/widgets/footer-social';
7
5
  import FooterSubscription from '@theme/widgets/footer-subscription';
6
+ import FooterMenuTwo from '@theme/widgets/footer-menu-two';
7
+ import FooterMenuCompact from '@theme/widgets/footer-menu-compact';
8
+ import FooterAppBanner from '@theme/widgets/footer-app-banner';
9
+ import FooterValueProps from '@theme/widgets/footer-value-props';
10
+ import { getWidgetData } from '@akinon/next/data/server';
11
+ import { getThemeSettings } from '@theme/data/server/theme';
12
+ import FooterNativeWidgets from './footer/footer-native-widgets';
13
+ import FooterLayoutSwitcher from './footer/footer-layout-switcher';
14
+ import FooterLayoutRegistrar from './footer/footer-layout-registrar';
15
+ import FooterBottomWrapper from './footer/footer-bottom-wrapper';
16
+ import {
17
+ FOOTER_NATIVE_SECTIONS,
18
+ FOOTER_SUBSCRIPTION_SECTION_ID,
19
+ FOOTER_MENU_SECTION_ID,
20
+ FOOTER_SOCIAL_SECTION_ID,
21
+ FOOTER_BOTTOM_SECTION_ID,
22
+ FOOTER_APP_BANNER_SECTION_ID,
23
+ FOOTER_VALUE_PROPS_SECTION_ID,
24
+ FOOTER_SOCIAL_ICONS_BLOCK_ID,
25
+ type FooterNativeWidgetSection,
26
+ parseWidgetBlockAttributes
27
+ } from './footer/native-widget-config';
28
+ import { getFooterLogoInitialSettingsFromSections } from './footer/logo-settings';
29
+ import { FooterSubscriptionProvider } from './footer/footer-subscription-context';
30
+ import { FooterMenuProvider } from './footer/footer-menu-context';
31
+ import { FooterSocialProvider, type SocialIconItem } from './footer/footer-social-context';
32
+ import { FooterBottomProvider } from './footer/footer-bottom-context';
33
+ import { FooterAppBannerProvider } from './footer/footer-app-banner-context';
34
+ import { FooterValuePropsProvider } from './footer/footer-value-props-context';
35
+ import {
36
+ FOOTER_SUBSCRIPTION_DEFAULT_CONTENT,
37
+ getFooterSubscriptionInitialBlocks,
38
+ mergeFooterSubscriptionContent
39
+ } from './footer/subscription-settings';
40
+ import {
41
+ FOOTER_LAYOUT_SECTION_ID,
42
+ FOOTER_LAYOUT_WIDGET_SLUG,
43
+ FOOTER_LAYOUT_BLOCK_IDS,
44
+ type FooterLayoutType
45
+ } from './footer/footer-layout-constants';
46
+
47
+ // Block styles type for footer layout
48
+ type FooterBlockStyles = Record<string, Record<string, unknown>>;
49
+
50
+ /**
51
+ * Parse footer layout settings from widget data (server-side)
52
+ */
53
+ function parseFooterLayoutSettings(
54
+ widgetData: { attributes?: Record<string, unknown> } | null
55
+ ): { layout: FooterLayoutType; blockStyles: FooterBlockStyles } {
56
+ const defaultResult = { layout: 'default' as FooterLayoutType, blockStyles: {} };
57
+
58
+ if (!widgetData?.attributes) return defaultResult;
59
+
60
+ const result: { layout: FooterLayoutType; blockStyles: FooterBlockStyles } = {
61
+ layout: 'default',
62
+ blockStyles: {}
63
+ };
64
+
65
+ // Parse section-level properties (layout)
66
+ const sectionData = widgetData.attributes[FOOTER_LAYOUT_SECTION_ID];
67
+ if (sectionData) {
68
+ try {
69
+ const data =
70
+ typeof sectionData === 'string'
71
+ ? JSON.parse(sectionData)
72
+ : typeof sectionData === 'object' && sectionData !== null && 'value' in sectionData
73
+ ? JSON.parse((sectionData as { value: string }).value)
74
+ : null;
75
+
76
+ if (data?.properties?.layout) {
77
+ const layout = data.properties.layout;
78
+ result.layout = (typeof layout === 'object' ? layout.desktop : layout) as FooterLayoutType;
79
+ }
80
+ } catch {
81
+ // Ignore parse errors
82
+ }
83
+ }
84
+
85
+ // Parse block styles (MAIN_ROW, BOTTOM_ROW)
86
+ Object.values(FOOTER_LAYOUT_BLOCK_IDS).forEach((blockId) => {
87
+ const blockData = widgetData.attributes?.[blockId];
88
+ if (!blockData) return;
89
+
90
+ try {
91
+ const data =
92
+ typeof blockData === 'string'
93
+ ? JSON.parse(blockData)
94
+ : typeof blockData === 'object' && blockData !== null && 'value' in blockData
95
+ ? JSON.parse((blockData as { value: string }).value)
96
+ : null;
97
+
98
+ if (data?.styles) {
99
+ // Convert responsive styles to flat styles (desktop first)
100
+ const flatStyles: Record<string, string> = {};
101
+ Object.entries(data.styles).forEach(([key, value]) => {
102
+ if (typeof value === 'object' && value !== null) {
103
+ const responsiveValue = value as Record<string, string>;
104
+ flatStyles[key] =
105
+ responsiveValue.desktop ||
106
+ responsiveValue.mobile ||
107
+ Object.values(responsiveValue)[0] ||
108
+ '';
109
+ } else if (typeof value === 'string') {
110
+ flatStyles[key] = value;
111
+ }
112
+ });
113
+
114
+ if (Object.keys(flatStyles).length > 0) {
115
+ result.blockStyles[blockId] = flatStyles;
116
+ }
117
+ }
118
+ } catch {
119
+ // Ignore parse errors
120
+ }
121
+ });
122
+
123
+ return result;
124
+ }
125
+
126
+ function getFooterMenuInitialBlocks(
127
+ sections: FooterNativeWidgetSection[]
128
+ ): FooterNativeWidgetSection['blocks'] {
129
+ const menuSection = sections.find(
130
+ (s) => s.sectionId === FOOTER_MENU_SECTION_ID
131
+ );
132
+ return menuSection?.blocks ?? [];
133
+ }
134
+
135
+ function getFooterSocialInitialBlocks(
136
+ sections: FooterNativeWidgetSection[],
137
+ defaultIcons?: SocialIconItem[]
138
+ ): FooterNativeWidgetSection['blocks'] {
139
+ const socialSection = sections.find(
140
+ (s) => s.sectionId === FOOTER_SOCIAL_SECTION_ID
141
+ );
142
+ const blocks = socialSection?.blocks ?? [];
143
+
144
+ // If we have default icons, merge them into the footer-social-icons block
145
+ if (defaultIcons?.length) {
146
+ return blocks.map((block) => {
147
+ if (block.id === FOOTER_SOCIAL_ICONS_BLOCK_ID) {
148
+ const existingIcons = (block.properties as Record<string, unknown>)?.icons;
149
+ // Only set default icons if no icons are already configured
150
+ if (!existingIcons || (Array.isArray(existingIcons) && existingIcons.length === 0)) {
151
+ return {
152
+ ...block,
153
+ properties: {
154
+ ...block.properties,
155
+ icons: defaultIcons
156
+ }
157
+ };
158
+ }
159
+ }
160
+ return block;
161
+ });
162
+ }
163
+
164
+ return blocks;
165
+ }
166
+
167
+ function getFooterBottomInitialBlocks(
168
+ sections: FooterNativeWidgetSection[]
169
+ ): FooterNativeWidgetSection['blocks'] {
170
+ const bottomSection = sections.find(
171
+ (s) => s.sectionId === FOOTER_BOTTOM_SECTION_ID
172
+ );
173
+ return bottomSection?.blocks ?? [];
174
+ }
175
+
176
+ function getFooterAppBannerInitialData(
177
+ sections: FooterNativeWidgetSection[]
178
+ ): {
179
+ blocks: FooterNativeWidgetSection['blocks'];
180
+ properties: Record<string, unknown>;
181
+ styles: Record<string, unknown>;
182
+ } {
183
+ const section = sections.find(
184
+ (s) => s.sectionId === FOOTER_APP_BANNER_SECTION_ID
185
+ );
186
+ return {
187
+ blocks: section?.blocks ?? [],
188
+ properties: section?.sectionProperties ?? {},
189
+ styles: section?.sectionStyles ?? {}
190
+ };
191
+ }
192
+
193
+ function getFooterValuePropsInitialData(
194
+ sections: FooterNativeWidgetSection[]
195
+ ): {
196
+ blocks: FooterNativeWidgetSection['blocks'];
197
+ properties: Record<string, unknown>;
198
+ styles: Record<string, unknown>;
199
+ } {
200
+ const section = sections.find(
201
+ (s) => s.sectionId === FOOTER_VALUE_PROPS_SECTION_ID
202
+ );
203
+ return {
204
+ blocks: section?.blocks ?? [],
205
+ properties: section?.sectionProperties ?? {},
206
+ styles: section?.sectionStyles ?? {}
207
+ };
208
+ }
209
+
210
+ async function getFooterNativeWidgetSections() {
211
+ const sections = await Promise.all(
212
+ FOOTER_NATIVE_SECTIONS.map(async section => {
213
+ let widgetData: Awaited<ReturnType<typeof getWidgetData>> | null = null;
214
+
215
+ try {
216
+ widgetData = await getWidgetData({ slug: section.widgetSlug });
217
+ } catch {
218
+ widgetData = null;
219
+ }
220
+
221
+ const blocks = section.blocks.map(block => ({
222
+ ...block,
223
+ ...parseWidgetBlockAttributes(widgetData as { attributes?: Record<string, unknown> }, block.id)
224
+ }));
225
+
226
+ const sectionAttrs = parseWidgetBlockAttributes(
227
+ widgetData as { attributes?: Record<string, unknown> },
228
+ section.sectionId
229
+ );
230
+
231
+ return {
232
+ ...section,
233
+ blocks,
234
+ sectionProperties: sectionAttrs.properties,
235
+ sectionStyles: sectionAttrs.styles
236
+ } satisfies FooterNativeWidgetSection;
237
+ })
238
+ );
239
+
240
+ return sections;
241
+ }
242
+
243
+ export default async function Footer() {
244
+ const [footerNativeSections, themeSettings, footerSubscriptionData, footerLayoutSettingsData] =
245
+ await Promise.all([
246
+ getFooterNativeWidgetSections(),
247
+ getThemeSettings(),
248
+ getWidgetData({ slug: 'footer-subscription' }),
249
+ getWidgetData({ slug: FOOTER_LAYOUT_WIDGET_SLUG }).catch(() => null)
250
+ ]);
251
+ const { layout: initialFooterLayout, blockStyles: initialBlockStyles } = parseFooterLayoutSettings(footerLayoutSettingsData);
252
+ const fallbackLogoSrc = themeSettings.logo || '/logo.svg';
253
+ const initialFooterLogoSettings = getFooterLogoInitialSettingsFromSections(
254
+ footerNativeSections,
255
+ fallbackLogoSrc
256
+ );
257
+ const footerSubscriptionTitle =
258
+ (footerSubscriptionData?.attributes as Record<string, { value?: string }> | undefined)?.title?.value || '';
259
+ const footerSubscriptionDescription =
260
+ (footerSubscriptionData?.attributes as Record<string, { value?: string }> | undefined)?.description?.value || '';
261
+ const initialFooterSubscriptionBlocks = mergeFooterSubscriptionContent(
262
+ getFooterSubscriptionInitialBlocks(footerNativeSections),
263
+ {
264
+ title:
265
+ footerSubscriptionTitle ||
266
+ FOOTER_SUBSCRIPTION_DEFAULT_CONTENT.title,
267
+ description:
268
+ footerSubscriptionDescription ||
269
+ FOOTER_SUBSCRIPTION_DEFAULT_CONTENT.description
270
+ }
271
+ );
272
+
273
+ // Convert theme-config socialNetworks to SocialIconItem format for editor
274
+ // Must be calculated before getFooterSocialInitialBlocks to pass as default
275
+ const defaultSocialIcons: SocialIconItem[] = Object.entries(themeSettings.socialNetworks || {})
276
+ .filter(([, value]: [string, unknown]) => {
277
+ const v = value as { icon?: string };
278
+ return v.icon;
279
+ })
280
+ .map(([key, value]: [string, unknown]) => {
281
+ const v = value as { url?: string };
282
+ return {
283
+ icon: key,
284
+ link: v.url || '#',
285
+ size: 24,
286
+ color: undefined
287
+ };
288
+ });
289
+
290
+ const initialFooterSocialBlocks = getFooterSocialInitialBlocks(
291
+ footerNativeSections,
292
+ defaultSocialIcons
293
+ );
294
+
295
+ const footerNativeSectionsWithContent = footerNativeSections.map(
296
+ (section) => {
297
+ if (section.sectionId === FOOTER_SUBSCRIPTION_SECTION_ID) {
298
+ return { ...section, blocks: initialFooterSubscriptionBlocks };
299
+ }
300
+ if (section.sectionId === FOOTER_SOCIAL_SECTION_ID) {
301
+ return { ...section, blocks: initialFooterSocialBlocks };
302
+ }
303
+ if (section.sectionId === FOOTER_APP_BANNER_SECTION_ID) {
304
+ return section; // App Banner blocks come from FOOTER_NATIVE_SECTIONS
305
+ }
306
+ if (section.sectionId === FOOTER_VALUE_PROPS_SECTION_ID) {
307
+ return section; // Value Props blocks come from FOOTER_NATIVE_SECTIONS
308
+ }
309
+ return section;
310
+ }
311
+ );
312
+ const initialFooterMenuBlocks = getFooterMenuInitialBlocks(
313
+ footerNativeSectionsWithContent
314
+ );
315
+ const initialFooterBottomBlocks = getFooterBottomInitialBlocks(
316
+ footerNativeSectionsWithContent
317
+ );
318
+ const initialAppBannerData = getFooterAppBannerInitialData(
319
+ footerNativeSectionsWithContent
320
+ );
321
+ const initialValuePropsData = getFooterValuePropsInitialData(
322
+ footerNativeSectionsWithContent
323
+ );
8
324
 
9
- export default function Footer() {
10
325
  return (
11
- <div className="lg:pt-16 lg:border-t lg:border-gray">
12
- <div className="container px-4 pb-0 mx-auto md:flex md:flex-wrap lg:pb-16 lg:px-0">
13
- <FooterInfo />
14
- <FooterMenu />
15
- <FooterSubscription />
16
- </div>
17
- <FooterSocial />
18
- <FooterCopyright />
19
- </div>
326
+ <footer className="px-4 pt-16 border-t border-[#d0d0d0] lg:px-0">
327
+ <FooterNativeWidgets sections={footerNativeSectionsWithContent} />
328
+
329
+ <FooterValuePropsProvider
330
+ initialBlocks={initialValuePropsData.blocks}
331
+ initialProperties={initialValuePropsData.properties}
332
+ initialStyles={initialValuePropsData.styles}
333
+ >
334
+ <FooterValueProps renderPosition="footer-top" />
335
+ </FooterValuePropsProvider>
336
+
337
+ <FooterAppBannerProvider
338
+ initialBlocks={initialAppBannerData.blocks}
339
+ initialProperties={initialAppBannerData.properties}
340
+ initialStyles={initialAppBannerData.styles}
341
+ >
342
+ <FooterAppBanner renderPosition="footer-top" />
343
+ </FooterAppBannerProvider>
344
+
345
+ <FooterLayoutRegistrar
346
+ initialLayout={initialFooterLayout}
347
+ initialBlockStyles={initialBlockStyles}
348
+ >
349
+ <FooterLayoutSwitcher
350
+ defaultLayout={
351
+ <>
352
+ <FooterMenuProvider initialBlocks={initialFooterMenuBlocks}>
353
+ <FooterMenuTwo
354
+ fallbackLogoSrc={fallbackLogoSrc}
355
+ initialLogoSettings={initialFooterLogoSettings}
356
+ />
357
+ </FooterMenuProvider>
358
+ <div className="container flex flex-col py-16 gap-10 lg:items-center lg:justify-between lg:flex-row">
359
+ <FooterSubscriptionProvider
360
+ initialBlocks={initialFooterSubscriptionBlocks}
361
+ >
362
+ <FooterSubscription
363
+ title={footerSubscriptionTitle}
364
+ description={footerSubscriptionDescription}
365
+ />
366
+ </FooterSubscriptionProvider>
367
+ <FooterSocialProvider initialBlocks={initialFooterSocialBlocks} defaultIcons={defaultSocialIcons}>
368
+ <FooterSocial />
369
+ </FooterSocialProvider>
370
+ </div>
371
+ </>
372
+ }
373
+ compactLayout={
374
+ <div className="container flex flex-col lg:flex-row gap-10 lg:gap-16 py-8">
375
+ {/* Menu on the left - takes more space */}
376
+ <div className="flex-1 lg:flex-[2]">
377
+ <FooterMenuProvider initialBlocks={initialFooterMenuBlocks}>
378
+ <FooterMenuCompact />
379
+ </FooterMenuProvider>
380
+ </div>
381
+
382
+ {/* Subscription + Social stacked on the right */}
383
+ <div className="flex flex-col gap-8 lg:flex-1">
384
+ <FooterSubscriptionProvider
385
+ initialBlocks={initialFooterSubscriptionBlocks}
386
+ >
387
+ <FooterSubscription
388
+ title={footerSubscriptionTitle}
389
+ description={footerSubscriptionDescription}
390
+ />
391
+ </FooterSubscriptionProvider>
392
+ <FooterSocialProvider initialBlocks={initialFooterSocialBlocks} defaultIcons={defaultSocialIcons}>
393
+ <FooterSocial />
394
+ </FooterSocialProvider>
395
+ </div>
396
+ </div>
397
+ }
398
+ />
399
+ <FooterBottomWrapper>
400
+ <FooterBottomProvider initialBlocks={initialFooterBottomBlocks}>
401
+ <FooterBottom />
402
+ </FooterBottomProvider>
403
+ </FooterBottomWrapper>
404
+ </FooterLayoutRegistrar>
405
+
406
+ <FooterValuePropsProvider
407
+ initialBlocks={initialValuePropsData.blocks}
408
+ initialProperties={initialValuePropsData.properties}
409
+ initialStyles={initialValuePropsData.styles}
410
+ >
411
+ <FooterValueProps renderPosition="footer-bottom" />
412
+ </FooterValuePropsProvider>
413
+
414
+ <FooterAppBannerProvider
415
+ initialBlocks={initialAppBannerData.blocks}
416
+ initialProperties={initialAppBannerData.properties}
417
+ initialStyles={initialAppBannerData.styles}
418
+ >
419
+ <FooterAppBanner renderPosition="footer-bottom" />
420
+ </FooterAppBannerProvider>
421
+ </footer>
20
422
  );
21
423
  }
@@ -62,68 +62,72 @@ const GuestLogin = () => {
62
62
  };
63
63
 
64
64
  return (
65
- <form onSubmit={handleSubmit(onSubmit)}>
66
- <div className={clsx(errors.user_email ? 'mb-8' : 'mb-4')}>
67
- <Input
68
- labelStyle="floating"
69
- label={t('checkout.auth.form.login.email.placeholder')}
70
- name="email"
71
- className="h-14"
72
- {...register('user_email')}
73
- error={errors.user_email}
74
- data-testid="guest-email"
75
- required
76
- />
77
- </div>
65
+ <section className="w-full mt-6 lg:mt-8">
66
+ <form onSubmit={handleSubmit(onSubmit)}>
67
+ <div>
68
+ <Input
69
+ labelStyle="floating"
70
+ label={t('checkout.auth.form.login.email.placeholder')}
71
+ name="email"
72
+ className="h-12"
73
+ {...register('user_email')}
74
+ error={errors.user_email}
75
+ data-testid="guest-email"
76
+ required
77
+ />
78
+ </div>
78
79
 
79
- <div className={clsx(errors.phone_number ? 'mb-8' : 'mb-4')}>
80
- <Input
81
- labelStyle="floating"
82
- label={t('checkout.auth.form.login.phone.placeholder')}
83
- className="h-14"
84
- name="phone_number"
85
- type="tel"
86
- format={user_phone_format.replace(/9/g, '#')}
87
- mask="_"
88
- control={control}
89
- {...register('phone_number')}
90
- error={errors.phone_number}
91
- data-testid="guest-phone"
92
- required
93
- />
94
- </div>
80
+ <div className="mt-4">
81
+ <Input
82
+ labelStyle="floating"
83
+ label={t('checkout.auth.form.login.phone.placeholder')}
84
+ className="h-12"
85
+ name="phone_number"
86
+ type="tel"
87
+ format={user_phone_format.replace(/\9/g, '#')}
88
+ mask="_"
89
+ control={control}
90
+ {...register('phone_number')}
91
+ error={errors.phone_number}
92
+ data-testid="guest-phone"
93
+ required
94
+ />
95
+ </div>
95
96
 
96
- <Button
97
- type="submit"
98
- className="w-full h-12 uppercase text-xs font-semibold mb-4"
99
- data-testid="guest-submit"
100
- >
101
- {t('checkout.auth.form.login.button')}
102
- </Button>
103
-
104
- <div className="text-sm text-black-400 md:text-xs">
105
- <p className="mb-4">{t('checkout.auth.form.login.agreement.label')}</p>
106
- <Checkbox
107
- name="sms_allowed"
108
- className={clsx('underline', errors.sms_allowed ? 'mb-8' : 'mb-4')}
109
- {...register('sms_allowed')}
110
- error={errors.sms_allowed}
111
- data-testid="guest-sms"
97
+ <Button
98
+ type="submit"
99
+ className="w-full h-12 uppercase text-xs font-semibold mt-[38px]"
100
+ data-testid="guest-submit"
112
101
  >
113
- {t('checkout.auth.form.login.agreement.terms_conditions')}
114
- </Checkbox>
102
+ {t('checkout.auth.form.login.button')}
103
+ </Button>
115
104
 
116
- <Checkbox
117
- name="confirm"
118
- className={clsx('underline', errors.kvkk_confirm ? 'mb-8' : 'mb-4')}
119
- {...register('kvkk_confirm')}
120
- error={errors.kvkk_confirm}
121
- data-testid="guest-kvkk"
122
- >
123
- {t('checkout.auth.form.login.agreement.privacy_policy')}
124
- </Checkbox>
125
- </div>
126
- </form>
105
+ <div className="mt-[38px] text-sm text-black-400 md:text-xs">
106
+ <p className="mb-4">
107
+ {t('checkout.auth.form.login.agreement.label')}
108
+ </p>
109
+ <Checkbox
110
+ name="sms_allowed"
111
+ className="underline"
112
+ {...register('sms_allowed')}
113
+ error={errors.sms_allowed}
114
+ data-testid="guest-sms"
115
+ >
116
+ {t('checkout.auth.form.login.agreement.terms_conditions')}
117
+ </Checkbox>
118
+
119
+ <Checkbox
120
+ name="confirm"
121
+ className="underline mt-4"
122
+ {...register('kvkk_confirm')}
123
+ error={errors.kvkk_confirm}
124
+ data-testid="guest-kvkk"
125
+ >
126
+ {t('checkout.auth.form.login.agreement.privacy_policy')}
127
+ </Checkbox>
128
+ </div>
129
+ </form>
130
+ </section>
127
131
  );
128
132
  };
129
133