@akinon/next 2.0.0-beta.2 → 2.0.0-beta.21

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 (133) hide show
  1. package/.eslintrc.js +12 -0
  2. package/CHANGELOG.md +400 -7
  3. package/__tests__/next-config.test.ts +83 -0
  4. package/__tests__/tsconfig.json +23 -0
  5. package/api/auth.ts +381 -60
  6. package/api/barcode-search.ts +59 -0
  7. package/api/cache.ts +41 -5
  8. package/api/client.ts +21 -4
  9. package/api/form.ts +85 -0
  10. package/api/image-proxy.ts +75 -0
  11. package/api/product-categories.ts +53 -0
  12. package/api/similar-product-list.ts +63 -0
  13. package/api/similar-products.ts +111 -0
  14. package/api/virtual-try-on.ts +382 -0
  15. package/assets/styles/index.scss +84 -0
  16. package/babel.config.js +6 -0
  17. package/bin/pz-generate-routes.js +115 -0
  18. package/bin/pz-install-plugins.js +1 -1
  19. package/bin/pz-prebuild.js +1 -0
  20. package/bin/pz-predev.js +1 -0
  21. package/bin/pz-run-tests.js +99 -0
  22. package/bin/run-prebuild-tests.js +46 -0
  23. package/components/accordion.tsx +20 -5
  24. package/components/button.tsx +51 -36
  25. package/components/client-root.tsx +138 -2
  26. package/components/file-input.tsx +65 -3
  27. package/components/index.ts +1 -0
  28. package/components/input.tsx +1 -1
  29. package/components/link.tsx +46 -16
  30. package/components/logger-popup.tsx +213 -0
  31. package/components/modal.tsx +32 -16
  32. package/components/plugin-module.tsx +62 -3
  33. package/components/price.tsx +2 -2
  34. package/components/select.tsx +1 -1
  35. package/components/selected-payment-option-view.tsx +21 -0
  36. package/data/client/account.ts +17 -2
  37. package/data/client/api.ts +2 -0
  38. package/data/client/basket.ts +66 -5
  39. package/data/client/checkout.ts +391 -99
  40. package/data/client/misc.ts +38 -2
  41. package/data/client/product.ts +19 -2
  42. package/data/client/user.ts +16 -8
  43. package/data/server/category.ts +11 -9
  44. package/data/server/flatpage.ts +11 -4
  45. package/data/server/form.ts +15 -4
  46. package/data/server/landingpage.ts +11 -4
  47. package/data/server/list.ts +5 -4
  48. package/data/server/menu.ts +11 -3
  49. package/data/server/product.ts +111 -55
  50. package/data/server/seo.ts +14 -4
  51. package/data/server/special-page.ts +5 -4
  52. package/data/server/widget.ts +90 -5
  53. package/data/urls.ts +16 -5
  54. package/hocs/client/with-segment-defaults.tsx +2 -2
  55. package/hocs/server/with-segment-defaults.tsx +65 -20
  56. package/hooks/index.ts +4 -0
  57. package/hooks/use-localization.ts +24 -10
  58. package/hooks/use-logger-context.tsx +114 -0
  59. package/hooks/use-logger.ts +92 -0
  60. package/hooks/use-loyalty-availability.ts +21 -0
  61. package/hooks/use-payment-options.ts +2 -1
  62. package/hooks/use-pz-params.ts +37 -0
  63. package/hooks/use-router.ts +51 -14
  64. package/hooks/use-sentry-uncaught-errors.ts +24 -0
  65. package/instrumentation/index.ts +10 -1
  66. package/instrumentation/node.ts +2 -20
  67. package/jest.config.js +25 -0
  68. package/lib/cache-handler.mjs +534 -16
  69. package/lib/cache.ts +272 -37
  70. package/localization/index.ts +2 -1
  71. package/localization/provider.tsx +2 -5
  72. package/middlewares/bfcache-headers.ts +18 -0
  73. package/middlewares/checkout-provider.ts +1 -1
  74. package/middlewares/complete-gpay.ts +32 -26
  75. package/middlewares/complete-masterpass.ts +33 -26
  76. package/middlewares/complete-wallet.ts +182 -0
  77. package/middlewares/default.ts +360 -215
  78. package/middlewares/index.ts +10 -2
  79. package/middlewares/locale.ts +34 -11
  80. package/middlewares/masterpass-rest-callback.ts +230 -0
  81. package/middlewares/oauth-login.ts +200 -57
  82. package/middlewares/pretty-url.ts +21 -8
  83. package/middlewares/redirection-payment.ts +32 -26
  84. package/middlewares/saved-card-redirection.ts +33 -26
  85. package/middlewares/three-d-redirection.ts +32 -26
  86. package/middlewares/url-redirection.ts +11 -1
  87. package/middlewares/wallet-complete-redirection.ts +206 -0
  88. package/package.json +24 -9
  89. package/plugins.d.ts +19 -4
  90. package/plugins.js +10 -1
  91. package/redux/actions.ts +47 -0
  92. package/redux/middlewares/checkout.ts +63 -138
  93. package/redux/middlewares/index.ts +14 -10
  94. package/redux/middlewares/pre-order/address.ts +7 -2
  95. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
  96. package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
  97. package/redux/middlewares/pre-order/delivery-option.ts +7 -1
  98. package/redux/middlewares/pre-order/index.ts +16 -10
  99. package/redux/middlewares/pre-order/installment-option.ts +8 -1
  100. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  101. package/redux/middlewares/pre-order/payment-option.ts +7 -1
  102. package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
  103. package/redux/middlewares/pre-order/redirection.ts +8 -2
  104. package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
  105. package/redux/middlewares/pre-order/shipping-option.ts +7 -1
  106. package/redux/middlewares/pre-order/shipping-step.ts +5 -1
  107. package/redux/reducers/checkout.ts +23 -3
  108. package/redux/reducers/index.ts +11 -3
  109. package/redux/reducers/root.ts +7 -2
  110. package/redux/reducers/widget.ts +80 -0
  111. package/sentry/index.ts +69 -13
  112. package/tailwind/content.js +16 -0
  113. package/types/commerce/account.ts +5 -1
  114. package/types/commerce/checkout.ts +35 -1
  115. package/types/commerce/widget.ts +33 -0
  116. package/types/index.ts +114 -6
  117. package/types/next-auth.d.ts +2 -2
  118. package/types/widget.ts +80 -0
  119. package/utils/app-fetch.ts +7 -2
  120. package/utils/generate-commerce-search-params.ts +3 -2
  121. package/utils/get-checkout-path.ts +3 -0
  122. package/utils/get-root-hostname.ts +28 -0
  123. package/utils/index.ts +64 -10
  124. package/utils/localization.ts +4 -0
  125. package/utils/mobile-3d-iframe.ts +8 -2
  126. package/utils/override-middleware.ts +7 -12
  127. package/utils/pz-segments.ts +92 -0
  128. package/utils/redirect-ignore.ts +35 -0
  129. package/utils/redirect.ts +9 -3
  130. package/utils/redirection-iframe.ts +8 -2
  131. package/utils/widget-styles.ts +107 -0
  132. package/views/error-page.tsx +93 -0
  133. package/with-pz-config.js +21 -7
