mirror of
https://github.com/ershisan99/todolist_next.git
synced 2025-12-17 12:34:03 +00:00
add sign-up page
This commit is contained in:
@@ -10,7 +10,8 @@ import { useMeQuery } from "@/services";
|
|||||||
export const AuthRedirect: FC<{ children: ReactNode }> = ({ children }) => {
|
export const AuthRedirect: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: user, isLoading } = useMeQuery();
|
const { data: user, isLoading } = useMeQuery();
|
||||||
const isAuthPage = router.pathname === "/login";
|
const isAuthPage =
|
||||||
|
router.pathname === "/login" || router.pathname === "/sign-up";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading && !user && !isAuthPage) {
|
if (!isLoading && !user && !isAuthPage) {
|
||||||
|
|||||||
49
src/pages/sign-up.tsx
Normal file
49
src/pages/sign-up.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { FormEvent } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import type { NextPage } from "next";
|
||||||
|
|
||||||
|
import { Button, Input } from "@/components";
|
||||||
|
import { useSignUpMutation } from "@/services";
|
||||||
|
|
||||||
|
const Login: NextPage = () => {
|
||||||
|
const { mutate: signUp } = useSignUpMutation();
|
||||||
|
|
||||||
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
|
||||||
|
const values = Object.fromEntries(formData) as any;
|
||||||
|
signUp(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"flex h-screen items-center justify-center"}>
|
||||||
|
<form
|
||||||
|
className={"flex w-96 flex-col gap-3 rounded-md border p-6"}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<h1 className={"text-2xl font-bold"}>Sign up</h1>
|
||||||
|
<label className={"flex flex-col gap-1"}>
|
||||||
|
Username (optional)
|
||||||
|
<Input name={"username"} type="text" />
|
||||||
|
</label>
|
||||||
|
<label className={"flex flex-col gap-1"}>
|
||||||
|
Email
|
||||||
|
<Input name={"email"} type="email" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className={"flex flex-col gap-1"}>
|
||||||
|
Password
|
||||||
|
<Input type="password" name={"password"} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Button className={"w-full"} type={"submit"}>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
||||||
@@ -3,6 +3,7 @@ import type {
|
|||||||
LogoutResponse,
|
LogoutResponse,
|
||||||
MeResponse,
|
MeResponse,
|
||||||
PostLoginArgs,
|
PostLoginArgs,
|
||||||
|
PostSignUpArgs,
|
||||||
} from "../todolists";
|
} from "../todolists";
|
||||||
|
|
||||||
import { handleError } from "@/helpers";
|
import { handleError } from "@/helpers";
|
||||||
@@ -12,7 +13,7 @@ export const AuthApi = {
|
|||||||
async login(args: PostLoginArgs) {
|
async login(args: PostLoginArgs) {
|
||||||
const res = await todolistApiInstance.post<LoginResponse>(
|
const res = await todolistApiInstance.post<LoginResponse>(
|
||||||
"/auth/login",
|
"/auth/login",
|
||||||
args
|
args,
|
||||||
);
|
);
|
||||||
|
|
||||||
localStorage.setItem("accessToken", res.data.accessToken);
|
localStorage.setItem("accessToken", res.data.accessToken);
|
||||||
@@ -20,7 +21,11 @@ export const AuthApi = {
|
|||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
async signUp(args: PostSignUpArgs) {
|
||||||
|
const res = await todolistApiInstance.post<any>("/auth/sign-up", args);
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
const res = await todolistApiInstance.delete<LogoutResponse>("/auth/login");
|
const res = await todolistApiInstance.delete<LogoutResponse>("/auth/login");
|
||||||
|
|
||||||
|
|||||||
@@ -38,3 +38,9 @@ export const useLogoutMutation = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSignUpMutation = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: AuthApi.signUp,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const mutex = new Mutex();
|
|||||||
let refreshedAt: number | null = null;
|
let refreshedAt: number | null = null;
|
||||||
|
|
||||||
export const todolistApiInstance = axios.create({
|
export const todolistApiInstance = axios.create({
|
||||||
baseURL: "http://localhost:3000"
|
baseURL: "http://localhost:3000",
|
||||||
});
|
});
|
||||||
|
|
||||||
async function refreshAccessToken(): Promise<string> {
|
async function refreshAccessToken(): Promise<string> {
|
||||||
@@ -19,9 +19,9 @@ async function refreshAccessToken(): Promise<string> {
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${localStorage.getItem("refreshToken")}`
|
Authorization: `Bearer ${localStorage.getItem("refreshToken")}`,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
localStorage.setItem("accessToken", res.data.accessToken);
|
localStorage.setItem("accessToken", res.data.accessToken);
|
||||||
@@ -42,14 +42,15 @@ todolistApiInstance.interceptors.request.use(
|
|||||||
|
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error) => Promise.reject(error)
|
(error) => Promise.reject(error),
|
||||||
);
|
);
|
||||||
|
|
||||||
todolistApiInstance.interceptors.response.use(
|
todolistApiInstance.interceptors.response.use(
|
||||||
(response) => response,
|
(response) => response,
|
||||||
async (error) => {
|
async (error) => {
|
||||||
if(error?.response?.request?.responseURL?.includes("refresh-token")){
|
if (error?.response?.request?.responseURL?.includes("refresh-token")) {
|
||||||
return }
|
return;
|
||||||
|
}
|
||||||
await mutex.waitForUnlock();
|
await mutex.waitForUnlock();
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config;
|
||||||
// Check for a 401 response and if this request hasn't been retried yet
|
// Check for a 401 response and if this request hasn't been retried yet
|
||||||
@@ -61,9 +62,7 @@ todolistApiInstance.interceptors.response.use(
|
|||||||
|
|
||||||
if (refreshedAt && refreshedAt + 60000 > new Date().getTime()) {
|
if (refreshedAt && refreshedAt + 60000 > new Date().getTime()) {
|
||||||
// If the token has been refreshed within the last minute, use the refreshed token
|
// If the token has been refreshed within the last minute, use the refreshed token
|
||||||
originalRequest.headers[
|
originalRequest.headers.Authorization = `Bearer ${localStorage.getItem("accessToken")}`;
|
||||||
"Authorization"
|
|
||||||
] = `Bearer ${localStorage.getItem("accessToken")}`;
|
|
||||||
release();
|
release();
|
||||||
|
|
||||||
return todolistApiInstance(originalRequest);
|
return todolistApiInstance(originalRequest);
|
||||||
@@ -72,11 +71,14 @@ todolistApiInstance.interceptors.response.use(
|
|||||||
try {
|
try {
|
||||||
const newAccessToken = await refreshAccessToken();
|
const newAccessToken = await refreshAccessToken();
|
||||||
|
|
||||||
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
|
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
||||||
|
|
||||||
return todolistApiInstance(originalRequest); // Retry the original request with the new token
|
return todolistApiInstance(originalRequest); // Retry the original request with the new token
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
router.push("/login");
|
console.log(window.location.pathname);
|
||||||
|
if (!["/login", "/sign-up"].includes(window.location.pathname)) {
|
||||||
|
router.push("/login");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
@@ -84,12 +86,12 @@ todolistApiInstance.interceptors.response.use(
|
|||||||
await mutex.waitForUnlock();
|
await mutex.waitForUnlock();
|
||||||
const newAccessToken = localStorage.getItem("accessToken");
|
const newAccessToken = localStorage.getItem("accessToken");
|
||||||
|
|
||||||
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
|
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
||||||
|
|
||||||
return todolistApiInstance(originalRequest); // Retry the original request with the new token
|
return todolistApiInstance(originalRequest); // Retry the original request with the new token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ export type PostLoginArgs = {
|
|||||||
rememberMe: boolean;
|
rememberMe: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PostSignUpArgs = {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
username?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type LoginResponseData = {
|
export type LoginResponseData = {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
@@ -51,8 +57,8 @@ export type CreateTodolistResponseData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type CreateTaskResponse = Task;
|
export type CreateTaskResponse = Task;
|
||||||
export type DeleteTaskResponse = void;
|
export type DeleteTaskResponse = undefined;
|
||||||
export type UpdateTaskResponse = void;
|
export type UpdateTaskResponse = undefined;
|
||||||
|
|
||||||
export enum TaskStatus {
|
export enum TaskStatus {
|
||||||
OPEN = "OPEN",
|
OPEN = "OPEN",
|
||||||
|
|||||||
Reference in New Issue
Block a user