@akinon/projectzero 2.0.0-beta.1 → 2.0.0-beta.10
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.
- package/CHANGELOG.md +35 -0
- package/app-template/.env.example +5 -0
- package/app-template/.gitignore +2 -0
- package/app-template/CHANGELOG.md +225 -0
- package/app-template/README.md +6 -0
- package/app-template/config/prebuild-tests.json +5 -0
- package/app-template/{next.config.mjs → next.config.ts} +6 -3
- package/app-template/package.json +30 -27
- package/app-template/postcss.config.mjs +8 -0
- package/app-template/public/locales/en/account.json +4 -0
- package/app-template/public/locales/en/common.json +10 -0
- package/app-template/public/locales/tr/account.json +4 -0
- package/app-template/public/locales/tr/common.json +10 -0
- package/app-template/src/__tests__/middleware-matcher.test.ts +135 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/cancellation/page.tsx +93 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/page.tsx +66 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/page.tsx +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/address/stores/page.tsx +2 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/auth/page.tsx +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +2 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +12 -15
- package/app-template/src/app/[commerce]/[locale]/[currency]/forms/[pk]/generate/page.tsx +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/{pz-not-found/page.tsx → not-found.tsx} +2 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +7 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +47 -1
- package/app-template/src/assets/globals.scss +162 -34
- package/app-template/src/components/__tests__/badge.test.tsx +2 -2
- package/app-template/src/components/accordion.tsx +1 -1
- package/app-template/src/components/button.tsx +50 -35
- package/app-template/src/components/file-input.tsx +44 -2
- package/app-template/src/components/input.tsx +3 -3
- package/app-template/src/components/modal.tsx +1 -1
- package/app-template/src/components/select.tsx +2 -2
- package/app-template/src/components/shimmer.tsx +1 -1
- package/app-template/src/components/tabs.tsx +2 -2
- package/app-template/src/components/types/index.ts +4 -1
- package/app-template/src/middleware.ts +1 -0
- package/app-template/src/plugins.js +2 -1
- package/app-template/src/redux/middlewares/category.ts +1 -1
- package/app-template/src/redux/reducers/category.ts +1 -1
- package/app-template/src/redux/store.ts +4 -3
- package/app-template/src/settings.js +1 -2
- package/app-template/src/utils/convert-facet-search-params.ts +1 -1
- package/app-template/src/views/account/contact-form.tsx +3 -8
- package/app-template/src/views/account/content-header.tsx +2 -3
- package/app-template/src/views/account/order.tsx +1 -1
- package/app-template/src/views/account/orders/order-cancellation-item.tsx +5 -4
- package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +1 -1
- package/app-template/src/views/basket/basket-item.tsx +1 -0
- package/app-template/src/views/category/category-active-filters.tsx +1 -1
- package/app-template/src/views/category/category-header.tsx +12 -6
- package/app-template/src/views/category/category-info.tsx +4 -4
- package/app-template/src/views/category/filters/index.tsx +2 -2
- package/app-template/src/views/checkout/auth.tsx +1 -1
- package/app-template/src/views/checkout/layout/header.tsx +1 -1
- package/app-template/src/views/checkout/steps/payment/index.tsx +1 -1
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -1
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +4 -4
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +3 -3
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +1 -1
- package/app-template/src/views/checkout/summary.tsx +2 -2
- package/app-template/src/views/header/action-menu.tsx +6 -3
- package/app-template/src/views/header/band.tsx +2 -2
- package/app-template/src/views/header/mini-basket.tsx +15 -4
- package/app-template/src/views/header/mobile-menu.tsx +6 -6
- package/app-template/src/views/header/navbar.tsx +1 -1
- package/app-template/src/views/header/pwa-back-button.tsx +1 -1
- package/app-template/src/views/header/search/index.tsx +16 -4
- package/app-template/src/views/header/search/results.tsx +1 -1
- package/app-template/src/views/header/user-menu.tsx +3 -1
- package/app-template/src/views/installment-options/index.tsx +1 -1
- package/app-template/src/views/login/index.tsx +30 -6
- package/app-template/src/views/otp-login/index.tsx +12 -14
- package/app-template/src/views/product/product-info.tsx +2 -2
- package/app-template/src/views/product/slider.tsx +1 -1
- package/app-template/src/views/product-pointer-banner-item.tsx +1 -1
- package/app-template/src/views/register/index.tsx +29 -4
- package/app-template/src/views/sales-contract-modal/index.tsx +17 -17
- package/app-template/src/widgets/footer-info.tsx +1 -1
- package/app-template/src/widgets/footer-menu.tsx +1 -1
- package/app-template/src/widgets/footer-subscription/index.tsx +1 -1
- package/app-template/src/widgets/home-stories-eng.tsx +1 -1
- package/app-template/tailwind.config.js +1 -137
- package/codemods/sentry-9/index.js +30 -0
- package/codemods/sentry-9/remove-sentry-configs.js +14 -0
- package/codemods/sentry-9/remove-sentry-dependency.js +25 -0
- package/codemods/sentry-9/replace-error-page.js +32 -0
- package/commands/codemod.ts +18 -0
- package/commands/index.ts +3 -1
- package/commands/plugins.ts +4 -0
- package/dist/codemods/sentry-9/templates/error.js +14 -0
- package/dist/commands/codemod.js +16 -0
- package/dist/commands/index.js +3 -1
- package/dist/commands/plugins.js +4 -0
- package/package.json +1 -1
- package/app-template/postcss.config.js +0 -6
- package/app-template/sentry.client.config.ts +0 -16
- package/app-template/sentry.edge.config.ts +0 -3
- package/app-template/sentry.properties +0 -4
- package/app-template/sentry.server.config.ts +0 -3
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
|
@@ -130,7 +130,7 @@ const AddressBox = ({
|
|
|
130
130
|
onClick={() => handleAddressClick(addressType, address)}
|
|
131
131
|
key={address.pk}
|
|
132
132
|
className={clsx(
|
|
133
|
-
'cursor-pointer relative w-full border shadow p-4 min-h-[8rem]',
|
|
133
|
+
'cursor-pointer relative w-full border border-gray-200 shadow-sm p-4 min-h-[8rem]',
|
|
134
134
|
"hover:after:content-[''] hover:after:border-4 hover:after:opacity-30 hover:after:transition-opacity",
|
|
135
135
|
'after:border-secondary-400 after:absolute after:inset-0 after:duration-150 after:-z-10',
|
|
136
136
|
{
|
|
@@ -167,7 +167,7 @@ const AddressBox = ({
|
|
|
167
167
|
<div className="text-xs flex justify-between">
|
|
168
168
|
<Button
|
|
169
169
|
appearance="ghost"
|
|
170
|
-
className="italic underline hover:text-secondary-500 hover
|
|
170
|
+
className="italic underline hover:text-secondary-500 hover:bg-white! hover:border-white! p-0 h-auto"
|
|
171
171
|
onClick={() => setIsEditAddressModalOpen(true)}
|
|
172
172
|
data-testid="checkout-address-edit"
|
|
173
173
|
>
|
|
@@ -193,7 +193,7 @@ const AddressBox = ({
|
|
|
193
193
|
</Modal>
|
|
194
194
|
<Button
|
|
195
195
|
appearance="ghost"
|
|
196
|
-
className="italic underline hover:text-secondary-500 hover
|
|
196
|
+
className="italic underline hover:text-secondary-500 hover:bg-white! hover:border-white! p-0 h-auto"
|
|
197
197
|
onClick={() => setRemoveAddressModalOpen(true)}
|
|
198
198
|
data-testid="checkout-address-remove"
|
|
199
199
|
>
|
|
@@ -153,7 +153,7 @@ const Addresses = () => {
|
|
|
153
153
|
role="button"
|
|
154
154
|
onClick={() => setIsModalOpen(true)}
|
|
155
155
|
className={clsx(
|
|
156
|
-
'relative cursor-pointer w-full min-h-[8rem] border shadow p-4',
|
|
156
|
+
'relative cursor-pointer w-full min-h-[8rem] border border-gray-200 shadow-sm p-4',
|
|
157
157
|
"hover:after:content-[''] hover:after:border-4 hover:after:opacity-30 hover:after:transition-opacity",
|
|
158
158
|
'after:border-secondary-400 after:absolute after:inset-0 after:opacity-0 after:duration-150 after:-z-10',
|
|
159
159
|
{
|
|
@@ -149,7 +149,7 @@ export const Summary = () => {
|
|
|
149
149
|
</div>
|
|
150
150
|
</div>
|
|
151
151
|
<div className="flex flex-col py-4 px-4 text-black-800 text-xs sm:px-5">
|
|
152
|
-
<div className="w-full overflow-hidden
|
|
152
|
+
<div className="w-full overflow-hidden text-ellipsis mb-1 last:mb-0">
|
|
153
153
|
<Trans
|
|
154
154
|
i18nKey="checkout.summary.info"
|
|
155
155
|
components={{
|
|
@@ -162,7 +162,7 @@ export const Summary = () => {
|
|
|
162
162
|
}}
|
|
163
163
|
/>
|
|
164
164
|
</div>
|
|
165
|
-
<div className="w-full overflow-hidden
|
|
165
|
+
<div className="w-full overflow-hidden text-ellipsis mb-1 last:mb-0">
|
|
166
166
|
{preOrder.shipping_address?.line}{' '}
|
|
167
167
|
{preOrder.shipping_address?.postcode}{' '}
|
|
168
168
|
{preOrder.shipping_address?.district && (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ReactNode, useMemo, useRef, useCallback } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { useGetMiniBasketQuery } from '@akinon/next/data/client/basket';
|
|
5
5
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
6
6
|
import {
|
|
7
7
|
closeMiniBasket,
|
|
@@ -29,8 +29,11 @@ interface MenuItem {
|
|
|
29
29
|
export default function ActionMenu() {
|
|
30
30
|
const dispatch = useAppDispatch();
|
|
31
31
|
|
|
32
|
-
const { data } =
|
|
33
|
-
const totalQuantity = useMemo(
|
|
32
|
+
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
33
|
+
const totalQuantity = useMemo(
|
|
34
|
+
() => miniBasket?.total_quantity ?? 0,
|
|
35
|
+
[miniBasket]
|
|
36
|
+
);
|
|
34
37
|
|
|
35
38
|
const { open: miniBasketOpen } = useAppSelector(
|
|
36
39
|
(state) => state.root.miniBasket
|
|
@@ -16,13 +16,13 @@ export default function HeaderBand() {
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div className="header-grid-area-nav bg-gray-100 h-auto p-3 sm:header-grid-area-band-m sm:h-9 sm:py-0">
|
|
19
|
-
<div className="text-center
|
|
19
|
+
<div className="text-center text-ellipsis line-clamp-2 h-full flex items-center justify-center">
|
|
20
20
|
<HeaderBandText />
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
24
|
<div className="header-grid-area-main-r h-full pr-4 sm:header-grid-area-band-r sm:pr-0">
|
|
25
|
-
<div className="flex items-center justify-end h-full">
|
|
25
|
+
<div className="flex items-center justify-end h-full gap-10">
|
|
26
26
|
<UserMenu isMobile={false} />
|
|
27
27
|
<ActionMenu />
|
|
28
28
|
</div>
|
|
@@ -5,6 +5,7 @@ import clsx from 'clsx';
|
|
|
5
5
|
import {
|
|
6
6
|
basketApi,
|
|
7
7
|
useGetBasketQuery,
|
|
8
|
+
useGetMiniBasketQuery,
|
|
8
9
|
useUpdateQuantityMutation
|
|
9
10
|
} from '@akinon/next/data/client/basket';
|
|
10
11
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
@@ -150,13 +151,23 @@ export default function MiniBasket() {
|
|
|
150
151
|
(state) => state.root.miniBasket
|
|
151
152
|
);
|
|
152
153
|
const dispatch = useAppDispatch();
|
|
153
|
-
const {
|
|
154
|
+
const {
|
|
155
|
+
data: basket,
|
|
156
|
+
isLoading,
|
|
157
|
+
isSuccess
|
|
158
|
+
} = useGetBasketQuery(undefined, {
|
|
159
|
+
skip: !miniBasketOpen
|
|
160
|
+
});
|
|
161
|
+
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
154
162
|
const { t } = useLocalization();
|
|
155
163
|
const { highlightedItem } = useAppSelector((state) => state.root.miniBasket);
|
|
156
164
|
const [highlightedItemPk, setHighlightedItemPk] = useState(0);
|
|
157
165
|
const [sortedBasket, setSortedBasket] = useState([]);
|
|
158
166
|
|
|
159
|
-
const totalQuantity = useMemo(
|
|
167
|
+
const totalQuantity = useMemo(
|
|
168
|
+
() => miniBasket?.total_quantity ?? 0,
|
|
169
|
+
[miniBasket]
|
|
170
|
+
);
|
|
160
171
|
const miniBasketList = useRef(null);
|
|
161
172
|
|
|
162
173
|
useEffect(() => {
|
|
@@ -192,7 +203,7 @@ export default function MiniBasket() {
|
|
|
192
203
|
miniBasketOpen
|
|
193
204
|
? 'opacity-100 visible lg:invisible'
|
|
194
205
|
: 'opacity-0 invisible',
|
|
195
|
-
'fixed top-0 left-0 z-50 w-screen h-screen bg-black
|
|
206
|
+
'fixed top-0 left-0 z-50 w-screen h-screen bg-black/80 transition-all duration-300'
|
|
196
207
|
)}
|
|
197
208
|
onClick={() => {
|
|
198
209
|
dispatch(closeMiniBasket());
|
|
@@ -206,7 +217,7 @@ export default function MiniBasket() {
|
|
|
206
217
|
'fixed lg:absolute bottom-0 lg:-bottom-1 right-0 w-80 h-screen lg:h-auto bg-white lg:border-l lg:border-t lg:border-r-2 lg:border-b-2 lg:border-gray-500 p-5 z-50 transition-all duration-300'
|
|
207
218
|
)}
|
|
208
219
|
>
|
|
209
|
-
<header className="flex items-center gap-2 pb-3 border-b">
|
|
220
|
+
<header className="flex items-center gap-2 pb-3 border-b border-gray-200">
|
|
210
221
|
<h3 className="text-xl font-bold">
|
|
211
222
|
{t('basket.mini_basket.my_bag')}
|
|
212
223
|
</h3>
|
|
@@ -58,9 +58,9 @@ export default function MobileMenu(props: MobileMenuProps) {
|
|
|
58
58
|
<>
|
|
59
59
|
<div
|
|
60
60
|
className={clsx(
|
|
61
|
-
'fixed top-0 left-0 z-30 w-screen h-screen invisible opacity-0 bg-black
|
|
61
|
+
'fixed top-0 left-0 z-30 w-screen h-screen invisible opacity-0 bg-black/80 transition duration-500',
|
|
62
62
|
{
|
|
63
|
-
'!
|
|
63
|
+
'visible! opacity-100! scroll-lock': isMobileMenuOpen
|
|
64
64
|
}
|
|
65
65
|
)}
|
|
66
66
|
/>
|
|
@@ -70,7 +70,7 @@ export default function MobileMenu(props: MobileMenuProps) {
|
|
|
70
70
|
className={clsx(
|
|
71
71
|
'fixed top-0 left-0 z-50 flex flex-col bg-white w-72 pt-4 h-screen invisible opacity-0 transition duration-500 transform -translate-x-72',
|
|
72
72
|
{
|
|
73
|
-
'!
|
|
73
|
+
'visible! opacity-100! translate-x-0': isMobileMenuOpen
|
|
74
74
|
}
|
|
75
75
|
)}
|
|
76
76
|
>
|
|
@@ -106,15 +106,15 @@ export default function MobileMenu(props: MobileMenuProps) {
|
|
|
106
106
|
className={clsx(
|
|
107
107
|
'absolute top-0 left-0 right-0 px-8 bg-white invisible opacity-0 transition duration-500 transform translate-x-full',
|
|
108
108
|
{
|
|
109
|
-
'!
|
|
109
|
+
'visible! opacity-100! translate-x-0!': selectedSubMenu
|
|
110
110
|
}
|
|
111
111
|
)}
|
|
112
112
|
>
|
|
113
|
-
<header className="flex items-center justify-between border-b h-[61px] py-4 mb-4">
|
|
113
|
+
<header className="flex items-center justify-between border-b border-gray-200 h-[61px] py-4 mb-4">
|
|
114
114
|
<Button
|
|
115
115
|
appearance="ghost"
|
|
116
116
|
onClick={() => setSelectedSubMenu(null)}
|
|
117
|
-
className="underline text-xs font-semibold flex items-center gap-2
|
|
117
|
+
className="underline text-xs font-semibold flex items-center gap-2 p-0!"
|
|
118
118
|
>
|
|
119
119
|
<Icon name="chevron-start" size={12} className="shrink-0" />
|
|
120
120
|
{t('common.mobile_menu.back')}
|
|
@@ -34,7 +34,7 @@ export const PwaBackButton = () => {
|
|
|
34
34
|
return (
|
|
35
35
|
<div className="relative z-10 md:top-0 md:left-0 md:fixed">
|
|
36
36
|
<button
|
|
37
|
-
className="bg-secondary-600 text-white flex items-center justify-center
|
|
37
|
+
className="bg-secondary-600 text-white flex items-center justify-center shrink-0 w-12 h-12 md:w-10 md:h-9 active:bg-secondary-700"
|
|
38
38
|
onClick={() => router.back()}
|
|
39
39
|
>
|
|
40
40
|
<svg
|
|
@@ -22,19 +22,31 @@ export default function Search() {
|
|
|
22
22
|
if (isSearchOpen) {
|
|
23
23
|
inputRef.current?.focus();
|
|
24
24
|
document.body.style.overflow = 'hidden';
|
|
25
|
+
|
|
26
|
+
const handleEscKey = (e: KeyboardEvent) => {
|
|
27
|
+
if (e.key === 'Escape') {
|
|
28
|
+
dispatch(closeSearch());
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
document.addEventListener('keydown', handleEscKey);
|
|
33
|
+
return () => {
|
|
34
|
+
document.removeEventListener('keydown', handleEscKey);
|
|
35
|
+
document.body.style.overflow = 'auto';
|
|
36
|
+
};
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
return () => {
|
|
28
40
|
document.body.style.overflow = 'auto';
|
|
29
41
|
};
|
|
30
|
-
}, [isSearchOpen]);
|
|
42
|
+
}, [isSearchOpen, dispatch]);
|
|
31
43
|
|
|
32
44
|
return (
|
|
33
45
|
<>
|
|
34
46
|
<div
|
|
35
47
|
className={clsx(
|
|
36
48
|
// 177px is the height of the header
|
|
37
|
-
'absolute bg-black
|
|
49
|
+
'absolute bg-black/75 w-screen h-screen transition duration-500 left-0 bottom-0 translate-y-full z-30',
|
|
38
50
|
isSearchOpen && searchText
|
|
39
51
|
? 'visible opacity-100'
|
|
40
52
|
: 'invisible opacity-0'
|
|
@@ -48,7 +60,7 @@ export default function Search() {
|
|
|
48
60
|
isSearchOpen ? 'visible opacity-100' : 'invisible opacity-0'
|
|
49
61
|
)}
|
|
50
62
|
>
|
|
51
|
-
<div className="max-w-
|
|
63
|
+
<div className="max-w-(--breakpoint-2xl) mx-auto flex flex-col gap-12">
|
|
52
64
|
<div className="border-b border-gray-400 flex flex-col py-1.5 gap-2 self-center items-center md:flex-row">
|
|
53
65
|
<span className="text-xl lg:text-2xl">
|
|
54
66
|
{t('common.search.results_for')}
|
|
@@ -62,7 +74,7 @@ export default function Search() {
|
|
|
62
74
|
router.push(`${ROUTES.LIST}/?search_text=${searchText}`);
|
|
63
75
|
}
|
|
64
76
|
}}
|
|
65
|
-
className="border-0 text-2xl outline-
|
|
77
|
+
className="border-0 text-2xl outline-hidden text-secondary placeholder:text-xl lg:placeholder:text-2xl"
|
|
66
78
|
placeholder={t('common.search.placeholder')}
|
|
67
79
|
ref={inputRef}
|
|
68
80
|
/>
|
|
@@ -80,7 +80,7 @@ export default function Results(props: ResultsProps) {
|
|
|
80
80
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-8">
|
|
81
81
|
{products.map((product, index) => (
|
|
82
82
|
<Link href={product?.url} key={index} className="flex flex-col">
|
|
83
|
-
<div className="relative aspect-
|
|
83
|
+
<div className="relative aspect-315/448">
|
|
84
84
|
{product.extra.image ? (
|
|
85
85
|
<Image
|
|
86
86
|
src={product.extra.image}
|
|
@@ -36,7 +36,9 @@ export const UserMenu = (props: UserMenuProps) => {
|
|
|
36
36
|
<ul
|
|
37
37
|
className={clsx(
|
|
38
38
|
'items-center divide-x divide-black',
|
|
39
|
-
isMobile
|
|
39
|
+
isMobile
|
|
40
|
+
? 'flex pt-2 text-sm pb-6 border-b border-gray-200 mx-8'
|
|
41
|
+
: 'hidden sm:flex'
|
|
40
42
|
)}
|
|
41
43
|
id="user-menu"
|
|
42
44
|
>
|
|
@@ -103,10 +103,34 @@ export const Login = () => {
|
|
|
103
103
|
)?.data as string[];
|
|
104
104
|
|
|
105
105
|
fieldErrors?.forEach((item) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
let parsedValue: Record<string, string[]> | string[] = [];
|
|
107
|
+
|
|
108
|
+
if (typeof item.value === 'string') {
|
|
109
|
+
try {
|
|
110
|
+
parsedValue = JSON.parse(item.value);
|
|
111
|
+
} catch {
|
|
112
|
+
parsedValue = [item.value];
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
parsedValue = item.value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (Array.isArray(parsedValue)) {
|
|
119
|
+
setError(item.name as keyof LoginFormType, {
|
|
120
|
+
type: 'custom',
|
|
121
|
+
message: parsedValue.join(', '),
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
Object.keys(parsedValue).forEach((key) => {
|
|
125
|
+
const fieldName = key as keyof LoginFormType;
|
|
126
|
+
const errorMessages = parsedValue[key] as string[];
|
|
127
|
+
|
|
128
|
+
setError(fieldName, {
|
|
129
|
+
type: 'custom',
|
|
130
|
+
message: errorMessages.join(', '),
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
110
134
|
});
|
|
111
135
|
|
|
112
136
|
if (nonFieldErrors?.length) {
|
|
@@ -195,7 +219,7 @@ export const Login = () => {
|
|
|
195
219
|
{t('auth.login.form.submit')}
|
|
196
220
|
</Button>
|
|
197
221
|
|
|
198
|
-
<p className="relative text-gray-600 text-center my-4 before:absolute before:h-[1px] before:w-5/12 before:bg-gray-600
|
|
222
|
+
<p className="relative text-gray-600 text-center my-4 before:absolute before:h-[1px] before:w-5/12 before:bg-gray-600/25 before:top-1/2 before:left-0 after:absolute after:h-[1px] after:w-5/12 after:bg-gray-600/25 after:top-1/2 after:right-0">
|
|
199
223
|
{t('auth.login.form.or')}
|
|
200
224
|
</p>
|
|
201
225
|
|
|
@@ -224,7 +248,7 @@ export const Login = () => {
|
|
|
224
248
|
alt={provider.label}
|
|
225
249
|
width={provider.label === 'Facebook' ? 10 : 18}
|
|
226
250
|
height={18}
|
|
227
|
-
className="
|
|
251
|
+
className="shrink-0"
|
|
228
252
|
/>
|
|
229
253
|
)}
|
|
230
254
|
|
|
@@ -10,9 +10,10 @@ import { yupResolver } from '@hookform/resolvers/yup';
|
|
|
10
10
|
import clsx from 'clsx';
|
|
11
11
|
import { useLocalization } from '@akinon/next/hooks';
|
|
12
12
|
import { useOtpLoginMutation } from '@akinon/next/data/client/user';
|
|
13
|
-
import { useAppSelector } from '@akinon/next/redux/hooks';
|
|
13
|
+
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
14
14
|
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
15
15
|
import { AuthError } from '@akinon/next/types';
|
|
16
|
+
import { showPopup } from '@akinon/pz-otp/src/redux/reducer';
|
|
16
17
|
|
|
17
18
|
const loginFormSchema = (t) =>
|
|
18
19
|
yup.object().shape({
|
|
@@ -25,9 +26,9 @@ const loginFormSchema = (t) =>
|
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
export const OtpLogin = () => {
|
|
29
|
+
const dispatch = useAppDispatch();
|
|
28
30
|
const { user_phone_format } = useAppSelector((state) => state.config);
|
|
29
31
|
const { t, locale } = useLocalization();
|
|
30
|
-
const [showOtpModal, setShowOtpModal] = useState(false);
|
|
31
32
|
const [otpLoginMutation] = useOtpLoginMutation();
|
|
32
33
|
|
|
33
34
|
const {
|
|
@@ -79,7 +80,7 @@ export const OtpLogin = () => {
|
|
|
79
80
|
})
|
|
80
81
|
.unwrap()
|
|
81
82
|
.then(() => {
|
|
82
|
-
|
|
83
|
+
dispatch(showPopup());
|
|
83
84
|
})
|
|
84
85
|
.catch((error) => {
|
|
85
86
|
if (error.status === 429) {
|
|
@@ -136,17 +137,14 @@ export const OtpLogin = () => {
|
|
|
136
137
|
</Button>
|
|
137
138
|
</form>
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}}
|
|
148
|
-
/>
|
|
149
|
-
)}
|
|
140
|
+
<PluginModule
|
|
141
|
+
component={Component.Otp}
|
|
142
|
+
props={{
|
|
143
|
+
data: getValues(),
|
|
144
|
+
submitAction: loginHandler,
|
|
145
|
+
error: formError
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
150
148
|
</section>
|
|
151
149
|
);
|
|
152
150
|
};
|
|
@@ -159,7 +159,7 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
159
159
|
<>
|
|
160
160
|
<div
|
|
161
161
|
className={clsx(
|
|
162
|
-
'fixed bottom-0 left-0 w-1/2 h-14 z-
|
|
162
|
+
'fixed bottom-0 left-0 w-1/2 h-14 z-20 bg-white mt-0 border-t border-gray-500 items-center justify-center',
|
|
163
163
|
'sm:relative sm:flex sm:items-center sm:mt-5 sm:border-none'
|
|
164
164
|
)}
|
|
165
165
|
>
|
|
@@ -188,7 +188,7 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
188
188
|
<Button
|
|
189
189
|
disabled={isAddToCartLoading || isAddToStockAlertLoading}
|
|
190
190
|
className={clsx(
|
|
191
|
-
'fixed bottom-0 right-0 w-1/2 h-14 z-
|
|
191
|
+
'fixed bottom-0 right-0 w-1/2 h-14 z-20 flex items-center justify-center fill-primary-foreground',
|
|
192
192
|
'hover:fill-primary sm:relative sm:w-full sm:mt-3 sm:font-semibold sm:h-12'
|
|
193
193
|
)}
|
|
194
194
|
onClick={() => {
|
|
@@ -86,7 +86,7 @@ export default function ProductInfoSlider({ product }: ProductSliderItem) {
|
|
|
86
86
|
</div>
|
|
87
87
|
|
|
88
88
|
<div className="relative lg:col-span-5">
|
|
89
|
-
<FavButton className="absolute right-8 top-6 z-
|
|
89
|
+
<FavButton className="absolute right-8 top-6 z-20 sm:hidden" />
|
|
90
90
|
|
|
91
91
|
<CarouselCore
|
|
92
92
|
responsive={{
|
|
@@ -109,7 +109,7 @@ const ProductPointerWidget = (props: ProductPointerWidgetProps) => {
|
|
|
109
109
|
hidden: buttonStatus
|
|
110
110
|
})}
|
|
111
111
|
>
|
|
112
|
-
<div className="w-full h-full flex items-center gap-2
|
|
112
|
+
<div className="w-full h-full flex items-center gap-2 shrink-0">
|
|
113
113
|
<Image
|
|
114
114
|
src={productItem?.kwargs?.value?.card_image?.url}
|
|
115
115
|
alt={productItem?.value?.alt}
|
|
@@ -143,6 +143,7 @@ export const Register = () => {
|
|
|
143
143
|
if (registerResponse.error) {
|
|
144
144
|
const errors: AuthError[] = JSON.parse(registerResponse.error);
|
|
145
145
|
|
|
146
|
+
|
|
146
147
|
if (errors.find((error) => error.type === 'captcha')) {
|
|
147
148
|
if (await validateCaptcha()) {
|
|
148
149
|
onSubmit(data);
|
|
@@ -164,10 +165,34 @@ export const Register = () => {
|
|
|
164
165
|
)?.data as string[];
|
|
165
166
|
|
|
166
167
|
fieldErrors?.forEach((item) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
let parsedValue: Record<string, string[]> | string[] = [];
|
|
169
|
+
|
|
170
|
+
if (typeof item.value === 'string') {
|
|
171
|
+
try {
|
|
172
|
+
parsedValue = JSON.parse(item.value);
|
|
173
|
+
} catch {
|
|
174
|
+
parsedValue = [item.value];
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
parsedValue = item.value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (Array.isArray(parsedValue)) {
|
|
181
|
+
setError(item.name as keyof RegisterFormType, {
|
|
182
|
+
type: 'custom',
|
|
183
|
+
message: parsedValue.join(', '),
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
Object.keys(parsedValue).forEach((key) => {
|
|
187
|
+
const fieldName = key as keyof RegisterFormType;
|
|
188
|
+
const errorMessages = parsedValue[key] as string[];
|
|
189
|
+
|
|
190
|
+
setError(fieldName, {
|
|
191
|
+
type: 'custom',
|
|
192
|
+
message: errorMessages.join(', '),
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
171
196
|
});
|
|
172
197
|
|
|
173
198
|
if (nonFieldErrors?.length) {
|
|
@@ -169,19 +169,19 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
169
169
|
</div>
|
|
170
170
|
</div>
|
|
171
171
|
|
|
172
|
-
<table className="w-full border">
|
|
172
|
+
<table className="w-full border border-gray-200">
|
|
173
173
|
<thead className="text-sm">
|
|
174
174
|
<tr>
|
|
175
|
-
<th className="border font-normal text-left py-2 px-3">
|
|
175
|
+
<th className="border border-gray-200 font-normal text-left py-2 px-3">
|
|
176
176
|
{t('account.sales_contract.product')}
|
|
177
177
|
</th>
|
|
178
|
-
<th className="border font-normal py-2 px-3">
|
|
178
|
+
<th className="border border-gray-200 font-normal py-2 px-3">
|
|
179
179
|
{t('account.sales_contract.quantity')}
|
|
180
180
|
</th>
|
|
181
|
-
<th className="border font-normal text-left py-2 px-3">
|
|
181
|
+
<th className="border border-gray-200 font-normal text-left py-2 px-3">
|
|
182
182
|
{t('account.sales_contract.product_price')}
|
|
183
183
|
</th>
|
|
184
|
-
<th className="border font-normal text-left py-2 px-3">
|
|
184
|
+
<th className="border border-gray-200 font-normal text-left py-2 px-3">
|
|
185
185
|
{t('account.sales_contract.total_price')}
|
|
186
186
|
</th>
|
|
187
187
|
</tr>
|
|
@@ -191,20 +191,20 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
191
191
|
{data.orderitem_set.map((value, index) => {
|
|
192
192
|
return (
|
|
193
193
|
<tr key={index.toString()}>
|
|
194
|
-
<td className="border font-light text-left py-2 px-3">
|
|
194
|
+
<td className="border border-gray-200 font-light text-left py-2 px-3">
|
|
195
195
|
{value.product.name}
|
|
196
196
|
</td>
|
|
197
|
-
<td className="border font-light text-center py-2 px-3">
|
|
197
|
+
<td className="border border-gray-200 font-light text-center py-2 px-3">
|
|
198
198
|
{value.quantity ? value.quantity : '1'}
|
|
199
199
|
</td>
|
|
200
|
-
<td className="border font-light text-center py-2 px-3">
|
|
200
|
+
<td className="border border-gray-200 font-light text-center py-2 px-3">
|
|
201
201
|
<Price
|
|
202
202
|
value={
|
|
203
203
|
value.unit_price ? value.unit_price : value.price
|
|
204
204
|
}
|
|
205
205
|
/>
|
|
206
206
|
</td>
|
|
207
|
-
<td className="border font-light text-right py-2 px-3">
|
|
207
|
+
<td className="border border-gray-200 font-light text-right py-2 px-3">
|
|
208
208
|
<Price
|
|
209
209
|
value={
|
|
210
210
|
value.total_amount
|
|
@@ -222,12 +222,12 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
222
222
|
<tr>
|
|
223
223
|
<th
|
|
224
224
|
colSpan={3}
|
|
225
|
-
className="border text-left py-2 px-3 font-normal"
|
|
225
|
+
className="border border-gray-200 text-left py-2 px-3 font-normal"
|
|
226
226
|
>
|
|
227
227
|
{t('account.sales_contract.products_total')}
|
|
228
228
|
</th>
|
|
229
229
|
<td
|
|
230
|
-
className="border text-right py-2 px-3 font-light"
|
|
230
|
+
className="border border-gray-200 text-right py-2 px-3 font-light"
|
|
231
231
|
colSpan={1}
|
|
232
232
|
>
|
|
233
233
|
<Price value={data.discount_amount} />
|
|
@@ -237,12 +237,12 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
237
237
|
<tr>
|
|
238
238
|
<th
|
|
239
239
|
colSpan={3}
|
|
240
|
-
className="border text-left py-2 px-3 font-normal"
|
|
240
|
+
className="border border-gray-200 text-left py-2 px-3 font-normal"
|
|
241
241
|
>
|
|
242
242
|
{t('account.sales_contract.shipping_price')}
|
|
243
243
|
</th>
|
|
244
244
|
<td
|
|
245
|
-
className="border text-right py-2 px-3 font-light"
|
|
245
|
+
className="border border-gray-200 text-right py-2 px-3 font-light"
|
|
246
246
|
colSpan={1}
|
|
247
247
|
>
|
|
248
248
|
<Price value={data.shipping_amount} />
|
|
@@ -252,12 +252,12 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
252
252
|
<tr>
|
|
253
253
|
<th
|
|
254
254
|
colSpan={3}
|
|
255
|
-
className="border text-left py-2 px-3 font-normal"
|
|
255
|
+
className="border border-gray-200 text-left py-2 px-3 font-normal"
|
|
256
256
|
>
|
|
257
257
|
{t('account.sales_contract.payment_type')}
|
|
258
258
|
</th>
|
|
259
259
|
<td
|
|
260
|
-
className="border text-right py-2 px-3 font-light"
|
|
260
|
+
className="border border-gray-200 text-right py-2 px-3 font-light"
|
|
261
261
|
colSpan={1}
|
|
262
262
|
>
|
|
263
263
|
{data.payment_option_slug}
|
|
@@ -267,12 +267,12 @@ export const SalesContractModal = ({ data }: Props) => {
|
|
|
267
267
|
<tr>
|
|
268
268
|
<th
|
|
269
269
|
colSpan={3}
|
|
270
|
-
className="border text-left py-2 px-3 font-normal"
|
|
270
|
+
className="border border-gray-200 text-left py-2 px-3 font-normal"
|
|
271
271
|
>
|
|
272
272
|
{t('account.sales_contract.order_total')}
|
|
273
273
|
</th>
|
|
274
274
|
<td
|
|
275
|
-
className="border text-right py-2 px-3 font-light"
|
|
275
|
+
className="border border-gray-200 text-right py-2 px-3 font-light"
|
|
276
276
|
colSpan={1}
|
|
277
277
|
>
|
|
278
278
|
<Price value={data.amount} />
|
|
@@ -19,7 +19,7 @@ export default async function FooterInfo() {
|
|
|
19
19
|
const data = await getWidgetData<FooterInfoType>({ slug: 'footer-info' });
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
|
-
<div className="flex flex-col w-full mb-4 pb-4 text-xs border-b md:border-b-0 md:pb-0 md:mb-0 md:w-3/12 lg:w-1/6 md:pr-7">
|
|
22
|
+
<div className="flex flex-col w-full mb-4 pb-4 text-xs border-b border-gray-200 md:border-b-0 md:pb-0 md:mb-0 md:w-3/12 lg:w-1/6 md:pr-7">
|
|
23
23
|
<div className="flex items-center mb-8">
|
|
24
24
|
<div className="mr-3">
|
|
25
25
|
<svg
|
|
@@ -50,7 +50,7 @@ export default async function FooterMenu() {
|
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
52
|
<div className="flex-1">
|
|
53
|
-
<div className="hidden justify-between text-xs md:flex md:px-6 md:py-4 md:border-r md:border-l">
|
|
53
|
+
<div className="hidden justify-between text-xs md:flex md:px-6 md:py-4 md:border-r md:border-l md:border-gray-200">
|
|
54
54
|
<div>
|
|
55
55
|
<div className="mb-4 font-medium" data-testid="footer-categories">
|
|
56
56
|
{data?.attributes?.first_column_title?.value}
|
|
@@ -19,7 +19,7 @@ export default async function FooterSubscription() {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
|
-
<div className="py-4 border-t md:border-t-0 lg:pl-7">
|
|
22
|
+
<div className="py-4 border-t border-gray-200 md:border-t-0 lg:pl-7">
|
|
23
23
|
<h3 className="mb-1 text-xs font-medium">
|
|
24
24
|
{data?.attributes?.title?.value}
|
|
25
25
|
</h3>
|
|
@@ -82,7 +82,7 @@ export default async function HomeStoriesEng() {
|
|
|
82
82
|
{data?.attributes?.stories?.map((story, index) => {
|
|
83
83
|
return (
|
|
84
84
|
<div
|
|
85
|
-
className="mr-4
|
|
85
|
+
className="mr-4 shrink-0 first:ms-4 w-32 md:w-32"
|
|
86
86
|
key={`story__${index}`}
|
|
87
87
|
>
|
|
88
88
|
<Link href={story?.value?.url} aria-label={story?.value?.alt}>
|