Files
MSPE/admin/pages.html
T

513 lines
25 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>Pages 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 active">
<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>
</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">Pages 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 website pages and their content sections</p>
</div>
<!-- Pages Grid (loaded dynamically) -->
<div class="pages-grid" id="pages-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 pages...</p>
</div>
</div>
<!-- Global Sections -->
<div class="card" style="margin-top: 2rem;">
<div class="card-header">
<h3><i class="fas fa-layer-group"></i> Global Sections</h3>
<p class="card-subtitle">These sections appear across multiple pages</p>
</div>
<div class="card-body">
<div class="global-sections-grid" id="global-sections-grid">
<div style="text-align:center;padding:2rem;color:var(--gray-400);grid-column:1/-1;">
<i class="fas fa-spinner fa-spin"></i> Loading...
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Page Editor Modal -->
<div class="modal" id="page-modal">
<div class="modal-overlay" onclick="closePageModal()"></div>
<div class="modal-content" style="max-width:700px;">
<div class="modal-header">
<h3 id="page-modal-title">Edit Page</h3>
<button class="modal-close" onclick="closePageModal()"><i class="fas fa-times"></i></button>
</div>
<div class="modal-body" style="max-height:70vh;overflow-y:auto;">
<form id="page-form">
<input type="hidden" id="page-slug">
<!-- SEO & Meta -->
<div style="margin-bottom:1.5rem;">
<h4 style="font-size:.875rem;font-weight:600;color:var(--dark);margin-bottom:.75rem;display:flex;align-items:center;gap:.5rem;">
<i class="fas fa-search" style="color:var(--primary);"></i> SEO & Metadata
</h4>
<div class="form-group" style="margin-bottom:.75rem;">
<label>Meta Title</label>
<input type="text" class="form-control" id="page-meta-title" placeholder="Page title for search engines">
<small style="color:var(--gray-400);font-size:.75rem;">Recommended: 5060 characters</small>
</div>
<div class="form-group" style="margin-bottom:.75rem;">
<label>Meta Description</label>
<textarea class="form-control" id="page-meta-desc" rows="2" placeholder="Brief description for search results"></textarea>
<small style="color:var(--gray-400);font-size:.75rem;">Recommended: 150160 characters</small>
</div>
<div class="form-group">
<label>Status</label>
<select class="form-control" id="page-status">
<option value="published">Published</option>
<option value="draft">Draft</option>
</select>
</div>
</div>
<!-- Sections -->
<div id="page-sections-editor"></div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-outline" onclick="closePageModal()">Cancel</button>
<button class="btn btn-primary" onclick="savePage()"><i class="fas fa-save"></i> Save Changes</button>
</div>
</div>
</div>
<!-- Global Section Editor Modal -->
<div class="modal" id="global-modal">
<div class="modal-overlay" onclick="closeGlobalModal()"></div>
<div class="modal-content" style="max-width:550px;">
<div class="modal-header">
<h3 id="global-modal-title">Edit Section</h3>
<button class="modal-close" onclick="closeGlobalModal()"><i class="fas fa-times"></i></button>
</div>
<div class="modal-body">
<form id="global-form">
<input type="hidden" id="global-slug">
<div id="global-fields-editor"></div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-outline" onclick="closeGlobalModal()">Cancel</button>
<button class="btn btn-primary" onclick="saveGlobalSection()"><i class="fas fa-save"></i> Save</button>
</div>
</div>
</div>
<script src="/admin/js/admin.js"></script>
<script>
let allPages = [];
let globalSections = [];
let currentPageData = null;
document.addEventListener('DOMContentLoaded', function() {
loadPages();
loadGlobalSections();
});
// ── Load & Render ────────────────────────────────────
async function loadPages() {
try {
const res = await MSPE.API.get('pages.php');
if (res && res.success) {
allPages = res.data;
renderPages(allPages);
}
} catch (err) {
console.error('Error loading pages:', err);
document.getElementById('pages-grid').innerHTML =
'<div class="empty-state" style="grid-column:1/-1;"><i class="fas fa-exclamation-circle"></i><p>Failed to load pages</p></div>';
}
}
function renderPages(pages) {
const grid = document.getElementById('pages-grid');
if (!pages.length) {
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1;"><i class="fas fa-file-alt"></i><p>No pages found</p></div>';
return;
}
grid.innerHTML = pages.map(page => {
const sectionTags = page.sections.map(s => `<span class="section-tag">${s.label || s}</span>`).join('');
const statusClass = page.status === 'draft' ? 'draft' : 'published';
const statusLabel = page.status === 'draft' ? 'Draft' : 'Published';
const lastSaved = page.updated_at ? `<span style="font-size:.7rem;color:var(--gray-400);display:block;margin-top:.25rem;">Last saved: ${MSPE.formatDate(page.updated_at)}</span>` : '';
return `
<div class="page-admin-card">
<div class="page-admin-header">
<div class="page-icon"><i class="fas ${page.icon || 'fa-file-alt'}"></i></div>
<div class="page-status ${statusClass}"><i class="fas fa-circle"></i> ${statusLabel}</div>
</div>
<h3>${page.title}</h3>
<p>${page.description || ''}${lastSaved}</p>
<div class="page-sections">${sectionTags}</div>
<div class="page-admin-actions">
<a href="${page.url || '#'}" target="_blank" class="btn btn-sm btn-outline">
<i class="fas fa-eye"></i> View
</a>
<button class="btn btn-sm btn-primary" onclick="editPage('${page.slug}')">
<i class="fas fa-edit"></i> Edit
</button>
</div>
</div>`;
}).join('');
}
async function loadGlobalSections() {
try {
const res = await MSPE.API.get('pages.php?type=global');
if (res && res.success) {
globalSections = res.data;
renderGlobalSections(globalSections);
}
} catch (err) {
console.error('Error loading global sections:', err);
}
}
function renderGlobalSections(sections) {
const grid = document.getElementById('global-sections-grid');
grid.innerHTML = sections.map(s => `
<div class="global-section-item">
<i class="fas ${s.icon}"></i>
<div>
<h4>${s.label}</h4>
<p>${s.description}</p>
</div>
<button class="btn btn-sm btn-outline" onclick="editGlobalSection('${s.slug}')">Edit</button>
</div>
`).join('');
}
// ── Page Editor ──────────────────────────────────────
async function editPage(slug) {
try {
const res = await MSPE.API.get('pages.php?id=' + slug);
if (!res || !res.success) {
MSPE.showNotification('Failed to load page data', 'error');
return;
}
currentPageData = res.data;
document.getElementById('page-modal-title').textContent = 'Edit: ' + currentPageData.title;
document.getElementById('page-slug').value = currentPageData.slug;
document.getElementById('page-meta-title').value = currentPageData.meta_title || '';
document.getElementById('page-meta-desc').value = currentPageData.meta_description || '';
document.getElementById('page-status').value = currentPageData.status || 'published';
// Build section editors
const container = document.getElementById('page-sections-editor');
container.innerHTML = currentPageData.sections.map((section, si) => {
const fields = section.fields.map((f, fi) => {
const inputId = 'sect-' + section.key + '-' + f.name;
if (f.type === 'textarea') {
return `<div class="form-group" style="margin-bottom:.75rem;">
<label>${f.label}</label>
<textarea class="form-control section-field" id="${inputId}" data-section="${section.key}" data-field="${f.name}" rows="2" placeholder="${f.label}">${f.value || ''}</textarea>
</div>`;
}
return `<div class="form-group" style="margin-bottom:.75rem;">
<label>${f.label}</label>
<input type="text" class="form-control section-field" id="${inputId}" data-section="${section.key}" data-field="${f.name}" value="${(f.value || '').replace(/"/g, '&quot;')}" placeholder="${f.label}">
</div>`;
}).join('');
return `<div style="margin-bottom:1.5rem;border:1px solid var(--gray-200);border-radius:var(--radius-sm);overflow:hidden;">
<div style="background:var(--gray-100);padding:.625rem 1rem;font-size:.8rem;font-weight:600;color:var(--dark);display:flex;align-items:center;gap:.5rem;cursor:pointer;" onclick="this.parentElement.querySelector('.sect-body').classList.toggle('collapsed')">
<i class="fas fa-puzzle-piece" style="color:var(--primary);"></i> ${section.label}
<i class="fas fa-chevron-down" style="margin-left:auto;font-size:.65rem;color:var(--gray-400);"></i>
</div>
<div class="sect-body" style="padding:1rem;">
${fields.length ? fields : '<p style="color:var(--gray-400);font-size:.8rem;">No editable fields for this section.</p>'}
</div>
</div>`;
}).join('');
document.getElementById('page-modal').classList.add('active');
} catch (err) {
console.error(err);
MSPE.showNotification('Error loading page', 'error');
}
}
function closePageModal() {
document.getElementById('page-modal').classList.remove('active');
currentPageData = null;
}
async function savePage() {
const slug = document.getElementById('page-slug').value;
if (!slug) return;
// Gather section field values
const sectionsData = {};
document.querySelectorAll('.section-field').forEach(el => {
const sKey = el.dataset.section;
const fName = el.dataset.field;
if (!sectionsData[sKey]) sectionsData[sKey] = {};
sectionsData[sKey][fName] = el.value;
});
const payload = {
slug,
meta_title: document.getElementById('page-meta-title').value,
meta_description: document.getElementById('page-meta-desc').value,
status: document.getElementById('page-status').value,
sections_data: sectionsData,
};
try {
const res = await MSPE.API.post('pages.php', payload);
if (res && res.success) {
MSPE.showNotification('Page saved!', 'success');
closePageModal();
loadPages();
} else {
MSPE.showNotification(res?.message || 'Save failed', 'error');
}
} catch (err) {
console.error(err);
MSPE.showNotification('Error saving page', 'error');
}
}
// ── Global Section Editor ────────────────────────────
function editGlobalSection(slug) {
const section = globalSections.find(s => s.slug === slug);
if (!section) return;
document.getElementById('global-modal-title').textContent = 'Edit: ' + section.label;
document.getElementById('global-slug').value = slug;
const container = document.getElementById('global-fields-editor');
container.innerHTML = section.fields.map(f => {
const inputId = 'gf-' + slug + '-' + f.name;
if (f.type === 'textarea') {
return `<div class="form-group" style="margin-bottom:.75rem;">
<label>${f.label}</label>
<textarea class="form-control global-field" id="${inputId}" data-field="${f.name}" rows="2" placeholder="${f.label}">${f.value || ''}</textarea>
</div>`;
}
return `<div class="form-group" style="margin-bottom:.75rem;">
<label>${f.label}</label>
<input type="text" class="form-control global-field" id="${inputId}" data-field="${f.name}" value="${(f.value || '').replace(/"/g, '&quot;')}" placeholder="${f.label}">
</div>`;
}).join('');
document.getElementById('global-modal').classList.add('active');
}
function closeGlobalModal() {
document.getElementById('global-modal').classList.remove('active');
}
async function saveGlobalSection() {
const slug = document.getElementById('global-slug').value;
if (!slug) return;
const fields = {};
document.querySelectorAll('.global-field').forEach(el => {
fields[el.dataset.field] = el.value;
});
try {
const res = await MSPE.API.post('pages.php?type=global', { section_slug: slug, fields });
if (res && res.success) {
MSPE.showNotification('Section saved!', 'success');
closeGlobalModal();
loadGlobalSections();
} else {
MSPE.showNotification(res?.message || 'Save failed', 'error');
}
} catch (err) {
console.error(err);
MSPE.showNotification('Error saving section', 'error');
}
}
</script>
</body>
</html>