446 lines
20 KiB
HTML
Executable File
446 lines
20 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>Services Management - 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">
|
|
<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 active">
|
|
<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">Services Management</h1>
|
|
</div>
|
|
|
|
<div class="header-right">
|
|
<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="/admin/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>
|
|
|
|
<!-- Content -->
|
|
<div class="content-wrapper">
|
|
<div class="content-header">
|
|
<p class="content-description">Manage your service offerings displayed on the website</p>
|
|
<button class="btn btn-primary" id="add-service-btn">
|
|
<i class="fas fa-plus"></i> Add Service
|
|
</button>
|
|
</div>
|
|
|
|
<div class="services-admin-grid" id="services-admin-grid">
|
|
<div style="grid-column:1/-1;text-align:center;padding:3rem;color:var(--gray-400);">
|
|
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
|
<p style="margin-top:1rem;">Loading services...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Service Edit Modal -->
|
|
<div class="modal" id="service-modal">
|
|
<div class="modal-overlay"></div>
|
|
<div class="modal-content modal-lg">
|
|
<div class="modal-header">
|
|
<h2>Edit Service</h2>
|
|
<button class="modal-close" id="service-modal-close-btn">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="service-form">
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label>Service Name</label>
|
|
<input type="text" class="form-control" id="service-name" placeholder="Service name">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Icon</label>
|
|
<select class="form-control" id="service-icon">
|
|
<option value="fa-laptop-code">Laptop Code</option>
|
|
<option value="fa-shield-alt">Shield</option>
|
|
<option value="fa-cloud">Cloud</option>
|
|
<option value="fa-lightbulb">Lightbulb</option>
|
|
<option value="fa-server">Server</option>
|
|
<option value="fa-database">Database</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Short Description</label>
|
|
<textarea class="form-control" id="service-description" rows="3" placeholder="Brief service description..."></textarea>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Features (one per line)</label>
|
|
<textarea class="form-control" id="service-features" rows="6" placeholder="24/7 Remote Support
|
|
On-Site Support
|
|
Proactive Monitoring
|
|
Network Management"></textarea>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label>Starting Price</label>
|
|
<input type="text" class="form-control" id="service-price" placeholder="$150/month or Custom">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>CTA Button Text</label>
|
|
<input type="text" class="form-control" id="service-cta" placeholder="Contact Us">
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline" id="service-modal-cancel-btn">Cancel</button>
|
|
<button class="btn btn-primary" id="service-modal-save-btn">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/admin/js/admin.js"></script>
|
|
<script>
|
|
let allServices = [];
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadServices();
|
|
document.getElementById('add-service-btn').addEventListener('click', openAddServiceModal);
|
|
|
|
document.getElementById('service-modal-close-btn')?.addEventListener('click', closeModal);
|
|
document.getElementById('service-modal-cancel-btn')?.addEventListener('click', closeModal);
|
|
document.getElementById('service-modal-save-btn')?.addEventListener('click', saveService);
|
|
document.querySelector('#service-modal .modal-overlay')?.addEventListener('click', closeModal);
|
|
|
|
const servicesGrid = document.getElementById('services-admin-grid');
|
|
servicesGrid?.addEventListener('click', function(e) {
|
|
const editBtn = e.target.closest('[data-action="edit-service"]');
|
|
if (editBtn) {
|
|
const id = editBtn.dataset.id;
|
|
if (id) editService(id);
|
|
return;
|
|
}
|
|
|
|
const deleteBtn = e.target.closest('[data-action="delete-service"]');
|
|
if (deleteBtn) {
|
|
const id = deleteBtn.dataset.id;
|
|
if (id) deleteService(id);
|
|
}
|
|
});
|
|
|
|
servicesGrid?.addEventListener('change', function(e) {
|
|
const toggle = e.target.closest('[data-action="toggle-service"]');
|
|
if (!toggle) return;
|
|
const id = toggle.dataset.id;
|
|
if (id) toggleService(id, toggle.checked);
|
|
});
|
|
});
|
|
|
|
async function loadServices() {
|
|
try {
|
|
const response = await MSPE.API.get('services.php');
|
|
if (response && response.success) {
|
|
allServices = response.data;
|
|
renderServices(allServices);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading services:', error);
|
|
}
|
|
}
|
|
|
|
function renderServices(services) {
|
|
const container = document.querySelector('#services-admin-grid');
|
|
if (services.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-concierge-bell"></i>
|
|
<p>No services found</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = services.map(service => `
|
|
<div class="service-admin-card ${service.active ? '' : 'opacity-50'}">
|
|
<div class="service-admin-header">
|
|
<div class="service-icon" style="background: var(--primary); color: white;">
|
|
<i class="fas ${service.icon || 'fa-cogs'}"></i>
|
|
</div>
|
|
<div class="service-actions">
|
|
<button class="btn btn-sm btn-icon" data-action="edit-service" data-id="${MSPE.escapeHtml(service.id || '')}">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" ${service.active ? 'checked' : ''} data-action="toggle-service" data-id="${MSPE.escapeHtml(service.id || '')}">
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
<button class="btn btn-sm btn-icon text-danger" data-action="delete-service" data-id="${MSPE.escapeHtml(service.id || '')}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<h3>${service.name}</h3>
|
|
<p>${service.description || ''}</p>
|
|
${service.features ? `<ul class="service-features-list">${service.features.split('\n').filter(f => f.trim()).map(f => `<li><i class="fas fa-check"></i> ${f.trim()}</li>`).join('')}</ul>` : ''}
|
|
<div class="service-meta">
|
|
<span><i class="fas fa-dollar-sign"></i> ${service.price || 'Custom'}</span>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function openAddServiceModal() {
|
|
const modal = document.getElementById('service-modal');
|
|
const form = document.getElementById('service-form');
|
|
form.reset();
|
|
form.dataset.id = '';
|
|
modal.querySelector('.modal-header h2').textContent = 'Add Service';
|
|
modal.classList.add('active');
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('service-modal').classList.remove('active');
|
|
}
|
|
|
|
function editService(id) {
|
|
const service = allServices.find(s => s.id === id);
|
|
if (!service) return;
|
|
|
|
const modal = document.getElementById('service-modal');
|
|
const form = document.getElementById('service-form');
|
|
|
|
form.dataset.id = id;
|
|
document.getElementById('service-name').value = service.name;
|
|
document.getElementById('service-icon').value = service.icon || 'fa-laptop-code';
|
|
document.getElementById('service-description').value = service.description || '';
|
|
document.getElementById('service-features').value = service.features || '';
|
|
document.getElementById('service-price').value = service.price || '';
|
|
document.getElementById('service-cta').value = service.cta_text || '';
|
|
|
|
modal.querySelector('.modal-header h2').textContent = 'Edit Service';
|
|
modal.classList.add('active');
|
|
}
|
|
|
|
async function saveService() {
|
|
const form = document.getElementById('service-form');
|
|
const id = form.dataset.id;
|
|
|
|
const formData = new FormData();
|
|
if (id) formData.append('id', id);
|
|
|
|
formData.append('name', document.getElementById('service-name').value);
|
|
formData.append('icon', document.getElementById('service-icon').value);
|
|
formData.append('description', document.getElementById('service-description').value);
|
|
formData.append('features', document.getElementById('service-features').value);
|
|
formData.append('price', document.getElementById('service-price').value);
|
|
formData.append('cta_text', document.getElementById('service-cta').value);
|
|
|
|
try {
|
|
const result = await MSPE.API.upload('services.php', formData);
|
|
if (result && result.success) {
|
|
MSPE.showNotification('Service saved successfully', 'success');
|
|
closeModal();
|
|
loadServices();
|
|
} else {
|
|
MSPE.showNotification(result?.message || 'Error saving service', 'error');
|
|
}
|
|
} catch (error) {
|
|
MSPE.showNotification('Error saving service', 'error');
|
|
}
|
|
}
|
|
|
|
async function toggleService(id, active) {
|
|
const formData = new FormData();
|
|
formData.append('id', id);
|
|
formData.append('active', active);
|
|
|
|
try {
|
|
await MSPE.API.upload('services.php', formData);
|
|
MSPE.showNotification(`Service ${active ? 'activated' : 'deactivated'}`, 'success');
|
|
} catch (error) {
|
|
MSPE.showNotification('Error updating status', 'error');
|
|
loadServices();
|
|
}
|
|
}
|
|
|
|
async function deleteService(id) {
|
|
if (!await MSPE.confirmDialog('Delete this service?')) return;
|
|
|
|
try {
|
|
const response = await MSPE.API.delete(`services.php?id=${id}`);
|
|
if (response.success) {
|
|
MSPE.showNotification('Service deleted', 'success');
|
|
loadServices();
|
|
}
|
|
} catch (error) {
|
|
MSPE.showNotification('Error deleting service', 'error');
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|