Add online menu management, modernized and hardened
Adds a public menu page (menu.php) and an admin CRUD interface (admin/menu.php + admin/menu.js) backed by new menu_categories and menu_items tables, replacing the "menu lives on Facebook" placeholder on the homepage. The admin JS/PHP came out of an AI-assisted pass that used outdated JS (var, .then() chains, string-built HTML) and had some rough edges security-wise, so this also: - Rewrites the admin JS to modern ES (const/let, async/await, template literals, dataset API) and splits it out of admin/menu.php into admin/menu.js for proper separation of concerns - Adds declare(strict_types=1) and PHP 8 match-based action dispatch in place of an if-chain - Validates category_id/item_id/category_id server-side before use, so a bad ID returns a clean JSON error instead of an uncaught PDOException or a null item breaking the client - Rejects non-string POST fields instead of crashing trim() on them - Replaces the category-save handler's full-page location.reload() with in-place DOM updates (panel insert/update, resort by sort_order, keep the item modal's category dropdown in sync), so every admin action is now AJAX with no reload - Sets data-name/data-desc via dataset assignment rather than string- interpolating them into an HTML attribute, closing a stored-XSS path through the category name/description fields Removes seed-menu.php, a leftover stub with no working code left in it after the AI's one-time seeding run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
21ad6c31cf
commit
f1bbf59548
8 files changed
+1382
-27
No files matched your search
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/config.php';
|
||||
require __DIR__ . '/inc/announcements.php';
|
||||
require __DIR__ . '/inc/menu.php';
|
||||
|
||||
$activeAnnouncements = get_active_announcements();
|
||||
$menuCategories = get_full_menu(true); // available items only
|
||||
$siteUrl = rtrim(SITE_URL, '/');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Pub Menu | The Old Fitz Public House</title>
|
||||
<meta name="description" content="Explore the authentic food and drink menu at The Old Fitz Public House in Marion, Indiana. Traditional fare, shareable plates, and proper pub classics.">
|
||||
<link rel="canonical" href="<?= $siteUrl ?>/menu.php">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
|
||||
<!-- Social sharing -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="The Old Fitz Public House">
|
||||
<meta property="og:title" content="Pub Menu | The Old Fitz Public House">
|
||||
<meta property="og:description" content="Authentic Irish comfort food, fare to share, and traditional pub staples at The Old Fitz in Marion, Indiana.">
|
||||
<meta property="og:url" content="<?= $siteUrl ?>/menu.php">
|
||||
<meta property="og:image" content="<?= $siteUrl ?>/assets/og-image.png">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Pub Menu | The Old Fitz Public House">
|
||||
<meta name="twitter:description" content="Authentic Irish comfort food, fare to share, and traditional pub staples at The Old Fitz in Marion, Indiana.">
|
||||
<meta name="twitter:image" content="<?= $siteUrl ?>/assets/og-image.png">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;0,9..144,700;1,9..144,400;1,9..144,500&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
|
||||
<header class="site">
|
||||
<div class="container nav-inner">
|
||||
<a class="brand" href="./">
|
||||
<img class="knot" src="./assets/knot.png" alt="">
|
||||
<span>THE OLD FITZ</span>
|
||||
</a>
|
||||
<nav class="links" aria-label="Primary">
|
||||
<a href="./#about">About</a>
|
||||
<a href="./#visit">Hours & Visit</a>
|
||||
<a href="./#music">Live Music</a>
|
||||
<a href="menu.php" style="color: var(--gold-bright);">Menu</a>
|
||||
<a href="./#contact">Contact</a>
|
||||
</nav>
|
||||
<a class="nav-cta" href="https://www.google.com/maps/search/?api=1&query=100+N+Washington+St+Marion+IN" target="_blank" rel="noopener">Get Directions</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<?php
|
||||
$bannerAnnouncements = array_filter($activeAnnouncements, function($a) {
|
||||
return !empty($a['show_banner']);
|
||||
});
|
||||
?>
|
||||
<?php if (!empty($bannerAnnouncements)): ?>
|
||||
<?php foreach ($bannerAnnouncements as $banner): ?>
|
||||
<div class="announcement-banner banner-<?= htmlspecialchars($banner['type']) ?>" role="region" aria-label="Important Announcement">
|
||||
<div class="container announcement-inner">
|
||||
<span class="announcement-tag"><?= htmlspecialchars($banner['headline']) ?></span>
|
||||
<span class="announcement-sep" aria-hidden="true">·</span>
|
||||
<span class="announcement-text"><?= htmlspecialchars($banner['message']) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<main id="main">
|
||||
|
||||
<section class="menu-page-header">
|
||||
<div class="container" style="text-align: center;">
|
||||
<p class="eyebrow">The Old Fitz Public House</p>
|
||||
<h1 style="font-size: clamp(2.4rem, 5vw, 3.8rem); color: var(--gold-bright); margin-top: 0.5rem;">Pub Menu</h1>
|
||||
<div class="rule-row" style="margin-top: 1.5rem; margin-bottom: 1.5rem;">
|
||||
<span class="line"></span>
|
||||
<img class="knot" src="./assets/knot.png" alt="">
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<p class="muted" style="max-width: 52ch; margin: 0 auto; font-size: 1.05rem;">
|
||||
Traditional Irish staples, shareable plates, and comforting pub classics cooked fresh. Kitchen serves until 9:00 PM.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="menu-page-content" style="padding-top: 0;">
|
||||
<div class="container" style="max-width: 62rem;">
|
||||
<?php if (!empty($menuCategories)): ?>
|
||||
<div class="menu-grid" style="margin-top: 0;">
|
||||
<?php foreach ($menuCategories as $category): ?>
|
||||
<?php if (!empty($category['items'])): ?>
|
||||
<div class="menu-category">
|
||||
<div class="menu-category-header">
|
||||
<h3><?= htmlspecialchars($category['name']) ?></h3>
|
||||
<?php if (!empty($category['description'])): ?>
|
||||
<p><?= htmlspecialchars($category['description']) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="menu-items-list">
|
||||
<?php foreach ($category['items'] as $item): ?>
|
||||
<div class="menu-item-row">
|
||||
<div class="menu-item-top">
|
||||
<span class="menu-item-name">
|
||||
<?= htmlspecialchars($item['name']) ?>
|
||||
<?php if (!empty($item['badge'])): ?>
|
||||
<span class="menu-item-badge"><?= htmlspecialchars($item['badge']) ?></span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<span class="menu-item-price"><?= htmlspecialchars($item['price']) ?></span>
|
||||
</div>
|
||||
<?php if (!empty($item['description'])): ?>
|
||||
<p class="menu-item-desc"><?= htmlspecialchars($item['description']) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="panel" style="text-align: center; padding: 3rem 1.5rem;">
|
||||
<p class="muted">Our kitchen menu is currently being refreshed. Please check our Facebook page for today's specials!</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Allergen / Food Safety Disclaimer from the original print menu -->
|
||||
<div class="menu-disclaimer" style="margin-top: 3.5rem; text-align: center; padding-top: 2rem; border-top: 1px solid var(--rule);">
|
||||
<p class="muted" style="font-size: 0.8rem; line-height: 1.6; max-width: 68ch; margin: 0 auto;">
|
||||
Consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of foodborne illness. Our kitchen handles a variety of ingredients, and our menu items may contain or come into contact with common allergens such as milk, eggs, wheat, soy, nuts, or shellfish.
|
||||
</p>
|
||||
<div class="hero-ctas" style="margin-top: 2rem; justify-content: center;">
|
||||
<a class="btn btn-primary" href="https://www.facebook.com/profile.php?id=100086635646712" target="_blank" rel="noopener">See Daily Specials on Facebook</a>
|
||||
<a class="btn btn-outline" href="mailto:info@theoldfitzpub.com">Ask a Kitchen Question</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="footer-grid">
|
||||
<div>
|
||||
<a class="footer-brand" href="./">
|
||||
<img class="knot" src="./assets/knot.png" alt="">
|
||||
<span>THE OLD FITZ PUBLIC HOUSE</span>
|
||||
</a>
|
||||
<address>
|
||||
100 N Washington Street<br>
|
||||
Marion, Indiana
|
||||
</address>
|
||||
</div>
|
||||
<div>
|
||||
<p class="col-title">Hours</p>
|
||||
<div class="hours-mini">
|
||||
<span>Thu — Sat · 4:00–10:00 PM</span>
|
||||
<span>Kitchen closes at 9 PM</span>
|
||||
<span>Sun — Wed · Closed</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="col-title">Get in Touch</p>
|
||||
<a class="email" href="mailto:info@theoldfitzpub.com">info@theoldfitzpub.com</a>
|
||||
<div class="social-row">
|
||||
<a href="https://www.instagram.com/theoldfitzpub/?hl=en" target="_blank" rel="noopener" aria-label="Instagram">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6">
|
||||
<rect x="3" y="3" width="18" height="18" rx="5" />
|
||||
<circle cx="12" cy="12" r="4.2" />
|
||||
<circle cx="17.2" cy="6.8" r="1" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.facebook.com/profile.php?id=100086635646712" target="_blank" rel="noopener" aria-label="Facebook">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6">
|
||||
<path d="M15 4h-2.2C10.7 4 9.5 5.3 9.5 7.3V10H7v3.2h2.5V21h3.3v-7.8h2.6l.4-3.2h-3V7.6c0-.8.4-1.3 1.4-1.3H15V4Z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<span>© 2026 The Old Fitz Public House</span>
|
||||
<span>A neighborhood pub in Marion, Indiana</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in new issue
Block a user