feat: app shell avec sidebar, header et pages placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
import { Header } from '@/components/layout/Header'
|
||||||
|
export default function ImagesPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="🖼️ Images" />
|
||||||
|
<div className="p-6"><p className="text-gray-400 text-sm">Module Images — à implémenter (Plan 2)</p></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Sidebar } from '@/components/layout/Sidebar'
|
||||||
|
|
||||||
|
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-gray-50">
|
||||||
|
<Sidebar />
|
||||||
|
<main className="flex-1 flex flex-col overflow-hidden">{children}</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Header } from '@/components/layout/Header'
|
||||||
|
export default function DashboardPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="📊 Dashboard" />
|
||||||
|
<div className="p-6"><p className="text-gray-400 text-sm">Dashboard — à implémenter (Plan 4)</p></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Header } from '@/components/layout/Header'
|
||||||
|
export default function ParametresPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="⚙️ Paramètres" />
|
||||||
|
<div className="p-6"><p className="text-gray-400 text-sm">Module Paramètres — à implémenter (Plan 4)</p></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Header } from '@/components/layout/Header'
|
||||||
|
export default function ProduitsPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="📦 Produits" />
|
||||||
|
<div className="p-6"><p className="text-gray-400 text-sm">Module Produits — à implémenter (Plan 4)</p></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Header } from '@/components/layout/Header'
|
||||||
|
export default function SyncPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="🔄 Sync Sheets" />
|
||||||
|
<div className="p-6"><p className="text-gray-400 text-sm">Module Sync — à implémenter (Plan 3)</p></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<div className="p-6 text-gray-500">
|
|
||||||
Gestion — Matériaux Destock
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
interface HeaderProps {
|
||||||
|
title: string
|
||||||
|
actions?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Header({ title, actions }: HeaderProps) {
|
||||||
|
return (
|
||||||
|
<div className="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between">
|
||||||
|
<h1 className="text-base font-bold text-gray-900">{title}</h1>
|
||||||
|
{actions && <div className="flex items-center gap-3">{actions}</div>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
'use client'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { usePathname } from 'next/navigation'
|
||||||
|
import { signOut, useSession } from 'next-auth/react'
|
||||||
|
|
||||||
|
type NavItem = {
|
||||||
|
href: string
|
||||||
|
label: string
|
||||||
|
icon: string
|
||||||
|
adminOnly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const NAV_ITEMS: NavItem[] = [
|
||||||
|
{ href: '/', label: 'Dashboard', icon: '📊' },
|
||||||
|
{ href: '/produits', label: 'Produits', icon: '📦' },
|
||||||
|
{ href: '/images', label: 'Images', icon: '🖼️' },
|
||||||
|
{ href: '/sync', label: 'Sync Sheets', icon: '🔄' },
|
||||||
|
{ href: '/parametres', label: 'Paramètres', icon: '⚙️', adminOnly: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function Sidebar() {
|
||||||
|
const pathname = usePathname()
|
||||||
|
const { data: session } = useSession()
|
||||||
|
const isAdmin = session?.user?.role === 'admin'
|
||||||
|
|
||||||
|
const visible = NAV_ITEMS.filter(item => !item.adminOnly || isAdmin)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="w-48 bg-slate-900 flex flex-col h-screen sticky top-0 border-r border-slate-800">
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="px-4 py-4 border-b border-slate-800">
|
||||||
|
<span className="text-sm font-extrabold text-white tracking-tight">
|
||||||
|
Matériaux<span className="text-indigo-400">Destock</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="flex-1 py-3 overflow-y-auto">
|
||||||
|
{visible.map(item => {
|
||||||
|
const isActive =
|
||||||
|
item.href === '/' ? pathname === '/' : pathname.startsWith(item.href)
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className={`flex items-center gap-2.5 px-4 py-2 text-xs border-l-2 transition-colors ${
|
||||||
|
isActive
|
||||||
|
? 'bg-slate-800 text-white border-indigo-500 font-semibold'
|
||||||
|
: 'text-slate-400 border-transparent hover:text-slate-200 hover:bg-slate-800/50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-sm">{item.icon}</span>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* User */}
|
||||||
|
<div className="px-4 py-3 border-t border-slate-800">
|
||||||
|
<p className="text-xs text-slate-400 truncate mb-1">{session?.user?.name}</p>
|
||||||
|
<p className="text-xs text-slate-600 truncate mb-2">{session?.user?.email}</p>
|
||||||
|
<button
|
||||||
|
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||||
|
className="text-xs text-slate-500 hover:text-slate-300 transition-colors"
|
||||||
|
>
|
||||||
|
Déconnexion →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user