Domains tab updates
This commit is contained in:
@@ -29,17 +29,19 @@ window.paginationState = {
|
||||
|
||||
// Helper function to get consensus status badge
|
||||
function getConsensusStatusBadge(status) {
|
||||
const badges = {
|
||||
'resolved': '<span class="px-2 py-1 bg-green-500 text-white rounded text-xs">Resolved</span>',
|
||||
'tie': '<span class="px-2 py-1 bg-yellow-500 text-white rounded text-xs">Tie</span>',
|
||||
'conflict': '<span class="px-2 py-1 bg-red-500 text-white rounded text-xs">Conflict</span>',
|
||||
'insufficient_quorum': '<span class="px-2 py-1 bg-orange-500 text-white rounded text-xs">No Quorum</span>',
|
||||
'no_claims': '<span class="px-2 py-1 bg-gray-500 text-white rounded text-xs">No Claims</span>',
|
||||
'error': '<span class="px-2 py-1 bg-red-500 text-white rounded text-xs">Error</span>',
|
||||
'internal': '<span class="px-2 py-1 bg-blue-500 text-white rounded text-xs">Internal</span>',
|
||||
'unknown': '<span class="px-2 py-1 bg-gray-400 text-white rounded text-xs">Unknown</span>'
|
||||
const labels = {
|
||||
resolved: 'Resolved',
|
||||
tie: 'Tie',
|
||||
conflict: 'Conflict',
|
||||
insufficient_quorum: 'No quorum',
|
||||
no_claims: 'No claims',
|
||||
error: 'Error',
|
||||
internal: 'Internal',
|
||||
unknown: 'Unknown'
|
||||
};
|
||||
return badges[status] || badges['unknown'];
|
||||
const key = labels[status] ? status : 'unknown';
|
||||
const esc = window.escapeHtml || ((value) => String(value));
|
||||
return `<span class="domain-consensus-badge domain-consensus-badge--${key}">${esc(labels[key])}</span>`;
|
||||
}
|
||||
|
||||
// Make function globally available
|
||||
@@ -142,53 +144,60 @@ window.tabs = {
|
||||
filteredKey: 'filteredDomains',
|
||||
containerId: 'domainsTable',
|
||||
paginationId: 'domainsPagination',
|
||||
sentinelId: 'domainsScrollSentinel',
|
||||
sort: (a, b) => a.domain.localeCompare(b.domain, undefined, { sensitivity: 'base' }),
|
||||
filter: (item, query) => item.domain.toLowerCase().includes(query) || item.hash.toLowerCase().includes(query) || (item.consensusStatus || '').toLowerCase().includes(query),
|
||||
renderItem: (item) => {
|
||||
const esc = window.escapeHtml || ((value) => String(value));
|
||||
const domainAttr = esc(item.domain).replace(/"/g, '"');
|
||||
const tr = document.createElement('tr');
|
||||
tr.className = 'border-b hover:bg-gray-50 dark:hover:bg-gray-700';
|
||||
|
||||
// Use consensus status from postFetch (which may include 'conflict')
|
||||
let consensusInfo = '';
|
||||
tr.className = 'domain-row';
|
||||
|
||||
const localBadge = item.isLocal
|
||||
? ' <span class="domain-local-badge" title="Local claim"><i class="fas fa-house-user" aria-hidden="true"></i></span>'
|
||||
: '';
|
||||
|
||||
let consensusCell = '';
|
||||
if (item.consensusState) {
|
||||
const consensusState = item.consensusState;
|
||||
const statusBadge = getConsensusStatusBadge(item.consensusStatus);
|
||||
const voteInfo = Object.keys(consensusState.voteCounts || {}).length > 0
|
||||
? ` (${Object.values(consensusState.voteCounts).reduce((a, b) => a + b, 0)} votes)`
|
||||
: '';
|
||||
const quorumInfo = consensusState.quorumMet ? '✓' : '✗';
|
||||
consensusInfo = `<td class="p-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>${statusBadge}</span>
|
||||
<span class="text-xs" style="color: var(--text-primary);">${voteInfo}</span>
|
||||
<span class="text-xs" style="color: var(--text-primary);" title="Quorum: ${consensusState.quorumMet ? 'Met' : 'Not Met'}">${quorumInfo}</span>
|
||||
const voteTotal = Object.values(item.consensusState.voteCounts || {}).reduce((a, b) => a + b, 0);
|
||||
const quorumMet = item.consensusState.quorumMet;
|
||||
consensusCell = `<td class="p-3">
|
||||
<div class="domain-consensus-cell">
|
||||
${statusBadge}
|
||||
${voteTotal > 0 ? `<span class="domain-vote-count theme-text-tertiary">${voteTotal} vote${voteTotal === 1 ? '' : 's'}</span>` : ''}
|
||||
<span class="domain-quorum ${quorumMet ? 'domain-quorum--met' : 'domain-quorum--miss'}" title="Quorum ${quorumMet ? 'met' : 'not met'}">${quorumMet ? '✓' : '✗'}</span>
|
||||
</div>
|
||||
</td>`;
|
||||
} else if (item.consensusStatus === 'internal') {
|
||||
consensusInfo = `<td class="p-3">${getConsensusStatusBadge('internal')}</td>`;
|
||||
consensusCell = `<td class="p-3">${getConsensusStatusBadge('internal')}</td>`;
|
||||
} else {
|
||||
consensusInfo = `<td class="p-3">${getConsensusStatusBadge('unknown')}</td>`;
|
||||
consensusCell = `<td class="p-3">${getConsensusStatusBadge(item.consensusStatus || 'unknown')}</td>`;
|
||||
}
|
||||
|
||||
tr.innerHTML = `<td class="p-3">${item.domain}${item.isLocal ? ' <span class="domain-local-badge" title="Local claim"><i class="fas fa-house-user" aria-hidden="true"></i></span>' : ''}</td>
|
||||
<td class="p-3 break-all">${item.hash}</td>
|
||||
${consensusInfo}
|
||||
<td class="p-3">${item.isLocal && item.hash !== 'internal' ? `<button onclick="removeDomain('${item.domain}')" class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600">Remove</button>` : ''}</td>`;
|
||||
|
||||
const canRemove = item.isLocal && item.hash !== 'internal';
|
||||
const removeBtn = canRemove
|
||||
? `<button type="button" class="admin-btn admin-btn--danger admin-btn--sm" data-domain="${domainAttr}" onclick="removeDomain(this.getAttribute('data-domain'))" title="Remove local claim">
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
</button>`
|
||||
: '';
|
||||
|
||||
tr.innerHTML = `
|
||||
<td class="p-3"><code class="domain-name">${esc(item.domain)}</code>${localBadge}</td>
|
||||
<td class="p-3"><code class="domain-hash">${esc(item.hash)}</code></td>
|
||||
${consensusCell}
|
||||
<td class="p-3 text-right">
|
||||
<div class="domain-row-actions">${removeBtn}</div>
|
||||
</td>`;
|
||||
return tr;
|
||||
},
|
||||
postFetch: (data) => {
|
||||
// Data already contains consensusState from backend
|
||||
return data.map(item => {
|
||||
if (item.consensusStatus === 'internal' || item.hash === 'internal' || item.hash === 'none') {
|
||||
return { ...item, consensusStatus: 'internal' };
|
||||
}
|
||||
// Use consensusStatus set by backend (including 'conflict'), fallback to consensusState status
|
||||
return {
|
||||
...item,
|
||||
consensusStatus: item.consensusStatus || item.consensusState?.status || 'unknown'
|
||||
};
|
||||
});
|
||||
}
|
||||
preRender: (visibleCount) => {
|
||||
if (window.updateDomainsChrome) window.updateDomainsChrome(visibleCount);
|
||||
},
|
||||
onAllItemsLoaded: () => {
|
||||
if (window.showDomainsListEnd) window.showDomainsListEnd();
|
||||
},
|
||||
postFetch: (data) => (window.normalizeDomainsData ? window.normalizeDomainsData(data) : data)
|
||||
},
|
||||
entries: {
|
||||
api: '/api/entries',
|
||||
|
||||
@@ -361,7 +361,13 @@ function filterDomains() {
|
||||
const data = window[config.dataKey];
|
||||
if (!data || !Array.isArray(data)) return;
|
||||
|
||||
const filtered = data.filter(item => {
|
||||
if (window.normalizeDomainsData) {
|
||||
window[config.dataKey] = window.normalizeDomainsData(data);
|
||||
}
|
||||
|
||||
const normalizedData = window[config.dataKey];
|
||||
|
||||
const filtered = normalizedData.filter(item => {
|
||||
// Text search filter (existing functionality)
|
||||
const matchesText = !query || config.filter(item, query);
|
||||
if (!matchesText) return false;
|
||||
|
||||
@@ -1,4 +1,75 @@
|
||||
// Domains UI functions
|
||||
|
||||
function normalizeDomainsData(data) {
|
||||
return (Array.isArray(data) ? data : []).map((item) => {
|
||||
if (item.consensusStatus === 'internal' || item.hash === 'internal' || item.hash === 'none') {
|
||||
return { ...item, consensusStatus: 'internal' };
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
consensusStatus: item.consensusStatus || item.consensusState?.status || 'unknown'
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function hasActiveDomainFilters() {
|
||||
const searchEl = document.getElementById('search-domains');
|
||||
const consensusEl = document.getElementById('filter-consensus-status');
|
||||
const hashTypeEl = document.getElementById('filter-hash-type');
|
||||
const ownershipEl = document.getElementById('filter-ownership');
|
||||
|
||||
return Boolean(
|
||||
searchEl?.value?.trim() ||
|
||||
(consensusEl && consensusEl.value !== 'all') ||
|
||||
(hashTypeEl && hashTypeEl.value !== 'all') ||
|
||||
(ownershipEl && ownershipEl.value !== 'all')
|
||||
);
|
||||
}
|
||||
|
||||
function updateDomainsChrome(filteredCount) {
|
||||
const totalCount = (window.domainsData || []).length;
|
||||
const visibleCount = filteredCount != null
|
||||
? filteredCount
|
||||
: (window.filteredDomains || window.domainsData || []).length;
|
||||
const disconnected = !window.wsConnected;
|
||||
|
||||
const disconnectedEl = document.getElementById('domainsDisconnected');
|
||||
const emptyEl = document.getElementById('domainsEmpty');
|
||||
const filteredEmptyEl = document.getElementById('domainsFilteredEmpty');
|
||||
const endEl = document.getElementById('domainsEnd');
|
||||
const tableEl = document.querySelector('.domains-table');
|
||||
const countEl = document.getElementById('domainsCount');
|
||||
|
||||
const trulyEmpty = !disconnected && totalCount === 0;
|
||||
const filteredToZero = !disconnected && !trulyEmpty && visibleCount === 0 && hasActiveDomainFilters();
|
||||
|
||||
if (disconnectedEl) disconnectedEl.classList.toggle('local-dns-empty--visible', disconnected);
|
||||
if (emptyEl) emptyEl.classList.toggle('local-dns-empty--visible', trulyEmpty);
|
||||
if (filteredEmptyEl) filteredEmptyEl.classList.toggle('local-dns-empty--visible', filteredToZero);
|
||||
if (tableEl) tableEl.classList.toggle('domains-table--hidden', disconnected || trulyEmpty || filteredToZero);
|
||||
if (endEl) endEl.classList.remove('local-dns-list-end--visible');
|
||||
|
||||
if (countEl) {
|
||||
if (disconnected) {
|
||||
countEl.textContent = 'Disconnected';
|
||||
} else if (trulyEmpty) {
|
||||
countEl.textContent = 'No domains';
|
||||
} else if (hasActiveDomainFilters() && visibleCount !== totalCount) {
|
||||
countEl.textContent = `${visibleCount.toLocaleString()} of ${totalCount.toLocaleString()} shown`;
|
||||
} else {
|
||||
countEl.textContent = `${totalCount.toLocaleString()} domain${totalCount === 1 ? '' : 's'}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showDomainsListEnd() {
|
||||
const endEl = document.getElementById('domainsEnd');
|
||||
const totalCount = (window.filteredDomains || window.domainsData || []).length;
|
||||
if (endEl && totalCount > 0 && window.wsConnected) {
|
||||
endEl.classList.add('local-dns-list-end--visible');
|
||||
}
|
||||
}
|
||||
|
||||
function renderDnsConflicts() {
|
||||
if (window.genericFilter) window.genericFilter('dns-conflicts');
|
||||
}
|
||||
@@ -132,4 +203,7 @@ window.submitAddDomain = submitAddDomain;
|
||||
window.removeDomain = removeDomain;
|
||||
window.loadDomainFilterSettings = loadDomainFilterSettings;
|
||||
window.saveDomainFilterSettings = saveDomainFilterSettings;
|
||||
window.updateDomainsChrome = updateDomainsChrome;
|
||||
window.showDomainsListEnd = showDomainsListEnd;
|
||||
window.normalizeDomainsData = normalizeDomainsData;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user