feat: Implement community badge rendering and enhance community suggestions in ASNs, Domains, and IPRanges managers for improved user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m9s

This commit is contained in:
2025-08-10 17:43:49 +07:00
parent e5f8544c2e
commit f74586c4ff
3 changed files with 57 additions and 12 deletions
+19 -4
View File
@@ -48,6 +48,18 @@ function ASNsNewManager() {
})();
}, []);
const renderCommunityBadge = (value) => {
const v = String(value ?? '').trim();
if (!v) return <span className="badge bg-blue-lt text-blue"></span>;
const meta = communities.find(c => c.value === v);
const color = (meta?.color || 'blue').toLowerCase().replace(/[^a-z-]/g, '');
const cls = `badge bg-${color}-lt text-${color}`;
const title = meta ? `${meta.name ? meta.name + ' — ' : ''}${meta.description || ''}${meta.tags && meta.tags.length ? ' (' + meta.tags.join(', ') + ')' : ''}` : '';
return (
<span className={cls} title={title}>{v}</span>
);
};
const isValidAsn = (value) => /^[0-9]+$/.test(String(value).trim());
// Допускаем как числовые, так и строковые (AS:NNN) community
const isValidCommunity = (value) => /^(\d+|\d+:\d+)$/.test(String(value).trim());
@@ -423,7 +435,7 @@ function ASNsNewManager() {
{paginatedItems.map((item) => (
<tr key={item.asn} className={editingAsn === item.asn ? 'table-info' : ''}>
<td>{item.asn}</td>
<td><span className="badge bg-blue-lt text-blue">{item.community}</span></td>
<td>{renderCommunityBadge(item.community)}</td>
<td className="text-end">
{editingAsn === item.asn ? (
<>
@@ -543,9 +555,12 @@ function ASNsNewManager() {
)}
{/* Общий список подсказок community */}
<datalist id="community-options">
{communities.map(c => (
<option key={c.value} value={c.value}>{c.name ? `${c.name}${c.value}` : c.value}</option>
))}
{communities.map(c => {
const label = `${c.name ? c.name + ' — ' : ''}${c.value}${c.description ? ' — ' + c.description : ''}${c.tags && c.tags.length ? ' (' + c.tags.join(', ') + ')' : ''}`;
return (
<option key={c.value} value={c.value} label={label}>{label}</option>
);
})}
</datalist>
</>
);
+19 -4
View File
@@ -48,6 +48,18 @@ function DomainsNewManager() {
})();
}, []);
const renderCommunityBadge = (value) => {
const v = String(value ?? '').trim();
if (!v) return <span className="badge bg-blue-lt text-blue"></span>;
const meta = communities.find(c => c.value === v);
const color = (meta?.color || 'blue').toLowerCase().replace(/[^a-z-]/g, '');
const cls = `badge bg-${color}-lt text-${color}`;
const title = meta ? `${meta.name ? meta.name + ' — ' : ''}${meta.description || ''}${meta.tags && meta.tags.length ? ' (' + meta.tags.join(', ') + ')' : ''}` : '';
return (
<span className={cls} title={title}>{v}</span>
);
};
const isValidDomain = (value) => {
const v = String(value).trim().toLowerCase();
// Простой валидатор FQDN: sub.domain.tld
@@ -429,7 +441,7 @@ function DomainsNewManager() {
{paginatedItems.map((item) => (
<tr key={item.domain} className={editingDomain === item.domain ? 'table-info' : ''}>
<td>{item.domain}</td>
<td><span className="badge bg-blue-lt text-blue">{item.community}</span></td>
<td>{renderCommunityBadge(item.community)}</td>
<td className="text-end">
{editingDomain === item.domain ? (
<>
@@ -549,9 +561,12 @@ function DomainsNewManager() {
)}
{/* Общий список подсказок community */}
<datalist id="community-options">
{communities.map(c => (
<option key={c.value} value={c.value}>{c.name ? `${c.name}${c.value}` : c.value}</option>
))}
{communities.map(c => {
const label = `${c.name ? c.name + ' — ' : ''}${c.value}${c.description ? ' — ' + c.description : ''}${c.tags && c.tags.length ? ' (' + c.tags.join(', ') + ')' : ''}`;
return (
<option key={c.value} value={c.value} label={label}>{label}</option>
);
})}
</datalist>
</>
);
+19 -4
View File
@@ -48,6 +48,18 @@ function IPRangesManager() {
})();
}, []);
const renderCommunityBadge = (value) => {
const v = String(value ?? '').trim();
if (!v) return <span className="badge bg-blue-lt text-blue"></span>;
const meta = communities.find(c => c.value === v);
const color = (meta?.color || 'blue').toLowerCase().replace(/[^a-z-]/g, '');
const cls = `badge bg-${color}-lt text-${color}`;
const title = meta ? `${meta.name ? meta.name + ' — ' : ''}${meta.description || ''}${meta.tags && meta.tags.length ? ' (' + meta.tags.join(', ') + ')' : ''}` : '';
return (
<span className={cls} title={title}>{v}</span>
);
};
const isValidIPv4 = (ip) => {
const octets = ip.split('.');
if (octets.length !== 4) return false;
@@ -438,7 +450,7 @@ function IPRangesManager() {
{paginatedItems.map((item) => (
<tr key={item.ipRange} className={editingIp === item.ipRange ? 'table-info' : ''}>
<td>{item.ipRange}</td>
<td><span className="badge bg-blue-lt text-blue">{item.community}</span></td>
<td>{renderCommunityBadge(item.community)}</td>
<td className="text-end">
{editingIp === item.ipRange ? (
<>
@@ -563,9 +575,12 @@ function IPRangesManager() {
)}
{/* Общий список подсказок community */}
<datalist id="community-options">
{communities.map(c => (
<option key={c.value} value={c.value}>{c.name ? `${c.name}${c.value}` : c.value}</option>
))}
{communities.map(c => {
const label = `${c.name ? c.name + ' — ' : ''}${c.value}${c.description ? ' — ' + c.description : ''}${c.tags && c.tags.length ? ' (' + c.tags.join(', ') + ')' : ''}`;
return (
<option key={c.value} value={c.value} label={label}>{label}</option>
);
})}
</datalist>
</>
);