'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Check, ChevronDown, Search } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Field, FieldGroup, FieldLabel } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; import { useTranslations } from 'next-intl'; import { cn } from '@/lib/utils'; type ScopeZoneGroup = { zoneDomain: string; domains: string[]; }; export function ScopeDomainDialog({ open, onOpenChange, zones, selected, pending, onSubmit, }: { open: boolean; onOpenChange: (open: boolean) => void; zones: ScopeZoneGroup[]; selected: string[]; pending: boolean; onSubmit: (domains: string[]) => void; }) { const t = useTranslations('responses'); const tc = useTranslations('common'); const [keyword, setKeyword] = useState(''); const [selectedSet, setSelectedSet] = useState>(() => new Set()); const [collapsedZones, setCollapsedZones] = useState>( () => new Set(), ); const selectedRef = useRef(selected); selectedRef.current = selected; const prevOpenRef = useRef(open); useEffect(() => { if (open && !prevOpenRef.current) { setKeyword(''); setSelectedSet(new Set(selectedRef.current)); setCollapsedZones(new Set()); } prevOpenRef.current = open; }, [open]); const filtered = useMemo(() => { const normalized = keyword.trim().toLowerCase(); if (!normalized) return zones; return zones .map((group) => ({ zoneDomain: group.zoneDomain, domains: group.domains.filter( (d) => d.toLowerCase().includes(normalized) || group.zoneDomain.toLowerCase().includes(normalized), ), })) .filter((group) => group.domains.length > 0); }, [zones, keyword]); const visibleDomains = useMemo( () => filtered.flatMap((group) => group.domains), [filtered], ); const allVisibleSelected = visibleDomains.length > 0 && visibleDomains.every((d) => selectedSet.has(d)); const toggleOne = (domain: string) => { setSelectedSet((prev) => { const next = new Set(prev); if (next.has(domain)) next.delete(domain); else next.add(domain); return next; }); }; const toggleGroup = (domains: string[]) => { setSelectedSet((prev) => { const next = new Set(prev); const allSelected = domains.every((d) => next.has(d)); for (const d of domains) { if (allSelected) next.delete(d); else next.add(d); } return next; }); }; const toggleAllVisible = () => { setSelectedSet((prev) => { const next = new Set(prev); if (allVisibleSelected) { for (const d of visibleDomains) next.delete(d); } else { for (const d of visibleDomains) next.add(d); } return next; }); }; const toggleCollapsed = (zoneDomain: string) => { setCollapsedZones((prev) => { const next = new Set(prev); if (next.has(zoneDomain)) next.delete(zoneDomain); else next.add(zoneDomain); return next; }); }; return ( {t('selectScopeTitle')} {t('selectScopeDesc')} {t('domain')}
setKeyword(event.target.value)} placeholder={t('searchDomainOrZone')} className='pl-8' disabled={pending} />
{t('selectedCount', { count: selectedSet.size })} {t('visibleCount', { count: visibleDomains.length })}
{zones.length === 0 ? (
{t('noAvailableDomains')}
) : filtered.length === 0 ? (
{t('noMatchingDomains')}
) : (
{filtered.map((group) => { const groupSelected = group.domains.filter((d) => selectedSet.has(d), ); const allSelected = groupSelected.length === group.domains.length; const someSelected = groupSelected.length > 0 && !allSelected; const open = !collapsedZones.has(group.zoneDomain); return ( toggleCollapsed(group.zoneDomain)} >
toggleGroup(group.domains)} aria-label={t('selectZone', { domain: group.zoneDomain, })} className='ml-1' />
{group.domains.map((domain) => { const checked = selectedSet.has(domain); const isApex = domain === group.zoneDomain; return ( ); })}
); })}
)}
); }