156 lines
5.1 KiB
TypeScript
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; |