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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
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 { useState } from "react";
|
||||
import type { Detection } from "@shared/schema";
|
||||
import type { Detection, Whitelist } from "@shared/schema";
|
||||
import { getFlag } from "@/lib/country-flags";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@ -39,6 +39,14 @@ export default function Detections() {
|
||||
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) =>
|
||||
d.sourceIp.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
d.anomalyType.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
@ -278,17 +286,30 @@ export default function Detections() {
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => addToWhitelistMutation.mutate(detection)}
|
||||
disabled={addToWhitelistMutation.isPending}
|
||||
className="w-full"
|
||||
data-testid={`button-whitelist-${detection.id}`}
|
||||
>
|
||||
<ShieldPlus className="h-3 w-3 mr-1" />
|
||||
Whitelist
|
||||
</Button>
|
||||
{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
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => addToWhitelistMutation.mutate(detection)}
|
||||
disabled={addToWhitelistMutation.isPending}
|
||||
className="w-full"
|
||||
data-testid={`button-whitelist-${detection.id}`}
|
||||
>
|
||||
<ShieldPlus className="h-3 w-3 mr-1" />
|
||||
Whitelist
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { queryClient, apiRequest } from "@/lib/queryClient";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
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 { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@ -44,6 +44,7 @@ const whitelistFormSchema = insertWhitelistSchema.extend({
|
||||
export default function WhitelistPage() {
|
||||
const { toast } = useToast();
|
||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const form = useForm<z.infer<typeof whitelistFormSchema>>({
|
||||
resolver: zodResolver(whitelistFormSchema),
|
||||
@ -59,6 +60,13 @@ export default function WhitelistPage() {
|
||||
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({
|
||||
mutationFn: async (data: z.infer<typeof whitelistFormSchema>) => {
|
||||
return await apiRequest("POST", "/api/whitelist", data);
|
||||
@ -189,11 +197,27 @@ export default function WhitelistPage() {
|
||||
</Dialog>
|
||||
</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">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Shield className="h-5 w-5" />
|
||||
IP Protetti ({whitelist?.length || 0})
|
||||
IP Protetti ({filteredWhitelist?.length || 0}{searchQuery && whitelist ? ` di ${whitelist.length}` : ''})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@ -201,9 +225,9 @@ export default function WhitelistPage() {
|
||||
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
|
||||
Caricamento...
|
||||
</div>
|
||||
) : whitelist && whitelist.length > 0 ? (
|
||||
) : filteredWhitelist && filteredWhitelist.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{whitelist.map((item) => (
|
||||
{filteredWhitelist.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="p-4 rounded-lg border hover-elevate"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user