Skip to content

Commit a5ebf32

Browse files
authored
Merge pull request #76 from billilge/feat/#75-admin-page
feat: 관리자 데스크탑 로그인 페이지 구현
2 parents cf64fc7 + 439c00d commit a5ebf32

File tree

4 files changed

+131
-20
lines changed

4 files changed

+131
-20
lines changed

src/app/callback/page.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useEffect, Suspense } from 'react';
44
import { useSearchParams, useRouter } from 'next/navigation';
5-
import { decode } from 'js-base64';
5+
import { handleLoginSuccess } from '@/utils/loginHandler';
66

77
function CallbackContent() {
88
const searchParams = useSearchParams();
@@ -27,20 +27,7 @@ function CallbackContent() {
2727
break;
2828
case 'SUCCESS':
2929
default:
30-
if (accessToken) {
31-
const payload = accessToken.split('.')[1] || '';
32-
const decodedPayload = decode(payload);
33-
const payloadObject = JSON.parse(decodedPayload);
34-
35-
const tokenRole = payloadObject.role;
36-
const tokenName = payloadObject.name;
37-
const tokenId = payloadObject.sub;
38-
39-
const userInfo = { name: tokenName, id: tokenId, role: tokenRole };
40-
41-
localStorage.setItem('token', accessToken);
42-
localStorage.setItem('user', JSON.stringify(userInfo));
43-
}
30+
handleLoginSuccess(accessToken);
4431
router.replace('/mobile/main');
4532
break;
4633
}

src/app/desktop/login/page.tsx

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,101 @@
1+
'use client';
2+
3+
import ImageLoginLogo from 'public/assets/images/image-login-logo.svg';
4+
import { useRef } from 'react';
5+
import axios from 'axios';
6+
import { postAdminLogin } from '@/services/admins';
7+
import { handleLoginSuccess } from '@/utils/loginHandler';
8+
import { useRouter } from 'next/navigation';
9+
110
export default function Login() {
11+
const router = useRouter();
12+
const studentIdRef = useRef<HTMLInputElement | null>(null);
13+
const passwordRef = useRef<HTMLInputElement | null>(null);
14+
15+
const validateLoginForm = (studentId: string, password: string) => {
16+
if (!studentId) {
17+
alert('학번을 입력해 주세요!');
18+
return false;
19+
}
20+
21+
if (!password) {
22+
alert('비밀번호를 입력해 주세요!');
23+
return false;
24+
}
25+
26+
const idRegex = /^\d{8}$/;
27+
if (!idRegex.test(studentId)) {
28+
alert('학번은 숫자 8자리여야 합니다.');
29+
return false;
30+
}
31+
32+
return true;
33+
};
34+
35+
const handleAdminLogin = async () => {
36+
const studentId = studentIdRef?.current?.value || '';
37+
const password = passwordRef?.current?.value || '';
38+
39+
if (!validateLoginForm(studentId, password)) return;
40+
41+
try {
42+
const data = await postAdminLogin({
43+
studentId,
44+
password,
45+
});
46+
47+
handleLoginSuccess(data.accessToken);
48+
router.push('/desktop/rental-history');
49+
} catch (error) {
50+
if (axios.isAxiosError(error)) {
51+
const errorResponse = error.response?.data;
52+
53+
if (!errorResponse) {
54+
alert('관리자 로그인 중 오류가 발생했습니다!');
55+
return;
56+
}
57+
58+
alert(errorResponse.message);
59+
}
60+
}
61+
};
62+
263
return (
3-
<div className="flex min-h-screen flex-col items-center justify-center gap-16 p-8 pb-20">
4-
<div className="text-center">
5-
<h1>국민대학교 소프트웨어융합대학</h1>
6-
<p className="text-3xl font-bold">복지물품 대여 시스템</p>
7-
</div>
64+
<div className="flex min-h-screen flex-row items-center justify-center gap-80 p-8 pb-20">
65+
<section>
66+
<ImageLoginLogo />
67+
</section>
68+
<section className="flex w-[512px] flex-col items-center justify-center">
69+
<div className="flex w-[300px] flex-col items-center justify-center gap-11 text-center">
70+
<div>
71+
<h1>국민대학교 소프트웨어융합대학</h1>
72+
<p className="text-3xl font-bold">복지물품 대여 시스템</p>
73+
</div>
74+
<div className="flex w-full flex-col items-center justify-center gap-5">
75+
<input
76+
className="w-full border-b-2 border-gray-secondary pb-1 pl-1 pt-1 text-start text-body-1-normal_medi placeholder-gray-secondary placeholder:text-body-1-normal_medi focus:outline-none"
77+
type="text"
78+
placeholder="학번"
79+
ref={studentIdRef}
80+
/>
81+
<input
82+
className="w-full border-b-2 border-gray-secondary pb-1 pl-1 pt-1 text-start text-body-1-normal_medi placeholder-gray-secondary placeholder:text-body-1-normal_medi focus:outline-none"
83+
type="password"
84+
placeholder="비밀번호"
85+
ref={passwordRef}
86+
/>
87+
</div>
88+
<div className="w-full items-center justify-center">
89+
<button
90+
className="w-full rounded bg-on-kookmin pb-2 pt-2 text-center text-body-1-normal_medi text-white"
91+
type="button"
92+
onClick={handleAdminLogin}
93+
>
94+
로그인
95+
</button>
96+
</div>
97+
</div>
98+
</section>
899
</div>
9100
);
10101
}

src/services/admins.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import PublicAxiosInstance from '@/services/publicAxiosInstance';
12
import PrivateAxiosInstance from './privateAxiosInstance';
23

34
export const getAdmins = async () => {
@@ -23,3 +24,20 @@ export const deleteAdmins = async (memberIds: number[]) => {
2324
});
2425
return response.data;
2526
};
27+
28+
interface AdminLoginProps {
29+
studentId: string;
30+
password: string;
31+
}
32+
33+
export const postAdminLogin = async ({
34+
studentId,
35+
password,
36+
}: AdminLoginProps) => {
37+
const response = await PublicAxiosInstance.post('/auth/admin-login', {
38+
studentId,
39+
password,
40+
});
41+
42+
return response.data;
43+
};

src/utils/loginHandler.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { decode } from 'js-base64';
2+
3+
export const handleLoginSuccess = (accessToken: string | null) => {
4+
if (accessToken) {
5+
const payload = accessToken.split('.')[1] || '';
6+
const decodedPayload = decode(payload);
7+
const payloadObject = JSON.parse(decodedPayload);
8+
const { role: tokenRole, name: tokenName, sub: tokenId } = payloadObject;
9+
10+
const userInfo = { name: tokenName, id: tokenId, role: tokenRole };
11+
12+
localStorage.setItem('token', accessToken);
13+
localStorage.setItem('user', JSON.stringify(userInfo));
14+
}
15+
};

0 commit comments

Comments
 (0)