import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import type { NodeInfo } from '@/api/adminRemnawave'; import { GeoCheckIcon, PlayIcon, WarningIcon, XCloseIcon } from '@/components/icons'; import { Spinner } from '@/components/ui/Spinner'; import { useFocusTrap } from '@/hooks/useFocusTrap'; import { cn } from '@/lib/utils'; import { GeoCheckReport } from './GeoCheckReport'; import { buildGeoCheckRequest, isRouteReady, type GeoCheckRouteMode } from './geoCheckRoute'; import { GeoCheckSetup } from './GeoCheckSetup'; import { useGeoCheckJob } from './useGeoCheckJob'; interface GeoCheckModalProps { node: NodeInfo; onClose: () => void; } /** * GeoCheck ноды: выбор маршрута → ожидание → отчёт. * * Полноэкранный просмотр — режим самой модалки, а не отдельный оверлей: * оверлей поверх портала выпал бы из ловушки фокуса, и его кнопка закрытия * стала бы недоступна с клавиатуры. */ export function GeoCheckModal({ node, onClose }: GeoCheckModalProps) { const { t } = useTranslation(); const [mode, setMode] = useState('default'); const [value, setValue] = useState(''); const [fullscreen, setFullscreen] = useState(false); const job = useGeoCheckJob(node.uuid); const isRunning = job.phase === 'running'; // Пока идёт проверка, закрывать по Escape нельзя: задача уже поставлена // в панель, и молча потерять её результат — хуже, чем подождать. const dialogRef = useFocusTrap(true, { onEscape: isRunning ? undefined : fullscreen ? () => setFullscreen(false) : onClose, }); useEffect(() => { if (job.phase !== 'done') setFullscreen(false); }, [job.phase]); const canStart = isRouteReady(mode, value); const handleStart = () => { if (!canStart) return; job.start(buildGeoCheckRequest(mode, value)); }; const handleBackdropClick = () => { if (!isRunning) onClose(); }; const errorText = job.error?.kind === 'timeout' ? t( 'admin.remnawave.geoCheck.error.timeout', 'The node did not answer in time. Try running the check again.', ) : (job.error?.message ?? t('admin.remnawave.geoCheck.error.generic', 'The check could not be completed.')); return createPortal(

{t('admin.remnawave.geoCheck.title', 'GeoCheck')}

{node.name}

{!isRunning && ( )}
{job.phase === 'idle' && ( <> { setMode(next); setValue(''); }} onValueChange={setValue} />
)} {isRunning && (

{t('admin.remnawave.geoCheck.running', 'Running the geo check')}

{t( 'admin.remnawave.geoCheck.runningHint', 'The node is testing its connection — this usually takes up to a minute.', )}

)} {job.phase === 'error' && (

{t('admin.remnawave.geoCheck.error.title', 'Check failed')}

{errorText}

)} {job.phase === 'done' && job.result && ( setFullscreen((v) => !v)} onRerun={job.retry} /> )}
, document.body, ); }