'use client'; import {useEffect, useMemo, useState} from 'react'; import {Search} from 'lucide-react'; import {Button} from '@/components/ui/button'; import {Checkbox} from '@/components/ui/checkbox'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import {Input} from '@/components/ui/input'; import {Label} from '@/components/ui/label'; import {Textarea} from '@/components/ui/textarea'; import {cn} from '@/lib/utils'; import type {WAFIPGroup} from '@/lib/services/openflare'; import { type CountryOption, normalizeItems, type RuleDimension, type RuleListType, type RuleModalState, } from './helpers'; interface RuleEntryDialogProps { state: RuleModalState; countryOptions: CountryOption[]; ipGroups: WAFIPGroup[]; onClose: () => void; onChange: (patch: Partial) => void; onSubmit: () => void; } export function RuleEntryDialog({ state, countryOptions, ipGroups, onClose, onChange, onSubmit, }: RuleEntryDialogProps) { const [keyword, setKeyword] = useState(''); useEffect(() => { if (!state.open) return; setKeyword(''); }, [state.dimension, state.open]); const selectedCountrySet = useMemo( () => new Set(state.countryValues), [state.countryValues], ); const selectedIPGroupSet = useMemo( () => new Set(state.ipGroupIDs), [state.ipGroupIDs], ); const filteredCountries = useMemo(() => { const normalized = keyword.trim().toLowerCase(); return countryOptions .filter((option) => !normalized || option.searchText.includes(normalized)) .sort((left, right) => { const leftSelected = selectedCountrySet.has(left.code) ? 1 : 0; const rightSelected = selectedCountrySet.has(right.code) ? 1 : 0; return rightSelected - leftSelected || left.code.localeCompare(right.code); }); }, [countryOptions, keyword, selectedCountrySet]); const toggleCountry = (code: string) => { const values = selectedCountrySet.has(code) ? state.countryValues.filter((item) => item !== code) : normalizeItems([...state.countryValues, code]); onChange({ countryValues: values }); }; const toggleIPGroup = (id: number) => { const values = selectedIPGroupSet.has(id) ? state.ipGroupIDs.filter((item) => item !== id) : [...state.ipGroupIDs, id].sort((left, right) => left - right); onChange({ ipGroupIDs: values }); }; const typeLabel = state.listType === 'blacklist' ? '黑名单' : '白名单'; const dimensionLabel = state.dimension === 'ip' ? 'IP' : state.dimension === 'ip_group' ? 'IP 组' : '地域'; return ( !open && onClose()}> 添加{typeLabel}规则 当前准备新增 {dimensionLabel} 维度的{typeLabel}项。
{[ { value: 'blacklist', label: '黑名单' }, { value: 'whitelist', label: '白名单' }, ].map((option) => ( ))}
{[ { value: 'ip', label: 'IP' }, { value: 'ip_group', label: 'IP 组' }, { value: 'country', label: '地域' }, ].map((option) => ( ))}
{state.dimension === 'ip' ? (