Files
ncuetalk_front/src/App.jsx
dsyoon d56d136522 init
2025-08-18 09:40:54 +09:00

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>
);
}