| import React, { useState, useEffect } from 'react'; |
| import { Layout } from './components/Layout'; |
| import { Home } from './components/Home'; |
| import { ProjectWorkspace } from './components/ProjectWorkspace'; |
| import { ResearchSpace } from './components/ResearchSpace'; |
| import { Library } from './components/Library'; |
| import { SmartResearcher } from './components/SmartResearcher'; |
| import { Project, Source, ProjectOutput, Language, ChatMessage, Note } from './types'; |
| import { INITIAL_SOURCES } from './constants'; |
| import { translations } from './translations'; |
|
|
| const App: React.FC = () => { |
| const [lang, setLang] = useState<Language>('ar'); |
| const [view, setView] = useState<'home' | 'smart-researcher' | 'library' | 'research' | 'workspace'>('home'); |
| const [projects, setProjects] = useState<Project[]>([]); |
| const [activeProject, setActiveProject] = useState<Project | null>(null); |
| const [allSources, setAllSources] = useState<Source[]>(INITIAL_SOURCES); |
|
|
| const t = translations[lang]; |
|
|
| |
| useEffect(() => { |
| const savedProjects = localStorage.getItem('mouride_projects'); |
| const savedSources = localStorage.getItem('mouride_sources'); |
| const savedLang = localStorage.getItem('mouride_lang') as Language; |
| |
| if (savedProjects) setProjects(JSON.parse(savedProjects)); |
| if (savedSources) setAllSources(JSON.parse(savedSources)); |
| if (savedLang) setLang(savedLang); |
| }, []); |
|
|
| const saveProjects = (newProjects: Project[]) => { |
| setProjects(newProjects); |
| localStorage.setItem('mouride_projects', JSON.stringify(newProjects)); |
| }; |
|
|
| const saveSources = (newSources: Source[]) => { |
| setAllSources(newSources); |
| localStorage.setItem('mouride_sources', JSON.stringify(newSources)); |
| }; |
|
|
| const handleLanguageChange = (newLang: Language) => { |
| setLang(newLang); |
| localStorage.setItem('mouride_lang', newLang); |
| }; |
|
|
| const createProject = (name: string, description: string) => { |
| const newProject: Project = { |
| id: `proj-${Date.now()}`, |
| name, |
| description, |
| createdAt: Date.now(), |
| sources: [], |
| notes: [], |
| outputs: [] |
| }; |
| const updatedProjects = [...projects, newProject]; |
| saveProjects(updatedProjects); |
| handleOpenProject(newProject); |
| }; |
|
|
| const updateProject = (id: string, name: string, description: string) => { |
| const updatedProjects = projects.map(p => |
| p.id === id ? { ...p, name, description } : p |
| ); |
| saveProjects(updatedProjects); |
| if (activeProject?.id === id) { |
| setActiveProject({ ...activeProject, name, description }); |
| } |
| }; |
|
|
| const handlePromoteToProject = (chatHistory: ChatMessage[]) => { |
| const firstUserMsg = chatHistory.find(m => m.role === 'user')?.content || 'موضوع بحثي جديد'; |
| const citedSourceIds = new Set<string>(); |
| chatHistory.forEach(msg => { |
| if (msg.citations) msg.citations.forEach(cit => citedSourceIds.add(cit.sourceId)); |
| if (msg.sources) msg.sources.forEach(sid => citedSourceIds.add(sid)); |
| }); |
|
|
| const newProject: Project = { |
| id: `proj-${Date.now()}`, |
| name: firstUserMsg.substring(0, 50) + (firstUserMsg.length > 50 ? '...' : ''), |
| description: 'مشروع بحثي متكامل تم استخلاصه من الحوار.', |
| createdAt: Date.now(), |
| sources: Array.from(citedSourceIds), |
| notes: chatHistory.filter(m => m.role === 'assistant').map((m, idx) => ({ |
| id: `note-${Date.now()}-${idx}`, |
| title: 'استنتاج علمي مرجعي', |
| content: m.content, |
| timestamp: Date.now() |
| })), |
| outputs: [] |
| }; |
|
|
| saveProjects([...projects, newProject]); |
| setActiveProject(newProject); |
| setView('workspace'); |
| }; |
|
|
| const handleOpenProject = (project: Project) => { |
| setActiveProject(project); |
| setView('workspace'); |
| }; |
|
|
| const handleAddSourceToProject = (sourceId: string, projectId?: string) => { |
| const targetId = projectId || activeProject?.id; |
| if (!targetId) return; |
| |
| const updatedProjects = projects.map(p => { |
| if (p.id === targetId && !p.sources.includes(sourceId)) { |
| const updated = { ...p, sources: [...p.sources, sourceId] }; |
| if (activeProject?.id === targetId) setActiveProject(updated); |
| return updated; |
| } |
| return p; |
| }); |
| saveProjects(updatedProjects); |
| }; |
|
|
| const handleRemoveSourceFromProject = (sourceId: string) => { |
| if (!activeProject) return; |
| const updatedProject = { |
| ...activeProject, |
| sources: activeProject.sources.filter(id => id !== sourceId) |
| }; |
| setActiveProject(updatedProject); |
| saveProjects(projects.map(p => p.id === updatedProject.id ? updatedProject : p)); |
| }; |
|
|
| const handleUpdateNotes = (notes: Note[]) => { |
| if (!activeProject) return; |
| const updatedProject = { ...activeProject, notes }; |
| setActiveProject(updatedProject); |
| saveProjects(projects.map(p => p.id === updatedProject.id ? updatedProject : p)); |
| }; |
|
|
| const handleSaveOutput = (output: ProjectOutput) => { |
| if (!activeProject) return; |
| const updatedProject = { |
| ...activeProject, |
| outputs: [output, ...activeProject.outputs] |
| }; |
| setActiveProject(updatedProject); |
| saveProjects(projects.map(p => p.id === updatedProject.id ? updatedProject : p)); |
| }; |
|
|
| const handleAddGlobalSource = (source: Source) => { |
| const newSources = [...allSources, source]; |
| saveSources(newSources); |
| }; |
|
|
| const handleAddCustomSource = (source: Source) => { |
| const newSources = [...allSources, source]; |
| saveSources(newSources); |
| if (activeProject) handleAddSourceToProject(source.id); |
| }; |
|
|
| return ( |
| <Layout |
| currentView={view} |
| setView={setView} |
| activeProject={activeProject} |
| lang={lang} |
| onLangChange={handleLanguageChange} |
| t={t} |
| > |
| {view === 'home' && ( |
| <Home |
| onStartResearch={() => setView('smart-researcher')} |
| onExploreLibrary={() => setView('library')} |
| t={t} |
| /> |
| )} |
| |
| {view === 'smart-researcher' && ( |
| <SmartResearcher |
| allSources={allSources} |
| onPromoteToProject={handlePromoteToProject} |
| t={t} |
| /> |
| )} |
| |
| {view === 'library' && ( |
| <Library |
| sources={allSources} |
| onAddToProject={handleAddSourceToProject} |
| onAddGlobalSource={handleAddGlobalSource} |
| activeProjectId={activeProject?.id} |
| projects={projects} |
| t={t} |
| /> |
| )} |
| |
| {view === 'research' && ( |
| <ResearchSpace |
| projects={projects} |
| onCreateProject={createProject} |
| onUpdateProject={updateProject} |
| onOpenProject={handleOpenProject} |
| t={t} |
| /> |
| )} |
| |
| {view === 'workspace' && activeProject && ( |
| <ProjectWorkspace |
| project={activeProject} |
| allSources={allSources} |
| onRemoveSource={handleRemoveSourceFromProject} |
| onUpdateNotes={handleUpdateNotes} |
| onAddCustomSource={handleAddCustomSource} |
| onSaveOutput={handleSaveOutput} |
| t={t} |
| /> |
| )} |
| </Layout> |
| ); |
| }; |
|
|
| export default App; |