optimisation de la latence des pages ajout d'option clique droit plus paragraphe
170 lines
12 KiB
TypeScript
170 lines
12 KiB
TypeScript
'use client';
|
|
|
|
|
|
import React from 'react';
|
|
import { BookProject, UserProfile } from '@/lib/types';
|
|
import { Plus, Book, Clock, Star, ChevronRight, LogOut, LayoutDashboard, User, Target, Flame, Edit3 } from 'lucide-react';
|
|
import { useLanguage } from '@/providers/LanguageProvider';
|
|
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
|
|
|
interface DashboardProps {
|
|
user: UserProfile;
|
|
projects: BookProject[];
|
|
onSelect: (id: string) => void;
|
|
onCreate: () => void;
|
|
onLogout: () => void;
|
|
onPricing: () => void;
|
|
onProfile: () => void;
|
|
}
|
|
|
|
const Dashboard: React.FC<DashboardProps> = ({ user, projects, onSelect, onCreate, onLogout, onPricing, onProfile }) => {
|
|
const { t } = useLanguage();
|
|
|
|
return (
|
|
<div className="min-h-screen bg-theme-bg p-4 md:p-8 font-sans transition-colors duration-300">
|
|
<div className="max-w-6xl mx-auto space-y-8">
|
|
|
|
{/* User Card */}
|
|
<div className="flex flex-col md:flex-row justify-between items-center bg-theme-panel p-6 md:p-8 rounded-[2rem] shadow-sm border border-theme-border gap-6">
|
|
<div className="flex items-center gap-6">
|
|
<div className="relative">
|
|
{user.avatar ? (
|
|
<img src={user.avatar} className="w-20 h-20 rounded-full border-4 border-slate-50 shadow-lg object-cover" alt="Avatar" />
|
|
) : (
|
|
<div className="w-20 h-20 rounded-full border-4 border-slate-50 shadow-lg bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center text-white text-2xl font-black">
|
|
{user.name?.charAt(0)?.toUpperCase() || '?'}
|
|
</div>
|
|
)}
|
|
<div className="absolute -bottom-1 -right-1 bg-green-500 w-5 h-5 rounded-full border-4 border-white" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-3xl font-black text-theme-text">{t('dashboard.hello')}, {user.name} 👋</h2>
|
|
<div className="flex items-center gap-3 mt-1">
|
|
<span className="px-3 py-1 rounded-full bg-indigo-100 text-indigo-700 text-[10px] uppercase font-black tracking-widest">{user.subscription.planDetails?.displayName || user.subscription.plan}</span>
|
|
<span className="text-theme-muted text-xs font-medium">{t('dashboard.member_since')} 24 janv.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<LanguageSwitcher />
|
|
<button onClick={onProfile} data-umami-event="Go To Profile" className="bg-theme-bg text-theme-text px-4 md:px-5 py-2 md:py-2.5 rounded-xl text-xs md:text-sm font-bold hover:opacity-80 transition-all flex items-center gap-2 border border-theme-border">
|
|
<User size={18} /> {t('dashboard.my_profile')}
|
|
</button>
|
|
<button onClick={onLogout} data-umami-event="Logout" title={t('sidebar.logout')} className="p-3 text-theme-muted hover:text-red-500 rounded-full hover:bg-red-500/10 transition-colors"><LogOut size={20} /></button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats Section */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div className="bg-theme-panel p-4 sm:p-6 rounded-3xl shadow-sm border border-theme-border flex items-center gap-4">
|
|
<div className="bg-orange-100 p-3 rounded-2xl text-orange-600"><Flame size={24} /></div>
|
|
<div>
|
|
<p className="text-xs font-bold text-theme-muted uppercase tracking-wider">{t('dashboard.streak')}</p>
|
|
<p className="text-2xl font-black text-theme-text">{user.stats.writingStreak} {t('dashboard.days')}</p>
|
|
</div>
|
|
</div>
|
|
<div className="bg-theme-panel p-4 sm:p-6 rounded-3xl shadow-sm border border-theme-border flex items-center gap-4">
|
|
<div className="bg-blue-100 p-3 rounded-2xl text-blue-600"><Edit3 size={24} /></div>
|
|
<div>
|
|
<p className="text-xs font-bold text-theme-muted uppercase tracking-wider">{t('dashboard.words_written')}</p>
|
|
<p className="text-2xl font-black text-theme-text">{user.stats.totalWordsWritten.toLocaleString()}</p>
|
|
</div>
|
|
</div>
|
|
<div className="bg-theme-panel p-4 sm:p-6 rounded-3xl shadow-sm border border-theme-border flex items-center gap-4">
|
|
<div className="bg-indigo-100 p-3 rounded-2xl text-indigo-600"><Target size={24} /></div>
|
|
<div>
|
|
<p className="text-xs font-bold text-theme-muted uppercase tracking-wider">{t('dashboard.daily_goal')}</p>
|
|
<p className="text-2xl font-black text-theme-text">{user.stats.dailyWordCount} / {user.preferences.dailyWordGoal} {t('dashboard.words')}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
{/* Project List */}
|
|
<div className="lg:col-span-2 space-y-4">
|
|
<div className="flex justify-between items-center mb-6">
|
|
<h3 className="text-2xl font-black text-theme-text">{t('dashboard.my_novels')}</h3>
|
|
<button
|
|
onClick={onCreate}
|
|
data-umami-event="Create Project"
|
|
className="flex items-center gap-2 bg-blue-600 text-white px-6 py-3 rounded-2xl font-bold hover:bg-blue-700 transition-all shadow-xl shadow-blue-200"
|
|
>
|
|
<Plus size={20} /> {t('dashboard.write_new')}
|
|
</button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{projects.map(p => (
|
|
<div
|
|
key={p.id}
|
|
onClick={() => onSelect(p.id)}
|
|
data-umami-event="Open Project"
|
|
className="bg-theme-panel p-6 md:p-8 rounded-[2.5rem] border border-theme-border shadow-sm hover:shadow-2xl hover:scale-[1.02] transition-all cursor-pointer group flex flex-col justify-between h-64"
|
|
>
|
|
<div>
|
|
<div className="bg-blue-500/10 w-12 h-12 rounded-2xl flex items-center justify-center text-blue-500 mb-6 group-hover:bg-blue-600 group-hover:text-white transition-colors">
|
|
<Book size={24} />
|
|
</div>
|
|
<h4 className="font-black text-theme-text text-xl truncate mb-1">{p.title}</h4>
|
|
<p className="text-theme-muted text-sm">{t('dashboard.last_modified')} : {new Date(p.lastModified).toLocaleDateString('fr-FR')}</p>
|
|
</div>
|
|
<div className="flex justify-between items-center text-[10px] text-theme-muted font-black uppercase tracking-widest border-t border-theme-border pt-4 mt-auto">
|
|
<span>{p.chapters.length} {t('nav.chapters')}</span>
|
|
<ChevronRight size={20} className="group-hover:text-blue-600 transition-transform group-hover:translate-x-1 duration-300" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
{projects.length === 0 && (
|
|
<div className="col-span-2 py-24 bg-theme-panel rounded-[3rem] border-2 border-dashed border-theme-border flex flex-col items-center justify-center text-theme-muted">
|
|
<Book size={64} className="mb-6 opacity-20" />
|
|
<p className="font-bold text-lg">{t('dashboard.empty_projects')}</p>
|
|
<button onClick={onCreate} className="mt-4 text-blue-600 font-bold hover:underline">{t('dashboard.create_now')}</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sidebar Stats & Plan */}
|
|
<div className="space-y-6">
|
|
<div className="bg-slate-900 text-white p-6 md:p-8 rounded-[2.5rem] shadow-xl relative overflow-hidden">
|
|
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/20 blur-[60px] -z-1" />
|
|
<h3 className="font-black text-xl mb-6 flex items-center gap-2"><Star size={20} className="text-yellow-400" /> {t('dashboard.usage')}</h3>
|
|
<div className="space-y-8">
|
|
<div>
|
|
<div className="flex justify-between text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2">
|
|
<span>{t('sidebar.ai_actions')}</span>
|
|
<span>{user.usage.aiActionsCurrent} / {user.usage.aiActionsLimit === 999999 ? '∞' : user.usage.aiActionsLimit}</span>
|
|
</div>
|
|
<div className="h-3 w-full bg-slate-800 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-blue-500 transition-all duration-1000 shadow-[0_0_10px_rgba(59,130,246,0.5)]"
|
|
style={{ width: `${Math.min(100, (user.usage.aiActionsCurrent / user.usage.aiActionsLimit) * 100)}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="flex justify-between text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2">
|
|
<span>{t('dashboard.novel_slots')}</span>
|
|
<span>{projects.length} / {user.usage.projectsLimit}</span>
|
|
</div>
|
|
<div className="h-3 w-full bg-slate-800 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-indigo-500 transition-all duration-1000 shadow-[0_0_10px_rgba(99,102,241,0.5)]"
|
|
style={{ width: `${Math.min(100, (projects.length / user.usage.projectsLimit) * 100)}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button onClick={onPricing} data-umami-event="Pricing Click" className="w-full mt-10 bg-white/10 hover:bg-white/20 py-4 rounded-2xl text-sm font-bold transition-all">
|
|
{t('dashboard.upgrade_plan')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Dashboard;
|