- 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>
160 lines
5.7 KiB
PHP
160 lines
5.7 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* Database connection & schema management for SQLite
|
||
*/
|
||
|
||
function get_db(): PDO {
|
||
static $pdo = null;
|
||
if ($pdo !== null) {
|
||
return $pdo;
|
||
}
|
||
|
||
$dbDir = __DIR__ . '/../db';
|
||
if (!is_dir($dbDir)) {
|
||
mkdir($dbDir, 0755, true);
|
||
}
|
||
|
||
$dbPath = $dbDir . '/fitz.sqlite';
|
||
$isNew = !file_exists($dbPath);
|
||
|
||
$pdo = new PDO('sqlite:' . $dbPath, null, null, [
|
||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||
PDO::ATTR_TIMEOUT => 5
|
||
]);
|
||
|
||
// Enable WAL mode for concurrency and performance
|
||
$pdo->exec('PRAGMA journal_mode = WAL;');
|
||
$pdo->exec('PRAGMA foreign_keys = ON;');
|
||
|
||
// Initialize tables if needed
|
||
init_db_schema($pdo);
|
||
|
||
return $pdo;
|
||
}
|
||
|
||
function init_db_schema(PDO $pdo): void {
|
||
$pdo->exec("
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
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
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS announcements (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
is_active INTEGER NOT NULL DEFAULT 1,
|
||
type TEXT NOT NULL DEFAULT 'holiday',
|
||
headline TEXT NOT NULL,
|
||
message TEXT NOT NULL,
|
||
start_date TEXT NULL,
|
||
end_date TEXT NULL,
|
||
show_banner INTEGER NOT NULL DEFAULT 1,
|
||
show_in_hours INTEGER NOT NULL DEFAULT 1,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS menu_categories (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
name TEXT NOT NULL,
|
||
description TEXT NULL,
|
||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS menu_items (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
category_id INTEGER NOT NULL REFERENCES menu_categories(id) ON DELETE CASCADE,
|
||
name TEXT NOT NULL,
|
||
description TEXT NULL,
|
||
price TEXT NOT NULL,
|
||
badge TEXT NULL,
|
||
is_available INTEGER NOT NULL DEFAULT 1,
|
||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS pub_hours (
|
||
day_of_week INTEGER PRIMARY KEY,
|
||
is_open INTEGER NOT NULL DEFAULT 0,
|
||
open_time TEXT NULL,
|
||
close_time TEXT NULL,
|
||
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();
|
||
if ($userCount == 0) {
|
||
$defaultPassHash = password_hash('password', PASSWORD_DEFAULT);
|
||
$insert = $pdo->prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, 'admin')");
|
||
$insert->execute(['admin', $defaultPassHash]);
|
||
}
|
||
|
||
// Seed default weekly hours if the table is empty: closed Sun–Wed,
|
||
// 4–10pm Thu–Sat with the kitchen closing at 9 — matches the hours the
|
||
// site originally shipped with as hardcoded copy.
|
||
$stmt = $pdo->query("SELECT COUNT(*) as count FROM pub_hours");
|
||
$hoursCount = $stmt->fetchColumn();
|
||
if ($hoursCount == 0) {
|
||
$insert = $pdo->prepare("
|
||
INSERT INTO pub_hours (day_of_week, is_open, open_time, close_time, kitchen_close_time)
|
||
VALUES (?, ?, ?, ?, ?)
|
||
");
|
||
// 0=Sunday ... 6=Saturday
|
||
$defaults = [
|
||
0 => null, // Sunday — closed
|
||
1 => null, // Monday — closed
|
||
2 => null, // Tuesday — closed
|
||
3 => null, // Wednesday — closed
|
||
4 => ['16:00', '22:00', '21:00'], // Thursday
|
||
5 => ['16:00', '22:00', '21:00'], // Friday
|
||
6 => ['16:00', '22:00', '21:00'], // Saturday
|
||
];
|
||
foreach ($defaults as $day => $times) {
|
||
if ($times === null) {
|
||
$insert->execute([$day, 0, null, null, null]);
|
||
} else {
|
||
$insert->execute([$day, 1, $times[0], $times[1], $times[2]]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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)");
|
||
}
|
||
}
|