- Public site: replace the always-hidden mobile nav with a hamburger
toggle (index.php, menu.php, main.js, styles.css)
- Admin: render the Menu/Announcements/Users tables as labeled cards
below ~760px instead of squeezing descriptions into unreadable
single-word-per-line columns
- Admin menu: replace the manual "Order" number (which allowed
duplicates and never reflected in real time) with ↑/↓ buttons that
reorder items and categories immediately and renumber sort_order
sequentially on every move, self-healing any stale duplicates
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Implement admin user management interface (, , ) with async CRUD and password updating
- Add Users tab link to admin navigation across and
- Use in to lower specificity for native button resets, allowing modifier classes like to take precedence
CSS redundancy cleanup on the online menu management feature:
- .menu-category was a byte-for-byte duplicate of the existing .panel
class (background/border/border-radius/padding); folds it into
.panel's shared selector group instead, keeping only what's not
shared (no margin-bottom, since these sit in a grid whose own gap
already spaces them)
- Several inline style="..." attributes were hand-copied verbatim
between admin/menu.php's PHP template and admin/menu.js's client-
side row/panel templates (price/sort/description cell formatting,
category heading spacing, the empty-state row). Replaced with real
CSS classes (.item-price-cell, .item-sort-cell, .item-desc-cell,
.cat-name-heading, .cat-desc-text, .no-items-row td) so the styling
lives in one place instead of two
- class="font-mono" was referenced four times but never defined
anywhere in CSS (silently doing nothing); it's now a real one-line
utility, which also fixes two previously-dead uses of it already
sitting in admin/index.php
- .form-actions already existed as a class with its own margin-top,
but every usage (two new modals here, plus a pre-existing one in
admin/index.php) redundantly re-specified layout via inline style
with a slightly different margin-top value each time; extended the
shared class to own the whole pattern and dropped all three inline
overrides
- Removed a no-op `justify-content: flex-start` (the default value)
from the new .admin-toolbar
Caught and fixed a regression from the above before it shipped:
removing the inline margin-bottom override on the category heading
exposed that .panel h2's margin-bottom (from the existing panel
styles) has higher specificity than a bare .cat-name-heading class,
so the heading would have rendered with 5x the intended spacing.
Rescoped as .category-panel .cat-name-heading to reliably win.
Also restores the "Mark Sold Out" / "Mark Available" button wording
(both directions of the toggle, in both the PHP-rendered rows and
the two JS render paths) after it had been shortened to just "Sold
Out" / "Available".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>