gitscope
← marzneshin / v0.1.1 / app/dashboard/src/pages/login/index.tsx
https://dev.kronoz.win/r/marzneshin.git
app/dashboard/src/pages/login/index.tsx · TypeScript
Blame Raw
  1import {
  2  Alert,
  3  AlertDescription,
  4  AlertIcon,
  5  Box,
  6  Button,
  7  chakra,
  8  FormControl,
  9  HStack,
 10  Text,
 11  VStack,
 12} from '@chakra-ui/react';
 13import { ArrowRightStartOnRectangleIcon } from '@heroicons/react/24/outline';
 14import { zodResolver } from '@hookform/resolvers/zod';
 15import { FC, useEffect, useState } from 'react';
 16import { FieldValues, useForm } from 'react-hook-form';
 17import { useLocation, useNavigate } from 'react-router-dom';
 18import { z } from 'zod';
 19import { Footer } from 'components/footer';
 20import { Input } from 'components/input';
 21import { fetch } from 'service/http';
 22import { ReactComponent as Logo } from 'assets/logo.svg';
 23import { useTranslation } from 'react-i18next';
 24import { Language } from 'components/language';
 25import { useAdmin } from 'stores';
 26
 27const schema = z.object({
 28  username: z.string().min(1, 'login.fieldRequired'),
 29  password: z.string().min(1, 'login.fieldRequired'),
 30});
 31
 32export const LogoIcon = chakra(Logo, {
 33  baseStyle: {
 34    strokeWidth: '10px',
 35    w: 12,
 36    h: 12,
 37  },
 38});
 39
 40const LoginIcon = chakra(ArrowRightStartOnRectangleIcon, {
 41  baseStyle: {
 42    w: 5,
 43    h: 5,
 44    strokeWidth: '2px',
 45  },
 46});
 47
 48export const Login: FC = () => {
 49  const [error, setError] = useState('');
 50  const [loading, setLoading] = useState(false);
 51  const navigate = useNavigate();
 52  const { setAuthToken, removeAuthToken } = useAdmin();
 53  const { t } = useTranslation();
 54  let location = useLocation();
 55  const {
 56    register,
 57    formState: { errors },
 58    handleSubmit,
 59  } = useForm({
 60    resolver: zodResolver(schema),
 61  });
 62  useEffect(() => {
 63    removeAuthToken();
 64    if (location.pathname !== '/login') {
 65      navigate('/login', { replace: true });
 66    }
 67  }, []);
 68  const login = (values: FieldValues) => {
 69    setError('');
 70    const formData = new FormData();
 71    formData.append('username', values.username);
 72    formData.append('password', values.password);
 73    formData.append('grant_type', 'password');
 74    setLoading(true);
 75    fetch('/admins/token', { method: 'post', body: formData })
 76      .then(({ access_token: token }) => {
 77        setAuthToken(token);
 78        navigate('/');
 79      })
 80      .catch((err) => {
 81        setError(err.response._data.detail);
 82      })
 83      .finally(setLoading.bind(null, false));
 84  };
 85  return (
 86    <VStack justifyContent="space-between" minH="100vh" p="6" w="full">
 87      <Box w="full">
 88        <HStack justifyContent="end" w="full">
 89          <Language />
 90        </HStack>
 91        <HStack w="full" justifyContent="center" alignItems="center">
 92          <Box w="full" maxW="340px" mt="6">
 93            <VStack alignItems="center" w="full">
 94              <LogoIcon />
 95              <Text fontSize="2xl" fontWeight="semibold">
 96                {t('login.loginYourAccount')}
 97              </Text>
 98              <Text color="gray.600" _dark={{ color: 'gray.400' }}>
 99                {t('login.welcomeBack')}
100              </Text>
101            </VStack>
102            <Box w="full" maxW="300px" m="auto" pt="4">
103              <form onSubmit={handleSubmit(login)}>
104                <VStack mt={4} rowGap={2}>
105                  <FormControl>
106                    <Input
107                      w="full"
108                      placeholder={t('username')}
109                      {...register('username')}
110                      error={t(errors?.username?.message as string)}
111                    />
112                  </FormControl>
113                  <FormControl>
114                    <Input
115                      w="full"
116                      type="password"
117                      placeholder={t('password')}
118                      {...register('password')}
119                      error={t(errors?.password?.message as string)}
120                    />
121                  </FormControl>
122                  {error && (
123                    <Alert status="error" rounded="md">
124                      <AlertIcon />
125                      <AlertDescription>{error}</AlertDescription>
126                    </Alert>
127                  )}
128                  <Button
129                    isLoading={loading}
130                    type="submit"
131                    w="full"
132                    colorScheme="primary"
133                  >
134                    {<LoginIcon marginRight={1} />}
135                    {t('login.login')}
136                  </Button>
137                </VStack>
138              </form>
139            </Box>
140          </Box>
141        </HStack>
142      </Box>
143      <Footer bottom={3} />
144    </VStack>
145  );
146};
147
148export default Login;