113 lines
4.4 KiB
JavaScript
113 lines
4.4 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import Sidebar from './components/Sidebar';
|
|
import ChatPage from './pages/ChatPage';
|
|
import ToolsPage from './pages/ToolsPage';
|
|
import LecturePage from './pages/LecturePage';
|
|
import AiNewsPage from './pages/AiNewsPage';
|
|
import QnaPage from './pages/QnaPage';
|
|
import { ToolProvider } from './context/ToolContext';
|
|
import { AuthProvider, useAuth } from './context/AuthContext';
|
|
import LoginPage from './pages/LoginPage';
|
|
import { useTool } from './context/ToolContext';
|
|
|
|
function TopbarTitle({ currentPage, onNavigate }) {
|
|
const { selectedTool } = useTool();
|
|
const { user, logout } = useAuth();
|
|
const titleMap = { tools: '도구', lecture: '강의', ai_news: 'AI뉴스', qna: '질의응답' };
|
|
const title = currentPage === 'chat' ? (selectedTool?.name || '채팅') : (titleMap[currentPage] || '');
|
|
return (
|
|
<header className="topbar">
|
|
<span className="page-title">{title}</span>
|
|
<UserBadge user={user} onLogout={logout} onNavigate={onNavigate} />
|
|
</header>
|
|
);
|
|
}
|
|
|
|
function UserBadge({ user, onLogout, onNavigate }) {
|
|
const [open, setOpen] = React.useState(false);
|
|
if (!user) return <a className="login-btn" href="/login" onClick={(e)=>{ e.preventDefault(); onNavigate('login'); }}>로그인</a>;
|
|
return (
|
|
<div style={{ position:'relative' }}>
|
|
<button className="login-btn" onClick={()=>setOpen(!open)}>{user.user_id}</button>
|
|
{open && (
|
|
<div style={{ position:'absolute', right:0, top:36, background:'#fff', border:'1px solid #eee', borderRadius:12, padding:'10px 12px', boxShadow:'0 4px 16px rgba(0,0,0,.08)', zIndex: 1000, pointerEvents:'auto' }}>
|
|
<div style={{ fontSize:'.95rem', color:'#333', marginBottom:8 }}>{user.email}</div>
|
|
<button onClick={(e)=>{ e.stopPropagation(); onLogout(); setOpen(false); }} style={{ background:'#fff6f6', color:'#e53935', border:'1px solid #ffd6d6', padding:'6px 10px', borderRadius:8, cursor:'pointer' }}>logout</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
const [currentPage, setCurrentPage] = useState('chat');
|
|
|
|
const PAGES = {
|
|
chat: <ChatPage />,
|
|
tools: <ToolsPage onNavigate={handleNavigate} />,
|
|
lecture: <LecturePage />,
|
|
// community 제거
|
|
login: <LoginPage onLoggedIn={() => { handleNavigate('chat'); }} />,
|
|
ai_news: <AiNewsPage />,
|
|
qna: <QnaPage />,
|
|
};
|
|
|
|
// URL → 상태 동기화 (초기 진입 및 뒤로가기 대응)
|
|
useEffect(() => {
|
|
const path = window.location.pathname.replace(/^\/+/, '');
|
|
const menuPaths = { chatting: 'chat', tools: 'tools', lecture: 'lecture', ai_news: 'ai_news', qna: 'qna', login: 'login' };
|
|
const maybeMenu = menuPaths[path];
|
|
if (maybeMenu) {
|
|
setCurrentPage(maybeMenu);
|
|
} else if (path === '') {
|
|
setCurrentPage('chat');
|
|
} else {
|
|
// 카드 경로일 때는 채팅 페이지로 전환
|
|
setCurrentPage('chat');
|
|
}
|
|
}, []);
|
|
|
|
// 브라우저 뒤/앞으로가기(popstate) 시 URL에 맞게 화면 전환
|
|
useEffect(() => {
|
|
const onPopState = () => {
|
|
const path = window.location.pathname.replace(/^\/+/, '');
|
|
const menuPaths = { chatting: 'chat', tools: 'tools', lecture: 'lecture', ai_news: 'ai_news', qna: 'qna', login: 'login' };
|
|
const maybeMenu = menuPaths[path];
|
|
if (maybeMenu) {
|
|
setCurrentPage(maybeMenu);
|
|
} else if (path === '') {
|
|
setCurrentPage('chat');
|
|
} else {
|
|
// 카드 경로는 채팅 화면으로
|
|
setCurrentPage('chat');
|
|
}
|
|
};
|
|
window.addEventListener('popstate', onPopState);
|
|
return () => window.removeEventListener('popstate', onPopState);
|
|
}, []);
|
|
|
|
// 메뉴 전환 시 URL 경로 갱신
|
|
function handleNavigate(page) {
|
|
setCurrentPage(page);
|
|
const menuToPath = { chat: 'chatting', tools: 'tools', lecture: 'lecture', ai_news: 'ai_news', qna: 'qna', login: 'login' };
|
|
const p = menuToPath[page] || '';
|
|
const newPath = `/${p}`;
|
|
if (window.location.pathname !== newPath) {
|
|
window.history.pushState({}, '', newPath);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<AuthProvider>
|
|
<ToolProvider>
|
|
<div className="container">
|
|
<Sidebar current={currentPage} onChange={handleNavigate} />
|
|
<main className="main-content">
|
|
<TopbarTitle currentPage={currentPage} onNavigate={handleNavigate} />
|
|
{PAGES[currentPage]}
|
|
</main>
|
|
</div>
|
|
</ToolProvider>
|
|
</AuthProvider>
|
|
);
|
|
}
|