Login con idiomas

This commit is contained in:
David Arranz 2024-09-17 17:00:33 +02:00
parent 7293385550
commit af12f841b1
3 changed files with 206 additions and 0 deletions

View File

@ -50,6 +50,7 @@
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.2.0",
"flag-icons": "^7.2.3",
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"joi": "^17.13.1",

View File

@ -0,0 +1,200 @@
import { Container, FormTextField } from "@/components";
import { FormSubmitButton } from "@/components/Forms/FormSubmitButton";
import { UeckoLogo } from "@/components/UeckoLogo/UeckoLogo";
import { useLogin } from "@/lib/hooks";
import {
Alert,
AlertDescription,
AlertTitle,
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Form,
} from "@/ui";
import { joiResolver } from "@hookform/resolvers/joi";
import { ILogin_DTO } from "@shared/contexts";
import Joi from "joi";
import { AlertCircleIcon, ChevronDownIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import SpanishJoiMessages from "../../spanish-joi-messages.json";
import "/node_modules/flag-icons/css/flag-icons.min.css";
type LoginDataForm = ILogin_DTO;
const languages = [
{ code: "en", name: "English", flag: () => <span className='fi fi-gb'></span> },
{ code: "es", name: "Español", flag: () => <span className='fi fi-es'></span> },
{ code: "fr", name: "Français", flag: () => <span className='fi fi-fr'></span> },
{ code: "it", name: "Italiano", flag: () => <span className='fi fi-it'></span> },
];
export const LoginPageWithLanguageSelector = () => {
const { t, i18n } = useTranslation();
const [language, setLanguage] = useState(i18n.language);
useEffect(() => {
const browserLang = navigator.language.split("-")[0];
const supportedLang = languages.find((lang) => lang.code === browserLang);
if (supportedLang) {
changeLanguage(supportedLang.code);
}
}, []);
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
setLanguage(lng);
};
const { mutate: login } = useLogin({
onSuccess: (data) => {
const { success, error } = data;
if (!success && error) {
form.setError("root", error);
}
},
onError: (error) => {
console.debug(error);
},
});
const form = useForm<LoginDataForm>({
mode: "onBlur",
defaultValues: {
email: "",
password: "",
},
resolver: joiResolver(
Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } })
.required(),
password: Joi.string().min(4).alphanum().required(),
}),
{
messages: SpanishJoiMessages,
}
),
});
const onSubmit: SubmitHandler<LoginDataForm> = async (data) => {
login({ email: data.email, password: data.password }, {});
};
return (
<Container
variant={"full"}
className='p-0 lg:grid lg:min-h-[600px] lg:grid-cols-2 xl:min-h-[800px] h-screen '
>
<div className='flex items-center justify-center py-12 '>
<div className='mx-auto grid w-[650px] gap-6 relative '>
<div className='absolute flex space-x-2 top-4 right-4 '>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='outline' className='w-[140px] justify-start'>
<span className='mr-2'>
{languages.find((lang) => lang.code === language)?.flag()}
</span>
{languages.find((lang) => lang.code === language)?.name}
<ChevronDownIcon className='w-4 h-4 ml-auto opacity-50' />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className='w-[140px]'>
{languages.map((lang) => (
<DropdownMenuItem key={lang.code} onSelect={() => changeLanguage(lang.code)}>
<span className='mr-2'>{lang.flag()}</span>
{lang.name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
<Card className='px-12 py-6 bg-muted/50'>
<CardHeader>
<UeckoLogo className='inline-block m-auto mb-6 align-middle max-w-32' />
<CardTitle>
<Trans i18nKey='login_page.title' />
</CardTitle>
<CardDescription>
<Trans i18nKey='login_page.description' />
</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='grid items-start gap-6'>
<div className='grid gap-6'>
<FormTextField
required
name='email'
label={t("login_page.email_label")}
type='email'
placeholder={t("login_page.email_placeholder")}
/>
</div>
<div className='grid gap-6'>
<FormTextField
required
name='password'
label={t("login_page.password_label")}
type='password'
/>
<div className='mb-4 -mt-2 text-sm'>
<Trans i18nKey='login_page.forgotten_password' />
<br />
<Link to='https://uecko.com/distribuidores' className='underline'>
<Trans i18nKey='login_page.contact_us' />
</Link>
</div>
</div>
{form.formState.errors.root?.message && (
<Alert variant='destructive'>
<AlertCircleIcon className='w-4 h-4' />
<AlertTitle>
<Trans i18nKey='common.error' />
</AlertTitle>
<AlertDescription>{form.formState.errors.root?.message}</AlertDescription>
</Alert>
)}
<FormSubmitButton className='w-full' label={t("login_page.login")} />
<div className='mt-4 text-sm text-center'>
<Trans i18nKey='login_page.become_dealer' />
<br />
<Link to='https://uecko.com/distribuidores' className='underline'>
<Trans i18nKey='login_page.contact_us' />
</Link>
</div>
</div>
</form>
</Form>
</CardContent>
</Card>
<img className='block w-11/12 mx-auto aspect-auto' src='/img/uecko-footer_logos.jpg' />
</div>
</div>
<div className='hidden bg-muted lg:block'>
<img
src='/img/nara2.jpg'
alt='Image'
width='1920'
height='1080'
className='h-full w-full object-cover dark:brightness-[0.2] dark:grayscale'
/>
</div>
</Container>
);
};

View File

@ -3308,6 +3308,11 @@ find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
flag-icons@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/flag-icons/-/flag-icons-7.2.3.tgz#b67f379fa0ef28c4e605319a78035131bdd8ced7"
integrity sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg==
flat-cache@^3.0.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"