File: /home/workzeni/stream-flix.workzenix.com/public/admin/js/script.js
/*
* StreamFlix Admin Panel - Professional OTT Platform Dashboard
* Modern JavaScript with ES6+ Features
*
* Features:
* - Sidebar Navigation & Responsive Layout
* - Theme Toggle (Dark/Light Mode)
* - Modal Management
* - Form Validation & Submission
* - Data Tables with Search, Sort, Pagination
* - Chart.js Integration for Analytics
* - Real-time Notifications
* - Local Storage for Settings
*/
// Global Admin Panel Class
class StreamFlixAdmin {
constructor() {
this.sidebar = null;
this.theme = localStorage.getItem('admin-theme') || 'light';
this.sidebarCollapsed = localStorage.getItem('sidebar-collapsed') === 'true';
this.currentPage = window.location.pathname.split('/').pop() || 'dashboard.html';
this.init();
}
// Initialize the admin panel
init() {
this.initTheme();
this.initSidebar();
this.initDropdowns();
this.initModals();
this.initForms();
this.initTables();
this.initCharts();
this.initNotifications();
this.initTooltips();
this.initAnimations();
// Set active navigation
this.setActiveNavigation();
console.log('StreamFlix Admin Panel Initialized');
}
// Theme Management
initTheme() {
// Set initial theme using data-theme attribute
document.documentElement.setAttribute('data-theme', this.theme);
const themeToggle = document.querySelector('.theme-toggle');
if (themeToggle) {
// Update toggle button text and icon
this.updateThemeToggleUI();
// Add click event listener
themeToggle.addEventListener('click', () => this.toggleTheme());
}
}
toggleTheme() {
// Switch between light and dark themes
this.theme = this.theme === 'light' ? 'dark' : 'light';
// Update document attribute for CSS theme switching
document.documentElement.setAttribute('data-theme', this.theme);
// Save to localStorage
localStorage.setItem('admin-theme', this.theme);
// Update UI
this.updateThemeToggleUI();
// Show notification
this.showNotification(`Switched to ${this.theme} theme`, 'success');
}
updateThemeToggleUI() {
const themeToggle = document.querySelector('.theme-toggle');
const themeIcon = themeToggle?.querySelector('.theme-icon');
const themeText = themeToggle?.querySelector('.theme-text');
if (themeIcon && themeText) {
if (this.theme === 'light') {
themeIcon.className = 'theme-icon fas fa-sun';
themeText.textContent = 'Light';
} else {
themeIcon.className = 'theme-icon fas fa-moon';
themeText.textContent = 'Dark';
}
}
}
// Dropdown Management
initDropdowns() {
// Create backdrop element
this.createDropdownBackdrop();
// Initialize notification dropdown
this.initNotificationDropdown();
// Initialize profile dropdown
this.initProfileDropdown();
// Global click handler to close dropdowns
document.addEventListener('click', (e) => {
if (!e.target.closest('.dropdown')) {
this.closeAllDropdowns();
}
});
// ESC key to close dropdowns
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.closeAllDropdowns();
}
});
}
createDropdownBackdrop() {
if (!document.querySelector('.dropdown-backdrop')) {
const backdrop = document.createElement('div');
backdrop.className = 'dropdown-backdrop';
backdrop.addEventListener('click', () => this.closeAllDropdowns());
document.body.appendChild(backdrop);
}
}
initNotificationDropdown() {
const notificationBtn = document.querySelector('.notification-dropdown-trigger');
if (notificationBtn) {
notificationBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleDropdown('notification-dropdown');
});
}
}
initProfileDropdown() {
const profileTrigger = document.querySelector('.profile-trigger');
if (profileTrigger) {
profileTrigger.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleDropdown('profile-dropdown');
});
}
}
toggleDropdown(dropdownId) {
const dropdown = document.querySelector(`.${dropdownId}`);
const backdrop = document.querySelector('.dropdown-backdrop');
if (!dropdown) return;
// Close other dropdowns first
this.closeAllDropdowns(dropdownId);
// Toggle current dropdown
const isOpen = dropdown.classList.contains('open');
if (isOpen) {
dropdown.classList.remove('open');
backdrop.classList.remove('show');
} else {
dropdown.classList.add('open');
backdrop.classList.add('show');
}
}
closeAllDropdowns(except = null) {
const dropdowns = document.querySelectorAll('.dropdown');
const backdrop = document.querySelector('.dropdown-backdrop');
dropdowns.forEach(dropdown => {
if (!except || !dropdown.classList.contains(except)) {
dropdown.classList.remove('open');
}
});
if (backdrop) {
backdrop.classList.remove('show');
}
}
// Notification Management
markNotificationAsRead(notificationId) {
const notification = document.querySelector(`[data-notification-id="${notificationId}"]`);
if (notification) {
notification.classList.remove('unread');
this.updateNotificationBadge();
}
}
markAllNotificationsAsRead() {
const notifications = document.querySelectorAll('.notification-item.unread');
notifications.forEach(notification => {
notification.classList.remove('unread');
});
this.updateNotificationBadge();
this.showNotification('All notifications marked as read', 'success');
}
updateNotificationBadge() {
const badge = document.querySelector('.notification-badge');
const unreadCount = document.querySelectorAll('.notification-item.unread').length;
if (badge) {
if (unreadCount > 0) {
badge.textContent = unreadCount > 99 ? '99+' : unreadCount;
badge.style.display = 'block';
} else {
badge.style.display = 'none';
}
}
}
// Profile Actions
handleProfileAction(action) {
switch (action) {
case 'profile':
this.showNotification('Opening profile settings...', 'info');
// Redirect to profile page or open modal
break;
case 'settings':
this.showNotification('Opening account settings...', 'info');
// Redirect to settings page
break;
case 'help':
this.showNotification('Opening help center...', 'info');
// Open help modal or redirect
break;
case 'logout':
this.handleLogout();
break;
default:
console.log('Unknown profile action:', action);
}
this.closeAllDropdowns();
}
handleLogout() {
if (confirm('Are you sure you want to logout?')) {
this.showNotification('Logging out...', 'info');
// Simulate logout delay
setTimeout(() => {
// Clear localStorage
localStorage.removeItem('admin-theme');
localStorage.removeItem('sidebar-collapsed');
// Redirect to login page
window.location.href = 'login.html';
}, 1000);
}
}
// Sidebar Management
initSidebar() {
this.sidebar = document.querySelector('.sidebar');
const toggleBtn = document.querySelector('.sidebar-toggle');
const overlay = document.querySelector('.sidebar-overlay');
if (this.sidebar) {
// Apply saved state
if (this.sidebarCollapsed) {
this.sidebar.classList.add('collapsed');
}
// Toggle button
if (toggleBtn) {
toggleBtn.addEventListener('click', () => this.toggleSidebar());
}
// Overlay for mobile
if (overlay) {
overlay.addEventListener('click', () => this.closeSidebar());
}
// Handle window resize
window.addEventListener('resize', () => this.handleResize());
}
}
toggleSidebar() {
if (this.sidebar) {
this.sidebarCollapsed = !this.sidebarCollapsed;
this.sidebar.classList.toggle('collapsed', this.sidebarCollapsed);
localStorage.setItem('sidebar-collapsed', this.sidebarCollapsed);
}
}
openSidebar() {
if (this.sidebar) {
this.sidebar.classList.add('open');
}
}
closeSidebar() {
if (this.sidebar) {
this.sidebar.classList.remove('open');
}
}
handleResize() {
const isMobile = window.innerWidth <= 1024;
if (isMobile) {
this.sidebar?.classList.remove('collapsed');
} else {
this.sidebar?.classList.remove('open');
}
}
// Set active navigation based on current page
setActiveNavigation() {
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const href = link.getAttribute('href');
if (href === this.currentPage ||
(this.currentPage === '' && href === 'dashboard.html')) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
}
// Modal Management
// initModals() {
// // Modal triggers
// document.addEventListener('click', (e) => {
// if (e.target.matches('[data-modal]')) {
// e.preventDefault();
// const modalId = e.target.dataset.modal;
// this.openModal(modalId);
// }
// if (e.target.matches('.modal-close') || e.target.matches('.modal-overlay')) {
// this.closeModal();
// }
// });
// // ESC key to close modal
// document.addEventListener('keydown', (e) => {
// if (e.key === 'Escape') {
// this.closeModal();
// }
// });
// }
openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('show');
document.body.style.overflow = 'hidden';
// Focus first input
const firstInput = modal.querySelector('input, select, textarea');
if (firstInput) {
setTimeout(() => firstInput.focus(), 100);
}
}
}
// closeModal() {
// const activeModal = document.querySelector('.modal-overlay.show');
// if (activeModal) {
// activeModal.classList.remove('show');
// document.body.style.overflow = '';
// }
// }
// Form Management
initForms() {
const forms = document.querySelectorAll('form[data-form]');
forms.forEach(form => {
form.addEventListener('submit', (e) => this.handleFormSubmit(e));
// Real-time validation
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
input.addEventListener('blur', () => this.validateField(input));
input.addEventListener('input', () => this.clearFieldError(input));
});
});
}
handleFormSubmit(e) {
e.preventDefault();
const form = e.target;
const formType = form.dataset.form;
if (this.validateForm(form)) {
this.submitForm(form, formType);
}
}
validateForm(form) {
let isValid = true;
const inputs = form.querySelectorAll('input[required], select[required], textarea[required]');
inputs.forEach(input => {
if (!this.validateField(input)) {
isValid = false;
}
});
return isValid;
}
validateField(field) {
const value = field.value.trim();
const type = field.type;
let isValid = true;
let errorMessage = '';
// Required validation
if (field.hasAttribute('required') && !value) {
isValid = false;
errorMessage = 'This field is required';
}
// Email validation
else if (type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
isValid = false;
errorMessage = 'Please enter a valid email address';
}
}
// Password validation
else if (type === 'password' && value) {
if (value.length < 6) {
isValid = false;
errorMessage = 'Password must be at least 6 characters';
}
}
// Number validation
else if (type === 'number' && value) {
if (isNaN(value) || value < 0) {
isValid = false;
errorMessage = 'Please enter a valid number';
}
}
// URL validation
else if (type === 'url' && value) {
try {
new URL(value);
} catch {
isValid = false;
errorMessage = 'Please enter a valid URL';
}
}
this.showFieldError(field, isValid ? '' : errorMessage);
return isValid;
}
showFieldError(field, message) {
this.clearFieldError(field);
if (message) {
field.classList.add('error');
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error';
errorDiv.textContent = message;
field.parentNode.appendChild(errorDiv);
}
}
clearFieldError(field) {
field.classList.remove('error');
const existingError = field.parentNode.querySelector('.field-error');
if (existingError) {
existingError.remove();
}
}
async submitForm(form, formType) {
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
// Show loading state
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
try {
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
// Simulate API call
await this.simulateApiCall(formType, data);
// Success
this.showNotification(`${formType} saved successfully!`, 'success');
this.closeModal();
form.reset();
// Refresh data if needed
if (formType.includes('add') || formType.includes('edit')) {
this.refreshTableData();
}
} catch (error) {
this.showNotification(`Error saving ${formType}: ${error.message}`, 'error');
} finally {
// Reset button
submitBtn.disabled = false;
submitBtn.textContent = originalText;
}
}
// Simulate API calls for demo
async simulateApiCall(type, data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulate random success/failure for demo
if (Math.random() > 0.1) {
// Store in localStorage for demo
this.saveToLocalStorage(type, data);
resolve(data);
} else {
reject(new Error('Network error'));
}
}, 1000 + Math.random() * 1000);
});
}
saveToLocalStorage(type, data) {
const key = type.replace('add-', '').replace('edit-', '') + 's';
let items = JSON.parse(localStorage.getItem(key) || '[]');
if (type.includes('add')) {
data.id = Date.now();
data.createdAt = new Date().toISOString();
items.push(data);
} else if (type.includes('edit')) {
const index = items.findIndex(item => item.id == data.id);
if (index !== -1) {
items[index] = { ...items[index], ...data, updatedAt: new Date().toISOString() };
}
}
localStorage.setItem(key, JSON.stringify(items));
}
// Table Management
initTables() {
const tables = document.querySelectorAll('[data-table]');
tables.forEach(table => {
this.initTableFeatures(table);
});
}
initTableFeatures(table) {
const tableContainer = table.closest('.table-container');
if (!tableContainer) return;
// Search functionality
const searchInput = tableContainer.querySelector('.table-search');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
this.filterTable(table, e.target.value);
});
}
// Sort functionality
const sortableHeaders = table.querySelectorAll('th[data-sort]');
sortableHeaders.forEach(header => {
header.style.cursor = 'pointer';
header.addEventListener('click', () => {
this.sortTable(table, header.dataset.sort);
});
});
// Pagination
this.initPagination(table);
}
filterTable(table, searchTerm) {
const rows = table.querySelectorAll('tbody tr');
const term = searchTerm.toLowerCase();
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(term) ? '' : 'none';
});
}
sortTable(table, column) {
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
const columnIndex = Array.from(table.querySelectorAll('th')).findIndex(th => th.dataset.sort === column);
if (columnIndex === -1) return;
const isAscending = table.dataset.sortDirection !== 'asc';
table.dataset.sortDirection = isAscending ? 'asc' : 'desc';
rows.sort((a, b) => {
const aVal = a.cells[columnIndex].textContent.trim();
const bVal = b.cells[columnIndex].textContent.trim();
// Try to parse as numbers
const aNum = parseFloat(aVal);
const bNum = parseFloat(bVal);
if (!isNaN(aNum) && !isNaN(bNum)) {
return isAscending ? aNum - bNum : bNum - aNum;
}
// String comparison
return isAscending ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
});
// Re-append sorted rows
rows.forEach(row => tbody.appendChild(row));
// Update sort indicators
table.querySelectorAll('th').forEach(th => th.classList.remove('sort-asc', 'sort-desc'));
const sortedHeader = table.querySelector(`th[data-sort="${column}"]`);
if (sortedHeader) {
sortedHeader.classList.add(isAscending ? 'sort-asc' : 'sort-desc');
}
}
initPagination(table) {
const paginationContainer = table.closest('.table-container').querySelector('.pagination-container');
if (!paginationContainer) return;
const rowsPerPage = parseInt(table.dataset.perPage) || 10;
const rows = table.querySelectorAll('tbody tr');
const totalPages = Math.ceil(rows.length / rowsPerPage);
if (totalPages <= 1) return;
let currentPage = 1;
const showPage = (page) => {
const start = (page - 1) * rowsPerPage;
const end = start + rowsPerPage;
rows.forEach((row, index) => {
row.style.display = (index >= start && index < end) ? '' : 'none';
});
currentPage = page;
this.updatePaginationUI(paginationContainer, currentPage, totalPages);
};
// Create pagination UI
this.createPaginationUI(paginationContainer, totalPages, showPage);
showPage(1);
}
createPaginationUI(container, totalPages, showPageCallback) {
container.innerHTML = `
<div class="pagination">
<button class="btn btn-sm btn-outline" data-page="prev">
<i class="fas fa-chevron-left"></i>
</button>
<div class="page-numbers"></div>
<button class="btn btn-sm btn-outline" data-page="next">
<i class="fas fa-chevron-right"></i>
</button>
</div>
`;
container.addEventListener('click', (e) => {
const btn = e.target.closest('[data-page]');
if (!btn) return;
const action = btn.dataset.page;
const currentPage = parseInt(container.dataset.currentPage) || 1;
if (action === 'prev' && currentPage > 1) {
showPageCallback(currentPage - 1);
} else if (action === 'next' && currentPage < totalPages) {
showPageCallback(currentPage + 1);
} else if (!isNaN(action)) {
showPageCallback(parseInt(action));
}
});
}
updatePaginationUI(container, currentPage, totalPages) {
container.dataset.currentPage = currentPage;
const pageNumbers = container.querySelector('.page-numbers');
const prevBtn = container.querySelector('[data-page="prev"]');
const nextBtn = container.querySelector('[data-page="next"]');
// Update buttons state
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage === totalPages;
// Generate page numbers
let pages = [];
const maxVisible = 5;
if (totalPages <= maxVisible) {
pages = Array.from({ length: totalPages }, (_, i) => i + 1);
} else {
const start = Math.max(1, currentPage - 2);
const end = Math.min(totalPages, start + maxVisible - 1);
pages = Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
pageNumbers.innerHTML = pages.map(page => `
<button class="btn btn-sm ${page === currentPage ? 'btn-primary' : 'btn-outline'}"
data-page="${page}">${page}</button>
`).join('');
}
refreshTableData() {
// Refresh table data after CRUD operations
const tables = document.querySelectorAll('[data-table]');
tables.forEach(table => {
const tableType = table.dataset.table;
this.loadTableData(table, tableType);
});
}
loadTableData(table, type) {
const data = JSON.parse(localStorage.getItem(type + 's') || '[]');
const tbody = table.querySelector('tbody');
if (!tbody || !data.length) return;
// Generate table rows based on type
tbody.innerHTML = this.generateTableRows(data, type);
}
generateTableRows(data, type) {
switch (type) {
case 'movie':
return data.map(item => `
<tr>
<td><img src="${item.poster || '/placeholder.jpg'}" alt="${item.title}" style="width: 50px; height: 75px; object-fit: cover; border-radius: 4px;"></td>
<td>${item.title}</td>
<td>${item.genre}</td>
<td>${item.year}</td>
<td><span class="badge badge-${item.status === 'active' ? 'success' : 'warning'}">${item.status}</span></td>
<td>
<button class="btn btn-sm btn-outline" data-modal="editMovieModal" onclick="editMovie(${item.id})">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline" onclick="deleteMovie(${item.id})">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
`).join('');
case 'user':
return data.map(item => `
<tr>
<td>${item.name}</td>
<td>${item.email}</td>
<td><span class="badge badge-${item.role === 'admin' ? 'primary' : item.role === 'editor' ? 'info' : 'success'}">${item.role}</span></td>
<td>${new Date(item.createdAt).toLocaleDateString()}</td>
<td><span class="badge badge-${item.status === 'active' ? 'success' : 'warning'}">${item.status}</span></td>
<td>
<button class="btn btn-sm btn-outline" data-modal="editUserModal" onclick="editUser(${item.id})">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline" onclick="deleteUser(${item.id})">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
`).join('');
default:
return '';
}
}
// Chart Management
initCharts() {
// Initialize Chart.js charts if the library is loaded
if (typeof Chart !== 'undefined') {
this.initDashboardCharts();
}
}
initDashboardCharts() {
// Views Chart
const viewsChart = document.getElementById('viewsChart');
if (viewsChart) {
new Chart(viewsChart, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Views',
data: [12000, 19000, 15000, 25000, 22000, 30000],
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: '#8892b0'
}
},
x: {
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: '#8892b0'
}
}
}
}
});
}
// Revenue Chart
const revenueChart = document.getElementById('revenueChart');
if (revenueChart) {
new Chart(revenueChart, {
type: 'doughnut',
data: {
labels: ['Subscriptions', 'Ads', 'Premium'],
datasets: [{
data: [60, 25, 15],
backgroundColor: ['#667eea', '#e94560', '#0f3460'],
borderWidth: 0
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom',
labels: {
color: '#8892b0',
padding: 20
}
}
}
}
});
}
}
// Notification System
initNotifications() {
// Create notification container if it doesn't exist
if (!document.querySelector('.notification-container')) {
const container = document.createElement('div');
container.className = 'notification-container';
container.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
pointer-events: none;
`;
document.body.appendChild(container);
}
}
showNotification(message, type = 'info', duration = 5000) {
const container = document.querySelector('.notification-container');
const notification = document.createElement('div');
const icons = {
success: 'fas fa-check-circle',
error: 'fas fa-exclamation-circle',
warning: 'fas fa-exclamation-triangle',
info: 'fas fa-info-circle'
};
notification.className = `notification notification-${type} slide-in-right`;
notification.style.cssText = `
background: var(--surface-bg);
border: 1px solid var(--glass-border);
border-left: 4px solid var(--accent-${type === 'error' ? 'primary' : type === 'success' ? 'success' : 'secondary'});
border-radius: var(--border-radius-md);
padding: var(--spacing-md) var(--spacing-lg);
margin-bottom: var(--spacing-md);
box-shadow: var(--shadow-lg);
backdrop-filter: blur(20px);
pointer-events: auto;
min-width: 300px;
max-width: 400px;
`;
notification.innerHTML = `
<div class="d-flex align-items-center gap-md">
<i class="${icons[type]}" style="color: var(--accent-${type === 'error' ? 'primary' : type === 'success' ? 'success' : 'secondary'}); font-size: 1.2rem;"></i>
<div class="flex-1">
<div style="color: var(--text-primary); font-weight: 500;">${message}</div>
</div>
<button class="notification-close" style="background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 4px;">
<i class="fas fa-times"></i>
</button>
</div>
`;
// Close button functionality
notification.querySelector('.notification-close').addEventListener('click', () => {
this.removeNotification(notification);
});
container.appendChild(notification);
// Auto remove after duration
if (duration > 0) {
setTimeout(() => {
this.removeNotification(notification);
}, duration);
}
}
removeNotification(notification) {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}
// Tooltip Management
initTooltips() {
const tooltipElements = document.querySelectorAll('[data-tooltip]');
tooltipElements.forEach(element => {
element.classList.add('tooltip');
});
}
// Animation Management
initAnimations() {
// Intersection Observer for scroll animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe elements with animation classes
document.querySelectorAll('.animate-on-scroll').forEach(el => {
observer.observe(el);
});
}
// Utility Methods
formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
}
formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
}
formatDate(date) {
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
}).format(new Date(date));
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
// Initialize admin panel when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.adminPanel = new StreamFlixAdmin();
});
// Global utility functions for inline event handlers
function editMovie(id) {
const movies = JSON.parse(localStorage.getItem('movies') || '[]');
const movie = movies.find(m => m.id == id);
if (movie) {
// Populate edit form
Object.keys(movie).forEach(key => {
const input = document.querySelector(`#editMovieModal [name="${key}"]`);
if (input) input.value = movie[key];
});
window.adminPanel.openModal('editMovieModal');
}
}
function deleteMovie(id) {
if (confirm('Are you sure you want to delete this movie?')) {
let movies = JSON.parse(localStorage.getItem('movies') || '[]');
movies = movies.filter(m => m.id != id);
localStorage.setItem('movies', JSON.stringify(movies));
window.adminPanel.refreshTableData();
window.adminPanel.showNotification('Movie deleted successfully', 'success');
}
}
function editUser(id) {
const users = JSON.parse(localStorage.getItem('users') || '[]');
const user = users.find(u => u.id == id);
if (user) {
// Populate edit form
Object.keys(user).forEach(key => {
const input = document.querySelector(`#editUserModal [name="${key}"]`);
if (input) input.value = user[key];
});
window.adminPanel.openModal('editUserModal');
}
}
function deleteUser(id) {
if (confirm('Are you sure you want to delete this user?')) {
let users = JSON.parse(localStorage.getItem('users') || '[]');
users = users.filter(u => u.id != id);
localStorage.setItem('users', JSON.stringify(users));
window.adminPanel.refreshTableData();
window.adminPanel.showNotification('User deleted successfully', 'success');
}
}
function toggleStatus(type, id) {
const items = JSON.parse(localStorage.getItem(type + 's') || '[]');
const item = items.find(i => i.id == id);
if (item) {
item.status = item.status === 'active' ? 'inactive' : 'active';
localStorage.setItem(type + 's', JSON.stringify(items));
window.adminPanel.refreshTableData();
window.adminPanel.showNotification(`${type} status updated`, 'success');
}
}
// Dropdown utility functions
function markNotificationAsRead(notificationId) {
window.adminPanel.markNotificationAsRead(notificationId);
}
function markAllNotificationsAsRead() {
window.adminPanel.markAllNotificationsAsRead();
}
function handleProfileAction(action) {
window.adminPanel.handleProfileAction(action);
}
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = StreamFlixAdmin;
}