@akinon/projectzero 2.0.0-beta.0 → 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 +41 -0
- package/app-template/.env.example +5 -0
- package/app-template/.gitignore +2 -0
- package/app-template/CHANGELOG.md +250 -0
- package/app-template/README.md +6 -0
- package/app-template/config/prebuild-tests.json +5 -0
- package/app-template/next-env.d.ts +1 -1
- package/app-template/{next.config.mjs → next.config.ts} +6 -3
- package/app-template/package.json +43 -40
- 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]/[...prettyurl]/page.tsx +6 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/cancellation/page.tsx +98 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/page.tsx +71 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/page.tsx +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/profile/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]/category/[pk]/page.tsx +4 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +12 -15
- package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +3 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/forms/[pk]/generate/page.tsx +5 -3
- package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +7 -5
- package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +3 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/layout.tsx +3 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/list/page.tsx +3 -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]/orders/completed/[token]/page.tsx +3 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +8 -5
- package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +3 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/users/email-set-primary/[[...id]]/page.tsx +4 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/users/registration/account-confirm-email/[[...id]]/page.tsx +3 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/users/reset/[[...id]]/page.tsx +11 -3
- package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +48 -2
- 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/pwa-tags.tsx +0 -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/address-form.tsx +2 -2
- package/app-template/src/views/account/contact-form.tsx +4 -9
- 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/guest-login/index.tsx +1 -1
- 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 +16 -5
- 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 +13 -15
- 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 +30 -5
- 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/commerce-url.js +17 -7
- package/dist/commands/create.js +17 -7
- package/dist/commands/index.js +3 -1
- package/dist/commands/plugins.js +21 -7
- 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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
describe('Middleware matcher regex tests', () => {
|
|
5
|
+
const middlewareFilePath = path.resolve(__dirname, '../middleware.ts');
|
|
6
|
+
const middlewareContent = fs.readFileSync(middlewareFilePath, 'utf8');
|
|
7
|
+
|
|
8
|
+
let actualMatcherStrings: string[] = [];
|
|
9
|
+
let matcherPatterns: RegExp[] = [];
|
|
10
|
+
|
|
11
|
+
const matcherBlockRegex = middlewareContent.match(/matcher:\s*\[([\s\S]*?)\](?=\s*[,}])/);
|
|
12
|
+
|
|
13
|
+
if (matcherBlockRegex && matcherBlockRegex[1]) {
|
|
14
|
+
const matcherContentInsideBrackets = matcherBlockRegex[1];
|
|
15
|
+
|
|
16
|
+
actualMatcherStrings = matcherContentInsideBrackets
|
|
17
|
+
.split(',')
|
|
18
|
+
.map(line => {
|
|
19
|
+
const uncommentedLine = line.replace(/\/\/.*$/, '').trim();
|
|
20
|
+
const quoteMatch = uncommentedLine.match(/^(['"])(.*)\1$/);
|
|
21
|
+
return quoteMatch ? quoteMatch[2] : null;
|
|
22
|
+
})
|
|
23
|
+
.filter((pattern): pattern is string => pattern !== null && pattern !== '');
|
|
24
|
+
|
|
25
|
+
matcherPatterns = matcherContentInsideBrackets
|
|
26
|
+
.split(',')
|
|
27
|
+
.map((pattern) => pattern.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.map((pattern) => {
|
|
30
|
+
let cleanPattern = pattern
|
|
31
|
+
.replace(/^['"]|['"]$/g, '');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
if (cleanPattern.includes('(?!') && cleanPattern.includes('api') && cleanPattern.includes('_next')) {
|
|
35
|
+
cleanPattern = '^(?!/(?:api|_next)/)(?!.*\\.\\w+$).*$';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return new RegExp(cleanPattern);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Invalid simplified regex: ${cleanPattern}`, error);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.filter(Boolean) as RegExp[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const testPath = (path: string): boolean => {
|
|
48
|
+
return matcherPatterns.some((pattern) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = pattern.test(path);
|
|
51
|
+
return result;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Error testing path: ${path} | Pattern: ${pattern.toString()}`,
|
|
55
|
+
error
|
|
56
|
+
);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
it('should NOT match api routes', () => {
|
|
63
|
+
const apiPaths = ['/api/products', '/api/auth/login', '/api/v1/users'];
|
|
64
|
+
apiPaths.forEach((path) => {
|
|
65
|
+
expect(testPath(path)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should NOT match _next routes', () => {
|
|
70
|
+
const nextPaths = [
|
|
71
|
+
'/_next/static/chunks/main.js',
|
|
72
|
+
'/_next/image',
|
|
73
|
+
'/_next/data/build-id/products.json'
|
|
74
|
+
];
|
|
75
|
+
nextPaths.forEach((path) => {
|
|
76
|
+
expect(testPath(path)).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should NOT match static files with extensions', () => {
|
|
81
|
+
const staticFiles = [
|
|
82
|
+
'/images/logo.png',
|
|
83
|
+
'/styles/main.css',
|
|
84
|
+
'/fonts/roboto.woff2',
|
|
85
|
+
'/favicon.ico',
|
|
86
|
+
'/manifest.json'
|
|
87
|
+
];
|
|
88
|
+
staticFiles.forEach((path) => {
|
|
89
|
+
expect(testPath(path)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should match dynamic routes and specific patterns', () => {
|
|
94
|
+
const validPaths = [
|
|
95
|
+
'/profile/settings',
|
|
96
|
+
'/dashboard/stats',
|
|
97
|
+
'/products/123'
|
|
98
|
+
];
|
|
99
|
+
validPaths.forEach((path) => {
|
|
100
|
+
expect(testPath(path)).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should match checkout-with-token routes', () => {
|
|
105
|
+
const expectedRegexString = '\'/(.*orders\\\\/checkout-with-token.*)\'';
|
|
106
|
+
expect(middlewareContent.includes(expectedRegexString)).toBe(true);
|
|
107
|
+
|
|
108
|
+
const checkoutPaths = [
|
|
109
|
+
'/orders/checkout-with-token/123',
|
|
110
|
+
'/orders/checkout-with-token/abc-xyz',
|
|
111
|
+
'/orders/checkout-with-token'
|
|
112
|
+
];
|
|
113
|
+
checkoutPaths.forEach((path) => {
|
|
114
|
+
expect(testPath(path)).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should contain the exact specific extensions regex string in the file content', () => {
|
|
119
|
+
const expectedRegexString = '\'/(.+\\\\.)(html|htm|aspx|asp|php)\'';
|
|
120
|
+
expect(middlewareContent.includes(expectedRegexString)).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should include the sitemap pattern specifically within the matcher array', () => {
|
|
124
|
+
const sitemapPattern = '/(.*sitemap\\\\.xml)';
|
|
125
|
+
expect(actualMatcherStrings).toContain(sitemapPattern);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should verify that api pattern is excluded in the matcher configuration', () => {
|
|
129
|
+
expect(/api/.test(middlewareContent)).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should verify that _next pattern is excluded in the matcher configuration', () => {
|
|
133
|
+
expect(/_next/.test(middlewareContent)).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -56,7 +56,8 @@ const resolvePrettyUrlHandler =
|
|
|
56
56
|
return prettyUrlResult;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export async function generateMetadata(
|
|
59
|
+
export async function generateMetadata(props: PageProps) {
|
|
60
|
+
const params = await props.params;
|
|
60
61
|
let result: Metadata = {};
|
|
61
62
|
const { prettyurl } = params;
|
|
62
63
|
const pageSlug = prettyurl
|
|
@@ -81,7 +82,7 @@ export async function generateMetadata({ params }: PageProps) {
|
|
|
81
82
|
...params,
|
|
82
83
|
pk: prettyUrlResult.pk
|
|
83
84
|
},
|
|
84
|
-
searchParams
|
|
85
|
+
searchParams: Promise.resolve(searchParams)
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
try {
|
|
@@ -123,7 +124,8 @@ export async function generateMetadata({ params }: PageProps) {
|
|
|
123
124
|
export const dynamic = 'force-static';
|
|
124
125
|
export const revalidate = 300;
|
|
125
126
|
|
|
126
|
-
export default async function Page(
|
|
127
|
+
export default async function Page(props) {
|
|
128
|
+
const params = await props.params;
|
|
127
129
|
const { prettyurl } = params;
|
|
128
130
|
const pageSlug = prettyurl
|
|
129
131
|
.filter((x) => !x.startsWith('searchparams'))
|
|
@@ -159,7 +161,7 @@ export default async function Page({ params }) {
|
|
|
159
161
|
...params,
|
|
160
162
|
pk: result.pk
|
|
161
163
|
},
|
|
162
|
-
searchParams: urlSearchParams
|
|
164
|
+
searchParams: Promise.resolve(urlSearchParams)
|
|
163
165
|
};
|
|
164
166
|
|
|
165
167
|
if (result.path.startsWith('/category/')) {
|
|
@@ -8,16 +8,17 @@ import {
|
|
|
8
8
|
useGetOrderQuery,
|
|
9
9
|
useGetCancellationReasonsQuery
|
|
10
10
|
} from '@akinon/next/data/client/account';
|
|
11
|
-
import { AccountOrderCancellation } from '@akinon/next/types';
|
|
11
|
+
import type { AccountOrderCancellation } from '@akinon/next/types';
|
|
12
12
|
import {
|
|
13
13
|
Button,
|
|
14
14
|
Checkbox,
|
|
15
15
|
Select,
|
|
16
16
|
Modal,
|
|
17
17
|
LoaderSpinner,
|
|
18
|
-
Link
|
|
18
|
+
Link,
|
|
19
|
+
FileInput
|
|
19
20
|
} from '@theme/components';
|
|
20
|
-
import { useState } from 'react';
|
|
21
|
+
import { useState, use } from 'react';
|
|
21
22
|
import { OrderDetailHeader } from '@theme/views/account/orders/order-detail-header';
|
|
22
23
|
import { OrderCancellationItem } from '@theme/views/account/orders/order-cancellation-item';
|
|
23
24
|
import { useLocalization } from '@akinon/next/hooks';
|
|
@@ -27,6 +28,8 @@ const accountOrderCancellationSchema = yup.object().shape({
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
const AccountOrderCancellation = ({ params }) => {
|
|
31
|
+
const pageParams = use(params) as { id: string };
|
|
32
|
+
|
|
30
33
|
const { t } = useLocalization();
|
|
31
34
|
const {
|
|
32
35
|
register,
|
|
@@ -39,6 +42,7 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
39
42
|
} = useForm<AccountOrderCancellation>({
|
|
40
43
|
resolver: yupResolver(accountOrderCancellationSchema)
|
|
41
44
|
});
|
|
45
|
+
|
|
42
46
|
const { data: cancellationReasons, isSuccess: cancellationReasonsSuccess } =
|
|
43
47
|
useGetCancellationReasonsQuery();
|
|
44
48
|
|
|
@@ -46,7 +50,7 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
46
50
|
data: order,
|
|
47
51
|
isLoading,
|
|
48
52
|
isSuccess: orderSuccess
|
|
49
|
-
} = useGetOrderQuery(
|
|
53
|
+
} = useGetOrderQuery(pageParams.id);
|
|
50
54
|
|
|
51
55
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
52
56
|
const [responseMessage, setResponseMessage] = useState({
|
|
@@ -56,6 +60,9 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
56
60
|
const watchAllFields = watch();
|
|
57
61
|
const cancelItemsLength = watchAllFields?.cancel_order_items?.length;
|
|
58
62
|
const [cancelOrder] = useCancelOrderMutation();
|
|
63
|
+
const [files, setFiles] = useState<
|
|
64
|
+
{ itemId: string; image: string; description: string }[]
|
|
65
|
+
>([]);
|
|
59
66
|
|
|
60
67
|
const modalHandleClick = () => {
|
|
61
68
|
setIsModalOpen(false);
|
|
@@ -112,8 +119,68 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
112
119
|
]);
|
|
113
120
|
};
|
|
114
121
|
|
|
122
|
+
const handleFileChange = async (
|
|
123
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
124
|
+
itemId: string,
|
|
125
|
+
description: string
|
|
126
|
+
) => {
|
|
127
|
+
const selectedFiles = Array.from(e.target.files || []);
|
|
128
|
+
|
|
129
|
+
const base64Files = await Promise.all(
|
|
130
|
+
selectedFiles.map((file) => {
|
|
131
|
+
return new Promise<string>((resolve, reject) => {
|
|
132
|
+
const reader = new FileReader();
|
|
133
|
+
reader.onload = () => resolve(reader.result as string);
|
|
134
|
+
reader.onerror = reject;
|
|
135
|
+
reader.readAsDataURL(file);
|
|
136
|
+
});
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const validFiles = base64Files.filter((file) =>
|
|
141
|
+
/^data:image\/(jpeg|png|jpg);base64,.+/.test(file)
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const formattedFiles = validFiles.map((file) => ({
|
|
145
|
+
itemId,
|
|
146
|
+
image: file,
|
|
147
|
+
description
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
setFiles((prevFiles) => [
|
|
151
|
+
...prevFiles.filter((f) => f.itemId !== itemId),
|
|
152
|
+
...formattedFiles
|
|
153
|
+
]);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const fileInputCondition = (item, description: string) => {
|
|
157
|
+
if (item.is_refundable && !item.active_cancellation_request) {
|
|
158
|
+
return (
|
|
159
|
+
<FileInput
|
|
160
|
+
name="files"
|
|
161
|
+
title="files"
|
|
162
|
+
multiple
|
|
163
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
164
|
+
handleFileChange(e, item.id, description)
|
|
165
|
+
}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
115
171
|
const onSubmit: SubmitHandler<AccountOrderCancellation> = (orderItems) => {
|
|
116
|
-
|
|
172
|
+
const mergedData = {
|
|
173
|
+
...orderItems,
|
|
174
|
+
cancel_order_items: orderItems.cancel_order_items.map((orderItem) => ({
|
|
175
|
+
...orderItem,
|
|
176
|
+
...(files.length > 0 && {
|
|
177
|
+
cancellation_request_image_set: files.filter(
|
|
178
|
+
(file) => file.itemId === orderItem.order_item
|
|
179
|
+
)
|
|
180
|
+
})
|
|
181
|
+
}))
|
|
182
|
+
};
|
|
183
|
+
cancelOrder({ id: order.number, ...mergedData })
|
|
117
184
|
.unwrap()
|
|
118
185
|
.then(() => {
|
|
119
186
|
setResponseMessage({
|
|
@@ -122,10 +189,33 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
122
189
|
});
|
|
123
190
|
setIsModalOpen(true);
|
|
124
191
|
})
|
|
125
|
-
.catch(() => {
|
|
192
|
+
.catch((err) => {
|
|
193
|
+
console.error('Err', err);
|
|
194
|
+
|
|
195
|
+
const errorMessages = new Set();
|
|
196
|
+
|
|
197
|
+
if (err?.data?.cancel_order_items) {
|
|
198
|
+
err.data.cancel_order_items.forEach((item) => {
|
|
199
|
+
if (item.cancellation_request_image_set) {
|
|
200
|
+
item.cancellation_request_image_set.forEach((error) => {
|
|
201
|
+
if (typeof error === 'string') {
|
|
202
|
+
errorMessages.add(error);
|
|
203
|
+
} else if (typeof error === 'object' && error?.image) {
|
|
204
|
+
error.image.forEach((msg) => errorMessages.add(msg));
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const errorContent =
|
|
212
|
+
errorMessages.size > 0
|
|
213
|
+
? Array.from(errorMessages).join('\n')
|
|
214
|
+
: t('account.my_orders.return.error.description').toString();
|
|
215
|
+
|
|
126
216
|
setResponseMessage({
|
|
127
217
|
title: t('account.my_orders.return.error.title').toString(),
|
|
128
|
-
content:
|
|
218
|
+
content: errorContent
|
|
129
219
|
});
|
|
130
220
|
setIsModalOpen(true);
|
|
131
221
|
});
|
|
@@ -180,6 +270,7 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
180
270
|
onChange={onChange}
|
|
181
271
|
value={value}
|
|
182
272
|
selectOption={selectOption}
|
|
273
|
+
fileInput={fileInputCondition(item, item.product.name)}
|
|
183
274
|
/>
|
|
184
275
|
);
|
|
185
276
|
}}
|
|
@@ -5,7 +5,7 @@ import { Image } from '@akinon/next/components/image';
|
|
|
5
5
|
import clsx from 'clsx';
|
|
6
6
|
import { SalesContractModal } from '@theme/views/sales-contract-modal';
|
|
7
7
|
import { useGetOrderQuery } from '@akinon/next/data/client/account';
|
|
8
|
-
import { useEffect, useState } from 'react';
|
|
8
|
+
import { useEffect, useState, use } from 'react';
|
|
9
9
|
|
|
10
10
|
import { OrderDetailHeader } from '@theme/views/account/orders/order-detail-header';
|
|
11
11
|
import { ROUTES } from '@theme/routes';
|
|
@@ -13,7 +13,9 @@ import { useLocalization } from '@akinon/next/hooks';
|
|
|
13
13
|
import settings from 'settings';
|
|
14
14
|
import { getOrderStatus } from 'utils';
|
|
15
15
|
|
|
16
|
-
const AccountOrderDetail = ({ params
|
|
16
|
+
const AccountOrderDetail = ({ params }) => {
|
|
17
|
+
const pageParams = use(params) as { id: string };
|
|
18
|
+
|
|
17
19
|
const { locale, t } = useLocalization();
|
|
18
20
|
|
|
19
21
|
const localeValue = settings.localization.locales.find(
|
|
@@ -25,7 +27,7 @@ const AccountOrderDetail = ({ params: { id } }) => {
|
|
|
25
27
|
isLoading,
|
|
26
28
|
isSuccess,
|
|
27
29
|
isFetching
|
|
28
|
-
} = useGetOrderQuery(id);
|
|
30
|
+
} = useGetOrderQuery(pageParams.id);
|
|
29
31
|
const [orderDate, setOrderDate] = useState('');
|
|
30
32
|
|
|
31
33
|
const groupedOrder = [];
|
|
@@ -142,7 +144,7 @@ const AccountOrderDetail = ({ params: { id } }) => {
|
|
|
142
144
|
key={index}
|
|
143
145
|
>
|
|
144
146
|
<div className="flex gap-3 mb-5 lg:mb-0">
|
|
145
|
-
<div className="
|
|
147
|
+
<div className="shrink-0">
|
|
146
148
|
<Link
|
|
147
149
|
className="block"
|
|
148
150
|
href={item.product.absolute_url}
|
|
@@ -206,8 +208,7 @@ const AccountOrderDetail = ({ params: { id } }) => {
|
|
|
206
208
|
</div>
|
|
207
209
|
|
|
208
210
|
{(item.is_cancellable || item.is_refundable) &&
|
|
209
|
-
order.is_cancellable &&
|
|
210
|
-
item.status.value == '400' && (
|
|
211
|
+
order.is_cancellable && (
|
|
211
212
|
<div className="lg:ml-24">
|
|
212
213
|
<Link
|
|
213
214
|
href={`${ROUTES.ACCOUNT_ORDERS}/${order.id}/cancellation`}
|
|
@@ -225,7 +226,6 @@ const AccountOrderDetail = ({ params: { id } }) => {
|
|
|
225
226
|
</div>
|
|
226
227
|
)}
|
|
227
228
|
</div>
|
|
228
|
-
|
|
229
229
|
<div className="flex flex-col justify-center items-end lg:ml-6 lg:min-w-[7rem]">
|
|
230
230
|
{parseFloat(item.retail_price) >
|
|
231
231
|
parseFloat(item.price) && (
|
|
@@ -249,6 +249,70 @@ const AccountOrderDetail = ({ params: { id } }) => {
|
|
|
249
249
|
);
|
|
250
250
|
})}
|
|
251
251
|
</div>
|
|
252
|
+
|
|
253
|
+
{group.map((item) =>
|
|
254
|
+
item.cancellationrequest_set?.map((cancellationItem) => {
|
|
255
|
+
const status = cancellationItem.status?.value;
|
|
256
|
+
const isRejected = status === 'rejected';
|
|
257
|
+
const isCompleted = status === 'completed';
|
|
258
|
+
|
|
259
|
+
const filteredImages =
|
|
260
|
+
cancellationItem.cancellation_request_image_set?.filter(
|
|
261
|
+
(img) =>
|
|
262
|
+
isRejected
|
|
263
|
+
? !img.is_uploaded_by_user
|
|
264
|
+
: isCompleted
|
|
265
|
+
? img.is_uploaded_by_user
|
|
266
|
+
: false
|
|
267
|
+
) || [];
|
|
268
|
+
|
|
269
|
+
const statusText = isRejected
|
|
270
|
+
? t('account.my_orders.return.rejected')
|
|
271
|
+
: isCompleted
|
|
272
|
+
? t('account.my_orders.return.completed')
|
|
273
|
+
: null;
|
|
274
|
+
|
|
275
|
+
if (!statusText || filteredImages.length === 0) return null;
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div
|
|
279
|
+
className="w-full px-4 lg:px-7"
|
|
280
|
+
key={cancellationItem.id}
|
|
281
|
+
>
|
|
282
|
+
<div className="flex flex-col py-2 gap-4 border-t border-gray">
|
|
283
|
+
<div className="flex flex-col">
|
|
284
|
+
<div className="text-sm font-semibold">
|
|
285
|
+
{t('account.my_orders.return.return_status')}
|
|
286
|
+
<span className="font-normal"> {statusText}</span>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<div className="flex gap-2 mt-2 flex-wrap">
|
|
290
|
+
{filteredImages.map((img) => (
|
|
291
|
+
<div className="flex flex-col gap-2" key={img.id}>
|
|
292
|
+
<Link href={img.image} target="_blank">
|
|
293
|
+
<Image
|
|
294
|
+
src={img.image}
|
|
295
|
+
width={112}
|
|
296
|
+
height={150}
|
|
297
|
+
alt={img.description}
|
|
298
|
+
/>
|
|
299
|
+
</Link>
|
|
300
|
+
|
|
301
|
+
{img.description && (
|
|
302
|
+
<p className="text-xs">
|
|
303
|
+
{t('account.my_orders.return.explanation')}:
|
|
304
|
+
{img.description}
|
|
305
|
+
</p>
|
|
306
|
+
)}
|
|
307
|
+
</div>
|
|
308
|
+
))}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
})
|
|
315
|
+
)}
|
|
252
316
|
</div>
|
|
253
317
|
);
|
|
254
318
|
})}
|
|
@@ -53,7 +53,7 @@ export default function Page() {
|
|
|
53
53
|
<div className="hidden lg:block">
|
|
54
54
|
<div className="bg-gray-150">
|
|
55
55
|
{orderLoading && (
|
|
56
|
-
<SkeletonWrapper className="w-full px-6 mb-12 h-28 items-center justify-center
|
|
56
|
+
<SkeletonWrapper className="w-full px-6 mb-12 h-28 items-center justify-center flex-row! xl:h-[5.5rem]">
|
|
57
57
|
<Skeleton className="w-[11.375rem] h-16 mr-4 xl:w-[16rem] xl:h-10" />
|
|
58
58
|
<Skeleton className="w-56 h-10 mr-4" />
|
|
59
59
|
<Skeleton className="w-[12.75rem] h-10 xl:w-56" />
|
|
@@ -252,7 +252,7 @@ export default function Page() {
|
|
|
252
252
|
<Input
|
|
253
253
|
label={t('account.my_profile.form.phone.placeholder')}
|
|
254
254
|
type="tel"
|
|
255
|
-
format={user_phone_format.replace(
|
|
255
|
+
format={user_phone_format.replace(/9/g, '#')}
|
|
256
256
|
mask="_"
|
|
257
257
|
allowEmptyFormatting={true}
|
|
258
258
|
control={control}
|
|
@@ -122,7 +122,7 @@ export default function Stores() {
|
|
|
122
122
|
|
|
123
123
|
<div className="flex gap-6 mt-4 flex-col md:flex-row">
|
|
124
124
|
{city && (
|
|
125
|
-
<div className="w-full
|
|
125
|
+
<div className="w-full shrink-0 md:w-60 overflow-y-scroll max-h-[36rem]">
|
|
126
126
|
{cityLoading && (
|
|
127
127
|
<SkeletonWrapper>
|
|
128
128
|
<Skeleton className="w-full h-20" />
|
|
@@ -174,7 +174,7 @@ export default function Stores() {
|
|
|
174
174
|
title={store.name}
|
|
175
175
|
titleClassName="text-xs font-bold"
|
|
176
176
|
iconSize={12}
|
|
177
|
-
className="relative py-3 border-b justify-center mb-0"
|
|
177
|
+
className="relative py-3 border-b border-gray-200 justify-center mb-0"
|
|
178
178
|
>
|
|
179
179
|
<div className="text-xs">
|
|
180
180
|
{store.address && (
|
|
@@ -51,7 +51,7 @@ export default function Auth() {
|
|
|
51
51
|
{t('auth.register.mobile_title')}
|
|
52
52
|
</Button>
|
|
53
53
|
</div>
|
|
54
|
-
<div className="w-full flex flex-wrap border md:border-0">
|
|
54
|
+
<div className="w-full flex flex-wrap border border-gray-200 md:border-0">
|
|
55
55
|
<div
|
|
56
56
|
className={twMerge(
|
|
57
57
|
clsx('w-full md:block md:w-1/2', {
|
|
@@ -25,7 +25,7 @@ export default function Page() {
|
|
|
25
25
|
}, [basket, isSuccess]);
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<div className="max-w-
|
|
28
|
+
<div className="max-w-(--breakpoint-xl) p-4 flex flex-col text-primary-800 lg:p-8 xl:flex-row xl:mx-auto">
|
|
29
29
|
{isLoading && (
|
|
30
30
|
<div className="flex justify-center w-full">
|
|
31
31
|
<LoaderSpinner />
|
|
@@ -62,7 +62,7 @@ export default function Page() {
|
|
|
62
62
|
<Summary basket={basket} />
|
|
63
63
|
</>
|
|
64
64
|
) : (
|
|
65
|
-
<div className="flex flex-col items-center container max-w-
|
|
65
|
+
<div className="flex flex-col items-center container max-w-(--breakpoint-sm) py-4 px-4 xs:py-6 xs:px-6 sm:py-8 sm:px-8 lg:max-w-(--breakpoint-xl)">
|
|
66
66
|
<h1
|
|
67
67
|
className="w-full text-xl font-light text-secondary text-center sm:text-2xl"
|
|
68
68
|
data-testid="basket-empty"
|
|
@@ -3,7 +3,10 @@ import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
|
3
3
|
import { PageProps } from '@akinon/next/types';
|
|
4
4
|
import CategoryLayout from '@theme/views/category/layout';
|
|
5
5
|
|
|
6
|
-
async function Page(
|
|
6
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
7
|
+
const params = await props.params;
|
|
8
|
+
const searchParams = await props.searchParams;
|
|
9
|
+
|
|
7
10
|
const { data, breadcrumbData } = await getCategoryData({
|
|
8
11
|
pk: params.pk,
|
|
9
12
|
searchParams
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import { ROUTES } from '@theme/routes';
|
|
3
|
+
import { useSentryUncaughtErrors } from '@akinon/next/hooks';
|
|
4
|
+
import PzErrorPage from '@akinon/next/views/error-page';
|
|
6
5
|
|
|
7
|
-
export default function
|
|
8
|
-
|
|
6
|
+
export default function ErrorPage({
|
|
7
|
+
error,
|
|
8
|
+
reset
|
|
9
|
+
}: {
|
|
10
|
+
error: Error & { digest?: string; isServerError?: boolean };
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}) {
|
|
13
|
+
// DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
|
|
14
|
+
useSentryUncaughtErrors(error);
|
|
9
15
|
|
|
10
|
-
return
|
|
11
|
-
<section className="text-center px-6 my-14 md:px-0 md:m-14">
|
|
12
|
-
<div className="text-7xl font-bold md:text-8xl">500</div>
|
|
13
|
-
<h1 className="text-lg md:text-xl"> {t('common.page_500.title')} </h1>
|
|
14
|
-
<p className="text-lg md:text-xl"> {t('common.page_500.description')} </p>
|
|
15
|
-
<Link href={ROUTES.HOME} className="text-lg underline">
|
|
16
|
-
{t('common.page_500.link_text')}
|
|
17
|
-
</Link>
|
|
18
|
-
</section>
|
|
19
|
-
);
|
|
16
|
+
return <PzErrorPage error={error} reset={reset} />;
|
|
20
17
|
}
|
|
@@ -2,7 +2,9 @@ import { getFlatPageData } from '@akinon/next/data/server';
|
|
|
2
2
|
import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
3
3
|
import { PageProps } from '@akinon/next/types';
|
|
4
4
|
|
|
5
|
-
async function Page(
|
|
5
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
6
|
+
const params = await props.params;
|
|
7
|
+
|
|
6
8
|
const data = await getFlatPageData({ pk: params.pk });
|
|
7
9
|
|
|
8
10
|
return (
|
|
@@ -2,7 +2,9 @@ import { getFormData } from '@akinon/next/data/server';
|
|
|
2
2
|
import { t } from '@akinon/next/utils/server-translation';
|
|
3
3
|
import { GenerateFormFields } from '@theme/components/generate-form-fields';
|
|
4
4
|
|
|
5
|
-
export default async function Page(
|
|
5
|
+
export default async function Page(props) {
|
|
6
|
+
const params = await props.params;
|
|
7
|
+
|
|
6
8
|
const data = await getFormData({ pk: params.pk });
|
|
7
9
|
const { schema, is_active, name, pk } = data;
|
|
8
10
|
|
|
@@ -19,7 +21,7 @@ export default async function Page({ params }) {
|
|
|
19
21
|
schema={schema}
|
|
20
22
|
allFieldClasses={{
|
|
21
23
|
className:
|
|
22
|
-
'border border-[#d4d4d4] text-[#4a4f54] mt-1.5 h-[38px] p-2.5 text-xs outline-
|
|
24
|
+
'border border-[#d4d4d4] text-[#4a4f54] mt-1.5 h-[38px] p-2.5 text-xs outline-hidden focus:border-black',
|
|
23
25
|
labelClassName: 'text-[#4a4f54] text-xs',
|
|
24
26
|
wrapperClassName: 'flex flex-col mb-6'
|
|
25
27
|
}}
|
|
@@ -39,7 +41,7 @@ export default async function Page({ params }) {
|
|
|
39
41
|
formProperties={{
|
|
40
42
|
actionUrl: `/api/form/${pk}/`,
|
|
41
43
|
className:
|
|
42
|
-
'w-[calc(100%-36px)] md:w-[570px] px-[18px] py-[60px] md:p-[100px] border border-[#cbc8c8] border-t-[3px] border-t-[#e95151] mx-auto -mt-[100px] bg-white mb-14'
|
|
44
|
+
'w-[calc(100%-36px)] md:w-[570px] px-[18px] py-[60px] md:p-[100px] border border-[#cbc8c8] border-t-[3px] border-t-[#e95151] mx-auto -mt-[100px] bg-white mb-14'
|
|
43
45
|
}}
|
|
44
46
|
submitButtonText={t('form.form_page.submit_button_text')}
|
|
45
47
|
/>
|
|
@@ -5,11 +5,10 @@ import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
|
5
5
|
import { PageProps, Metadata } from '@akinon/next/types';
|
|
6
6
|
import { generateJsonLd } from '@theme/utils/generate-jsonld';
|
|
7
7
|
|
|
8
|
-
export async function generateMetadata({
|
|
9
|
-
params,
|
|
10
|
-
searchParams
|
|
11
|
-
}: PageProps<{ pk: number }>) {
|
|
8
|
+
export async function generateMetadata(props: PageProps): Promise<Metadata> {
|
|
12
9
|
let result: Metadata = {};
|
|
10
|
+
const searchParams = await props.searchParams;
|
|
11
|
+
const params = await props.params;
|
|
13
12
|
|
|
14
13
|
try {
|
|
15
14
|
const {
|
|
@@ -41,7 +40,10 @@ export async function generateMetadata({
|
|
|
41
40
|
return result;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
async function Page(
|
|
43
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
44
|
+
const params = await props.params;
|
|
45
|
+
const searchParams = await props.searchParams;
|
|
46
|
+
|
|
45
47
|
const [{ data, breadcrumbData }, deliveryReturn] = await Promise.all([
|
|
46
48
|
getProductData({
|
|
47
49
|
pk: params.pk,
|
|
@@ -2,7 +2,9 @@ import { getLandingPageData } from '@akinon/next/data/server';
|
|
|
2
2
|
import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
3
3
|
import { PageProps } from '@akinon/next/types';
|
|
4
4
|
|
|
5
|
-
async function Page(
|
|
5
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
6
|
+
const params = await props.params;
|
|
7
|
+
|
|
6
8
|
const data = await getLandingPageData({ pk: params.pk });
|
|
7
9
|
|
|
8
10
|
const content = data.landing_page;
|