- Created `admin-head.php` for consistent <head> markup across admin pages.
- Created `admin-nav.php` for shared navigation bar, reducing duplication.
- Updated all admin pages (account, hours, index, login, menu, users) to utilize the new components.
- Added security headers to `auth.php` and `index.php` for improved security.
- Introduced `security-headers.php` to manage security headers across the application.
- Moved inline styles from admin pages to SCSS files to comply with Content Security Policy (CSP).
- Normalized file permissions during build process to prevent permission issues on deployment.
- Added role-based access control for user management links in admin navigation.
- Improved user account display with a dedicated account link.
- Implemented form submission confirmation for delete actions in the admin interface.
- Introduced a password visibility toggle on the login page.
- Added login throttling to prevent brute-force attacks, recording failed attempts.
- Created a CLI script for creating an admin user, replacing the previous hardcoded admin credentials.
- Updated database schema to support login attempt tracking.
- Enhanced user editing functionality, allowing role changes with restrictions.
- Improved modal handling for user actions, ensuring better focus management.
- Added styles for new account management page and improved modal transitions.
Safari caches static assets far more aggressively than Chrome/Firefox
and largely ignores heuristic revalidation, so a new deploy could keep
showing the old CSS/JS indefinitely even on a hard refresh — no
Cache-Control headers were set anywhere to guide it otherwise.
inc/assets.php adds asset_version($path), which appends a ?v=<mtime>
query string derived from the file's last-modified time. Every page
that links styles.css or main.js (index.php, menu.php, and all five
admin pages) now uses it, so the URL changes whenever the file's
contents change and browsers always fetch the new version.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>
- 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>
- Replace the <table>-based item and category-header rows with CSS Grid
containers using named grid-template-areas, so desktop and mobile each
get their own independent arrangement of the same fields instead of
mobile being a display:block hack forced onto table markup
- Make the status badge itself the availability toggle (no separate
"Mark Sold Out" button), fixed-width so toggling never shifts the
Status column, and shorten its label to "Sold Out"
- Merge the item Tag into the name line as an inline badge instead of
giving it its own column
- Group the move/edit/delete controls into two stacked button columns
(was 4-5 buttons wide) and right-align them on mobile
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>
- Update inc/announcements.php to support listing, fetching by ID, toggling, and deleting multiple announcements
- Upgrade admin/index.php to an announcement management view with data table, status indicators, and create/edit sub-views
- Add admin table and action styles to styles.css without using inline style blocks
- Update index.php to render all currently active banner and hours announcements dynamically
- Add --font-serif/--font-mono/--font-sans tokens to :root and point
all ~24 hardcoded font-family declarations at them
- Drop an exact-duplicate .footer-brand .knot rule
- Merge the shared background/border of .stat-card, .card,
.login-card, and .panel into one grouped selector
- Combine .btn-outline:hover and .nav-cta:hover into one rule
- Move remaining inline style="" attributes in index.php and
admin/index.php into named classes/rules (.about-grid h2,
footer address/.social-row spacing, .panel-head,
.checkbox-label-lg, .field-hint, .form-actions)
No visual changes intended — values carried over as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Move all admin dashboard and login rules into styles.css
- Remove embedded <style> blocks from admin/login.php and admin/index.php
- Clean up redundant preview action button
- Add SQLite database initialization and management in inc/db.php
- Implement session authentication and CSRF security helpers in inc/auth.php
- Implement announcement querying and saving in inc/announcements.php
- Add management dashboard, login, and logout in admin/
- Protect db/ directory with Apache access controls