From 3080c0d0643274ddaa73652c57a87a14dce4fc91 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Thu, 14 Aug 2025 09:46:17 +0000 Subject: [PATCH] Enable creating new support tickets within the SAV module Add a modal and mutation to create new SAV tickets, including form handling and error feedback. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 31f9bd88-0285-4787-81b3-31ceeea5a08b Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/31f9bd88-0285-4787-81b3-31ceeea5a08b/tftmNHr --- client/src/pages/SavTickets.tsx | 257 +++++++++++++++++++++++++++++++- server/migrations.production.ts | 6 +- 2 files changed, 255 insertions(+), 8 deletions(-) diff --git a/client/src/pages/SavTickets.tsx b/client/src/pages/SavTickets.tsx index a394eae..250d09f 100644 --- a/client/src/pages/SavTickets.tsx +++ b/client/src/pages/SavTickets.tsx @@ -5,6 +5,9 @@ import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useToast } from "@/hooks/use-toast"; import { apiRequest } from "@/lib/queryClient"; @@ -26,7 +29,7 @@ import { Ban } from "lucide-react"; import { safeFormat } from "@/lib/dateUtils"; -import type { SavTicketWithRelations, Supplier } from "@shared/schema"; +import type { SavTicketWithRelations, Supplier, Group, InsertSavTicket } from "@shared/schema"; const statusConfig = { nouveau: { label: "Nouveau", color: "bg-blue-100 text-blue-800", icon: AlertTriangle }, @@ -53,6 +56,19 @@ export default function SavTickets() { const [statusFilter, setStatusFilter] = useState("all"); const [priorityFilter, setPriorityFilter] = useState("all"); const [supplierFilter, setSupplierFilter] = useState("all"); + const [showCreateModal, setShowCreateModal] = useState(false); + const [formData, setFormData] = useState({ + supplierId: "", + groupId: "", + productGencode: "", + productReference: "", + productDesignation: "", + problemType: "defectueux", + problemDescription: "", + priority: "normale", + clientName: "", + clientPhone: "" + }); // Build query parameters const queryParams = new URLSearchParams(); @@ -72,6 +88,11 @@ export default function SavTickets() { staleTime: 1000 * 60 * 10, // 10 minutes }); + const { data: groupsData = [] } = useQuery({ + queryKey: ['/api/groups'], + staleTime: 1000 * 60 * 10, // 10 minutes + }); + const { data: statsData } = useQuery<{ totalTickets: number; newTickets: number; @@ -83,11 +104,78 @@ export default function SavTickets() { staleTime: 1000 * 60 * 2, // 2 minutes }); + // Create ticket mutation + const createTicketMutation = useMutation({ + mutationFn: async (data: InsertSavTicket) => { + const response = await apiRequest('/api/sav/tickets', { + method: 'POST', + body: JSON.stringify(data), + }); + return response; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['/api/sav/tickets'] }); + queryClient.invalidateQueries({ queryKey: ['/api/sav/stats'] }); + setShowCreateModal(false); + setFormData({ + supplierId: "", + groupId: "", + productGencode: "", + productReference: "", + productDesignation: "", + problemType: "defectueux", + problemDescription: "", + priority: "normale", + clientName: "", + clientPhone: "" + }); + toast({ + title: "Ticket créé", + description: "Le ticket SAV a été créé avec succès.", + }); + }, + onError: (error: any) => { + console.error('Error creating ticket:', error); + toast({ + title: "Erreur", + description: error?.message || "Impossible de créer le ticket.", + variant: "destructive", + }); + }, + }); + // Check permissions AFTER all hooks are called const canView = ['admin', 'directeur', 'manager', 'employee'].includes(user?.role || ''); const canModify = ['admin', 'directeur', 'manager'].includes(user?.role || ''); const canDelete = ['admin', 'directeur'].includes(user?.role || ''); + // Handle form submission + const handleCreateTicket = () => { + if (!formData.supplierId || !formData.productGencode || !formData.productDesignation || !formData.problemDescription) { + toast({ + title: "Erreur de validation", + description: "Veuillez remplir tous les champs obligatoires.", + variant: "destructive", + }); + return; + } + + const ticketData: InsertSavTicket = { + supplierId: parseInt(formData.supplierId), + groupId: formData.groupId ? parseInt(formData.groupId) : undefined, + productGencode: formData.productGencode, + productReference: formData.productReference || undefined, + productDesignation: formData.productDesignation, + problemType: formData.problemType as "defectueux" | "pieces_manquantes" | "non_conforme" | "autre", + problemDescription: formData.problemDescription, + priority: formData.priority as "faible" | "normale" | "haute" | "critique", + clientName: formData.clientName || undefined, + clientPhone: formData.clientPhone || undefined, + }; + + createTicketMutation.mutate(ticketData); + }; + // Handle permission check without early return if (!canView) { return ( @@ -131,10 +219,169 @@ export default function SavTickets() {

Gestion des tickets SAV et suivi des réparations

{canModify && ( - + + + + + + + Créer un nouveau ticket SAV + + Remplissez les informations pour créer un nouveau ticket de service après-vente. + + + +
+ {/* Informations produit */} +
+

Informations produit

+ +
+ + setFormData({...formData, productGencode: e.target.value})} + placeholder="Code-barres du produit" + /> +
+ +
+ + setFormData({...formData, productReference: e.target.value})} + placeholder="Référence du produit" + /> +
+ +
+ + setFormData({...formData, productDesignation: e.target.value})} + placeholder="Nom/désignation du produit" + /> +
+ +
+ + +
+ +
+ + +
+
+ + {/* Informations problème et client */} +
+

Problème et client

+ +
+ + +
+ +
+ + +
+ +
+ +