Files
myeasycms-v2/packages/features/auth/src/components/update-password-form.tsx
Giancarlo Buomprisco 7ebff31475 Next.js Supabase V3 (#463)
Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
2026-03-24 13:40:38 +08:00

149 lines
3.8 KiB
TypeScript

'use client';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { TriangleAlert } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { useUpdateUser } from '@kit/supabase/hooks/use-update-user-mutation';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
import { Button } from '@kit/ui/button';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormMessage,
} from '@kit/ui/form';
import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import { PasswordResetSchema } from '../schemas/password-reset.schema';
import { PasswordInput } from './password-input';
export function UpdatePasswordForm(params: {
redirectTo: string;
heading?: React.ReactNode;
}) {
const updateUser = useUpdateUser();
const router = useRouter();
const t = useTranslations();
const form = useForm({
resolver: zodResolver(PasswordResetSchema),
defaultValues: {
password: '',
repeatPassword: '',
},
});
if (updateUser.error) {
const error = updateUser.error as unknown as { code: string };
return <ErrorState error={error} onRetry={() => updateUser.reset()} />;
}
return (
<div className={'flex w-full flex-col space-y-6'}>
<div className={'flex justify-center'}>
{params.heading && (
<Heading className={'text-center'} level={4}>
{params.heading}
</Heading>
)}
</div>
<Form {...form}>
<form
className={'flex w-full flex-1 flex-col'}
onSubmit={form.handleSubmit(async ({ password }) => {
await updateUser.mutateAsync({
password,
redirectTo: params.redirectTo,
});
router.replace(params.redirectTo);
toast.success(t('account.updatePasswordSuccessMessage'));
})}
>
<div className={'flex-col space-y-2.5'}>
<FormField
name={'password'}
render={({ field }) => (
<FormItem>
<FormControl>
<PasswordInput autoComplete={'new-password'} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
name={'repeatPassword'}
render={({ field }) => (
<FormItem>
<FormControl>
<PasswordInput autoComplete={'new-password'} {...field} />
</FormControl>
<FormDescription>
<Trans i18nKey={'common.repeatPassword'} />
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button
disabled={updateUser.isPending}
type="submit"
className={'w-full'}
>
<Trans i18nKey={'auth.passwordResetLabel'} />
</Button>
</div>
</form>
</Form>
</div>
);
}
function ErrorState(props: {
onRetry: () => void;
error: {
code: string;
};
}) {
const t = useTranslations('auth');
const errorMessage = t(`errors.${props.error.code}`, {
defaultValue: t('errors.resetPasswordError'),
});
return (
<div className={'flex flex-col space-y-4'}>
<Alert variant={'destructive'}>
<TriangleAlert className={'s-6'} />
<AlertTitle>
<Trans i18nKey={'common.genericError'} />
</AlertTitle>
<AlertDescription>{errorMessage}</AlertDescription>
</Alert>
<Button onClick={props.onRetry} variant={'outline'}>
<Trans i18nKey={'common.retry'} />
</Button>
</div>
);
}