Commit Graph
43 Commits
Author SHA1 Message Date
David Dyer 51bdfafaed Remove Docker CLI installation step from GitHub Actions workflow
Build image / build (push) Successful in 10s
2026-10-27 22:16:53 -04:00
David Dyer 3a77bae21e Change build environment from debian to ubuntu-latest in GitHub Actions workflow
Build image / build (push) Successful in 1m29s
2026-10-27 22:12:39 -04:00
David Dyer 69be043f9d Add deployment step to GitHub Actions workflow for Docker host
Build image / build (push) Failing after 32s
2026-10-25 19:45:30 -04:00
David Dyer a7fb530061 Add step to trust home CA for improved security in Docker builds 2026-10-25 19:09:34 -04:00
David Dyer cf9fed749b Install Docker CLI in build workflow for Debian environment 2026-10-25 17:35:25 -04:00
David Dyer 7cac2ebcae Change build environment from ubuntu-latest to debian for consistency in workflow 2026-10-25 17:26:32 -04:00
David Dyer e2c47bed61 Add GitHub Actions workflow for building and pushing Docker images 2026-10-25 17:20:51 -04:00
David Dyer 759c22afaf Add Dockerfile and .dockerignore for improved containerization and security 2026-10-25 17:09:05 -04:00
David Dyer bb65ecd43c Refactor configuration handling: implement dynamic loading of config and cache paths for improved security and flexibility in Docker environments 2026-10-25 16:49:43 -04:00
David Dyer c7b836bcf2 Refactor admin pages to use shared head and navigation components
- 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.
2026-10-25 16:06:15 -04:00
David Dyer 3286bce6b1 Enhance Docker setup and local development: bind database directory, update PHP server command, and add local dev router to prevent exposure of sensitive files 2026-10-25 14:38:09 -04:00
David Dyer 02551eebbc Refactor admin modals: replace backdrop divs with native <dialog> elements for improved accessibility and functionality 2026-10-25 13:08:40 -04:00
David Dyer 242c5165e1 Refactor admin panel: enhance user management and security features
- 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.
2026-10-25 12:58:19 -04:00
David DyerandClaude Sonnet 5 91ec95a4a9 Upgrade PHP image to 8.5-apache
PHP 8.3.33 (running in prod) is vulnerable to the PHP2Shell RCE flaws
(CVE-2026-109618, CVE-2026-109619) fixed upstream in 8.3.36/8.4.27/8.5.12.
8.5 also has the longest remaining support window and matches the local
dev PHP version already in use.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-22 17:35:44 -04:00
David DyerandClaude Sonnet 5 e0904b368c Derive meta-description hours from Hours & Visit data instead of hardcoding
The <meta description>, og:description, and twitter:description tags all
had "Open Thursday through Saturday, 4–10pm" typed in by hand, so editing
hours in the admin panel silently left the page's search/social preview
copy wrong.

Added hours_meta_summary() to inc/hours.php, built from the same
group_hours_into_ranges() grouping the Hours & Visit card and footer
already use, so all three now read the live pub_hours data and stay in
sync with whatever's set under Hours & Visit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 10:27:42 -04:00
David DyerandClaude Sonnet 5 bbe84e8614 Add filemtime-based cache busting for styles.css and main.js
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>
2026-09-02 10:23:40 -04:00
David DyerandClaude Sonnet 5 375407245c Fix dist script: point it at build:sass, not the removed build script
npm run dist called "npm run build", but that script had been renamed
to "build:sass" without updating the reference, so dist errored out
before ever running scripts/build-dist.js.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 22:07:42 -04:00
David DyerandClaude Sonnet 5 11c72d1231 Point Say Hello at Facebook, drop redundant Contact nav item
- Hero "Say Hello" button now opens the Facebook page instead of a
  mailto: link, matching the other social CTAs on the page.
- Removed the "Contact" nav link since "Hours & Visit" already scrolls
  to the same section (which includes the Find Us / contact card).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 22:00:43 -04:00
