Add menu enable toggle, user status, and mobile/UI polish

- Admin menu page: mobile item cards now put Actions in the top-right
  corner next to Status, matching the desktop row layout instead of
  stacking at the bottom; same treatment applied to announcements and
  users for consistency across all three admin lists
- Center button label text everywhere via .btn { justify-content: center }
  instead of one-off overrides
- Add a Disable/Enable Menu toggle to the admin menu toolbar; the public
  menu page shows its existing "menu being updated" message while disabled
- Add an Enabled/Disabled status to user accounts, with its own toggle
  badge, guardrails against disabling your own account or the last
  enabled account, and an immediate session kick for anyone disabled
  mid-session
- Fix the announcement banner / hero tagline "·" separators to hide only
  when their neighboring text actually wraps onto a different line,
  instead of a fixed breakpoint or always-on dot

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
David DyerandClaude Sonnet 5 committed 2026-09-01 14:10:08 -04:00
1 parent eab20899de
commit cd4c6c1969
13 files changed
+405 -50

No files matched your search

+14 -1
View File
@@ -25,6 +25,19 @@ function require_auth(): void {
header('Location: login.php');
exit;
}
// A user disabled mid-session should lose access immediately rather
// than keep it until their session expires — re-check against the DB
// instead of trusting the session's now-possibly-stale copy.
$pdo = get_db();
$stmt = $pdo->prepare("SELECT is_enabled FROM users WHERE id = ?");
$stmt->execute([$_SESSION['admin_user_id']]);
$stillEnabled = $stmt->fetchColumn();
if ($stillEnabled === false || (int) $stillEnabled !== 1) {
logout_user();
header('Location: login.php');
exit;
}
}
function get_logged_in_user(): ?array {
@@ -44,7 +57,7 @@ function authenticate_user(string $username, string $password): bool {
$stmt->execute([trim($username)]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
if ($user && (int) $user['is_enabled'] === 1 && password_verify($password, $user['password_hash'])) {
session_regenerate_id(true);
$_SESSION['admin_user_id'] = $user['id'];
$_SESSION['admin_username'] = $user['username'];
+31
View File
@@ -42,6 +42,7 @@ function init_db_schema(PDO $pdo): void {
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'admin',
is_enabled INTEGER NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
@@ -86,8 +87,31 @@ function init_db_schema(PDO $pdo): void {
kitchen_close_time TEXT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Single-row table (id is pinned to 1) for site-wide toggles that
-- don't belong to any one record — starting with whether the
-- public menu page shows its items at all.
CREATE TABLE IF NOT EXISTS site_settings (
id INTEGER PRIMARY KEY CHECK (id = 1),
menu_enabled INTEGER NOT NULL DEFAULT 1,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
");
// users.is_enabled shipped after the initial release — CREATE TABLE IF
// NOT EXISTS above is a no-op against an existing table, so an install
// from before this needs the column added explicitly, once.
$hasIsEnabled = false;
foreach ($pdo->query("PRAGMA table_info(users)") as $col) {
if ($col['name'] === 'is_enabled') {
$hasIsEnabled = true;
break;
}
}
if (!$hasIsEnabled) {
$pdo->exec("ALTER TABLE users ADD COLUMN is_enabled INTEGER NOT NULL DEFAULT 1");
}
// Seed default admin user if table is empty (username: admin / password: password)
$stmt = $pdo->query("SELECT COUNT(*) as count FROM users");
$userCount = $stmt->fetchColumn();
@@ -125,4 +149,11 @@ function init_db_schema(PDO $pdo): void {
}
}
}
// Seed the singleton settings row (menu on by default) if it doesn't exist yet.
$stmt = $pdo->query("SELECT COUNT(*) as count FROM site_settings");
$settingsCount = $stmt->fetchColumn();
if ($settingsCount == 0) {
$pdo->exec("INSERT INTO site_settings (id, menu_enabled) VALUES (1, 1)");
}
}
+16
View File
@@ -7,6 +7,22 @@ declare(strict_types=1);
require_once __DIR__ . '/db.php';
// Whether the public menu page should show its items at all — separate
// from any individual item's is_available, this is the admin-facing "take
// the whole menu down for maintenance" switch. Defaults to enabled if the
// singleton settings row is somehow missing.
function is_menu_enabled(): bool {
$pdo = get_db();
$value = $pdo->query("SELECT menu_enabled FROM site_settings WHERE id = 1")->fetchColumn();
return $value === false ? true : (bool) $value;
}
function set_menu_enabled(bool $enabled): void {
$pdo = get_db();
$stmt = $pdo->prepare("UPDATE site_settings SET menu_enabled = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1");
$stmt->execute([$enabled ? 1 : 0]);
}
function get_full_menu(bool $availableOnly = false): array {
$pdo = get_db();
+13 -2
View File
@@ -13,13 +13,13 @@ require_once __DIR__ . '/db.php';
function get_all_users(): array {
$pdo = get_db();
$stmt = $pdo->query("SELECT id, username, role, created_at FROM users ORDER BY username ASC");
$stmt = $pdo->query("SELECT id, username, role, is_enabled, created_at FROM users ORDER BY username ASC");
return $stmt->fetchAll() ?: [];
}
function get_user_by_id(int $id): ?array {
$pdo = get_db();
$stmt = $pdo->prepare("SELECT id, username, role, created_at FROM users WHERE id = ?");
$stmt = $pdo->prepare("SELECT id, username, role, is_enabled, created_at FROM users WHERE id = ?");
$stmt->execute([$id]);
$res = $stmt->fetch();
return $res ?: null;
@@ -30,6 +30,17 @@ function count_users(): int {
return (int) $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
}
function count_enabled_users(): int {
$pdo = get_db();
return (int) $pdo->query("SELECT COUNT(*) FROM users WHERE is_enabled = 1")->fetchColumn();
}
function toggle_user_enabled(int $id): bool {
$pdo = get_db();
$stmt = $pdo->prepare("UPDATE users SET is_enabled = CASE WHEN is_enabled = 1 THEN 0 ELSE 1 END WHERE id = ?");
return $stmt->execute([$id]);
}
function username_exists(string $username, ?int $excludeId = null): bool {
$pdo = get_db();
if ($excludeId !== null) {