'use client'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Loader2, Plus, RefreshCw, Shield } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'sonner'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { EmptyStateWithBorder } from '@/components/layout/empty'; import { ErrorInline } from '@/components/layout/error'; import { LoadingStateWithBorder } from '@/components/layout/loading'; import type { WAFRule } from '@/lib/services/openflare'; import { WafService } from '@/lib/services/openflare'; import { CreateRuleDialog } from './components/create-rule-dialog'; import { getErrorMessage } from './components/helpers'; import { RuleGroupsTable } from './components/rule-groups-table'; const ruleGroupsQueryKey = ['openflare', 'waf', 'rule-groups']; export default function WafPage() { const t = useTranslations('waf'); const tCommon = useTranslations('common'); const router = useRouter(); const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const groupsQuery = useQuery({ queryKey: ruleGroupsQueryKey, queryFn: () => WafService.listRuleGroups(), }); const invalidate = async () => { await Promise.all([ queryClient.invalidateQueries({ queryKey: ruleGroupsQueryKey }), queryClient.invalidateQueries({ queryKey: ['openflare', 'config-versions', 'diff'], }), ]); }; const createMutation = useMutation({ mutationFn: (name: string) => WafService.createRule({ name }), onSuccess: (rule) => { toast.success(t('created')); setCreateOpen(false); router.push(`/waf/rules/editor?id=${rule.id}`); }, onError: (error) => { toast.error(getErrorMessage(error, t('operationFailed'))); }, }); const deleteMutation = useMutation({ mutationFn: (id: number) => WafService.deleteRuleGroup(id), onSuccess: async () => { toast.success(t('groupDeleted')); setDeleteTarget(null); await invalidate(); }, onError: (error) => { toast.error(getErrorMessage(error, t('operationFailed'))); }, }); const handleRefresh = () => { void queryClient.invalidateQueries({ queryKey: ruleGroupsQueryKey }); }; const groups = groupsQuery.data ?? []; const loading = groupsQuery.isLoading; const error = groupsQuery.error ?? null; return (

{t('title')}

{t('ruleGroups')} {t('ruleGroupsDesc')}
{loading ? ( ) : error ? (
) : groups.length === 0 ? ( ) : ( router.push(`/waf/rules/editor?id=${rule.id}`)} onDelete={setDeleteTarget} /> )}
{ await createMutation.mutateAsync(name); }} /> !open && setDeleteTarget(null)} > {t('deleteTitle')} {t('deleteDesc', { name: deleteTarget?.name ?? '' })} {tCommon('cancel')} deleteTarget && deleteMutation.mutate(deleteTarget.id) } > {deleteMutation.isPending ? t('deleting') : t('confirmDelete')}
); }