Files
p2ns/plugin-sites/domain.consensus/www/js/utils.js
T
2025-12-17 20:05:50 -05:00

233 lines
5.6 KiB
JavaScript

/**
* Utility functions for domain consensus plugin
*/
/**
* Format a timestamp to human-readable string
*/
function formatTimestamp(timestamp) {
if (!timestamp) return 'Never';
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
if (diff < 60000) {
return `${Math.floor(diff / 1000)}s ago`;
} else if (diff < 3600000) {
return `${Math.floor(diff / 60000)}m ago`;
} else if (diff < 86400000) {
return `${Math.floor(diff / 3600000)}h ago`;
} else {
return date.toLocaleString();
}
}
/**
* Format a peer ID to shortened version
*/
function formatPeerId(peerId) {
if (!peerId) return 'N/A';
if (peerId.length <= 16) return peerId;
return `${peerId.slice(0, 8)}...${peerId.slice(-8)}`;
}
/**
* Format a hash to shortened version
*/
function formatHash(hash) {
if (!hash) return 'N/A';
if (hash.length <= 20) return hash;
return `${hash.slice(0, 10)}...${hash.slice(-10)}`;
}
/**
* Get status badge class based on consensus status
*/
function getStatusBadgeClass(status) {
switch (status) {
case 'resolved':
return 'status-badge resolved';
case 'insufficient_quorum':
return 'status-badge insufficient_quorum';
case 'tie':
return 'status-badge tie';
case 'no_claims':
return 'status-badge no_claims';
case 'error':
return 'status-badge error';
default:
return 'status-badge no_claims';
}
}
/**
* Get status text for consensus status
*/
function getStatusText(status) {
switch (status) {
case 'resolved':
return 'Resolved';
case 'insufficient_quorum':
return 'Insufficient Quorum';
case 'tie':
return 'Tie';
case 'no_claims':
return 'No Claims';
case 'error':
return 'Error';
default:
return 'Unknown';
}
}
/**
* Get status color for consensus status
*/
function getStatusColor(status) {
switch (status) {
case 'resolved':
return '#10b981'; // green-500
case 'insufficient_quorum':
return '#eab308'; // yellow-500
case 'tie':
return '#f97316'; // orange-500
case 'no_claims':
return '#6b7280'; // gray-500
case 'error':
return '#ef4444'; // red-500
default:
return '#6b7280'; // gray-500
}
}
/**
* Calculate quorum percentage
*/
function calculateQuorumPercentage(totalVotes, minVotes) {
if (minVotes === 0) return 0;
return Math.min(100, Math.round((totalVotes / minVotes) * 100));
}
/**
* Escape HTML to prevent XSS
*/
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
/**
* Copy text to clipboard
*/
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
document.body.removeChild(textArea);
return true;
} catch (err) {
document.body.removeChild(textArea);
return false;
}
}
}
/**
* Debounce function
*/
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
/**
* Format number with commas
*/
function formatNumber(num) {
if (num === null || num === undefined) return '0';
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
/**
* Create a progress bar element
*/
function createProgressBar(percentage, color = '#3b82f6') {
const bar = document.createElement('div');
bar.className = 'w-full progress-bar-container';
bar.innerHTML = `
<div class="progress-bar" style="width: ${percentage}%; background-color: ${color};"></div>
`;
return bar;
}
/**
* Update sidebar stats (can be called from any view)
*/
async function updateSidebarStats() {
try {
// Fetch overview and peers data to get stats
const [overviewData, peersData] = await Promise.all([
window.apiClient.getOverview(),
window.apiClient.getPeers()
]);
const stats = overviewData.stats || {};
const activePeers = peersData.activePeers || 0;
const totalDomainsEl = document.getElementById('statTotalDomains');
const resolvedEl = document.getElementById('statResolved');
const activePeersEl = document.getElementById('statActivePeers');
if (totalDomainsEl) totalDomainsEl.textContent = formatNumber(stats.totalDomains || 0);
if (resolvedEl) resolvedEl.textContent = formatNumber(stats.resolved || 0);
if (activePeersEl) activePeersEl.textContent = formatNumber(activePeers);
} catch (error) {
console.error('Error updating sidebar stats:', error);
// Set to 0 on error
const totalDomainsEl = document.getElementById('statTotalDomains');
const resolvedEl = document.getElementById('statResolved');
const activePeersEl = document.getElementById('statActivePeers');
if (totalDomainsEl) totalDomainsEl.textContent = '0';
if (resolvedEl) resolvedEl.textContent = '0';
if (activePeersEl) activePeersEl.textContent = '0';
}
}
/**
* Export functions
*/
window.utils = {
formatTimestamp,
formatPeerId,
formatHash,
getStatusBadgeClass,
getStatusText,
getStatusColor,
calculateQuorumPercentage,
escapeHtml,
copyToClipboard,
debounce,
formatNumber,
createProgressBar,
updateSidebarStats
};