Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { ROUTES } from "./constants/router";
import AuthLayout from "./layouts/AuthLayout";
import MainLayout from "./layouts/MainLayout";

const SignupPage = lazy(() => import("@/pages/SignupPage"));
const SigninPage = lazy(() => import("@/pages/SigninPage"));
const SignupPage = lazy(() => import("@/pages/AuthPage/SignupPage"));
const SigninPage = lazy(() => import("@/pages/AuthPage/SigninPage"));

const ProfilePage = lazy(() => import("@/pages/ProfilePage/ProfilePage"));
const ProfileRegisterPage = lazy(() => import("@/pages/ProfileRegisterPage"));
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/AuthLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Outlet } from "react-router-dom";

export default function AuthLayout() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="min-h-screen flex justify-center items-start">
<div className="w-[21.875rem]">
<Outlet />
</div>
Expand Down
38 changes: 30 additions & 8 deletions src/pages/SigninPage.tsx → src/pages/AuthPage/SigninPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { useState, useEffect } from "react";

import { AxiosError } from "axios";
import { useNavigate, Link } from "react-router-dom";

import Logo from "../assets/logo/thejulge.svg?react";
import { useAuthForm } from "../hooks/useAuthForm";
import Logo from "../../assets/logo/thejulge.svg?react";

import Spinner from "./components/Spinner";
import { useAuthForm } from "./hooks/useAuthForm";

import { postAuthentication } from "@/apis/services/authenticationService";
import Button from "@/components/Button";
Expand All @@ -13,13 +17,28 @@ import { useModalStore } from "@/store/useModalStore";

export default function SigninPage() {
const navigate = useNavigate();
const { setUserAndToken } = useUserStore();
const { user, setUserAndToken } = useUserStore();
const { openModal, closeModal } = useModalStore();

const { formData, errors, isFormValid, handleChange, resetForm } =
useAuthForm("signin");

const [isSubmitting, setIsSubmitting] = useState(false);

useEffect(() => {
if (user) {
if (user.type === "employer") {
navigate(ROUTES.SHOP.ROOT);
} else if (user.type === "employee") {
navigate(ROUTES.PROFILE.ROOT);
}
}
}, [user, navigate]);

const handleSubmit = async () => {
if (isSubmitting) return;
setIsSubmitting(true);

try {
const res = await postAuthentication({
email: formData.email,
Expand Down Expand Up @@ -72,13 +91,15 @@ export default function SigninPage() {
},
],
});
} finally {
setIsSubmitting(false);
}
};

