Initial commit: MSPE website - full site with admin panel, API, and public pages

This commit is contained in:
Krikorios
2026-02-22 02:24:15 +02:00
commit 7a90e63301
106 changed files with 477087 additions and 0 deletions
+512
View File
@@ -0,0 +1,512 @@
<!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>