'use client' import { useState, useEffect } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { ScrollArea } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Wrench, Clock, Play, CheckCircle2, Bell, User, Package, Eye, Calendar, FileText, AlertCircle, CheckCheck } from 'lucide-react' import { toast } from '@/hooks/use-toast' // Types interface Produit { id: string reference: string designation: string categorie: string marque?: string typeMonture?: string typeVerre?: string materiau?: string couleur?: string } interface Client { id: string nom: string prenom: string email?: string telephone: string } interface Patient { id: string odSphere?: number odCylindre?: number odAxe?: number ogSphere?: number ogCylindre?: number ogAxe?: number addition?: number pd?: number hauteur?: number } interface LigneVente { id: string produit: Produit quantite: number montantHT: number montantTTC: number } interface WorkOrder { id: string numero: string date: string statutAtelier: 'EN_ATTENTE' | 'EN_COURS' | 'TERMINE' | 'PRET' | 'RETIRE' montantTTC: number client?: Client lignes: LigneVente[] patients?: Patient[] dateAtelier?: string dateRetrait?: string notes?: string } type StatusFilter = 'ALL' | 'EN_ATTENTE' | 'EN_COURS' | 'TERMINE' | 'PRET' | 'RETIRE' export default function AtelierModule() { const [workOrders, setWorkOrders] = useState([]) const [selectedOrder, setSelectedOrder] = useState(null) const [statusFilter, setStatusFilter] = useState('ALL') const [loading, setLoading] = useState(false) const [showDetailDialog, setShowDetailDialog] = useState(false) const [showNotifyDialog, setShowNotifyDialog] = useState(false) const [activeTab, setActiveTab] = useState<'orders' | 'ready' | 'history'>('orders') // Load work orders const loadWorkOrders = async () => { setLoading(true) try { const response = await fetch('/api/atelier/orders?XTransformPort=3000') if (response.ok) { const data = await response.json() setWorkOrders(data) } else { toast({ title: 'Erreur', description: 'Impossible de charger les commandes atelier', variant: 'destructive' }) } } catch (error) { console.error('Error loading work orders:', error) toast({ title: 'Erreur', description: 'Impossible de charger les commandes atelier', variant: 'destructive' }) } finally { setLoading(false) } } // Update order status const updateOrderStatus = async (orderId: string, newStatus: string) => { console.log('Frontend: updateOrderStatus called') console.log('Frontend: orderId =', orderId) console.log('Frontend: newStatus =', newStatus) try { const response = await fetch(`/api/atelier/orders/${orderId}?XTransformPort=3000`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ statutAtelier: newStatus }) }) console.log('Frontend: response.ok =', response.ok) console.log('Frontend: response.status =', response.status) if (response.ok) { toast({ title: 'Statut mis à jour', description: 'Le statut de la commande a été mis à jour' }) loadWorkOrders() if (selectedOrder?.id === orderId) { setSelectedOrder({ ...selectedOrder, statutAtelier: newStatus as any }) } } else { const errorData = await response.json() console.log('Frontend: errorData =', errorData) toast({ title: 'Erreur', description: errorData.error || 'Impossible de mettre à jour le statut', variant: 'destructive' }) } } catch (error) { console.error('Error updating order status:', error) toast({ title: 'Erreur', description: 'Impossible de mettre à jour le statut', variant: 'destructive' }) } } // View order details const viewOrderDetails = async (order: WorkOrder) => { setSelectedOrder(order) setShowDetailDialog(true) } // Mark as ready for pickup const markAsReady = async () => { if (!selectedOrder) return try { const response = await fetch(`/api/atelier/orders/${selectedOrder.id}?XTransformPort=3000`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ statutAtelier: 'PRET' }) }) if (response.ok) { toast({ title: 'Commande prête', description: 'La commande est marquée comme prête pour le retrait' }) loadWorkOrders() setShowNotifyDialog(false) setShowDetailDialog(false) } else { toast({ title: 'Erreur', description: 'Impossible de marquer la commande comme prête', variant: 'destructive' }) } } catch (error) { console.error('Error marking order as ready:', error) toast({ title: 'Erreur', description: 'Impossible de marquer la commande comme prête', variant: 'destructive' }) } } // Confirm order pickup (ready → retrieved) const confirmRetrait = async (orderId: string) => { if (!confirm('Confirmer que le client a récupéré ses lunettes ?')) { return } try { const response = await fetch(`/api/atelier/orders/${orderId}?XTransformPort=3000`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ statutAtelier: 'RETIRE' }) }) if (response.ok) { toast({ title: 'Retrait confirmé', description: 'La commande a été marquée comme retirée' }) loadWorkOrders() } else { toast({ title: 'Erreur', description: 'Impossible de confirmer le retrait', variant: 'destructive' }) } } catch (error) { console.error('Error confirming pickup:', error) toast({ title: 'Erreur', description: 'Impossible de confirmer le retrait', variant: 'destructive' }) } } // Seed sample data const seedSampleData = async () => { try { const response = await fetch('/api/atelier/seed?XTransformPort=3000', { method: 'POST' }) if (response.ok) { toast({ title: 'Données ajoutées', description: 'Les données de test ont été créées avec succès' }) loadWorkOrders() } else { const error = await response.json() toast({ title: 'Erreur', description: error.error || 'Impossible de créer les données de test', variant: 'destructive' }) } } catch (error) { console.error('Error seeding data:', error) toast({ title: 'Erreur', description: 'Impossible de créer les données de test', variant: 'destructive' }) } } useEffect(() => { loadWorkOrders() }, []) // Filter orders const filteredOrders = workOrders.filter(order => { if (statusFilter === 'ALL') return true return order.statutAtelier === statusFilter }) const readyOrders = workOrders.filter(order => order.statutAtelier === 'PRET') // Get status badge const getStatusBadge = (statut: string) => { const variants: Record = { 'EN_ATTENTE': { color: 'bg-gray-500', label: 'En attente', icon: }, 'EN_COURS': { color: 'bg-blue-500', label: 'En cours', icon: }, 'TERMINE': { color: 'bg-purple-500', label: 'Terminé', icon: }, 'PRET': { color: 'bg-green-500', label: 'Prêt', icon: }, 'RETIRE': { color: 'bg-orange-500', label: 'Retiré', icon: } } const status = variants[statut] || variants['EN_ATTENTE'] return ( {status.icon} {status.label} ) } // Format vision data const formatVisionData = (patient?: Patient) => { if (!patient) return null return { od: `Sph: ${patient.odSphere || '-'} | Cyl: ${patient.odCylindre || '-'} | Axe: ${patient.odAxe || '-'}`, og: `Sph: ${patient.ogSphere || '-'} | Cyl: ${patient.ogCylindre || '-'} | Axe: ${patient.ogAxe || '-'}`, addition: patient.addition ? `Add: ${patient.addition}` : null, pd: patient.pd ? `PD: ${patient.pd}mm` : null, hauteur: patient.hauteur ? `Haut: ${patient.hauteur}mm` : null } } return (
{/* Statistics Cards */}
En attente
{workOrders.filter(o => o.statutAtelier === 'EN_ATTENTE').length}
En cours
{workOrders.filter(o => o.statutAtelier === 'EN_COURS').length}
Terminé
{workOrders.filter(o => o.statutAtelier === 'TERMINE').length}
Prêt à retirer
{workOrders.filter(o => o.statutAtelier === 'PRET').length}
Retiré
{workOrders.filter(o => o.statutAtelier === 'RETIRE').length}
{/* Main Content */} setActiveTab(v as 'orders' | 'ready')}> Commandes Prêtes {readyOrders.length > 0 && ( {readyOrders.length} )} Historique {workOrders.filter(o => o.statutAtelier === 'RETIRE').length > 0 && ( {workOrders.filter(o => o.statutAtelier === 'RETIRE').length} )}
Commandes de Montage Gérez les commandes de montage de lunettes
{loading ? (
Chargement...
) : filteredOrders.length === 0 ? (

{statusFilter === 'ALL' ? 'Aucune commande de montage en cours' : statusFilter === 'RETIRE' ? 'Aucune commande retirée' : `Aucune commande avec le statut "${statusFilter === 'EN_ATTENTE' ? 'En attente' : statusFilter === 'EN_COURS' ? 'En cours' : statusFilter === 'TERMINE' ? 'Terminé' : 'Prêt'}"` }

{statusFilter === 'ALL' && workOrders.length === 0 && ( )}
) : ( N° Commande Date Client Produits Statut Actions {filteredOrders.map((order) => ( {order.numero} {new Date(order.date).toLocaleDateString('fr-FR')} {order.client ? `${order.client.prenom} ${order.client.nom}` : 'Client anonyme' }
{order.lignes.slice(0, 2).map((ligne) => ( {ligne.produit.designation} ))} {order.lignes.length > 2 && ( +{order.lignes.length - 2} autre(s) )}
{getStatusBadge(order.statutAtelier)}
))}
)}
Commandes prêtes pour retrait Les commandes terminées et prêtes à être remises aux clients {readyOrders.length === 0 ? (

Aucune commande prête pour le retrait

) : ( N° Commande Date Client Téléphone Prêt depuis Montant TTC Action {readyOrders.map((order) => ( {order.numero} {new Date(order.date).toLocaleDateString('fr-FR')} {order.client ? `${order.client.prenom} ${order.client.nom}` : 'Client anonyme' } {order.client?.telephone || '-'} {order.dateAtelier ? new Date(order.dateAtelier).toLocaleDateString('fr-FR') : new Date(order.date).toLocaleDateString('fr-FR') } {order.montantTTC.toFixed(2)} € ))}
)}
{/* History Tab */} Historique des retraits Les commandes qui ont été retirées par les clients {workOrders.filter(o => o.statutAtelier === 'RETIRE').length === 0 ? (

Aucune commande retirée

) : ( N° Commande Date de vente Client Téléphone Prêt le Retiré le Montant TTC Action {workOrders.filter(o => o.statutAtelier === 'RETIRE').map((order) => ( {order.numero} {new Date(order.date).toLocaleDateString('fr-FR')} {order.client ? `${order.client.prenom} ${order.client.nom}` : 'Client anonyme' } {order.client?.telephone || '-'} {order.dateAtelier ? new Date(order.dateAtelier).toLocaleDateString('fr-FR') : '-' } {order.dateRetrait ? new Date(order.dateRetrait).toLocaleDateString('fr-FR') : '-' } {order.montantTTC.toFixed(2)} € ))}
)}
{/* Order Detail Dialog */} Détails de la commande {selectedOrder?.numero} Gérez le montage et le statut de cette commande {selectedOrder && (
{/* Status and Actions */}

Statut actuel

{getStatusBadge(selectedOrder.statutAtelier)}
{selectedOrder.statutAtelier === 'EN_ATTENTE' && ( )} {selectedOrder.statutAtelier === 'EN_COURS' && ( )} {selectedOrder.statutAtelier === 'TERMINE' && ( )}
{/* Client Information */}

Client

{selectedOrder.client ? (

{selectedOrder.client.prenom} {selectedOrder.client.nom}

{selectedOrder.client.telephone}

{selectedOrder.client.email && (

{selectedOrder.client.email}

)}
) : (

Client anonyme

)}
{/* Vision Measurements */} {selectedOrder.patients && selectedOrder.patients.length > 0 && (

Mesures de vision

{selectedOrder.patients.map((patient) => { const vision = formatVisionData(patient) if (!vision) return null return (

Œil Droit (OD)

{vision.od}

Œil Gauche (OG)

{vision.og}

{(vision.addition || vision.pd || vision.hauteur) && (
{[vision.addition, vision.pd, vision.hauteur].filter(Boolean).join(' | ')}
)}
) })}
)} {/* Products */}

Produits à monter

Référence Désignation Catégorie Quantité {selectedOrder.lignes.map((ligne) => ( {ligne.produit.reference} {ligne.produit.designation} {ligne.produit.categorie} {ligne.quantite} ))}
{/* Dates */}

Dates

Date de vente:

{new Date(selectedOrder.date).toLocaleDateString('fr-FR')} à{' '} {new Date(selectedOrder.date).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}

Dernière mise à jour atelier:

{selectedOrder.dateAtelier ? `${new Date(selectedOrder.dateAtelier).toLocaleDateString('fr-FR')} à ${new Date(selectedOrder.dateAtelier).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}` : '-' }

{selectedOrder.statutAtelier === 'RETIRE' && selectedOrder.dateRetrait && (

Date de retrait:

{new Date(selectedOrder.dateRetrait).toLocaleDateString('fr-FR')} à{' '} {new Date(selectedOrder.dateRetrait).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}

)}
{selectedOrder.notes && ( <>

Notes

{selectedOrder.notes}

)}
Total TTC: {selectedOrder.montantTTC.toFixed(2)} €
)}
{/* Notify Client Dialog */} Notifier le client Confirmer que cette commande est prête pour le retrait {selectedOrder && (

La commande {selectedOrder.numero} est prête!

{selectedOrder.client ? `Le client ${selectedOrder.client.prenom} ${selectedOrder.client.nom} pourra être notifié que ses lunettes sont prêtes.` : 'La commande est marquée comme prête pour retrait.' }

)}
) }