import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '@evobgp/ui/components/dialog' import { Input } from '@evobgp/ui/components/input' import { Label } from '@evobgp/ui/components/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@evobgp/ui/components/select' import { Button } from '@evobgp/ui/components/button' import { LoadingButton } from '@/components/loading-button' import { CommunitySelect } from '@/components/modules/community-select' import { ApiError, apiMutate } from '@/lib/api-client' import { normalizeCdnSourceKind } from '@/lib/modules/helpers' import type { BgpCommunity, CdnPreviewResponse, CdnSource, CdnSourceCreate } from '@/types/api' const CDN_KIND_ITEMS = [ { value: 'plaintext', label: 'plaintext' }, { value: 'json', label: 'json' }, ] as const interface ModuleCdnSourceDialogProps { open: boolean moduleId: string edit: CdnSource | null communities: BgpCommunity[] onOpenChange: (open: boolean) => void onSaved: () => void | Promise } type CdnForm = CdnSourceCreate & { refresh_interval_sec?: number | null } export function ModuleCdnSourceDialog({ open, moduleId, edit, communities, onOpenChange, onSaved, }: ModuleCdnSourceDialogProps) { const [saving, setSaving] = useState(false) const [previewLoading, setPreviewLoading] = useState(false) const [previewItems, setPreviewItems] = useState([]) const [previewTotal, setPreviewTotal] = useState(0) const [previewTruncated, setPreviewTruncated] = useState(false) const [previewError, setPreviewError] = useState(null) const [previewOk, setPreviewOk] = useState(false) const [form, setForm] = useState({ url: '', source_kind: 'plaintext', prefix_path: '', community_id: null, }) const kindItems = useMemo(() => [...CDN_KIND_ITEMS], []) function clearPreview() { setPreviewLoading(false) setPreviewItems([]) setPreviewTotal(0) setPreviewTruncated(false) setPreviewError(null) setPreviewOk(false) } useEffect(() => { if (!open) { clearPreview() return } setForm( edit ? { url: edit.url, source_kind: normalizeCdnSourceKind(edit.source_kind), prefix_path: edit.prefix_path ?? '', community_id: edit.community_id, refresh_interval_sec: edit.refresh_interval_sec, } : { url: '', source_kind: 'plaintext', prefix_path: '', community_id: null }, ) clearPreview() }, [open, edit]) async function previewCdn() { const urlTrim = form.url.trim() if (!urlTrim) { toast.error('Укажите URL') return } setPreviewLoading(true) setPreviewError(null) setPreviewOk(false) try { const res = await apiMutate( `/v1/modules/${moduleId}/cdn-sources/preview`, 'POST', { url: urlTrim, source_kind: form.source_kind, prefix_path: form.prefix_path?.trim() ?? '', }, ) setPreviewItems(res.items) setPreviewTotal(res.total) setPreviewTruncated(res.truncated) setPreviewOk(true) } catch (e) { setPreviewError(e instanceof ApiError ? e.message : String(e)) setPreviewItems([]) setPreviewTotal(0) setPreviewTruncated(false) setPreviewOk(false) } finally { setPreviewLoading(false) } } async function save() { const urlTrim = form.url.trim() if (!urlTrim) { toast.error('Укажите URL') return } setSaving(true) try { const body = { ...form, url: urlTrim, source_kind: form.source_kind, prefix_path: form.prefix_path?.trim() ?? '', } if (edit) { await apiMutate(`/v1/modules/${moduleId}/cdn-sources/${edit.id}`, 'PATCH', body) toast.success('Источник обновлён') } else { await apiMutate(`/v1/modules/${moduleId}/cdn-sources`, 'POST', body) toast.success('Источник добавлен') } clearPreview() onOpenChange(false) await onSaved() } catch (e) { toast.error(e instanceof ApiError ? e.message : String(e)) } finally { setSaving(false) } } return ( {edit ? 'Редактировать источник' : 'Новый CDN-источник'}
setForm((s) => ({ ...s, url: e.target.value }))} />
setForm((s) => ({ ...s, prefix_path: e.target.value }))} /> {form.source_kind === 'json' && !form.prefix_path?.trim() ? (

Для JSON укажите путь к полям с CIDR; пустой путь может не дать префиксов.

) : null}
setForm((s) => ({ ...s, community_id: v }))} communities={communities} nullable />
setForm((s) => ({ ...s, refresh_interval_sec: e.target.value ? Number(e.target.value) : null, })) } />
{previewError ? ( {previewError} ) : previewOk ? ( Всего: {previewTotal} {previewTruncated ? ( (обрезано) ) : null} ) : null}
{previewItems.length > 0 ? (
    {previewItems.map((item, i) => (
  • {item}
  • ))}
) : null}
onOpenChange(false)}> Отмена void save()}> {edit ? 'Сохранить' : 'Добавить'}
) }