'use client'; import Link from 'next/link'; import {usePathname, useRouter, useSearchParams} from 'next/navigation'; import {useCallback, useMemo, useState} from 'react'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import {ArrowLeft, Globe, Pencil, Trash2} from 'lucide-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 {EmptyStateWithBorder} from '@/components/layout/empty'; import {LoadingStateWithBorder} from '@/components/layout/loading'; import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'; import { ProxyRouteService, TlsCertificateService, ZoneService, zoneQueryKey, } from '@/lib/services/openflare'; import {getErrorMessage} from '../components/website-utils'; import {ZoneCertificatesPanel} from './components/zone-certificates'; import {ZoneDomainsTable} from './components/zone-domains-table'; import {ZoneEditorDialog} from './components/zone-editor-dialog'; import {ZoneOverviewPanel} from './components/zone-overview'; const zoneTabs = ['overview', 'domains', 'certificates', 'settings'] as const; export type ZonePageTab = (typeof zoneTabs)[number]; function getZonePageTab(value: string | null | undefined): ZonePageTab { return zoneTabs.includes(value as ZonePageTab) ? (value as ZonePageTab) : 'overview'; } export function ZonePageClient({zoneId}: {zoneId: number}) { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const queryClient = useQueryClient(); const activeTab = useMemo( () => getZonePageTab(searchParams.get('tab')), [searchParams], ); const [editZone, setEditZone] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const setActiveTab = useCallback( (tab: string) => { const next = getZonePageTab(tab); const params = new URLSearchParams(searchParams.toString()); if (next === 'overview') { params.delete('tab'); } else { params.set('tab', next); } const query = params.toString(); router.replace(query ? `${pathname}?${query}` : pathname, {scroll: false}); }, [pathname, router, searchParams], ); const overviewQuery = useQuery({ queryKey: [...zoneQueryKey, zoneId], queryFn: () => ZoneService.getOverview(zoneId), enabled: Number.isInteger(zoneId) && zoneId > 0, }); const certificatesQuery = useQuery({ queryKey: ['openflare', 'tls-certificates'], queryFn: () => TlsCertificateService.list(), enabled: overviewQuery.isSuccess, }); const routesQuery = useQuery({ queryKey: ['openflare', 'proxy-routes'], queryFn: () => ProxyRouteService.list(), enabled: overviewQuery.isSuccess, }); const deleteZone = useMutation({ mutationFn: () => ZoneService.deleteById(zoneId), onSuccess: async () => { toast.success('Zone 已删除'); await queryClient.invalidateQueries({queryKey: zoneQueryKey}); window.location.assign('/websites'); }, onError: (error) => toast.error(getErrorMessage(error)), }); if (!Number.isInteger(zoneId) || zoneId <= 0) { return (
Zone 网站管理
Zone 设置
根域和备注可在此 Zone 的编辑操作中维护。