Add tablet seller wizard and access controls
This commit is contained in:
375
src/app/page.tsx
375
src/app/page.tsx
@@ -1,23 +1,25 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useSession, signOut } from 'next-auth/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { signOut, useSession } from 'next-auth/react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import {
|
||||
Users,
|
||||
Package,
|
||||
Truck,
|
||||
ShoppingCart,
|
||||
FileText,
|
||||
BarChart3,
|
||||
BrainCircuit,
|
||||
Eye,
|
||||
LayoutDashboard,
|
||||
Wrench,
|
||||
LogOut,
|
||||
User
|
||||
Package,
|
||||
PanelTop,
|
||||
ShoppingCart,
|
||||
Truck,
|
||||
User,
|
||||
UserCog,
|
||||
Users,
|
||||
Wrench,
|
||||
} from 'lucide-react'
|
||||
import POSModule from '@/components/pos/POSModule'
|
||||
import { ProduitListe } from '@/components/products/ProduitListe'
|
||||
@@ -26,8 +28,24 @@ import AtelierModule from '@/components/atelier/AtelierModule'
|
||||
import { SupplierList } from '@/components/suppliers/SupplierList'
|
||||
import PurchaseModule from '@/components/purchases/PurchaseModule'
|
||||
import ReportsModule from '@/components/reports/ReportsModule'
|
||||
import { EmployeeManagement } from '@/components/employees/EmployeeManagement'
|
||||
import { AIAssistant } from '@/components/ai/AIAssistant'
|
||||
import { ThemeToggle } from '@/components/theme-toggle'
|
||||
import { SellerWizard } from '@/components/seller-wizard/SellerWizard'
|
||||
|
||||
type Module = 'HOME' | 'CLIENTS' | 'PRODUITS' | 'FOURNISSEURS' | 'ACHATS' | 'VENTE' | 'RAPPORTS' | 'ATELIER'
|
||||
type RoleEmploye = 'VENDEUR' | 'RESPONSABLE' | 'ADMIN'
|
||||
type Module =
|
||||
| 'HOME'
|
||||
| 'CLIENTS'
|
||||
| 'PRODUITS'
|
||||
| 'FOURNISSEURS'
|
||||
| 'ACHATS'
|
||||
| 'VENTE'
|
||||
| 'RAPPORTS'
|
||||
| 'ATELIER'
|
||||
| 'UTILISATEURS'
|
||||
| 'IA'
|
||||
| 'VENDEUR_WIZARD'
|
||||
|
||||
interface ModuleCard {
|
||||
id: Module
|
||||
@@ -36,15 +54,26 @@ interface ModuleCard {
|
||||
icon: React.ReactNode
|
||||
badge?: string
|
||||
color: string
|
||||
roles: RoleEmploye[]
|
||||
}
|
||||
|
||||
const modules: ModuleCard[] = [
|
||||
{
|
||||
id: 'VENDEUR_WIZARD',
|
||||
title: 'Vendeur Express',
|
||||
description: 'Parcours rapide client, service, recu',
|
||||
icon: <PanelTop className="h-8 w-8" />,
|
||||
badge: 'Tablette',
|
||||
color: 'bg-rose-500',
|
||||
roles: ['VENDEUR', 'RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'CLIENTS',
|
||||
title: 'Gestion Clients',
|
||||
description: 'Fiches clients, mesures de vision, ordonnances',
|
||||
icon: <Users className="h-8 w-8" />,
|
||||
color: 'bg-blue-500'
|
||||
color: 'bg-blue-500',
|
||||
roles: ['VENDEUR', 'RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'PRODUITS',
|
||||
@@ -52,21 +81,24 @@ const modules: ModuleCard[] = [
|
||||
description: 'Catalogue, stock, images, QR codes',
|
||||
icon: <Package className="h-8 w-8" />,
|
||||
badge: 'Alertes',
|
||||
color: 'bg-emerald-500'
|
||||
color: 'bg-emerald-500',
|
||||
roles: ['VENDEUR', 'RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'FOURNISSEURS',
|
||||
title: 'Fournisseurs',
|
||||
description: 'Gestion des fournisseurs et contacts',
|
||||
icon: <Truck className="h-8 w-8" />,
|
||||
color: 'bg-orange-500'
|
||||
color: 'bg-orange-500',
|
||||
roles: ['RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'ACHATS',
|
||||
title: 'Achats & Stock',
|
||||
description: 'Réception, factures fournisseurs, entrées stock',
|
||||
description: 'Reception, factures fournisseurs, entrees stock',
|
||||
icon: <ShoppingCart className="h-8 w-8" />,
|
||||
color: 'bg-purple-500'
|
||||
color: 'bg-purple-500',
|
||||
roles: ['RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'VENTE',
|
||||
@@ -74,7 +106,8 @@ const modules: ModuleCard[] = [
|
||||
description: 'Encaissement, facturation, POS',
|
||||
icon: <ShoppingCart className="h-8 w-8" />,
|
||||
badge: 'Actif',
|
||||
color: 'bg-green-500'
|
||||
color: 'bg-green-500',
|
||||
roles: ['VENDEUR', 'RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'ATELIER',
|
||||
@@ -82,48 +115,107 @@ const modules: ModuleCard[] = [
|
||||
description: 'Montage de lunettes, commandes en cours',
|
||||
icon: <Wrench className="h-8 w-8" />,
|
||||
badge: 'En cours',
|
||||
color: 'bg-amber-500'
|
||||
color: 'bg-amber-500',
|
||||
roles: ['VENDEUR', 'RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'RAPPORTS',
|
||||
title: 'Rapports',
|
||||
description: 'Statistiques, exports Excel/CSV/PDF',
|
||||
icon: <BarChart3 className="h-8 w-8" />,
|
||||
color: 'bg-cyan-500'
|
||||
}
|
||||
color: 'bg-cyan-500',
|
||||
roles: ['RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'UTILISATEURS',
|
||||
title: 'Utilisateurs',
|
||||
description: 'Employes, roles et niveaux d acces',
|
||||
icon: <UserCog className="h-8 w-8" />,
|
||||
color: 'bg-indigo-500',
|
||||
roles: ['ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'IA',
|
||||
title: 'Assistant IA',
|
||||
description: 'Conseils, priorites et aide a la decision',
|
||||
icon: <BrainCircuit className="h-8 w-8" />,
|
||||
badge: 'Nouveau',
|
||||
color: 'bg-sky-500',
|
||||
roles: ['RESPONSABLE', 'ADMIN'],
|
||||
},
|
||||
]
|
||||
|
||||
export default function Home() {
|
||||
const { data: session } = useSession()
|
||||
const { data: session, status } = useSession()
|
||||
const router = useRouter()
|
||||
const [currentModule, setCurrentModule] = useState<Module>('HOME')
|
||||
|
||||
if (!session) {
|
||||
router.push('/login')
|
||||
return null
|
||||
const currentRole = ((session?.user as any)?.role || 'VENDEUR') as RoleEmploye
|
||||
const visibleModules = modules.filter((module) => module.roles.includes(currentRole))
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'unauthenticated') {
|
||||
router.push('/login')
|
||||
}
|
||||
}, [status, router])
|
||||
|
||||
useEffect(() => {
|
||||
const current = modules.find((module) => module.id === currentModule)
|
||||
if (current && !current.roles.includes(currentRole)) {
|
||||
setCurrentModule('HOME')
|
||||
}
|
||||
}, [currentModule, currentRole])
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background">
|
||||
<p className="text-muted-foreground">Chargement...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'unauthenticated') return null
|
||||
|
||||
const moduleInfo = modules.find((module) => module.id === currentModule)
|
||||
|
||||
const moduleHeader = (
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour a l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 rounded-lg p-4 ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderModule = () => {
|
||||
if (currentModule === 'HOME') {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center space-y-2">
|
||||
<h1 className="text-4xl font-bold text-gray-900">OptiqueStock</h1>
|
||||
<p className="text-lg text-gray-600">Système de Gestion de Magasin d'Optique</p>
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-4xl font-bold text-foreground">OptiqueStock</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Systeme de Gestion de Magasin d'Optique
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{modules.map((module) => (
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{visibleModules.map((module) => (
|
||||
<Card
|
||||
key={module.id}
|
||||
className="group cursor-pointer transition-all duration-200 hover:shadow-lg hover:scale-105 border-2 hover:border-primary"
|
||||
className="group cursor-pointer border-2 transition-all duration-200 hover:scale-105 hover:border-primary hover:shadow-lg"
|
||||
onClick={() => setCurrentModule(module.id)}
|
||||
>
|
||||
<CardHeader>
|
||||
<div className={`flex items-center justify-between mb-2`}>
|
||||
<div className={`p-3 rounded-lg ${module.color} text-white`}>
|
||||
{module.icon}
|
||||
</div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div className={`rounded-lg p-3 ${module.color} text-white`}>{module.icon}</div>
|
||||
{module.badge && (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{module.badge}
|
||||
@@ -131,9 +223,7 @@ export default function Home() {
|
||||
)}
|
||||
</div>
|
||||
<CardTitle className="text-lg">{module.title}</CardTitle>
|
||||
<CardDescription className="text-sm">
|
||||
{module.description}
|
||||
</CardDescription>
|
||||
<CardDescription className="text-sm">{module.description}</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
))}
|
||||
@@ -142,231 +232,125 @@ export default function Home() {
|
||||
)
|
||||
}
|
||||
|
||||
const moduleInfo = modules.find(m => m.id === currentModule)
|
||||
|
||||
// Render Client Management module if selected
|
||||
if (currentModule === 'CLIENTS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<ClientList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render POS module if selected
|
||||
if (currentModule === 'VENTE') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<POSModule />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render Products module if selected
|
||||
if (currentModule === 'PRODUITS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<ProduitListe />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render Atelier module if selected
|
||||
if (currentModule === 'ATELIER') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<AtelierModule />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render Suppliers module if selected
|
||||
if (currentModule === 'FOURNISSEURS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<SupplierList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render Purchases module if selected
|
||||
if (currentModule === 'ACHATS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<PurchaseModule />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render Reports module if selected
|
||||
if (currentModule === 'RAPPORTS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
</div>
|
||||
{moduleHeader}
|
||||
<ReportsModule />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentModule('HOME')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${moduleInfo?.color} text-white`}>
|
||||
{moduleInfo?.icon}
|
||||
<h2 className="text-2xl font-bold">{moduleInfo?.title}</h2>
|
||||
</div>
|
||||
if (currentModule === 'UTILISATEURS') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{moduleHeader}
|
||||
<EmployeeManagement />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<Card className="border-2 border-dashed">
|
||||
<CardContent className="flex flex-col items-center justify-center py-16">
|
||||
<div className={`p-6 rounded-full ${moduleInfo?.color} bg-opacity-10 mb-4`}>
|
||||
<Eye className="h-16 w-16 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-700 mb-2">
|
||||
Module en développement
|
||||
</h3>
|
||||
<p className="text-gray-500 text-center max-w-md">
|
||||
Le module <strong>{moduleInfo?.title}</strong> est actuellement en cours de développement.
|
||||
Veuillez revenir ultérieurement.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
if (currentModule === 'IA') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{moduleHeader}
|
||||
<AIAssistant />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (currentModule === 'VENDEUR_WIZARD') {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{moduleHeader}
|
||||
<SellerWizard />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
|
||||
<header className="bg-white border-b shadow-sm">
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<header className="border-b bg-card shadow-sm">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-primary rounded-lg">
|
||||
<div className="rounded-lg bg-primary p-2">
|
||||
<Eye className="h-6 w-6 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-gray-900">OptiqueStock</h1>
|
||||
<p className="text-xs text-gray-500">Gestion de Magasin d'Optique</p>
|
||||
<h1 className="text-xl font-bold text-foreground">OptiqueStock</h1>
|
||||
<p className="text-xs text-muted-foreground">Gestion de Magasin d'Optique</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Badge variant="outline" className="text-sm">
|
||||
v1.0.0
|
||||
</Badge>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<div className="flex items-center gap-3">
|
||||
<ThemeToggle />
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<User className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">{session?.user?.name}</span>
|
||||
<Badge variant="secondary">{currentRole}</Badge>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||
title="Se déconnecter"
|
||||
onClick={() => signOut({ redirect: false }).then(() => router.replace('/login'))}
|
||||
title="Se deconnecter"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -375,19 +359,12 @@ export default function Home() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
{renderModule()}
|
||||
</main>
|
||||
<main className="container mx-auto px-4 py-8">{renderModule()}</main>
|
||||
|
||||
<footer className="bg-white border-t mt-auto">
|
||||
<footer className="mt-auto border-t bg-card">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
<p className="text-sm text-gray-500">
|
||||
© 2024 OptiqueStock. Tous droits réservés.
|
||||
</p>
|
||||
<div className="flex items-center gap-4 text-sm text-gray-500">
|
||||
<span>Support: support@optiquestock.com</span>
|
||||
<span>•</span>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<span>Version 1.0.0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user