@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
|
@@ -48,11 +48,13 @@ async function RootLayout({
|
|
|
48
48
|
translations,
|
|
49
49
|
children
|
|
50
50
|
}: RootLayoutProps) {
|
|
51
|
+
const layoutParams = await params;
|
|
52
|
+
|
|
51
53
|
return (
|
|
52
54
|
<html lang={locale.isoCode} {...(locale.rtl ? { dir: 'rtl' } : {})}>
|
|
53
55
|
<head />
|
|
54
56
|
<body className="overflow-x-hidden">
|
|
55
|
-
<PzRoot translations={translations} {...
|
|
57
|
+
<PzRoot translations={translations} {...layoutParams}>
|
|
56
58
|
<ClientRoot>
|
|
57
59
|
<div className="overflow-x-hidden">
|
|
58
60
|
<MobileAppToggler>
|
|
@@ -3,7 +3,9 @@ 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) {
|
|
7
|
+
const searchParams = await props.searchParams;
|
|
8
|
+
|
|
7
9
|
const data = await getListData({ searchParams });
|
|
8
10
|
|
|
9
11
|
return (
|
package/app-template/src/app/[commerce]/[locale]/[currency]/{pz-not-found/page.tsx → not-found.tsx}
RENAMED
|
@@ -8,10 +8,10 @@ const NotFound = () => {
|
|
|
8
8
|
const { t } = useLocalization();
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
|
-
<div className="py-
|
|
11
|
+
<div className="py-10 lg:py-8 flex flex-col items-center justify-center px-4 lg:px-0">
|
|
12
12
|
<div className="text-8xl font-bold">404</div>
|
|
13
13
|
<h1 className="text-4xl font-bold mb-4">{t('not_found.title')}</h1>
|
|
14
|
-
<p className="text-lg mb-6">{t('not_found.sub_title')}</p>
|
|
14
|
+
<p className="text-lg mb-6 text-center">{t('not_found.sub_title')}</p>
|
|
15
15
|
<Link href={'/'}>
|
|
16
16
|
<Button className="h-auto mt-4 text-base py-3 px-6">
|
|
17
17
|
{t('not_found.button')}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@akinon/next/redux/reducers/checkout';
|
|
12
12
|
import { RootState } from '@theme/redux/store';
|
|
13
13
|
import { ROUTES } from '@theme/routes';
|
|
14
|
-
import { useFetchCheckoutQuery } from '@akinon/next/data/client/checkout';
|
|
14
|
+
import { useFetchCheckoutQuery, useResetCheckoutStateQuery } from '@akinon/next/data/client/checkout';
|
|
15
15
|
import { Button, LoaderSpinner } from '@theme/components';
|
|
16
16
|
import { pushAddPaymentInfo, pushAddShippingInfo } from '@theme/utils/gtm';
|
|
17
17
|
import { CheckoutStep } from '@akinon/next/types';
|
|
@@ -25,6 +25,8 @@ const Checkout = () => {
|
|
|
25
25
|
(state: RootState) => state.checkout
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
+
const { data: indexData, isLoading: isResetStateLoading } = useResetCheckoutStateQuery(null);
|
|
29
|
+
|
|
28
30
|
const {
|
|
29
31
|
data: checkoutData,
|
|
30
32
|
isFetching,
|
|
@@ -32,7 +34,8 @@ const Checkout = () => {
|
|
|
32
34
|
isSuccess,
|
|
33
35
|
refetch: refetchCheckout
|
|
34
36
|
} = useFetchCheckoutQuery(null, {
|
|
35
|
-
refetchOnMountOrArgChange: true
|
|
37
|
+
refetchOnMountOrArgChange: true,
|
|
38
|
+
skip: isResetStateLoading || !indexData
|
|
36
39
|
});
|
|
37
40
|
const initialStepChanged = useRef<boolean>(false);
|
|
38
41
|
const router = useRouter();
|
|
@@ -94,10 +97,10 @@ const Checkout = () => {
|
|
|
94
97
|
);
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
if (isFetching || isError) {
|
|
100
|
+
if (isResetStateLoading || isFetching || isError) {
|
|
98
101
|
return (
|
|
99
102
|
<div className="flex flex-col items-center justify-center h-80">
|
|
100
|
-
{isFetching ? (
|
|
103
|
+
{isResetStateLoading || isFetching ? (
|
|
101
104
|
<LoaderSpinner />
|
|
102
105
|
) : (
|
|
103
106
|
<>
|
package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useEffect, useRef, use } from 'react';
|
|
4
4
|
import { useAppDispatch } from '@akinon/next/redux/hooks';
|
|
5
5
|
import { setCurrentStep } from '@akinon/next/redux/reducers/checkout';
|
|
6
6
|
import { ROUTES } from '@theme/routes';
|
|
@@ -14,14 +14,10 @@ import { Image } from '@akinon/next/components/image';
|
|
|
14
14
|
import { Trans } from '@akinon/next/components/trans';
|
|
15
15
|
import { pushPurchaseEvent } from '@theme/utils/gtm';
|
|
16
16
|
|
|
17
|
-
const CheckoutCompleted = ({
|
|
18
|
-
params
|
|
19
|
-
}: PageProps<{
|
|
20
|
-
token: string;
|
|
21
|
-
}>) => {
|
|
17
|
+
const CheckoutCompleted = (props) => {
|
|
22
18
|
const { t } = useLocalization();
|
|
23
19
|
const dispatch = useAppDispatch();
|
|
24
|
-
|
|
20
|
+
const params = use(props.params) as { token: string };
|
|
25
21
|
const { data, isLoading } = useFetchCheckoutResultQuery(String(params.token));
|
|
26
22
|
|
|
27
23
|
const carouselRef = useRef(null);
|
|
@@ -5,12 +5,12 @@ import { generateJsonLd } from '@theme/utils/generate-jsonld';
|
|
|
5
5
|
import { AccordionWrapper, ProductInfo } from '@theme/views/product';
|
|
6
6
|
import ProductLayout from '@theme/views/product/layout';
|
|
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 = {};
|
|
13
10
|
|
|
11
|
+
const params = await props.params;
|
|
12
|
+
const searchParams = await props.searchParams;
|
|
13
|
+
|
|
14
14
|
try {
|
|
15
15
|
const {
|
|
16
16
|
data: { product }
|
|
@@ -40,7 +40,10 @@ export async function generateMetadata({
|
|
|
40
40
|
return result;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
async function Page(
|
|
43
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
44
|
+
const params = await props.params;
|
|
45
|
+
const searchParams = await props.searchParams;
|
|
46
|
+
|
|
44
47
|
const [{ data, breadcrumbData }, deliveryReturn] = await Promise.all([
|
|
45
48
|
getProductData({
|
|
46
49
|
pk: params.pk,
|
|
@@ -5,7 +5,9 @@ import CategoryLayout from '@theme/views/category/layout';
|
|
|
5
5
|
import SpecialPageBanner from '@theme/widgets/special-page-banner';
|
|
6
6
|
import SpecialPageCarousel from '@theme/widgets/special-page-carousel';
|
|
7
7
|
|
|
8
|
-
async function Page(
|
|
8
|
+
async function Page(props: PageProps<{ pk: number }>) {
|
|
9
|
+
const params = await props.params;
|
|
10
|
+
const searchParams = await props.searchParams;
|
|
9
11
|
const data = await getSpecialPageData({ pk: params.pk, searchParams });
|
|
10
12
|
|
|
11
13
|
return (
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import { Icon, Link, LoaderSpinner } from '@theme/components';
|
|
3
3
|
import { useLocalization } from '@akinon/next/hooks';
|
|
4
4
|
import { useChangeEmailVerificationQuery } from '@akinon/next/data/client/user';
|
|
5
|
+
import { use } from 'react';
|
|
6
|
+
|
|
7
|
+
export default function Page(props) {
|
|
8
|
+
const { id } = use(props.params) as { id: string[] };
|
|
5
9
|
|
|
6
|
-
export default function Page({ params: { id } }) {
|
|
7
10
|
const { t } = useLocalization();
|
|
8
11
|
const { isSuccess, isLoading } = useChangeEmailVerificationQuery(
|
|
9
12
|
id.join('/')
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { Icon, Link, LoaderSpinner } from '@theme/components';
|
|
3
3
|
import { useLocalization } from '@akinon/next/hooks';
|
|
4
4
|
import { useConfirmEmailVerificationQuery } from '@akinon/next/data/client/user';
|
|
5
|
+
import { use } from 'react';
|
|
5
6
|
|
|
6
|
-
export default function Page(
|
|
7
|
+
export default function Page(props) {
|
|
8
|
+
const { id } = use(props.params) as { id: string[] };
|
|
7
9
|
const { t } = useLocalization();
|
|
8
10
|
const { isSuccess, isLoading } = useConfirmEmailVerificationQuery(
|
|
9
11
|
id.join('/')
|
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
} from '@akinon/next/data/client/account';
|
|
11
11
|
import { useLocalization } from '@akinon/next/hooks';
|
|
12
12
|
import PasswordRulesFeedback from '@theme/components/password-rules-feedback';
|
|
13
|
+
import { use } from 'react';
|
|
13
14
|
|
|
14
|
-
export default function NewPassword(
|
|
15
|
+
export default function NewPassword(props) {
|
|
16
|
+
const { id } = use(props.params) as { id: string };
|
|
15
17
|
const { t } = useLocalization();
|
|
16
18
|
const [newPassword, { isSuccess: formSuccess }] = usePasswordResetMutation();
|
|
17
19
|
|
|
@@ -24,7 +26,10 @@ export default function NewPassword({ params: { id } }) {
|
|
|
24
26
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{6,}$/,
|
|
25
27
|
t('forgot_password.create_new_password.form.error.password_rule')
|
|
26
28
|
)
|
|
27
|
-
.max(
|
|
29
|
+
.max(
|
|
30
|
+
50,
|
|
31
|
+
t('forgot_password.create_new_password.form.error.password_max')
|
|
32
|
+
),
|
|
28
33
|
repeatPassword: yup
|
|
29
34
|
.string()
|
|
30
35
|
.required(t('forgot_password.create_new_password.form.error.required'))
|
|
@@ -79,7 +84,10 @@ export default function NewPassword({ params: { id } }) {
|
|
|
79
84
|
required
|
|
80
85
|
/>
|
|
81
86
|
|
|
82
|
-
<PasswordRulesFeedback
|
|
87
|
+
<PasswordRulesFeedback
|
|
88
|
+
password={passwordValue}
|
|
89
|
+
isVisible={errors?.password ? true : false}
|
|
90
|
+
/>
|
|
83
91
|
</div>
|
|
84
92
|
|
|
85
93
|
<div className="mb-2 text-left">
|
|
@@ -1,15 +1,61 @@
|
|
|
1
1
|
import { urlLocaleMatcherRegex } from '@akinon/next/utils';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* XML Sitemap Route
|
|
5
|
+
*
|
|
6
|
+
* This route serves XML sitemaps from an S3 bucket.
|
|
7
|
+
*
|
|
8
|
+
* Required environment variables:
|
|
9
|
+
* - SITEMAP_S3_BUCKET_NAME: The name of the S3 bucket containing the sitemaps
|
|
10
|
+
* Example: "0fb534"
|
|
11
|
+
*
|
|
12
|
+
* If the environment variable is not set, the route will return a 503 Service Unavailable
|
|
13
|
+
* response with a JSON error message.
|
|
14
|
+
*/
|
|
15
|
+
|
|
3
16
|
export const dynamic = 'force-dynamic';
|
|
4
17
|
|
|
5
18
|
export async function GET(request: Request, context: { params }) {
|
|
6
|
-
const node = context.params.node;
|
|
19
|
+
const node = (await context.params).node;
|
|
7
20
|
const url = new URL(request.url);
|
|
8
21
|
const matchedLocale = url.pathname.match(urlLocaleMatcherRegex);
|
|
9
22
|
|
|
23
|
+
const s3BucketName = process.env.SITEMAP_S3_BUCKET_NAME;
|
|
24
|
+
|
|
25
|
+
if (!s3BucketName) {
|
|
26
|
+
return new Response(
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
error: 'Configuration error',
|
|
29
|
+
message: 'Please set the SITEMAP_S3_BUCKET_NAME environment variable'
|
|
30
|
+
}),
|
|
31
|
+
{
|
|
32
|
+
status: 503,
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
10
40
|
const sitemap = await fetch(
|
|
11
|
-
`https://s3.eu-central-1.amazonaws.com/
|
|
41
|
+
`https://s3.eu-central-1.amazonaws.com/${s3BucketName}/sitemaps/sitemaps/sitemap-${node}.xml.gz`
|
|
12
42
|
);
|
|
43
|
+
|
|
44
|
+
if (!sitemap.ok) {
|
|
45
|
+
return new Response(
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
error: 'Sitemap not found',
|
|
48
|
+
message: `Failed to fetch sitemap for node: ${node}`
|
|
49
|
+
}),
|
|
50
|
+
{
|
|
51
|
+
status: 503,
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
13
59
|
let sitemapContent = await sitemap.text();
|
|
14
60
|
|
|
15
61
|
sitemapContent = sitemapContent.replace(
|
|
@@ -2,48 +2,176 @@
|
|
|
2
2
|
@use './fonts/index.scss' as fonts;
|
|
3
3
|
@use './fonts/pz-icon.css' as icons;
|
|
4
4
|
|
|
5
|
-
@
|
|
6
|
-
@tailwind components;
|
|
7
|
-
@tailwind utilities;
|
|
8
|
-
|
|
9
|
-
@layer utilities {
|
|
10
|
-
.header-grid-template-areas {
|
|
11
|
-
grid-template-areas:
|
|
12
|
-
'band-l band-m band-r'
|
|
13
|
-
'main-l main-m main-r'
|
|
14
|
-
'nav nav nav';
|
|
15
|
-
}
|
|
5
|
+
@import 'tailwindcss';
|
|
16
6
|
|
|
17
|
-
|
|
18
|
-
grid-area: band-l;
|
|
19
|
-
}
|
|
7
|
+
@plugin '@tailwindcss/typography';
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
grid-area: band-m;
|
|
23
|
-
}
|
|
9
|
+
@config '../../tailwind.config.js';
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
@source '../app/**/*.{js,ts,jsx,tsx}';
|
|
12
|
+
@source '../pages/**/*.{js,ts,jsx,tsx}';
|
|
13
|
+
@source '../components/**/*.{js,ts,jsx,tsx}';
|
|
14
|
+
@source '../views/**/*.{js,ts,jsx,tsx}';
|
|
15
|
+
@source '../widgets/**/*.{js,ts,jsx,tsx}';
|
|
16
|
+
@source '../hooks/**/*.{js,ts,jsx,tsx}';
|
|
17
|
+
@source '../utils/**/*.{js,ts,jsx,tsx}';
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
@theme {
|
|
20
|
+
--font-sans: 'Jost', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
|
|
21
|
+
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
22
|
+
--font-size-2xs: 0.5rem;
|
|
23
|
+
--outline-off: none;
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
--width-1-10: 10%;
|
|
26
|
+
--width-2-10: 20%;
|
|
27
|
+
--width-3-10: 30%;
|
|
28
|
+
--width-4-10: 40%;
|
|
29
|
+
--width-5-10: 50%;
|
|
30
|
+
--width-6-10: 60%;
|
|
31
|
+
--width-7-10: 70%;
|
|
32
|
+
--width-8-10: 80%;
|
|
33
|
+
--width-9-10: 90%;
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
--transition-property-max-width: max-width;
|
|
36
|
+
|
|
37
|
+
--background-image-skeleton-shimmer: linear-gradient(
|
|
38
|
+
90deg,
|
|
39
|
+
#d7d7d7 0%,
|
|
40
|
+
#ebebeb 40%,
|
|
41
|
+
#eeeeee 60%,
|
|
42
|
+
#d7d7d7
|
|
43
|
+
);
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
--animate-skeleton-shimmer: skeleton-shimmer 2s linear infinite;
|
|
46
|
+
|
|
47
|
+
@keyframes skeleton-shimmer {
|
|
48
|
+
to {
|
|
49
|
+
transform: translateX(100%);
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
--color-transparent: transparent;
|
|
54
|
+
--color-white: #ffffff;
|
|
55
|
+
--color-primary: #000000;
|
|
56
|
+
--color-primary-hover: #181818;
|
|
57
|
+
--color-primary-foreground: #ffffff;
|
|
58
|
+
--color-primary-100: #525252;
|
|
59
|
+
--color-primary-200: #404040;
|
|
60
|
+
--color-primary-300: #3d3d3d;
|
|
61
|
+
--color-primary-400: #333333;
|
|
62
|
+
--color-primary-500: #2d2d2d;
|
|
63
|
+
--color-primary-600: #292929;
|
|
64
|
+
--color-primary-700: #2b2b2b;
|
|
65
|
+
--color-primary-800: #181818;
|
|
66
|
+
--color-primary-900: #000000;
|
|
67
|
+
|
|
68
|
+
--color-secondary: #e95151;
|
|
69
|
+
--color-secondary-hover: #d03838;
|
|
70
|
+
--color-secondary-foreground: #ffffff;
|
|
71
|
+
--color-secondary-100: #ffb7b7;
|
|
72
|
+
--color-secondary-200: #ff9e9e;
|
|
73
|
+
--color-secondary-300: #ff8484;
|
|
74
|
+
--color-secondary-400: #ff6b6b;
|
|
75
|
+
--color-secondary-500: #e95151;
|
|
76
|
+
--color-secondary-600: #d72b01;
|
|
77
|
+
--color-secondary-700: #b61e1e;
|
|
78
|
+
--color-secondary-800: #9d0505;
|
|
79
|
+
--color-secondary-900: #830000;
|
|
80
|
+
|
|
81
|
+
--color-black: #000000;
|
|
82
|
+
--color-black-100: #525252;
|
|
83
|
+
--color-black-200: #404040;
|
|
84
|
+
--color-black-300: #3d3d3d;
|
|
85
|
+
--color-black-400: #333333;
|
|
86
|
+
--color-black-500: #2d2d2d;
|
|
87
|
+
--color-black-600: #292929;
|
|
88
|
+
--color-black-700: #2b2b2b;
|
|
89
|
+
--color-black-800: #181818;
|
|
90
|
+
--color-black-900: #000000;
|
|
91
|
+
|
|
92
|
+
--color-gray: #ebebeb;
|
|
93
|
+
--color-gray-25: #fdfdfd;
|
|
94
|
+
--color-gray-50: #f7f7f7;
|
|
95
|
+
--color-gray-100: #f5f5f5;
|
|
96
|
+
--color-gray-150: #f4f4f4;
|
|
97
|
+
--color-gray-200: #eeeeee;
|
|
98
|
+
--color-gray-300: #ebebeb;
|
|
99
|
+
--color-gray-400: #d7d7d7;
|
|
100
|
+
--color-gray-450: #d4d4d4;
|
|
101
|
+
--color-gray-500: #c9c9c9;
|
|
102
|
+
--color-gray-600: #9d9d9d;
|
|
103
|
+
--color-gray-700: #686868;
|
|
104
|
+
--color-gray-800: #615f62;
|
|
105
|
+
--color-gray-850: #58585a;
|
|
106
|
+
--color-gray-900: #4a4f54;
|
|
107
|
+
--color-gray-950: #424242;
|
|
108
|
+
|
|
109
|
+
--color-error: #d72b01;
|
|
110
|
+
--color-error-100: #e20008;
|
|
111
|
+
|
|
112
|
+
--color-success: #7b9d76;
|
|
113
|
+
--color-success-100: #7b9d76;
|
|
114
|
+
|
|
115
|
+
--container-center: true;
|
|
116
|
+
--container-padding: 0rem;
|
|
117
|
+
--container-padding-sm: 2rem;
|
|
118
|
+
--container-padding-2xl: 0rem;
|
|
119
|
+
|
|
120
|
+
--breakpoint-xs: 575px;
|
|
121
|
+
--breakpoint-sm: 640px;
|
|
122
|
+
--breakpoint-md: 768px;
|
|
123
|
+
--breakpoint-lg: 1024px;
|
|
124
|
+
--breakpoint-xl: 1170px;
|
|
125
|
+
--breakpoint-2xl: 1370px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@utility header-grid-template-areas {
|
|
129
|
+
grid-template-areas:
|
|
130
|
+
'band-l band-m band-r'
|
|
131
|
+
'main-l main-m main-r'
|
|
132
|
+
'nav nav nav';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@utility header-grid-area-band-l {
|
|
136
|
+
grid-area: band-l;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@utility header-grid-area-band-m {
|
|
140
|
+
grid-area: band-m;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@utility header-grid-area-band-r {
|
|
144
|
+
grid-area: band-r;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@utility header-grid-area-main-l {
|
|
148
|
+
grid-area: main-l;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@utility header-grid-area-main-m {
|
|
152
|
+
grid-area: main-m;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@utility header-grid-area-main-r {
|
|
156
|
+
grid-area: main-r;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@utility header-grid-area-nav {
|
|
160
|
+
grid-area: nav;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@utility header-m-template-cols {
|
|
164
|
+
grid-template-columns: auto 1fr 1fr;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@utility container {
|
|
168
|
+
margin-inline: auto;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@layer base {
|
|
172
|
+
input::placeholder,
|
|
173
|
+
textarea::placeholder {
|
|
174
|
+
color: theme(--color-gray-400);
|
|
47
175
|
}
|
|
48
176
|
|
|
49
177
|
input {
|
|
@@ -51,7 +179,7 @@
|
|
|
51
179
|
&:not(:placeholder-shown),
|
|
52
180
|
&:autofill {
|
|
53
181
|
& + label {
|
|
54
|
-
|
|
182
|
+
top: calc(1 / 3 * 100%);
|
|
55
183
|
}
|
|
56
184
|
}
|
|
57
185
|
}
|
|
@@ -5,7 +5,7 @@ describe('Badge Component', () => {
|
|
|
5
5
|
let badge;
|
|
6
6
|
|
|
7
7
|
beforeEach(() => {
|
|
8
|
-
render(<Badge className="text-md rounded-
|
|
8
|
+
render(<Badge className="text-md rounded-xs">%90 off</Badge>);
|
|
9
9
|
|
|
10
10
|
badge = screen.getByText('%90 off');
|
|
11
11
|
});
|
|
@@ -15,6 +15,6 @@ describe('Badge Component', () => {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it('should be classname checked', () => {
|
|
18
|
-
expect(badge).toHaveClass('text-md rounded-
|
|
18
|
+
expect(badge).toHaveClass('text-md rounded-xs');
|
|
19
19
|
});
|
|
20
20
|
});
|
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Link } from '@theme/components';
|
|
3
4
|
import { ButtonProps } from '@theme/components/types';
|
|
4
5
|
import clsx from 'clsx';
|
|
5
6
|
import { twMerge } from 'tailwind-merge';
|
|
6
7
|
|
|
7
8
|
export const Button = (props: ButtonProps) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
9
|
+
const {
|
|
10
|
+
appearance = 'filled',
|
|
11
|
+
size = 'md',
|
|
12
|
+
href,
|
|
13
|
+
target,
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
...rest
|
|
17
|
+
} = props;
|
|
18
|
+
|
|
19
|
+
const variants = {
|
|
20
|
+
filled:
|
|
21
|
+
'bg-primary text-primary-foreground border border-primary hover:bg-white hover:border-primary hover:text-primary',
|
|
22
|
+
outlined:
|
|
23
|
+
'bg-transparent text-primary hover:bg-primary hover:text-primary-foreground',
|
|
24
|
+
ghost:
|
|
25
|
+
'bg-transparent border-transparent text-primary hover:bg-primary hover:text-primary-foreground',
|
|
26
|
+
link: 'px-0 h-auto underline underline-offset-2'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const sizes = {
|
|
30
|
+
sm: 'h-8',
|
|
31
|
+
md: 'h-10',
|
|
32
|
+
lg: 'h-12',
|
|
33
|
+
xl: 'h-14'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const buttonClasses = twMerge(
|
|
37
|
+
clsx(
|
|
38
|
+
'px-4 text-xs transition-all duration-200',
|
|
39
|
+
'inline-flex gap-2 justify-center items-center',
|
|
40
|
+
variants[appearance],
|
|
41
|
+
sizes[size],
|
|
42
|
+
className
|
|
43
|
+
),
|
|
44
|
+
className
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return props.href ? (
|
|
48
|
+
<Link
|
|
49
|
+
prefetch={false}
|
|
50
|
+
target={target}
|
|
51
|
+
href={href}
|
|
52
|
+
className={buttonClasses}
|
|
42
53
|
>
|
|
43
|
-
{
|
|
54
|
+
{children}
|
|
55
|
+
</Link>
|
|
56
|
+
) : (
|
|
57
|
+
<button {...rest} className={buttonClasses}>
|
|
58
|
+
{children}
|
|
44
59
|
</button>
|
|
45
60
|
);
|
|
46
61
|
};
|
|
@@ -1,8 +1,50 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
1
2
|
import { forwardRef } from 'react';
|
|
2
3
|
import { FileInputProps } from '@theme/components/types';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
3
6
|
|
|
4
7
|
export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
|
|
5
|
-
function
|
|
6
|
-
|
|
8
|
+
function FileInput({ className, onChange, ...props }, ref) {
|
|
9
|
+
const { t } = useLocalization();
|
|
10
|
+
const [fileNames, setFileNames] = useState<string[]>([]);
|
|
11
|
+
|
|
12
|
+
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
13
|
+
const files = Array.from(event.target.files || []);
|
|
14
|
+
setFileNames(files.map((file) => file.name));
|
|
15
|
+
|
|
16
|
+
if (onChange) {
|
|
17
|
+
onChange(event);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="relative">
|
|
23
|
+
<input
|
|
24
|
+
type="file"
|
|
25
|
+
{...props}
|
|
26
|
+
ref={ref}
|
|
27
|
+
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
28
|
+
onChange={handleFileChange}
|
|
29
|
+
/>
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
className={clsx('bg-primary text-white py-2 px-4 text-sm', className)}
|
|
33
|
+
>
|
|
34
|
+
{t('common.file_input.select_file')}
|
|
35
|
+
</button>
|
|
36
|
+
<div className="mt-1 text-gray-500">
|
|
37
|
+
{fileNames.length > 0 ? (
|
|
38
|
+
<ul className="list-disc pl-4 text-xs">
|
|
39
|
+
{fileNames.map((name, index) => (
|
|
40
|
+
<li key={index}>{name}</li>
|
|
41
|
+
))}
|
|
42
|
+
</ul>
|
|
43
|
+
) : (
|
|
44
|
+
<span className="text-xs">{t('common.file_input.no_file')}</span>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
7
49
|
}
|
|
8
50
|
);
|