Files
plume/App.tsx

156 lines
5.1 KiB
TypeScript

import React, { useState } from 'react';
import { ViewMode } from './types';
import { useProjects, useChat } from './hooks';
import { AuthProvider, useAuthContext } from './AuthContext';
import AppRouter from './components/AppRouter';
import { Loader2, BookOpen } from 'lucide-react';
/**
* MainContent : Ce composant est "sous" le AuthProvider.
* Il peut donc utiliser useAuthContext() sans recevoir de 'null'.
*/
const MainContent: React.FC = () => {
const [viewMode, setViewMode] = useState<ViewMode>('landing');
// --- 1. DÉCLARATION DE TOUS LES HOOKS (IMPÉRATIF : TOUJOURS EN HAUT) ---
// On récupère l'état global partagé via le Contexte
const {
user,
login,
signup,
logout,
incrementUsage,
loading: authLoading
} = useAuthContext();
// On initialise les projets. Si user est null, useProjects retournera une liste vide.
const {
projects, currentProjectId, setCurrentProjectId,
createProject, updateProject, updateChapter, addChapter,
createEntity, updateEntity, deleteEntity
} = useProjects(user);
// On initialise le chat
const { chatHistory, isGenerating, sendMessage } = useChat();
// --- 2. LOGIQUE DE CALCUL ---
const currentProject = projects.find(p => p.id === currentProjectId);
const handleSendMessage = (msg: string) => {
if (currentProject && user) {
sendMessage(currentProject, 'global', msg, user, incrementUsage);
}
};
// --- 3. RENDU CONDITIONNEL (Seulement APRES les hooks) ---
// On attend que l'Auth + Profil soient chargés (tes 2 secondes d'attente)
if (authLoading) {
return (
<div className="h-screen w-full flex flex-col items-center justify-center bg-slate-900 text-white">
<div className="relative mb-8">
<Loader2 className="animate-spin text-blue-500" size={56} />
<div className="absolute inset-0 blur-2xl bg-blue-500/30 animate-pulse rounded-full" />
</div>
<div className="flex items-center gap-2 mb-2">
<BookOpen className="text-blue-500" size={24} />
<span className="text-2xl font-black tracking-tighter">PlumeIA</span>
</div>
<p className="text-slate-400 font-medium animate-pulse tracking-wide">
Synchronisation de votre espace créatif...
</p>
</div>
);
}
// --- 4. RENDU FINAL : AppRouter ---
return (
<AppRouter
// État utilisateur
user={user}
viewMode={viewMode}
onViewModeChange={setViewMode}
// Actions Auth
onLogin={login}
onSignup={signup}
onLogout={() => {
logout();
setViewMode('landing');
}}
// Gestion des Projets
projects={projects}
currentProjectId={currentProjectId}
onSelectProject={(id) => {
setCurrentProjectId(id);
setViewMode('write');
}}
onCreateProject={async () => {
const id = await createProject();
if (id) {
setCurrentProjectId(id);
setViewMode('write');
}
}}
onUpdateProject={(updates) => {
if (currentProjectId) updateProject(currentProjectId, updates);
}}
// Gestion du contenu (Chapitres & Entités)
onUpdateChapter={(chapterId, data) => {
if (currentProjectId) updateChapter(currentProjectId, chapterId, data);
}}
onAddChapter={async () => {
if (currentProjectId) {
await addChapter(currentProjectId, {});
// Fetch the latest chapter or return ID if addChapter returns it
// verified: addChapter doesn't return ID in hooks.ts yet, let's assume it does or we reload
// Actually, let's fix hooks.ts to return ID for addChapter too, or just reload.
// For now, let's assume standard flow.
// Wait, AppRouter expects a return string | null.
// We should update hooks.ts addChapter to return ID as well.
// For this step, I'll implement the call.
return null; // temporary until hooks.ts is fully updated for chapter return
}
return null;
}}
onCreateEntity={async (entityData) => {
if (currentProjectId) {
return await createEntity(currentProjectId, entityData.type, entityData);
}
return null;
}}
onUpdateEntity={(entityId, data) => {
if (currentProjectId) updateEntity(currentProjectId, entityId, data);
}}
onDeleteEntity={(entityId) => {
if (currentProjectId) deleteEntity(currentProjectId, entityId);
}}
// Chat & IA
chatHistory={chatHistory}
isGenerating={isGenerating}
onSendMessage={handleSendMessage}
onIncrementUsage={incrementUsage}
// Callbacks divers
onUpdateProfile={() => console.log("Profil géré via useAuth")}
onUpgradePlan={() => window.open('/billing', '_blank')}
/>
);
};
/**
* Composant App : Point d'entrée de l'application.
* Il installe la "bulle" AuthProvider pour que tout le monde y ait accès.
*/
const App: React.FC = () => {
return (
<AuthProvider>
<MainContent />
</AuthProvider>
);
};
// EXPORT PAR DÉFAUT (Essentiel pour index.tsx)
export default App;