diff --git a/.replit b/.replit index 4827ed6..9a975de 100644 --- a/.replit +++ b/.replit @@ -23,6 +23,10 @@ externalPort = 3001 localPort = 37831 externalPort = 5173 +[[ports]] +localPort = 41295 +externalPort = 6000 + [[ports]] localPort = 41343 externalPort = 3000 diff --git a/client/src/App.tsx b/client/src/App.tsx index cb19b8f..215fea0 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -25,6 +25,7 @@ import Planning from "@/pages/planning"; import OperationalPlanning from "@/pages/operational-planning"; import GeneralPlanning from "@/pages/general-planning"; import ServicePlanning from "@/pages/service-planning"; +import Customers from "@/pages/customers"; function Router() { const { isAuthenticated, isLoading } = useAuth(); @@ -39,6 +40,7 @@ function Router() { + diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx index 0a6fffc..bae3694 100644 --- a/client/src/components/app-sidebar.tsx +++ b/client/src/components/app-sidebar.tsx @@ -85,6 +85,12 @@ const menuItems = [ icon: MapPin, roles: ["admin", "coordinator", "client"], }, + { + title: "Clienti", + url: "/customers", + icon: Briefcase, + roles: ["admin", "coordinator"], + }, { title: "Servizi", url: "/services", diff --git a/client/src/pages/customers.tsx b/client/src/pages/customers.tsx new file mode 100644 index 0000000..58148e5 --- /dev/null +++ b/client/src/pages/customers.tsx @@ -0,0 +1,616 @@ +import { useState } from "react"; +import { useQuery, useMutation } from "@tanstack/react-query"; +import { Customer, InsertCustomer, insertCustomerSchema } from "@shared/schema"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Switch } from "@/components/ui/switch"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Plus, Building2, Pencil, Trash2, Phone, Mail } from "lucide-react"; +import { apiRequest, queryClient } from "@/lib/queryClient"; +import { useToast } from "@/hooks/use-toast"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; + +export default function Customers() { + const { toast } = useToast(); + const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); + const [editingCustomer, setEditingCustomer] = useState(null); + const [deletingCustomerId, setDeletingCustomerId] = useState(null); + + const { data: customers, isLoading } = useQuery({ + queryKey: ["/api/customers"], + }); + + const form = useForm({ + resolver: zodResolver(insertCustomerSchema), + defaultValues: { + name: "", + businessName: "", + vatNumber: "", + fiscalCode: "", + address: "", + city: "", + province: "", + zipCode: "", + phone: "", + email: "", + pec: "", + contactPerson: "", + notes: "", + isActive: true, + }, + }); + + const createMutation = useMutation({ + mutationFn: async (data: InsertCustomer) => { + return await apiRequest("POST", "/api/customers", data); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/customers"] }); + toast({ + title: "Cliente creato", + description: "Il cliente è stato aggiunto con successo", + }); + setIsCreateDialogOpen(false); + form.reset(); + }, + onError: (error) => { + toast({ + title: "Errore", + description: error.message, + variant: "destructive", + }); + }, + }); + + const updateMutation = useMutation({ + mutationFn: async ({ id, data }: { id: string; data: InsertCustomer }) => { + return await apiRequest("PATCH", `/api/customers/${id}`, data); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/customers"] }); + toast({ + title: "Cliente aggiornato", + description: "I dati del cliente sono stati aggiornati", + }); + setEditingCustomer(null); + form.reset(); + }, + onError: (error) => { + toast({ + title: "Errore", + description: error.message, + variant: "destructive", + }); + }, + }); + + const deleteMutation = useMutation({ + mutationFn: async (id: string) => { + return await apiRequest("DELETE", `/api/customers/${id}`, undefined); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/customers"] }); + toast({ + title: "Cliente eliminato", + description: "Il cliente è stato eliminato con successo", + }); + setDeletingCustomerId(null); + }, + onError: (error) => { + toast({ + title: "Errore", + description: error.message, + variant: "destructive", + }); + setDeletingCustomerId(null); + }, + }); + + const onSubmit = (data: InsertCustomer) => { + if (editingCustomer) { + updateMutation.mutate({ id: editingCustomer.id, data }); + } else { + createMutation.mutate(data); + } + }; + + const openEditDialog = (customer: Customer) => { + setEditingCustomer(customer); + form.reset({ + name: customer.name || "", + businessName: customer.businessName || "", + vatNumber: customer.vatNumber || "", + fiscalCode: customer.fiscalCode || "", + address: customer.address || "", + city: customer.city || "", + province: customer.province || "", + zipCode: customer.zipCode || "", + phone: customer.phone || "", + email: customer.email || "", + pec: customer.pec || "", + contactPerson: customer.contactPerson || "", + notes: customer.notes || "", + isActive: customer.isActive ?? true, + }); + setIsCreateDialogOpen(true); + }; + + const handleDialogOpenChange = (open: boolean) => { + setIsCreateDialogOpen(open); + if (!open) { + // Reset only on close + setEditingCustomer(null); + form.reset(); + } + }; + + if (isLoading) { + return ( +
+ + +
+ ); + } + + return ( +
+ {/* Header */} +
+
+

+ Anagrafica Clienti +

+

+ Gestione anagrafica clienti e contratti +

+
+ + + + + + + + {editingCustomer ? "Modifica Cliente" : "Nuovo Cliente"} + + + Inserisci i dati anagrafici del cliente + + +
+ +
+ ( + + Nome Cliente * + + + + + + )} + /> + + ( + + Ragione Sociale + + + + + + )} + /> + + ( + + Partita IVA + + + + + + )} + /> + + ( + + Codice Fiscale + + + + + + )} + /> + + ( + + Indirizzo + + + + + + )} + /> + + ( + + Città + + + + + + )} + /> + + ( + + Provincia + + + + + + )} + /> + + ( + + CAP + + + + + + )} + /> + + ( + + Telefono + + + + + + )} + /> + + ( + + Email + + + + + + )} + /> + + ( + + PEC + + + + + + )} + /> + + ( + + Referente + + + + + + )} + /> + + ( + + Note + +