- 로그인 버튼 비활성화 오류 해결 - 로그인 요칭 및 응답에 따른 토스트 구현
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"local": "vite --mode local",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import reactLogo from './assets/react.svg';
|
|
||||||
import viteLogo from '/vite.svg';
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import SignUpPage from './ui/page/signup/SignUpPage';
|
import SignUpPage from './ui/page/signup/SignUpPage';
|
||||||
import Layout from './layouts/Layout';
|
import Layout from './layouts/Layout';
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ import { BaseResponse } from "../BaseResponse";
|
|||||||
|
|
||||||
export class LoginResponse extends BaseResponse {
|
export class LoginResponse extends BaseResponse {
|
||||||
success!: boolean;
|
success!: boolean;
|
||||||
|
accessToken?: string;
|
||||||
|
refreshToken?: string;
|
||||||
}
|
}
|
||||||
1
src/hooks/use-toast.ts
Normal file
1
src/hooks/use-toast.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import { toast } from 'sonner';
|
||||||
@@ -3,21 +3,40 @@ import { Outlet } from "react-router-dom";
|
|||||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||||
import Header from "@/ui/component/Header";
|
import Header from "@/ui/component/Header";
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
import { Toaster, type ToasterProps } from "sonner";
|
||||||
|
import {
|
||||||
|
CircleCheckIcon,
|
||||||
|
InfoIcon,
|
||||||
|
Loader2Icon,
|
||||||
|
OctagonXIcon,
|
||||||
|
TriangleAlertIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
export default function Layout() {
|
export default function Layout() {
|
||||||
const { authData } = useAuthStore();
|
const { authData } = useAuthStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider
|
<>
|
||||||
defaultOpen={false}
|
<Toaster
|
||||||
id="root"
|
position="top-center"
|
||||||
>
|
icons={{
|
||||||
<SideBar />
|
success: <CircleCheckIcon className="size-4" fill="#15b815" color="white" />,
|
||||||
<div className="flex flex-col w-full h-full">
|
error: <OctagonXIcon className="size-4" fill="#f14e4e" color="white" />,
|
||||||
{ authData?.isLogedIn ? <Header /> : null}
|
info: <InfoIcon className="size-4" fill="black" color="white" />,
|
||||||
{/* <Header /> */}
|
warning: <TriangleAlertIcon className="size-4" fill="#ffd500" color="white" />,
|
||||||
<Outlet />
|
loading: <Loader2Icon className="size-4 animate-spin" fill="white" color="black" />
|
||||||
</div>
|
}}
|
||||||
</SidebarProvider>
|
/>
|
||||||
|
<SidebarProvider
|
||||||
|
defaultOpen={false}
|
||||||
|
id="root"
|
||||||
|
>
|
||||||
|
<SideBar />
|
||||||
|
<div className="flex flex-col w-full h-full">
|
||||||
|
{ authData?.isLogedIn ? <Header /> : null}
|
||||||
|
{/* <Header /> */}
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ import './index.css'
|
|||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
// <StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
// </StrictMode>,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Validator } from '@/util/Validator';
|
|||||||
import { LoginRequest } from '@/data/request/account/LoginRequest';
|
import { LoginRequest } from '@/data/request/account/LoginRequest';
|
||||||
import { AccountNetwork } from '@/network/AccountNetwork';
|
import { AccountNetwork } from '@/network/AccountNetwork';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { LoginResponse } from '@/data/response';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
@@ -26,6 +27,7 @@ export default function LoginPage() {
|
|||||||
password: ""
|
password: ""
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const { id, password } = { id: loginForm.watch('id'), password: loginForm.watch('password') };
|
||||||
|
|
||||||
const moveToSignUpPage = useCallback(() => {
|
const moveToSignUpPage = useCallback(() => {
|
||||||
navigate(PageRouting["SIGN_UP"].path);
|
navigate(PageRouting["SIGN_UP"].path);
|
||||||
@@ -42,28 +44,38 @@ export default function LoginPage() {
|
|||||||
// TODO 33 로그인 기능 구현
|
// TODO 33 로그인 기능 구현
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
|
|
||||||
const { id, password } = loginForm.getValues();
|
|
||||||
const type = Validator.isEmail(id) ? 'email' : 'accountId';
|
const type = Validator.isEmail(id) ? 'email' : 'accountId';
|
||||||
|
|
||||||
const data: LoginRequest = new LoginRequest(type, id, password);
|
const data: LoginRequest = new LoginRequest(type, id, password);
|
||||||
|
|
||||||
const loginPromise = accountNetwork.login(data);
|
const loginPromise = accountNetwork.login(data);
|
||||||
|
|
||||||
toast.promise(
|
toast.promise<{ message?: string }>(
|
||||||
loginPromise,
|
() => new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
loginPromise.then((res) => {
|
||||||
|
if (res.data.success) {
|
||||||
|
resolve({message: ''})
|
||||||
|
} else {
|
||||||
|
reject(res.data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
reject ("서버 에러 발생");
|
||||||
|
}
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
loading: "로그인 중입니다.",
|
loading: "로그인 중입니다.",
|
||||||
success: (res) => res.data.success ? "로그인이 완료되었습니다." : res.data.message,
|
success: "로그인이 완료되었습니다.",
|
||||||
error: "로그인에 실패하였습니다."
|
error: (err) => `${err}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
loginPromise.then((res) => {
|
// loginPromise.then((res) => {
|
||||||
if (res.data.success) {
|
// if (res.data.success) {
|
||||||
moveToMainPage();
|
// moveToMainPage();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextSeparator = ({ text }: { text: string }) => {
|
const TextSeparator = ({ text }: { text: string }) => {
|
||||||
@@ -89,7 +101,7 @@ export default function LoginPage() {
|
|||||||
control={loginForm.control}
|
control={loginForm.control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field data-invalid={fieldState.invalid}>
|
<Field data-invalid={fieldState.invalid}>
|
||||||
<FieldLabel htmlFor="form-login-id">이메일</FieldLabel>
|
<FieldLabel htmlFor="form-login-id">아이디 또는 이메일</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
type="text"
|
type="text"
|
||||||
@@ -111,6 +123,7 @@ export default function LoginPage() {
|
|||||||
<Button
|
<Button
|
||||||
className="p-0 bg-transparent hover:bg-transparent h-fit w-fit text-xs text-gray-400 hover:text-gray-500 cursor-pointer"
|
className="p-0 bg-transparent hover:bg-transparent h-fit w-fit text-xs text-gray-400 hover:text-gray-500 cursor-pointer"
|
||||||
onClick={moveToResetPasswordPage}
|
onClick={moveToResetPasswordPage}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
비밀번호를 잊으셨습니까?
|
비밀번호를 잊으셨습니까?
|
||||||
</Button>
|
</Button>
|
||||||
@@ -133,17 +146,16 @@ export default function LoginPage() {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-indigo-500 hover:bg-indigo-400"
|
className="w-full bg-indigo-500 hover:bg-indigo-400"
|
||||||
type="submit"
|
type="button"
|
||||||
form="form-login"
|
disabled={id.trim().length < 1 || password.trim().length < 1}
|
||||||
disabled={
|
onClick={login}
|
||||||
(loginForm.getValues("id").trim.length < 1)
|
>
|
||||||
&& (loginForm.getValues("password").trim.length < 1)
|
|
||||||
}>
|
|
||||||
로그인
|
로그인
|
||||||
</Button>
|
</Button>
|
||||||
<TextSeparator text="또는" />
|
<TextSeparator text="또는" />
|
||||||
<Button
|
<Button
|
||||||
className="w-full text-violet-500 bg-white border border-violet-500 hover:bg-violet-500 hover:text-white"
|
className="w-full text-violet-500 bg-white border border-violet-500 hover:bg-violet-500 hover:text-white"
|
||||||
|
type="button"
|
||||||
onClick={moveToSignUpPage}
|
onClick={moveToSignUpPage}
|
||||||
>
|
>
|
||||||
회원가입
|
회원가입
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
export class Validator {
|
export class Validator {
|
||||||
static isEmail = (value: string): boolean => {
|
static isEmail = (value: any) => {
|
||||||
return /^[^\s@]+@[^\s@]+\.[*\s@]+$/.test(value);
|
if (typeof value !== 'string') return false;
|
||||||
}
|
const email = value.trim();
|
||||||
|
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(email);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user