|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- "use client";
-
- import { FormHelperText, IconButton, InputAdornment } from "@mui/material";
- import Button from "@mui/material/Button";
- import Stack from "@mui/material/Stack";
- import TextField from "@mui/material/TextField";
- import Typography from "@mui/material/Typography";
- import { TFunction } from "i18next";
- import { Session } from "next-auth";
- import { getSession, signIn } from "next-auth/react";
- import { useRouter } from "next/navigation";
- import { useEffect, useState } from "react";
- import { SubmitHandler, useForm } from "react-hook-form";
- import { useTranslation } from "react-i18next";
- import { SetupAxiosInterceptors } from "@/app/(main)/axios/axiosInstance";
- import { persistBackendTokens } from "@/app/utils/authToken";
- import { Visibility, VisibilityOff } from "@mui/icons-material";
-
- type LoginFields = {
- username: string;
- password: string;
- };
-
- type SessionWithAbilities =
- | ({
- abilities: string[];
- accessToken?: string;
- refreshToken?: string;
- } & Session)
- | null;
-
- // Error codes in https://next-auth.js.org/configuration/pages#sign-in-page
- const getHumanFriendlyErrorMessage = (
- t: TFunction,
- serverError: string,
- ): string => {
- switch (serverError) {
- case "CredentialsSignin":
- return t("Invalid username or password.");
- case "Default":
- default:
- return t("Something went wrong. Please try again later.");
- }
- };
-
- const LoginForm: React.FC = () => {
- const { t } = useTranslation("login");
- const {
- register,
- handleSubmit,
- formState: { errors, isSubmitting },
- } = useForm<LoginFields>();
-
- const [serverError, setServerError] = useState<string>();
-
- const router = useRouter();
-
- const [showPassword, setShowPassword] = useState(false);
- const handleClickShowPassword = () => setShowPassword(!showPassword);
- const handleMouseDownPassword = () => setShowPassword(!showPassword);
-
- const onSubmit: SubmitHandler<LoginFields> = async (data) => {
- console.log(data);
- const res = await signIn("credentials", {
- redirect: false,
- ...data,
- });
-
- if (res?.error) {
- setServerError(res.error);
- return;
- }
-
- // Persist access + refresh in localStorage (Login is outside AxiosProvider)
- const session = (await getSession()) as SessionWithAbilities;
- const access = session?.accessToken ?? "";
- if (access) {
- persistBackendTokens(access, session?.refreshToken);
- SetupAxiosInterceptors(access);
- }
- window.localStorage.setItem(
- "abilities",
- JSON.stringify(session?.abilities),
- );
-
- const callbackUrl =
- new URLSearchParams(window.location.search).get("callbackUrl") || "/";
-
- router.push(callbackUrl);
- };
-
- useEffect(() => {
- // clean abilities before login
- window.localStorage.removeItem("abilities");
- }, []);
-
- return (
- <Stack
- spacing={3}
- margin={5}
- component="form"
- onSubmit={handleSubmit(onSubmit)}
- >
- <Typography variant="h1">{t("Sign In")}</Typography>
- <TextField
- label={t("Username")}
- {...register("username", { required: t("Please enter a username") })}
- error={Boolean(errors.username)}
- helperText={errors.username?.message}
- />
- <TextField
- label={t("Password")}
- type={showPassword ? "text" : "password"}
- {...register("password", { required: t("Please enter a password") })}
- error={Boolean(errors.password)}
- helperText={errors.password?.message}
- InputProps={{
- endAdornment: (
- <InputAdornment position="end">
- <IconButton
- aria-label={t("toggle password visibility")}
- onClick={handleClickShowPassword}
- onMouseDown={handleMouseDownPassword}
- >
- {showPassword ? <Visibility /> : <VisibilityOff />}
- </IconButton>
- </InputAdornment>
- ),
- }}
- />
- {serverError && (
- <FormHelperText error>
- {getHumanFriendlyErrorMessage(t, serverError)}
- </FormHelperText>
- )}
- <Button
- variant="contained"
- size="large"
- type="submit"
- disabled={isSubmitting}
- >
- {t("Login")}
- </Button>
- </Stack>
- );
- };
-
- export default LoginForm;
|