diff --git a/src/app/api/demo/seed/route.ts b/src/app/api/demo/seed/route.ts
new file mode 100644
index 0000000..92eaf21
--- /dev/null
+++ b/src/app/api/demo/seed/route.ts
@@ -0,0 +1,272 @@
+import { NextResponse } from 'next/server'
+import bcrypt from 'bcryptjs'
+import { ModePaiement, StatutAtelier, StatutVente } from '@prisma/client'
+import { db } from '@/lib/db'
+import { getSession } from '@/lib/auth-utils'
+
+async function requireAdmin() {
+ const session = await getSession()
+ if (!session?.user) {
+ return NextResponse.json({ error: 'Non authentifie' }, { status: 401 })
+ }
+
+ if ((session.user as any).role !== 'ADMIN') {
+ return NextResponse.json({ error: 'Acces reserve aux administrateurs' }, { status: 403 })
+ }
+
+ return null
+}
+
+function htFromTtc(ttc: number, tva = 20) {
+ return ttc / (1 + tva / 100)
+}
+
+export async function POST() {
+ const authError = await requireAdmin()
+ if (authError) return authError
+
+ try {
+ const suppliers = await Promise.all([
+ db.fournisseur.upsert({
+ where: { id: 'demo-fournisseur-lumioptic' },
+ update: {
+ nom: 'LumiOptic Distribution',
+ contact: 'Nadia Benali',
+ email: 'contact@demo-lumioptic.local',
+ telephone: '0522000001',
+ ville: 'Casablanca',
+ actif: true,
+ },
+ create: {
+ id: 'demo-fournisseur-lumioptic',
+ nom: 'LumiOptic Distribution',
+ contact: 'Nadia Benali',
+ email: 'contact@demo-lumioptic.local',
+ telephone: '0522000001',
+ ville: 'Casablanca',
+ },
+ }),
+ db.fournisseur.upsert({
+ where: { id: 'demo-fournisseur-clairverre' },
+ update: {
+ nom: 'ClairVerre Pro',
+ contact: 'Yassine El Amrani',
+ email: 'contact@demo-clairverre.local',
+ telephone: '0522000002',
+ ville: 'Rabat',
+ actif: true,
+ },
+ create: {
+ id: 'demo-fournisseur-clairverre',
+ nom: 'ClairVerre Pro',
+ contact: 'Yassine El Amrani',
+ email: 'contact@demo-clairverre.local',
+ telephone: '0522000002',
+ ville: 'Rabat',
+ },
+ }),
+ ])
+
+ const productsData = [
+ ['DEMO-MNT-001', 'Monture acetate noir Atlas', 'MONTURE', 42, 129, 12, 4, 'Atlas', 'COMPLET', suppliers[0].id],
+ ['DEMO-MNT-002', 'Monture metal fine Sofia', 'MONTURE', 38, 115, 9, 3, 'Sofia', 'COMPLET', suppliers[0].id],
+ ['DEMO-MNT-003', 'Monture enfant Flex Kids', 'MONTURE', 25, 79, 14, 5, 'Flex Kids', 'NATUREL', suppliers[0].id],
+ ['DEMO-VR-001', 'Verres simple vision anti-reflet', 'VERRE', 18, 59, 40, 10, null, null, suppliers[1].id],
+ ['DEMO-VR-002', 'Verres progressifs confort', 'VERRE', 58, 189, 18, 6, null, null, suppliers[1].id],
+ ['DEMO-VR-003', 'Verres anti-lumiere bleue', 'VERRE', 24, 89, 22, 8, null, null, suppliers[1].id],
+ ['DEMO-LEN-001', 'Lentilles mensuelles confort', 'LENTILLE', 9, 29, 30, 8, 'ClearDay', null, suppliers[1].id],
+ ['DEMO-ACC-001', 'Kit nettoyage premium', 'ACCESSOIRE', 3, 12, 50, 12, null, null, suppliers[0].id],
+ ['DEMO-ACC-002', 'Etui rigide signature', 'ACCESSOIRE', 5, 18, 35, 10, null, null, suppliers[0].id],
+ ['DEMO-ACC-003', 'Cordon lunettes sport', 'ACCESSOIRE', 2, 9, 5, 10, null, null, suppliers[0].id],
+ ] as const
+
+ const products = await Promise.all(
+ productsData.map(([reference, designation, categorie, prixAchatHT, prixVenteTTC, stock, stockMin, marque, typeMonture, fournisseurId]) =>
+ db.produit.upsert({
+ where: { reference },
+ update: {
+ designation,
+ categorie,
+ prixAchatHT,
+ prixVenteTTC,
+ stock,
+ stockMin,
+ marque,
+ typeMonture: typeMonture as any,
+ fournisseurId,
+ actif: true,
+ },
+ create: {
+ reference,
+ designation,
+ categorie,
+ prixAchatHT,
+ prixVenteTTC,
+ tva: 20,
+ stock,
+ stockMin,
+ marque,
+ typeMonture: typeMonture as any,
+ fournisseurId,
+ actif: true,
+ },
+ })
+ )
+ )
+
+ const clientsData = [
+ ['demo-client-1', 'Amina', 'Rachid', '0600000101', 'amina.rachid@demo.local', 'Casablanca'],
+ ['demo-client-2', 'Karim', 'Bennani', '0600000102', 'karim.bennani@demo.local', 'Rabat'],
+ ['demo-client-3', 'Sara', 'El Fassi', '0600000103', 'sara.elfassi@demo.local', 'Marrakech'],
+ ['demo-client-4', 'Youssef', 'Idrissi', '0600000104', null, 'Tanger'],
+ ['demo-client-5', 'Leila', 'Mansouri', '0600000105', 'leila.mansouri@demo.local', 'Fes'],
+ ] as const
+
+ const clients = await Promise.all(
+ clientsData.map(([id, prenom, nom, telephone, email, ville]) =>
+ db.client.upsert({
+ where: { telephone },
+ update: {
+ prenom,
+ nom,
+ email,
+ ville,
+ },
+ create: {
+ id,
+ prenom,
+ nom,
+ telephone,
+ email,
+ ville,
+ },
+ })
+ )
+ )
+
+ const password = await bcrypt.hash('demo123', 12)
+ const employees = await Promise.all([
+ db.employe.upsert({
+ where: { email: 'vendeur.demo@optiquestock.local' },
+ update: {
+ nom: 'Demo',
+ prenom: 'Vendeur',
+ role: 'VENDEUR',
+ actif: true,
+ },
+ create: {
+ email: 'vendeur.demo@optiquestock.local',
+ nom: 'Demo',
+ prenom: 'Vendeur',
+ role: 'VENDEUR',
+ actif: true,
+ motDePasse: password,
+ },
+ }),
+ db.employe.upsert({
+ where: { email: 'responsable.demo@optiquestock.local' },
+ update: {
+ nom: 'Demo',
+ prenom: 'Responsable',
+ role: 'RESPONSABLE',
+ actif: true,
+ },
+ create: {
+ email: 'responsable.demo@optiquestock.local',
+ nom: 'Demo',
+ prenom: 'Responsable',
+ role: 'RESPONSABLE',
+ actif: true,
+ motDePasse: password,
+ },
+ }),
+ ])
+
+ const saleSpecs = [
+ {
+ numero: 'DEMO-VENTE-001',
+ client: clients[0],
+ statutAtelier: StatutAtelier.EN_COURS,
+ items: [products[0], products[3], products[7]],
+ payment: ModePaiement.CARTE,
+ },
+ {
+ numero: 'DEMO-VENTE-002',
+ client: clients[1],
+ statutAtelier: StatutAtelier.PRET,
+ items: [products[1], products[4]],
+ payment: ModePaiement.ESPECES,
+ },
+ {
+ numero: 'DEMO-VENTE-003',
+ client: clients[2],
+ statutAtelier: StatutAtelier.EN_ATTENTE,
+ items: [products[2], products[5]],
+ payment: ModePaiement.CHEQUE,
+ },
+ ]
+
+ let salesCreated = 0
+ for (const spec of saleSpecs) {
+ const existingSale = await db.vente.findUnique({ where: { numero: spec.numero } })
+ if (existingSale) continue
+
+ const montantTTC = spec.items.reduce((sum, product) => sum + product.prixVenteTTC, 0)
+ const montantHT = spec.items.reduce((sum, product) => sum + htFromTtc(product.prixVenteTTC), 0)
+
+ await db.vente.create({
+ data: {
+ numero: spec.numero,
+ clientId: spec.client.id,
+ employeId: employees[0].id,
+ statut: StatutVente.PAYEE,
+ statutAtelier: spec.statutAtelier,
+ montantHT,
+ montantTVA: montantTTC - montantHT,
+ montantTTC,
+ notes: 'Vente demo generee automatiquement',
+ dateAtelier: new Date(),
+ lignes: {
+ create: spec.items.map((product) => ({
+ produitId: product.id,
+ quantite: 1,
+ prixUnitaireHT: htFromTtc(product.prixVenteTTC),
+ prixUnitaireTTC: product.prixVenteTTC,
+ remise: 0,
+ montantHT: htFromTtc(product.prixVenteTTC),
+ montantTTC: product.prixVenteTTC,
+ })),
+ },
+ paiements: {
+ create: {
+ mode: spec.payment,
+ montant: montantTTC,
+ employeId: employees[0].id,
+ reference: spec.numero,
+ },
+ },
+ },
+ })
+ salesCreated += 1
+ }
+
+ return NextResponse.json({
+ message: 'Demo data ready',
+ clients: clients.length,
+ products: products.length,
+ suppliers: suppliers.length,
+ employees: employees.length,
+ salesCreated,
+ demoLogins: [
+ { email: 'vendeur.demo@optiquestock.local', password: 'demo123' },
+ { email: 'responsable.demo@optiquestock.local', password: 'demo123' },
+ ],
+ })
+ } catch (error) {
+ console.error('Demo seed error:', error)
+ return NextResponse.json(
+ { error: error instanceof Error ? error.message : 'Failed to seed demo data' },
+ { status: 500 }
+ )
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e15fdd7..1492db9 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,7 @@ import "./globals.css";
import { Toaster } from "@/components/ui/toaster";
import SessionProvider from "@/components/auth/SessionProvider";
import { ThemeProvider } from "@/components/theme-provider";
+import { CurrencyProvider } from "@/components/currency-provider";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -46,9 +47,11 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background text-foreground`}
>
-
- {children}
-
+
+
+ {children}
+
+