Files
plume/components/layout/EditorShell.tsx

133 lines
8.6 KiB
TypeScript

import React, { useState } from 'react';
import { BookProject, UserProfile, ViewMode, ChatMessage } from '../../types';
import AIPanel from '../AIPanel';
import { Book, FileText, Globe, GitGraph, Lightbulb, Settings, Menu, ChevronRight, ChevronLeft, Share2, HelpCircle, LogOut, LayoutDashboard, User, Plus, Trash2 } from 'lucide-react';
interface EditorShellProps {
project: BookProject;
user: UserProfile;
viewMode: ViewMode;
currentChapterId: string;
chatHistory: ChatMessage[];
isGenerating: boolean;
onViewModeChange: (mode: ViewMode) => void;
onChapterSelect: (id: string) => void;
onUpdateProject: (updates: Partial<BookProject>) => void;
onAddChapter: () => void;
onDeleteChapter: (id: string) => void;
onLogout: () => void;
onSendMessage: (msg: string) => void;
onInsertText: (text: string) => void;
onOpenExport: () => void;
onOpenHelp: () => void;
children: React.ReactNode;
}
const EditorShell: React.FC<EditorShellProps> = (props) => {
const { project, user, viewMode, currentChapterId, children } = props;
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [isAiPanelOpen, setIsAiPanelOpen] = useState(true);
const currentChapter = project.chapters.find(c => c.id === currentChapterId);
return (
<div className={`flex h-screen overflow-hidden no-print ${user.preferences.theme === 'dark' ? 'bg-slate-900 text-white' : user.preferences.theme === 'sepia' ? 'bg-[#f4ecd8]' : 'bg-[#eef2ff]'}`}>
{/* SIDEBAR */}
<aside className={`${isSidebarOpen ? 'w-64' : 'w-0'} bg-slate-900 text-slate-300 flex-shrink-0 transition-all duration-300 overflow-hidden flex flex-col border-r border-slate-800`}>
<div className="p-4 border-b border-slate-700">
<h1 className="text-white font-bold flex items-center gap-2 mb-4 cursor-pointer" onClick={() => props.onViewModeChange('dashboard')}>
<Book className="text-blue-400" /> PlumeIA
</h1>
<input
type="text"
value={project.title}
onChange={(e) => props.onUpdateProject({ title: e.target.value })}
className="w-full bg-transparent font-serif font-bold text-white text-lg mb-1 focus:outline-none focus:border-b focus:border-blue-500 truncate"
placeholder="Titre du livre"
/>
<button onClick={() => props.onViewModeChange('dashboard')} className="w-full flex items-center gap-2 text-xs hover:bg-slate-800 p-2 rounded transition-colors text-slate-400">
<LayoutDashboard size={14} /> Retour au Dashboard
</button>
</div>
<div className="flex-1 overflow-y-auto py-2">
<div className="px-4 py-2 text-xs font-semibold text-slate-500 uppercase flex justify-between items-center">
Chapitres <button onClick={props.onAddChapter} className="hover:text-blue-400"><Plus size={14} /></button>
</div>
{project.chapters.map((chap, idx) => (
<div key={chap.id} className="group relative">
<button
onClick={() => props.onChapterSelect(chap.id)}
className={`w-full text-left px-4 py-2 text-sm truncate transition-colors ${currentChapterId === chap.id && viewMode === 'write' ? 'bg-blue-900 text-white border-r-2 border-blue-400' : 'hover:bg-slate-800'}`}
>
{idx + 1}. {chap.title}
</button>
<button onClick={() => props.onDeleteChapter(chap.id)} className="absolute right-2 top-2 text-slate-600 hover:text-red-400 opacity-0 group-hover:opacity-100"><Trash2 size={14} /></button>
</div>
))}
<div className="mt-6 px-4 py-2 text-xs font-semibold text-slate-500 uppercase">Outils & Bible</div>
<button onClick={() => props.onViewModeChange('write')} className={`w-full text-left px-4 py-2 text-sm flex items-center gap-2 ${viewMode === 'write' ? 'bg-blue-900 text-white' : 'hover:bg-slate-800'}`}><FileText size={16} /> Éditeur</button>
<button onClick={() => props.onViewModeChange('world_building')} className={`w-full text-left px-4 py-2 text-sm flex items-center gap-2 ${viewMode === 'world_building' ? 'bg-indigo-900 text-white' : 'hover:bg-slate-800'}`}><Globe size={16} /> Bible du Monde</button>
<button onClick={() => props.onViewModeChange('workflow')} className={`w-full text-left px-4 py-2 text-sm flex items-center gap-2 ${viewMode === 'workflow' ? 'bg-indigo-900 text-white' : 'hover:bg-slate-800'}`}><GitGraph size={16} /> Workflow</button>
<button onClick={() => props.onViewModeChange('ideas')} className={`w-full text-left px-4 py-2 text-sm flex items-center gap-2 ${viewMode === 'ideas' ? 'bg-indigo-900 text-white' : 'hover:bg-slate-800'}`}><Lightbulb size={16} /> Boîte à Idées</button>
<button onClick={() => props.onViewModeChange('settings')} className={`w-full text-left px-4 py-2 text-sm flex items-center gap-2 ${viewMode === 'settings' ? 'bg-indigo-900 text-white' : 'hover:bg-slate-800'}`}><Settings size={16} /> Paramètres</button>
</div>
<div className="p-4 border-t border-slate-800">
<div className="bg-slate-800 rounded-lg p-3 mb-4">
<div className="flex justify-between text-[10px] text-slate-400 uppercase font-bold mb-1">
<span>Actions IA</span>
<span>{user.usage.aiActionsCurrent} / {user.usage.aiActionsLimit === 999999 ? '∞' : user.usage.aiActionsLimit}</span>
</div>
<div className="h-1.5 w-full bg-slate-700 rounded-full overflow-hidden">
<div className="h-full bg-blue-500" style={{ width: `${Math.min(100, (user.usage.aiActionsCurrent / user.usage.aiActionsLimit) * 100)}%` }} />
</div>
</div>
<button onClick={() => props.onViewModeChange('profile')} className="w-full flex items-center gap-2 px-3 py-2 text-xs text-slate-400 hover:bg-slate-800 rounded mb-2"><User size={14} /> Mon Compte</button>
<button onClick={props.onLogout} className="w-full flex items-center gap-2 px-3 py-2 text-xs text-red-400 hover:bg-red-900/20 rounded"><LogOut size={14} /> Déconnexion</button>
</div>
</aside>
{/* MAIN CONTENT */}
<div className="flex-1 flex flex-col h-full overflow-hidden">
<header className="h-14 bg-white border-b border-slate-200 flex items-center justify-between px-4 shadow-sm z-10 text-slate-800">
<div className="flex items-center gap-4">
<button onClick={() => setIsSidebarOpen(!isSidebarOpen)} className="text-slate-500 hover:text-slate-800"><Menu size={20} /></button>
{viewMode === 'write' ? (
<input
type="text"
value={currentChapter?.title || ""}
onChange={(e) => props.onUpdateProject({ chapters: project.chapters.map(c => c.id === currentChapterId ? {...c, title: e.target.value} : c) })}
className="font-serif font-bold text-lg bg-transparent border-b border-transparent focus:border-blue-500 focus:outline-none"
/>
) : (
<span className="font-bold uppercase tracking-widest text-xs">{viewMode}</span>
)}
</div>
<div className="flex items-center gap-3">
<button onClick={props.onOpenExport} className="bg-blue-600 text-white px-4 py-1.5 rounded-lg text-sm font-medium hover:bg-blue-700 flex items-center gap-2"><Share2 size={16} /> Publier</button>
<button onClick={props.onOpenHelp} className="p-2 text-slate-400 hover:text-blue-600 rounded-full"><HelpCircle size={20} /></button>
<button onClick={() => setIsAiPanelOpen(!isAiPanelOpen)} className={`p-2 rounded-full ${isAiPanelOpen ? 'bg-indigo-100 text-indigo-600' : 'text-slate-500 hover:bg-slate-100'}`}>
{isAiPanelOpen ? <ChevronRight size={20} /> : <ChevronLeft size={20} />}
</button>
</div>
</header>
<main className="flex-1 overflow-hidden relative">
{children}
</main>
</div>
{/* AI PANEL */}
<div className={`${isAiPanelOpen ? 'w-80 lg:w-96' : 'w-0'} transition-all duration-300 flex-shrink-0 h-full border-l border-slate-200 relative`}>
{isAiPanelOpen && <AIPanel chatHistory={props.chatHistory} onSendMessage={props.onSendMessage} onInsertText={props.onInsertText} selectedText="" isGenerating={props.isGenerating} usage={user.usage} />}
</div>
</div>
);
};
export default EditorShell;