sentinel-hawk / script.js
hakandinger's picture
Accounts Screen Prompt
58c73e7 verified
// 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'
? '<i data-feather="twitter" class="w-4 h-4 text-[#1da1f2]"></i>'
: '<i data-feather="message-circle" class="w-4 h-4 text-[#25d366]"></i>';
const platformName = account.platform === 'twitter' ? 'Twitter' : 'WhatsApp';
const statusBadge = account.status === 'ACTIVE'
? `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 status-active"><span class="w-1.5 h-1.5 bg-green-500 rounded-full mr-1.5 animate-pulse"></span>Active</span>`
: `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800"><span class="w-1.5 h-1.5 bg-yellow-500 rounded-full mr-1.5"></span>Inactive</span>`;
const actionButton = account.status === 'ACTIVE'
? `<button onclick="toggleStatus(${account.id})" class="inline-flex items-center gap-1.5 px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-all duration-200 btn-press"><i data-feather="power" class="w-3 h-3"></i>Deactivate</button>`
: `<button onclick="toggleStatus(${account.id})" class="inline-flex items-center gap-1.5 px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 btn-press"><i data-feather="play" class="w-3 h-3"></i>Activate</button>`;
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<img class="h-10 w-10 rounded-full object-cover ring-2 ring-gray-100" src="${account.avatar}" alt="">
</div>
<div class="ml-4">
<div class="text-sm font-medium text-amber-600 hover:text-amber-700 cursor-pointer">${account.username}</div>
<div class="text-xs text-gray-500">ID: ${account.userId}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center gap-2">
${platformIcon}
<span class="text-sm text-gray-700">${platformName}</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-700">${formatDate(account.createdAt)}</div>
<div class="text-xs text-gray-500">${timeAgo(account.createdAt)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
${statusBadge}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
${actionButton}
</td>
`;
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 = '<i data-feather="check" class="w-5 h-5 text-green-600"></i>';
} else {
toastIcon.className = 'w-10 h-10 rounded-full bg-red-100 flex items-center justify-center';
toastIcon.innerHTML = '<i data-feather="alert-circle" class="w-5 h-5 text-red-600"></i>';
}
// 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();
});