brief-07-front/src/components/auth-form.tsx
Mathis 4e0de3be06
feat(apiRequest): use JSON.parse for accessToken in Authorization header
The Authorization header in the apiRequest now utilizes JSON.parse for the accessToken. This change ensures proper parsing and retrieval of the item from the local storage when the window object is defined.
2024-06-20 14:56:35 +02:00

248 lines
6.6 KiB
TypeScript

"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import AutoForm, { AutoFormSubmit } from "@/components/auto-form";
import { UserDataContext } from "@/components/providers/userdata-provider";
import { ToastBox, toastType } from "@/components/ui/toast-box";
import { useToast } from "@/components/ui/use-toast";
import type {
IApiLoginReq,
IApiLoginRes,
IApiRegisterReq,
IApiRegisterRes,
} from "@/interfaces/api.interface";
import { EReturnState, type IStandardisedReturn } from "@/interfaces/general.interface";
import type { IUserData } from "@/interfaces/userdata.interface";
import ApiRequest from "@/services/apiRequest";
import { useLocalStorage } from "@/services/localStorage";
import { Bug, RefreshCw } from "lucide-react";
import Link from "next/link";
import { type Dispatch, type SetStateAction, useContext, useState } from "react";
import * as z from "zod";
const loginSchema = z.object({
email: z
.string({
required_error: "Email is needed.",
})
.email({
message: "Should be a valid email.",
})
.describe("Your account email."),
password: z
.string({
required_error: "Password is needed.",
})
.describe("Your account password."),
});
const registerSchema = z.object({
firstName: z.string({
required_error: "",
}),
lastName: z.string(),
age: z.number().min(18).max(120),
pseudo: z.string({
required_error: "",
}),
city: z.string({
required_error: "",
}),
email: z
.string({
required_error: "Email is needed.",
})
.email("Should be a valid email."),
password: z
.string({
required_error: "Password is needed.",
})
.describe("Your account password."),
});
export function AuthForms() {
const [isLoading, setIsLoading] = useState(false);
const [sub, setSub] = useLocalStorage<string | undefined>("sub", "");
const userContext = useContext(UserDataContext);
const { toast } = useToast();
async function doRegister(
registerData: IApiRegisterReq,
userDataSetter: Dispatch<SetStateAction<IUserData | null>>,
): Promise<IStandardisedReturn<IApiRegisterRes>> {
console.trace(registerData);
try {
const ReqRes = await ApiRequest.standard.post.json<
IApiRegisterReq,
IApiRegisterRes
>("auth/signup", registerData);
console.trace(ReqRes.data);
if (ReqRes.data.user) {
userDataSetter({
...ReqRes.data.user,
wallet: {
uat: Date.now(),
update_interval: 30_000,
owned_cryptos: [],
},
});
setSub(ReqRes.data.access_token);
}
console.debug(ReqRes.data.message || "Not additional message from request");
return {
state: EReturnState.done,
resolved: ReqRes.data,
};
} catch (error) {
console.error("Error during registration:", error);
return {
state: EReturnState.serverError,
message: error as string,
};
}
}
async function doLogin(
loginData: IApiLoginReq,
): Promise<IStandardisedReturn<IApiLoginRes>> {
try {
const ReqRes = await ApiRequest.standard.post.json<IApiLoginReq, IApiLoginRes>(
"auth/signin",
loginData,
);
console.trace(ReqRes.data);
if (ReqRes.data.access_token) {
setSub(ReqRes.data.access_token);
}
return {
state: EReturnState.done,
};
} catch (err) {
console.error("Error during login:", err);
return {
state: EReturnState.serverError,
message: err as string,
};
}
}
if (!userContext || !userContext.setUserData) {
return (
<div
className={
"bg-destructive text-destructive-foreground p-3 gap-2 border rounded flex flex-row justify-center items-center"
}
>
<Bug />
<p>It seems that the context is missing..</p>
</div>
);
}
return (
<Tabs defaultValue="login" className="w-full p-2 md:w-[400px] my-4">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="login">Login</TabsTrigger>
<TabsTrigger value="register">Register</TabsTrigger>
</TabsList>
<TabsContent value="login">
<AutoForm
// Pass the schema to the form
formSchema={loginSchema}
onSubmit={(data: IApiLoginReq) => {
setIsLoading(true);
doLogin(data).then((res) => {
if (res.state !== EReturnState.done) {
toast({
description: res.message || "An unexpected error occurred..",
variant: "destructive",
});
setIsLoading(false);
return;
}
//toast.custom(<ToastBox message={"Login successful ! \n You will be redirected."} type={toastType.success}/>)
toast({
description: "Login successful ! \n You will be redirected.",
});
setTimeout(() => {
setIsLoading(false);
location.href = "/";
console.log("Moving to home.");
}, 3_000);
});
}}
fieldConfig={{
password: {
inputProps: {
type: "password",
placeholder: "••••••••",
},
},
}}
>
<AutoFormSubmit
disabled={!!isLoading}
className={"gap-2 disabled:bg-secondary"}
>
{/* biome-ignore lint/style/useTemplate: <explanation> */}
<RefreshCw className={"animate-spin" + isLoading && "hidden"} />
<p>Login</p>
</AutoFormSubmit>
</AutoForm>
</TabsContent>
<TabsContent value="register">
<AutoForm
// Pass the schema to the form
formSchema={registerSchema}
onSubmit={(data: IApiRegisterReq) => {
setIsLoading(true);
doRegister(
data,
userContext.setUserData as Dispatch<SetStateAction<IUserData | null>>,
).then((res) => {
if (res.state !== EReturnState.done) {
//toast.custom(<ToastBox message={res.message || "An unexpected error occurred.."} type={toastType.error}/>)
setIsLoading(false);
return;
}
//toast.custom(<ToastBox message={"Register successful ! \n You will be redirected."} type={toastType.success}/>)
setTimeout(() => {
setIsLoading(false);
//location.href = "/"
console.log("Moving to home.");
}, 5_000);
});
}}
fieldConfig={{
password: {
inputProps: {
type: "password",
placeholder: "••••••••",
},
},
}}
>
<AutoFormSubmit
disabled={!!isLoading}
className={"gap-2 disabled:bg-secondary"}
>
{/* biome-ignore lint/style/useTemplate: <explanation> */}
<RefreshCw className={"animate-spin" + !isLoading && "hidden"} />
<p>Register</p>
</AutoFormSubmit>
<p className="text-gray-500 text-sm">
By submitting this form, you agree to our{" "}
<Link href="#" className="text-primary underline">
terms and conditions
</Link>
.
</p>
</AutoForm>
</TabsContent>
</Tabs>
);
}