@@ -0,0 +1,213 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useCallback, memo, useMemo } from 'react';
4
+ import { LogEntry } from '../hooks/use-logger';
5
+ import { useLoggerContext } from '../hooks/use-logger-context';
6
+
7
+ const LoggerAnimations = ({ color = '#dc2626' }: { color?: string }) => (
8
+ <style jsx global>{`
9
+ @keyframes pulse {
10
+ 0% {
11
+ transform: scale(0.95);
12
+ box-shadow: 0 0 0 0 ${color}80;
13
+ }
14
+
15
+ 70% {
16
+ transform: scale(1.05);
17
+ box-shadow: 0 0 0 10px ${color}00;
18
+ }
19
+
20
+ 100% {
21
+ transform: scale(0.95);
22
+ box-shadow: 0 0 0 0 ${color}00;
23
+ }
24
+ }
25
+ `}</style>
26
+ );
27
+
28
+ const LogLevelColors = {
29
+ warn: '#ff9800', // orange
30
+ error: '#f44336' // red
31
+ };
32
+
33
+ const LoggerTrigger = memo(
34
+ ({
35
+ onClick,
36
+ logCount = 0,
37
+ currentColor
38
+ }: {
39
+ onClick: () => void;
40
+ logCount?: number;
41
+ currentColor: string;
42
+ }) => {
43
+ return (
44
+ <button
45
+ onClick={onClick}
46
+ className="fixed bottom-4 right-4 w-14 h-14 border border-white rounded-full flex items-center justify-center shadow-lg z-[9999] hover:opacity-90 transition-colors"
47
+ aria-label="Open Logger"
48
+ style={{
49
+ backgroundColor: currentColor,
50
+ ...(logCount > 0 && {
51
+ animation: 'pulse 2s infinite'
52
+ })
53
+ }}
54
+ >
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ fill="none"
58
+ viewBox="0 0 24 24"
59
+ strokeWidth="1.5"
60
+ stroke="currentColor"
61
+ className="text-white size-6"
62
+ >
63
+ <path
64
+ strokeLinecap="round"
65
+ strokeLinejoin="round"
66
+ d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
67
+ />
68
+ </svg>
69
+
70
+ {logCount > 0 && (
71
+ <span
72
+ className="absolute w-5 h-5 -bottom-[5px] p-1 -left-[5px] border-2 border-white rounded-full flex items-center justify-center text-[8px] text-white font-bold"
73
+ style={{
74
+ backgroundColor: currentColor
75
+ }}
76
+ >
77
+ {logCount > 99 ? '99+' : logCount}
78
+ </span>
79
+ )}
80
+ </button>
81
+ );
82
+ }
83
+ );
84
+
85
+ LoggerTrigger.displayName = 'LoggerTrigger';
86
+
87
+ const LogItem = memo(({ log }: { log: LogEntry }) => {
88
+ const [expanded, setExpanded] = useState(false);
89
+ const hasPayload = log.payload && Object.keys(log.payload).length > 0;
90
+
91
+ const toggleExpanded = useCallback(() => {
92
+ setExpanded((prev) => !prev);
93
+ }, []);
94
+
95
+ return (
96
+ <div className="border-b border-gray-200 py-2">
97
+ <div className="relative">
98
+ <div
99
+ className="absolute top-0 left-0 w-3 h-3 rounded-full mt-1.5 mr-2 flex-shrink-0"
100
+ style={{
101
+ backgroundColor: LogLevelColors[log.level],
102
+ boxShadow: `0 0 5px ${LogLevelColors[log.level]}`
103
+ }}
104
+ />
105
+ <div className="ml-6">
106
+ <div className="flex justify-between">
107
+ <span
108
+ className="font-medium capitalize"
109
+ style={{
110
+ color: LogLevelColors[log.level]
111
+ }}
112
+ >
113
+ {log.level}
114
+ </span>
115
+ <span className="text-xs text-gray-500">
116
+ {log.timestamp.toLocaleTimeString()}
117
+ </span>
118
+ </div>
119
+ <p className="text-sm">{log.message}</p>
120
+ {hasPayload && (
121
+ <button
122
+ onClick={toggleExpanded}
123
+ className="text-xs text-blue-500 mt-1 hover:text-blue-700 transition-colors"
124
+ >
125
+ {expanded ? 'Hide Details' : 'Show Details'}
126
+ </button>
127
+ )}
128
+ {expanded && hasPayload && (
129
+ <pre className="text-xs bg-gray-100 p-2 mt-1 rounded overflow-auto max-h-96 max-w-full">
130
+ {JSON.stringify(log.payload, null, 2)}
131
+ </pre>
132
+ )}
133
+ </div>
134
+ </div>
135
+ </div>
136
+ );
137
+ });
138
+
139
+ LogItem.displayName = 'LogItem';
140
+
141
+ export const LoggerPopup = () => {
142
+ const {
143
+ logs,
144
+ isVisible,
145
+ toggleVisibility,
146
+ clearLogs,
147
+ isDevelopment,
148
+ hasError,
149
+ hasWarning
150
+ } = useLoggerContext();
151
+
152
+ const currentColor = useMemo(() => {
153
+ if (logs.length === 0) return '#b5afaf';
154
+ if (hasError) return '#dc2626';
155
+ if (hasWarning) return '#ff9800';
156
+
157
+ return '#b5afaf';
158
+ }, [logs.length, hasError, hasWarning]);
159
+
160
+ if (!isDevelopment) {
161
+ return null;
162
+ }
163
+
164
+ if (!isVisible) {
165
+ return (
166
+ <>
167
+ <LoggerAnimations color={currentColor} />
168
+ <LoggerTrigger
169
+ onClick={toggleVisibility}
170
+ logCount={logs.length}
171
+ currentColor={currentColor}
172
+ />
173
+ </>
174
+ );
175
+ }
176
+
177
+ return (
178
+ <>
179
+ <LoggerAnimations color={currentColor} />
180
+ <LoggerTrigger
181
+ onClick={toggleVisibility}
182
+ logCount={logs.length}
183
+ currentColor={currentColor}
184
+ />
185
+ <div className="fixed bottom-20 right-4 w-96 max-w-[calc(100vw-2rem)] bg-white rounded-lg shadow-xl z-50 border-2 border-gray-200 max-h-[70vh] flex flex-col">
186
+ <div className="flex items-center justify-between p-3 border-b border-gray-200">
187
+ <h3 className="font-bold flex items-center">Development Logger</h3>
188
+ <div className="flex space-x-2">
189
+ <button
190
+ onClick={clearLogs}
191
+ className="text-xs bg-gray-200 hover:bg-gray-300 px-2 py-1 rounded transition-colors"
192
+ >
193
+ Clear
194
+ </button>
195
+ <button
196
+ onClick={toggleVisibility}
197
+ className="text-xs bg-gray-200 hover:bg-gray-300 px-2 py-1 rounded transition-colors"
198
+ >
199
+ Close
200
+ </button>
201
+ </div>
202
+ </div>
203
+ <div className="overflow-y-auto flex-grow p-3">
204
+ {logs.length === 0 ? (
205
+ <p className="text-gray-500 text-center py-4">No logs yet</p>
206
+ ) : (
207
+ logs.map((log) => <LogItem key={log.id} log={log} />)
208
+ )}
209
+ </div>
210
+ </div>
211
+ </>
212
+ );
213
+ };
@@ -4,16 +4,7 @@ import { ReactPortal } from './react-portal';
4
4
  import { Icon } from './icon';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { useEffect } from 'react';
