Introduces a new sidebar component in `client/src/App.tsx` for navigation, along with new pages for Dashboard, Detections, and Routers. The backend in `server/routes.ts` is updated to include API endpoints for managing routers, fetching network logs, and retrieving detection data. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 99f17f6a-6021-4354-9517-5610b878cb21 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/c9ITWqD
146 lines
5.7 KiB
TypeScript
146 lines
5.7 KiB
TypeScript
import { useQuery, useMutation } from "@tanstack/react-query";
|
|
import { queryClient, apiRequest } from "@/lib/queryClient";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Server, Plus, Trash2 } from "lucide-react";
|
|
import { format } from "date-fns";
|
|
import type { Router } from "@shared/schema";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
|
|
export default function Routers() {
|
|
const { toast } = useToast();
|
|
const { data: routers, isLoading } = useQuery<Router[]>({
|
|
queryKey: ["/api/routers"],
|
|
});
|
|
|
|
const deleteMutation = useMutation({
|
|
mutationFn: async (id: string) => {
|
|
await apiRequest("DELETE", `/api/routers/${id}`);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["/api/routers"] });
|
|
toast({
|
|
title: "Router eliminato",
|
|
description: "Il router è stato rimosso con successo",
|
|
});
|
|
},
|
|
onError: () => {
|
|
toast({
|
|
title: "Errore",
|
|
description: "Impossibile eliminare il router",
|
|
variant: "destructive",
|
|
});
|
|
},
|
|
});
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 p-6" data-testid="page-routers">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-semibold" data-testid="text-page-title">Router MikroTik</h1>
|
|
<p className="text-muted-foreground" data-testid="text-page-subtitle">
|
|
Gestisci i router connessi al sistema IDS
|
|
</p>
|
|
</div>
|
|
<Button data-testid="button-add-router">
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Aggiungi Router
|
|
</Button>
|
|
</div>
|
|
|
|
<Card data-testid="card-routers">
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Server className="h-5 w-5" />
|
|
Router Configurati ({routers?.length || 0})
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{isLoading ? (
|
|
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
|
|
Caricamento...
|
|
</div>
|
|
) : routers && routers.length > 0 ? (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{routers.map((router) => (
|
|
<div
|
|
key={router.id}
|
|
className="p-4 rounded-lg border hover-elevate"
|
|
data-testid={`router-card-${router.id}`}
|
|
>
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div>
|
|
<h3 className="font-semibold text-lg" data-testid={`text-name-${router.id}`}>
|
|
{router.name}
|
|
</h3>
|
|
<p className="text-sm font-mono text-muted-foreground" data-testid={`text-ip-${router.id}`}>
|
|
{router.ipAddress}:{router.apiPort}
|
|
</p>
|
|
</div>
|
|
<Badge
|
|
variant={router.enabled ? "default" : "secondary"}
|
|
data-testid={`badge-status-${router.id}`}
|
|
>
|
|
{router.enabled ? "Attivo" : "Disabilitato"}
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Username:</span>
|
|
<span className="font-mono" data-testid={`text-username-${router.id}`}>
|
|
{router.username}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Creato:</span>
|
|
<span data-testid={`text-created-${router.id}`}>
|
|
{format(new Date(router.createdAt), "dd/MM/yyyy")}
|
|
</span>
|
|
</div>
|
|
{router.lastSync && (
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Ultima sync:</span>
|
|
<span data-testid={`text-sync-${router.id}`}>
|
|
{format(new Date(router.lastSync), "HH:mm:ss")}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex gap-2 mt-4">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="flex-1"
|
|
data-testid={`button-test-${router.id}`}
|
|
>
|
|
Test Connessione
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => deleteMutation.mutate(router.id)}
|
|
disabled={deleteMutation.isPending}
|
|
data-testid={`button-delete-${router.id}`}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-12 text-muted-foreground" data-testid="text-no-routers">
|
|
<Server className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
|
<p className="mb-2">Nessun router configurato</p>
|
|
<p className="text-sm">Aggiungi il primo router per iniziare</p>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|