import { useState } from 'react' import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import DataTable from '../../components/DataTable' import apiClient, { isEnvelope } from '../../api/client' import type { FwService, ServiceGroup } from './types' interface FormValues { name: string description?: string member_ids?: number[] } async function listGroups(): Promise { const r = await apiClient.get('/firewall/service-groups') if (!isEnvelope(r.data)) return [] return (r.data.data as { service_groups?: ServiceGroup[] }).service_groups ?? [] } async function listServices(): Promise { const r = await apiClient.get('/firewall/services') if (!isEnvelope(r.data)) return [] return (r.data.data as { services?: FwService[] }).services ?? [] } export default function ServiceGroupsTab() { const { t } = useTranslation() const qc = useQueryClient() const { data: groups, isLoading } = useQuery({ queryKey: ['fw', 'svc-grp'], queryFn: listGroups }) const { data: services } = useQuery({ queryKey: ['fw', 'svc'], queryFn: listServices }) const svcLabel = (id: number) => services?.find(s => s.id === id)?.name ?? `#${id}` const [editing, setEditing] = useState(null) const [creating, setCreating] = useState(false) const [form] = Form.useForm() const create = useMutation({ mutationFn: async (v: FormValues) => { await apiClient.post('/firewall/service-groups', v) }, onSuccess: () => { message.success(t('common.save')); setCreating(false); form.resetFields() void qc.invalidateQueries({ queryKey: ['fw', 'svc-grp'] }) }, }) const update = useMutation({ mutationFn: async ({ id, v }: { id: number; v: FormValues }) => { await apiClient.put(`/firewall/service-groups/${id}`, v) }, onSuccess: () => { message.success(t('common.save')); setEditing(null); form.resetFields() void qc.invalidateQueries({ queryKey: ['fw', 'svc-grp'] }) }, }) const del = useMutation({ mutationFn: async (id: number) => { await apiClient.delete(`/firewall/service-groups/${id}`) }, onSuccess: () => { void qc.invalidateQueries({ queryKey: ['fw', 'svc-grp'] }) }, }) const columns: ColumnsType = [ { title: t('fw.sg.name'), dataIndex: 'name', key: 'name' }, { title: t('fw.sg.members'), key: 'members', render: (_, row) => ( {(row.member_ids ?? []).map((id) => {svcLabel(id)})} {(row.member_ids?.length ?? 0) === 0 && } ), }, { title: t('fw.sg.description'), dataIndex: 'description', key: 'desc', render: (v?: string) => v ?? '—' }, { title: t('common.edit'), key: 'actions', render: (_, row) => ( del.mutate(row.id)}> ), }, ] return ( <> { setEditing(null); setCreating(false) }} onOk={() => { void form.submit() }} confirmLoading={create.isPending || update.isPending} >
{ if (editing) update.mutate({ id: editing.id, v }); else create.mutate(v) }} >
) }