Added 404, 500 & Dashboard Page

This commit is contained in:
2026-01-20 17:46:14 +01:00
parent 5fd5f4a843
commit 88f2e6f0f9
4 changed files with 291 additions and 28 deletions

View File

@@ -12,10 +12,14 @@ export default async function DashboardPage() {
return ( return (
<DashboardLayout user={user}> <DashboardLayout user={user}>
<div className="p-6"> <div className="p-6">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-6"> <h1 className="text-3xl font-semibold text-cblack mb-1">
Tableau de bord Content de vous revoir <span className="text-dyellow">{user.name || user.email}</span>
</h1> </h1>
<p className="text-sm text-cgray mb-6">
Bienvenue sur votre tableau de bord.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> <div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2"> <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">

75
app/error.tsx Normal file
View File

@@ -0,0 +1,75 @@
'use client';
import { useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';
interface ErrorProps {
error: Error & { digest?: string };
reset: () => void;
}
export default function Error({ error, reset }: ErrorProps) {
useEffect(() => {
// Log l'erreur dans la console/terminal
console.error('Erreur serveur:', error);
}, [error]);
return (
<div className="min-h-screen flex items-center justify-center bg-cwhite">
<div className="text-center px-4">
{/* Logo */}
<div className="flex justify-center mb-8">
<Image
src="/logo.svg"
alt="MAD Logo"
width={120}
height={120}
/>
</div>
{/* 500 Content */}
<div className="max-w-md mx-auto">
<h1 className="text-9xl font-bold text-lorange mb-4">500</h1>
<h2 className="text-3xl font-semibold text-gray-900 mb-4">
Erreur serveur
</h2>
<p className="text-gray-600 mb-2">
Une erreur inattendue s'est produite sur le serveur.
</p>
{error.message && (
<p className="text-sm text-gray-500 mb-8 font-mono bg-gray-100 p-3 rounded-lg">
{error.message}
</p>
)}
{!error.message && (
<p className="text-gray-600 mb-8">
Veuillez réessayer dans quelques instants.
</p>
)}
{/* Action Buttons */}
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button
onClick={reset}
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lorange hover:bg-dorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors"
>
Réessayer
</button>
<Link
href="/dashboard"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lorange transition-colors"
>
Retour au tableau de bord
</Link>
</div>
</div>
{/* Footer */}
<div className="mt-12 text-center text-xs text-gray-500">
© 2025 MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
</div>
</div>
</div>
);
}

52
app/not-found.tsx Normal file
View File

@@ -0,0 +1,52 @@
import Link from 'next/link';
import Image from 'next/image';
export default function NotFound() {
return (
<div className="min-h-screen flex items-center justify-center bg-cwhite">
<div className="text-center px-4">
{/* Logo */}
<div className="flex justify-center mb-8">
<Image
src="/logo.svg"
alt="MAD Logo"
width={120}
height={120}
/>
</div>
{/* 404 Content */}
<div className="max-w-md mx-auto">
<h1 className="text-9xl font-bold text-lblue mb-4">404</h1>
<h2 className="text-3xl font-semibold text-gray-900 mb-4">
Page non trouvée
</h2>
<p className="text-gray-600 mb-8">
Désolé, la page que vous recherchez n'existe pas ou a é déplacée.
</p>
{/* Action Buttons */}
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
href="/dashboard"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-lg text-sm font-medium text-white bg-lblue hover:bg-dblue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors"
>
Retour au tableau de bord
</Link>
<Link
href="/login"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-lblue transition-colors"
>
Page de connexion
</Link>
</div>
</div>
{/* Footer */}
<div className="mt-12 text-center text-xs text-gray-500">
© 2025 MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
</div>
</div>
</div>
);
}

View File

@@ -1,7 +1,9 @@
'use client'; 'use client';
import { useRouter } from 'next/navigation'; import { useRouter, usePathname } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
import Image from 'next/image';
import Link from 'next/link';
interface User { interface User {
id: string; id: string;
@@ -14,10 +16,84 @@ interface DashboardLayoutProps {
children: React.ReactNode; children: React.ReactNode;
} }
interface NavItem {
label: string;
href: string;
icon: React.ReactNode;
}
export default function DashboardLayout({ user, children }: DashboardLayoutProps) { export default function DashboardLayout({ user, children }: DashboardLayoutProps) {
const router = useRouter(); const router = useRouter();
const pathname = usePathname();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const navItems: NavItem[] = [
{
label: 'Tableau de Board',
href: '/dashboard',
icon: (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V4zM8 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1H9a1 1 0 01-1-1V4zM15 3a1 1 0 00-1 1v12a1 1 0 001 1h2a1 1 0 001-1V4a1 1 0 00-1-1h-2z" />
</svg>
),
},
{
label: 'Calendrier',
href: '/dashboard/calendrier',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
),
},
{
label: 'Chauffeurs',
href: '/dashboard/chauffeurs',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0" />
</svg>
),
},
{
label: 'Adhérents',
href: '/dashboard/adherents',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
),
},
{
label: 'Univers Pro',
href: '/dashboard/univers-pro',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
),
},
{
label: 'Messagerie',
href: '/dashboard/messagerie',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
),
},
{
label: 'Factures',
href: '/dashboard/factures',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
),
},
];
const handleLogout = async () => { const handleLogout = async () => {
setLoading(true); setLoading(true);
try { try {
@@ -32,34 +108,90 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
}; };
return ( return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900"> <div className="min-h-screen bg-cwhite flex">
<nav className="bg-white dark:bg-gray-800 shadow"> {/* Sidebar */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <aside className="w-64 bg-white flex flex-col h-screen sticky top-0 border-r border-gray-200">
<div className="flex justify-between h-16"> {/* Logo Section */}
<div className="flex items-center"> <div className="py-5 m-auto">
<h1 className="text-xl font-bold text-gray-900 dark:text-white"> <div className="flex items-center gap-3">
Platform SaaS <Image
</h1> src="/logo.svg"
</div> alt="MAD Logo"
<div className="flex items-center space-x-4"> width={32}
<span className="text-sm text-gray-700 dark:text-gray-300"> height={32}
{user.name || user.email} />
</span> <div className="flex items-center gap-2">
<button <span className="font-semibold text-gray-900">Association MAD</span>
onClick={handleLogout}
disabled={loading}
className="px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-md disabled:opacity-50"
>
{loading ? 'Déconnexion...' : 'Déconnexion'}
</button>
</div> </div>
</div> </div>
</div> </div>
</nav>
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8"> {/* Navigation */}
{children} <nav className="flex-1 p-4 border-t border-gray-200">
</main> <ul className="space-y-1">
{navItems.map((item) => {
const isActive = pathname === item.href;
return (
<li key={item.href}>
<Link
href={item.href}
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
isActive
? 'bg-lblue text-white'
: 'text-gray-700 hover:bg-lblue/10'
}`}
>
<span className={isActive ? 'text-white' : 'text-lblue'}>
{item.icon}
</span>
<span className={`text-sm font-medium ${isActive ? '' : ''}`}>
{item.label}
</span>
</Link>
</li>
);
})}
</ul>
</nav>
{/* Footer */}
<div className="p-6 border-t border-gray-200">
<p className="text-xs text-gray-500 text-center">
© 2025 MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
</p>
</div>
</aside>
{/* Main Content Area */}
<div className="flex-1 flex flex-col">
{/* Top Navbar */}
<header className="bg-white border-b border-gray-200 px-6 py-4">
<div className="flex items-center justify-between">
<h1 className="text-xl font-semibold text-gray-900"></h1>
<div className="flex items-center gap-4">
{/* Notification Icon */}
<button className="relative w-10 h-10 rounded-full bg-[#6B46C1] flex items-center justify-center hover:bg-[#5B21B6] transition-colors">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 bg-red-500 rounded-full border-2 border-white"></span>
</button>
{/* Profile Avatar */}
<button className="w-10 h-10 rounded-full bg-[#6B46C1] flex items-center justify-center hover:bg-[#5B21B6] transition-colors">
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clipRule="evenodd" />
</svg>
</button>
</div>
</div>
</header>
{/* Page Content */}
<main className="flex-1 overflow-auto">
{children}
</main>
</div>
</div> </div>
); );
} }