generated from pricelees/issue-pr-template
[#44] 매장 기능 도입 #45
@ -1,8 +1,9 @@
|
|||||||
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||||
import AdminRoute from './components/AdminRoute';
|
|
||||||
import Layout from './components/Layout';
|
import Layout from './components/Layout';
|
||||||
import {AuthProvider} from './context/AuthContext';
|
import { AdminAuthProvider } from './context/AdminAuthContext';
|
||||||
|
import { AuthProvider } from './context/AuthContext';
|
||||||
import AdminLayout from './pages/admin/AdminLayout';
|
import AdminLayout from './pages/admin/AdminLayout';
|
||||||
|
import AdminLoginPage from './pages/admin/AdminLoginPage';
|
||||||
import AdminPage from './pages/admin/AdminPage';
|
import AdminPage from './pages/admin/AdminPage';
|
||||||
import AdminSchedulePage from './pages/admin/AdminSchedulePage';
|
import AdminSchedulePage from './pages/admin/AdminSchedulePage';
|
||||||
import AdminThemeEditPage from './pages/admin/AdminThemeEditPage';
|
import AdminThemeEditPage from './pages/admin/AdminThemeEditPage';
|
||||||
@ -16,26 +17,27 @@ import ReservationStep2Page from '@_pages/ReservationStep2Page';
|
|||||||
import ReservationSuccessPage from '@_pages/ReservationSuccessPage';
|
import ReservationSuccessPage from '@_pages/ReservationSuccessPage';
|
||||||
import SignupPage from '@_pages/SignupPage';
|
import SignupPage from '@_pages/SignupPage';
|
||||||
|
|
||||||
const AdminRoutes = () => (
|
|
||||||
<AdminLayout>
|
|
||||||
<Routes>
|
|
||||||
<Route path="/" element={<AdminPage />} />
|
|
||||||
<Route path="/theme" element={<AdminThemePage />} />
|
|
||||||
<Route path="/theme/edit/:themeId" element={<AdminThemeEditPage />} />
|
|
||||||
<Route path="/schedule" element={<AdminSchedulePage />} />
|
|
||||||
</Routes>
|
|
||||||
</AdminLayout>
|
|
||||||
);
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/admin/*" element={
|
<Route path="/admin/*" element={
|
||||||
<AdminRoute>
|
<AdminAuthProvider>
|
||||||
<AdminRoutes />
|
<Routes>
|
||||||
</AdminRoute>
|
<Route path="/login" element={<AdminLoginPage />} />
|
||||||
|
<Route path="/*" element={
|
||||||
|
<AdminLayout>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<AdminPage />} />
|
||||||
|
<Route path="/theme" element={<AdminThemePage />} />
|
||||||
|
<Route path="/theme/edit/:themeId" element={<AdminThemeEditPage />} />
|
||||||
|
<Route path="/schedule" element={<AdminSchedulePage />} />
|
||||||
|
</Routes>
|
||||||
|
</AdminLayout>
|
||||||
|
} />
|
||||||
|
</Routes>
|
||||||
|
</AdminAuthProvider>
|
||||||
} />
|
} />
|
||||||
<Route path="/*" element={
|
<Route path="/*" element={
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@ -1,19 +1,31 @@
|
|||||||
import apiClient from '@_api/apiClient';
|
import apiClient from '@_api/apiClient';
|
||||||
import type {CurrentUserContext, LoginRequest, LoginSuccessResponse} from './authTypes';
|
import {
|
||||||
|
type AdminLoginSuccessResponse,
|
||||||
|
type LoginRequest,
|
||||||
|
PrincipalType,
|
||||||
|
type UserLoginSuccessResponse,
|
||||||
|
} from './authTypes';
|
||||||
|
|
||||||
|
export const userLogin = async (
|
||||||
export const login = async (data: LoginRequest): Promise<LoginSuccessResponse> => {
|
data: Omit<LoginRequest, 'principalType'>,
|
||||||
const response = await apiClient.post<LoginSuccessResponse>('/auth/login', data, false);
|
): Promise<UserLoginSuccessResponse> => {
|
||||||
localStorage.setItem('accessToken', response.accessToken);
|
return await apiClient.post<UserLoginSuccessResponse>(
|
||||||
|
'/auth/login',
|
||||||
return response;
|
{ ...data, principalType: PrincipalType.USER },
|
||||||
|
false,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkLogin = async (): Promise<CurrentUserContext> => {
|
export const adminLogin = async (
|
||||||
return await apiClient.get<CurrentUserContext>('/auth/login/check', true);
|
data: Omit<LoginRequest, 'principalType'>,
|
||||||
|
): Promise<AdminLoginSuccessResponse> => {
|
||||||
|
return await apiClient.post<AdminLoginSuccessResponse>(
|
||||||
|
'/auth/login',
|
||||||
|
{ ...data, principalType: PrincipalType.ADMIN },
|
||||||
|
false,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logout = async (): Promise<void> => {
|
export const logout = async (): Promise<void> => {
|
||||||
await apiClient.post('/auth/logout', {}, true);
|
await apiClient.post('/auth/logout', {}, true);
|
||||||
localStorage.removeItem('accessToken');
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,6 +5,13 @@ export const PrincipalType = {
|
|||||||
|
|
||||||
export type PrincipalType = typeof PrincipalType[keyof typeof PrincipalType];
|
export type PrincipalType = typeof PrincipalType[keyof typeof PrincipalType];
|
||||||
|
|
||||||
|
export const AdminType = {
|
||||||
|
HQ: 'HQ',
|
||||||
|
STORE: 'STORE',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type AdminType = typeof AdminType[keyof typeof AdminType];
|
||||||
|
|
||||||
export interface LoginRequest {
|
export interface LoginRequest {
|
||||||
account: string,
|
account: string,
|
||||||
password: string;
|
password: string;
|
||||||
@ -13,6 +20,15 @@ export interface LoginRequest {
|
|||||||
|
|
||||||
export interface LoginSuccessResponse {
|
export interface LoginSuccessResponse {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserLoginSuccessResponse extends LoginSuccessResponse {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminLoginSuccessResponse extends LoginSuccessResponse {
|
||||||
|
type: AdminType;
|
||||||
|
storeId: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CurrentUserContext {
|
export interface CurrentUserContext {
|
||||||
|
|||||||
96
frontend/src/context/AdminAuthContext.tsx
Normal file
96
frontend/src/context/AdminAuthContext.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { adminLogin as apiLogin, logout as apiLogout } from '@_api/auth/authAPI';
|
||||||
|
import {
|
||||||
|
type AdminLoginSuccessResponse,
|
||||||
|
type AdminType,
|
||||||
|
type LoginRequest,
|
||||||
|
} from '@_api/auth/authTypes';
|
||||||
|
import React, { createContext, type ReactNode, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
interface AdminAuthContextType {
|
||||||
|
isAdmin: boolean;
|
||||||
|
name: string | null;
|
||||||
|
type: AdminType | null;
|
||||||
|
storeId: number | null;
|
||||||
|
loading: boolean;
|
||||||
|
login: (data: Omit<LoginRequest, 'principalType'>) => Promise<AdminLoginSuccessResponse>;
|
||||||
|
logout: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminAuthContext = createContext<AdminAuthContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const AdminAuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
|
const [name, setName] = useState<string | null>(null);
|
||||||
|
const [type, setType] = useState<AdminType | null>(null);
|
||||||
|
const [storeId, setStoreId] = useState<number | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('adminAccessToken');
|
||||||
|
const storedName = localStorage.getItem('adminName');
|
||||||
|
const storedType = localStorage.getItem('adminType') as AdminType | null;
|
||||||
|
const storedStoreId = localStorage.getItem('adminStoreId');
|
||||||
|
|
||||||
|
if (token && storedName && storedType) {
|
||||||
|
setIsAdmin(true);
|
||||||
|
setName(storedName);
|
||||||
|
setType(storedType);
|
||||||
|
setStoreId(storedStoreId ? parseInt(storedStoreId, 10) : null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load admin auth state from storage", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const login = async (data: Omit<LoginRequest, 'principalType'>) => {
|
||||||
|
const response = await apiLogin(data);
|
||||||
|
|
||||||
|
localStorage.setItem('adminAccessToken', response.accessToken);
|
||||||
|
localStorage.setItem('adminName', response.name);
|
||||||
|
localStorage.setItem('adminType', response.type);
|
||||||
|
if (response.storeId) {
|
||||||
|
localStorage.setItem('adminStoreId', response.storeId.toString());
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('adminStoreId');
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAdmin(true);
|
||||||
|
setName(response.name);
|
||||||
|
setType(response.type);
|
||||||
|
setStoreId(response.storeId);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = async () => {
|
||||||
|
try {
|
||||||
|
await apiLogout();
|
||||||
|
} finally {
|
||||||
|
localStorage.removeItem('adminAccessToken');
|
||||||
|
localStorage.removeItem('adminName');
|
||||||
|
localStorage.removeItem('adminType');
|
||||||
|
localStorage.removeItem('adminStoreId');
|
||||||
|
setIsAdmin(false);
|
||||||
|
setName(null);
|
||||||
|
setType(null);
|
||||||
|
setStoreId(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminAuthContext.Provider value={{ isAdmin, name, type, storeId, loading, login, logout }}>
|
||||||
|
{children}
|
||||||
|
</AdminAuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminAuth = (): AdminAuthContextType => {
|
||||||
|
const context = useContext(AdminAuthContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useAdminAuth must be used within an AdminAuthProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -1,15 +1,13 @@
|
|||||||
import {checkLogin as apiCheckLogin, login as apiLogin, logout as apiLogout} from '@_api/auth/authAPI';
|
import { logout as apiLogout, userLogin as apiLogin } from '@_api/auth/authAPI';
|
||||||
import {type LoginRequest, type LoginSuccessResponse, PrincipalType} from '@_api/auth/authTypes';
|
import { type LoginRequest, type UserLoginSuccessResponse } from '@_api/auth/authTypes';
|
||||||
import React, {createContext, type ReactNode, useContext, useEffect, useState} from 'react';
|
import React, { createContext, type ReactNode, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
userName: string | null;
|
userName: string | null;
|
||||||
type: PrincipalType | null;
|
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
login: (data: LoginRequest) => Promise<LoginSuccessResponse>;
|
login: (data: Omit<LoginRequest, 'principalType'>) => Promise<UserLoginSuccessResponse>;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
checkLogin: () => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||||
@ -17,33 +15,33 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|||||||
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const [loggedIn, setLoggedIn] = useState(false);
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
const [userName, setUserName] = useState<string | null>(null);
|
const [userName, setUserName] = useState<string | null>(null);
|
||||||
const [type, setType] = useState<PrincipalType | null>(null);
|
const [loading, setLoading] = useState(true);
|
||||||
const [loading, setLoading] = useState(true); // Add loading state
|
|
||||||
|
|
||||||
const checkLogin = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiCheckLogin();
|
|
||||||
setLoggedIn(true);
|
|
||||||
setUserName(response.name);
|
|
||||||
setType(response.type);
|
|
||||||
} catch (error) {
|
|
||||||
setLoggedIn(false);
|
|
||||||
setUserName(null);
|
|
||||||
setType(null);
|
|
||||||
localStorage.removeItem('accessToken');
|
|
||||||
} finally {
|
|
||||||
setLoading(false); // Set loading to false after check is complete
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkLogin();
|
try {
|
||||||
|
const token = localStorage.getItem('accessToken');
|
||||||
|
const storedUserName = localStorage.getItem('userName');
|
||||||
|
|
||||||
|
if (token && storedUserName) {
|
||||||
|
setLoggedIn(true);
|
||||||
|
setUserName(storedUserName);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load user auth state from storage", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = async (data: LoginRequest) => {
|
const login = async (data: Omit<LoginRequest, 'principalType'>) => {
|
||||||
const response = await apiLogin({ ...data });
|
const response = await apiLogin(data);
|
||||||
|
|
||||||
|
localStorage.setItem('accessToken', response.accessToken);
|
||||||
|
localStorage.setItem('userName', response.name);
|
||||||
|
|
||||||
setLoggedIn(true);
|
setLoggedIn(true);
|
||||||
setType(data.principalType);
|
setUserName(response.name);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,15 +49,15 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
try {
|
try {
|
||||||
await apiLogout();
|
await apiLogout();
|
||||||
} finally {
|
} finally {
|
||||||
|
localStorage.removeItem('accessToken');
|
||||||
|
localStorage.removeItem('userName');
|
||||||
setLoggedIn(false);
|
setLoggedIn(false);
|
||||||
setUserName(null);
|
setUserName(null);
|
||||||
setType(null);
|
|
||||||
localStorage.removeItem('accessToken');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ loggedIn, userName, type, loading, login, logout, checkLogin }}>
|
<AuthContext.Provider value={{ loggedIn, userName, loading, login, logout }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,11 +15,11 @@ const LoginPage: React.FC = () => {
|
|||||||
const handleLogin = async (e: React.FormEvent) => {
|
const handleLogin = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
const principalType = from.startsWith('/admin') ? 'ADMIN' : 'USER';
|
await login({ account: email, password: password });
|
||||||
await login({ account: email, password: password, principalType: principalType });
|
|
||||||
|
|
||||||
alert('로그인에 성공했어요!');
|
alert('로그인에 성공했어요!');
|
||||||
navigate(from, { replace: true });
|
const redirectTo = from.startsWith('/admin') ? '/' : from;
|
||||||
|
navigate(redirectTo, { replace: true });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const message = error.response?.data?.message || '로그인에 실패했어요. 이메일과 비밀번호를 확인해주세요.';
|
const message = error.response?.data?.message || '로그인에 실패했어요. 이메일과 비밀번호를 확인해주세요.';
|
||||||
alert(message);
|
alert(message);
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import React, {type ReactNode} from 'react';
|
import { useAdminAuth } from '@_context/AdminAuthContext';
|
||||||
|
import React, { type ReactNode, useEffect } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import AdminNavbar from './AdminNavbar';
|
import AdminNavbar from './AdminNavbar';
|
||||||
|
|
||||||
interface AdminLayoutProps {
|
interface AdminLayoutProps {
|
||||||
@ -6,6 +8,23 @@ interface AdminLayoutProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AdminLayout: React.FC<AdminLayoutProps> = ({ children }) => {
|
const AdminLayout: React.FC<AdminLayoutProps> = ({ children }) => {
|
||||||
|
const { isAdmin, loading } = useAdminAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && !isAdmin) {
|
||||||
|
navigate('/admin/login');
|
||||||
|
}
|
||||||
|
}, [isAdmin, loading, navigate]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AdminNavbar />
|
<AdminNavbar />
|
||||||
|
|||||||
61
frontend/src/pages/admin/AdminLoginPage.tsx
Normal file
61
frontend/src/pages/admin/AdminLoginPage.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
import { useAdminAuth } from '@_context/AdminAuthContext';
|
||||||
|
import '@_css/login-page-v2.css';
|
||||||
|
|
||||||
|
const AdminLoginPage: React.FC = () => {
|
||||||
|
const [account, setAccount] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const { login } = useAdminAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const from = location.state?.from?.pathname || '/admin';
|
||||||
|
|
||||||
|
const handleLogin = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
await login({ account: account, password: password });
|
||||||
|
alert('관리자 로그인에 성공했어요!');
|
||||||
|
navigate(from, { replace: true });
|
||||||
|
} catch (error: any) {
|
||||||
|
const message = error.response?.data?.message || '로그인에 실패했어요. 계정과 비밀번호를 확인해주세요.';
|
||||||
|
alert(message);
|
||||||
|
console.error('관리자 로그인 실패:', error);
|
||||||
|
setPassword('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-container-v2">
|
||||||
|
<h2 className="page-title">관리자 로그인</h2>
|
||||||
|
<form className="login-form-v2" onSubmit={handleLogin}>
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-input"
|
||||||
|
placeholder="계정"
|
||||||
|
value={account}
|
||||||
|
onChange={e => setAccount(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
className="form-input"
|
||||||
|
placeholder="비밀번호"
|
||||||
|
value={password}
|
||||||
|
onChange={e => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="button-group">
|
||||||
|
<button type="submit" className="btn btn-primary">로그인</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdminLoginPage;
|
||||||
@ -1,10 +1,10 @@
|
|||||||
|
import {useAdminAuth} from '@_context/AdminAuthContext';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link, useNavigate} from 'react-router-dom';
|
import {Link, useNavigate} from 'react-router-dom';
|
||||||
import {useAuth} from '@_context/AuthContext';
|
|
||||||
import '@_css/navbar.css';
|
import '@_css/navbar.css';
|
||||||
|
|
||||||
const AdminNavbar: React.FC = () => {
|
const AdminNavbar: React.FC = () => {
|
||||||
const { loggedIn, userName, logout } = useAuth();
|
const { isAdmin, name, logout } = useAdminAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleLogout = async (e: React.MouseEvent) => {
|
const handleLogout = async (e: React.MouseEvent) => {
|
||||||
@ -25,12 +25,12 @@ const AdminNavbar: React.FC = () => {
|
|||||||
<Link className="nav-link" to="/admin/schedule">일정</Link>
|
<Link className="nav-link" to="/admin/schedule">일정</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-actions">
|
<div className="nav-actions">
|
||||||
{!loggedIn ? (
|
{!isAdmin ? (
|
||||||
<button className="btn btn-primary" onClick={() => navigate('/v2/login')}>Login</button>
|
<button className="btn btn-primary" onClick={() => navigate('/admin/login')}>Login</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="profile-info">
|
<div className="profile-info">
|
||||||
<img className="profile-image" src="/image/default-profile.png" alt="Profile" />
|
<img className="profile-image" src="/image/default-profile.png" alt="Profile" />
|
||||||
<span>{userName || 'Profile'}</span>
|
<span>{name || 'Profile'}</span>
|
||||||
<div className="dropdown-menu">
|
<div className="dropdown-menu">
|
||||||
<a className="dropdown-item" href="#" onClick={handleLogout}>Logout</a>
|
<a className="dropdown-item" href="#" onClick={handleLogout}>Logout</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const AdminSchedulePage: React.FC = () => {
|
|||||||
const handleError = (err: any) => {
|
const handleError = (err: any) => {
|
||||||
if (isLoginRequiredError(err)) {
|
if (isLoginRequiredError(err)) {
|
||||||
alert('로그인이 필요해요.');
|
alert('로그인이 필요해요.');
|
||||||
navigate('/login', { state: { from: location } });
|
navigate('/admin/login', { state: { from: location } });
|
||||||
} else {
|
} else {
|
||||||
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
||||||
alert(message);
|
alert(message);
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const AdminThemeEditPage: React.FC = () => {
|
|||||||
const handleError = (err: any) => {
|
const handleError = (err: any) => {
|
||||||
if (isLoginRequiredError(err)) {
|
if (isLoginRequiredError(err)) {
|
||||||
alert('로그인이 필요해요.');
|
alert('로그인이 필요해요.');
|
||||||
navigate('/login', { state: { from: location } });
|
navigate('/admin/login', { state: { from: location } });
|
||||||
} else {
|
} else {
|
||||||
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
||||||
alert(message);
|
alert(message);
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const AdminThemePage: React.FC = () => {
|
|||||||
const handleError = (err: any) => {
|
const handleError = (err: any) => {
|
||||||
if (isLoginRequiredError(err)) {
|
if (isLoginRequiredError(err)) {
|
||||||
alert('로그인이 필요해요.');
|
alert('로그인이 필요해요.');
|
||||||
navigate('/login', { state: { from: location } });
|
navigate('/admin/login', { state: { from: location } });
|
||||||
} else {
|
} else {
|
||||||
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.';
|
||||||
alert(message);
|
alert(message);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user