Add search functionality to the whitelist page and improve IP status indication
Add a search bar to the whitelist page and filter results by IP, reason, and notes. Modify the detections page to visually indicate when an IP is already whitelisted by changing the button color to green and using a different icon. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 4231475f-0a12-42cd-bf3f-3401022fd4e5 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/8i4FqXF
This commit is contained in:
parent
0298b4a790
commit
1b47e08129
@ -5,10 +5,10 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus } from "lucide-react";
|
import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus, ShieldCheck } from "lucide-react";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { Detection } from "@shared/schema";
|
import type { Detection, Whitelist } from "@shared/schema";
|
||||||
import { getFlag } from "@/lib/country-flags";
|
import { getFlag } from "@/lib/country-flags";
|
||||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
@ -39,6 +39,14 @@ export default function Detections() {
|
|||||||
refetchInterval: 5000,
|
refetchInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch whitelist to check if IP is already whitelisted
|
||||||
|
const { data: whitelistData } = useQuery<Whitelist[]>({
|
||||||
|
queryKey: ["/api/whitelist"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a Set of whitelisted IPs for fast lookup
|
||||||
|
const whitelistedIps = new Set(whitelistData?.map(w => w.ipAddress) || []);
|
||||||
|
|
||||||
const filteredDetections = detections?.filter((d) =>
|
const filteredDetections = detections?.filter((d) =>
|
||||||
d.sourceIp.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
d.sourceIp.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
d.anomalyType.toLowerCase().includes(searchQuery.toLowerCase())
|
d.anomalyType.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
@ -278,6 +286,18 @@ export default function Detections() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{whitelistedIps.has(detection.sourceIp) ? (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled
|
||||||
|
className="w-full bg-green-500/10 border-green-500 text-green-600 dark:text-green-400"
|
||||||
|
data-testid={`button-whitelist-${detection.id}`}
|
||||||
|
>
|
||||||
|
<ShieldCheck className="h-3 w-3 mr-1" />
|
||||||
|
In Whitelist
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -289,6 +309,7 @@ export default function Detections() {
|
|||||||
<ShieldPlus className="h-3 w-3 mr-1" />
|
<ShieldPlus className="h-3 w-3 mr-1" />
|
||||||
Whitelist
|
Whitelist
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useQuery, useMutation } from "@tanstack/react-query";
|
|||||||
import { queryClient, apiRequest } from "@/lib/queryClient";
|
import { queryClient, apiRequest } from "@/lib/queryClient";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Shield, Plus, Trash2, CheckCircle2, XCircle } from "lucide-react";
|
import { Shield, Plus, Trash2, CheckCircle2, XCircle, Search } from "lucide-react";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@ -44,6 +44,7 @@ const whitelistFormSchema = insertWhitelistSchema.extend({
|
|||||||
export default function WhitelistPage() {
|
export default function WhitelistPage() {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
|
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof whitelistFormSchema>>({
|
const form = useForm<z.infer<typeof whitelistFormSchema>>({
|
||||||
resolver: zodResolver(whitelistFormSchema),
|
resolver: zodResolver(whitelistFormSchema),
|
||||||
@ -59,6 +60,13 @@ export default function WhitelistPage() {
|
|||||||
queryKey: ["/api/whitelist"],
|
queryKey: ["/api/whitelist"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Filter whitelist based on search query
|
||||||
|
const filteredWhitelist = whitelist?.filter((item) =>
|
||||||
|
item.ipAddress.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
item.reason?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
item.comment?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
const addMutation = useMutation({
|
const addMutation = useMutation({
|
||||||
mutationFn: async (data: z.infer<typeof whitelistFormSchema>) => {
|
mutationFn: async (data: z.infer<typeof whitelistFormSchema>) => {
|
||||||
return await apiRequest("POST", "/api/whitelist", data);
|
return await apiRequest("POST", "/api/whitelist", data);
|
||||||
@ -189,11 +197,27 @@ export default function WhitelistPage() {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Search Bar */}
|
||||||
|
<Card data-testid="card-search">
|
||||||
|
<CardContent className="pt-6">
|
||||||
|
<div className="relative">
|
||||||
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
placeholder="Cerca per IP, motivo o note..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="pl-9"
|
||||||
|
data-testid="input-search-whitelist"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card data-testid="card-whitelist">
|
<Card data-testid="card-whitelist">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<Shield className="h-5 w-5" />
|
<Shield className="h-5 w-5" />
|
||||||
IP Protetti ({whitelist?.length || 0})
|
IP Protetti ({filteredWhitelist?.length || 0}{searchQuery && whitelist ? ` di ${whitelist.length}` : ''})
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@ -201,9 +225,9 @@ export default function WhitelistPage() {
|
|||||||
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
|
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
|
||||||
Caricamento...
|
Caricamento...
|
||||||
</div>
|
</div>
|
||||||
) : whitelist && whitelist.length > 0 ? (
|
) : filteredWhitelist && filteredWhitelist.length > 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{whitelist.map((item) => (
|
{filteredWhitelist.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="p-4 rounded-lg border hover-elevate"
|
className="p-4 rounded-lg border hover-elevate"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user