return (
<div className="w-full">
<div className="w-full pt-[8.75rem] sm:pt-[17.5rem] lg:pt-[19.5rem] pb-[15.625rem] sm:pb-[26.125rem] lg:pb-[15.5rem]">
<Link to={ROUTES.NOTICE.ROOT}>
<Logo className="mx-auto mb-2 h-[2.8125rem] w-[15.5rem]" />
<Logo className="mx-auto mb-2 h-[2.375rem] sm:h-[2.8125rem] w-[13rem] sm:w-[15.5rem]" />
</Link>

<form
Expand Down Expand Up @@ -115,11 +136,12 @@ export default function SigninPage() {
<Button
type="submit"
fullWidth
className="py-[0.875rem]"
className="py-[0.875rem] flex justify-center items-center gap-2"
onClick={handleSubmit}
disabled={!isFormValid}
disabled={!isFormValid || isSubmitting}
>
로그인하기
{isSubmitting && <Spinner />}
{isSubmitting ? "로그인 중..." : "로그인하기"}
</Button>
</form>

Expand Down
40 changes: 31 additions & 9 deletions src/pages/SignupPage.tsx → src/pages/AuthPage/SignupPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useState, useEffect } from "react";

import { AxiosError } from "axios";
import clsx from "clsx";
import { useNavigate, Link } from "react-router-dom";

import IconCheck from "../assets/icon/check.svg?react";
import Logo from "../assets/logo/thejulge.svg?react";
import { useAuthForm } from "../hooks/useAuthForm";
import IconCheck from "../../assets/icon/check.svg?react";
import Logo from "../../assets/logo/thejulge.svg?react";

import Spinner from "./components/Spinner";
import { useAuthForm } from "./hooks/useAuthForm";

import { postAuthentication } from "@/apis/services/authenticationService";
import { postUser } from "@/apis/services/userService";
Expand All @@ -16,7 +20,7 @@ import { useModalStore } from "@/store/useModalStore";

export default function SignupPage() {
const navigate = useNavigate();
const { setUserAndToken } = useUserStore();
const { user, setUserAndToken } = useUserStore();
const { openModal, closeModal } = useModalStore();

const {
Expand All @@ -28,7 +32,22 @@ export default function SignupPage() {
resetForm,
} = useAuthForm("signup");

const [isSubmitting, setIsSubmitting] = useState(false);

useEffect(() => {
if (user) {
if (user.type === "employer") {
navigate(ROUTES.SHOP.ROOT);
} else if (user.type === "employee") {
navigate(ROUTES.PROFILE.ROOT);
}
}
}, [user, navigate]);

const handleSubmit = async () => {
if (isSubmitting) return;
setIsSubmitting(true);

try {
const response = await postUser({
email: formData.email,
Expand Down Expand Up @@ -90,13 +109,15 @@ export default function SignupPage() {
},
],
});
} finally {
setIsSubmitting(false);
}
};

return (
<div className="w-full">
<div className="w-full pt-[4.5625rem] sm:pt-[8.6875rem] lg:pt-[9.75rem] pb-[5.1975rem] sm:pb-[20.6875rem] lg:pb-[12.8125rem] ">
<Link to={ROUTES.NOTICE.ROOT}>
<Logo className="mx-auto mb-2 h-[2.8125rem] w-[15.5rem]" />
<Logo className="mx-auto mb-2 h-[2.375rem] sm:h-[2.8125rem] w-[13rem] sm:w-[15.5rem]" />
</Link>

<form
Expand Down Expand Up @@ -215,10 +236,11 @@ export default function SignupPage() {
<Button
type="submit"
fullWidth
className="py-[0.875rem]"
disabled={!isFormValid}
className="py-[0.875rem] flex justify-center items-center gap-2"
disabled={!isFormValid || isSubmitting}
>
가입하기
{isSubmitting && <Spinner />}
{isSubmitting ? "가입 중..." : "가입하기"}
</Button>
</form>

Expand Down
5 changes: 5 additions & 0 deletions src/pages/AuthPage/components/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function Spinner() {
return (
<div className="w-5 h-5 border-2 border-t-white border-gray-300 rounded-full animate-spin" />
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,34 @@ export function useAuthForm(mode: Mode) {
(field: keyof FormData) => (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setFormData((prev) => ({ ...prev, [field]: value }));

if (field === "email") {
const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
setErrors((prev) => ({
...prev,
email: isValidEmail ? undefined : "올바른 이메일 형식이 아닙니다.",
email:
value.length === 0
? undefined
: value.includes("@")
? /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
? undefined
: "올바른 이메일 형식이 아닙니다."
: undefined,
}));
}

if (field === "password") {
const trimmed = value.trim();
setErrors((prev) => ({
...prev,
password:
value.length >= 8 ? undefined : "비밀번호는 8자 이상이어야 합니다.",
value.length > 0 && trimmed.length === 0
? "비밀번호에 공백만 입력할 수 없습니다."
: trimmed.length > 0 && trimmed.length < 8
? "비밀번호는 최소 8자 이상이어야 합니다."
: undefined,
...(mode === "signup" &&
formData.confirmPassword && {
confirmPassword:
value !== formData.confirmPassword
trimmed !== formData.confirmPassword
? "비밀번호가 일치하지 않습니다."
: undefined,
}),
Expand Down