// Sentinel Hawk - Accounts Management Script // Mock data for demonstration const mockAccounts = [ { id: 1, username: '@elonmusk', userId: '44196397', platform: 'twitter', createdAt: '2024-01-15T10:30:00Z', status: 'ACTIVE', avatar: 'https://static.photos/people/200x200/1' }, { id: 2, username: '@sama', userId: '50393960', platform: 'twitter', createdAt: '2024-02-20T14:15:00Z', status: 'ACTIVE', avatar: 'https://static.photos/people/200x200/2' }, { id: 3, username: '+1-555-0123', userId: 'whatsapp_12345', platform: 'whatsapp', createdAt: '2024-03-01T09:00:00Z', status: 'INACTIVE', avatar: 'https://static.photos/people/200x200/3' }, { id: 4, username: '@naval', userId: '745273', platform: 'twitter', createdAt: '2024-03-10T16:45:00Z', status: 'ACTIVE', avatar: 'https://static.photos/people/200x200/4' }, { id: 5, username: '+1-555-0456', userId: 'whatsapp_67890', platform: 'whatsapp', createdAt: '2024-03-15T11:20:00Z', status: 'INACTIVE', avatar: 'https://static.photos/people/200x200/5' }, { id: 6, username: '@paulg', userId: '183749319', platform: 'twitter', createdAt: '2024-03-20T08:30:00Z', status: 'ACTIVE', avatar: 'https://static.photos/people/200x200/6' } ]; // State management let accounts = []; let isLoading = false; let error = null; // DOM Elements const elements = { loadingState: document.getElementById('loading-state'), errorState: document.getElementById('error-state'), emptyState: document.getElementById('empty-state'), successState: document.getElementById('success-state'), tableBody: document.getElementById('accounts-table-body'), errorMessage: document.getElementById('error-message'), statTotal: document.getElementById('stat-total'), statActive: document.getElementById('stat-active'), statInactive: document.getElementById('stat-inactive'), lastUpdated: document.getElementById('last-updated'), showingCount: document.getElementById('showing-count'), toast: document.getElementById('toast'), toastTitle: document.getElementById('toast-title'), toastMessage: document.getElementById('toast-message'), toastIcon: document.getElementById('toast-icon') }; // Simulated API calls const mockApi = { async fetchAccounts() { // Simulate network delay await new Promise(resolve => setTimeout(resolve, 1500)); // Random error simulation (10% chance) if (Math.random() < 0.1) { throw new Error('Network error: Failed to fetch accounts from server'); } return [...mockAccounts]; }, async toggleAccountStatus(accountId, newStatus) { await new Promise(resolve => setTimeout(resolve, 800)); // Random error simulation (5% chance) if (Math.random() < 0.05) { throw new Error('Failed to update account status'); } return { success: true, newStatus }; } }; // Core functions async function fetchData() { isLoading = true; error = null; updateUI(); try { accounts = await mockApi.fetchAccounts(); updateStats(); updateLastUpdated(); } catch (err) { error = err.message || 'An unexpected error occurred'; console.error('Fetch error:', err); } finally { isLoading = false; updateUI(); } } function refreshData() { fetchData(); } function updateStats() { const total = accounts.length; const active = accounts.filter(a => a.status === 'ACTIVE').length; const inactive = accounts.filter(a => a.status === 'INACTIVE').length; animateCounter(elements.statTotal, parseInt(elements.statTotal.textContent), total); animateCounter(elements.statActive, parseInt(elements.statActive.textContent), active); animateCounter(elements.statInactive, parseInt(elements.statInactive.textContent), inactive); } function animateCounter(element, start, end) { const duration = 600; const startTime = performance.now(); function update(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // Ease out cubic const easeProgress = 1 - Math.pow(1 - progress, 3); const current = Math.round(start + (end - start) * easeProgress); element.textContent = current; if (progress < 1) { requestAnimationFrame(update); } } requestAnimationFrame(update); } function updateLastUpdated() { const now = new Date(); elements.lastUpdated.textContent = `Last updated: ${now.toLocaleTimeString()}`; } function updateUI() { // Hide all states elements.loadingState.classList.add('hidden'); elements.errorState.classList.add('hidden'); elements.emptyState.classList.add('hidden'); elements.successState.classList.add('hidden'); if (isLoading) { elements.loadingState.classList.remove('hidden'); } else if (error) { elements.errorState.classList.remove('hidden'); elements.errorMessage.textContent = error; } else if (accounts.length === 0) { elements.emptyState.classList.remove('hidden'); } else { elements.successState.classList.remove('hidden'); renderTable(); } } function renderTable() { elements.tableBody.innerHTML = ''; elements.showingCount.textContent = accounts.length; accounts.forEach((account, index) => { const row = document.createElement('tr'); row.className = 'hover:bg-gray-50/80 transition-colors duration-200 table-row-hover group'; row.style.animationDelay = `${index * 0.05}s`; const platformIcon = account.platform === 'twitter' ? '' : ''; const platformName = account.platform === 'twitter' ? 'Twitter' : 'WhatsApp'; const statusBadge = account.status === 'ACTIVE' ? `Active` : `Inactive`; const actionButton = account.status === 'ACTIVE' ? `` : ``; row.innerHTML = `
${account.username}
ID: ${account.userId}
${platformIcon} ${platformName}
${formatDate(account.createdAt)}
${timeAgo(account.createdAt)}
${statusBadge} ${actionButton} `; elements.tableBody.appendChild(row); }); // Re-initialize feather icons for new content feather.replace(); } function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } function timeAgo(dateString) { const date = new Date(dateString); const now = new Date(); const seconds = Math.floor((now - date) / 1000); let interval = Math.floor(seconds / 31536000); if (interval > 1) return `${interval} years ago`; if (interval === 1) return '1 year ago'; interval = Math.floor(seconds / 2592000); if (interval > 1) return `${interval} months ago`; if (interval === 1) return '1 month ago'; interval = Math.floor(seconds / 86400); if (interval > 1) return `${interval} days ago`; if (interval === 1) return '1 day ago'; interval = Math.floor(seconds / 3600); if (interval > 1) return `${interval} hours ago`; if (interval === 1) return '1 hour ago'; interval = Math.floor(seconds / 60); if (interval > 1) return `${interval} minutes ago`; return 'Just now'; } async function toggleStatus(accountId) { const account = accounts.find(a => a.id === accountId); if (!account) return; const newStatus = account.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE'; const actionText = newStatus === 'ACTIVE' ? 'activated' : 'deactivated'; try { // Optimistic update account.status = newStatus; renderTable(); // API call await mockApi.toggleAccountStatus(accountId, newStatus); // Show success toast showToast('success', 'Status Updated', `Account ${account.username} has been ${actionText}`); updateStats(); } catch (err) { // Revert on error account.status = account.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE'; renderTable(); showToast('error', 'Update Failed', err.message || 'Failed to update account status'); } } function showToast(type, title, message) { const toast = elements.toast; const toastTitle = elements.toastTitle; const toastMessage = elements.toastMessage; const toastIcon = elements.toastIcon; // Set content toastTitle.textContent = title; toastMessage.textContent = message; // Set icon and colors if (type === 'success') { toastIcon.className = 'w-10 h-10 rounded-full bg-green-100 flex items-center justify-center'; toastIcon.innerHTML = ''; } else { toastIcon.className = 'w-10 h-10 rounded-full bg-red-100 flex items-center justify-center'; toastIcon.innerHTML = ''; } // Show toast toast.classList.remove('translate-y-20', 'opacity-0'); feather.replace(); // Auto hide after 5 seconds setTimeout(() => { hideToast(); }, 5000); } function hideToast() { elements.toast.classList.add('translate-y-20', 'opacity-0'); } // Initialize on page load document.addEventListener('DOMContentLoaded', () => { fetchData(); });