@akinon/projectzero 1.41.0-rc.0 ā 1.41.0
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 +1 -36
- package/app-template/CHANGELOG.md +23 -330
- package/app-template/package.json +16 -15
- package/app-template/public/locales/en/account.json +4 -4
- package/app-template/public/locales/en/auth.json +1 -2
- package/app-template/public/locales/tr/account.json +1 -1
- package/app-template/public/locales/tr/auth.json +1 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/coupons/page.tsx +4 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/profile/page.tsx +0 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx +8 -12
- package/app-template/src/components/checkbox.tsx +2 -2
- package/app-template/src/components/input.tsx +7 -19
- package/app-template/src/components/price.tsx +3 -3
- package/app-template/src/plugins.js +2 -1
- package/app-template/src/views/account/address-form.tsx +7 -22
- package/app-template/src/views/account/contact-form.tsx +6 -22
- package/app-template/src/views/account/favourite-products/favourite-products-list.tsx +1 -5
- package/app-template/src/views/basket/summary.tsx +35 -26
- package/app-template/src/views/category/filters/index.tsx +105 -5
- package/app-template/src/views/login/index.tsx +20 -9
- package/app-template/src/views/product/product-info.tsx +42 -32
- package/app-template/tsconfig.json +4 -14
- package/commands/create.ts +5 -29
- package/dist/commands/create.js +2 -25
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/[...prettyurl]/page.tsx +0 -8
- package/app-template/src/views/category/filters/filter-item.tsx +0 -131
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import NumberFormat, { NumberFormatProps } from 'react-number-format';
|
|
3
3
|
import { getCurrency } from '@akinon/next/utils';
|
|
4
4
|
import { PriceProps } from '@theme/types';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
|
|
7
|
-
export const Price = (props:
|
|
7
|
+
export const Price = (props: NumberFormatProps & PriceProps) => {
|
|
8
8
|
const {
|
|
9
9
|
value,
|
|
10
10
|
currencyCode,
|
|
@@ -38,7 +38,7 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
|
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<
|
|
41
|
+
<NumberFormat
|
|
42
42
|
value={useNegative ? `-${useNegativeSpace}${_value}` : _value}
|
|
43
43
|
{...{
|
|
44
44
|
[useCurrencyAfterPrice ? 'suffix' : 'prefix']: currency
|
|
@@ -31,7 +31,7 @@ interface Props {
|
|
|
31
31
|
onSubmit: (data: any) => void;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const makeAddressFormSchema = (t, { phoneNumberLength
|
|
34
|
+
const makeAddressFormSchema = (t, { phoneNumberLength }) =>
|
|
35
35
|
yup.object().shape({
|
|
36
36
|
title: yup.string().required(t('account.address_book.form.error.required')),
|
|
37
37
|
first_name: yup
|
|
@@ -65,9 +65,8 @@ const makeAddressFormSchema = (t, { phoneNumberLength, postCodeLength }) =>
|
|
|
65
65
|
.max(255, t('account.address_book.form.error.line_max')),
|
|
66
66
|
postcode: yup
|
|
67
67
|
.string()
|
|
68
|
-
.
|
|
69
|
-
.
|
|
70
|
-
.max(postCodeLength, t('account.address_book.form.error.postcode_max'))
|
|
68
|
+
.min(5, t('account.address_book.form.error.postcode_min'))
|
|
69
|
+
.max(5, t('account.address_book.form.error.postcode_max'))
|
|
71
70
|
.required(t('account.address_book.form.error.required')),
|
|
72
71
|
company_name: yup.string().nullable(),
|
|
73
72
|
tax_no: yup.string().nullable(),
|
|
@@ -81,8 +80,7 @@ export const AddressForm = (props: Props) => {
|
|
|
81
80
|
const { data, onSubmit } = props;
|
|
82
81
|
const config = useAppSelector((state) => state.config);
|
|
83
82
|
const addressFormSchema = makeAddressFormSchema(t, {
|
|
84
|
-
phoneNumberLength: config.user_phone_format.length
|
|
85
|
-
postCodeLength: config.user_post_code_format.length
|
|
83
|
+
phoneNumberLength: config.user_phone_format.length
|
|
86
84
|
});
|
|
87
85
|
const {
|
|
88
86
|
register,
|
|
@@ -183,22 +181,12 @@ export const AddressForm = (props: Props) => {
|
|
|
183
181
|
if (data && country) {
|
|
184
182
|
reset({
|
|
185
183
|
...data,
|
|
186
|
-
is_corporate:
|
|
184
|
+
is_corporate:
|
|
185
|
+
String(data.is_corporate) === AddressType.company ? 'true' : 'false' // TODO: Fix this! This hack for radio buttons can't be set to boolean value
|
|
187
186
|
});
|
|
188
187
|
}
|
|
189
188
|
}, [data, country, reset]);
|
|
190
189
|
|
|
191
|
-
useEffect(() => {
|
|
192
|
-
if (selectedFormType !== AddressType.company) {
|
|
193
|
-
reset({
|
|
194
|
-
...watch(),
|
|
195
|
-
company_name: '',
|
|
196
|
-
tax_office: '',
|
|
197
|
-
tax_no: ''
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}, [selectedFormType, reset, watch]);
|
|
201
|
-
|
|
202
190
|
return (
|
|
203
191
|
<form
|
|
204
192
|
onSubmit={handleSubmit(onSubmit)}
|
|
@@ -334,15 +322,12 @@ export const AddressForm = (props: Props) => {
|
|
|
334
322
|
)}
|
|
335
323
|
</label>
|
|
336
324
|
<Input
|
|
325
|
+
type="number"
|
|
337
326
|
label={t('account.address_book.form.post_code.placeholder')}
|
|
338
327
|
{...register('postcode')}
|
|
339
328
|
error={errors.postcode}
|
|
340
329
|
data-testid="address-form-post-code"
|
|
341
330
|
required
|
|
342
|
-
format={config.user_post_code_format.replaceAll(/\9/g, '#')}
|
|
343
|
-
control={control}
|
|
344
|
-
mask="_"
|
|
345
|
-
allowEmptyFormatting
|
|
346
331
|
/>
|
|
347
332
|
{selectedFormType === AddressType.company && (
|
|
348
333
|
<>
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
Button,
|
|
4
4
|
FileInput,
|
|
5
5
|
Input,
|
|
6
|
-
Link,
|
|
7
6
|
LoaderSpinner,
|
|
8
|
-
Select
|
|
7
|
+
Select,
|
|
8
|
+
Link
|
|
9
9
|
} from '@theme/components';
|
|
10
10
|
import { useSession } from 'next-auth/react';
|
|
11
11
|
import { useEffect, useState } from 'react';
|
|
@@ -45,8 +45,7 @@ const contactFormSchema = (t) =>
|
|
|
45
45
|
.when('subject', {
|
|
46
46
|
is: (value) => value === '2',
|
|
47
47
|
then: yup.string().required(t('account.contact.form.error.required'))
|
|
48
|
-
})
|
|
49
|
-
file: yup.mixed()
|
|
48
|
+
})
|
|
50
49
|
});
|
|
51
50
|
|
|
52
51
|
const ContactForm = () => {
|
|
@@ -111,18 +110,8 @@ const ContactForm = () => {
|
|
|
111
110
|
resolver: yupResolver(contactFormSchema(t))
|
|
112
111
|
});
|
|
113
112
|
|
|
114
|
-
const onSubmit: SubmitHandler<ContactFormType> = (data
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Object.keys(data ?? {}).forEach((key) => {
|
|
118
|
-
if (key === "file" && data[key]){
|
|
119
|
-
formData.append(key, data[key][0]);
|
|
120
|
-
} else if (data[key]) {
|
|
121
|
-
formData.append(key, data[key]);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
sendContact(formData);
|
|
113
|
+
const onSubmit: SubmitHandler<ContactFormType> = (data) => {
|
|
114
|
+
sendContact(data);
|
|
126
115
|
};
|
|
127
116
|
|
|
128
117
|
const handleChange = (e) => {
|
|
@@ -253,12 +242,7 @@ const ContactForm = () => {
|
|
|
253
242
|
<label className="text-xs text-gray-800 mb-2 block">
|
|
254
243
|
{t('account.contact.form.file.title')}
|
|
255
244
|
</label>
|
|
256
|
-
<FileInput
|
|
257
|
-
name="file"
|
|
258
|
-
title="file"
|
|
259
|
-
className="w-full mb-5"
|
|
260
|
-
{...register('file')}
|
|
261
|
-
/>
|
|
245
|
+
<FileInput className="w-full mb-5" title="test" />
|
|
262
246
|
<Button type="submit" className="w-full font-medium">
|
|
263
247
|
{t('account.contact.form.submit_button')}
|
|
264
248
|
</Button>
|
|
@@ -30,11 +30,7 @@ const FavoriteProductsList = () => {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (isLoading || isFetching) {
|
|
33
|
-
return
|
|
34
|
-
<div className="flex items-center justify-center h-80">
|
|
35
|
-
<LoaderSpinner />
|
|
36
|
-
</div>
|
|
37
|
-
);
|
|
33
|
+
return <LoaderSpinner />; // TODO: Fix loader spinner position
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import { useAppDispatch } from '@akinon/next/redux/hooks';
|
|
3
3
|
import { useForm } from 'react-hook-form';
|
|
4
4
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
useRemoveVoucherCodeMutation
|
|
10
10
|
} from '@akinon/next/data/client/basket';
|
|
11
11
|
import { Basket, Error } from '@akinon/next/types';
|
|
12
|
-
import {
|
|
12
|
+
import { Button, Input, Price } from '@theme/components';
|
|
13
13
|
import { pushBeginCheckout } from '@theme/utils/gtm';
|
|
14
14
|
import { ROUTES } from '@theme/routes';
|
|
15
|
-
import {
|
|
15
|
+
import { useLocalization, useRouter } from '@akinon/next/hooks';
|
|
16
16
|
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
17
17
|
import clsx from 'clsx';
|
|
18
18
|
|
|
@@ -35,7 +35,9 @@ export const Summary = (props: Props) => {
|
|
|
35
35
|
setError,
|
|
36
36
|
reset,
|
|
37
37
|
formState: { errors }
|
|
38
|
-
} = useForm<{
|
|
38
|
+
} = useForm<{
|
|
39
|
+
voucherCode: string;
|
|
40
|
+
}>({
|
|
39
41
|
resolver: yupResolver(voucherCodeFormSchema(t))
|
|
40
42
|
});
|
|
41
43
|
const dispatch = useAppDispatch();
|
|
@@ -88,6 +90,29 @@ export const Summary = (props: Props) => {
|
|
|
88
90
|
});
|
|
89
91
|
};
|
|
90
92
|
|
|
93
|
+
const checkoutProviderProps = {
|
|
94
|
+
className: clsx([
|
|
95
|
+
'py-2.5',
|
|
96
|
+
'bg-black',
|
|
97
|
+
'relative',
|
|
98
|
+
'hover:bg-black',
|
|
99
|
+
'before:content-[""]',
|
|
100
|
+
'before:w-6',
|
|
101
|
+
'before:h-6',
|
|
102
|
+
'before:bg-white',
|
|
103
|
+
'before:absolute',
|
|
104
|
+
'before:rounded-r-[18px]',
|
|
105
|
+
'before:left-0',
|
|
106
|
+
'after:content-[""]',
|
|
107
|
+
'after:absolute',
|
|
108
|
+
'after:w-3',
|
|
109
|
+
'after:h-3',
|
|
110
|
+
'after:bg-[#d02c2f]',
|
|
111
|
+
'after:rounded-xl',
|
|
112
|
+
'after:left-1'
|
|
113
|
+
])
|
|
114
|
+
};
|
|
115
|
+
|
|
91
116
|
useEffect(() => {
|
|
92
117
|
const products = basket.basketitem_set.map((basketItem) => ({
|
|
93
118
|
...basketItem.product
|
|
@@ -189,30 +214,14 @@ export const Summary = (props: Props) => {
|
|
|
189
214
|
{t('basket.summary.proceed_to_checkout')}
|
|
190
215
|
</Button>
|
|
191
216
|
|
|
217
|
+
<PluginModule
|
|
218
|
+
component={Component.AkifastCheckoutButton}
|
|
219
|
+
props={checkoutProviderProps}
|
|
220
|
+
/>
|
|
221
|
+
|
|
192
222
|
<PluginModule
|
|
193
223
|
component={Component.OneClickCheckoutButtons}
|
|
194
|
-
props={
|
|
195
|
-
className: clsx([
|
|
196
|
-
'py-2.5',
|
|
197
|
-
'bg-black',
|
|
198
|
-
'relative',
|
|
199
|
-
'hover:bg-black',
|
|
200
|
-
'before:content-[""]',
|
|
201
|
-
'before:w-6',
|
|
202
|
-
'before:h-6',
|
|
203
|
-
'before:bg-white',
|
|
204
|
-
'before:absolute',
|
|
205
|
-
'before:rounded-r-[18px]',
|
|
206
|
-
'before:left-0',
|
|
207
|
-
'after:content-[""]',
|
|
208
|
-
'after:absolute',
|
|
209
|
-
'after:w-3',
|
|
210
|
-
'after:h-3',
|
|
211
|
-
'after:bg-[#d02c2f]',
|
|
212
|
-
'after:rounded-xl',
|
|
213
|
-
'after:left-1'
|
|
214
|
-
])
|
|
215
|
-
}}
|
|
224
|
+
props={checkoutProviderProps}
|
|
216
225
|
/>
|
|
217
226
|
</div>
|
|
218
227
|
</div>
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { WIDGET_TYPE } from '@theme/types';
|
|
3
4
|
import clsx from 'clsx';
|
|
4
5
|
|
|
5
|
-
import { Button, Icon } from '@theme/components';
|
|
6
|
-
import {
|
|
6
|
+
import { Accordion, Button, Checkbox, Icon, Radio } from '@theme/components';
|
|
7
|
+
import { SizeFilter } from './size-filter';
|
|
8
|
+
|
|
9
|
+
import { useLocalization, useRouter } from '@akinon/next/hooks';
|
|
10
|
+
import { Facet, FacetChoice } from '@akinon/next/types';
|
|
7
11
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
8
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
resetSelectedFacets,
|
|
14
|
+
toggleFacet
|
|
15
|
+
} from '@theme/redux/reducers/category';
|
|
9
16
|
import CategoryActiveFilters from '@theme/views/category/category-active-filters';
|
|
10
17
|
import { useMemo } from 'react';
|
|
11
|
-
import {
|
|
18
|
+
import { commonProductAttributes } from '@theme/settings';
|
|
19
|
+
|
|
20
|
+
const COMPONENT_TYPES = {
|
|
21
|
+
[WIDGET_TYPE.category]: Radio,
|
|
22
|
+
[WIDGET_TYPE.multiselect]: Checkbox
|
|
23
|
+
};
|
|
12
24
|
|
|
13
25
|
interface Props {
|
|
14
26
|
isMenuOpen: boolean;
|
|
@@ -16,11 +28,31 @@ interface Props {
|
|
|
16
28
|
}
|
|
17
29
|
|
|
18
30
|
export const Filters = (props: Props) => {
|
|
31
|
+
const router = useRouter();
|
|
19
32
|
const facets = useAppSelector((state) => state.category.facets);
|
|
20
33
|
const dispatch = useAppDispatch();
|
|
21
34
|
const { t } = useLocalization();
|
|
22
35
|
const { isMenuOpen, setIsMenuOpen } = props;
|
|
23
36
|
|
|
37
|
+
const handleSelectFilter = ({
|
|
38
|
+
facet,
|
|
39
|
+
choice
|
|
40
|
+
}: {
|
|
41
|
+
facet: Facet;
|
|
42
|
+
choice: FacetChoice;
|
|
43
|
+
}) => {
|
|
44
|
+
if (facet.key === 'category_ids') {
|
|
45
|
+
router.push(choice.url);
|
|
46
|
+
} else {
|
|
47
|
+
dispatch(
|
|
48
|
+
toggleFacet({
|
|
49
|
+
facet,
|
|
50
|
+
choice
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
24
56
|
const haveFilter = useMemo(() => {
|
|
25
57
|
return (
|
|
26
58
|
facets.filter(
|
|
@@ -34,6 +66,10 @@ export const Filters = (props: Props) => {
|
|
|
34
66
|
dispatch(resetSelectedFacets());
|
|
35
67
|
};
|
|
36
68
|
|
|
69
|
+
const sizeKey = commonProductAttributes.find(
|
|
70
|
+
(item) => item.translationKey === 'size'
|
|
71
|
+
).key;
|
|
72
|
+
|
|
37
73
|
return (
|
|
38
74
|
<div
|
|
39
75
|
className={clsx(
|
|
@@ -52,7 +88,71 @@ export const Filters = (props: Props) => {
|
|
|
52
88
|
<span>{t('category.filters.ready_to_wear')}</span>
|
|
53
89
|
</div>
|
|
54
90
|
{facets.map((facet) => {
|
|
55
|
-
|
|
91
|
+
let Component = null;
|
|
92
|
+
const choices = [...facet.data.choices];
|
|
93
|
+
|
|
94
|
+
if (facet.key === sizeKey) {
|
|
95
|
+
// If it's a size facet, use the custom size filter component
|
|
96
|
+
Component = SizeFilter;
|
|
97
|
+
|
|
98
|
+
const order = ['xs', 's', 'm', 'l', 'xl'];
|
|
99
|
+
choices.sort((a, b) => {
|
|
100
|
+
return (
|
|
101
|
+
order.indexOf(a.label.toLowerCase()) -
|
|
102
|
+
order.indexOf(b.label.toLowerCase())
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
Component =
|
|
107
|
+
COMPONENT_TYPES[facet.widget_type] ||
|
|
108
|
+
COMPONENT_TYPES[WIDGET_TYPE.category];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Accordion
|
|
113
|
+
key={facet.key}
|
|
114
|
+
title={facet.name}
|
|
115
|
+
isCollapse={choices.some((choice) => choice.is_selected)}
|
|
116
|
+
dataTestId={`filter-${facet.name}`}
|
|
117
|
+
>
|
|
118
|
+
<div
|
|
119
|
+
className={clsx(
|
|
120
|
+
'flex gap-4 flex-wrap',
|
|
121
|
+
facet.key === sizeKey ? 'flex-row' : 'flex-col' // TODO: This condition must be refactor to a better way
|
|
122
|
+
)}
|
|
123
|
+
>
|
|
124
|
+
{choices.map((choice, index) => (
|
|
125
|
+
<Component // TODO: This dynamic component can be a hook or higher order component so it props can be standardized
|
|
126
|
+
key={choice.label}
|
|
127
|
+
data={choice}
|
|
128
|
+
name={facet.key}
|
|
129
|
+
onChange={() => {
|
|
130
|
+
if (facet.key !== sizeKey) {
|
|
131
|
+
// TODO: This condition must be refactor to a better way
|
|
132
|
+
handleSelectFilter({ facet, choice });
|
|
133
|
+
}
|
|
134
|
+
}}
|
|
135
|
+
onClick={() => {
|
|
136
|
+
if (facet.key === sizeKey) {
|
|
137
|
+
// TODO: This condition must be refactor to a better way
|
|
138
|
+
handleSelectFilter({ facet, choice });
|
|
139
|
+
}
|
|
140
|
+
}}
|
|
141
|
+
checked={choice.is_selected}
|
|
142
|
+
data-testid={`${choice.label.trim()}`}
|
|
143
|
+
>
|
|
144
|
+
{choice.label} (
|
|
145
|
+
<span
|
|
146
|
+
data-testid={`filter-count-${facet.name.toLowerCase()}-${index}`}
|
|
147
|
+
>
|
|
148
|
+
{choice.quantity}
|
|
149
|
+
</span>
|
|
150
|
+
)
|
|
151
|
+
</Component>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
154
|
+
</Accordion>
|
|
155
|
+
);
|
|
56
156
|
})}
|
|
57
157
|
<div className="lg:hidden">
|
|
58
158
|
<CategoryActiveFilters />
|
|
@@ -11,12 +11,17 @@ import * as yup from 'yup';
|
|
|
11
11
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
12
12
|
import clsx from 'clsx';
|
|
13
13
|
import { useGetBasketQuery } from '@akinon/next/data/client/basket';
|
|
14
|
-
import { useCaptcha } from '@akinon/next/hooks';
|
|
14
|
+
import { useCaptcha, useLocalization } from '@akinon/next/hooks';
|
|
15
15
|
import { AuthError } from '@akinon/next/types';
|
|
16
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
17
16
|
import { Image } from '@akinon/next/components/image';
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
18
|
+
|
|
19
|
+
const oauthProviders: Array<{
|
|
20
|
+
key: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
image?: string;
|
|
23
|
+
localeKey?: string;
|
|
24
|
+
}> = [
|
|
20
25
|
{
|
|
21
26
|
key: 'google',
|
|
22
27
|
label: 'Google',
|
|
@@ -31,10 +36,6 @@ const oauthProviders = [
|
|
|
31
36
|
key: 'apple',
|
|
32
37
|
label: 'Apple',
|
|
33
38
|
image: '/apple.svg'
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
key: 'akifast',
|
|
37
|
-
localeKey: 'auth.login.form.quick_login'
|
|
38
39
|
}
|
|
39
40
|
];
|
|
40
41
|
|
|
@@ -93,7 +94,10 @@ export const Login = () => {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
const fieldErrors = errors.find((error) => error.type === 'field_errors')
|
|
96
|
-
?.data as {
|
|
97
|
+
?.data as {
|
|
98
|
+
name: string;
|
|
99
|
+
value: string[];
|
|
100
|
+
}[];
|
|
97
101
|
const nonFieldErrors = errors.find(
|
|
98
102
|
(error) => error.type === 'non_field_errors'
|
|
99
103
|
)?.data as string[];
|
|
@@ -227,6 +231,13 @@ export const Login = () => {
|
|
|
227
231
|
{provider.localeKey ? t(provider.localeKey) : provider.label}
|
|
228
232
|
</Button>
|
|
229
233
|
))}
|
|
234
|
+
<PluginModule
|
|
235
|
+
component={Component.AkifastQuickLoginButton}
|
|
236
|
+
props={{
|
|
237
|
+
isCaptchaVisible,
|
|
238
|
+
captchaValidated
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
230
241
|
</div>
|
|
231
242
|
</section>
|
|
232
243
|
);
|
|
@@ -123,6 +123,40 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
+
const checkoutProviderProps = {
|
|
127
|
+
product: data.product,
|
|
128
|
+
clearBasket: true,
|
|
129
|
+
addBeforeClick: variantsSelectionCheck,
|
|
130
|
+
openMiniBasket: false,
|
|
131
|
+
className: clsx([
|
|
132
|
+
'py-2.5',
|
|
133
|
+
'bg-black',
|
|
134
|
+
'relative',
|
|
135
|
+
'hover:bg-black',
|
|
136
|
+
'before:content-[""]',
|
|
137
|
+
'before:w-6',
|
|
138
|
+
'before:h-6',
|
|
139
|
+
'before:bg-white',
|
|
140
|
+
'before:absolute',
|
|
141
|
+
'before:rounded-r-[18px]',
|
|
142
|
+
'before:left-0',
|
|
143
|
+
'after:content-[""]',
|
|
144
|
+
'after:absolute',
|
|
145
|
+
'after:w-3',
|
|
146
|
+
'after:h-3',
|
|
147
|
+
'after:bg-[#d02c2f]',
|
|
148
|
+
'after:rounded-xl',
|
|
149
|
+
'after:left-1'
|
|
150
|
+
]),
|
|
151
|
+
onError: (error) =>
|
|
152
|
+
setProductError(
|
|
153
|
+
error?.data?.non_field_errors ||
|
|
154
|
+
Object.keys(error?.data).map(
|
|
155
|
+
(key) => `${key}: ${error?.data[key].join(', ')}`
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
};
|
|
159
|
+
|
|
126
160
|
return (
|
|
127
161
|
<>
|
|
128
162
|
<div
|
|
@@ -181,42 +215,18 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
181
215
|
</Button>
|
|
182
216
|
|
|
183
217
|
<PluginModule
|
|
184
|
-
component={Component.
|
|
218
|
+
component={Component.AkifastCheckoutButton}
|
|
185
219
|
props={{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
addBeforeClick: variantsSelectionCheck,
|
|
189
|
-
openMiniBasket: false,
|
|
190
|
-
className: clsx([
|
|
191
|
-
'py-2.5',
|
|
192
|
-
'bg-black',
|
|
193
|
-
'relative',
|
|
194
|
-
'hover:bg-black',
|
|
195
|
-
'before:content-[""]',
|
|
196
|
-
'before:w-6',
|
|
197
|
-
'before:h-6',
|
|
198
|
-
'before:bg-white',
|
|
199
|
-
'before:absolute',
|
|
200
|
-
'before:rounded-r-[18px]',
|
|
201
|
-
'before:left-0',
|
|
202
|
-
'after:content-[""]',
|
|
203
|
-
'after:absolute',
|
|
204
|
-
'after:w-3',
|
|
205
|
-
'after:h-3',
|
|
206
|
-
'after:bg-[#d02c2f]',
|
|
207
|
-
'after:rounded-xl',
|
|
208
|
-
'after:left-1'
|
|
209
|
-
]),
|
|
210
|
-
onError: (error) =>
|
|
211
|
-
setProductError(
|
|
212
|
-
error?.data?.non_field_errors ||
|
|
213
|
-
Object.keys(error?.data).map(
|
|
214
|
-
(key) => `${key}: ${error?.data[key].join(', ')}`
|
|
215
|
-
)
|
|
216
|
-
)
|
|
220
|
+
...checkoutProviderProps,
|
|
221
|
+
isPdp: true
|
|
217
222
|
}}
|
|
218
223
|
/>
|
|
219
224
|
|
|
225
|
+
<PluginModule
|
|
226
|
+
component={Component.OneClickCheckoutButtons}
|
|
227
|
+
props={checkoutProviderProps}
|
|
228
|
+
/>
|
|
229
|
+
|
|
220
230
|
<MiscButtons
|
|
221
231
|
productName={data.product.name}
|
|
222
232
|
productPk={data.product.pk}
|
|
@@ -3,19 +3,7 @@
|
|
|
3
3
|
"display": "Default",
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
"baseUrl": "./src",
|
|
6
|
-
"paths": {
|
|
7
|
-
"@theme/*": ["./*"],
|
|
8
|
-
"@root/*": ["./app/[commerce]/[locale]/[currency]/*"],
|
|
9
|
-
"@product/*": ["./app/[commerce]/[locale]/[currency]/product/*"],
|
|
10
|
-
"@group-product/*": [
|
|
11
|
-
"./app/[commerce]/[locale]/[currency]/group-product/*"
|
|
12
|
-
],
|
|
13
|
-
"@category/*": ["./app/[commerce]/[locale]/[currency]/category/*"],
|
|
14
|
-
"@special-page/*": [
|
|
15
|
-
"./app/[commerce]/[locale]/[currency]/special-page/*"
|
|
16
|
-
],
|
|
17
|
-
"@flat-page/*": ["./app/[commerce]/[locale]/[currency]/flat-page/*"]
|
|
18
|
-
},
|
|
6
|
+
"paths": { "@theme/*": ["./*"] },
|
|
19
7
|
"allowSyntheticDefaultImports": true,
|
|
20
8
|
"composite": false,
|
|
21
9
|
"declaration": true,
|
|
@@ -52,5 +40,7 @@
|
|
|
52
40
|
".next/types/**/*.ts",
|
|
53
41
|
"../../packages/**/*"
|
|
54
42
|
],
|
|
55
|
-
"exclude": ["node_modules",
|
|
43
|
+
"exclude": ["node_modules",
|
|
44
|
+
"../../packages/projectzero/app-template"
|
|
45
|
+
]
|
|
56
46
|
}
|
package/commands/create.ts
CHANGED
|
@@ -3,7 +3,6 @@ import * as fs from 'fs';
|
|
|
3
3
|
import * as readline from 'readline';
|
|
4
4
|
import { slugify } from '../utils';
|
|
5
5
|
|
|
6
|
-
const { execSync } = require('child_process');
|
|
7
6
|
const loadingSpinner = require('loading-spinner');
|
|
8
7
|
|
|
9
8
|
interface Question {
|
|
@@ -140,8 +139,7 @@ export default async (): Promise<void> => {
|
|
|
140
139
|
const answers = await getAnswers();
|
|
141
140
|
const brandName =
|
|
142
141
|
answers.brandName === '.' ? path.basename(workingDir) : answers.brandName;
|
|
143
|
-
const projectDir =
|
|
144
|
-
const relativeProjectDir = answers.brandName === '.' ? '.' : slugify(brandName);
|
|
142
|
+
const projectDir = path.resolve(workingDir, slugify(brandName));
|
|
145
143
|
|
|
146
144
|
if (!fs.existsSync(projectDir)) {
|
|
147
145
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
@@ -174,34 +172,12 @@ export default async (): Promise<void> => {
|
|
|
174
172
|
name: slugify(brandName)
|
|
175
173
|
});
|
|
176
174
|
|
|
177
|
-
|
|
178
|
-
console.log('\x1b[34m%s\x1b[0m', '\nš Installing packages...\n');
|
|
179
|
-
|
|
180
|
-
execSync(`cd ${relativeProjectDir} && yarn install`, { stdio: 'ignore' });
|
|
181
|
-
|
|
182
175
|
loadingSpinner.stop();
|
|
183
176
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
\x1b[35m$ yarn dev\x1b[0m
|
|
190
|
-
\x1b[32mLaunches the development server.\x1b[0m
|
|
191
|
-
|
|
192
|
-
\x1b[35m$ yarn build\x1b[0m
|
|
193
|
-
\x1b[32mCompiles the app into static files for production.\x1b[0m
|
|
194
|
-
|
|
195
|
-
\x1b[35m$ yarn start\x1b[0m
|
|
196
|
-
\x1b[32mRuns the production server.\x1b[0m
|
|
197
|
-
`;
|
|
198
|
-
|
|
199
|
-
const getStartedMessage = answers.brandName === '.'
|
|
200
|
-
? 'To get started, you can type:\n\n \x1b[35m$ yarn dev\x1b[0m\n'
|
|
201
|
-
: `To get started, you can type:\n\n \x1b[35m$ cd ${relativeProjectDir}\x1b[0m\n \x1b[35m$ yarn dev\x1b[0m\n`;
|
|
177
|
+
console.log(
|
|
178
|
+
'\x1b[32m%s\x1b[0m',
|
|
179
|
+
`\n ā ${answers.brandName} project is ready.\n`
|
|
180
|
+
);
|
|
202
181
|
|
|
203
|
-
console.log('\x1b[32m%s\x1b[0m', successMessage);
|
|
204
|
-
console.log('\x1b[36m%s\x1b[0m', getStartedMessage);
|
|
205
|
-
console.log('\x1b[33m%s\x1b[0m', 'Project setup is complete\n');
|
|
206
182
|
console.log('\x1b[33m%s\x1b[0m', 'Project Zero - Akinon\n');
|
|
207
183
|
};
|