447 lines
22 KiB
HTML
Executable File
447 lines
22 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="robots" content="noindex, nofollow, noarchive">
|
|
<title>Dashboard - MSPE Admin</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha384-t1nt8BQoYMLFN5p42tRAtuAAFQaCQODekUVeKKZrEnEyp4H2R0RHFz0KWpmj7i8g" crossorigin="anonymous">
|
|
<link rel="stylesheet" href="/admin/css/admin.css">
|
|
</head>
|
|
<body>
|
|
<div class="admin-wrapper">
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar" id="sidebar">
|
|
<div class="sidebar-header">
|
|
<a href="/admin/dashboard.html" class="sidebar-logo">
|
|
<i class="fas fa-cube"></i>
|
|
<span>MSPE</span>
|
|
</a>
|
|
<button class="sidebar-toggle" id="sidebar-toggle">
|
|
<i class="fas fa-bars"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
<div class="nav-section">
|
|
<span class="nav-section-title">Main</span>
|
|
<ul class="nav-menu">
|
|
<li class="nav-item active">
|
|
<a href="/admin/dashboard.html" class="nav-link">
|
|
<i class="fas fa-th-large"></i>
|
|
<span>Dashboard</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-section">
|
|
<span class="nav-section-title">Content</span>
|
|
<ul class="nav-menu">
|
|
<li class="nav-item">
|
|
<a href="/admin/pages.html" class="nav-link">
|
|
<i class="fas fa-file-alt"></i>
|
|
<span>Pages</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/services.html" class="nav-link">
|
|
<i class="fas fa-concierge-bell"></i>
|
|
<span>Services</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/portfolio.html" class="nav-link">
|
|
<i class="fas fa-briefcase"></i>
|
|
<span>Portfolio</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/news.html" class="nav-link">
|
|
<i class="fas fa-newspaper"></i>
|
|
<span>News & Events</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/testimonials.html" class="nav-link">
|
|
<i class="fas fa-quote-right"></i>
|
|
<span>Testimonials</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/team.html" class="nav-link">
|
|
<i class="fas fa-users"></i>
|
|
<span>Team</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-section">
|
|
<span class="nav-section-title">Media</span>
|
|
<ul class="nav-menu">
|
|
<li class="nav-item">
|
|
<a href="/admin/media.html" class="nav-link">
|
|
<i class="fas fa-images"></i>
|
|
<span>Media Library</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-section">
|
|
<span class="nav-section-title">Inquiries</span>
|
|
<ul class="nav-menu">
|
|
<li class="nav-item">
|
|
<a href="/admin/messages.html" class="nav-link">
|
|
<i class="fas fa-envelope"></i>
|
|
<span>Messages</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/subscribers.html" class="nav-link">
|
|
<i class="fas fa-bell"></i>
|
|
<span>Subscribers</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/bookings.html" class="nav-link">
|
|
<i class="fas fa-calendar-check"></i>
|
|
<span>Bookings</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/availability.html" class="nav-link">
|
|
<i class="fas fa-clock"></i>
|
|
<span>Availability</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-section">
|
|
<span class="nav-section-title">Settings</span>
|
|
<ul class="nav-menu">
|
|
<li class="nav-item">
|
|
<a href="/admin/settings.html" class="nav-link">
|
|
<i class="fas fa-cog"></i>
|
|
<span>Site Settings</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="/admin/users.html" class="nav-link">
|
|
<i class="fas fa-user-shield"></i>
|
|
<span>Admin Users</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<a href="/index.html" class="view-site-btn" target="_blank">
|
|
<i class="fas fa-external-link-alt"></i>
|
|
<span>View Website</span>
|
|
</a>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="main-content">
|
|
<!-- Header -->
|
|
<header class="admin-header">
|
|
<div class="header-left">
|
|
<button class="mobile-toggle" id="mobile-toggle">
|
|
<i class="fas fa-bars"></i>
|
|
</button>
|
|
<h1 class="page-title">Dashboard</h1>
|
|
</div>
|
|
|
|
<div class="header-right">
|
|
<div class="header-search">
|
|
<i class="fas fa-search"></i>
|
|
<input type="text" placeholder="Search...">
|
|
</div>
|
|
|
|
<div class="header-notifications">
|
|
<button class="notification-btn">
|
|
<i class="fas fa-bell"></i>
|
|
<span class="notification-badge" id="notif-badge">0</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="header-user">
|
|
<div class="user-avatar">
|
|
<img src="https://ui-avatars.com/api/?name=Admin&background=0ea5e9&color=fff" alt="Admin">
|
|
</div>
|
|
<div class="user-info">
|
|
<span class="user-name">Admin</span>
|
|
<span class="user-role">Administrator</span>
|
|
</div>
|
|
<div class="user-dropdown">
|
|
<button class="dropdown-toggle">
|
|
<i class="fas fa-chevron-down"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a href="settings.html"><i class="fas fa-user"></i> Profile</a></li>
|
|
<li><a href="settings.html"><i class="fas fa-cog"></i> Settings</a></li>
|
|
<li class="divider"></li>
|
|
<li><a href="#" id="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Dashboard Content -->
|
|
<div class="content-wrapper">
|
|
<!-- Stats Cards -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card" id="stat-messages">
|
|
<div class="stat-icon bg-blue">
|
|
<i class="fas fa-envelope"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<h3 id="stat-messages-count">0</h3>
|
|
<p>Unread Messages</p>
|
|
</div>
|
|
<div class="stat-trend" id="stat-messages-trend">
|
|
<span id="stat-messages-total">0 total</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card" id="stat-bookings">
|
|
<div class="stat-icon bg-green">
|
|
<i class="fas fa-calendar-check"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<h3 id="stat-bookings-count">0</h3>
|
|
<p>Pending Bookings</p>
|
|
</div>
|
|
<div class="stat-trend" id="stat-bookings-trend">
|
|
<span id="stat-bookings-week">0 this week</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card" id="stat-news">
|
|
<div class="stat-icon bg-purple">
|
|
<i class="fas fa-newspaper"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<h3 id="stat-news-count">0</h3>
|
|
<p>Published Articles</p>
|
|
</div>
|
|
<div class="stat-trend" id="stat-news-trend">
|
|
<span id="stat-news-draft">0 drafts</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card" id="stat-subscribers">
|
|
<div class="stat-icon bg-orange">
|
|
<i class="fas fa-users"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<h3 id="stat-subscribers-count">0</h3>
|
|
<p>Subscribers</p>
|
|
</div>
|
|
<div class="stat-trend" id="stat-subscribers-trend">
|
|
<span id="stat-subscribers-active">0 active</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="dashboard-row">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Quick Actions</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="quick-actions">
|
|
<a href="/admin/news.html?action=add" class="quick-action-btn">
|
|
<i class="fas fa-newspaper"></i>
|
|
<span>Add Article</span>
|
|
</a>
|
|
<a href="/admin/portfolio.html?action=add" class="quick-action-btn">
|
|
<i class="fas fa-briefcase"></i>
|
|
<span>Add Project</span>
|
|
</a>
|
|
<a href="/admin/messages.html" class="quick-action-btn">
|
|
<i class="fas fa-envelope"></i>
|
|
<span>Messages</span>
|
|
</a>
|
|
<a href="/admin/bookings.html" class="quick-action-btn">
|
|
<i class="fas fa-calendar-check"></i>
|
|
<span>Bookings</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Content -->
|
|
<div class="dashboard-row two-col">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Recent News</h2>
|
|
<a href="/admin/news.html" class="view-all">View All</a>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="content-list" id="recent-news">
|
|
<!-- Will be populated by JavaScript -->
|
|
<div class="loading-state">
|
|
<i class="fas fa-spinner fa-spin"></i> Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Recent Messages</h2>
|
|
<a href="/admin/messages.html" class="view-all">View All</a>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="content-list" id="recent-messages">
|
|
<div class="loading-state">
|
|
<i class="fas fa-spinner fa-spin"></i> Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="/admin/js/admin.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadDashboardStats();
|
|
loadRecentNews();
|
|
loadRecentMessages();
|
|
});
|
|
|
|
async function loadDashboardStats() {
|
|
try {
|
|
const response = await MSPE.API.get('stats.php');
|
|
if (response && response.success) {
|
|
const stats = response.data;
|
|
|
|
// Update Messages Stats
|
|
const unreadCount = stats.messages?.unread || 0;
|
|
document.getElementById('stat-messages-count').textContent = unreadCount;
|
|
document.getElementById('stat-messages-total').textContent = `${stats.messages?.total || 0} total`;
|
|
const badge = document.getElementById('notif-badge');
|
|
if (badge) {
|
|
badge.textContent = unreadCount;
|
|
badge.style.display = unreadCount > 0 ? '' : 'none';
|
|
}
|
|
|
|
// Update Bookings Stats
|
|
document.getElementById('stat-bookings-count').textContent = stats.bookings?.pending || 0;
|
|
document.getElementById('stat-bookings-week').textContent = `${stats.bookings?.this_week || 0} this week`;
|
|
|
|
// Update News Stats
|
|
document.getElementById('stat-news-count').textContent = stats.news?.published || 0;
|
|
document.getElementById('stat-news-draft').textContent = `${stats.news?.draft || 0} drafts`;
|
|
|
|
// Update Subscribers Stats
|
|
document.getElementById('stat-subscribers-count').textContent = stats.subscribers?.total || 0;
|
|
document.getElementById('stat-subscribers-active').textContent = `${stats.subscribers?.active || 0} active`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load stats:', error);
|
|
// Show zeros on error
|
|
}
|
|
}
|
|
|
|
async function loadRecentNews() {
|
|
try {
|
|
const response = await MSPE.API.get('news.php?limit=3');
|
|
const container = document.getElementById('recent-news');
|
|
|
|
if (response && response.success && response.data && response.data.length > 0) {
|
|
container.innerHTML = response.data.map(item => `
|
|
<div class="content-item">
|
|
<div class="item-image">
|
|
<img src="${item.featured_image || '/images/logo/logo.png'}" alt="${MSPE.escapeHtml(item.title)}" style="object-fit: cover;">
|
|
</div>
|
|
<div class="item-info">
|
|
<h4>${MSPE.escapeHtml(item.title)}</h4>
|
|
<span class="item-meta">${MSPE.formatDate(item.created_at)} • ${MSPE.escapeHtml(item.category || 'News')}</span>
|
|
</div>
|
|
<div class="item-actions">
|
|
<a href="/admin/news.html?edit=${encodeURIComponent(item.id)}" class="action-btn edit"><i class="fas fa-edit"></i></a>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
} else {
|
|
container.innerHTML = `
|
|
<div class="empty-state" style="padding: 2rem; text-align: center;">
|
|
<i class="fas fa-newspaper" style="font-size: 2rem; opacity: 0.3; margin-bottom: 0.5rem;"></i>
|
|
<p>No articles yet</p>
|
|
<a href="/admin/news.html?action=add" class="btn btn-primary btn-sm" style="margin-top: 1rem;">Create First Article</a>
|
|
</div>
|
|
`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load recent news:', error);
|
|
document.getElementById('recent-news').innerHTML = `
|
|
<div class="empty-state" style="padding: 2rem; text-align: center;">
|
|
<i class="fas fa-exclamation-circle" style="font-size: 2rem; opacity: 0.3;"></i>
|
|
<p>Could not load articles</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
async function loadRecentMessages() {
|
|
try {
|
|
const response = await MSPE.API.get('contact.php?limit=3');
|
|
const container = document.getElementById('recent-messages');
|
|
|
|
if (response && response.success && response.data && response.data.length > 0) {
|
|
container.innerHTML = response.data.map(msg => {
|
|
const safeName = MSPE.escapeHtml((msg.first_name || 'Unknown') + ' ' + (msg.last_name || ''));
|
|
const safePreview = MSPE.escapeHtml((msg.message || '').substring(0, 50)) + (msg.message && msg.message.length > 50 ? '...' : '');
|
|
const safeStatus = ['unread', 'read', 'replied'].includes(msg.status) ? msg.status : 'unread';
|
|
const safeId = encodeURIComponent(msg.id);
|
|
return `
|
|
<a class="message-item" style="cursor: pointer; text-decoration: none; color: inherit;" href="/admin/messages.html?id=${safeId}">
|
|
<div class="message-avatar">
|
|
<img src="https://ui-avatars.com/api/?name=${encodeURIComponent((msg.first_name || 'U') + ' ' + (msg.last_name || ''))}&background=random" alt="Avatar">
|
|
</div>
|
|
<div class="message-info">
|
|
<h4>${safeName}</h4>
|
|
<p>${safePreview}</p>
|
|
<span class="message-time">${MSPE.formatDate(msg.created_at)}</span>
|
|
</div>
|
|
<span class="message-status ${safeStatus}"></span>
|
|
</a>`;
|
|
}).join('');
|
|
} else {
|
|
container.innerHTML = `
|
|
<div class="empty-state" style="padding: 2rem; text-align: center;">
|
|
<i class="fas fa-inbox" style="font-size: 2rem; opacity: 0.3; margin-bottom: 0.5rem;"></i>
|
|
<p>No messages yet</p>
|
|
<span style="font-size: 0.85rem; opacity: 0.7;">Messages from your contact form will appear here</span>
|
|
</div>
|
|
`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load recent messages:', error);
|
|
document.getElementById('recent-messages').innerHTML = `
|
|
<div class="empty-state" style="padding: 2rem; text-align: center;">
|
|
<i class="fas fa-exclamation-circle" style="font-size: 2rem; opacity: 0.3;"></i>
|
|
<p>Could not load messages</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|