Skip to content

Commit

Permalink
refactor: update wishlist book and wishlist drawer with new account s…
Browse files Browse the repository at this point in the history
…tructure

feat(core): add delete wishlist mutation (#975)

feat(core): add create wishlist mutation (#958)

feat(core): update customer account modal component (#1001)

feat(core): add wishlist items mutation (#1268)

feat(core): delete wishlist items mutation (#1269)

feat(core): update delete wishlists mutation (#1273)

feat(core): update create wishlist mutation (#1272)

feat(core): add wishlist book (#998)

Add wishlist test (#1314)
Update Wishlist test

feat(core): add wishlist drawer (#1182)
  • Loading branch information
yurytut1993 committed Sep 17, 2024
1 parent 3176491 commit c28c716
Show file tree
Hide file tree
Showing 41 changed files with 1,640 additions and 48 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-turtles-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add delete wishlist mutation
5 changes: 5 additions & 0 deletions .changeset/chilly-forks-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

update delete wishlists mutation
5 changes: 5 additions & 0 deletions .changeset/heavy-otters-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add create wishlist mutation
5 changes: 5 additions & 0 deletions .changeset/lovely-days-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

delete wishlist items mutation
5 changes: 5 additions & 0 deletions .changeset/odd-berries-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

update customer account modal component
5 changes: 5 additions & 0 deletions .changeset/odd-oranges-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add wishlist book
5 changes: 5 additions & 0 deletions .changeset/rich-camels-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add wishlist drawer
5 changes: 5 additions & 0 deletions .changeset/rich-starfishes-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add wishlist items mutation
5 changes: 5 additions & 0 deletions .changeset/strange-cobras-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

Add Wishlist test and update wishlist naming
5 changes: 5 additions & 0 deletions .changeset/twelve-spiders-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

update create wishlist mutation because only minor data set is needed
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use server';

import { isRedirectError } from 'next/dist/client/components/redirect';
import { redirect } from 'next/navigation';

import { Credentials, signIn } from '~/auth';

Expand All @@ -16,10 +15,8 @@ export const login = async (formData: FormData) => {
...credentials,
// We want to use next/navigation for the redirect as it
// follows basePath and trailing slash configurations.
redirect: false,
redirectTo: '/account?register=true',
});

redirect('/account');
} catch (error: unknown) {
if (isRedirectError(error)) {
throw error;
Expand Down
45 changes: 29 additions & 16 deletions core/app/[locale]/(default)/account/(tabs)/_components/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,40 @@

import * as DialogPrimitive from '@radix-ui/react-alert-dialog';
import { X } from 'lucide-react';
import { MouseEventHandler, PropsWithChildren, useId } from 'react';
import { Dispatch, MouseEventHandler, PropsWithChildren, SetStateAction, useId } from 'react';

import { Button } from '~/components/ui/button';

interface Props extends PropsWithChildren {
actionHandler?: MouseEventHandler<HTMLButtonElement>;
title: string;
trigger: React.ReactNode;
descriptionText?: string;
confirmationText?: string;
abortText?: string;
open?: boolean;
setOpen?: Dispatch<SetStateAction<boolean>>;
showCancelButton?: boolean;
}

export const Modal = ({
abortText = 'Cancel',
actionHandler,
confirmationText = 'OK',
descriptionText,
open,
setOpen,
showCancelButton = true,
title,
trigger,
children,
}: Props) => {
const id = useId();

return (
<DialogPrimitive.Root>
<DialogPrimitive.Root onOpenChange={setOpen} open={open}>
<DialogPrimitive.Trigger aria-controls={id} asChild>
{children}
{trigger}
</DialogPrimitive.Trigger>

<DialogPrimitive.Portal>
Expand All @@ -36,12 +44,12 @@ export const Modal = ({
className="data-[state=open]:animate-contentShow fixed left-[50%] top-[50%] w-full translate-x-[-50%] translate-y-[-50%] bg-white shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none sm:w-8/12 md:w-3/4 md:w-6/12 lg:w-3/5 lg:w-5/12"
id={id}
>
<div className="flex justify-between gap-4 p-6">
<DialogPrimitive.Title className="w-5/6 grow font-semibold">
<div className="flex items-start justify-between gap-4 p-6">
<DialogPrimitive.Title className="w-5/6 grow text-xl font-semibold lg:text-2xl">
{title}
</DialogPrimitive.Title>
<DialogPrimitive.Cancel asChild>
<Button className="ms-auto w-min p-2" type="button" variant="subtle">
<Button className="ms-auto w-min p-0" type="button" variant="subtle">
<X>
<title>{abortText}</title>
</X>
Expand All @@ -52,16 +60,21 @@ export const Modal = ({
<DialogPrimitive.Description>{descriptionText}</DialogPrimitive.Description>
)}
<div className="flex flex-col gap-2 p-6 lg:flex-row">
<DialogPrimitive.Action asChild>
<Button className="w-full lg:w-fit" onClick={actionHandler} variant="primary">
{confirmationText}
</Button>
</DialogPrimitive.Action>
<DialogPrimitive.Cancel asChild>
<Button className="w-full lg:w-fit" variant="subtle">
{abortText}
</Button>
</DialogPrimitive.Cancel>
{children}
{actionHandler && (
<DialogPrimitive.Action asChild>
<Button className="w-full lg:w-fit" onClick={actionHandler} variant="primary">
{confirmationText}
</Button>
</DialogPrimitive.Action>
)}
{showCancelButton && (
<DialogPrimitive.Cancel asChild>
<Button className="w-full lg:w-fit" variant="subtle">
{abortText}
</Button>
</DialogPrimitive.Cancel>
)}
</div>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ const AddressChangeButtons = ({ addressId, isAddressRemovable, onDelete }: Addre
actionHandler={handleDeleteAddress}
confirmationText={t('confirmDeleteAddress')}
title={t('deleteModalTitle')}
>
<Button aria-label={t('deleteButton')} disabled={!isAddressRemovable} variant="subtle">
{t('deleteButton')}
</Button>
</Modal>
trigger={
<Button aria-label={t('deleteButton')} disabled={!isAddressRemovable} variant="subtle">
{t('deleteButton')}
</Button>
}
/>
</div>
);
};
Expand Down
3 changes: 2 additions & 1 deletion core/app/[locale]/(default)/account/(tabs)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Link } from '~/components/link';
import { LocaleType } from '~/i18n/routing';
import { cn } from '~/lib/utils';

const tabList = ['addresses', 'settings'] as const;
const tabList = ['addresses', 'wishlists', 'settings'] as const;

export type TabType = (typeof tabList)[number];

Expand All @@ -21,6 +21,7 @@ export default function AccountTabLayout({ children, params: { locale } }: Props

const tabsTitles = {
addresses: t('addresses'),
wishlists: t('wishlists'),
settings: t('settings'),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use server';

import { revalidatePath } from 'next/cache';
import { z } from 'zod';

import { createWishlist as createWishlistMutation } from '~/client/mutations/create-wishlist';

const CreateWishlistSchema = z.object({
name: z.string(),
});

export const createWishlist = async (formData: FormData) => {
const parsedData = CreateWishlistSchema.parse({
name: formData.get('name'),
});

const input = {
...parsedData,
isPublic: true,
};

try {
const newWishlist = await createWishlistMutation({ input });

revalidatePath('/account/wishlists', 'page');

if (newWishlist) {
return {
status: 'success' as const,
data: newWishlist,
};
}
} catch (error: unknown) {
if (error instanceof Error) {
return {
status: 'error' as const,
message: error.message,
};
}
}

return { status: 'error' as const, message: 'Unknown error.' };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as DialogPrimitive from '@radix-ui/react-alert-dialog';
import { useTranslations } from 'next-intl';
import { ChangeEvent, useState } from 'react';
import { useFormStatus } from 'react-dom';

import { createWishlist as createWishlistMutation } from '~/client/mutations/create-wishlist';
import { Button } from '~/components/ui/button';
import {
Field,
FieldControl,
FieldLabel,
FieldMessage,
Form,
FormSubmit,
Input,
} from '~/components/ui/form';

import { useAccountStatusContext } from '../../../_components/account-status-provider';

import { createWishlist } from './_actions/create-wishlist';

type Wishlist = NonNullable<Awaited<ReturnType<typeof createWishlistMutation>>>;

interface CreateWishlistFormProps {
onWishlistCreated: (newWishlist: Wishlist) => void;
}

const SubmitButton = () => {
const { pending } = useFormStatus();
const t = useTranslations('Account.Wishlist');

return (
<Button
className="relative w-full items-center px-8 py-2 lg:w-fit"
loading={pending}
loadingText={t('onSubmitText')}
variant="primary"
>
{t('create')}
</Button>
);
};

export const CreateWishlistForm = ({ onWishlistCreated }: CreateWishlistFormProps) => {
const [isInputValid, setInputValidation] = useState(true);
const { setAccountState } = useAccountStatusContext();

const t = useTranslations('Account.Wishlist');

const handleInputValidation = (e: ChangeEvent<HTMLInputElement>) => {
const validationStatus = e.target.validity.valueMissing;

setInputValidation(!validationStatus);
};

const onSubmit = async (formData: FormData) => {
const submit = await createWishlist(formData);

if (submit.status === 'success') {
onWishlistCreated(submit.data);
setAccountState({
status: submit.status,
message: t('messages.created', { name: submit.data.name }),
});
}

if (submit.status === 'error') {
setAccountState({ status: submit.status, message: submit.message });
}
};

return (
<Form action={onSubmit} className="w-full" onSubmit={(e) => e.stopPropagation()}>
<Field className="relative space-y-2 pb-7" name="name">
<FieldLabel htmlFor="wishlist-name">{t('inputLabel')}</FieldLabel>
<FieldControl asChild>
<Input
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
error={!isInputValid}
id="wishlist-name"
onChange={handleInputValidation}
onInvalid={handleInputValidation}
required
type="text"
/>
</FieldControl>
<FieldMessage
className="absolute inset-x-0 bottom-0 inline-flex w-full text-xs font-normal text-error"
match="valueMissing"
>
{t('emptyName')}
</FieldMessage>
</Field>
<div className="mt-3 flex flex-col lg:flex-row">
<FormSubmit asChild>
<SubmitButton />
</FormSubmit>
<DialogPrimitive.Cancel asChild>
<Button className="mt-2 w-full border-0 lg:ms-2 lg:mt-0 lg:w-fit" variant="secondary">
{t('cancel')}
</Button>
</DialogPrimitive.Cancel>
</div>
</Form>
);
};
Loading

0 comments on commit c28c716

Please sign in to comment.