| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import React, { useEffect, useState } from 'react'
- import { setDragDropEnabled } from './components/GlobalDragDropOverlay'
- import { setNotebookDragDropEnabled } from './components/NotebookGlobalDragDropOverlay'
- import LoginPage from './components/LoginPage'
- import { SidebarRail, ViewType } from './components/layouts/SidebarRail'
- import { ChatView } from './components/views/ChatView'
- import { KnowledgeBaseView } from './components/views/KnowledgeBaseView'
- import { NotebooksView } from './components/views/NotebooksView'
- import { SettingsView } from './components/views/SettingsView'
- import { LanguageProvider } from './contexts/LanguageContext'
- import { ToastProvider } from './contexts/ToastContext'
- import { ConfirmProvider } from './contexts/ConfirmContext'
- import { modelConfigService } from './services/modelConfigService'
- import { authService } from './services/authService'
- import { ModelConfig, DEFAULT_MODELS } from './types'
- const AppContent: React.FC = () => {
- const [authToken, setAuthToken] = useState<string | null>(null)
- const [currentUser, setCurrentUser] = useState<any>(null); // Add current user state
- const [currentView, setCurrentView] = useState<ViewType>('chat')
- const [isVerifying, setIsVerifying] = useState(true)
- // Chat Context State
- const [chatContext, setChatContext] = useState<{ selectedGroups?: string[], selectedFiles?: string[] } | null>(null)
- // Model State
- const [modelConfigs, setModelConfigs] = useState<ModelConfig[]>(DEFAULT_MODELS)
- // Disable drag drop when view changes
- useEffect(() => {
- // ビュー切り替え時にドラッグアンドドロップ機能を一時的に無効にして、ナビゲーション時の点滅を防ぐ
- setDragDropEnabled(false);
- setNotebookDragDropEnabled(false);
- // 次のイベントループで有効にして、ビュー切り替えが完了することを確認
- const timer = setTimeout(() => {
- setDragDropEnabled(true);
- setNotebookDragDropEnabled(true);
- }, 0);
- return () => {
- clearTimeout(timer);
- };
- }, [currentView]);
- // Load token from localStorage on initial render
- useEffect(() => {
- const verifyToken = async () => {
- const storedToken = localStorage.getItem('authToken')
- if (storedToken) {
- try {
- const userProfile = await authService.getProfile(storedToken) // Get full profile
- setAuthToken(storedToken)
- setCurrentUser(userProfile) // Store user profile
- } catch (error) {
- console.error('Invalid token, logging out:', error)
- localStorage.removeItem('authToken')
- setAuthToken(null)
- setCurrentUser(null)
- }
- }
- setIsVerifying(false)
- }
- verifyToken()
- }, [])
- const handleLoginSuccess = async (token: string) => {
- setAuthToken(token)
- localStorage.setItem('authToken', token)
- try {
- const userProfile = await authService.getProfile(token)
- setCurrentUser(userProfile)
- } catch (error) {
- console.error('Failed to fetch user profile:', error)
- }
- }
- const handleLogout = () => {
- setAuthToken(null)
- setCurrentUser(null)
- localStorage.removeItem('authToken')
- }
- // Fetch Models
- const fetchAndSetModels = React.useCallback(async () => {
- if (!authToken) return
- try {
- const backendModels = await modelConfigService.getAll(authToken)
- const mergedModelsMap = new Map<string, ModelConfig>()
- DEFAULT_MODELS.forEach(m => mergedModelsMap.set(m.id, m))
- backendModels.forEach(bm => {
- mergedModelsMap.set(bm.id, { ...bm })
- })
- const mergedModels = Array.from(mergedModelsMap.values())
- setModelConfigs(mergedModels)
- } catch (error) {
- console.error('Failed to fetch model configs:', error)
- setModelConfigs(DEFAULT_MODELS)
- }
- }, [authToken])
- useEffect(() => {
- if (authToken) {
- fetchAndSetModels()
- }
- }, [authToken, fetchAndSetModels])
- const handleUpdateModels = React.useCallback(async (action: 'create' | 'update' | 'delete', model: ModelConfig) => {
- if (!authToken) return
- try {
- if (action === 'create') {
- await modelConfigService.create(authToken, model)
- } else if (action === 'update') {
- await modelConfigService.update(authToken, model.id, model)
- } else if (action === 'delete') {
- await modelConfigService.remove(authToken, model.id)
- }
- await fetchAndSetModels()
- } catch (error) {
- console.error(`Failed to perform ${action} on model config:`, error)
- throw error
- }
- }, [authToken, fetchAndSetModels])
- const handleChatWithContext = (context: { selectedGroups?: string[], selectedFiles?: string[] }) => {
- setChatContext(context)
- setCurrentView('chat')
- }
- // Login Flow
- if (isVerifying) {
- return (
- <div className="h-screen w-full flex items-center justify-center bg-slate-50">
- <div className="flex flex-col items-center gap-4">
- <div className="w-12 h-12 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
- <p className="text-slate-600 font-medium">Verifying session...</p>
- </div>
- </div>
- )
- }
- if (!authToken) {
- return <LoginPage onLoginSuccess={handleLoginSuccess} />
- }
- // Main Layout (Rail + View)
- return (
- <div className='flex h-screen w-full bg-slate-50 overflow-hidden relative'>
- <SidebarRail
- currentView={currentView}
- onViewChange={setCurrentView}
- onLogout={handleLogout}
- currentUser={currentUser}
- />
- <div className="flex-1 overflow-hidden relative">
- {currentView === 'chat' && (
- <ChatView
- authToken={authToken}
- onLogout={handleLogout}
- modelConfigs={modelConfigs}
- onNavigate={(view) => setCurrentView(view)}
- initialChatContext={chatContext}
- onClearContext={() => setChatContext(null)}
- isAdmin={!!currentUser?.isAdmin}
- />
- )}
- {currentView === 'knowledge' && (
- <KnowledgeBaseView
- authToken={authToken}
- onLogout={handleLogout}
- modelConfigs={modelConfigs}
- onNavigate={(view) => setCurrentView(view)}
- isAdmin={!!currentUser?.isAdmin}
- />
- )}
- {currentView === 'notebooks' && (
- <NotebooksView
- authToken={authToken}
- onChatWithContext={handleChatWithContext}
- isAdmin={!!currentUser?.isAdmin}
- />
- )}
- {currentView === 'settings' && (
- <SettingsView
- models={modelConfigs}
- onUpdateModels={handleUpdateModels}
- authToken={authToken}
- isAdmin={!!currentUser?.isAdmin} // Pass isAdmin status
- currentUser={currentUser} // Pass current user
- />
- )}
- </div>
- </div>
- )
- }
- const App: React.FC = () => {
- return (
- <LanguageProvider>
- <ToastProvider>
- <ConfirmProvider>
- <AppContent />
- </ConfirmProvider>
- </ToastProvider>
- </LanguageProvider>
- )
- }
- export default App
|