@akinon/projectzero 1.102.0-rc.79 → 1.102.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 +5 -236
- package/app-template/.env.example +0 -1
- package/app-template/CHANGELOG.md +330 -5014
- package/app-template/README.md +1 -25
- package/app-template/package.json +19 -21
- package/app-template/public/locales/en/checkout.json +0 -6
- package/app-template/public/locales/en/common.json +1 -48
- package/app-template/public/locales/tr/checkout.json +0 -6
- package/app-template/public/locales/tr/common.json +1 -48
- package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +82 -9
- package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +11 -16
- package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +11 -17
- package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +38 -36
- package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +1 -12
- package/app-template/src/app/[commerce]/[locale]/[currency]/{pz-not-found/page.tsx → not-found.tsx} +5 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +48 -46
- package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +20 -21
- package/app-template/src/app/api/form/[...id]/route.ts +7 -1
- package/app-template/src/assets/fonts/pz-icon.css +0 -3
- package/app-template/src/components/__tests__/link.test.tsx +0 -2
- package/app-template/src/components/accordion.tsx +19 -22
- package/app-template/src/components/currency-select.tsx +0 -1
- package/app-template/src/components/file-input.tsx +7 -27
- package/app-template/src/components/generate-form-fields.tsx +4 -43
- package/app-template/src/components/input.tsx +2 -9
- package/app-template/src/components/modal.tsx +16 -32
- package/app-template/src/components/pagination.tsx +0 -1
- package/app-template/src/components/price.tsx +1 -1
- package/app-template/src/components/select.tsx +26 -38
- package/app-template/src/components/types/index.ts +1 -25
- package/app-template/src/hooks/index.ts +0 -2
- package/app-template/src/plugins.js +1 -3
- package/app-template/src/settings.js +2 -8
- package/app-template/src/types/index.ts +0 -17
- package/app-template/src/views/account/address-form.tsx +4 -8
- package/app-template/src/views/account/contact-form.tsx +1 -1
- package/app-template/src/views/account/content-header.tsx +2 -2
- package/app-template/src/views/account/faq/faq-tabs.tsx +2 -8
- package/app-template/src/views/basket/basket-item.tsx +14 -22
- package/app-template/src/views/basket/summary.tsx +7 -10
- package/app-template/src/views/breadcrumb.tsx +2 -2
- package/app-template/src/views/category/category-info.tsx +0 -1
- package/app-template/src/views/category/filters/index.tsx +1 -1
- package/app-template/src/views/checkout/summary.tsx +0 -10
- package/app-template/src/views/guest-login/index.tsx +1 -6
- package/app-template/src/views/header/action-menu.tsx +1 -1
- package/app-template/src/views/header/search/index.tsx +5 -17
- package/app-template/src/views/product/product-info.tsx +263 -62
- package/app-template/src/views/product/slider.tsx +73 -86
- package/app-template/src/widgets/footer-menu.tsx +2 -6
- package/commands/plugins.ts +16 -63
- package/dist/commands/plugins.js +16 -57
- package/package.json +1 -1
- package/app-template/.github/instructions/routing.instructions.md +0 -603
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
- package/app-template/src/app/api/image-proxy/route.ts +0 -1
- package/app-template/src/app/api/similar-product-list/route.ts +0 -1
- package/app-template/src/app/api/similar-products/route.ts +0 -1
- package/app-template/src/hooks/use-product-cart.ts +0 -77
- package/app-template/src/hooks/use-stock-alert.ts +0 -74
- package/app-template/src/utils/variant-validation.ts +0 -41
- package/app-template/src/views/basket/basket-content.tsx +0 -106
- package/app-template/src/views/checkout/steps/payment/options/store-credit.tsx +0 -121
- package/app-template/src/views/product/product-actions.tsx +0 -165
- package/app-template/src/views/product/product-share.tsx +0 -56
- package/app-template/src/views/product/product-variants.tsx +0 -26
|
@@ -13,87 +13,89 @@ export async function generateMetadata({
|
|
|
13
13
|
let result: Metadata = {};
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
const
|
|
16
|
+
const { data } = await getProductData({
|
|
17
17
|
pk: params.pk,
|
|
18
18
|
searchParams
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
21
|
+
const product = data?.product;
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
if (!product) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
26
|
|
|
27
27
|
result = {
|
|
28
28
|
title: product.name,
|
|
29
|
-
description: String(product.attributes
|
|
29
|
+
description: String(product.attributes?.description),
|
|
30
30
|
twitter: {
|
|
31
31
|
title: product.name,
|
|
32
|
-
description: String(product.attributes
|
|
32
|
+
description: String(product.attributes?.description)
|
|
33
33
|
},
|
|
34
34
|
openGraph: {
|
|
35
35
|
title: product.name,
|
|
36
|
-
description: String(product.attributes
|
|
36
|
+
description: String(product.attributes?.description),
|
|
37
37
|
images: product.productimage_set?.map((item) => ({
|
|
38
38
|
url: item.image
|
|
39
39
|
}))
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
} catch (error: unknown) {
|
|
43
|
+
if ((error as Error & { status?: number })?.status === 404) {
|
|
44
|
+
notFound();
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
44
48
|
|
|
45
49
|
return result;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
async function Page({ params, searchParams }: PageProps<{ pk: number }>) {
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
if (params.pk === undefined || isNaN(Number(params.pk))) {
|
|
54
|
+
notFound();
|
|
55
|
+
}
|
|
51
56
|
|
|
52
57
|
try {
|
|
53
|
-
[
|
|
58
|
+
const [{ data, breadcrumbData }, deliveryReturn] = await Promise.all([
|
|
54
59
|
getProductData({
|
|
55
60
|
pk: params.pk,
|
|
56
61
|
searchParams
|
|
57
62
|
}),
|
|
58
63
|
getWidgetData({ slug: 'product-delivery-returns' })
|
|
59
64
|
]);
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return notFound();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// If product data is not found, return 404
|
|
65
|
-
if (!productData || !productData.data || !productData.data.product) {
|
|
66
|
-
return notFound();
|
|
67
|
-
}
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
const jsonLd = generateJsonLd(data.product);
|
|
66
|
+
const jsonLd = generateJsonLd(data.product);
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<ProductLayout data={data} breadcrumbData={breadcrumbData}>
|
|
71
|
+
<div className="flex flex-col items-center">
|
|
72
|
+
<h1
|
|
73
|
+
className="mt-4 text-2xl text-center md:mt-0"
|
|
74
|
+
data-testid="product-name"
|
|
75
|
+
>
|
|
76
|
+
{data.product.name}
|
|
77
|
+
</h1>
|
|
82
78
|
|
|
83
|
-
|
|
79
|
+
<ProductInfo data={data} />
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
<AccordionWrapper
|
|
82
|
+
data={data}
|
|
83
|
+
deliveryReturn={deliveryReturn?.attributes}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</ProductLayout>
|
|
87
|
+
<script
|
|
88
|
+
type="application/ld+json"
|
|
89
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
} catch (error: unknown) {
|
|
94
|
+
if ((error as Error & { status?: number })?.status === 404) {
|
|
95
|
+
notFound();
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
export default withSegmentDefaults(Page, { segmentType: 'page' });
|
|
@@ -7,30 +7,29 @@ import SpecialPageCarousel from '@theme/widgets/special-page-carousel';
|
|
|
7
7
|
import { notFound } from 'next/navigation';
|
|
8
8
|
|
|
9
9
|
async function Page({ params, searchParams }: PageProps<{ pk: number }>) {
|
|
10
|
-
let data;
|
|
11
|
-
|
|
12
10
|
try {
|
|
13
|
-
data = await getSpecialPageData({ pk: params.pk, searchParams });
|
|
14
|
-
} catch (error) {
|
|
15
|
-
return notFound();
|
|
16
|
-
}
|
|
11
|
+
const data = await getSpecialPageData({ pk: params.pk, searchParams });
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
<CategoryLayout data={data}>
|
|
16
|
+
{data.special_page && (
|
|
17
|
+
<>
|
|
18
|
+
<SpecialPageBanner data={data.special_page} />
|
|
19
|
+
<SpecialPageCarousel
|
|
20
|
+
slug={data.special_page.video_embedded_code}
|
|
21
|
+
/>
|
|
22
|
+
</>
|
|
23
|
+
)}
|
|
24
|
+
</CategoryLayout>
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
} catch (error: unknown) {
|
|
28
|
+
if ((error as Error & { status?: number })?.status === 404) {
|
|
29
|
+
notFound();
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
20
32
|
}
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<CategoryLayout data={data}>
|
|
25
|
-
{data.special_page && (
|
|
26
|
-
<>
|
|
27
|
-
<SpecialPageBanner data={data.special_page} />
|
|
28
|
-
<SpecialPageCarousel slug={data.special_page.video_embedded_code} />
|
|
29
|
-
</>
|
|
30
|
-
)}
|
|
31
|
-
</CategoryLayout>
|
|
32
|
-
</>
|
|
33
|
-
);
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export default withSegmentDefaults(Page, { segmentType: 'page' });
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState } from 'react';
|
|
3
|
+
import { ReactNode, useState } from 'react';
|
|
4
4
|
import { Icon } from './icon';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
type AccordionProps = {
|
|
8
|
+
isCollapse?: boolean;
|
|
9
|
+
title?: string;
|
|
10
|
+
subTitle?: string;
|
|
11
|
+
icons?: string[];
|
|
12
|
+
iconSize?: number;
|
|
13
|
+
iconColor?: string;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
titleClassName?: string;
|
|
17
|
+
dataTestId?: string;
|
|
18
|
+
};
|
|
7
19
|
|
|
8
20
|
export const Accordion = ({
|
|
9
21
|
isCollapse = false,
|
|
10
|
-
collapseClassName,
|
|
11
22
|
title,
|
|
12
23
|
subTitle,
|
|
13
24
|
icons = ['chevron-up', 'chevron-down'],
|
|
14
25
|
iconSize = 16,
|
|
15
26
|
iconColor = 'fill-[#000000]',
|
|
16
27
|
children,
|
|
17
|
-
headerClassName,
|
|
18
28
|
className,
|
|
19
29
|
titleClassName,
|
|
20
|
-
|
|
21
|
-
dataTestId,
|
|
22
|
-
contentClassName
|
|
30
|
+
dataTestId
|
|
23
31
|
}: AccordionProps) => {
|
|
24
32
|
const [collapse, setCollapse] = useState(isCollapse);
|
|
25
33
|
|
|
@@ -31,22 +39,15 @@ export const Accordion = ({
|
|
|
31
39
|
)}
|
|
32
40
|
>
|
|
33
41
|
<div
|
|
34
|
-
className=
|
|
35
|
-
'flex items-center justify-between cursor-pointer',
|
|
36
|
-
headerClassName
|
|
37
|
-
)}
|
|
42
|
+
className="flex items-center justify-between cursor-pointer"
|
|
38
43
|
onClick={() => setCollapse(!collapse)}
|
|
39
44
|
data-testid={dataTestId}
|
|
40
45
|
>
|
|
41
|
-
<div className=
|
|
46
|
+
<div className="flex flex-col">
|
|
42
47
|
{title && (
|
|
43
48
|
<h3 className={twMerge('text-sm', titleClassName)}>{title}</h3>
|
|
44
49
|
)}
|
|
45
|
-
{subTitle &&
|
|
46
|
-
<h4 className={twMerge('text-xs text-gray-700', subTitleClassName)}>
|
|
47
|
-
{subTitle}
|
|
48
|
-
</h4>
|
|
49
|
-
)}
|
|
50
|
+
{subTitle && <h4 className="text-xs text-gray-700">{subTitle}</h4>}
|
|
50
51
|
</div>
|
|
51
52
|
|
|
52
53
|
{icons && (
|
|
@@ -57,11 +58,7 @@ export const Accordion = ({
|
|
|
57
58
|
/>
|
|
58
59
|
)}
|
|
59
60
|
</div>
|
|
60
|
-
{collapse &&
|
|
61
|
-
<div className={twMerge('mt-3 text-sm', collapseClassName)}>
|
|
62
|
-
{children}
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
61
|
+
{collapse && <div className="mt-3 text-sm">{children}</div>}
|
|
65
62
|
</div>
|
|
66
63
|
);
|
|
67
64
|
};
|
|
@@ -70,7 +70,6 @@ export const CurrencySelect = (props: CurrencySelectProps) => {
|
|
|
70
70
|
onClick={confirmModalHandleClick}
|
|
71
71
|
appearance="filled"
|
|
72
72
|
className="font-medium px-10 py-4 h-12"
|
|
73
|
-
data-testid="currency-modal-confirm"
|
|
74
73
|
>
|
|
75
74
|
{t('common.currency_modal.continue')}
|
|
76
75
|
</Button>
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
3
|
import { FileInputProps } from '@theme/components/types';
|
|
4
|
+
import clsx from 'clsx';
|
|
4
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
|
|
7
7
|
export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
|
|
8
|
-
function FileInput(
|
|
9
|
-
{
|
|
10
|
-
buttonClassName,
|
|
11
|
-
onChange,
|
|
12
|
-
fileClassName,
|
|
13
|
-
fileNameWrapperClassName,
|
|
14
|
-
fileInputClassName,
|
|
15
|
-
...props
|
|
16
|
-
},
|
|
17
|
-
ref
|
|
18
|
-
) {
|
|
8
|
+
function FileInput({ className, onChange, ...props }, ref) {
|
|
19
9
|
const { t } = useLocalization();
|
|
20
10
|
const [fileNames, setFileNames] = useState<string[]>([]);
|
|
21
11
|
|
|
@@ -34,34 +24,24 @@ export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
|
|
|
34
24
|
type="file"
|
|
35
25
|
{...props}
|
|
36
26
|
ref={ref}
|
|
37
|
-
className=
|
|
38
|
-
'absolute inset-0 w-full h-full opacity-0 cursor-pointer',
|
|
39
|
-
fileInputClassName
|
|
40
|
-
)}
|
|
27
|
+
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
41
28
|
onChange={handleFileChange}
|
|
42
29
|
/>
|
|
43
30
|
<button
|
|
44
31
|
type="button"
|
|
45
|
-
className={
|
|
46
|
-
'bg-primary text-white py-2 px-4 text-sm',
|
|
47
|
-
buttonClassName
|
|
48
|
-
)}
|
|
32
|
+
className={clsx('bg-primary text-white py-2 px-4 text-sm', className)}
|
|
49
33
|
>
|
|
50
34
|
{t('common.file_input.select_file')}
|
|
51
35
|
</button>
|
|
52
|
-
<div
|
|
53
|
-
className={twMerge('mt-1 text-gray-500', fileNameWrapperClassName)}
|
|
54
|
-
>
|
|
36
|
+
<div className="mt-1 text-gray-500">
|
|
55
37
|
{fileNames.length > 0 ? (
|
|
56
|
-
<ul className=
|
|
38
|
+
<ul className="list-disc pl-4 text-xs">
|
|
57
39
|
{fileNames.map((name, index) => (
|
|
58
40
|
<li key={index}>{name}</li>
|
|
59
41
|
))}
|
|
60
42
|
</ul>
|
|
61
43
|
) : (
|
|
62
|
-
<span className=
|
|
63
|
-
{t('common.file_input.no_file')}
|
|
64
|
-
</span>
|
|
44
|
+
<span className="text-xs">{t('common.file_input.no_file')}</span>
|
|
65
45
|
)}
|
|
66
46
|
</div>
|
|
67
47
|
</div>
|
|
@@ -7,7 +7,6 @@ import { useForm } from 'react-hook-form';
|
|
|
7
7
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
8
8
|
import * as yup from 'yup';
|
|
9
9
|
import DynamicForm from './dynamic-form';
|
|
10
|
-
|
|
11
10
|
import {
|
|
12
11
|
AllFieldClassesType,
|
|
13
12
|
FieldPropertiesType,
|
|
@@ -15,7 +14,6 @@ import {
|
|
|
15
14
|
FormPropertiesType,
|
|
16
15
|
Schema
|
|
17
16
|
} from '@akinon/next/types';
|
|
18
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
19
17
|
|
|
20
18
|
export function GenerateFormFields({
|
|
21
19
|
schema,
|
|
@@ -30,14 +28,8 @@ export function GenerateFormFields({
|
|
|
30
28
|
formProperties: FormPropertiesType;
|
|
31
29
|
submitButtonText: string;
|
|
32
30
|
}) {
|
|
33
|
-
const { t } = useLocalization();
|
|
34
31
|
const [fields, setFields] = useState([]);
|
|
35
32
|
const [loading, setIsLoading] = useState(true);
|
|
36
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
37
|
-
const [submitStatus, setSubmitStatus] = useState<
|
|
38
|
-
'idle' | 'success' | 'error'
|
|
39
|
-
>('idle');
|
|
40
|
-
const [submitMessage, setSubmitMessage] = useState('');
|
|
41
33
|
|
|
42
34
|
const generateValidationSchema = () => {
|
|
43
35
|
const schemaObject = {};
|
|
@@ -88,7 +80,6 @@ export function GenerateFormFields({
|
|
|
88
80
|
const {
|
|
89
81
|
handleSubmit,
|
|
90
82
|
register,
|
|
91
|
-
reset,
|
|
92
83
|
formState: { errors }
|
|
93
84
|
} = useForm<FormField>({
|
|
94
85
|
resolver: yupResolver(generateValidationSchema())
|
|
@@ -117,10 +108,6 @@ export function GenerateFormFields({
|
|
|
117
108
|
}, [schema, fieldProperties]);
|
|
118
109
|
|
|
119
110
|
const onSubmit = async (data) => {
|
|
120
|
-
setIsSubmitting(true);
|
|
121
|
-
setSubmitStatus('idle');
|
|
122
|
-
setSubmitMessage('');
|
|
123
|
-
|
|
124
111
|
try {
|
|
125
112
|
const formData = new FormData();
|
|
126
113
|
|
|
@@ -128,25 +115,12 @@ export function GenerateFormFields({
|
|
|
128
115
|
formData.append(key, data[key]);
|
|
129
116
|
});
|
|
130
117
|
|
|
131
|
-
|
|
118
|
+
fetch(formProperties.actionUrl, {
|
|
132
119
|
method: 'POST',
|
|
133
120
|
body: formData
|
|
134
121
|
});
|
|
135
|
-
|
|
136
|
-
if (response.ok) {
|
|
137
|
-
setSubmitStatus('success');
|
|
138
|
-
setSubmitMessage(t('common.forms.success'));
|
|
139
|
-
reset();
|
|
140
|
-
} else {
|
|
141
|
-
setSubmitStatus('error');
|
|
142
|
-
setSubmitMessage(t('common.forms.error'));
|
|
143
|
-
}
|
|
144
122
|
} catch (error) {
|
|
145
|
-
console.error(
|
|
146
|
-
setSubmitStatus('error');
|
|
147
|
-
setSubmitMessage(t('common.forms.error'));
|
|
148
|
-
} finally {
|
|
149
|
-
setIsSubmitting(false);
|
|
123
|
+
console.error('Form submit error:', error);
|
|
150
124
|
}
|
|
151
125
|
};
|
|
152
126
|
|
|
@@ -363,22 +337,9 @@ export function GenerateFormFields({
|
|
|
363
337
|
<LoaderSpinner />
|
|
364
338
|
) : (
|
|
365
339
|
<>
|
|
366
|
-
{submitStatus === 'success' && (
|
|
367
|
-
<div className="mb-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded">
|
|
368
|
-
{submitMessage}
|
|
369
|
-
</div>
|
|
370
|
-
)}
|
|
371
|
-
|
|
372
|
-
{submitStatus === 'error' && (
|
|
373
|
-
<div className="mb-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded">
|
|
374
|
-
{submitMessage}
|
|
375
|
-
</div>
|
|
376
|
-
)}
|
|
377
|
-
|
|
378
340
|
{fields.map((field: FormField) => generateField(field))}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
{isSubmitting ? t('common.forms.sending') : submitButtonText}
|
|
341
|
+
<Button type="submit" className="w-full">
|
|
342
|
+
{submitButtonText}
|
|
382
343
|
</Button>
|
|
383
344
|
</>
|
|
384
345
|
)}
|
|
@@ -48,13 +48,7 @@ export const Input = forwardRef<
|
|
|
48
48
|
props.className
|
|
49
49
|
);
|
|
50
50
|
|
|
51
|
-
const inputProps: {
|
|
52
|
-
id?: string;
|
|
53
|
-
ref?: Ref<HTMLInputElement>;
|
|
54
|
-
className?: string;
|
|
55
|
-
onFocus?: () => void;
|
|
56
|
-
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
|
|
57
|
-
} = {
|
|
51
|
+
const inputProps: any = {
|
|
58
52
|
id,
|
|
59
53
|
ref,
|
|
60
54
|
className: inputClass,
|
|
@@ -78,8 +72,7 @@ export const Input = forwardRef<
|
|
|
78
72
|
hasFloatingLabel,
|
|
79
73
|
'mb-2': !hasFloatingLabel,
|
|
80
74
|
'-translate-y-2 bg-white inline-flex h-auto':
|
|
81
|
-
hasFloatingLabel &&
|
|
82
|
-
(focused || hasValue || props.value || props.format)
|
|
75
|
+
hasFloatingLabel && (focused || hasValue)
|
|
83
76
|
})
|
|
84
77
|
)}
|
|
85
78
|
>
|
|
@@ -4,7 +4,16 @@ import ReactPortal from './react-portal';
|
|
|
4
4
|
import { Icon } from './icon';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
export interface ModalProps {
|
|
9
|
+
portalId: string;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
open?: boolean;
|
|
12
|
+
setOpen?: (open: boolean) => void;
|
|
13
|
+
title?: React.ReactNode;
|
|
14
|
+
showCloseButton?: React.ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
8
17
|
|
|
9
18
|
export const Modal = (props: ModalProps) => {
|
|
10
19
|
const {
|
|
@@ -14,14 +23,7 @@ export const Modal = (props: ModalProps) => {
|
|
|
14
23
|
setOpen,
|
|
15
24
|
title = '',
|
|
16
25
|
showCloseButton = true,
|
|
17
|
-
className
|
|
18
|
-
overlayClassName,
|
|
19
|
-
headerWrapperClassName,
|
|
20
|
-
titleClassName,
|
|
21
|
-
closeButtonClassName,
|
|
22
|
-
iconName = 'close',
|
|
23
|
-
iconSize = 16,
|
|
24
|
-
iconClassName
|
|
26
|
+
className
|
|
25
27
|
} = props;
|
|
26
28
|
|
|
27
29
|
useEffect(() => {
|
|
@@ -36,12 +38,7 @@ export const Modal = (props: ModalProps) => {
|
|
|
36
38
|
|
|
37
39
|
return (
|
|
38
40
|
<ReactPortal wrapperId={portalId}>
|
|
39
|
-
<div
|
|
40
|
-
className={twMerge(
|
|
41
|
-
'fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50',
|
|
42
|
-
overlayClassName
|
|
43
|
-
)}
|
|
44
|
-
/>
|
|
41
|
+
<div className="fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50" />
|
|
45
42
|
<section
|
|
46
43
|
className={twMerge(
|
|
47
44
|
'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white',
|
|
@@ -49,28 +46,15 @@ export const Modal = (props: ModalProps) => {
|
|
|
49
46
|
)}
|
|
50
47
|
>
|
|
51
48
|
{(showCloseButton || title) && (
|
|
52
|
-
<div
|
|
53
|
-
className={
|
|
54
|
-
'flex px-6 py-4 border-b border-gray-400',
|
|
55
|
-
headerWrapperClassName
|
|
56
|
-
)}
|
|
57
|
-
>
|
|
58
|
-
{title && (
|
|
59
|
-
<h3 className={twMerge('text-lg font-light', titleClassName)}>
|
|
60
|
-
{title}
|
|
61
|
-
</h3>
|
|
62
|
-
)}
|
|
49
|
+
<div className="flex px-6 py-4 border-b border-gray-400">
|
|
50
|
+
{title && <h3 className="text-lg font-light">{title}</h3>}
|
|
63
51
|
{showCloseButton && (
|
|
64
52
|
<button
|
|
65
53
|
type="button"
|
|
66
54
|
onClick={() => setOpen(false)}
|
|
67
|
-
className=
|
|
55
|
+
className="ml-auto"
|
|
68
56
|
>
|
|
69
|
-
<Icon
|
|
70
|
-
name={iconName}
|
|
71
|
-
size={iconSize}
|
|
72
|
-
className={iconClassName}
|
|
73
|
-
/>
|
|
57
|
+
<Icon name="close" size={16} />
|
|
74
58
|
</button>
|
|
75
59
|
)}
|
|
76
60
|
</div>
|
|
@@ -56,7 +56,7 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
|
|
|
56
56
|
|
|
57
57
|
const currentCurrencyDecimalScale = Settings.localization.currencies.find(
|
|
58
58
|
(currency) => currency.code === currencyCode_
|
|
59
|
-
)
|
|
59
|
+
).decimalScale;
|
|
60
60
|
|
|
61
61
|
return (
|
|
62
62
|
<NumericFormat
|
|
@@ -14,18 +14,14 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
|
|
|
14
14
|
error,
|
|
15
15
|
label,
|
|
16
16
|
required = false,
|
|
17
|
-
labelClassName,
|
|
18
17
|
...rest
|
|
19
18
|
} = props;
|
|
20
19
|
|
|
21
20
|
return (
|
|
22
21
|
<label
|
|
23
|
-
className={
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}),
|
|
27
|
-
labelClassName
|
|
28
|
-
)}
|
|
22
|
+
className={clsx('flex flex-col relative text-xs text-gray-800', {
|
|
23
|
+
'pl-7': icon
|
|
24
|
+
})}
|
|
29
25
|
>
|
|
30
26
|
{icon && (
|
|
31
27
|
<Icon
|
|
@@ -36,40 +32,32 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
|
|
|
36
32
|
)}
|
|
37
33
|
|
|
38
34
|
{label && (
|
|
39
|
-
<span className=
|
|
35
|
+
<span className="mb-1">
|
|
40
36
|
{label} {required && <span className="text-secondary">*</span>}
|
|
41
37
|
</span>
|
|
42
38
|
)}
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
))}
|
|
66
|
-
</select>
|
|
67
|
-
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
|
68
|
-
<svg className="h-4 w-4 fill-current" viewBox="0 0 20 20">
|
|
69
|
-
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
|
|
70
|
-
</svg>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
39
|
+
<select
|
|
40
|
+
{...rest}
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={twMerge(
|
|
43
|
+
clsx(
|
|
44
|
+
'cursor-pointer truncate h-10 w-40 px-2.5 shrink-0 outline-none',
|
|
45
|
+
!borderless &&
|
|
46
|
+
'border border-gray-200 transition-all duration-150 hover:border-primary'
|
|
47
|
+
),
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
{options?.map((option) => (
|
|
52
|
+
<option
|
|
53
|
+
key={option.value}
|
|
54
|
+
value={option.value}
|
|
55
|
+
className={option.class}
|
|
56
|
+
>
|
|
57
|
+
{option.label}
|
|
58
|
+
</option>
|
|
59
|
+
))}
|
|
60
|
+
</select>
|
|
73
61
|
{error && (
|
|
74
62
|
<span className="mt-1 text-sm text-error">{error.message}</span>
|
|
75
63
|
)}
|