feat(firewall): Inline-Note + Labels direkt in der Tabelle
- Migration 0035: note TEXT + labels TEXT[] für firewall_rules + nat_rules - PATCH /firewall/rules/:id + /nat-rules/:id für note/labels Updates - InlineNote: gold Tag mit MessageOutlined, Klick zum Bearbeiten (Enter/Blur speichert) - InlineLabels: geekblue Tags mit X-Button zum Entfernen, "+" zum Hinzufügen - Name-Spalte: Name + Labels + Note in Zeile 1, comment/auto-desc in Zeile 2 - Gleiche UX für NAT-Regeln Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import type { ColumnsType } from 'antd/es/table'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ArrowDownOutlined, ArrowUpOutlined, BranchesOutlined, CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons'
|
||||
import { InlineNote, InlineLabels } from './InlineEditors'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
@@ -89,6 +90,22 @@ export default function NATRulesTab() {
|
||||
onError: (e: Error) => message.error(e.message),
|
||||
})
|
||||
|
||||
const patchNote = useMutation({
|
||||
mutationFn: async ({ id, note }: { id: number; note: string }) => {
|
||||
await apiClient.patch(`/firewall/nat-rules/${id}`, { note })
|
||||
},
|
||||
onSuccess: () => { void qc.invalidateQueries({ queryKey: ['fw', 'nat'] }) },
|
||||
onError: (e: Error) => message.error(e.message),
|
||||
})
|
||||
|
||||
const patchLabels = useMutation({
|
||||
mutationFn: async ({ id, labels }: { id: number; labels: string[] }) => {
|
||||
await apiClient.patch(`/firewall/nat-rules/${id}`, { labels })
|
||||
},
|
||||
onSuccess: () => { void qc.invalidateQueries({ queryKey: ['fw', 'nat'] }) },
|
||||
onError: (e: Error) => message.error(e.message),
|
||||
})
|
||||
|
||||
const duplicate = useMutation({
|
||||
mutationFn: async (r: NATRule) => {
|
||||
const { id: _id, created_at: _ca, updated_at: _ua, ...rest } = r as NATRule & { created_at?: unknown; updated_at?: unknown }
|
||||
@@ -174,11 +191,23 @@ export default function NATRulesTab() {
|
||||
title: t('fw.nat.name'), key: 'name',
|
||||
render: (_, r) => (
|
||||
<div>
|
||||
{r.name
|
||||
? <div className="fw-rule-name" style={{ fontWeight: 500, fontSize: 12, color: '#0F172A' }}>{r.name}</div>
|
||||
: <div className="fw-rule-name" style={{ fontSize: 12, color: '#94A3B8', fontStyle: 'italic' }}>{t('fw.rule.unnamed')}</div>
|
||||
}
|
||||
{r.comment && <div style={{ fontSize: 11, color: '#64748B', marginTop: 1 }}>{r.comment}</div>}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
|
||||
{r.name
|
||||
? <span className="fw-rule-name" style={{ fontWeight: 500, fontSize: 12, color: '#0F172A' }}>{r.name}</span>
|
||||
: <span className="fw-rule-name" style={{ fontSize: 12, color: '#94A3B8', fontStyle: 'italic' }}>{t('fw.rule.unnamed')}</span>
|
||||
}
|
||||
<InlineLabels
|
||||
labels={r.labels ?? []}
|
||||
disabled={isViewer}
|
||||
onSave={labels => patchLabels.mutate({ id: r.id, labels })}
|
||||
/>
|
||||
<InlineNote
|
||||
value={r.note}
|
||||
disabled={isViewer}
|
||||
onSave={note => patchNote.mutate({ id: r.id, note })}
|
||||
/>
|
||||
</div>
|
||||
{r.comment && <div style={{ fontSize: 11, color: '#64748B', marginTop: 2 }}>{r.comment}</div>}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user