7
-
8
- export interface ModalProps {
9
- portalId: string;
10
- children?: React.ReactNode;
11
- open?: boolean;
12
- setOpen?: (open: boolean) => void;
13
- title?: React.ReactNode;
14
- showCloseButton?: React.ReactNode;
15
- className?: string;
16
- }
7
+ import { ModalProps } from '../types';
17
8
 
18
9
  export const Modal = (props: ModalProps) => {
19
10
  const {
@@ -23,7 +14,14 @@ export const Modal = (props: ModalProps) => {
23
14
  setOpen,
24
15
  title = '',
25
16
  showCloseButton = true,
26
- className
17
+ className,
18
+ overlayClassName,
19
+ headerWrapperClassName,
20
+ titleClassName,
21
+ closeButtonClassName,
22
+ iconName = 'close',
23
+ iconSize = 16,
24
+ iconClassName
27
25
  } = props;
28
26
 
29
27
  useEffect(() => {
@@ -38,7 +36,12 @@ export const Modal = (props: ModalProps) => {
38
36
 
39
37
  return (
40
38
  <ReactPortal wrapperId={portalId}>
41
- <div className="fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50" />
39
+ <div
40
+ className={twMerge(
41
+ 'fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50',
42
+ overlayClassName
43
+ )}
44
+ />
42
45
  <section
43
46
  className={twMerge(
44
47
  'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white',
@@ -46,15 +49,28 @@ export const Modal = (props: ModalProps) => {
46
49
  )}
47
50
  >
48
51
  {(showCloseButton || title) && (
49
- <div className="flex px-6 py-4 border-b border-gray-400">
50
- {title && <h3 className="text-lg font-light">{title}</h3>}
52
+ <div
53
+ className={twMerge(
54
+ 'flex px-6 py-4 border-b border-gray-400',
55
+ headerWrapperClassName
56
+ )}
57
+ >
58
+ {title && (
59
+ <h3 className={twMerge('text-lg font-light', titleClassName)}>
60
+ {title}
61
+ </h3>
62
+ )}
51
63
  {showCloseButton && (
52
64
  <button
53
65
  type="button"
54
66
  onClick={() => setOpen(false)}
55
- className="ml-auto"
67
+ className={twMerge('ml-auto', closeButtonClassName)}
56
68
  >
57
- <Icon name="close" size={16} />
69
+ <Icon
70
+ name={iconName}
71
+ size={iconSize}
72
+ className={iconClassName}
73
+ />
58
74
  </button>
59
75
  )}
60
76
  </div>
@@ -20,7 +20,14 @@ enum Plugin {
20
20
  B2B = 'pz-b2b',
21
21
  Akifast = 'pz-akifast',
22
22
  MultiBasket = 'pz-multi-basket',
23
- SavedCard = 'pz-saved-card'
23
+ SavedCard = 'pz-saved-card',
24
+ FlowPayment = 'pz-flow-payment',
25
+ VirtualTryOn = 'pz-virtual-try-on',
26
+ Hepsipay = 'pz-hepsipay',
27
+ MasterpassRest = 'pz-masterpass-rest',
28
+ SimilarProducts = 'pz-similar-products',
29
+ Haso = 'pz-haso',
30
+ GooglePay = 'pz-google-pay'
24
31
  }
25
32
 
26
33
  export enum Component {
@@ -45,7 +52,23 @@ export enum Component {
45
52
  AkifastQuickLoginButton = 'QuickLoginButton',
46
53
  AkifastCheckoutButton = 'CheckoutButton',
47
54
  MultiBasket = 'MultiBasket',
48
- SavedCard = 'SavedCardOption'
55
+ SavedCard = 'SavedCardOption',
56
+ VirtualTryOnPlugin = 'VirtualTryOnPlugin',
57
+ BasketVirtualTryOn = 'BasketVirtualTryOn',
58
+ BarcodeScannerPlugin = 'BarcodeScannerPlugin',
59
+ IyzicoSavedCard = 'IyzicoSavedCardOption',
60
+ Hepsipay = 'Hepsipay',
61
+ FlowPayment = 'FlowPayment',
62
+ MasterpassRest = 'MasterpassRestOption',
63
+ SimilarProductsModal = 'SimilarProductsModal',
64
+ SimilarProductsFilterSidebar = 'SimilarProductsFilterSidebar',
65
+ SimilarProductsResultsGrid = 'SimilarProductsResultsGrid',
66
+ SimilarProductsPlugin = 'SimilarProductsPlugin',
67
+ ProductImageSearchFeature = 'ProductImageSearchFeature',
68
+ ImageSearchButton = 'ImageSearchButton',
69
+ HeaderImageSearchFeature = 'HeaderImageSearchFeature',
70
+ HasoPaymentGateway = 'HasoPaymentGateway',
71
+ GooglePay = 'GooglePay'
49
72
  }
50
73
 
51
74
  const PluginComponents = new Map([
@@ -78,7 +101,29 @@ const PluginComponents = new Map([
78
101
  [Component.AkifastQuickLoginButton, Component.AkifastCheckoutButton]
79
102
  ],
80
103
  [Plugin.MultiBasket, [Component.MultiBasket]],
81
- [Plugin.SavedCard, [Component.SavedCard]]
104
+ [
105
+ Plugin.SimilarProducts,
106
+ [
107
+ Component.SimilarProductsModal,
108
+ Component.SimilarProductsFilterSidebar,
109
+ Component.SimilarProductsResultsGrid,
110
+ Component.SimilarProductsPlugin,
111
+ Component.ProductImageSearchFeature,
112
+ Component.ImageSearchButton,
113
+ Component.HeaderImageSearchFeature
114
+ ]
115
+ ],
116
+ [Plugin.SavedCard, [Component.SavedCard, Component.IyzicoSavedCard]],
117
+ [Plugin.SavedCard, [Component.SavedCard]],
118
+ [Plugin.FlowPayment, [Component.FlowPayment]],
119
+ [
120
+ Plugin.VirtualTryOn,
121
+ [Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn, Component.BarcodeScannerPlugin]
122
+ ],
123
+ [Plugin.Hepsipay, [Component.Hepsipay]],
124
+ [Plugin.MasterpassRest, [Component.MasterpassRest]],
125
+ [Plugin.Haso, [Component.HasoPaymentGateway]],
126
+ [Plugin.GooglePay, [Component.GooglePay]]
82
127
  ]);
83
128
 
84
129
  const getPlugin = (component: Component) => {
@@ -143,6 +188,20 @@ export default function PluginModule({
143
188
  promise = import(`${'@akinon/pz-multi-basket'}`);
144
189
  } else if (plugin === Plugin.SavedCard) {
145
190
  promise = import(`${'@akinon/pz-saved-card'}`);
191
+ } else if (plugin === Plugin.Hepsipay) {
192
+ promise = import(`${'@akinon/pz-hepsipay'}`);
193
+ } else if (plugin === Plugin.FlowPayment) {
194
+ promise = import(`${'@akinon/pz-flow-payment'}`);
195
+ } else if (plugin === Plugin.VirtualTryOn) {
196
+ promise = import(`${'@akinon/pz-virtual-try-on'}`);
197
+ } else if (plugin === Plugin.MasterpassRest) {
198
+ promise = import(`${'@akinon/pz-masterpass-rest'}`);
199
+ } else if (plugin === Plugin.SimilarProducts) {
200
+ promise = import(`${'@akinon/pz-similar-products'}`);
201
+ } else if (plugin === Plugin.Haso) {
202
+ promise = import(`${'@akinon/pz-haso'}`);
203
+ } else if (plugin === Plugin.GooglePay) {
204
+ promise = import(`${'@akinon/pz-google-pay'}`);
146
205
  }
147
206
  } catch (error) {
148
207
  logger.error(error);
@@ -55,8 +55,8 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
55
55
  : formattedValue;
56
56
 
57
57
  const currentCurrencyDecimalScale = Settings.localization.currencies.find(
58
- (currency) => currency.code === currencyCode_
59
- ).decimalScale;
58
+ (currency) => currency?.code === currencyCode_
59
+ )?.decimalScale;
60
60
 
61
61
  return (
62
62
  <NumericFormat
@@ -59,7 +59,7 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
59
59
  ))}
60
60
  </select>
61
61
  {error && (
62
- <span className="mt-1 text-sm text-error">{error.message}</span>
62
+ <span className="mt-1 text-sm text-error">{String(error.message)}</span>
63
63
  )}
64
64
  </label>
65
65
  );
@@ -16,6 +16,7 @@ const paymentTypeToView = {
16
16
  gpay: 'gpay',
17
17
  loyalty_money: 'loyalty',
18
18
  masterpass: 'credit-card',
19
+ masterpass_rest: 'masterpass-rest',
19
20
  pay_on_delivery: 'pay-on-delivery',
20
21
  redirection: 'redirection',
21
22
  saved_card: 'saved-card'
@@ -57,6 +58,26 @@ export default function SelectedPaymentOptionView({
57
58
  ) {
58
59
  const mod = await import('@akinon/pz-apple-pay');
59
60
 
61
+ return typeof mod?.default === 'function'
62
+ ? mod.default
63
+ : fallbackView;
64
+ } else if (
65
+ payment_option?.payment_type === 'wallet' &&
66
+ wallet_method === 'cybersource_uc'
67
+ ) {
68
+ const mod = await import('@akinon/pz-cybersource-uc');
69
+
70
+ return typeof mod?.CyberSourceUcPaymentOption === 'function'
71
+ ? mod.CyberSourceUcPaymentOption
72
+ : fallbackView;
73
+ }
74
+
75
+ if (
76
+ payment_option?.payment_type === 'wallet' &&
77
+ wallet_method === 'checkout_flow'
78
+ ) {
79
+ const mod = await import('@akinon/pz-flow-payment');
80
+
60
81
  return typeof mod?.default === 'function'
61
82
  ? mod.default
62
83
  : fallbackView;
@@ -7,6 +7,7 @@ import {
7
7
  AccountOrderCancellation,
8
8
  AccountOrderCancellationReason,
9
9
  ContactFormType,
10
+ LoyaltyBalanceItem,
10
11
  Order,
11
12
  Quotations
12
13
  } from '../../types';
@@ -77,6 +78,10 @@ interface LoyaltyTransactions {
77
78
  }[];
78
79
  }
79
80
 
81
+ interface PasswordResetValidateResponse {
82
+ validlink: boolean;
83
+ }
84
+
80
85
  const accountApi = api.injectEndpoints({
81
86
  endpoints: (builder) => ({
82
87
  updatePassword: builder.mutation<void, AccountChangePasswordFormType>({
@@ -216,11 +221,20 @@ const accountApi = api.injectEndpoints({
216
221
  method: 'PATCH'
217
222
  })
218
223
  }),
219
- getLoyaltyBalance: builder.query<{ balance: number }, void>({
224
+ getLoyaltyBalance: builder.query<
225
+ { balance: number; balances?: LoyaltyBalanceItem[] },
226
+ void
227
+ >({
220
228
  query: () => buildClientRequestUrl(account.loyaltyBalance)
221
229
  }),
222
230
  getLoyaltyTransactions: builder.query<LoyaltyTransactions, void>({
223
231
  query: () => buildClientRequestUrl(account.loyaltyTransactions)
232
+ }),
233
+ getValidatePasswordResetToken: builder.query<
234
+ PasswordResetValidateResponse,
235
+ string
236
+ >({
237
+ query: (slug) => buildClientRequestUrl(account.passwordReset(slug))
224
238
  })
225
239
  }),
226
240
  overrideExisting: true
@@ -247,5 +261,6 @@ export const {
247
261
  usePasswordResetMutation,
248
262
  useAnonymizeMutation,
249
263
  useGetLoyaltyBalanceQuery,
250
- useGetLoyaltyTransactionsQuery
264
+ useGetLoyaltyTransactionsQuery,
265
+ useGetValidatePasswordResetTokenQuery
251
266
  } = accountApi;
@@ -68,10 +68,12 @@ export const api = createApi({
68
68
  tagTypes: [
69
69
  'Basket',
70
70
  'MultiBasket',
71
+ 'MiniBasket',
71
72
  'BasketB2b',
72
73
  'DraftsB2b',
73
74
  'Product',
74
75
  'Checkout',
76
+ 'PaymentOptions',
75
77
  'Favorite',
76
78
  'Addresses',
77
79
  'Profile',
@@ -24,6 +24,26 @@ export const basketApi = api.injectEndpoints({
24
24
  transformResponse: (response: { basket: Basket }) => response.basket,
25
25
  providesTags: ['Basket']
26
26
  }),
27
+ getMiniBasket: build.query<
28
+ { pk: number | null; total_quantity: number },
29
+ void
30
+ >({
31
+ query: () =>
32
+ buildClientRequestUrl(basket.getMiniBasket, {
33
+ contentType: 'application/json'
34
+ }),
35
+ providesTags: ['MiniBasket']
36
+ }),
37
+ getMiniBasketDetail: build.query<
38
+ { pk: number | null; total_quantity: number },
39
+ { namespace: string }
40
+ >({
41
+ query: ({ namespace }) =>
42
+ buildClientRequestUrl(basket.getMiniBasketDetail(namespace), {
43
+ contentType: 'application/json'
44
+ }),
45
+ providesTags: ['MiniBasket']
46
+ }),
27
47
  getBasketDetail: build.query<Basket, { namespace: string }>({
28
48
  query: ({ namespace }) =>
29
49
  buildClientRequestUrl(basket.getBasketDetail(namespace)),
@@ -46,7 +66,7 @@ export const basketApi = api.injectEndpoints({
46
66
  method: 'DELETE',
47
67
  body: { pk }
48
68
  }),
49
- invalidatesTags: ['MultiBasket', 'Basket']
69
+ invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
50
70
  }),
51
71
  selectMainBasket: build.mutation<Basket, { pk: number }>({
52
72
  query: ({ pk }) => ({
@@ -57,7 +77,7 @@ export const basketApi = api.injectEndpoints({
57
77
  body: { pk }
58
78
  }),
59
79
  transformResponse: (response: { baskets: Basket }) => response.baskets,
60
- invalidatesTags: ['MultiBasket', 'Basket']
80
+ invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
61
81
  }),
62
82
  selectNameSpaceMainBasket: build.mutation<Basket, { namespace: string }>({
63
83
  query: ({ namespace }) => ({
@@ -71,7 +91,7 @@ export const basketApi = api.injectEndpoints({
71
91
  body: { namespace }
72
92
  }),
73
93
  transformResponse: (response: { baskets: Basket }) => response.baskets,
74
- invalidatesTags: ['MultiBasket', 'Basket']
94
+ invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
75
95
  }),
76
96
  updateQuantity: build.mutation<
77
97
  UpdateQuantityResponse,
@@ -84,7 +104,46 @@ export const basketApi = api.injectEndpoints({
84
104
  method: 'PUT',
85
105
  body
86
106
  }),
87
- invalidatesTags: ['MultiBasket', 'Basket']
107
+ async onQueryStarted(_, { dispatch, queryFulfilled }) {
108
+ try {
109
+ const { data } = await queryFulfilled;
110
+
111
+ dispatch(
112
+ basketApi.util.updateQueryData(
113
+ 'getBasket',
114
+ undefined,
115
+ () => data.basket
116
+ )
117
+ );
118
+
119
+ if (data.basket.namespace) {
120
+ dispatch(
121
+ basketApi.util.updateQueryData(
122
+ 'getBasketDetail',
123
+ { namespace: data.basket.namespace },
124
+ () => data.basket
125
+ )
126
+ );
127
+ }
128
+
129
+ dispatch(
130
+ basketApi.util.updateQueryData(
131
+ 'getAllBaskets',
132
+ undefined,
133
+ (baskets) => {
134
+ if (!baskets) return baskets;
135
+
136
+ return baskets.map((basket) =>
137
+ basket.pk === data.basket.pk ? data.basket : basket
138
+ );
139
+ }
140
+ )
141
+ );
142
+ } catch (error) {
143
+ console.error('Error updating quantity:', error);
144
+ }
145
+ },
146
+ invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
88
147
  }),
89
148
  clearBasket: build.mutation<Basket, void>({
90
149
  query: (body) => ({
@@ -95,7 +154,7 @@ export const basketApi = api.injectEndpoints({
95
154
  body
96
155
  }),
97
156
  transformResponse: (response: { basket: Basket }) => response.basket,
98
- invalidatesTags: ['Basket']
157
+ invalidatesTags: ['Basket', 'MiniBasket', 'MiniBasket']
99
158
  }),
100
159
  applyVoucherCode: build.mutation<Basket, { voucher_code: string }>({
101
160
  query: (body) => ({
@@ -125,6 +184,8 @@ export const basketApi = api.injectEndpoints({
125
184
 
126
185
  export const {
127
186
  useGetBasketQuery,
187
+ useGetMiniBasketQuery,
188
+ useGetMiniBasketDetailQuery,
128
189
  useLazyGetBasketDetailQuery,
129
190
  useGetAllBasketsQuery,
130
191
  useRemoveBasketMutation,