Compare commits
2 Commits
c099eb6472
...
918c5f0226
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
918c5f0226 | ||
|
|
e6cac35763 |
@ -58,6 +58,7 @@ const getColorClasses = (color: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type SiteForm = z.infer<typeof insertSiteSchema>;
|
type SiteForm = z.infer<typeof insertSiteSchema>;
|
||||||
|
type ServiceTypeForm = z.infer<typeof insertServiceTypeSchema>;
|
||||||
|
|
||||||
export default function Services() {
|
export default function Services() {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -65,6 +66,12 @@ export default function Services() {
|
|||||||
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
||||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||||
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
|
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
|
||||||
|
|
||||||
|
// Service Type management states
|
||||||
|
const [manageTypesDialogOpen, setManageTypesDialogOpen] = useState(false);
|
||||||
|
const [createTypeDialogOpen, setCreateTypeDialogOpen] = useState(false);
|
||||||
|
const [editTypeDialogOpen, setEditTypeDialogOpen] = useState(false);
|
||||||
|
const [selectedType, setSelectedType] = useState<ServiceType | null>(null);
|
||||||
|
|
||||||
const { data: sites = [], isLoading: isLoadingSites } = useQuery<Site[]>({
|
const { data: sites = [], isLoading: isLoadingSites } = useQuery<Site[]>({
|
||||||
queryKey: ["/api/sites"],
|
queryKey: ["/api/sites"],
|
||||||
@ -150,6 +157,89 @@ export default function Services() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Service Type Forms
|
||||||
|
const createTypeForm = useForm<ServiceTypeForm>({
|
||||||
|
resolver: zodResolver(insertServiceTypeSchema),
|
||||||
|
defaultValues: {
|
||||||
|
code: "",
|
||||||
|
label: "",
|
||||||
|
description: "",
|
||||||
|
icon: "Building2",
|
||||||
|
color: "blue",
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const editTypeForm = useForm<ServiceTypeForm>({
|
||||||
|
resolver: zodResolver(insertServiceTypeSchema),
|
||||||
|
defaultValues: {
|
||||||
|
code: "",
|
||||||
|
label: "",
|
||||||
|
description: "",
|
||||||
|
icon: "Building2",
|
||||||
|
color: "blue",
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Service Type Mutations
|
||||||
|
const createTypeMutation = useMutation({
|
||||||
|
mutationFn: async (data: ServiceTypeForm) => {
|
||||||
|
return apiRequest("POST", "/api/service-types", data);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["/api/service-types"] });
|
||||||
|
toast({
|
||||||
|
title: "Tipologia creata",
|
||||||
|
description: "La tipologia di servizio è stata aggiunta con successo.",
|
||||||
|
});
|
||||||
|
setCreateTypeDialogOpen(false);
|
||||||
|
createTypeForm.reset();
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
toast({
|
||||||
|
title: "Errore",
|
||||||
|
description: error.message || "Impossibile creare la tipologia.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateTypeMutation = useMutation({
|
||||||
|
mutationFn: async ({ id, data }: { id: string; data: ServiceTypeForm }) => {
|
||||||
|
return apiRequest("PATCH", `/api/service-types/${id}`, data);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["/api/service-types"] });
|
||||||
|
toast({
|
||||||
|
title: "Tipologia aggiornata",
|
||||||
|
description: "Le modifiche sono state salvate con successo.",
|
||||||
|
});
|
||||||
|
setEditTypeDialogOpen(false);
|
||||||
|
setSelectedType(null);
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
toast({
|
||||||
|
title: "Errore",
|
||||||
|
description: error.message || "Impossibile aggiornare la tipologia.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleEditType = (type: ServiceType) => {
|
||||||
|
setSelectedType(type);
|
||||||
|
editTypeForm.reset({
|
||||||
|
code: type.code,
|
||||||
|
label: type.label,
|
||||||
|
description: type.description,
|
||||||
|
icon: type.icon,
|
||||||
|
color: type.color,
|
||||||
|
isActive: type.isActive,
|
||||||
|
});
|
||||||
|
setEditTypeDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateSite = (serviceType: string) => {
|
const handleCreateSite = (serviceType: string) => {
|
||||||
createForm.reset({
|
createForm.reset({
|
||||||
name: "",
|
name: "",
|
||||||
@ -206,6 +296,13 @@ export default function Services() {
|
|||||||
Panoramica tipologie di servizio e relative configurazioni
|
Panoramica tipologie di servizio e relative configurazioni
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => setManageTypesDialogOpen(true)}
|
||||||
|
data-testid="button-manage-service-types"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Gestisci Tipologie
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -712,6 +809,377 @@ export default function Services() {
|
|||||||
</Form>
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Manage Service Types Dialog */}
|
||||||
|
<Dialog open={manageTypesDialogOpen} onOpenChange={setManageTypesDialogOpen}>
|
||||||
|
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Gestione Tipologie Servizio</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Configura le tipologie di servizio disponibili nel sistema
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Button
|
||||||
|
onClick={() => setCreateTypeDialogOpen(true)}
|
||||||
|
className="w-full"
|
||||||
|
data-testid="button-add-service-type"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Aggiungi Nuova Tipologia
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
{serviceTypes.map((type) => {
|
||||||
|
const Icon = getIconComponent(type.icon);
|
||||||
|
return (
|
||||||
|
<Card key={type.id} className={!type.isActive ? "opacity-50" : ""}>
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3 flex-1">
|
||||||
|
<div className={`p-2 rounded-lg ${getColorClasses(type.color)}`}>
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h3 className="font-semibold">{type.label}</h3>
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
{type.code}
|
||||||
|
</Badge>
|
||||||
|
{!type.isActive && (
|
||||||
|
<Badge variant="secondary" className="text-xs">
|
||||||
|
Disattivato
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
{type.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => handleEditType(type)}
|
||||||
|
data-testid={`button-edit-service-type-${type.code}`}
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Create Service Type Dialog */}
|
||||||
|
<Dialog open={createTypeDialogOpen} onOpenChange={setCreateTypeDialogOpen}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Nuova Tipologia Servizio</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Crea una nuova tipologia di servizio
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...createTypeForm}>
|
||||||
|
<form
|
||||||
|
onSubmit={createTypeForm.handleSubmit((data) => createTypeMutation.mutate(data))}
|
||||||
|
className="space-y-4"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="code"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Codice*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} placeholder="es: fixed_post" data-testid="input-type-code" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="label"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nome*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} placeholder="es: Presidio Fisso" data-testid="input-type-label" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Descrizione*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea {...field} value={field.value || ""} placeholder="Descrizione del servizio" data-testid="input-type-description" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="icon"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Icona*</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger data-testid="select-type-icon">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="Building2">Building2</SelectItem>
|
||||||
|
<SelectItem value="Shield">Shield</SelectItem>
|
||||||
|
<SelectItem value="Eye">Eye</SelectItem>
|
||||||
|
<SelectItem value="Zap">Zap</SelectItem>
|
||||||
|
<SelectItem value="Car">Car</SelectItem>
|
||||||
|
<SelectItem value="Users">Users</SelectItem>
|
||||||
|
<SelectItem value="MapPin">MapPin</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="color"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Colore*</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger data-testid="select-type-color">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="blue">Blu</SelectItem>
|
||||||
|
<SelectItem value="green">Verde</SelectItem>
|
||||||
|
<SelectItem value="purple">Viola</SelectItem>
|
||||||
|
<SelectItem value="orange">Arancione</SelectItem>
|
||||||
|
<SelectItem value="red">Rosso</SelectItem>
|
||||||
|
<SelectItem value="yellow">Giallo</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={createTypeForm.control}
|
||||||
|
name="isActive"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex items-center justify-between p-3 border rounded-lg">
|
||||||
|
<FormLabel>Tipologia Attiva</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value ?? true}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
data-testid="switch-type-active"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setCreateTypeDialogOpen(false)}
|
||||||
|
data-testid="button-type-cancel"
|
||||||
|
>
|
||||||
|
Annulla
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={createTypeMutation.isPending}
|
||||||
|
data-testid="button-save-type"
|
||||||
|
>
|
||||||
|
{createTypeMutation.isPending ? "Salvataggio..." : "Crea Tipologia"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Edit Service Type Dialog */}
|
||||||
|
<Dialog open={editTypeDialogOpen} onOpenChange={setEditTypeDialogOpen}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Modifica Tipologia Servizio</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Modifica le informazioni della tipologia
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...editTypeForm}>
|
||||||
|
<form
|
||||||
|
onSubmit={editTypeForm.handleSubmit((data) =>
|
||||||
|
selectedType && updateTypeMutation.mutate({ id: selectedType.id, data })
|
||||||
|
)}
|
||||||
|
className="space-y-4"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="code"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Codice*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} data-testid="input-edit-type-code" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="label"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nome*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} data-testid="input-edit-type-label" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Descrizione*</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea {...field} value={field.value || ""} data-testid="input-edit-type-description" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="icon"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Icona*</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger data-testid="select-edit-type-icon">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="Building2">Building2</SelectItem>
|
||||||
|
<SelectItem value="Shield">Shield</SelectItem>
|
||||||
|
<SelectItem value="Eye">Eye</SelectItem>
|
||||||
|
<SelectItem value="Zap">Zap</SelectItem>
|
||||||
|
<SelectItem value="Car">Car</SelectItem>
|
||||||
|
<SelectItem value="Users">Users</SelectItem>
|
||||||
|
<SelectItem value="MapPin">MapPin</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="color"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Colore*</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger data-testid="select-edit-type-color">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="blue">Blu</SelectItem>
|
||||||
|
<SelectItem value="green">Verde</SelectItem>
|
||||||
|
<SelectItem value="purple">Viola</SelectItem>
|
||||||
|
<SelectItem value="orange">Arancione</SelectItem>
|
||||||
|
<SelectItem value="red">Rosso</SelectItem>
|
||||||
|
<SelectItem value="yellow">Giallo</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={editTypeForm.control}
|
||||||
|
name="isActive"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex items-center justify-between p-3 border rounded-lg">
|
||||||
|
<FormLabel>Tipologia Attiva</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value ?? true}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
data-testid="switch-edit-type-active"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setEditTypeDialogOpen(false);
|
||||||
|
setSelectedType(null);
|
||||||
|
}}
|
||||||
|
data-testid="button-edit-type-cancel"
|
||||||
|
>
|
||||||
|
Annulla
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={updateTypeMutation.isPending}
|
||||||
|
data-testid="button-update-type"
|
||||||
|
>
|
||||||
|
{updateTypeMutation.isPending ? "Salvataggio..." : "Aggiorna Tipologia"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
database-backups/vigilanzaturni_v1.0.12_20251017_094703.sql.gz
Normal file
BIN
database-backups/vigilanzaturni_v1.0.12_20251017_094703.sql.gz
Normal file
Binary file not shown.
Binary file not shown.
10
version.json
10
version.json
@ -1,7 +1,13 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.11",
|
"version": "1.0.12",
|
||||||
"lastUpdate": "2025-10-17T09:32:00.721Z",
|
"lastUpdate": "2025-10-17T09:47:18.311Z",
|
||||||
"changelog": [
|
"changelog": [
|
||||||
|
{
|
||||||
|
"version": "1.0.12",
|
||||||
|
"date": "2025-10-17",
|
||||||
|
"type": "patch",
|
||||||
|
"description": "Deployment automatico v1.0.12"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"date": "2025-10-17",
|
"date": "2025-10-17",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user