@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,323 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { WidgetContentProps } from '../types/widget';
5
+ import clsx from 'clsx';
6
+ import { getResponsiveStyle, ResponsiveSize } from '../utils/styles';
7
+ import { ActionTooltip } from '@theme/components';
8
+
9
+ export function WidgetContent({
10
+ widgetData,
11
+ skipParentWrapper = false,
12
+ onWidgetClick,
13
+ selectedWidget,
14
+ designMode,
15
+ responsive,
16
+ draggable,
17
+ onDragStart,
18
+ onDragOver,
19
+ onDragLeave,
20
+ onDrop
21
+ }: WidgetContentProps) {
22
+ const componentId = Object.keys(widgetData.attributes)[0];
23
+ const componentData = widgetData.attributes[componentId];
24
+ const parentData = widgetData.parent;
25
+
26
+ const combinedStyles = useMemo(
27
+ () =>
28
+ getResponsiveStyle(
29
+ componentData?.style || {},
30
+ responsive as ResponsiveSize
31
+ ),
32
+ [componentData?.style, responsive]
33
+ );
34
+
35
+ if (!componentData) return null;
36
+
37
+ const parentStyles = parentData?.style
38
+ ? Object.keys(parentData.style).reduce(
39
+ (acc, breakpoint) => ({
40
+ ...acc,
41
+ ...parentData.style[breakpoint]
42
+ }),
43
+ {}
44
+ )
45
+ : {};
46
+
47
+ const getClickableStyles = (isSelected: boolean) => {
48
+ const baseComponentId = componentId.split('-')[0];
49
+ const baseSelectedId = selectedWidget?.id?.split('-')[0];
50
+
51
+ return clsx(
52
+ designMode && [
53
+ 'cursor-pointer',
54
+ (isSelected || baseComponentId === baseSelectedId) &&
55
+ 'outline outline-2 outline-[#3B82F6] outline-offset-2'
56
+ ]
57
+ );
58
+ };
59
+
60
+ const renderActionTooltip = () => {
61
+ if (!designMode || !selectedWidget || selectedWidget.id !== componentId) {
62
+ return null;
63
+ }
64
+
65
+ return (
66
+ <ActionTooltip
67
+ widgetData={widgetData}
68
+ selectedWidget={selectedWidget}
69
+ designMode={designMode}
70
+ onDelete={() => {
71
+ window.parent.postMessage(
72
+ {
73
+ type: 'WIDGET_DELETE',
74
+ data: { id: componentId }
75
+ },
76
+ '*'
77
+ );
78
+ }}
79
+ onMoveUp={() => {
80
+ window.parent.postMessage(
81
+ {
82
+ type: 'WIDGET_MOVE',
83
+ data: { id: componentId, direction: 'up' }
84
+ },
85
+ '*'
86
+ );
87
+ }}
88
+ onMoveDown={() => {
89
+ window.parent.postMessage(
90
+ {
91
+ type: 'WIDGET_MOVE',
92
+ data: { id: componentId, direction: 'down' }
93
+ },
94
+ '*'
95
+ );
96
+ }}
97
+ onCopy={() => {
98
+ window.parent.postMessage(
99
+ {
100
+ type: 'WIDGET_COPY',
101
+ data: { id: componentId }
102
+ },
103
+ '*'
104
+ );
105
+ }}
106
+ />
107
+ );
108
+ };
109
+
110
+ const wrapWithTooltip = (component: React.ReactNode) => {
111
+ return (
112
+ <div className="relative">
113
+ {renderActionTooltip()}
114
+ {component}
115
+ </div>
116
+ );
117
+ };
118
+
119
+ const renderComponent = () => {
120
+ let iconSvg;
121
+ let Tag;
122
+ let CustomTag;
123
+
124
+ const dragProps =
125
+ designMode && draggable
126
+ ? {
127
+ draggable: true,
128
+ onDragStart,
129
+ onDragOver,
130
+ onDragLeave,
131
+ onDrop
132
+ }
133
+ : {};
134
+
135
+ switch (componentData.type) {
136
+ case 'text':
137
+ Tag = (
138
+ componentData.tag || 'div'
139
+ ).toLowerCase() as keyof JSX.IntrinsicElements;
140
+ return wrapWithTooltip(
141
+ <Tag
142
+ id={componentId}
143
+ style={combinedStyles}
144
+ onClick={onWidgetClick}
145
+ className={getClickableStyles(selectedWidget?.id === componentId)}
146
+ {...dragProps}
147
+ >
148
+ {componentData.value?.text}
149
+ </Tag>
150
+ );
151
+
152
+ case 'image': {
153
+ const {
154
+ src,
155
+ mobileSrc,
156
+ tabletSrc,
157
+ desktopSrc,
158
+ alt = '',
159
+ sizes,
160
+ mediaQueries = {
161
+ mobile: '(max-width: 767px)',
162
+ tablet: '(min-width: 768px) and (max-width: 1023px)',
163
+ desktop: '(min-width: 1024px)'
164
+ }
165
+ } = componentData.value || {};
166
+
167
+ return wrapWithTooltip(
168
+ <picture
169
+ id={componentId}
170
+ onClick={onWidgetClick}
171
+ className={clsx(
172
+ designMode && [
173
+ 'cursor-pointer relative',
174
+ selectedWidget?.id === componentId
175
+ ? 'outline outline-2 outline-[#3B82F6] outline-offset-2'
176
+ : ''
177
+ ]
178
+ )}
179
+ {...dragProps}
180
+ >
181
+ {mobileSrc && (
182
+ <source
183
+ media={mediaQueries.mobile}
184
+ srcSet={mobileSrc}
185
+ sizes={sizes?.mobile}
186
+ />
187
+ )}
188
+ {tabletSrc && (
189
+ <source
190
+ media={mediaQueries.tablet}
191
+ srcSet={tabletSrc}
192
+ sizes={sizes?.tablet}
193
+ />
194
+ )}
195
+ {desktopSrc && (
196
+ <source
197
+ media={mediaQueries.desktop}
198
+ srcSet={desktopSrc}
199
+ sizes={sizes?.desktop}
200
+ />
201
+ )}
202
+ <img
203
+ className={clsx(
204
+ designMode && [
205
+ 'cursor-pointer',
206
+ selectedWidget?.id === componentId
207
+ ? 'outline outline-2 outline-[#3B82F6] outline-offset-2'
208
+ : ''
209
+ ]
210
+ )}
211
+ src={src || desktopSrc || tabletSrc || mobileSrc || ''}
212
+ alt={alt}
213
+ style={combinedStyles}
214
+ loading="lazy"
215
+ />
216
+ </picture>
217
+ );
218
+ }
219
+
220
+ case 'icon':
221
+ iconSvg =
222
+ componentData.value?.sizes?.[
223
+ Object.keys(componentData.value.sizes || {})[0]
224
+ ];
225
+ if (!iconSvg) return null;
226
+ return wrapWithTooltip(
227
+ <div
228
+ id={componentId}
229
+ style={combinedStyles}
230
+ onClick={onWidgetClick}
231
+ className={clsx(
232
+ 'relative',
233
+ designMode && [
234
+ 'cursor-pointer',
235
+ selectedWidget?.id === componentId
236
+ ? 'outline outline-2 outline-[#3B82F6] outline-offset-2'
237
+ : ''
238
+ ]
239
+ )}
240
+ dangerouslySetInnerHTML={{ __html: iconSvg }}
241
+ {...dragProps}
242
+ />
243
+ );
244
+
245
+ case 'link':
246
+ return wrapWithTooltip(
247
+ <a
248
+ id={componentId}
249
+ href={componentData.value?.href || '#'}
250
+ target={componentData.value?.target}
251
+ style={combinedStyles}
252
+ onClick={onWidgetClick}
253
+ className={clsx(
254
+ 'relative data-container',
255
+ designMode && [
256
+ 'cursor-pointer',
257
+ selectedWidget?.id === componentId
258
+ ? 'outline outline-2 outline-[#3B82F6] outline-offset-2'
259
+ : ''
260
+ ]
261
+ )}
262
+ {...dragProps}
263
+ >
264
+ {componentData.value?.text || componentData.value?.title || 'Link'}
265
+ </a>
266
+ );
267
+
268
+ case 'divider':
269
+ return wrapWithTooltip(
270
+ <hr
271
+ id={componentId}
272
+ style={combinedStyles}
273
+ onClick={onWidgetClick}
274
+ {...dragProps}
275
+ />
276
+ );
277
+
278
+ default:
279
+ if (componentData.tag) {
280
+ CustomTag =
281
+ componentData.tag.toLowerCase() as keyof JSX.IntrinsicElements;
282
+ return wrapWithTooltip(
283
+ <CustomTag
284
+ id={componentId}
285
+ style={combinedStyles}
286
+ onClick={onWidgetClick}
287
+ className={getClickableStyles(selectedWidget?.id === componentId)}
288
+ {...dragProps}
289
+ >
290
+ {componentData.value?.text}
291
+ </CustomTag>
292
+ );
293
+ }
294
+ return wrapWithTooltip(
295
+ <div
296
+ id={componentId}
297
+ style={combinedStyles}
298
+ onClick={onWidgetClick}
299
+ {...dragProps}
300
+ >
301
+ {componentData.value?.text}
302
+ </div>
303
+ );
304
+ }
305
+ };
306
+
307
+ if (parentData && !skipParentWrapper) {
308
+ const ParentTag = (
309
+ parentData.tag || 'div'
310
+ ).toLowerCase() as keyof JSX.IntrinsicElements;
311
+ return (
312
+ <ParentTag
313
+ id={parentData.id}
314
+ style={parentStyles}
315
+ onClick={onWidgetClick}
316
+ >
317
+ {renderComponent()}
318
+ </ParentTag>
319
+ );
320
+ }
321
+
322
+ return renderComponent();
323
+ }
@@ -0,0 +1,70 @@
1
+ import 'server-only';
2
+
3
+ import { getWidgetData } from '@akinon/next/data/server';
4
+
5
+ interface ThemeSettings {
6
+ logo?: string;
7
+ favicon?: string;
8
+ plainHeader?: boolean;
9
+ fontFamily?: string;
10
+ backupFontFamily?: string;
11
+ fontWeight?: string;
12
+ fontSubsets?: string;
13
+ fontSize?: string;
14
+ fontColor?: string;
15
+ fontOpacity?: number;
16
+ mainMenuSticky?: boolean;
17
+ breadcrumbSeparator?: string;
18
+ socialNetworks?: Record<string, { url?: string; icon?: string }>;
19
+ }
20
+
21
+ interface ThemeConfigWidget {
22
+ attributes?: {
23
+ theme_name?: {
24
+ value?: string;
25
+ kwargs?: Record<string, unknown>;
26
+ };
27
+ theme_slug?: {
28
+ value?: string;
29
+ kwargs?: Record<string, unknown>;
30
+ };
31
+ theme_settings?: {
32
+ value?: string;
33
+ kwargs?: Record<string, unknown>;
34
+ };
35
+ theme_breakpoints?: {
36
+ value?: string;
37
+ kwargs?: Record<string, unknown>;
38
+ };
39
+ theme_dataSources?: {
40
+ value?: string;
41
+ kwargs?: Record<string, unknown>;
42
+ };
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Fetches theme configuration from the widget API
48
+ * This is a server-side function that reads theme settings stored in the theme-config widget
49
+ */
50
+ export async function getThemeSettings(): Promise<ThemeSettings> {
51
+ try {
52
+ const themeConfigData = await getWidgetData<any>({
53
+ slug: 'theme-config'
54
+ });
55
+
56
+ if (!themeConfigData?.attributes?.theme_settings?.value) {
57
+ return {};
58
+ }
59
+
60
+ // Parse the theme_settings JSON string from the value property
61
+ const settings: ThemeSettings = JSON.parse(
62
+ themeConfigData.attributes.theme_settings.value
63
+ );
64
+
65
+ return settings;
66
+ } catch (error) {
67
+ console.error('Error loading theme settings:', error);
68
+ return {};
69
+ }
70
+ }
@@ -15,6 +15,7 @@ interface FavButtonProps {
15
15
  label?: string;
16
16
  size?: number;
17
17
  className?: string;
18
+ style?: React.CSSProperties;
18
19
  }
19
20
 
20
21
  const useFavButton = (productPk: number) => {
@@ -73,12 +74,14 @@ const useFavButton = (productPk: number) => {
73
74
  'flex items-center hover:text-secondary-hover hover:cursor-pointer',
74
75
  props.className
75
76
  )}
77
+ style={props.style}
76
78
  onClick={handleClick}
77
79
  data-testid="favourites-icon"
78
80
  >
79
81
  <Icon
80
- name={isActive ? 'heart-full' : 'heart-stroke'}
81
- className={clsx('text-2xl md:text-xl hover:fill-secondary-hover')}
82
+ name={isActive ? 'fav-on' : 'fav-off'}
83
+ size={props.size ?? 16}
84
+ className={clsx('hover:fill-secondary-hover')}
82
85
  />
83
86
  {props.label && <span className="ml-2">{props.label}</span>}
84
87
  </button>
@@ -22,26 +22,29 @@ interface AddToCartError {
22
22
  }
23
23
 
24
24
  export const useProductCart = ({ product, variants }: UseProductCartProps) => {
25
- const [productError, setProductError] = useState<React.ReactNode | null>(null);
26
- const [addProduct, { isLoading: isAddToCartLoading }] = useAddProductToBasket();
25
+ const [productError, setProductError] = useState<React.ReactNode | null>(
26
+ null
27
+ );
28
+ const [addProduct, { isLoading: isAddToCartLoading }] =
29
+ useAddProductToBasket();
27
30
 
28
31
  const formatError = (error: AddToCartError) => {
29
32
  if (error?.data?.non_field_errors) {
30
33
  return error.data.non_field_errors;
31
34
  }
32
-
35
+
33
36
  if (error?.data) {
34
37
  return Object.keys(error.data).map(
35
38
  (key) => `${key}: ${error.data[key].join(', ')}`
36
39
  );
37
40
  }
38
-
41
+
39
42
  return 'An error occurred';
40
43
  };
41
44
 
42
- const addProductToCart = async () => {
45
+ const addProductToCart = async (quantity: number = 1) => {
43
46
  const validation = validateVariantSelection(variants);
44
-
47
+
45
48
  if (!validation.isValid) {
46
49
  setProductError(validation.errorMessage);
47
50
  return false;
@@ -50,7 +53,7 @@ export const useProductCart = ({ product, variants }: UseProductCartProps) => {
50
53
  try {
51
54
  await addProduct({
52
55
  product: product.pk,
53
- quantity: 1,
56
+ quantity,
54
57
  attributes: {}
55
58
  });
56
59
 
@@ -74,4 +77,4 @@ export const useProductCart = ({ product, variants }: UseProductCartProps) => {
74
77
  clearProductError,
75
78
  isAddToCartLoading
76
79
  };
77
- };
80
+ };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+
5
+ interface ThemeSettings {
6
+ logo?: string;
7
+ favicon?: string;
8
+ plainHeader?: boolean;
9
+ fontFamily?: string;
10
+ backupFontFamily?: string;
11
+ fontWeight?: string;
12
+ fontSubsets?: string;
13
+ }
14
+
15
+ /**
16
+ * Client-side hook to fetch theme settings
17
+ * Note: This makes a client-side API call. For server components, use getThemeSettings() instead.
18
+ */
19
+ export function useThemeSettings() {
20
+ const [settings, setSettings] = useState<ThemeSettings>({});
21
+ const [isLoading, setIsLoading] = useState(true);
22
+
23
+ useEffect(() => {
24
+ const fetchSettings = async () => {
25
+ try {
26
+ const response = await fetch('/api/theme-settings');
27
+ if (response.ok) {
28
+ const data = await response.json();
29
+ setSettings(data);
30
+ }
31
+ } catch (error) {
32
+ console.error('Failed to fetch theme settings:', error);
33
+ } finally {
34
+ setIsLoading(false);
35
+ }
36
+ };
37
+
38
+ fetchSettings();
39
+ }, []);
40
+
41
+ return { settings, isLoading };
42
+ }
@@ -0,0 +1,149 @@
1
+ import {
2
+ Roboto,
3
+ Open_Sans,
4
+ Lato,
5
+ Montserrat,
6
+ Jost,
7
+ Inter,
8
+ Poppins,
9
+ Playfair_Display,
10
+ Raleway,
11
+ Ubuntu
12
+ } from 'next/font/google';
13
+
14
+ // Google Fonts configuration
15
+ export const roboto = Roboto({
16
+ weight: ['100', '300', '400', '500', '700', '900'],
17
+ subsets: [
18
+ 'latin',
19
+ 'latin-ext',
20
+ 'cyrillic',
21
+ 'cyrillic-ext',
22
+ 'greek',
23
+ 'greek-ext',
24
+ 'vietnamese'
25
+ ],
26
+ display: 'swap',
27
+ variable: '--font-roboto'
28
+ });
29
+
30
+ export const openSans = Open_Sans({
31
+ weight: ['300', '400', '500', '600', '700', '800'],
32
+ subsets: [
33
+ 'latin',
34
+ 'latin-ext',
35
+ 'cyrillic',
36
+ 'cyrillic-ext',
37
+ 'greek',
38
+ 'greek-ext',
39
+ 'vietnamese'
40
+ ],
41
+ display: 'swap',
42
+ variable: '--font-open-sans'
43
+ });
44
+
45
+ export const lato = Lato({
46
+ weight: ['100', '300', '400', '700', '900'],
47
+ subsets: ['latin', 'latin-ext'],
48
+ display: 'swap',
49
+ variable: '--font-lato'
50
+ });
51
+
52
+ export const montserrat = Montserrat({
53
+ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
54
+ subsets: ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'vietnamese'],
55
+ display: 'swap',
56
+ variable: '--font-montserrat'
57
+ });
58
+
59
+ export const jost = Jost({
60
+ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
61
+ subsets: ['latin', 'latin-ext', 'cyrillic'],
62
+ display: 'swap',
63
+ variable: '--font-jost'
64
+ });
65
+
66
+ export const inter = Inter({
67
+ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
68
+ subsets: [
69
+ 'latin',
70
+ 'latin-ext',
71
+ 'cyrillic',
72
+ 'cyrillic-ext',
73
+ 'greek',
74
+ 'greek-ext',
75
+ 'vietnamese'
76
+ ],
77
+ display: 'swap',
78
+ variable: '--font-inter'
79
+ });
80
+
81
+ export const poppins = Poppins({
82
+ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
83
+ subsets: ['latin', 'latin-ext'],
84
+ display: 'swap',
85
+ variable: '--font-poppins'
86
+ });
87
+
88
+ export const playfairDisplay = Playfair_Display({
89
+ weight: ['400', '500', '600', '700', '800', '900'],
90
+ subsets: ['latin', 'latin-ext', 'cyrillic'],
91
+ display: 'swap',
92
+ variable: '--font-playfair-display'
93
+ });
94
+
95
+ export const raleway = Raleway({
96
+ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
97
+ subsets: ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext'],
98
+ display: 'swap',
99
+ variable: '--font-raleway'
100
+ });
101
+
102
+ export const ubuntu = Ubuntu({
103
+ weight: ['300', '400', '500', '700'],
104
+ subsets: [
105
+ 'latin',
106
+ 'latin-ext',
107
+ 'cyrillic',
108
+ 'cyrillic-ext',
109
+ 'greek',
110
+ 'greek-ext'
111
+ ],
112
+ display: 'swap',
113
+ variable: '--font-ubuntu'
114
+ });
115
+
116
+ // Font mapping for easy access
117
+ export const fontMap = {
118
+ Roboto: roboto,
119
+ 'Open Sans': openSans,
120
+ Lato: lato,
121
+ Montserrat: montserrat,
122
+ Jost: jost,
123
+ Inter: inter,
124
+ Poppins: poppins,
125
+ 'Playfair Display': playfairDisplay,
126
+ Raleway: raleway,
127
+ Ubuntu: ubuntu
128
+ } as const;
129
+
130
+ export type GoogleFontName = keyof typeof fontMap;
131
+
132
+ // Get font class name by font name
133
+ export function getFontClassName(fontName: string): string {
134
+ const font = fontMap[fontName as GoogleFontName];
135
+ return font?.className || '';
136
+ }
137
+
138
+ // Get font variable by font name
139
+ export function getFontVariable(fontName: string): string {
140
+ const font = fontMap[fontName as GoogleFontName];
141
+ return font?.variable || '';
142
+ }
143
+
144
+ // Get all font variables for CSS variables
145
+ export function getAllFontVariables(): string {
146
+ return Object.values(fontMap)
147
+ .map((font) => font.variable)
148
+ .join(' ');
149
+ }
@@ -16,14 +16,14 @@ module.exports = {
16
16
  localization: {
17
17
  locales: [
18
18
  {
19
- label: 'EN',
19
+ label: 'English',
20
20
  value: 'en',
21
21
  localePath: 'en',
22
22
  apiValue: 'en-us',
23
23
  rtl: false
24
24
  },
25
25
  {
26
- label: 'TR',
26
+ label: 'Turkish',
27
27
  value: 'tr',
28
28
  localePath: 'tr',
29
29
  apiValue: 'tr-tr',
@@ -0,0 +1,28 @@
1
+ declare module '@hookform/resolvers/yup' {
2
+ import { FieldValues, ResolverOptions, ResolverResult, UnpackNestedValue } from 'react-hook-form';
3
+ import * as Yup from 'yup';
4
+ import type Lazy from 'yup/lib/Lazy';
5
+
6
+ type Options<T extends Yup.AnyObjectSchema | Lazy<any>> =
7
+ Parameters<T['validate']>[1];
8
+
9
+ export type Resolver = <
10
+ T extends Yup.AnyObjectSchema | Lazy<any>
11
+ >(
12
+ schema: T,
13
+ schemaOptions?: Options<T>,
14
+ factoryOptions?: {
15
+ mode?: 'async' | 'sync';
16
+ rawValues?: boolean;
17
+ }
18
+ ) => <
19
+ TFieldValues extends FieldValues,
20
+ TContext
21
+ >(
22
+ values: UnpackNestedValue<TFieldValues>,
23
+ context: TContext | undefined,
24
+ options: ResolverOptions<TFieldValues>
25
+ ) => Promise<ResolverResult<TFieldValues>>;
26
+
27
+ export const yupResolver: Resolver;
28
+ }