diff --git a/frontend/app/[locale]/settings/blacklist/page.tsx b/frontend/app/[locale]/settings/blacklist/page.tsx new file mode 100644 index 00000000..4cce5665 --- /dev/null +++ b/frontend/app/[locale]/settings/blacklist/page.tsx @@ -0,0 +1,132 @@ +"use client" + +import React, { useState, useEffect } from "react" +import { useTranslations } from "next-intl" +import { AlertTriangle, Loader2, Ban } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Textarea } from "@/components/ui/textarea" +import { Skeleton } from "@/components/ui/skeleton" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { useGlobalBlacklist, useUpdateGlobalBlacklist } from "@/hooks/use-global-blacklist" + +/** + * Global blacklist settings page + */ +export default function GlobalBlacklistPage() { + const t = useTranslations("pages.settings.blacklist") + + const [blacklistText, setBlacklistText] = useState("") + const [hasChanges, setHasChanges] = useState(false) + + const { data, isLoading, error } = useGlobalBlacklist() + const updateBlacklist = useUpdateGlobalBlacklist() + + // Initialize text when data loads + useEffect(() => { + if (data?.patterns) { + setBlacklistText(data.patterns.join("\n")) + setHasChanges(false) + } + }, [data]) + + // Handle text change + const handleTextChange = (e: React.ChangeEvent) => { + setBlacklistText(e.target.value) + setHasChanges(true) + } + + // Handle save + const handleSave = () => { + const patterns = blacklistText + .split("\n") + .map((line) => line.trim()) + .filter((line) => line.length > 0) + + updateBlacklist.mutate( + { patterns }, + { + onSuccess: () => { + setHasChanges(false) + }, + } + ) + } + + if (isLoading) { + return ( +
+
+ + +
+ +
+ ) + } + + if (error) { + return ( +
+ +

{t("loadError")}

+
+ ) + } + + return ( +
+ {/* Page header */} +
+

{t("title")}

+

{t("description")}

+
+ + {/* Blacklist card */} + + +
+ + {t("card.title")} +
+ {t("card.description")} +
+ + {/* Rules hint */} +
+ {t("rules.title")}: + *.gov {t("rules.domain")} + *cdn* {t("rules.keyword")} + 192.168.1.1 {t("rules.ip")} + 10.0.0.0/8 {t("rules.cidr")} +
+ + {/* Scope hint */} +
+

{t("scopeHint")}

+
+ + {/* Input */} +