@akinon/projectzero 2.0.0-beta.1 → 2.0.0-beta.11
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 +251 -0
- package/app-template/README.md +6 -0
- package/app-template/config/prebuild-tests.json +5 -0
- package/app-template/docs/plugins.md +60 -25
- package/app-template/jest.config.ts +2 -2
- 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 +99 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/page.tsx +112 -47
- 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/checkbox.tsx +1 -0
- 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/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 +11 -9
- package/app-template/src/views/account/orders/order-cancellation-item.tsx +5 -4
- package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +45 -38
- 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/options/redirection.tsx +5 -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 +11 -4
- 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/price-wrapper.tsx +7 -2
- package/app-template/src/views/product/product-info.tsx +35 -5
- 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 +2 -134
- 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/codemods/update-tailwind-config/index.js +30 -0
- package/codemods/update-tailwind-config/transform.js +102 -0
- package/commands/codemod.ts +17 -0
- package/commands/index.ts +3 -1
- package/commands/plugins.ts +24 -30
- package/dist/codemods/sentry-9/templates/error.js +14 -0
- package/dist/commands/codemod.js +15 -0
- package/dist/commands/index.js +3 -1
- package/dist/commands/plugins.js +23 -20
- package/package.json +3 -2
- 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
|
@@ -254,6 +254,10 @@
|
|
|
254
254
|
"cancelled": "İptal edildi",
|
|
255
255
|
"cancellation_request_recieved": "İptal talebi alındı",
|
|
256
256
|
"close_button": "Kapat",
|
|
257
|
+
"return_status": "İade Durumu",
|
|
258
|
+
"rejected": "İade reddedildi",
|
|
259
|
+
"completed": "İade tamamlandı",
|
|
260
|
+
"explanation": "Açıklama",
|
|
257
261
|
"success": {
|
|
258
262
|
"title": "Başarılı",
|
|
259
263
|
"description": "Talebiniz alınmış olup en kısa sürede değerlendirilecektir."
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
"description": "Lütfen daha sonra tekrar deneyiniz.",
|
|
39
39
|
"link_text": "Ana sayfaya dön"
|
|
40
40
|
},
|
|
41
|
+
"client_error": {
|
|
42
|
+
"title": "Sayfada bir sorunla karşılaştık.",
|
|
43
|
+
"description": "Bu bir tarayıcı hatası gibi görünüyor. Lütfen sayfayı yenileyin veya tarayıcı önbelleğinizi temizleyin.",
|
|
44
|
+
"link_text": "Ana sayfaya dön"
|
|
45
|
+
},
|
|
46
|
+
"try_again": "Tekrar Dene",
|
|
41
47
|
"breadcrumb": {
|
|
42
48
|
"homepage": "Anasayfa"
|
|
43
49
|
},
|
|
@@ -61,5 +67,9 @@
|
|
|
61
67
|
"description": "Döviz kuru değiştiği için sepetinizdeki ürünler silinecektir.",
|
|
62
68
|
"close": "Kapat",
|
|
63
69
|
"continue": "Devam Et"
|
|
70
|
+
},
|
|
71
|
+
"file_input": {
|
|
72
|
+
"select_file": "Dosya Seç",
|
|
73
|
+
"no_file": "Dosya seçilmedi"
|
|
64
74
|
}
|
|
65
75
|
}
|
|
@@ -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
|
+
});
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
Select,
|
|
16
16
|
Modal,
|
|
17
17
|
LoaderSpinner,
|
|
18
|
-
Link
|
|
18
|
+
Link,
|
|
19
|
+
FileInput
|
|
19
20
|
} from '@theme/components';
|
|
20
21
|
import { useState, use } from 'react';
|
|
21
22
|
import { OrderDetailHeader } from '@theme/views/account/orders/order-detail-header';
|
|
@@ -41,6 +42,7 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
41
42
|
} = useForm<AccountOrderCancellation>({
|
|
42
43
|
resolver: yupResolver(accountOrderCancellationSchema)
|
|
43
44
|
});
|
|
45
|
+
|
|
44
46
|
const { data: cancellationReasons, isSuccess: cancellationReasonsSuccess } =
|
|
45
47
|
useGetCancellationReasonsQuery();
|
|
46
48
|
|
|
@@ -58,6 +60,9 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
58
60
|
const watchAllFields = watch();
|
|
59
61
|
const cancelItemsLength = watchAllFields?.cancel_order_items?.length;
|
|
60
62
|
const [cancelOrder] = useCancelOrderMutation();
|
|
63
|
+
const [files, setFiles] = useState<
|
|
64
|
+
{ itemId: string; image: string; description: string }[]
|
|
65
|
+
>([]);
|
|
61
66
|
|
|
62
67
|
const modalHandleClick = () => {
|
|
63
68
|
setIsModalOpen(false);
|
|
@@ -114,8 +119,68 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
114
119
|
]);
|
|
115
120
|
};
|
|
116
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
|
+
|
|
117
171
|
const onSubmit: SubmitHandler<AccountOrderCancellation> = (orderItems) => {
|
|
118
|
-
|
|
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 })
|
|
119
184
|
.unwrap()
|
|
120
185
|
.then(() => {
|
|
121
186
|
setResponseMessage({
|
|
@@ -124,10 +189,33 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
124
189
|
});
|
|
125
190
|
setIsModalOpen(true);
|
|
126
191
|
})
|
|
127
|
-
.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
|
+
|
|
128
216
|
setResponseMessage({
|
|
129
217
|
title: t('account.my_orders.return.error.title').toString(),
|
|
130
|
-
content:
|
|
218
|
+
content: errorContent
|
|
131
219
|
});
|
|
132
220
|
setIsModalOpen(true);
|
|
133
221
|
});
|
|
@@ -149,13 +237,13 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
149
237
|
<div>
|
|
150
238
|
<div className="pb-2 mb-3 text-lg border-gray border-b">
|
|
151
239
|
<span data-testid="account-orders-return-order-count">
|
|
152
|
-
{order
|
|
240
|
+
{order?.orderitem_set?.length}
|
|
153
241
|
</span>{' '}
|
|
154
242
|
<span>{t('account.my_orders.detail.products')}</span>
|
|
155
243
|
</div>
|
|
156
244
|
|
|
157
245
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
158
|
-
{order
|
|
246
|
+
{order?.orderitem_set?.map((item, index: number) => (
|
|
159
247
|
<Controller
|
|
160
248
|
defaultValue={[]}
|
|
161
249
|
control={control}
|
|
@@ -166,7 +254,7 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
166
254
|
return cancelItem.order_item === item.id;
|
|
167
255
|
});
|
|
168
256
|
|
|
169
|
-
const cancellationType = item
|
|
257
|
+
const cancellationType = item?.is_refundable
|
|
170
258
|
? 'refund'
|
|
171
259
|
: 'cancel';
|
|
172
260
|
|
|
@@ -182,6 +270,10 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
182
270
|
onChange={onChange}
|
|
183
271
|
value={value}
|
|
184
272
|
selectOption={selectOption}
|
|
273
|
+
fileInput={fileInputCondition(
|
|
274
|
+
item,
|
|
275
|
+
item?.product?.name
|
|
276
|
+
)}
|
|
185
277
|
/>
|
|
186
278
|
);
|
|
187
279
|
}}
|
|
@@ -33,14 +33,15 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
33
33
|
const groupedOrder = [];
|
|
34
34
|
|
|
35
35
|
if (order) {
|
|
36
|
-
const groupedData =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
groups[tracking_number]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
const groupedData =
|
|
37
|
+
order?.orderitem_set?.reduce((groups, item) => {
|
|
38
|
+
const { tracking_number } = item;
|
|
39
|
+
if (!groups[tracking_number]) {
|
|
40
|
+
groups[tracking_number] = [];
|
|
41
|
+
}
|
|
42
|
+
groups[tracking_number].push(item);
|
|
43
|
+
return groups;
|
|
44
|
+
}, {}) || {};
|
|
44
45
|
|
|
45
46
|
const result = Object.values(groupedData);
|
|
46
47
|
|
|
@@ -77,8 +78,8 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
77
78
|
|
|
78
79
|
<div>
|
|
79
80
|
<span className="text-base font-bold">
|
|
80
|
-
{order?.orderitem_set
|
|
81
|
-
{t('account.my_orders.detail.products')} {groupedOrder
|
|
81
|
+
{order?.orderitem_set?.length}{' '}
|
|
82
|
+
{t('account.my_orders.detail.products')} {groupedOrder?.length}{' '}
|
|
82
83
|
{t('account.my_orders.detail.packages')}
|
|
83
84
|
</span>
|
|
84
85
|
</div>
|
|
@@ -88,16 +89,17 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
88
89
|
className="break-words"
|
|
89
90
|
>
|
|
90
91
|
<span>{t('account.my_orders.detail.delivery_address')}</span>:{' '}
|
|
91
|
-
{order
|
|
92
|
-
{order
|
|
93
|
-
{order
|
|
92
|
+
{order?.shipping_address?.line}{' '}
|
|
93
|
+
{order?.shipping_address?.district?.name}{' '}
|
|
94
|
+
{order?.shipping_address?.township?.name}{' '}
|
|
95
|
+
{order?.shipping_address?.city?.name}
|
|
94
96
|
</div>
|
|
95
97
|
</OrderDetailHeader>
|
|
96
98
|
|
|
97
99
|
<div>
|
|
98
100
|
{groupedOrder.map((group, i) => {
|
|
99
101
|
const orderStatus = getOrderStatus(
|
|
100
|
-
group[0]
|
|
102
|
+
group[0]?.status?.value?.toString(),
|
|
101
103
|
t
|
|
102
104
|
);
|
|
103
105
|
|
|
@@ -121,8 +123,8 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
121
123
|
<div className="flex justify-between items-center lg:gap-x-12">
|
|
122
124
|
<div className="text-base">{orderStatus.label}</div>
|
|
123
125
|
|
|
124
|
-
{group[0]
|
|
125
|
-
<Link href={group[0]
|
|
126
|
+
{group[0]?.tracking_number && group[0]?.tracking_url && (
|
|
127
|
+
<Link href={group[0]?.tracking_url}>
|
|
126
128
|
<Button className="px-7" appearance="filled">
|
|
127
129
|
{t('account.my_orders.detail.track_shipment')}
|
|
128
130
|
</Button>
|
|
@@ -134,7 +136,7 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
134
136
|
<div className="px-4 lg:px-7">
|
|
135
137
|
{group.map((item, index) => {
|
|
136
138
|
const itemStatus = getOrderStatus(
|
|
137
|
-
item
|
|
139
|
+
item?.status?.value?.toString(),
|
|
138
140
|
t
|
|
139
141
|
);
|
|
140
142
|
|
|
@@ -144,18 +146,18 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
144
146
|
key={index}
|
|
145
147
|
>
|
|
146
148
|
<div className="flex gap-3 mb-5 lg:mb-0">
|
|
147
|
-
<div className="
|
|
149
|
+
<div className="shrink-0">
|
|
148
150
|
<Link
|
|
149
151
|
className="block"
|
|
150
|
-
href={item
|
|
152
|
+
href={item?.product?.absolute_url}
|
|
151
153
|
>
|
|
152
154
|
<Image
|
|
153
155
|
src={
|
|
154
|
-
item
|
|
156
|
+
item?.product?.image
|
|
155
157
|
? item.product.image
|
|
156
158
|
: '/noimage.jpg'
|
|
157
159
|
}
|
|
158
|
-
alt={item
|
|
160
|
+
alt={item?.product?.name}
|
|
159
161
|
width={112}
|
|
160
162
|
height={150}
|
|
161
163
|
/>
|
|
@@ -164,33 +166,33 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
164
166
|
|
|
165
167
|
<div className="flex flex-col justify-between lg:max-w-48">
|
|
166
168
|
<div className="text-sm">
|
|
167
|
-
<Link href={item
|
|
168
|
-
{item
|
|
169
|
+
<Link href={item?.product?.absolute_url}>
|
|
170
|
+
{item?.product?.name}
|
|
169
171
|
</Link>
|
|
170
172
|
</div>
|
|
171
173
|
|
|
172
174
|
<div className="text-gray-900 text-xs">
|
|
173
|
-
{item
|
|
175
|
+
{item?.product?.attributes?.filterable_color && (
|
|
174
176
|
<div>
|
|
175
177
|
<span>
|
|
176
178
|
{t('account.my_orders.detail.color')}
|
|
177
179
|
</span>
|
|
178
|
-
: {item
|
|
180
|
+
: {item?.product?.attributes?.filterable_color}
|
|
179
181
|
</div>
|
|
180
182
|
)}
|
|
181
183
|
|
|
182
|
-
{item
|
|
184
|
+
{item?.product?.attributes?.size && (
|
|
183
185
|
<div>
|
|
184
186
|
<span>
|
|
185
187
|
{t('account.my_orders.detail.size')}
|
|
186
188
|
</span>
|
|
187
|
-
:{item
|
|
189
|
+
:{item?.product?.attributes?.size}
|
|
188
190
|
</div>
|
|
189
191
|
)}
|
|
190
192
|
|
|
191
193
|
<div>
|
|
192
194
|
<span>{t('account.my_orders.detail.code')}</span>:{' '}
|
|
193
|
-
{item
|
|
195
|
+
{item?.product?.base_code}
|
|
194
196
|
</div>
|
|
195
197
|
</div>
|
|
196
198
|
</div>
|
|
@@ -207,12 +209,11 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
207
209
|
{itemStatus.label}
|
|
208
210
|
</div>
|
|
209
211
|
|
|
210
|
-
{(item
|
|
211
|
-
order
|
|
212
|
-
item.status.value == '400' && (
|
|
212
|
+
{(item?.is_cancellable || item?.is_refundable) &&
|
|
213
|
+
order?.is_cancellable && (
|
|
213
214
|
<div className="lg:ml-24">
|
|
214
215
|
<Link
|
|
215
|
-
href={`${ROUTES.ACCOUNT_ORDERS}/${order
|
|
216
|
+
href={`${ROUTES.ACCOUNT_ORDERS}/${order?.id}/cancellation`}
|
|
216
217
|
>
|
|
217
218
|
<Button
|
|
218
219
|
className="px-4 uppercase font-bold h-7"
|
|
@@ -227,23 +228,22 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
227
228
|
</div>
|
|
228
229
|
)}
|
|
229
230
|
</div>
|
|
230
|
-
|
|
231
231
|
<div className="flex flex-col justify-center items-end lg:ml-6 lg:min-w-[7rem]">
|
|
232
|
-
{parseFloat(item
|
|
233
|
-
parseFloat(item
|
|
232
|
+
{parseFloat(item?.retail_price || '0') >
|
|
233
|
+
parseFloat(item?.price || '0') && (
|
|
234
234
|
<Price
|
|
235
235
|
className="font-normal line-through"
|
|
236
|
-
value={item
|
|
236
|
+
value={item?.retail_price}
|
|
237
237
|
/>
|
|
238
238
|
)}
|
|
239
239
|
|
|
240
240
|
<Price
|
|
241
241
|
className={clsx('font-normal', {
|
|
242
242
|
'text-secondary-600':
|
|
243
|
-
parseFloat(item
|
|
244
|
-
parseFloat(item
|
|
243
|
+
parseFloat(item?.retail_price || '0') >
|
|
244
|
+
parseFloat(item?.price || '0')
|
|
245
245
|
})}
|
|
246
|
-
value={item
|
|
246
|
+
value={item?.price}
|
|
247
247
|
/>
|
|
248
248
|
</div>
|
|
249
249
|
</div>
|
|
@@ -251,6 +251,70 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
251
251
|
);
|
|
252
252
|
})}
|
|
253
253
|
</div>
|
|
254
|
+
|
|
255
|
+
{group.map((item) =>
|
|
256
|
+
item.cancellationrequest_set?.map((cancellationItem) => {
|
|
257
|
+
const status = cancellationItem.status?.value;
|
|
258
|
+
const isRejected = status === 'rejected';
|
|
259
|
+
const isCompleted = status === 'completed';
|
|
260
|
+
|
|
261
|
+
const filteredImages =
|
|
262
|
+
cancellationItem.cancellation_request_image_set?.filter(
|
|
263
|
+
(img) =>
|
|
264
|
+
isRejected
|
|
265
|
+
? !img.is_uploaded_by_user
|
|
266
|
+
: isCompleted
|
|
267
|
+
? img.is_uploaded_by_user
|
|
268
|
+
: false
|
|
269
|
+
) || [];
|
|
270
|
+
|
|
271
|
+
const statusText = isRejected
|
|
272
|
+
? t('account.my_orders.return.rejected')
|
|
273
|
+
: isCompleted
|
|
274
|
+
? t('account.my_orders.return.completed')
|
|
275
|
+
: null;
|
|
276
|
+
|
|
277
|
+
if (!statusText || filteredImages.length === 0) return null;
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<div
|
|
281
|
+
className="w-full px-4 lg:px-7"
|
|
282
|
+
key={cancellationItem.id}
|
|
283
|
+
>
|
|
284
|
+
<div className="flex flex-col py-2 gap-4 border-t border-gray">
|
|
285
|
+
<div className="flex flex-col">
|
|
286
|
+
<div className="text-sm font-semibold">
|
|
287
|
+
{t('account.my_orders.return.return_status')}
|
|
288
|
+
<span className="font-normal"> {statusText}</span>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<div className="flex gap-2 mt-2 flex-wrap">
|
|
292
|
+
{filteredImages.map((img) => (
|
|
293
|
+
<div className="flex flex-col gap-2" key={img.id}>
|
|
294
|
+
<Link href={img.image} target="_blank">
|
|
295
|
+
<Image
|
|
296
|
+
src={img.image}
|
|
297
|
+
width={112}
|
|
298
|
+
height={150}
|
|
299
|
+
alt={img.description}
|
|
300
|
+
/>
|
|
301
|
+
</Link>
|
|
302
|
+
|
|
303
|
+
{img.description && (
|
|
304
|
+
<p className="text-xs">
|
|
305
|
+
{t('account.my_orders.return.explanation')}:
|
|
306
|
+
{img.description}
|
|
307
|
+
</p>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
))}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
})
|
|
317
|
+
)}
|
|
254
318
|
</div>
|
|
255
319
|
);
|
|
256
320
|
})}
|
|
@@ -263,29 +327,29 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
263
327
|
<div className="flex justify-between text-sm text-black-700 mb-2">
|
|
264
328
|
<p>
|
|
265
329
|
<span>{t('account.my_orders.detail.subtotal')}</span> (
|
|
266
|
-
{order
|
|
330
|
+
{order?.orderitem_set?.length}{' '}
|
|
267
331
|
<span>{t('account.my_orders.detail.items')}</span>)
|
|
268
332
|
</p>
|
|
269
333
|
|
|
270
334
|
<Price
|
|
271
335
|
className="font-normal min-w-max"
|
|
272
336
|
value={
|
|
273
|
-
parseFloat(order
|
|
274
|
-
parseFloat(order
|
|
337
|
+
parseFloat(order?.amount_without_discount || '0') -
|
|
338
|
+
parseFloat(order?.shipping_amount || '0')
|
|
275
339
|
}
|
|
276
340
|
/>
|
|
277
341
|
</div>
|
|
278
342
|
|
|
279
|
-
{order
|
|
280
|
-
order
|
|
343
|
+
{order?.discountitem_set &&
|
|
344
|
+
order?.discountitem_set?.map((item, index) => (
|
|
281
345
|
<div
|
|
282
346
|
className="flex justify-between text-sm text-black-700 mb-2"
|
|
283
347
|
key={index}
|
|
284
348
|
>
|
|
285
|
-
<p>{item
|
|
349
|
+
<p>{item?.name}</p>
|
|
286
350
|
<Price
|
|
287
351
|
className="font-normal min-w-max"
|
|
288
|
-
value={item
|
|
352
|
+
value={item?.amount}
|
|
289
353
|
useNegative
|
|
290
354
|
/>
|
|
291
355
|
</div>
|
|
@@ -298,7 +362,8 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
298
362
|
className="font-normal min-w-max"
|
|
299
363
|
data-testid="account-orders-detail-total"
|
|
300
364
|
value={
|
|
301
|
-
parseFloat(order
|
|
365
|
+
parseFloat(order?.amount || '0') -
|
|
366
|
+
parseFloat(order?.shipping_amount || '0')
|
|
302
367
|
}
|
|
303
368
|
/>
|
|
304
369
|
</div>
|
|
@@ -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" />
|
|
@@ -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 && (
|