mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
Initial commit
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
ob_start();
|
||||
$pageTitle = 'About Us Content';
|
||||
$currentPage = 'about-us';
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
/* ── Actions ─────────────────────────────────────── */
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
$sectionId = $_POST['section_id'] ?? '';
|
||||
|
||||
if ($action === 'create') {
|
||||
$body = trim($_POST['body'] ?? '');
|
||||
if ($body) {
|
||||
db()->insert('about_us_sections', [
|
||||
'section_id' => generateId('sec_'),
|
||||
'heading' => trim($_POST['heading'] ?? '') ?: null,
|
||||
'body' => $body,
|
||||
'sort_order' => intval($_POST['sort_order'] ?? 0),
|
||||
'is_active' => 1,
|
||||
]);
|
||||
setFlash('success', 'Section added');
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'update' && $sectionId) {
|
||||
$body = trim($_POST['body'] ?? '');
|
||||
if ($body) {
|
||||
db()->update('about_us_sections', [
|
||||
'heading' => trim($_POST['heading'] ?? '') ?: null,
|
||||
'body' => $body,
|
||||
'sort_order' => intval($_POST['sort_order'] ?? 0),
|
||||
'is_active' => isset($_POST['is_active']) ? 1 : 0,
|
||||
], 'section_id = :id', ['id' => $sectionId]);
|
||||
setFlash('success', 'Section updated');
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'delete' && $sectionId) {
|
||||
db()->delete('about_us_sections', 'section_id = :id', ['id' => $sectionId]);
|
||||
setFlash('success', 'Section deleted');
|
||||
}
|
||||
|
||||
if ($action === 'reorder') {
|
||||
$ids = json_decode($_POST['order'] ?? '[]', true);
|
||||
foreach ($ids as $pos => $sid) {
|
||||
db()->update('about_us_sections', ['sort_order' => $pos + 1],
|
||||
'section_id = :id', ['id' => $sid]);
|
||||
}
|
||||
echo json_encode(['ok' => true]); exit;
|
||||
}
|
||||
|
||||
header('Location: /admin/about-us.php'); exit;
|
||||
}
|
||||
|
||||
$sections = db()->fetchAll(
|
||||
"SELECT * FROM about_us_sections ORDER BY sort_order ASC, id ASC"
|
||||
);
|
||||
?>
|
||||
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">About Us Content</h1>
|
||||
<button class="btn btn-primary" onclick="openModal()">
|
||||
<i class="fas fa-plus"></i> Add Section
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php if (hasFlash('success')): ?>
|
||||
<div class="alert alert-success"><i class="fas fa-check-circle"></i> <?= getFlash('success') ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="admin-card" style="margin-bottom:1.5rem">
|
||||
<div class="admin-card-body" style="padding:.85rem 1.5rem">
|
||||
<p class="text-muted" style="margin:0">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
These sections appear on the homepage between <strong>Our Story</strong> and the <strong>Explore Our Coffee</strong> button.
|
||||
Drag rows to reorder. Blank lines in the text become paragraph breaks.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live Preview -->
|
||||
<div class="admin-card" style="margin-bottom:1.5rem">
|
||||
<div class="admin-card-header">
|
||||
<h3><i class="fas fa-eye"></i> Homepage Preview</h3>
|
||||
<a href="/" target="_blank" class="btn btn-sm btn-secondary"><i class="fas fa-external-link-alt"></i> View Live</a>
|
||||
</div>
|
||||
<div class="admin-card-body" style="background:var(--admin-bg);border-radius:var(--radius-md);padding:2rem">
|
||||
<h2 style="font-family:Georgia,serif;font-size:1.75rem;margin:0 0 1rem">Our Story</h2>
|
||||
<div id="livePreview" style="font-size:.9375rem;color:var(--admin-text-muted);line-height:1.7">
|
||||
<?php foreach ($sections as $sec): if (!$sec['is_active']) continue; ?>
|
||||
<?php if (!empty($sec['heading'])): ?>
|
||||
<h3 style="font-size:1.1rem;font-weight:600;margin:.5rem 0 .4rem"><?= htmlspecialchars($sec['heading']) ?></h3>
|
||||
<?php endif; ?>
|
||||
<?php foreach (array_filter(array_map('trim', preg_split('/\n{2,}/', $sec['body']))) as $para): ?>
|
||||
<p style="margin:0 0 1rem"><?= nl2br(htmlspecialchars($para)) ?></p>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<a href="#" class="btn btn-primary" style="margin-top:.5rem;pointer-events:none;opacity:.8">Explore Our Coffee →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section List -->
|
||||
<div class="admin-card">
|
||||
<div class="admin-card-body" style="padding:0">
|
||||
<?php if (empty($sections)): ?>
|
||||
<div class="text-center text-muted" style="padding:3rem">No sections yet. Click <strong>Add Section</strong> to get started.</div>
|
||||
<?php else: ?>
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30px"></th>
|
||||
<th>Heading</th>
|
||||
<th>Body Text</th>
|
||||
<th style="width:60px">Order</th>
|
||||
<th style="width:80px">Status</th>
|
||||
<th style="width:100px">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sectionTbody">
|
||||
<?php foreach ($sections as $sec): ?>
|
||||
<tr data-id="<?= $sec['section_id'] ?>">
|
||||
<td style="color:var(--admin-text-muted);text-align:center;cursor:grab"><i class="fas fa-grip-vertical"></i></td>
|
||||
<td style="font-weight:600;min-width:120px"><?= htmlspecialchars($sec['heading'] ?? '—') ?></td>
|
||||
<td style="color:var(--admin-text-muted);font-size:.875rem">
|
||||
<div style="max-width:420px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
||||
<?= htmlspecialchars($sec['body']) ?>
|
||||
</div>
|
||||
</td>
|
||||
<td class="sort-cell"><?= $sec['sort_order'] ?></td>
|
||||
<td>
|
||||
<?= $sec['is_active']
|
||||
? '<span class="badge badge-success">Active</span>'
|
||||
: '<span class="badge badge-error">Hidden</span>' ?>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary" onclick='openModal(<?= json_encode($sec, JSON_HEX_APOS | JSON_HEX_QUOT) ?>)' title="Edit">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<form method="POST" style="display:inline">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="section_id" value="<?= $sec['section_id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-confirm="Delete this section?">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Modal ──────────────────────────────────────── -->
|
||||
<div class="modal-overlay" id="sectionModal">
|
||||
<div class="modal" style="max-width:680px;width:95vw">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="modalTitle">Add Section</h3>
|
||||
<button type="button" class="modal-close" onclick="Modal.close('sectionModal')">×</button>
|
||||
</div>
|
||||
<form method="POST" id="sectionForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="action" id="formAction" value="create">
|
||||
<input type="hidden" name="section_id" id="formSectionId">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
Heading <span class="text-muted" style="font-weight:400">(optional sub-heading above this block)</span>
|
||||
</label>
|
||||
<input type="text" name="heading" id="formHeading" class="form-input"
|
||||
placeholder="e.g. Our Mission, From Farm to Cup…">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Body Text *</label>
|
||||
<textarea name="body" id="formBody" class="form-input" rows="10"
|
||||
style="resize:vertical;font-size:.9375rem;line-height:1.6"
|
||||
placeholder="Type your text here. Leave a blank line between paragraphs to create separate paragraph blocks."
|
||||
required oninput="updatePreview()"></textarea>
|
||||
<small class="text-muted"><i class="fas fa-paragraph"></i> Blank line = new paragraph | <i class="fas fa-level-down-alt fa-rotate-90"></i> Single line break = <br></small>
|
||||
</div>
|
||||
|
||||
<!-- Inline preview inside modal -->
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label class="form-label" style="display:flex;justify-content:space-between">
|
||||
<span>Text Preview</span>
|
||||
<span class="text-muted" style="font-weight:400;font-size:.8rem">Updates as you type</span>
|
||||
</label>
|
||||
<div id="inlinePreview"
|
||||
style="border:1px solid var(--color-border);border-radius:var(--radius-md);padding:1rem 1.25rem;
|
||||
background:var(--admin-bg);min-height:80px;font-size:.9375rem;color:var(--admin-text-muted);line-height:1.7">
|
||||
<em style="opacity:.4">Preview will appear here…</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div id="statusWrap" style="display:none;margin-right:auto">
|
||||
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer">
|
||||
<input type="checkbox" name="is_active" id="formActive" checked> Active
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group mb-0" style="margin-right:auto">
|
||||
<label class="form-label" style="display:inline;margin-right:.5rem">Order</label>
|
||||
<input type="number" name="sort_order" id="formOrder" class="form-input"
|
||||
value="0" min="0" style="width:70px;display:inline-block">
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" onclick="Modal.close('sectionModal')">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" id="formSubmitBtn">Add Section</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#sectionTbody tr { cursor: grab; }
|
||||
#sectionTbody tr.drag-over { background: rgba(255,94,26,.06); }
|
||||
#formBody:focus { border-color: var(--admin-primary); }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/* ── Modal ───────────────────────────────────────── */
|
||||
function openModal(sec) {
|
||||
var isEdit = !!sec;
|
||||
document.getElementById('modalTitle').textContent = isEdit ? 'Edit Section' : 'Add Section';
|
||||
document.getElementById('formSubmitBtn').textContent = isEdit ? 'Save Changes' : 'Add Section';
|
||||
document.getElementById('formAction').value = isEdit ? 'update' : 'create';
|
||||
document.getElementById('formSectionId').value = isEdit ? sec.section_id : '';
|
||||
document.getElementById('formHeading').value = isEdit ? (sec.heading || '') : '';
|
||||
document.getElementById('formBody').value = isEdit ? sec.body : '';
|
||||
document.getElementById('formOrder').value = isEdit ? sec.sort_order : 0;
|
||||
document.getElementById('formActive').checked = isEdit ? !!parseInt(sec.is_active) : true;
|
||||
document.getElementById('statusWrap').style.display = isEdit ? '' : 'none';
|
||||
updatePreview();
|
||||
Modal.open('sectionModal');
|
||||
// Focus textarea after modal opens
|
||||
setTimeout(function() { document.getElementById('formBody').focus(); }, 150);
|
||||
}
|
||||
|
||||
/* ── Inline text preview ─────────────────────────── */
|
||||
function updatePreview() {
|
||||
var raw = document.getElementById('formBody').value;
|
||||
var box = document.getElementById('inlinePreview');
|
||||
if (!raw.trim()) {
|
||||
box.innerHTML = '<em style="opacity:.4">Preview will appear here…</em>';
|
||||
return;
|
||||
}
|
||||
var paras = raw.split(/\n{2,}/).map(function(p) { return p.trim(); }).filter(Boolean);
|
||||
box.innerHTML = paras.map(function(p) {
|
||||
return '<p style="margin:0 0 .75rem">' + p.replace(/\n/g, '<br>').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/<br>/g,'<br>') + '</p>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/* ── Drag-to-reorder ─────────────────────────────── */
|
||||
(function() {
|
||||
var tbody = document.getElementById('sectionTbody');
|
||||
if (!tbody) return;
|
||||
var dragging = null;
|
||||
|
||||
tbody.querySelectorAll('tr').forEach(function(row) {
|
||||
row.draggable = true;
|
||||
row.addEventListener('dragstart', function() { dragging = this; this.style.opacity = '.4'; });
|
||||
row.addEventListener('dragend', function() { this.style.opacity = ''; dragging = null; saveOrder(); });
|
||||
row.addEventListener('dragover', function(e) { e.preventDefault(); this.classList.add('drag-over'); });
|
||||
row.addEventListener('dragleave', function() { this.classList.remove('drag-over'); });
|
||||
row.addEventListener('drop', function(e) {
|
||||
e.preventDefault(); this.classList.remove('drag-over');
|
||||
if (dragging && dragging !== this) {
|
||||
if (Array.from(tbody.querySelectorAll('tr')).indexOf(dragging) <
|
||||
Array.from(tbody.querySelectorAll('tr')).indexOf(this)) this.after(dragging);
|
||||
else this.before(dragging);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function saveOrder() {
|
||||
var ids = Array.from(tbody.querySelectorAll('tr')).map(function(r) { return r.dataset.id; });
|
||||
fetch('/admin/about-us.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'action=reorder&order=' + encodeURIComponent(JSON.stringify(ids))
|
||||
});
|
||||
tbody.querySelectorAll('tr').forEach(function(r, i) {
|
||||
r.querySelector('.sort-cell').textContent = i + 1;
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
Reference in New Issue
Block a user