feat: Add ConfirmDialog for clearing community fields in ASNs, Domains, and IPRanges managers with appropriate state management and UI integration
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 15m31s

This commit is contained in:
2025-08-14 14:21:14 +07:00
parent 7348cc2c79
commit e6878a5b08
5 changed files with 109 additions and 7 deletions
+61 -6
View File
@@ -40,8 +40,56 @@ function CommunitiesManager() {
const fetchItems = async () => {
setLoading(true);
try {
const res = await api.get(`/communities`);
setItems(res.data);
const [dictRes, asnsRes, ipRes, domRes] = await Promise.all([
api.get(`/communities`),
api.get(`/asns`, { params: { offset: 0, limit: 0 } }).catch(() => ({ data: [] })),
api.get(`/ip-ranges`, { params: { offset: 0, limit: 0 } }).catch(() => ({ data: [] })),
api.get(`/domains-new`, { params: { offset: 0, limit: 0 } }).catch(() => ({ data: [] })),
]);
const dict = Array.isArray(dictRes.data) ? dictRes.data : [];
const asnsPayload = Array.isArray(asnsRes.data?.items) ? asnsRes.data.items : (Array.isArray(asnsRes.data) ? asnsRes.data : []);
const ipPayload = Array.isArray(ipRes.data?.items) ? ipRes.data.items : (Array.isArray(ipRes.data) ? ipRes.data : []);
const domPayload = Array.isArray(domRes.data?.items) ? domRes.data.items : (Array.isArray(domRes.data) ? domRes.data : []);
const usedCommunities = new Set();
// ASNs: fields domain/type
for (const it of asnsPayload) {
const v = String(it.type ?? '').trim();
if (v) usedCommunities.add(v);
}
// IP ranges: fields ipRange/community
for (const it of ipPayload) {
const v = String(it.community ?? '').trim();
if (v) usedCommunities.add(v);
}
// Domains: fields domain/community
for (const it of domPayload) {
const v = String(it.community ?? '').trim();
if (v) usedCommunities.add(v);
}
const map = new Map();
// put dictionary items first
for (const d of dict) {
const value = String(d.value || '').trim();
if (!value) continue;
map.set(value, {
value,
name: String(d.name || ''),
description: String(d.description || ''),
tags: Array.isArray(d.tags) ? d.tags : [],
gatewayDefault: String(d.gatewayDefault || ''),
color: String(d.color || ''),
_external: false,
});
}
// add unknown ones from usage
for (const v of usedCommunities) {
if (!map.has(v)) {
map.set(v, { value: v, name: '', description: '', tags: [], gatewayDefault: '', color: '', _external: true });
}
}
setItems(Array.from(map.values()));
setError('');
} catch (e) {
console.error('Error fetching communities:', e);
@@ -54,7 +102,9 @@ function CommunitiesManager() {
const saveAll = async (data) => {
setLoading(true);
try {
await api.post(`/communities`, { communities: data });
// не отправляем служебное поле _external
const payload = data.map(({ _external, ...rest }) => rest);
await api.post(`/communities`, { communities: payload });
setSuccess('Справочник сохранён!');
setTimeout(() => setSuccess(''), 3000);
} catch (e) {
@@ -256,7 +306,7 @@ function CommunitiesManager() {
<div className="table-responsive">
<table className="table card-table table-vcenter table-nowrap mb-0">
<thead>
<tr>
<tr>
<th className="cursor-pointer" onClick={()=>changeSort('value')}>Value {sortField==='value' && <span className="ms-1">{sortOrder==='asc'?'▲':'▼'}</span>}</th>
<th className="cursor-pointer" onClick={()=>changeSort('name')}>Название {sortField==='name' && <span className="ms-1">{sortOrder==='asc'?'▲':'▼'}</span>}</th>
<th>Описание</th>
@@ -271,12 +321,17 @@ function CommunitiesManager() {
) : paginated.length === 0 ? (
<tr><td colSpan={6} className="text-center text-muted py-4">Нет записей</td></tr>
) : paginated.map((item) => (
<tr key={item.value} className={editingValue === item.value ? 'table-info' : ''}>
<tr key={item.value} className={editingValue === item.value ? 'table-info' : ''}>
<td>
{editingValue === item.value ? (
<input className="form-control form-control-sm" value={editingDraft.value} onChange={(e)=>setEditingDraft({...editingDraft, value:e.target.value})} ref={editRef} />
) : (
<code className="text-blue">{item.value}</code>
<>
<code className="text-blue">{item.value}</code>
{item._external && (
<span className="badge bg-yellow-lt text-yellow ms-2" title="Не внесён в справочник">не в справ.</span>
)}
</>
)}
</td>
<td>