@akinon/projectzero 2.0.0-beta.12 → 2.0.0-beta.14
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 +102 -23
- package/app-template/.env.example +1 -0
- package/app-template/.github/instructions/account.instructions.md +749 -0
- package/app-template/.github/instructions/checkout.instructions.md +678 -0
- package/app-template/.github/instructions/default.instructions.md +279 -0
- package/app-template/.github/instructions/edge-cases.instructions.md +73 -0
- package/app-template/.github/instructions/routing.instructions.md +603 -0
- package/app-template/.github/instructions/settings.instructions.md +338 -0
- package/app-template/.gitignore +3 -0
- package/app-template/AGENTS.md +7 -0
- package/app-template/CHANGELOG.md +1387 -310
- package/app-template/Procfile +1 -1
- package/app-template/akinon.json +0 -3
- package/app-template/build.sh +10 -0
- package/app-template/docs/advanced-usage.md +101 -0
- package/app-template/docs/sentry-usage.md +35 -0
- package/app-template/next-env.d.ts +1 -0
- package/app-template/{next.config.ts → next.config.mjs} +6 -6
- package/app-template/package.json +58 -51
- package/app-template/postcss.config.mjs +1 -4
- package/app-template/public/locales/en/checkout.json +11 -0
- package/app-template/public/locales/en/common.json +50 -1
- package/app-template/public/locales/en/product.json +62 -1
- package/app-template/public/locales/tr/checkout.json +11 -0
- package/app-template/public/locales/tr/common.json +50 -1
- package/app-template/public/locales/tr/product.json +63 -0
- package/app-template/public/masterpass-javascript-sdk-web.min.js +1 -0
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/[...prettyurl]/page.tsx +9 -9
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/layout.tsx +2 -2
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/cancellation/page.tsx +6 -6
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/page.tsx +6 -6
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/page.tsx +1 -1
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/profile/page.tsx +2 -2
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/address/stores/page.tsx +2 -2
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/page.tsx +1 -1
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket/page.tsx +2 -2
- package/app-template/src/app/[pz]/category/[pk]/page.tsx +27 -0
- package/app-template/src/app/[pz]/flat-page/[pk]/page.tsx +23 -0
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/forms/[pk]/generate/page.tsx +2 -3
- package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +93 -0
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/page.tsx +2 -4
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/layout.tsx +3 -10
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/page.tsx +2 -4
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/not-found.tsx +5 -7
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/page.tsx +6 -4
- package/app-template/src/app/[pz]/product/[pk]/page.tsx +102 -0
- package/app-template/src/app/[pz]/special-page/[pk]/page.tsx +35 -0
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/email-set-primary/[[...id]]/page.tsx +3 -4
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/registration/account-confirm-email/[[...id]]/page.tsx +3 -3
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/reset/[[...id]]/page.tsx +6 -12
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/[node]/route.ts +2 -2
- package/app-template/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/app-template/src/app/api/form/[...id]/route.ts +1 -7
- package/app-template/src/app/api/image-proxy/route.ts +1 -0
- package/app-template/src/app/api/product-categories/route.ts +1 -0
- package/app-template/src/app/api/similar-product-list/route.ts +1 -0
- package/app-template/src/app/api/similar-products/route.ts +1 -0
- package/app-template/src/app/api/virtual-try-on/limited-categories/route.ts +1 -0
- package/app-template/src/app/api/virtual-try-on/route.ts +1 -0
- package/app-template/src/assets/globals.scss +4 -133
- package/app-template/src/auth.ts +3 -0
- package/app-template/src/components/__tests__/badge.test.tsx +2 -2
- package/app-template/src/components/__tests__/link.test.tsx +2 -0
- package/app-template/src/components/accordion.tsx +23 -20
- package/app-template/src/components/button.tsx +1 -1
- package/app-template/src/components/carousel-core.tsx +4 -11
- package/app-template/src/components/checkbox.tsx +1 -1
- package/app-template/src/components/currency-select.tsx +1 -0
- package/app-template/src/components/file-input.tsx +27 -7
- package/app-template/src/components/generate-form-fields.tsx +49 -10
- package/app-template/src/components/input.tsx +11 -5
- package/app-template/src/components/modal.tsx +32 -16
- package/app-template/src/components/pagination.tsx +1 -0
- package/app-template/src/components/price.tsx +1 -1
- package/app-template/src/components/pwa-tags.tsx +1 -0
- package/app-template/src/components/select.tsx +39 -27
- package/app-template/src/components/shimmer.tsx +1 -1
- package/app-template/src/components/types/index.ts +25 -1
- package/app-template/src/hooks/use-fav-button.tsx +4 -8
- package/app-template/src/hooks/use-product-cart.ts +77 -0
- package/app-template/src/hooks/use-stock-alert.ts +74 -0
- package/app-template/src/plugins.js +12 -2
- package/app-template/src/redux/middlewares/category.ts +5 -4
- package/app-template/src/redux/store.ts +21 -1
- package/app-template/src/routes/index.ts +2 -1
- package/app-template/src/settings.js +3 -1
- package/app-template/src/types/index.ts +74 -3
- package/app-template/src/types/next-auth.d.ts +2 -2
- package/app-template/src/utils/variant-validation.ts +41 -0
- package/app-template/src/views/account/address-form.tsx +8 -4
- package/app-template/src/views/account/contact-form.tsx +2 -2
- package/app-template/src/views/account/content-header.tsx +4 -3
- package/app-template/src/views/account/faq/faq-tabs.tsx +8 -2
- package/app-template/src/views/account/order.tsx +1 -1
- package/app-template/src/views/account/orders/order-cancellation-item.tsx +1 -1
- package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +1 -1
- package/app-template/src/views/basket/basket-item.tsx +6 -1
- package/app-template/src/views/basket/summary.tsx +16 -0
- package/app-template/src/views/breadcrumb.tsx +2 -2
- package/app-template/src/views/category/category-info.tsx +2 -1
- package/app-template/src/views/category/filters/index.tsx +1 -1
- 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/options/credit-card/index.tsx +22 -6
- package/app-template/src/views/checkout/steps/payment/options/funds-transfer.tsx +25 -5
- package/app-template/src/views/checkout/steps/payment/options/loyalty.tsx +21 -2
- package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +22 -4
- package/app-template/src/views/checkout/steps/payment/options/store-credit.tsx +121 -0
- 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 +12 -2
- package/app-template/src/views/find-in-store/index.tsx +2 -2
- package/app-template/src/views/header/action-menu.tsx +2 -6
- package/app-template/src/views/header/band.tsx +2 -2
- package/app-template/src/views/header/index.tsx +1 -1
- package/app-template/src/views/header/mini-basket.tsx +2 -2
- 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 +13 -3
- package/app-template/src/views/header/search/results.tsx +1 -1
- package/app-template/src/views/header/user-menu.tsx +1 -3
- package/app-template/src/views/login/index.tsx +14 -13
- package/app-template/src/views/otp-login/index.tsx +11 -6
- package/app-template/src/views/product/layout.tsx +15 -1
- package/app-template/src/views/product/product-actions.tsx +165 -0
- package/app-template/src/views/product/product-info.tsx +69 -261
- package/app-template/src/views/product/product-share.tsx +56 -0
- package/app-template/src/views/product/product-variants.tsx +26 -0
- package/app-template/src/views/product/slider.tsx +22 -1
- package/app-template/src/views/product-pointer-banner-item.tsx +1 -1
- package/app-template/src/views/register/index.tsx +17 -21
- 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 +7 -3
- package/app-template/src/widgets/footer-subscription/index.tsx +1 -1
- package/app-template/src/widgets/home-stories-eng.tsx +43 -35
- package/app-template/tailwind.config.js +129 -1
- package/app-template/tsconfig.json +29 -11
- package/codemods/migrate-segments/index.js +591 -0
- package/commands/plugins.ts +62 -14
- package/dist/commands/plugins.js +62 -14
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +0 -22
- package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +0 -20
- package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +0 -74
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +0 -84
- package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +0 -27
- package/app-template/src/pages/api/auth/[...nextauth].ts +0 -3
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/address/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-email/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-password/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/contact/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/coupons/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/email-verification/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/faq/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/favourite-products/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/my-quotations/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/layout.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/anonymous-tracking/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/oauth-login/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket-b2b/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/category/[pk]/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/client-root.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/contact-us/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/error.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/flat-page/[pk]/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/group-product/[pk]/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/checkout/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/layout.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/special-page/[pk]/loading.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/template.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/password/reset/page.tsx +0 -0
- /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/route.ts +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useAddStockAlertMutation } from '@akinon/next/data/client/wishlist';
|
|
3
|
+
import { Trans } from '@akinon/next/components/trans';
|
|
4
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
5
|
+
|
|
6
|
+
interface UseStockAlertProps {
|
|
7
|
+
productPk: number;
|
|
8
|
+
userEmail?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useStockAlert = ({ productPk, userEmail }: UseStockAlertProps) => {
|
|
12
|
+
const { t } = useLocalization();
|
|
13
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
14
|
+
const [stockAlertResponseMessage, setStockAlertResponseMessage] = useState<React.ReactNode | null>(null);
|
|
15
|
+
const [productError, setProductError] = useState<React.ReactNode | null>(null);
|
|
16
|
+
|
|
17
|
+
const [addStockAlert, { isLoading: isAddToStockAlertLoading }] = useAddStockAlertMutation();
|
|
18
|
+
|
|
19
|
+
const handleSuccess = () => {
|
|
20
|
+
setStockAlertResponseMessage(React.createElement(
|
|
21
|
+
Trans,
|
|
22
|
+
{
|
|
23
|
+
i18nKey: "product.stock_alert.success_description",
|
|
24
|
+
components: {
|
|
25
|
+
Email: React.createElement('span', {}, userEmail)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
));
|
|
29
|
+
setIsModalOpen(true);
|
|
30
|
+
setProductError(null);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleError = (err: any) => {
|
|
34
|
+
if (err.status !== 401) {
|
|
35
|
+
setStockAlertResponseMessage(
|
|
36
|
+
t('product.stock_alert.error_description').toString()
|
|
37
|
+
);
|
|
38
|
+
setIsModalOpen(true);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const addProductToStockAlertList = async () => {
|
|
43
|
+
try {
|
|
44
|
+
await addStockAlert({
|
|
45
|
+
productPk,
|
|
46
|
+
email: userEmail
|
|
47
|
+
})
|
|
48
|
+
.unwrap()
|
|
49
|
+
.then(handleSuccess)
|
|
50
|
+
.catch(handleError);
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
setProductError(error?.data?.non_field_errors || null);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const closeModal = () => {
|
|
57
|
+
setIsModalOpen(false);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const clearError = () => {
|
|
61
|
+
setProductError(null);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
addProductToStockAlertList,
|
|
66
|
+
isModalOpen,
|
|
67
|
+
setIsModalOpen,
|
|
68
|
+
stockAlertResponseMessage,
|
|
69
|
+
productError,
|
|
70
|
+
isAddToStockAlertLoading,
|
|
71
|
+
closeModal,
|
|
72
|
+
clearError
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// we are holding all of the plugins because of the development enviroment.
|
|
2
1
|
module.exports = [
|
|
3
2
|
'pz-basket-gift-pack',
|
|
4
3
|
'pz-click-collect',
|
|
@@ -12,7 +11,18 @@ module.exports = [
|
|
|
12
11
|
'pz-masterpass',
|
|
13
12
|
'pz-b2b',
|
|
14
13
|
'pz-akifast',
|
|
14
|
+
'pz-multi-basket',
|
|
15
15
|
'pz-saved-card',
|
|
16
16
|
'pz-tabby-extension',
|
|
17
|
-
'pz-
|
|
17
|
+
'pz-apple-pay',
|
|
18
|
+
'pz-tamara-extension',
|
|
19
|
+
'pz-hepsipay',
|
|
20
|
+
'pz-flow-payment',
|
|
21
|
+
'pz-virtual-try-on',
|
|
22
|
+
'pz-cybersource-uc',
|
|
23
|
+
'pz-hepsipay',
|
|
24
|
+
'pz-masterpass-rest',
|
|
25
|
+
'pz-similar-products',
|
|
26
|
+
'pz-haso',
|
|
27
|
+
'pz-google-pay'
|
|
18
28
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Facet } from '@akinon/next/types';
|
|
2
|
-
import { Middleware } from '@reduxjs/toolkit';
|
|
2
|
+
import { Middleware, UnknownAction } from '@reduxjs/toolkit';
|
|
3
3
|
import { setSelectedFacets } from '@theme/redux/reducers/category';
|
|
4
4
|
|
|
5
5
|
const getSelectedFacets = (facets: Array<Facet>) => {
|
|
@@ -21,13 +21,14 @@ const getSelectedFacets = (facets: Array<Facet>) => {
|
|
|
21
21
|
const categoryMiddleware: Middleware = ({ dispatch, getState }) => {
|
|
22
22
|
return (next) => (action) => {
|
|
23
23
|
const result = next(action);
|
|
24
|
+
const act = action as UnknownAction;
|
|
24
25
|
|
|
25
|
-
if (
|
|
26
|
-
const facets: Array<Facet> = result.payload;
|
|
26
|
+
if (act.type === 'category/setFacets') {
|
|
27
|
+
const facets: Array<Facet> = (result as { payload: Array<Facet> }).payload;
|
|
27
28
|
const selectedFacets = getSelectedFacets(facets);
|
|
28
29
|
|
|
29
30
|
dispatch(setSelectedFacets(selectedFacets));
|
|
30
|
-
} else if (
|
|
31
|
+
} else if (act.type === 'category/toggleFacet') {
|
|
31
32
|
const selectedFacets = getSelectedFacets(
|
|
32
33
|
getState().category.selectedFacets
|
|
33
34
|
);
|
|
@@ -14,6 +14,11 @@ import {
|
|
|
14
14
|
import categoryReducer from '@theme/redux/reducers/category';
|
|
15
15
|
import categoryMiddleware from '@theme/redux/middlewares/category';
|
|
16
16
|
|
|
17
|
+
// Import all action creators from akinon-next
|
|
18
|
+
import * as defaultActions from '@akinon/next/redux/actions';
|
|
19
|
+
import { rtkApiActionCreators } from '@akinon/next/redux/actions';
|
|
20
|
+
import * as categorySlice from './reducers/category';
|
|
21
|
+
|
|
17
22
|
const _middlewares: Middleware[] = [...middlewares, categoryMiddleware];
|
|
18
23
|
|
|
19
24
|
const _reducers = {
|
|
@@ -21,13 +26,28 @@ const _reducers = {
|
|
|
21
26
|
category: categoryReducer
|
|
22
27
|
};
|
|
23
28
|
|
|
29
|
+
const actionCreators = {
|
|
30
|
+
// Slice actions (automatically extracted from akinon-next)
|
|
31
|
+
...defaultActions,
|
|
32
|
+
// RTK Query API endpoints from akinon-next
|
|
33
|
+
...rtkApiActionCreators,
|
|
34
|
+
// Local category slice actions
|
|
35
|
+
...categorySlice
|
|
36
|
+
};
|
|
37
|
+
|
|
24
38
|
export const makeStore = (): Store<{
|
|
25
39
|
[key in keyof typeof _reducers]: ReturnType<typeof _reducers[key]>;
|
|
26
40
|
}> =>
|
|
27
41
|
configureStore({
|
|
28
42
|
reducer: _reducers,
|
|
29
43
|
middleware: (getDefaultMiddleware) =>
|
|
30
|
-
getDefaultMiddleware().concat(_middlewares)
|
|
44
|
+
getDefaultMiddleware().concat(_middlewares),
|
|
45
|
+
devTools:
|
|
46
|
+
process.env.NODE_ENV !== 'production'
|
|
47
|
+
? {
|
|
48
|
+
actionCreators
|
|
49
|
+
}
|
|
50
|
+
: false
|
|
31
51
|
});
|
|
32
52
|
|
|
33
53
|
export type AppStore = ReturnType<typeof makeStore>;
|
|
@@ -1,4 +1,58 @@
|
|
|
1
1
|
export * from '@theme/types/widgets';
|
|
2
|
+
import { SignInOptions } from 'next-auth/react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Form types for authentication
|
|
6
|
+
*/
|
|
7
|
+
export enum FormType {
|
|
8
|
+
login = 'login',
|
|
9
|
+
register = 'register',
|
|
10
|
+
loyaltyRegister = 'loyaltyRegister',
|
|
11
|
+
otpLogin = 'otpLogin'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extended SignInOptions with additional form type support for Project Zero Next authentication
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // For standard login
|
|
20
|
+
* signIn('default', {
|
|
21
|
+
* ...data,
|
|
22
|
+
* formType: FormType.login
|
|
23
|
+
* } as PzSignInOptions);
|
|
24
|
+
*
|
|
25
|
+
* // For standard user registration
|
|
26
|
+
* signIn('default', {
|
|
27
|
+
* ...data,
|
|
28
|
+
* formType: FormType.register
|
|
29
|
+
* } as PzSignInOptions);
|
|
30
|
+
*
|
|
31
|
+
* // For loyalty program registration
|
|
32
|
+
* signIn('default', {
|
|
33
|
+
* ...data,
|
|
34
|
+
* formType: FormType.loyaltyRegister
|
|
35
|
+
* } as PzSignInOptions);
|
|
36
|
+
*
|
|
37
|
+
* // For OTP login
|
|
38
|
+
* signIn('default', {
|
|
39
|
+
* ...data,
|
|
40
|
+
* formType: FormType.otpLogin
|
|
41
|
+
* } as PzSignInOptions);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export interface PzSignInOptions extends SignInOptions<boolean> {
|
|
45
|
+
/**
|
|
46
|
+
* Type of the authentication form being submitted
|
|
47
|
+
*
|
|
48
|
+
* Available options:
|
|
49
|
+
* - `FormType.login` - Standard email/password login
|
|
50
|
+
* - `FormType.register` - Standard user registration
|
|
51
|
+
* - `FormType.loyaltyRegister` - Loyalty program registration (use this for loyalty signups)
|
|
52
|
+
* - `FormType.otpLogin` - OTP-based login via SMS
|
|
53
|
+
*/
|
|
54
|
+
formType?: FormType;
|
|
55
|
+
}
|
|
2
56
|
|
|
3
57
|
export type RegisterFormType = {
|
|
4
58
|
email: string;
|
|
@@ -10,14 +64,14 @@ export type RegisterFormType = {
|
|
|
10
64
|
kvkk_confirm: boolean;
|
|
11
65
|
email_allowed: boolean;
|
|
12
66
|
sms_allowed: boolean;
|
|
13
|
-
formType:
|
|
67
|
+
formType: FormType;
|
|
14
68
|
locale: string;
|
|
15
69
|
};
|
|
16
70
|
|
|
17
71
|
export type LoginFormType = {
|
|
18
72
|
email: string;
|
|
19
73
|
password: string;
|
|
20
|
-
formType:
|
|
74
|
+
formType: FormType;
|
|
21
75
|
locale: string;
|
|
22
76
|
};
|
|
23
77
|
|
|
@@ -25,7 +79,7 @@ export type OtpLoginFormType = {
|
|
|
25
79
|
phone: string;
|
|
26
80
|
code?: string;
|
|
27
81
|
locale: string;
|
|
28
|
-
formType:
|
|
82
|
+
formType: FormType;
|
|
29
83
|
};
|
|
30
84
|
|
|
31
85
|
export enum WIDGET_TYPE {
|
|
@@ -71,3 +125,20 @@ export interface SeoProps {
|
|
|
71
125
|
ogPriceAmount?: string;
|
|
72
126
|
ogPriceCurrency?: string;
|
|
73
127
|
}
|
|
128
|
+
|
|
129
|
+
export interface ModalProps {
|
|
130
|
+
portalId: string;
|
|
131
|
+
children?: React.ReactNode;
|
|
132
|
+
open?: boolean;
|
|
133
|
+
setOpen?: (open: boolean) => void;
|
|
134
|
+
title?: React.ReactNode;
|
|
135
|
+
showCloseButton?: React.ReactNode;
|
|
136
|
+
className?: string;
|
|
137
|
+
overlayClassName?: string;
|
|
138
|
+
headerWrapperClassName?: string;
|
|
139
|
+
titleClassName?: string;
|
|
140
|
+
closeButtonClassName?: string;
|
|
141
|
+
iconName?: string;
|
|
142
|
+
iconSize?: number;
|
|
143
|
+
iconClassName?: string;
|
|
144
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Trans } from '@akinon/next/components/trans';
|
|
3
|
+
import { VariantType } from '@akinon/next/types';
|
|
4
|
+
|
|
5
|
+
export const isVariantSelectionComplete = (variants: VariantType[]): boolean => {
|
|
6
|
+
return variants?.every((variant) =>
|
|
7
|
+
variant?.options.some((opt) => opt.is_selected)
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const getUnselectedVariant = (variants: VariantType[]): VariantType | undefined => {
|
|
12
|
+
return variants.find((variant) =>
|
|
13
|
+
variant.options.every((opt) => !opt.is_selected)
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const createVariantErrorMessage = (unselectedVariant: VariantType) => {
|
|
18
|
+
const TransComponent = Trans as any;
|
|
19
|
+
return React.createElement(
|
|
20
|
+
TransComponent,
|
|
21
|
+
{
|
|
22
|
+
i18nKey: "product.please_select_variant",
|
|
23
|
+
components: {
|
|
24
|
+
VariantName: React.createElement('span', {}, unselectedVariant.attribute_name)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const validateVariantSelection = (variants: VariantType[]) => {
|
|
31
|
+
const unselectedVariant = getUnselectedVariant(variants);
|
|
32
|
+
|
|
33
|
+
if (unselectedVariant) {
|
|
34
|
+
return {
|
|
35
|
+
isValid: false,
|
|
36
|
+
errorMessage: createVariantErrorMessage(unselectedVariant)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { isValid: true, errorMessage: null };
|
|
41
|
+
};
|
|
@@ -259,23 +259,25 @@ export const AddressForm = (props: Props) => {
|
|
|
259
259
|
{/* TODO: Fix select and textarea components */}
|
|
260
260
|
|
|
261
261
|
<Select
|
|
262
|
-
className="w-full border-gray-500 text-sm
|
|
262
|
+
className="w-full border-gray-500 text-sm"
|
|
263
263
|
options={countryOptions}
|
|
264
264
|
{...register('country')}
|
|
265
265
|
error={errors.country}
|
|
266
266
|
data-testid="address-form-country"
|
|
267
267
|
label={t('account.address_book.form.country.title')}
|
|
268
|
+
labelClassName="mb-3"
|
|
268
269
|
required
|
|
269
270
|
/>
|
|
270
271
|
|
|
271
272
|
{city && (
|
|
272
273
|
<Select
|
|
273
|
-
className="w-full border-gray-500 text-sm
|
|
274
|
+
className="w-full border-gray-500 text-sm"
|
|
274
275
|
options={cityOptions}
|
|
275
276
|
{...register('city')}
|
|
276
277
|
error={errors.city}
|
|
277
278
|
data-testid="address-form-city"
|
|
278
279
|
label={t('account.address_book.form.province.title')}
|
|
280
|
+
labelClassName="mb-3"
|
|
279
281
|
required
|
|
280
282
|
/>
|
|
281
283
|
)}
|
|
@@ -283,24 +285,26 @@ export const AddressForm = (props: Props) => {
|
|
|
283
285
|
<div className="flex gap-4">
|
|
284
286
|
<div className="flex-1">
|
|
285
287
|
<Select
|
|
286
|
-
className="w-full border-gray-500 text-sm
|
|
288
|
+
className="w-full border-gray-500 text-sm"
|
|
287
289
|
options={townshipOptions}
|
|
288
290
|
{...register('township')}
|
|
289
291
|
error={errors.township}
|
|
290
292
|
data-testid="address-form-township"
|
|
291
293
|
label={t('account.address_book.form.township.title')}
|
|
294
|
+
labelClassName="mb-3"
|
|
292
295
|
required
|
|
293
296
|
/>
|
|
294
297
|
</div>
|
|
295
298
|
{district && (
|
|
296
299
|
<div className="flex-1">
|
|
297
300
|
<Select
|
|
298
|
-
className="w-full border-gray-500 text-sm
|
|
301
|
+
className="w-full border-gray-500 text-sm"
|
|
299
302
|
options={districtOptions}
|
|
300
303
|
{...register('district')}
|
|
301
304
|
error={errors.district}
|
|
302
305
|
data-testid="address-form-district"
|
|
303
306
|
label={t('account.address_book.form.district.title')}
|
|
307
|
+
labelClassName="mb-3"
|
|
304
308
|
required
|
|
305
309
|
/>
|
|
306
310
|
</div>
|
|
@@ -111,7 +111,7 @@ const ContactForm = () => {
|
|
|
111
111
|
resolver: yupResolver(contactFormSchema(t))
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
const onSubmit: SubmitHandler<ContactFormType> = (data
|
|
114
|
+
const onSubmit: SubmitHandler<ContactFormType> = (data) => {
|
|
115
115
|
const formData = new FormData();
|
|
116
116
|
|
|
117
117
|
Object.keys(data ?? {}).forEach((key) => {
|
|
@@ -242,7 +242,7 @@ const ContactForm = () => {
|
|
|
242
242
|
<span className="text-secondary">*</span>
|
|
243
243
|
</label>
|
|
244
244
|
<textarea
|
|
245
|
-
className="border-gray-500 border w-full text-xs p-2.5 focus-visible:outline-
|
|
245
|
+
className="border-gray-500 border w-full text-xs p-2.5 focus-visible:outline-none focus:border-black hover:border-black"
|
|
246
246
|
rows={7}
|
|
247
247
|
name="message"
|
|
248
248
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -31,14 +31,15 @@ export const ContentHeader = (props: Props) => {
|
|
|
31
31
|
</h3>
|
|
32
32
|
<Select
|
|
33
33
|
onChange={handleChange}
|
|
34
|
-
className="w-full mb-4
|
|
34
|
+
className="w-full mb-4 md:mb-0 md:w-56 text-xs"
|
|
35
35
|
options={orders}
|
|
36
36
|
data-testid="account-orders-header-select"
|
|
37
37
|
></Select>
|
|
38
38
|
<Button
|
|
39
39
|
className={clsx(
|
|
40
|
-
'w-full md:w-56',
|
|
41
|
-
isButtonDisabled &&
|
|
40
|
+
'w-full md:w-56 md:ms-4',
|
|
41
|
+
isButtonDisabled &&
|
|
42
|
+
'hover:bg-black hover:text-white disabled:opacity-75'
|
|
42
43
|
)}
|
|
43
44
|
onClick={handleClick}
|
|
44
45
|
data-testid="account-orders-header-button"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Accordion, LoaderSpinner, TabPanel, Tabs } from '@theme/components';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { useGetWidgetQuery } from '@akinon/next/data/client/misc';
|
|
6
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
searchKey?: string;
|
|
@@ -11,6 +12,7 @@ interface Props {
|
|
|
11
12
|
export function FaqTabs(props: Props) {
|
|
12
13
|
const { searchKey } = props;
|
|
13
14
|
const { data, isLoading } = useGetWidgetQuery('faq');
|
|
15
|
+
const { locale } = useLocalization();
|
|
14
16
|
|
|
15
17
|
if (isLoading) {
|
|
16
18
|
return <LoaderSpinner className="mt-4" />;
|
|
@@ -29,8 +31,12 @@ export function FaqTabs(props: Props) {
|
|
|
29
31
|
{data?.attributes?.faq_contents
|
|
30
32
|
?.filter(
|
|
31
33
|
(faq) =>
|
|
32
|
-
faq.value.content
|
|
33
|
-
|
|
34
|
+
faq.value.content
|
|
35
|
+
.toLocaleLowerCase(locale)
|
|
36
|
+
.includes(searchKey) ||
|
|
37
|
+
faq.value.title
|
|
38
|
+
.toLocaleLowerCase(locale)
|
|
39
|
+
.includes(searchKey)
|
|
34
40
|
)
|
|
35
41
|
.map((faq, index) => {
|
|
36
42
|
if (faq.value.category == item.value.category_id) {
|
|
@@ -60,7 +60,7 @@ export const Order = (props) => {
|
|
|
60
60
|
<ul className="flex flex-wrap gap-3.5 mb-6 items-center md:mb-0">
|
|
61
61
|
{props?.orderitem_set?.slice(0, 3).map((item, index) => (
|
|
62
62
|
// TODO: Static image will change (TR)
|
|
63
|
-
<li className="shrink-0" key={index}>
|
|
63
|
+
<li className="flex-shrink-0" key={index}>
|
|
64
64
|
<Image
|
|
65
65
|
src={
|
|
66
66
|
item?.product?.image ? item.product.image : '/noimage.jpg'
|
|
@@ -45,7 +45,7 @@ export const OrderCancellationItem = ({
|
|
|
45
45
|
onClick={handleClick}
|
|
46
46
|
/>
|
|
47
47
|
)}
|
|
48
|
-
<div className="shrink-0">
|
|
48
|
+
<div className="flex-shrink-0">
|
|
49
49
|
<Link href={item.product.absolute_url}>
|
|
50
50
|
<Image
|
|
51
51
|
src={item.product.image ? item.product.image : '/noimage.jpg'}
|
|
@@ -136,7 +136,7 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
|
|
|
136
136
|
key={index}
|
|
137
137
|
>
|
|
138
138
|
<div className="flex gap-3 mb-5 lg:mb-0">
|
|
139
|
-
<div className="shrink-0">
|
|
139
|
+
<div className="flex-shrink-0">
|
|
140
140
|
<Link
|
|
141
141
|
className="block"
|
|
142
142
|
href={item?.product?.absolute_url}
|
|
@@ -39,7 +39,12 @@ export const BasketItem = (props: Props) => {
|
|
|
39
39
|
quantity: number,
|
|
40
40
|
attributes: object = {}
|
|
41
41
|
) => {
|
|
42
|
-
const requestParams:
|
|
42
|
+
const requestParams: {
|
|
43
|
+
product: number;
|
|
44
|
+
quantity: number;
|
|
45
|
+
attributes: object;
|
|
46
|
+
namespace?: string;
|
|
47
|
+
} = {
|
|
43
48
|
product: productPk,
|
|
44
49
|
quantity,
|
|
45
50
|
attributes
|
|
@@ -223,6 +223,22 @@ export const Summary = (props: Props) => {
|
|
|
223
223
|
component={Component.OneClickCheckoutButtons}
|
|
224
224
|
props={checkoutProviderProps}
|
|
225
225
|
/>
|
|
226
|
+
|
|
227
|
+
<div className="mt-4">
|
|
228
|
+
<PluginModule
|
|
229
|
+
component={Component.BasketVirtualTryOn}
|
|
230
|
+
props={{
|
|
231
|
+
basketItems: basket.basketitem_set.map((item) => ({
|
|
232
|
+
pk: item.product.pk,
|
|
233
|
+
sku: item.product.sku,
|
|
234
|
+
name: item.product.name,
|
|
235
|
+
productimage_set: item.product.productimage_set,
|
|
236
|
+
attributes: item.product.attributes,
|
|
237
|
+
category: (item.product as any).category
|
|
238
|
+
}))
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
226
242
|
</div>
|
|
227
243
|
</div>
|
|
228
244
|
);
|
|
@@ -12,7 +12,7 @@ export interface BreadcrumbProps {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export default function Breadcrumb(props: BreadcrumbProps) {
|
|
15
|
-
const { t } = useLocalization();
|
|
15
|
+
const { t, locale } = useLocalization();
|
|
16
16
|
const { breadcrumbList = [] } = props;
|
|
17
17
|
|
|
18
18
|
const list = [
|
|
@@ -28,7 +28,7 @@ export default function Breadcrumb(props: BreadcrumbProps) {
|
|
|
28
28
|
{list.map((item, index) => (
|
|
29
29
|
<Fragment key={index}>
|
|
30
30
|
<Link href={item.url}>
|
|
31
|
-
{capitalize(item.text.toLocaleLowerCase())}
|
|
31
|
+
{capitalize(item.text.toLocaleLowerCase(locale))}
|
|
32
32
|
</Link>
|
|
33
33
|
{index !== list.length - 1 && <Icon name="chevron-end" size={8} />}
|
|
34
34
|
</Fragment>
|
|
@@ -57,6 +57,7 @@ export default function ListPage(props: ListPageProps) {
|
|
|
57
57
|
newUrl.searchParams.delete('page');
|
|
58
58
|
router.push(newUrl.pathname + newUrl.search, undefined);
|
|
59
59
|
}
|
|
60
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
61
|
}, [searchParams, data.products, page]);
|
|
61
62
|
|
|
62
63
|
const { t } = useLocalization();
|
|
@@ -79,7 +80,7 @@ export default function ListPage(props: ListPageProps) {
|
|
|
79
80
|
className={clsx(
|
|
80
81
|
'transition-opacity duration-300 ease-linear lg:hidden',
|
|
81
82
|
isMenuOpen
|
|
82
|
-
? 'fixed bg-black
|
|
83
|
+
? 'fixed bg-black bg-opacity-60 inset-0 z-10 opacity-100'
|
|
83
84
|
: 'opacity-0'
|
|
84
85
|
)}
|
|
85
86
|
></div>
|
|
@@ -6,7 +6,7 @@ import { useLocalization } from '@akinon/next/hooks';
|
|
|
6
6
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
7
7
|
import { resetSelectedFacets } from '@theme/redux/reducers/category';
|
|
8
8
|
import CategoryActiveFilters from '@theme/views/category/category-active-filters';
|
|
9
|
-
import { useMemo,
|
|
9
|
+
import { useMemo, useTransition } from 'react';
|
|
10
10
|
import { FilterItem } from './filter-item';
|
|
11
11
|
|
|
12
12
|
interface Props {
|
|
@@ -24,7 +24,7 @@ const CheckoutAuth = () => {
|
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<div className="flex flex-col w-full my-5 lg:flex-row">
|
|
27
|
-
<div className="flex-1 shrink-0">
|
|
27
|
+
<div className="flex-1 flex-shrink-0">
|
|
28
28
|
<Login />
|
|
29
29
|
<div className="text-center text-sm text-gray-600 uppercase mt-5">
|
|
30
30
|
<span>
|
|
@@ -6,7 +6,7 @@ import { Image } from '@akinon/next/components/image';
|
|
|
6
6
|
|
|
7
7
|
const CheckoutHeader = () => {
|
|
8
8
|
return (
|
|
9
|
-
<div className="relative border-b border-gray-100 shadow
|
|
9
|
+
<div className="relative border-b border-gray-100 shadow">
|
|
10
10
|
<header
|
|
11
11
|
className={clsx(['py-8', 'px-4', 'mx-auto', 'container', 'md:px-0'])}
|
|
12
12
|
>
|