Add pub hours admin panel, rebuild announcements/users lists on CSS Grid
- Add a Hours admin panel (admin/hours.php, admin/hours.js) for editing the weekly open/close schedule and an optional per-day kitchen closing time, with a live preview of how it'll read on the public site - Store hours in a new pub_hours table (inc/db.php), seeded with the site's existing Thu-Sat/9pm-kitchen schedule; inc/hours.php formats and groups it for display (day ranges, kitchen-close note, footer summary) - Wire the public Visit card and both site footers (index.php, menu.php) to read hours from that data instead of hardcoded copy - Rebuild the Announcements and Users admin tables onto the same CSS Grid list pattern already used for the Menu (admin-table-actions -> row-actions), and make the Announcements status toggle an in-place AJAX action like the menu item's (admin/index.js) - Add the Hours tab to admin nav across all four admin pages Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fd928a24cf
commit
eab20899de
14 files changed
+1224
-441
No files matched your search
@@ -0,0 +1,18 @@
|
||||
// Hours admin page: purely a progressive enhancement over the plain form
|
||||
// POST that actually saves — greys out (and stops submitting) a day's time
|
||||
// fields while its "Open" box is unchecked, so a closed day can't look like
|
||||
// it still has stale hours set.
|
||||
|
||||
document.querySelectorAll('.hours-day-row').forEach((row) => {
|
||||
const toggle = row.querySelector('input[type="checkbox"]');
|
||||
const timeFields = row.querySelectorAll('.day-time-field');
|
||||
|
||||
function applyState() {
|
||||
timeFields.forEach((field) => {
|
||||
field.disabled = !toggle.checked;
|
||||
});
|
||||
}
|
||||
|
||||
toggle.addEventListener('change', applyState);
|
||||
applyState();
|
||||
});
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../inc/auth.php';
|
||||
require_once __DIR__ . '/../inc/hours.php';
|
||||
|
||||
require_auth();
|
||||
$user = get_logged_in_user();
|
||||
|
||||
$errorMsg = '';
|
||||
|
||||
/**
|
||||
* Reads a day's submitted fields back into the same shape get_all_hours()
|
||||
* rows use, so the form can re-render exactly what was typed (rather than
|
||||
* falling back to the last-saved DB values) when validation fails.
|
||||
*/
|
||||
function hours_day_from_post(array $dayData): array {
|
||||
return [
|
||||
'is_open' => !empty($dayData['is_open']) ? 1 : 0,
|
||||
'open_time' => normalize_time_value($dayData['open_time'] ?? null),
|
||||
'close_time' => normalize_time_value($dayData['close_time'] ?? null),
|
||||
'kitchen_close_time' => normalize_time_value($dayData['kitchen_close_time'] ?? null),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A day marked "Open" needs both ends of its range set — everything else
|
||||
* (closed days, the optional kitchen closing time) is fine left blank.
|
||||
*/
|
||||
function validate_hours_input(array $daysInput): ?string {
|
||||
for ($d = 0; $d <= 6; $d++) {
|
||||
$day = hours_day_from_post(is_array($daysInput[$d] ?? null) ? $daysInput[$d] : []);
|
||||
if ($day['is_open'] && (!$day['open_time'] || !$day['close_time'])) {
|
||||
return HOURS_DAY_NAMES[$d] . ' is marked open but is missing an opening or closing time.';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
$submittedHours = null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!verify_csrf_token($_POST['csrf_token'] ?? '')) {
|
||||
$errorMsg = 'Session expired or invalid security token. Please try again.';
|
||||
} elseif (($_POST['action'] ?? '') === 'save_hours') {
|
||||
$daysInput = is_array($_POST['day'] ?? null) ? $_POST['day'] : [];
|
||||
|
||||
if ($error = validate_hours_input($daysInput)) {
|
||||
$errorMsg = $error;
|
||||
$submittedHours = [];
|
||||
for ($d = 0; $d <= 6; $d++) {
|
||||
$submittedHours[$d] = hours_day_from_post(is_array($daysInput[$d] ?? null) ? $daysInput[$d] : []);
|
||||
}
|
||||
} else {
|
||||
save_all_hours($daysInput);
|
||||
header('Location: hours.php?saved=1');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$hoursByDay = $submittedHours ?? get_all_hours();
|
||||
$justSaved = !$_POST && isset($_GET['saved']);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Hours | The Old Fitz Admin</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<div class="container admin-nav-inner">
|
||||
<a href="index.php" class="admin-brand">
|
||||
<img src="../assets/knot.png" alt="" class="knot">
|
||||
<span>The Old Fitz Admin</span>
|
||||
</a>
|
||||
<div class="admin-nav-links">
|
||||
<a href="index.php">Announcements</a>
|
||||
<a href="menu.php">Menu</a>
|
||||
<a href="users.php">Users</a>
|
||||
<a href="hours.php" class="active">Hours</a>
|
||||
</div>
|
||||
<div class="admin-user">
|
||||
<span>Logged in as <strong><?= htmlspecialchars($user['username']) ?></strong></span>
|
||||
<a href="../" target="_blank" rel="noopener">View Public Site ↗</a>
|
||||
<a href="logout.php">Sign Out</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="dashboard-layout">
|
||||
<div class="container">
|
||||
<div class="dash-header">
|
||||
<p class="eyebrow">Operations</p>
|
||||
<h1>Pub Hours</h1>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($errorMsg)): ?>
|
||||
<div class="alert-error" role="alert"><?= htmlspecialchars($errorMsg) ?></div>
|
||||
<?php elseif ($justSaved): ?>
|
||||
<div class="alert-success" role="status">Hours saved — the public site is now showing the updated schedule.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span>🕒 Weekly Schedule</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="hours.php">
|
||||
<input type="hidden" name="action" value="save_hours">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
|
||||
<div class="hours-grid-wrap">
|
||||
<div class="hours-grid-head" aria-hidden="true">
|
||||
<span class="col-day">Day</span>
|
||||
<span class="col-hopen">Open</span>
|
||||
<span class="col-hopens">Opens</span>
|
||||
<span class="col-hcloses">Closes</span>
|
||||
<span class="col-hkitchen">Kitchen Closes</span>
|
||||
</div>
|
||||
<div class="hours-day-rows">
|
||||
<?php for ($d = 0; $d <= 6; $d++): ?>
|
||||
<?php $day = $hoursByDay[$d]; ?>
|
||||
<div class="hours-day-row" data-day="<?= $d ?>">
|
||||
<div data-label="Day"><strong><?= HOURS_DAY_NAMES[$d] ?></strong></div>
|
||||
<div data-label="Open">
|
||||
<label class="checkbox-label day-open-toggle">
|
||||
<input type="checkbox" name="day[<?= $d ?>][is_open]" value="1" <?= !empty($day['is_open']) ? 'checked' : '' ?>>
|
||||
Open
|
||||
</label>
|
||||
</div>
|
||||
<div data-label="Opens">
|
||||
<input type="time" name="day[<?= $d ?>][open_time]" class="form-control day-time-field" value="<?= htmlspecialchars($day['open_time'] ?? '') ?>">
|
||||
</div>
|
||||
<div data-label="Closes">
|
||||
<input type="time" name="day[<?= $d ?>][close_time]" class="form-control day-time-field" value="<?= htmlspecialchars($day['close_time'] ?? '') ?>">
|
||||
</div>
|
||||
<div data-label="Kitchen Closes">
|
||||
<input type="time" name="day[<?= $d ?>][kitchen_close_time]" class="form-control day-time-field" value="<?= htmlspecialchars($day['kitchen_close_time'] ?? '') ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="muted field-hint" style="margin-top: 1.25rem;">Leave "Kitchen Closes" blank on a day if the kitchen doesn't have a separate cutoff from the bar. Special closures (holidays, weather) are handled on the <a href="index.php">Announcements</a> page instead of here.</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Save Hours</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span>👀 Public Site Preview</span>
|
||||
</h2>
|
||||
</div>
|
||||
<p class="muted" style="margin-bottom: 1.25rem;">This is how the schedule above will read on the site once saved.</p>
|
||||
|
||||
<div class="hours-preview">
|
||||
<div class="hours-preview-card">
|
||||
<dl class="hours-list">
|
||||
<?php foreach (hours_card_rows($hoursByDay) as $row): ?>
|
||||
<div class="hours-row">
|
||||
<dt><?= htmlspecialchars($row['label']) ?></dt>
|
||||
<dd><?= htmlspecialchars($row['text']) ?></dd>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</dl>
|
||||
<?php $kitchenNote = hours_kitchen_note($hoursByDay); ?>
|
||||
<p class="note">
|
||||
<?php if ($kitchenNote): ?><strong><?= htmlspecialchars($kitchenNote) ?></strong> — bar snacks stay out later. <?php endif; ?>Holiday hours may vary; check our Facebook page for the latest.
|
||||
</p>
|
||||
</div>
|
||||
<div class="hours-preview-footer">
|
||||
<p class="col-title">Hours</p>
|
||||
<div class="hours-mini">
|
||||
<?php foreach (hours_footer_lines($hoursByDay) as $line): ?>
|
||||
<span><?= htmlspecialchars($line) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="hours.js" defer></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
// Announcements admin page: intercepts the status badge's toggle form so
|
||||
// clicking it updates in place via fetch instead of reloading the whole
|
||||
// page — same interaction as the menu item's status toggle. Everything
|
||||
// else here (create/edit/delete) is still a plain form POST + reload.
|
||||
|
||||
document.addEventListener('submit', async (e) => {
|
||||
const form = e.target;
|
||||
if (!form.classList.contains('status-toggle-form')) return;
|
||||
|
||||
e.preventDefault();
|
||||
const btn = form.querySelector('button');
|
||||
btn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('index.php', {
|
||||
method: 'POST',
|
||||
body: new FormData(form),
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
btn.className = `status-badge ${data.status_class}`;
|
||||
btn.textContent = data.status_label;
|
||||
btn.title = `Click to turn ${data.is_active ? 'off' : 'on'}`;
|
||||
} else {
|
||||
alert(data.error || 'Failed to update status.');
|
||||
}
|
||||
} catch {
|
||||
alert('Network error. Please try again.');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
}
|
||||
});
|
||||
+219
-186
@@ -5,12 +5,40 @@ require_once __DIR__ . '/../inc/announcements.php';
|
||||
require_auth();
|
||||
$user = get_logged_in_user();
|
||||
|
||||
$successMsg = '';
|
||||
$errorMsg = '';
|
||||
$today = date('Y-m-d');
|
||||
|
||||
// Handle Actions (Save, Delete, Toggle)
|
||||
/**
|
||||
* Whether an announcement is actually showing right now — active flag on
|
||||
* AND within its optional date window. Matches the currently-live check
|
||||
* used when rendering the list below.
|
||||
*/
|
||||
function announcement_is_live(array $ann, string $today): bool
|
||||
{
|
||||
return (bool) $ann['is_active']
|
||||
&& (empty($ann['start_date']) || $ann['start_date'] <= $today)
|
||||
&& (empty($ann['end_date']) || $ann['end_date'] >= $today);
|
||||
}
|
||||
|
||||
// Handle Actions (Save, Delete, Toggle). Success isn't announced with a
|
||||
// banner — the list itself is the feedback (the row appears/updates/
|
||||
// disappears, the status badge flips) — same as the menu admin, and it
|
||||
// avoids a banner shoving the whole page down every time someone clicks
|
||||
// the status badge.
|
||||
//
|
||||
// The status toggle specifically is also fetched via AJAX (see index.js)
|
||||
// so clicking it doesn't reload the page, matching the menu item's status
|
||||
// toggle — everything else here (save/delete) is still a plain form POST.
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$isAjax = ($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'XMLHttpRequest';
|
||||
|
||||
if (!verify_csrf_token($_POST['csrf_token'] ?? '')) {
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'error' => 'Session expired or invalid security token. Please refresh.']);
|
||||
exit;
|
||||
}
|
||||
$errorMsg = 'Session expired or invalid security token. Please try again.';
|
||||
} else {
|
||||
$action = $_POST['action'] ?? '';
|
||||
@@ -27,19 +55,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'show_banner' => isset($_POST['show_banner']) ? 1 : 0,
|
||||
'show_in_hours' => isset($_POST['show_in_hours']) ? 1 : 0,
|
||||
], $annId);
|
||||
|
||||
$successMsg = $annId ? 'Announcement updated successfully!' : 'New announcement created successfully!';
|
||||
} elseif ($action === 'delete_announcement') {
|
||||
$annId = (int)($_POST['announcement_id'] ?? 0);
|
||||
if ($annId > 0) {
|
||||
delete_announcement($annId);
|
||||
$successMsg = 'Announcement deleted successfully.';
|
||||
}
|
||||
} elseif ($action === 'toggle_active') {
|
||||
$annId = (int)($_POST['announcement_id'] ?? 0);
|
||||
if ($annId > 0) {
|
||||
toggle_announcement_active($annId);
|
||||
$successMsg = 'Status updated.';
|
||||
}
|
||||
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$ann = $annId > 0 ? get_announcement_by_id($annId) : null;
|
||||
if (!$ann) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'error' => 'Announcement not found.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$isLive = announcement_is_live($ann, $today);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'is_active' => (bool) $ann['is_active'],
|
||||
'status_class' => $isLive ? 'active' : 'inactive',
|
||||
'status_label' => $isLive ? '● Live' : ($ann['is_active'] ? '⏳ Scheduled' : '○ Off'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +98,6 @@ if ($editingId) {
|
||||
}
|
||||
|
||||
$announcements = get_all_announcements();
|
||||
$today = date('Y-m-d');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -80,6 +122,7 @@ $today = date('Y-m-d');
|
||||
<a href="index.php" class="active">Announcements</a>
|
||||
<a href="menu.php">Menu</a>
|
||||
<a href="users.php">Users</a>
|
||||
<a href="hours.php">Hours</a>
|
||||
</div>
|
||||
<div class="admin-user">
|
||||
<span>Logged in as <strong><?= htmlspecialchars($user['username']) ?></strong></span>
|
||||
@@ -96,193 +139,183 @@ $today = date('Y-m-d');
|
||||
<h1>Site Announcements & Alerts</h1>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($successMsg)): ?>
|
||||
<div class="alert-success" role="status"><?= htmlspecialchars($successMsg) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($errorMsg)): ?>
|
||||
<div class="alert-error" role="alert"><?= htmlspecialchars($errorMsg) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isCreatingNew || $editItem): ?>
|
||||
<!-- Announcement Editor Form (Create / Edit) -->
|
||||
<form method="POST" action="index.php">
|
||||
<input type="hidden" name="action" value="save_announcement">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)($editItem['id'] ?? 0) ?>">
|
||||
<div class="admin-toolbar">
|
||||
<a href="index.php?new=1" class="btn btn-primary btn-sm">+ Create Announcement</a>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span><?= $editItem ? '✏️ Edit Announcement' : '✨ New Announcement' ?></span>
|
||||
</h2>
|
||||
<a href="index.php" class="btn btn-outline btn-sm">← Back to List</a>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-group full-width">
|
||||
<label class="checkbox-label checkbox-label-lg">
|
||||
<input type="checkbox" name="is_active" value="1" <?= (!empty($editItem['is_active']) || $isCreatingNew) ? 'checked' : '' ?>>
|
||||
<strong>Enable Announcement on Site</strong>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="headline">Notice Headline</label>
|
||||
<input type="text" id="headline" name="headline" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['headline'] ?? '') ?>"
|
||||
placeholder="e.g. Labor Day Closure" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type">Style / Category</label>
|
||||
<select id="type" name="type" class="form-control">
|
||||
<option value="holiday" <?= ($editItem['type'] ?? '') === 'holiday' ? 'selected' : '' ?>>Holiday / Special Schedule (Warm Brass & Green)</option>
|
||||
<option value="special" <?= ($editItem['type'] ?? '') === 'special' ? 'selected' : '' ?>>Event / Live Music Announcement</option>
|
||||
<option value="closure" <?= ($editItem['type'] ?? '') === 'closure' ? 'selected' : '' ?>>Urgent Closure / Weather Alert</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="message">Notice Message</label>
|
||||
<textarea id="message" name="message" class="form-control" required
|
||||
placeholder="Write details of closure, dates, and when regular hours resume..."><?= htmlspecialchars($editItem['message'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="start_date">Start Date (Optional Auto-Schedule)</label>
|
||||
<input type="date" id="start_date" name="start_date" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['start_date'] ?? '') ?>">
|
||||
<span class="muted font-mono" style="font-size: 0.78rem;">Leave blank to show immediately while enabled.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="end_date">End Date (Optional Expiration)</label>
|
||||
<input type="date" id="end_date" name="end_date" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['end_date'] ?? '') ?>">
|
||||
<span class="muted font-mono" style="font-size: 0.78rem;">Auto-hides after 11:59 PM on this date.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label>Display Placements</label>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="show_banner" value="1" <?= (!isset($editItem) || !empty($editItem['show_banner'])) ? 'checked' : '' ?>>
|
||||
Show as top alert banner across the site
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="show_in_hours" value="1" <?= (!isset($editItem) || !empty($editItem['show_in_hours'])) ? 'checked' : '' ?>>
|
||||
Highlight inside the "Hours & Visit" card
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Save Announcement</button>
|
||||
<a href="index.php" class="btn btn-outline">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<!-- Announcements List View -->
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span>📢 All Announcements (<?= count($announcements) ?>)</span>
|
||||
</h2>
|
||||
<a href="index.php?new=1" class="btn btn-primary btn-sm">+ Create Announcement</a>
|
||||
</div>
|
||||
|
||||
<?php if (empty($announcements)): ?>
|
||||
<div class="empty-state">
|
||||
<p>No announcements created yet.</p>
|
||||
<a href="index.php?new=1" class="btn btn-primary btn-sm">+ Create Your First Announcement</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="admin-table-wrap">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Headline</th>
|
||||
<th>Type</th>
|
||||
<th>Schedule</th>
|
||||
<th>Placements</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($announcements as $ann): ?>
|
||||
<?php
|
||||
$isCurrentlyActive = $ann['is_active'] &&
|
||||
(empty($ann['start_date']) || $ann['start_date'] <= $today) &&
|
||||
(empty($ann['end_date']) || $ann['end_date'] >= $today);
|
||||
?>
|
||||
<tr>
|
||||
<td data-label="Status">
|
||||
<?php if ($isCurrentlyActive): ?>
|
||||
<span class="status-badge active">● Live</span>
|
||||
<?php elseif ($ann['is_active']): ?>
|
||||
<span class="status-badge inactive">⏳ Scheduled</span>
|
||||
<?php else: ?>
|
||||
<span class="status-badge inactive">○ Off</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td data-label="Headline">
|
||||
<strong><?= htmlspecialchars($ann['headline']) ?></strong>
|
||||
<div class="muted" style="font-size: 0.82rem; margin-top: 0.2rem; max-width: 32ch; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<?= htmlspecialchars($ann['message']) ?>
|
||||
</div>
|
||||
</td>
|
||||
<td data-label="Type">
|
||||
<span class="eyebrow"><?= htmlspecialchars($ann['type']) ?></span>
|
||||
</td>
|
||||
<td class="font-mono" data-label="Schedule" style="font-size: 0.8rem; color: var(--ink-muted);">
|
||||
<?php if (!empty($ann['start_date']) || !empty($ann['end_date'])): ?>
|
||||
<?= htmlspecialchars($ann['start_date'] ?: 'Now') ?> → <?= htmlspecialchars($ann['end_date'] ?: 'Ongoing') ?>
|
||||
<?php else: ?>
|
||||
Always (while on)
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td data-label="Placements" style="font-size: 0.82rem; color: var(--ink-muted);">
|
||||
<?php
|
||||
$locs = [];
|
||||
if (!empty($ann['show_banner'])) $locs[] = 'Top Banner';
|
||||
if (!empty($ann['show_in_hours'])) $locs[] = 'Hours Card';
|
||||
echo implode(', ', $locs) ?: 'None';
|
||||
?>
|
||||
</td>
|
||||
<td data-label="Actions">
|
||||
<div class="admin-table-actions">
|
||||
<a href="index.php?edit=<?= (int)$ann['id'] ?>" class="btn btn-outline btn-sm">Edit</a>
|
||||
|
||||
<form method="POST" action="index.php" style="display: inline;">
|
||||
<input type="hidden" name="action" value="toggle_active">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)$ann['id'] ?>">
|
||||
<button type="submit" class="btn btn-outline btn-sm">
|
||||
<?= $ann['is_active'] ? 'Turn Off' : 'Turn On' ?>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="index.php" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this announcement?');">
|
||||
<input type="hidden" name="action" value="delete_announcement">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)$ann['id'] ?>">
|
||||
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- Announcements List View — always rendered; the editor below opens
|
||||
over it as a modal instead of replacing it, matching menu.php. -->
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span>📢 All Announcements (<?= count($announcements) ?>)</span>
|
||||
</h2>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($announcements)): ?>
|
||||
<div class="empty-state">
|
||||
<p>No announcements created yet.</p>
|
||||
<a href="index.php?new=1" class="btn btn-primary btn-sm">+ Create Your First Announcement</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="ann-grid-wrap">
|
||||
<div class="ann-grid-head" aria-hidden="true">
|
||||
<span class="col-status">Status</span>
|
||||
<span class="col-headline">Headline</span>
|
||||
<span class="col-type">Type</span>
|
||||
<span class="col-schedule">Schedule</span>
|
||||
<span class="col-placements">Placements</span>
|
||||
<span class="col-actions">Actions</span>
|
||||
</div>
|
||||
<div class="ann-rows">
|
||||
<?php foreach ($announcements as $ann): ?>
|
||||
<?php $isCurrentlyActive = announcement_is_live($ann, $today); ?>
|
||||
<div class="ann-row">
|
||||
<div data-label="Status">
|
||||
<form method="POST" action="index.php" class="status-toggle-form">
|
||||
<input type="hidden" name="action" value="toggle_active">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)$ann['id'] ?>">
|
||||
<?php if ($isCurrentlyActive): ?>
|
||||
<button type="submit" class="status-badge active" title="Click to turn off">● Live</button>
|
||||
<?php elseif ($ann['is_active']): ?>
|
||||
<button type="submit" class="status-badge inactive" title="Click to turn off">⏳ Scheduled</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" class="status-badge inactive" title="Click to turn on">○ Off</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
<div data-label="Headline">
|
||||
<strong><?= htmlspecialchars($ann['headline']) ?></strong>
|
||||
<div class="muted" style="font-size: 0.82rem; margin-top: 0.2rem; max-width: 32ch; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<?= htmlspecialchars($ann['message']) ?>
|
||||
</div>
|
||||
</div>
|
||||
<div data-label="Type">
|
||||
<span class="eyebrow"><?= htmlspecialchars($ann['type']) ?></span>
|
||||
</div>
|
||||
<div class="font-mono" data-label="Schedule" style="font-size: 0.8rem; color: var(--ink-muted);">
|
||||
<?php if (!empty($ann['start_date']) || !empty($ann['end_date'])): ?>
|
||||
<?= htmlspecialchars($ann['start_date'] ?: 'Now') ?> → <?= htmlspecialchars($ann['end_date'] ?: 'Ongoing') ?>
|
||||
<?php else: ?>
|
||||
Always (while on)
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div data-label="Placements" style="font-size: 0.82rem; color: var(--ink-muted);">
|
||||
<?php
|
||||
$locs = [];
|
||||
if (!empty($ann['show_banner'])) $locs[] = 'Top Banner';
|
||||
if (!empty($ann['show_in_hours'])) $locs[] = 'Hours Card';
|
||||
echo implode(', ', $locs) ?: 'None';
|
||||
?>
|
||||
</div>
|
||||
<div data-label="Actions">
|
||||
<div class="row-actions">
|
||||
<a href="index.php?edit=<?= (int)$ann['id'] ?>" class="btn btn-outline btn-sm">Edit</a>
|
||||
|
||||
<form method="POST" action="index.php" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this announcement?');">
|
||||
<input type="hidden" name="action" value="delete_announcement">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)$ann['id'] ?>">
|
||||
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Modal Backdrop: Announcement Editor (Create / Edit) — server-rendered
|
||||
open/closed via the is-open class, since this page has no JS file;
|
||||
the underlying save is still a plain form POST + reload, unchanged. -->
|
||||
<div class="admin-modal-backdrop<?= ($isCreatingNew || $editItem) ? ' is-open' : '' ?>" role="dialog" aria-modal="true" aria-labelledby="modal-ann-title">
|
||||
<div class="admin-modal">
|
||||
<div class="modal-header">
|
||||
<h2 id="modal-ann-title"><?= $editItem ? '✏️ Edit Announcement' : '✨ New Announcement' ?></h2>
|
||||
<a href="index.php" class="modal-close" aria-label="Close dialog">×</a>
|
||||
</div>
|
||||
<form method="POST" action="index.php">
|
||||
<input type="hidden" name="action" value="save_announcement">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
|
||||
<input type="hidden" name="announcement_id" value="<?= (int)($editItem['id'] ?? 0) ?>">
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-group full-width">
|
||||
<label class="checkbox-label checkbox-label-lg">
|
||||
<input type="checkbox" name="is_active" value="1" <?= (!empty($editItem['is_active']) || $isCreatingNew) ? 'checked' : '' ?>>
|
||||
<strong>Enable Announcement on Site</strong>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="headline">Notice Headline</label>
|
||||
<input type="text" id="headline" name="headline" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['headline'] ?? '') ?>"
|
||||
placeholder="e.g. Labor Day Closure" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type">Style / Category</label>
|
||||
<select id="type" name="type" class="form-control">
|
||||
<option value="holiday" <?= ($editItem['type'] ?? '') === 'holiday' ? 'selected' : '' ?>>Holiday / Special Schedule (Warm Brass & Green)</option>
|
||||
<option value="special" <?= ($editItem['type'] ?? '') === 'special' ? 'selected' : '' ?>>Event / Live Music Announcement</option>
|
||||
<option value="closure" <?= ($editItem['type'] ?? '') === 'closure' ? 'selected' : '' ?>>Urgent Closure / Weather Alert</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="message">Notice Message</label>
|
||||
<textarea id="message" name="message" class="form-control" required
|
||||
placeholder="Write details of closure, dates, and when regular hours resume..."><?= htmlspecialchars($editItem['message'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="start_date">Start Date (Optional Auto-Schedule)</label>
|
||||
<input type="date" id="start_date" name="start_date" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['start_date'] ?? '') ?>">
|
||||
<span class="muted font-mono" style="font-size: 0.78rem;">Leave blank to show immediately while enabled.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="end_date">End Date (Optional Expiration)</label>
|
||||
<input type="date" id="end_date" name="end_date" class="form-control"
|
||||
value="<?= htmlspecialchars($editItem['end_date'] ?? '') ?>">
|
||||
<span class="muted font-mono" style="font-size: 0.78rem;">Auto-hides after 11:59 PM on this date.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label>Display Placements</label>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="show_banner" value="1" <?= (!isset($editItem) || !empty($editItem['show_banner'])) ? 'checked' : '' ?>>
|
||||
Show as top alert banner across the site
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="show_in_hours" value="1" <?= (!isset($editItem) || !empty($editItem['show_in_hours'])) ? 'checked' : '' ?>>
|
||||
Highlight inside the "Hours & Visit" card
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Save Announcement</button>
|
||||
<a href="index.php" class="btn btn-outline">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="index.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+2
-2
@@ -352,7 +352,7 @@ function itemRowHtml(item, isAvail) {
|
||||
<div data-label="Status"><button type="button" class="status-badge btn-toggle-item ${isAvail ? 'active' : 'inactive'}" data-id="${item.id}" title="Click to mark as ${isAvail ? 'sold out' : 'available'}">${isAvail ? '● Available' : '○ Sold Out'}</button></div>
|
||||
<div data-label="Item"><span class="item-name-row"><strong class="item-name-cell">${escapeHtml(item.name)}</strong>${badgeHtml}</span><div class="muted item-desc-cell">${escapeHtml(item.description || '')}</div></div>
|
||||
<div class="font-mono item-price-cell" data-label="Price">${escapeHtml(item.price)}</div>
|
||||
<div data-label="Actions"><div class="admin-table-actions">
|
||||
<div data-label="Actions"><div class="row-actions">
|
||||
<div class="action-stack">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-item" data-direction="up" aria-label="Move ${escapeHtml(item.name)} up">↑</button>
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-item" data-direction="down" aria-label="Move ${escapeHtml(item.name)} down">↓</button>
|
||||
@@ -432,7 +432,7 @@ function categoryPanelHtml(category) {
|
||||
<span>${escapeHtml(category.name)}</span>
|
||||
</h2>
|
||||
<p class="muted cat-desc-text">${escapeHtml(category.description || '')}</p>
|
||||
<div class="admin-table-actions">
|
||||
<div class="row-actions">
|
||||
<div class="action-stack">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-cat" data-direction="up" data-id="${category.id}" aria-label="Move category up">↑</button>
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-cat" data-direction="down" data-id="${category.id}" aria-label="Move category down">↓</button>
|
||||
|
||||
+3
-2
@@ -193,6 +193,7 @@ $menuGrouped = get_full_menu(false);
|
||||
<a href="index.php">Announcements</a>
|
||||
<a href="menu.php" class="active">Menu</a>
|
||||
<a href="users.php">Users</a>
|
||||
<a href="hours.php">Hours</a>
|
||||
</div>
|
||||
<div class="admin-user">
|
||||
<span>Logged in as <strong><?= htmlspecialchars($user['username']) ?></strong></span>
|
||||
@@ -231,7 +232,7 @@ $menuGrouped = get_full_menu(false);
|
||||
<span><?= htmlspecialchars($category['name']) ?></span>
|
||||
</h2>
|
||||
<p class="muted cat-desc-text"><?= htmlspecialchars($category['description'] ?? '') ?></p>
|
||||
<div class="admin-table-actions">
|
||||
<div class="row-actions">
|
||||
<div class="action-stack">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-cat" data-direction="up" data-id="<?= (int) $category['id'] ?>" aria-label="Move <?= htmlspecialchars($category['name']) ?> category up" <?= $catIndex === 0 ? 'disabled' : '' ?>>↑</button>
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-cat" data-direction="down" data-id="<?= (int) $category['id'] ?>" aria-label="Move <?= htmlspecialchars($category['name']) ?> category down" <?= $catIndex === $catCount - 1 ? 'disabled' : '' ?>>↓</button>
|
||||
@@ -284,7 +285,7 @@ $menuGrouped = get_full_menu(false);
|
||||
<?= htmlspecialchars($item['price']) ?>
|
||||
</div>
|
||||
<div data-label="Actions">
|
||||
<div class="admin-table-actions">
|
||||
<div class="row-actions">
|
||||
<div class="action-stack">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-item" data-direction="up" aria-label="Move <?= htmlspecialchars($item['name']) ?> up" <?= $itemIndex === 0 ? 'disabled' : '' ?>>↑</button>
|
||||
<button type="button" class="btn btn-outline btn-sm btn-icon btn-move-item" data-direction="down" aria-label="Move <?= htmlspecialchars($item['name']) ?> down" <?= $itemIndex === $itemCount - 1 ? 'disabled' : '' ?>>↓</button>
|
||||
|
||||
+16
-15
@@ -152,8 +152,8 @@ document.getElementById('form-password').addEventListener('submit', async (e) =>
|
||||
}
|
||||
});
|
||||
|
||||
// Delegate table actions: Change Password, Delete
|
||||
document.getElementById('users-tbody').addEventListener('click', async (e) => {
|
||||
// Delegate row actions: Change Password, Delete
|
||||
document.getElementById('users-rows').addEventListener('click', async (e) => {
|
||||
const { target } = e;
|
||||
|
||||
if (target.classList.contains('btn-change-password')) {
|
||||
@@ -163,7 +163,7 @@ document.getElementById('users-tbody').addEventListener('click', async (e) => {
|
||||
|
||||
if (target.classList.contains('btn-delete-user')) {
|
||||
if (!confirm('Are you sure you want to delete this user? This cannot be undone.')) return;
|
||||
const row = target.closest('tr');
|
||||
const row = target.closest('.user-row');
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('action', 'delete_user');
|
||||
@@ -187,25 +187,26 @@ document.getElementById('users-tbody').addEventListener('click', async (e) => {
|
||||
});
|
||||
|
||||
function insertUserRow(user) {
|
||||
const tbody = document.getElementById('users-tbody');
|
||||
const rowsContainer = document.getElementById('users-rows');
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
tr.id = `user-row-${user.id}`;
|
||||
tr.dataset.userId = user.id;
|
||||
const row = document.createElement('div');
|
||||
row.className = 'user-row';
|
||||
row.id = `user-row-${user.id}`;
|
||||
row.dataset.userId = user.id;
|
||||
|
||||
// data-username is set via dataset below, never interpolated into the
|
||||
// HTML string — escapeHtml() only escapes for text-content positions,
|
||||
// not attribute values, so it can't safely build an attribute string.
|
||||
tr.innerHTML = `
|
||||
<td data-label="Username"><strong>${escapeHtml(user.username)}</strong></td>
|
||||
<td data-label="Role">${escapeHtml(user.role.charAt(0).toUpperCase() + user.role.slice(1))}</td>
|
||||
<td class="font-mono item-sort-cell" data-label="Created">${escapeHtml(user.created_at)}</td>
|
||||
<td data-label="Actions"><div class="admin-table-actions">
|
||||
row.innerHTML = `
|
||||
<div data-label="Username"><strong>${escapeHtml(user.username)}</strong></div>
|
||||
<div data-label="Role">${escapeHtml(user.role.charAt(0).toUpperCase() + user.role.slice(1))}</div>
|
||||
<div class="font-mono timestamp-cell" data-label="Created">${escapeHtml(user.created_at)}</div>
|
||||
<div data-label="Actions"><div class="row-actions">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-change-password" data-id="${user.id}">Change Password</button>
|
||||
<button type="button" class="btn btn-danger btn-sm btn-delete-user" data-id="${user.id}">Delete</button>
|
||||
</div></td>
|
||||
</div></div>
|
||||
`;
|
||||
tr.querySelector('.btn-change-password').dataset.username = user.username;
|
||||
row.querySelector('.btn-change-password').dataset.username = user.username;
|
||||
|
||||
tbody.appendChild(tr);
|
||||
rowsContainer.appendChild(row);
|
||||
}
|
||||
+41
-38
@@ -161,6 +161,7 @@ $users = get_all_users();
|
||||
<a href="index.php">Announcements</a>
|
||||
<a href="menu.php">Menu</a>
|
||||
<a href="users.php" class="active">Users</a>
|
||||
<a href="hours.php">Hours</a>
|
||||
</div>
|
||||
<div class="admin-user">
|
||||
<span>Logged in as <strong><?= htmlspecialchars($user['username']) ?></strong></span>
|
||||
@@ -182,45 +183,47 @@ $users = get_all_users();
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="admin-table-wrap">
|
||||
<table class="admin-table" id="users-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Role</th>
|
||||
<th>Created</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="users-tbody">
|
||||
<?php foreach ($users as $row): ?>
|
||||
<tr id="user-row-<?= (int) $row['id'] ?>" data-user-id="<?= (int) $row['id'] ?>">
|
||||
<td data-label="Username">
|
||||
<strong><?= htmlspecialchars($row['username']) ?></strong>
|
||||
<?php if ((int) $row['id'] === (int) $user['id']): ?>
|
||||
<span class="muted"> (you)</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td data-label="Role"><?= htmlspecialchars(ucfirst($row['role'])) ?></td>
|
||||
<td class="font-mono item-sort-cell" data-label="Created"><?= htmlspecialchars($row['created_at']) ?></td>
|
||||
<td data-label="Actions">
|
||||
<div class="admin-table-actions">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-change-password"
|
||||
data-id="<?= (int) $row['id'] ?>"
|
||||
data-username="<?= htmlspecialchars($row['username']) ?>">
|
||||
Change Password
|
||||
<div class="panel-head">
|
||||
<h2>
|
||||
<span>👤 All Users (<?= count($users) ?>)</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="user-grid-wrap">
|
||||
<div class="user-grid-head" aria-hidden="true">
|
||||
<span class="col-username">Username</span>
|
||||
<span class="col-role">Role</span>
|
||||
<span class="col-created">Created</span>
|
||||
<span class="col-actions">Actions</span>
|
||||
</div>
|
||||
<div class="user-rows" id="users-rows">
|
||||
<?php foreach ($users as $row): ?>
|
||||
<div class="user-row" id="user-row-<?= (int) $row['id'] ?>" data-user-id="<?= (int) $row['id'] ?>">
|
||||
<div data-label="Username">
|
||||
<strong><?= htmlspecialchars($row['username']) ?></strong>
|
||||
<?php if ((int) $row['id'] === (int) $user['id']): ?>
|
||||
<span class="muted"> (you)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div data-label="Role"><?= htmlspecialchars(ucfirst($row['role'])) ?></div>
|
||||
<div class="font-mono timestamp-cell" data-label="Created"><?= htmlspecialchars($row['created_at']) ?></div>
|
||||
<div data-label="Actions">
|
||||
<div class="row-actions">
|
||||
<button type="button" class="btn btn-outline btn-sm btn-change-password"
|
||||
data-id="<?= (int) $row['id'] ?>"
|
||||
data-username="<?= htmlspecialchars($row['username']) ?>">
|
||||
Change Password
|
||||
</button>
|
||||
<?php if ((int) $row['id'] !== (int) $user['id']): ?>
|
||||
<button type="button" class="btn btn-danger btn-sm btn-delete-user" data-id="<?= (int) $row['id'] ?>">
|
||||
Delete
|
||||
</button>
|
||||
<?php if ((int) $row['id'] !== (int) $user['id']): ?>
|
||||
<button type="button" class="btn btn-danger btn-sm btn-delete-user" data-id="<?= (int) $row['id'] ?>">
|
||||
Delete
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user