David DyerandClaude Sonnet 5 3e5d804bc3 Add top margin to homepage stat card; minor whitespace cleanup
- components/_cards.scss: .stat-card gets margin-top: 2rem; rebuilt
  styles.css to match.
- index.php: strip trailing whitespace on a blank line in the music
  section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:33:37 -04:00
David DyerandClaude Sonnet 5 9a32d34662 Split styles.scss into a standard 7-1-style Sass structure
Breaks the single 2,167-line styles.scss into abstracts/base/components/
layout/pages partials, forwarded from styles.scss (now a manifest only)
via @use. Organized by what each rule is/does rather than by page, since
a lot of it is intentionally shared — the item/announcement/user admin
list grids share one CSS Grid system across three admin pages, and
.knot/.rule-row/.hero-ctas/the announcement banner are used on both
index.php and menu.php. Splitting by page would have fought that.

  abstracts/tokens        — :root custom properties
  base/reset, utilities   — bare-element defaults, small reusable utility classes
  components/*            — buttons, cards/panels, forms, status-badge,
                             admin data-grid, admin modal, announcement
                             banner, dividers, social-row
  layout/*                — header/nav, footer, admin nav+dashboard shell
  pages/*                 — home, public menu, admin login/menu/hours

One real content move: .category-panel .panel-head's mobile override
now sits next to its desktop rule in pages/admin-menu instead of inside
the shared admin-data-grid mobile block — keeping it there would have
broken a same-specificity cascade order dependency once split across
files.

Verified behavior-preserving: fresh build's declaration count, class-
token set, and declaration multiset all match the pre-split styles.css
exactly; homepage, menu page, and admin login spot-checked visually and
via computed styles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:19:48 -04:00
David DyerandClaude Sonnet 5 5bed636cca Switch homepage review stat from Facebook to Google
- Replace inc/facebook-stats.php with inc/google-reviews.php: fetches
  average star rating + review count from the Places API (New) instead
  of Facebook's Graph API, same cache-to-JSON/fallback-on-failure shape
  as before.
- index.php: stat card now shows the native star rating ("4.8★ average
  rating") instead of a recommend %, sourced from Google. Drops the
  Facebook-followers line entirely — Google's API has no equivalent and
  showing a stale/static number wasn't worth keeping. "Follow on
  Facebook" links elsewhere on the site are untouched.
- config.example.php: swap FB_PAGE_ID/FB_PAGE_ACCESS_TOKEN/
  FB_STATS_CACHE_TTL/FB_FALLBACK_* for GOOGLE_PLACE_ID/
  GOOGLE_PLACES_API_KEY/GOOGLE_STATS_CACHE_TTL/GOOGLE_FALLBACK_RATING/
  GOOGLE_FALLBACK_REVIEW_COUNT (set to the pub's actual 4.8★/71 reviews),
  with setup instructions in comments. config.php (gitignored) updated
  to match.
- Remove the now-dead .stat-row CSS that only existed for the dropped
  followers line; rebuild styles.css.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 18:41:28 -04:00
David DyerandClaude Sonnet 5 2145094e55 Add Sass build pipeline and production deploy script
- Move hand-authored CSS into src/scss/styles.scss, compiled to the
  tracked styles.css via `npm run build` (sass, no source maps).
- Add package.json/package-lock.json with dev tooling: sass, and a
  `dev` script running php + sass --watch + browser-sync together.
- Add scripts/build-dist.js (npm run dist): assembles an allowlisted,
  production-only copy of the site into dist/ (gitignored) for pushing
  to hosting — excludes node_modules, src/, package files, and the
  live db/fitz.sqlite while including config.php.
- .gitignore: ignore node_modules/, styles.css.map, and dist/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 18:21:01 -04:00
David DyerandClaude Sonnet 5 cd4c6c1969 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>
2026-09-01 14:10:08 -04:00
David DyerandClaude Sonnet 5 eab20899de Add pub hours admin panel, rebuild announcements/users lists on CSS Grid
- 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>
2026-09-01 11:18:23 -04:00
David DyerandClaude Sonnet 5 fd928a24cf Rebuild menu admin item/category rows on CSS Grid, refine actions UI
- 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>
2026-08-31 22:54:11 -04:00
David DyerandClaude Sonnet 5 40f4c102b4 Add mobile nav toggle, responsive admin tables, and menu reordering
- 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>
2026-08-31 22:12:24 -04:00
David Dyer 7c96b952e9 Add password visibility toggle to user and admin forms 2026-08-30 23:07:24 -04:00
David Dyer e68b24e1c7 Add user management to admin dashboard and refine button specificity
- 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
2026-08-30 22:30:47 -04:00
David DyerandClaude Sonnet 5 803d02d758 Add photo and two-column layout to the Live Music section
Replaces the text-only Live Music section with a two-column layout
(photo alongside copy) featuring resident musician Jesse Hart, with
a responsive single-column fallback below 50em. Updates the section
copy to name a resident musician plus guest performers rather than
citing a single past artist by name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:05:45 -04:00
David DyerandClaude Sonnet 5 50eeb41375 Dedupe menu admin CSS/markup, restore Mark Sold Out/Available labels
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>
2026-08-30 22:03:22 -04:00
David DyerandClaude Sonnet 5 f1bbf59548 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>
2026-08-30 21:35:22 -04:00
David Dyer 21ad6c31cf Add dev.sh to .gitignore to prevent tracking of development scripts 2026-08-30 19:57:06 -04:00
David DyerandClaude Sonnet 5 197047f4ef Dedupe remaining CSS: danger colors, redundant background/cursor
- Add --danger-bg/--danger-text tokens and point .error-msg,
  .alert-error, and .btn-danger at them instead of repeating the
  same rgba/hex literals
- Drop .login-btn's cursor: pointer (already covered by button.btn)
- Drop .btn-outline's background: transparent (already covered by
  button.btn for buttons; default background for anchors)

No visual changes intended.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 23:13:25 -04:00
David Dyer 38b32f69fd Reset button background and border styles in styles.css
- Ensure <button class="btn"> elements default to transparent backgrounds
- Ensure .btn-outline uses transparent background to avoid browser-default user-agent button styling
- Set border: none on .btn-primary
2026-08-29 22:53:35 -04:00
David Dyer bd22299247 Add multi-announcement management system
- 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
2026-08-29 22:50:19 -04:00
David DyerandClaude Sonnet 5 e4cd4f6cfb Dedupe styles.css and remove remaining inline styles
- 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>
2026-08-29 22:37:02 -04:00
David Dyer b55c4f294f Consolidate admin styles into styles.css and remove inline style tags
- 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
2026-08-29 22:01:55 -04:00
David Dyer f851e8fd24 Remove unused video file from .gitignore 2026-08-29 21:59:02 -04:00
David Dyer 9b7d9ceccb Add SQLite-backed announcement system and admin portal
- 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
2026-08-29 21:57:42 -04:00
David Dyer b9d3262233 Refactor styles to relative rem/em units and enhance accessibility
- Refactor hardcoded px values to rem and em across layout, components, and media queries
- Improve WCAG color contrast by updating --gold-dim token
- Convert hours table to a semantic description list (<dl>)
- Wrap hero logo in h1 for proper heading hierarchy
- Add database ignore rules to .gitignore
2026-08-29 21:57:29 -04:00
David Dyer e8b62b5270 Improve accessibility, refactor CSS to relative units, and add fluid hero scaling pre-admin-page 2026-08-29 19:54:41 -04:00
David Dyer 9a359c09ba Rename config sample to config.example.php, update comments 2026-08-29 19:33:49 -04:00
David Dyer 48e26d671d Initial commit: The Old Fitz Public House site
Static-turned-PHP site for a pub in Marion, Indiana, with:
- Hero section with tiled brick-texture background
- Live-capable stat card (Facebook followers/reviews via Graph API,
  with graceful fallback to config.php constants when no token is set)
- Open Graph / Twitter Card meta tags for social sharing
- robots.txt

config.php is gitignored (holds a Facebook Page Access Token once
generated); config.sample.php is the tracked template — copy it to
config.php and fill in values to run this locally or in deployment.
2026-08-29 19:32:20 -04:00