/** * MobileCustomerOrdersPage.tsx * Version mobile de la page Commandes Clients avec création */ import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useStore } from "@/contexts/StoreContext"; import MobileLayout from "./MobileLayout"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Package, Search, Phone, User, ClipboardList, Plus, MoreVertical, X, Filter } from "lucide-react"; import { format } from "date-fns"; import { fr } from "date-fns/locale"; import { apiRequest } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; // Schema for form validation const customerOrderFormSchema = z.object({ customerName: z.string().min(1, "Nom du client est requis"), contactNumber: z.string().optional(), productName: z.string().min(1, "Désignation est requise"), productReference: z.string().optional(), gencode: z.string().optional(), quantity: z.coerce.number().int().positive().default(1), supplierId: z.coerce.number().int().positive().default(1), orderTaker: z.string().optional(), notes: z.string().optional(), deposit: z.coerce.number().optional().default(0), }); export default function MobileCustomerOrdersPage() { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); const [searchTerm, setSearchTerm] = useState(""); const [isCreateOpen, setIsCreateOpen] = useState(false); const { toast } = useToast(); const queryClient = useQueryClient(); // Queries const { data: orders = [], isLoading } = useQuery({ queryKey: ["/api/customer-orders", selectedStoreId], queryFn: async () => { if (!selectedStoreId) return []; const response = await fetch( `/api/customer-orders?storeId=${selectedStoreId}`, { credentials: 'include' } ); if (!response.ok) throw new Error("Failed to fetch customer orders"); return response.json(); }, enabled: !!selectedStoreId && !!user, }); const { data: suppliers = [] } = useQuery({ queryKey: ['/api/suppliers'], queryFn: async () => { const res = await fetch('/api/suppliers', { credentials: 'include' }); return res.json(); } }); const { data: groups = [] } = useQuery({ queryKey: ['/api/groups'], queryFn: async () => { const res = await fetch('/api/groups', { credentials: 'include' }); return res.json(); } }); // Mutations const statusMutation = useMutation({ mutationFn: ({ id, status }: { id: number; status: string }) => apiRequest(`/api/customer-orders/${id}`, 'PUT', { status }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/customer-orders"] }); toast({ title: "Statut mis à jour" }); }, }); const createMutation = useMutation({ mutationFn: (data: any) => apiRequest('/api/customer-orders', 'POST', data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/customer-orders"] }); toast({ title: "Commande créée avec succès" }); setIsCreateOpen(false); form.reset(); }, onError: (err: any) => { toast({ title: "Erreur lors de la création", description: err.message, variant: "destructive" }); } }); // Form setup const form = useForm({ resolver: zodResolver(customerOrderFormSchema), defaultValues: { customerName: "", contactNumber: "", productName: "", productReference: "", gencode: "", quantity: 1, supplierId: 1, orderTaker: user?.username || "", notes: "", deposit: 0 } }); const onSubmit = (data: any) => { // Validation basique groupe let groupId = selectedStoreId; if (!groupId && user?.userGroups?.[0]?.groupId) { groupId = user.userGroups[0].groupId; } if (!groupId && groups.length > 0 && user?.role === 'admin') { groupId = groups[0].id; } if (!groupId) { toast({ title: "Erreur", description: "Impossible de déterminer le magasin", variant: "destructive" }); return; } const submitData = { ...data, customerPhone: data.contactNumber, productDesignation: data.productName, status: "En attente de Commande", groupId, isPromotionalPrice: false, customerNotified: false }; createMutation.mutate(submitData); }; const filteredOrders = orders.filter((order: any) => order.customerName.toLowerCase().includes(searchTerm.toLowerCase()) || order.productDesignation.toLowerCase().includes(searchTerm.toLowerCase()) || (order.productReference && order.productReference.toLowerCase().includes(searchTerm.toLowerCase())) ); const getStatusColor = (status: string) => { switch (status) { case "Disponible": return "bg-green-100 text-green-800 border-green-200"; case "Commande en Cours": return "bg-blue-100 text-blue-800 border-blue-200"; case "Retiré": return "bg-gray-100 text-gray-800 border-gray-200"; case "Annulé": return "bg-red-100 text-red-800 border-red-200"; default: return "bg-yellow-100 text-yellow-800 border-yellow-200"; } }; return (
{/* Header */}

Commandes Clients

{/* Search */}
setSearchTerm(e.target.value)} className="pl-9 h-10" />
{/* Orders List */}
{isLoading ? (
) : filteredOrders.length === 0 ? (

Aucune commande trouvée

) : ( filteredOrders.map((order: any) => (
{order.status} {format(new Date(order.createdAt), "dd/MM", { locale: fr })}

{order.customerName}

{order.productDesignation}

statusMutation.mutate({ id: order.id, status: 'Disponible' })}> Marquer Disponible statusMutation.mutate({ id: order.id, status: 'Retiré' })}> Marquer Retiré statusMutation.mutate({ id: order.id, status: 'Annulé' })}> Annuler
{order.customerPhone && ( Appeler )}
Qty: {order.quantity || 1}
{order.deposit && parseFloat(order.deposit) > 0 && (
Acompte: {parseFloat(order.deposit)}€
)}
)) )}
{/* Create Order Sheet */} Nouvelle Commande

Client

( Nom )} /> ( Téléphone )} />

Produit

( Désignation )} />
( Qté )} /> ( Acompte (€) )} />
( Fournisseur )} /> ( Gencode (Scanner si dispo) )} />
( Notes internes