- Implemented the dark photo-based theme.

Moved all images to public/assets/images.
Added darkened photography across heroes, specials, menu cards, 
authentication, story, checkout, and footer.
Replaced placeholder branding with the supplied logo.
Preserved configurable menu item images.
Updated desktop and mobile styling for readable cream/gold text.
This commit is contained in:
Ty Clifford
2026-06-10 16:11:29 -04:00
parent 16235369cb
commit 34ca1be9b7
35 changed files with 554 additions and 134 deletions
+12
View File
@@ -0,0 +1,12 @@
RewriteEngine On
# When public/ is the DocumentRoot, allow its own .htaccess to handle the request.
RewriteRule ^public/ - [L]
# Never expose application internals when the repository root is public.
RewriteRule ^(?:app|config|storage|views)(?:/|$) - [F,END,NC]
# Serve public assets directly, then send every application URL to PHP.
RewriteRule ^assets/(.*)$ public/assets/$1 [END,NC]
RewriteRule ^favicon\.ico$ public/favicon.ico [END,NC]
RewriteRule ^ public/index.php [QSA,END]
+23
View File
@@ -45,6 +45,29 @@ Before public use, enter the restaurant's verified phone number, email address,
and hours in **Dashboard > Settings**. They are intentionally not invented in and hours in **Dashboard > Settings**. They are intentionally not invented in
the default configuration. the default configuration.
## Apache Routing
The repository includes two rewrite configurations:
- `public/.htaccess` when the Apache `DocumentRoot` points to `public/`
- `.htaccess` when shared hosting exposes the repository directory itself
Apache must have `mod_rewrite` enabled and must permit `.htaccess` overrides.
A typical virtual host uses:
```apache
DocumentRoot /path/to/fatbottomgrille/public
<Directory /path/to/fatbottomgrille>
AllowOverride All
Require all granted
</Directory>
```
The preferred setup is to point the web root at `public/`. If the application
is installed in a URL subdirectory, generated links, form actions, redirects,
and assets automatically include that subdirectory.
## Initial Administrator ## Initial Administrator
- Email: `admin@fatbottomgrille.com` - Email: `admin@fatbottomgrille.com`
+55 -3
View File
@@ -8,7 +8,7 @@ final class Http
{ {
public static function redirect(string $path): never public static function redirect(string $path): never
{ {
header('Location: ' . $path); header('Location: ' . self::url($path));
exit; exit;
} }
@@ -46,8 +46,60 @@ final class Http
public static function path(): string public static function path(): string
{ {
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$path = is_string($path) ? rtrim($path, '/') : '/'; $path = is_string($path) ? rawurldecode($path) : '/';
$basePath = self::basePath();
if ($basePath !== '' && ($path === $basePath || str_starts_with($path, $basePath . '/'))) {
$path = substr($path, strlen($basePath)) ?: '/';
}
$path = '/' . ltrim($path, '/');
$path = rtrim($path, '/');
return $path === '' ? '/' : $path; return $path === '' ? '/' : $path;
} }
}
public static function url(string $path = '/'): string
{
if (
preg_match('#^(?:[a-z][a-z0-9+.-]*:)?//#i', $path)
|| str_starts_with($path, 'mailto:')
|| str_starts_with($path, 'tel:')
|| str_starts_with($path, '#')
) {
return $path;
}
$basePath = self::basePath();
$path = '/' . ltrim($path, '/');
return ($basePath === '' ? '' : $basePath) . ($path === '/' ? '/' : $path);
}
public static function basePath(): string
{
static $basePath;
if (isset($basePath)) {
return $basePath;
}
$documentRoot = realpath((string) ($_SERVER['DOCUMENT_ROOT'] ?? ''));
$applicationRoot = realpath(BASE_PATH);
$publicRoot = realpath(BASE_PATH . '/public');
if ($documentRoot && $publicRoot && $documentRoot === $publicRoot) {
return $basePath = '';
}
if (
$documentRoot
&& $applicationRoot
&& str_starts_with($applicationRoot . DIRECTORY_SEPARATOR, rtrim($documentRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR)
) {
$relative = substr($applicationRoot, strlen(rtrim($documentRoot, DIRECTORY_SEPARATOR)));
return $basePath = rtrim('/' . trim(str_replace(DIRECTORY_SEPARATOR, '/', $relative), '/'), '/');
}
$script = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? ''));
$script = preg_replace('#/(?:public/)?index\.php$#', '', $script) ?? '';
return $basePath = rtrim($script, '/');
}
}
+11 -1
View File
@@ -21,10 +21,20 @@ function csrf_field(): string
function active_path(string $path): string function active_path(string $path): string
{ {
$current = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); $current = \FatBottom\Core\Http::path();
return $current === $path ? 'is-active' : ''; return $current === $path ? 'is-active' : '';
} }
function url(string $path = '/'): string
{
return \FatBottom\Core\Http::url($path);
}
function asset(string $path): string
{
return \FatBottom\Core\Http::url('/' . ltrim($path, '/'));
}
function selected(mixed $value, mixed $expected): string function selected(mixed $value, mixed $expected): string
{ {
return (string) $value === (string) $expected ? 'selected' : ''; return (string) $value === (string) $expected ? 'selected' : '';
+8
View File
@@ -0,0 +1,8 @@
RewriteEngine On
# Existing public files are served normally; all other URLs use the app router.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [END]
RewriteRule ^ index.php [QSA,END]
+317 -35
View File
@@ -31,8 +31,8 @@ html {
body { body {
margin: 0; margin: 0;
color: var(--ink); color: var(--cream);
background: var(--paper); background: var(--ink);
font-family: var(--font-body); font-family: var(--font-body);
font-size: 16px; font-size: 16px;
line-height: 1.6; line-height: 1.6;
@@ -41,8 +41,8 @@ body {
body.store-body { body.store-body {
background-image: background-image:
radial-gradient(circle at 0 0, rgba(200, 151, 59, 0.06), transparent 28%), radial-gradient(circle at 0 0, rgba(200, 151, 59, 0.08), transparent 28%),
linear-gradient(rgba(8, 18, 28, 0.018) 1px, transparent 1px); linear-gradient(rgba(255, 255, 255, 0.018) 1px, transparent 1px);
background-size: auto, 100% 22px; background-size: auto, 100% 22px;
} }
@@ -297,6 +297,14 @@ h3 {
font-size: 1rem; font-size: 1rem;
font-weight: 800; font-weight: 800;
letter-spacing: 0.02em; letter-spacing: 0.02em;
overflow: hidden;
}
.brand-mark img {
width: 100%;
height: 100%;
object-fit: cover;
transform: scale(1.05);
} }
.brand-copy, .brand-copy,
@@ -401,7 +409,8 @@ h3 {
overflow: hidden; overflow: hidden;
color: var(--cream); color: var(--cream);
background: background:
linear-gradient(108deg, rgba(8, 18, 28, 0.98) 0%, rgba(12, 30, 43, 0.96) 52%, rgba(16, 42, 58, 0.9) 100%); linear-gradient(90deg, rgba(5, 12, 18, 0.99) 0%, rgba(8, 20, 30, 0.96) 48%, rgba(8, 18, 28, 0.76) 100%),
url("images/reuben-fries.jpg") center 48% / cover no-repeat;
} }
.hero::after { .hero::after {
@@ -495,18 +504,61 @@ h3 {
place-items: center; place-items: center;
} }
.plate-ring { .hero-food-photo {
display: grid; position: relative;
width: min(470px, 40vw); width: min(460px, 39vw);
aspect-ratio: 1; aspect-ratio: 0.88;
place-items: center; overflow: hidden;
border: 2px solid rgba(225, 183, 94, 0.35); border: 1px solid rgba(225, 183, 94, 0.42);
border-radius: 50%; border-radius: 44% 44% 8px 8px;
box-shadow: 0 45px 90px rgba(0, 0, 0, 0.52);
transform: rotate(2deg);
}
.hero-food-photo::after {
position: absolute;
inset: 0;
background: background:
radial-gradient(circle, rgba(255, 255, 255, 0.11) 0 52%, rgba(255, 255, 255, 0.03) 53% 65%, transparent 66%), linear-gradient(180deg, rgba(5, 12, 18, 0.06), rgba(5, 12, 18, 0.22) 54%, rgba(5, 12, 18, 0.94)),
linear-gradient(145deg, rgba(255, 255, 255, 0.08), transparent 65%); linear-gradient(90deg, rgba(200, 151, 59, 0.08), transparent);
box-shadow: 0 45px 90px rgba(0, 0, 0, 0.35); content: "";
transform: rotate(-6deg); }
.hero-food-photo img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: 48% center;
filter: saturate(0.82) contrast(1.06) brightness(0.72);
}
.hero-photo-caption {
position: absolute;
z-index: 2;
right: 28px;
bottom: 25px;
left: 28px;
display: flex;
flex-direction: column;
padding-top: 18px;
border-top: 1px solid rgba(225, 183, 94, 0.48);
}
.hero-photo-caption span {
color: var(--gold-light);
font-size: 0.62rem;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.hero-photo-caption strong {
margin-top: 4px;
color: var(--cream);
font-family: var(--font-display);
font-size: 1.35rem;
letter-spacing: 0.02em;
text-transform: uppercase;
} }
.burger { .burger {
@@ -646,13 +698,37 @@ h3 {
padding: 30px; padding: 30px;
overflow: hidden; overflow: hidden;
color: var(--cream); color: var(--cream);
background: background-color: var(--ink-soft);
linear-gradient(145deg, rgba(255, 255, 255, 0.07), transparent 58%), background-position: center;
var(--ink-soft); background-size: cover;
border: 1px solid rgba(200, 151, 59, 0.28); border: 1px solid rgba(200, 151, 59, 0.28);
border-radius: var(--radius); border-radius: var(--radius);
} }
.special-card:nth-child(4n + 1) {
background-image:
linear-gradient(112deg, rgba(5, 12, 18, 0.98), rgba(8, 18, 28, 0.82)),
url("images/quesadilla.jpg");
}
.special-card:nth-child(4n + 2) {
background-image:
linear-gradient(112deg, rgba(5, 12, 18, 0.98), rgba(8, 18, 28, 0.8)),
url("images/sausage-kale-soup.jpg");
}
.special-card:nth-child(4n + 3) {
background-image:
linear-gradient(112deg, rgba(5, 12, 18, 0.98), rgba(8, 18, 28, 0.78)),
url("images/grilled-chicken-salad.jpg");
}
.special-card:nth-child(4n + 4) {
background-image:
linear-gradient(112deg, rgba(5, 12, 18, 0.98), rgba(8, 18, 28, 0.8)),
url("images/curry-rice.jpg");
}
.special-card::after { .special-card::after {
position: absolute; position: absolute;
right: -70px; right: -70px;
@@ -707,7 +783,9 @@ h3 {
} }
.menu-preview { .menu-preview {
background: var(--paper); background:
radial-gradient(circle at 86% 14%, rgba(200, 151, 59, 0.08), transparent 24%),
#09141e;
} }
.menu-grid { .menu-grid {
@@ -719,8 +797,9 @@ h3 {
.menu-card { .menu-card {
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
background: var(--white); color: var(--cream);
border: 1px solid #dedcd4; background: #0d1c28;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: var(--radius-large); border-radius: var(--radius-large);
box-shadow: 0 8px 28px rgba(8, 18, 28, 0.06); box-shadow: 0 8px 28px rgba(8, 18, 28, 0.06);
transition: transform 160ms ease, box-shadow 160ms ease; transition: transform 160ms ease, box-shadow 160ms ease;
@@ -750,6 +829,53 @@ h3 {
letter-spacing: -0.06em; letter-spacing: -0.06em;
} }
.menu-card:nth-child(7n + 1) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/reuben-fries.jpg");
}
.menu-card:nth-child(7n + 2) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/chicken-sandwich-fries.jpg");
}
.menu-card:nth-child(7n + 3) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/quesadilla.jpg");
}
.menu-card:nth-child(7n + 4) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/curry-rice.jpg");
}
.menu-card:nth-child(7n + 5) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/italian-sub.jpg");
}
.menu-card:nth-child(7n + 6) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/sausage-kale-soup.jpg");
}
.menu-card:nth-child(7n) .menu-card-art:not(.has-image) {
background-image:
linear-gradient(rgba(5, 12, 18, 0.62), rgba(5, 12, 18, 0.86)),
url("images/grilled-chicken-salad.jpg");
}
.menu-card-art:not(.has-image) {
background-position: center;
background-size: cover;
}
.menu-card-art::after { .menu-card-art::after {
position: absolute; position: absolute;
width: 150px; width: 150px;
@@ -798,14 +924,14 @@ h3 {
} }
.menu-card-heading > strong { .menu-card-heading > strong {
color: #8e6422; color: var(--gold-light);
white-space: nowrap; white-space: nowrap;
} }
.menu-card-body > p { .menu-card-body > p {
min-height: 50px; min-height: 50px;
margin: 12px 0 16px; margin: 12px 0 16px;
color: #68737a; color: #aebbc3;
font-size: 0.86rem; font-size: 0.86rem;
line-height: 1.5; line-height: 1.5;
} }
@@ -819,8 +945,8 @@ h3 {
.tag-list span { .tag-list span {
padding: 3px 8px; padding: 3px 8px;
color: #5f6b72; color: #b9c4ca;
background: #f0eee8; background: #152938;
border-radius: 20px; border-radius: 20px;
font-size: 0.65rem; font-size: 0.65rem;
font-weight: 600; font-weight: 600;
@@ -836,9 +962,10 @@ h3 {
width: 100%; width: 100%;
height: 38px; height: 38px;
padding-inline: 10px; padding-inline: 10px;
border: 1px solid #cbc9c1; color: var(--cream);
border: 1px solid #344857;
border-radius: var(--radius); border-radius: var(--radius);
background: var(--white); background: #08141f;
} }
.category-band { .category-band {
@@ -908,7 +1035,9 @@ h3 {
place-items: center; place-items: center;
overflow: hidden; overflow: hidden;
color: var(--gold); color: var(--gold);
background: var(--navy); background:
linear-gradient(rgba(5, 12, 18, 0.58), rgba(5, 12, 18, 0.9)),
url("images/italian-sub.jpg") center / cover no-repeat;
} }
.story-number { .story-number {
@@ -939,8 +1068,8 @@ h3 {
padding-block: 72px; padding-block: 72px;
color: var(--cream); color: var(--cream);
background: background:
radial-gradient(circle at 80% 0, rgba(200, 151, 59, 0.18), transparent 30%), linear-gradient(90deg, rgba(5, 12, 18, 0.97), rgba(8, 18, 28, 0.8)),
var(--navy); url("images/italian-sub.jpg") center 55% / cover no-repeat;
} }
.page-hero-compact { .page-hero-compact {
@@ -1812,7 +1941,10 @@ textarea:focus {
.site-footer { .site-footer {
padding-top: 68px; padding-top: 68px;
color: #b6c0c6; color: #b6c0c6;
background: #050c12; background:
linear-gradient(90deg, rgba(5, 12, 18, 0.99), rgba(5, 12, 18, 0.94)),
url("images/fat-bottom-grille-logo.jpg") right -80px center / 520px no-repeat,
#050c12;
} }
.footer-grid { .footer-grid {
@@ -1857,6 +1989,156 @@ textarea:focus {
text-transform: uppercase; text-transform: uppercase;
} }
/* Dark storefront surfaces */
.store-body main {
background:
radial-gradient(circle at 10% 18%, rgba(200, 151, 59, 0.055), transparent 25%),
#09141e;
}
.store-body .button-outline {
color: var(--cream);
border-color: #788892;
}
.store-body .button-outline:hover {
color: var(--ink);
background: var(--gold);
border-color: var(--gold);
}
.store-body .section-heading > p,
.store-body .story-grid p,
.store-body .menu-category-heading p,
.store-body .empty-state p,
.store-body .cart-item-copy p,
.store-body .cart-item-copy span,
.store-body .form-section-heading p,
.store-body .demo-payment p,
.store-body .account-email,
.store-body .order-history article > div span,
.store-body .auth-switch,
.store-body .form-footnote {
color: #9eacb5;
}
.store-body .story-section,
.store-body .menu-page,
.store-body .checkout-layout,
.store-body .account-grid {
color: var(--cream);
}
.store-body .story-section {
background:
linear-gradient(90deg, rgba(8, 18, 28, 0.99), rgba(8, 18, 28, 0.92)),
url("images/sausage-kale-soup.jpg") right center / 46% auto no-repeat;
}
.store-body .category-band {
background:
linear-gradient(90deg, rgba(5, 12, 18, 0.98), rgba(5, 12, 18, 0.86)),
url("images/curry-rice.jpg") center 56% / cover no-repeat;
}
.store-body .menu-category-heading {
border-bottom-color: rgba(225, 183, 94, 0.34);
}
.store-body .panel,
.store-body .cart-list,
.store-body .empty-state,
.store-body .auth-card {
color: var(--cream);
background: #0d1c28;
border-color: rgba(255, 255, 255, 0.11);
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.24);
}
.store-body .cart-item,
.store-body .form-section,
.store-body .order-history article {
border-color: rgba(255, 255, 255, 0.1);
}
.store-body label {
color: #b8c3ca;
}
.store-body input,
.store-body select,
.store-body textarea {
color: var(--cream);
background: #07131d;
border-color: #344653;
}
.store-body input::placeholder,
.store-body textarea::placeholder {
color: #71818c;
}
.store-body .demo-payment,
.store-body .notice,
.store-body .check-control,
.store-body .account-password {
color: #b8c3ca;
background: #0a1722;
border-color: #2d404e;
}
.store-body .check-control small {
color: #84939c;
}
.store-body .notice-error {
color: #efb1b1;
background: #2a171b;
border-color: var(--danger);
}
.store-body .order-summary {
background:
linear-gradient(145deg, rgba(8, 18, 28, 0.98), rgba(8, 18, 28, 0.88)),
url("images/quesadilla.jpg") center / cover no-repeat;
border: 1px solid rgba(200, 151, 59, 0.22);
}
.store-body .auth-section {
background:
linear-gradient(90deg, rgba(5, 12, 18, 0.97), rgba(8, 18, 28, 0.87)),
url("images/grilled-chicken-salad.jpg") center / cover no-repeat;
}
.store-body .confirmation-section {
background:
linear-gradient(rgba(5, 12, 18, 0.94), rgba(5, 12, 18, 0.97)),
url("images/sausage-kale-soup.jpg") center / cover no-repeat;
}
.store-body .status-pill {
color: #c2ccd2;
background: #182a37;
}
.store-body .status-paid,
.store-body .status-completed,
.store-body .status-ready {
color: #9de0c0;
background: #17382d;
}
.store-body .status-preparing {
color: #f0ce83;
background: #3b3019;
}
.store-body .status-cancelled,
.store-body .status-refunded {
color: #efaaaa;
background: #3a2024;
}
/* Dashboard */ /* Dashboard */
.admin-body { .admin-body {
color: #dbe2e6; color: #dbe2e6;
@@ -2769,7 +3051,7 @@ code {
margin-top: -50px; margin-top: -50px;
} }
.plate-ring { .hero-food-photo {
width: min(470px, 78vw); width: min(470px, 78vw);
} }
@@ -2904,8 +3186,8 @@ code {
min-height: 330px; min-height: 330px;
} }
.burger { .hero-food-photo {
height: 220px; width: min(430px, 86vw);
} }
.hero-stamp { .hero-stamp {
Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+47 -4
View File
@@ -1,10 +1,53 @@
<?php <?php
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$file = __DIR__ . $path; $path = is_string($path) ? $path : '/';
if ($path !== '/' && is_file($file)) { $documentRoot = realpath((string) ($_SERVER['DOCUMENT_ROOT'] ?? ''));
return false; $applicationRoot = realpath(dirname(__DIR__));
$publicRoot = realpath(__DIR__);
$basePath = '';
if ($documentRoot && $publicRoot && $documentRoot !== $publicRoot && $applicationRoot) {
if ($documentRoot === $applicationRoot) {
$basePath = '';
} elseif (str_starts_with($applicationRoot, rtrim($documentRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR)) {
$basePath = '/' . trim(substr($applicationRoot, strlen($documentRoot)), DIRECTORY_SEPARATOR);
}
}
$publicPath = $path;
if ($basePath !== '' && ($publicPath === $basePath || str_starts_with($publicPath, $basePath . '/'))) {
$publicPath = substr($publicPath, strlen($basePath)) ?: '/';
}
$file = realpath(__DIR__ . '/' . ltrim($publicPath, '/'));
$isPublicFile = $file
&& $publicRoot
&& str_starts_with($file, rtrim($publicRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR)
&& is_file($file);
if ($publicPath !== '/' && $isPublicFile) {
if ($documentRoot === $publicRoot) {
return false;
}
$contentTypes = [
'css' => 'text/css; charset=UTF-8',
'js' => 'application/javascript; charset=UTF-8',
'json' => 'application/json; charset=UTF-8',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'svg' => 'image/svg+xml',
'webp' => 'image/webp',
'ico' => 'image/x-icon',
];
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
header('Content-Type: ' . ($contentTypes[$extension] ?? 'application/octet-stream'));
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
} }
require __DIR__ . '/index.php'; require __DIR__ . '/index.php';
+2 -2
View File
@@ -14,7 +14,7 @@
</div> </div>
<span class="account-email"><?= e($currentUser['email']) ?></span> <span class="account-email"><?= e($currentUser['email']) ?></span>
</div> </div>
<form class="form-stack" action="/account" method="post"> <form class="form-stack" action="<?= e(url('/account')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid"> <div class="form-grid">
<label>Full name <label>Full name
@@ -68,7 +68,7 @@
<span class="eyebrow">Past pickups</span> <span class="eyebrow">Past pickups</span>
<h2>Order History</h2> <h2>Order History</h2>
</div> </div>
<a class="button button-small" href="/menu">Order Again</a> <a class="button button-small" href="<?= e(url('/menu')) ?>">Order Again</a>
</div> </div>
<?php if (!$orders): ?> <?php if (!$orders): ?>
<div class="empty-state compact"> <div class="empty-state compact">
+3 -3
View File
@@ -24,7 +24,7 @@
<?php if (password_verify('ChangeMe123!', (string) $currentUser['password_hash'])): ?> <?php if (password_verify('ChangeMe123!', (string) $currentUser['password_hash'])): ?>
<div class="notice admin-security-notice"> <div class="notice admin-security-notice">
<strong>Security reminder:</strong> the initial administrator password is still active. <strong>Security reminder:</strong> the initial administrator password is still active.
Change it from <a href="/account">My Account</a> before deployment. Change it from <a href="<?= e(url('/account')) ?>">My Account</a> before deployment.
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -35,7 +35,7 @@
<span class="eyebrow">Live queue</span> <span class="eyebrow">Live queue</span>
<h2>Recent Orders</h2> <h2>Recent Orders</h2>
</div> </div>
<a class="button button-small" href="/admin/orders">View All Orders</a> <a class="button button-small" href="<?= e(url('/admin/orders')) ?>">View All Orders</a>
</div> </div>
<div class="table-wrap"> <div class="table-wrap">
<table> <table>
@@ -46,7 +46,7 @@
<?php endif; ?> <?php endif; ?>
<?php foreach ($recentOrders as $order): ?> <?php foreach ($recentOrders as $order): ?>
<tr> <tr>
<td><a href="/admin/order?id=<?= (int) $order['id'] ?>"><?= e($order['order_number']) ?></a></td> <td><a href="<?= e(url('/admin/order')) ?>?id=<?= (int) $order['id'] ?>"><?= e($order['order_number']) ?></a></td>
<td><?= e($order['customer_name']) ?><small><?= e($order['customer_phone']) ?></small></td> <td><?= e($order['customer_name']) ?><small><?= e($order['customer_phone']) ?></small></td>
<td><?= e(store_datetime((string) $order['placed_at'], 'g:i A')) ?></td> <td><?= e(store_datetime((string) $order['placed_at'], 'g:i A')) ?></td>
<td><span class="status-pill status-<?= e($order['status']) ?>"><?= e(ucfirst((string) $order['status'])) ?></span></td> <td><span class="status-pill status-<?= e($order['status']) ?>"><?= e(ucfirst((string) $order['status'])) ?></span></td>
+5 -6
View File
@@ -1,6 +1,6 @@
<div class="admin-page-actions"> <div class="admin-page-actions">
<p>Categories and items appear on the storefront as soon as they are active.</p> <p>Categories and items appear on the storefront as soon as they are active.</p>
<a class="button button-ghost" href="/admin/export/menu.pdf">Download Styled PDF</a> <a class="button button-ghost" href="<?= e(url('/admin/export/menu.pdf')) ?>">Download Styled PDF</a>
</div> </div>
<div class="admin-stack"> <div class="admin-stack">
@@ -18,7 +18,7 @@
<span class="status-dot <?= $category['active'] ? 'is-on' : '' ?>"><?= $category['active'] ? 'Active' : 'Hidden' ?></span> <span class="status-dot <?= $category['active'] ? 'is-on' : '' ?>"><?= $category['active'] ? 'Active' : 'Hidden' ?></span>
<span class="edit-label">Edit</span> <span class="edit-label">Edit</span>
</summary> </summary>
<form class="inline-editor form-stack" action="/admin/category/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/category/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int) $category['id'] ?>"> <input type="hidden" name="id" value="<?= (int) $category['id'] ?>">
<div class="form-grid"> <div class="form-grid">
@@ -35,7 +35,7 @@
</div> </div>
<details class="new-record"> <details class="new-record">
<summary>+ Add Category</summary> <summary>+ Add Category</summary>
<form class="inline-editor form-stack" action="/admin/category/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/category/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid"> <div class="form-grid">
<label>Name<input type="text" name="name" required></label> <label>Name<input type="text" name="name" required></label>
@@ -65,7 +65,7 @@
<span class="status-dot <?= $item['active'] ? 'is-on' : '' ?>"><?= $item['active'] ? 'Active' : 'Hidden' ?></span> <span class="status-dot <?= $item['active'] ? 'is-on' : '' ?>"><?= $item['active'] ? 'Active' : 'Hidden' ?></span>
<span class="edit-label">Edit</span> <span class="edit-label">Edit</span>
</summary> </summary>
<form class="inline-editor form-stack" action="/admin/item/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/item/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int) $item['id'] ?>"> <input type="hidden" name="id" value="<?= (int) $item['id'] ?>">
<div class="form-grid form-grid-three"> <div class="form-grid form-grid-three">
@@ -96,7 +96,7 @@
</div> </div>
<details class="new-record"> <details class="new-record">
<summary>+ Add Menu Item</summary> <summary>+ Add Menu Item</summary>
<form class="inline-editor form-stack" action="/admin/item/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/item/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid form-grid-three"> <div class="form-grid form-grid-three">
<label>Name<input type="text" name="name" required></label> <label>Name<input type="text" name="name" required></label>
@@ -122,4 +122,3 @@
</details> </details>
</section> </section>
</div> </div>
+1 -1
View File
@@ -5,7 +5,7 @@
<div class="admin-detail-grid newsletter-grid"> <div class="admin-detail-grid newsletter-grid">
<section class="admin-panel"> <section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Send an update</span><h2>New Newsletter</h2></div></div> <div class="admin-panel-heading"><div><span class="eyebrow">Send an update</span><h2>New Newsletter</h2></div></div>
<form class="form-stack" action="/admin/newsletters/send" method="post"> <form class="form-stack" action="<?= e(url('/admin/newsletters/send')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<label>Subject line<input type="text" name="subject" maxlength="140" required placeholder="This weekend at Fat Bottom Grille"></label> <label>Subject line<input type="text" name="subject" maxlength="140" required placeholder="This weekend at Fat Bottom Grille"></label>
<label>Message<textarea name="content" rows="12" required placeholder="Share a new special, schedule update, or note from the kitchen..."></textarea></label> <label>Message<textarea name="content" rows="12" required placeholder="Share a new special, schedule update, or note from the kitchen..."></textarea></label>
+3 -3
View File
@@ -1,6 +1,6 @@
<div class="detail-heading"> <div class="detail-heading">
<div> <div>
<a class="back-link" href="/admin/orders">&larr; Back to orders</a> <a class="back-link" href="<?= e(url('/admin/orders')) ?>">&larr; Back to orders</a>
<div class="detail-title-row"> <div class="detail-title-row">
<h2><?= e($order['order_number']) ?></h2> <h2><?= e($order['order_number']) ?></h2>
<span class="status-pill status-<?= e($order['status']) ?>"><?= e(ucfirst((string) $order['status'])) ?></span> <span class="status-pill status-<?= e($order['status']) ?>"><?= e(ucfirst((string) $order['status'])) ?></span>
@@ -51,7 +51,7 @@
<aside> <aside>
<section class="admin-panel"> <section class="admin-panel">
<div class="admin-panel-heading"><h2>Update Order</h2></div> <div class="admin-panel-heading"><h2>Update Order</h2></div>
<form class="form-stack" action="/admin/order" method="post"> <form class="form-stack" action="<?= e(url('/admin/order')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>"> <input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>">
<label>Status <label>Status
@@ -78,7 +78,7 @@
</section> </section>
<section class="admin-panel"> <section class="admin-panel">
<div class="admin-panel-heading"><h2>Issue Refund</h2></div> <div class="admin-panel-heading"><h2>Issue Refund</h2></div>
<form class="form-stack" action="/admin/order/refund" method="post"> <form class="form-stack" action="<?= e(url('/admin/order/refund')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>"> <input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>">
<label>Amount <label>Amount
+3 -3
View File
@@ -1,6 +1,6 @@
<section class="admin-panel"> <section class="admin-panel">
<div class="admin-panel-heading admin-toolbar"> <div class="admin-panel-heading admin-toolbar">
<form class="admin-filters" action="/admin/orders" method="get"> <form class="admin-filters" action="<?= e(url('/admin/orders')) ?>" method="get">
<input type="search" name="q" value="<?= e($query) ?>" placeholder="Order, customer, or email"> <input type="search" name="q" value="<?= e($query) ?>" placeholder="Order, customer, or email">
<select name="status"> <select name="status">
<option value="">All statuses</option> <option value="">All statuses</option>
@@ -10,7 +10,7 @@
</select> </select>
<button class="button button-small" type="submit">Filter</button> <button class="button button-small" type="submit">Filter</button>
</form> </form>
<a class="button button-small button-ghost" href="/admin/export/orders.csv">Export CSV</a> <a class="button button-small button-ghost" href="<?= e(url('/admin/export/orders.csv')) ?>">Export CSV</a>
</div> </div>
<div class="table-wrap"> <div class="table-wrap">
<table> <table>
@@ -30,7 +30,7 @@
<?php if ((int) $order['refunded_cents'] > 0): ?><small><?= money($order['refunded_cents']) ?> returned</small><?php endif; ?> <?php if ((int) $order['refunded_cents'] > 0): ?><small><?= money($order['refunded_cents']) ?> returned</small><?php endif; ?>
</td> </td>
<td><?= money($order['total_cents']) ?></td> <td><?= money($order['total_cents']) ?></td>
<td><a class="button button-small button-ghost" href="/admin/order?id=<?= (int) $order['id'] ?>">Open</a></td> <td><a class="button button-small button-ghost" href="<?= e(url('/admin/order')) ?>?id=<?= (int) $order['id'] ?>">Open</a></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
+4 -4
View File
@@ -1,4 +1,4 @@
<form class="admin-stack" action="/admin/settings" method="post"> <form class="admin-stack" action="<?= e(url('/admin/settings')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<section class="admin-panel"> <section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Public identity</span><h2>Business Information</h2></div></div> <div class="admin-panel-heading"><div><span class="eyebrow">Public identity</span><h2>Business Information</h2></div></div>
@@ -70,7 +70,7 @@
<span class="status-dot <?= $tax['active'] ? 'is-on' : '' ?>"><?= $tax['active'] ? 'Active' : 'Disabled' ?></span> <span class="status-dot <?= $tax['active'] ? 'is-on' : '' ?>"><?= $tax['active'] ? 'Active' : 'Disabled' ?></span>
<span class="edit-label">Edit</span> <span class="edit-label">Edit</span>
</summary> </summary>
<form class="inline-editor form-stack" action="/admin/tax/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/tax/save')) ?>" method="post">
<?= csrf_field() ?><input type="hidden" name="id" value="<?= (int) $tax['id'] ?>"> <?= csrf_field() ?><input type="hidden" name="id" value="<?= (int) $tax['id'] ?>">
<div class="form-grid"> <div class="form-grid">
<label>Name<input type="text" name="name" value="<?= e($tax['name']) ?>" required></label> <label>Name<input type="text" name="name" value="<?= e($tax['name']) ?>" required></label>
@@ -85,7 +85,7 @@
</div> </div>
<details class="new-record"> <details class="new-record">
<summary>+ Add Tax Rate</summary> <summary>+ Add Tax Rate</summary>
<form class="inline-editor form-stack" action="/admin/tax/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/tax/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid"> <div class="form-grid">
<label>Name<input type="text" name="name" required></label> <label>Name<input type="text" name="name" required></label>
@@ -101,7 +101,7 @@
<section class="admin-panel settings-followup"> <section class="admin-panel settings-followup">
<div class="admin-panel-heading"><div><span class="eyebrow">Optional database mode</span><h2>MySQL / MariaDB Migration</h2></div><span class="role-badge"><?= e(strtoupper((string) $config->get('database.driver', 'sqlite'))) ?> active</span></div> <div class="admin-panel-heading"><div><span class="eyebrow">Optional database mode</span><h2>MySQL / MariaDB Migration</h2></div><span class="role-badge"><?= e(strtoupper((string) $config->get('database.driver', 'sqlite'))) ?> active</span></div>
<p class="muted">SQLite remains the default and requires no database server. This tool copies every application table and row into MySQL or MariaDB; switching drivers is optional.</p> <p class="muted">SQLite remains the default and requires no database server. This tool copies every application table and row into MySQL or MariaDB; switching drivers is optional.</p>
<form class="form-stack" action="/admin/migrate/mysql" method="post"> <form class="form-stack" action="<?= e(url('/admin/migrate/mysql')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid form-grid-three"> <div class="form-grid form-grid-three">
<label>Host<input type="text" value="<?= e($config->get('database.mysql_host')) ?>" disabled></label> <label>Host<input type="text" value="<?= e($config->get('database.mysql_host')) ?>" disabled></label>
+2 -2
View File
@@ -16,7 +16,7 @@
<p><?= e($special['description']) ?></p> <p><?= e($special['description']) ?></p>
<div><strong><?= $special['price_cents'] !== null ? money($special['price_cents']) : 'No override' ?></strong><span class="status-dot <?= $special['active'] ? 'is-on' : '' ?>"><?= $special['active'] ? 'Active' : 'Hidden' ?></span></div> <div><strong><?= $special['price_cents'] !== null ? money($special['price_cents']) : 'No override' ?></strong><span class="status-dot <?= $special['active'] ? 'is-on' : '' ?>"><?= $special['active'] ? 'Active' : 'Hidden' ?></span></div>
</summary> </summary>
<form class="inline-editor form-stack" action="/admin/special/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/special/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int) $special['id'] ?>"> <input type="hidden" name="id" value="<?= (int) $special['id'] ?>">
<div class="form-grid"> <div class="form-grid">
@@ -42,7 +42,7 @@
</div> </div>
<details class="new-record"> <details class="new-record">
<summary>+ Add Homepage Special</summary> <summary>+ Add Homepage Special</summary>
<form class="inline-editor form-stack" action="/admin/special/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/special/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid"> <div class="form-grid">
<label>Title<input type="text" name="title" required></label> <label>Title<input type="text" name="title" required></label>
+3 -4
View File
@@ -1,5 +1,5 @@
<div class="admin-page-actions"> <div class="admin-page-actions">
<form class="admin-filters" action="/admin/users" method="get"> <form class="admin-filters" action="<?= e(url('/admin/users')) ?>" method="get">
<input type="search" name="q" value="<?= e($query) ?>" placeholder="Search name, email, or phone"> <input type="search" name="q" value="<?= e($query) ?>" placeholder="Search name, email, or phone">
<button class="button button-small" type="submit">Search</button> <button class="button button-small" type="submit">Search</button>
</form> </form>
@@ -20,7 +20,7 @@
<span class="status-dot <?= $user['active'] ? 'is-on' : '' ?>"><?= $user['active'] ? 'Active' : 'Disabled' ?></span> <span class="status-dot <?= $user['active'] ? 'is-on' : '' ?>"><?= $user['active'] ? 'Active' : 'Disabled' ?></span>
<span class="edit-label">Edit</span> <span class="edit-label">Edit</span>
</summary> </summary>
<form class="inline-editor form-stack" action="/admin/user/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/user/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int) $user['id'] ?>"> <input type="hidden" name="id" value="<?= (int) $user['id'] ?>">
<div class="form-grid"> <div class="form-grid">
@@ -43,7 +43,7 @@
</div> </div>
<details class="new-record"> <details class="new-record">
<summary>+ Add Staff Member</summary> <summary>+ Add Staff Member</summary>
<form class="inline-editor form-stack" action="/admin/user/save" method="post"> <form class="inline-editor form-stack" action="<?= e(url('/admin/user/save')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-grid form-grid-three"> <div class="form-grid form-grid-three">
<label>Full name<input type="text" name="full_name" required></label> <label>Full name<input type="text" name="full_name" required></label>
@@ -62,4 +62,3 @@
</form> </form>
</details> </details>
</section> </section>
+2 -3
View File
@@ -9,7 +9,7 @@
<span>Your staff account uses the same secure sign-in.</span> <span>Your staff account uses the same secure sign-in.</span>
</div> </div>
</div> </div>
<form class="auth-card form-stack" action="/login" method="post"> <form class="auth-card form-stack" action="<?= e(url('/login')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div> <div>
<span class="eyebrow">Account access</span> <span class="eyebrow">Account access</span>
@@ -29,8 +29,7 @@
</label> </label>
<button class="button button-large button-block" type="submit">Continue Securely</button> <button class="button button-large button-block" type="submit">Continue Securely</button>
<p class="form-footnote">We will email a six-digit verification code after your password is accepted.</p> <p class="form-footnote">We will email a six-digit verification code after your password is accepted.</p>
<p class="auth-switch">New here? <a href="/register">Create an account</a></p> <p class="auth-switch">New here? <a href="<?= e(url('/register')) ?>">Create an account</a></p>
</form> </form>
</div> </div>
</section> </section>
+2 -2
View File
@@ -10,7 +10,7 @@
<li>Your payment details are handled by Square, not stored here</li> <li>Your payment details are handled by Square, not stored here</li>
</ul> </ul>
</div> </div>
<form class="auth-card form-stack" action="/register" method="post"> <form class="auth-card form-stack" action="<?= e(url('/register')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div> <div>
<span class="eyebrow">Customer account</span> <span class="eyebrow">Customer account</span>
@@ -55,7 +55,7 @@
</label> </label>
<div class="notice">Creating an account opts you into occasional Fat Bottom Grille news. You can opt out from any email or My Account.</div> <div class="notice">Creating an account opts you into occasional Fat Bottom Grille news. You can opt out from any email or My Account.</div>
<button class="button button-large button-block" type="submit">Create My Account</button> <button class="button button-large button-block" type="submit">Create My Account</button>
<p class="auth-switch">Already have an account? <a href="/login">Sign in</a></p> <p class="auth-switch">Already have an account? <a href="<?= e(url('/login')) ?>">Sign in</a></p>
</form> </form>
</div> </div>
</section> </section>
+2 -3
View File
@@ -1,6 +1,6 @@
<section class="auth-section section"> <section class="auth-section section">
<div class="container narrow-container"> <div class="container narrow-container">
<form class="auth-card form-stack verify-card" action="/verify" method="post"> <form class="auth-card form-stack verify-card" action="<?= e(url('/verify')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="verification-icon">6</div> <div class="verification-icon">6</div>
<div> <div>
@@ -12,8 +12,7 @@
<input class="code-input" type="text" name="code" required inputmode="numeric" maxlength="6" pattern="[0-9]{6}" autocomplete="one-time-code" autofocus> <input class="code-input" type="text" name="code" required inputmode="numeric" maxlength="6" pattern="[0-9]{6}" autocomplete="one-time-code" autofocus>
</label> </label>
<button class="button button-large button-block" type="submit">Verify & Sign In</button> <button class="button button-large button-block" type="submit">Verify & Sign In</button>
<a class="text-link text-center" href="/login">Start over</a> <a class="text-link text-center" href="<?= e(url('/login')) ?>">Start over</a>
</form> </form>
</div> </div>
</section> </section>
+14 -12
View File
@@ -16,7 +16,7 @@ $isAdmin = isset($adminPage);
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600;700;800&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600;700;800&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/app.css"> <link rel="stylesheet" href="<?= e(asset('assets/app.css')) ?>">
<?php if (!empty($squareEnabled) && !empty($squareAppId) && !empty($squareLocationId)): ?> <?php if (!empty($squareEnabled) && !empty($squareAppId) && !empty($squareLocationId)): ?>
<script src="<?= $squareEnvironment === 'production' <script src="<?= $squareEnvironment === 'production'
? 'https://web.squarecdn.com/v1/square.js' ? 'https://web.squarecdn.com/v1/square.js'
@@ -36,8 +36,8 @@ $isAdmin = isset($adminPage);
<h1><?= e($title ?? 'Dashboard') ?></h1> <h1><?= e($title ?? 'Dashboard') ?></h1>
</div> </div>
<div class="admin-top-actions"> <div class="admin-top-actions">
<a class="button button-ghost button-small" href="/" target="_blank" rel="noopener">View Store</a> <a class="button button-ghost button-small" href="<?= e(url('/')) ?>" target="_blank" rel="noopener">View Store</a>
<form action="/logout" method="post"> <form action="<?= e(url('/logout')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<button class="button button-small" type="submit">Sign Out</button> <button class="button button-small" type="submit">Sign Out</button>
</form> </form>
@@ -56,8 +56,10 @@ $isAdmin = isset($adminPage);
</div> </div>
<header class="site-header"> <header class="site-header">
<div class="container nav-wrap"> <div class="container nav-wrap">
<a class="brand" href="/" aria-label="<?= e($businessName) ?> home"> <a class="brand" href="<?= e(url('/')) ?>" aria-label="<?= e($businessName) ?> home">
<span class="brand-mark">FB</span> <span class="brand-mark">
<img src="<?= e(asset('assets/images/fat-bottom-grille-logo.jpg')) ?>" alt="">
</span>
<span class="brand-copy"> <span class="brand-copy">
<strong><?= e($businessName) ?></strong> <strong><?= e($businessName) ?></strong>
<small>Keyser, West Virginia</small> <small>Keyser, West Virginia</small>
@@ -68,17 +70,17 @@ $isAdmin = isset($adminPage);
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
</button> </button>
<nav class="site-nav" id="site-nav" aria-label="Main navigation"> <nav class="site-nav" id="site-nav" aria-label="Main navigation">
<a class="<?= active_path('/') ?>" href="/">Home</a> <a class="<?= active_path('/') ?>" href="<?= e(url('/')) ?>">Home</a>
<a class="<?= active_path('/menu') ?>" href="/menu">Order Online</a> <a class="<?= active_path('/menu') ?>" href="<?= e(url('/menu')) ?>">Order Online</a>
<?php if ($currentUser): ?> <?php if ($currentUser): ?>
<?php if (in_array($currentUser['role'], ['admin', 'manager', 'staff'], true)): ?> <?php if (in_array($currentUser['role'], ['admin', 'manager', 'staff'], true)): ?>
<a href="/admin">Dashboard</a> <a href="<?= e(url('/admin')) ?>">Dashboard</a>
<?php endif; ?> <?php endif; ?>
<a class="<?= active_path('/account') ?>" href="/account">My Account</a> <a class="<?= active_path('/account') ?>" href="<?= e(url('/account')) ?>">My Account</a>
<?php else: ?> <?php else: ?>
<a class="<?= active_path('/login') ?>" href="/login">Sign In</a> <a class="<?= active_path('/login') ?>" href="<?= e(url('/login')) ?>">Sign In</a>
<?php endif; ?> <?php endif; ?>
<a class="cart-link" href="/cart"> <a class="cart-link" href="<?= e(url('/cart')) ?>">
Your Order Your Order
<span class="cart-count"><?= (int) ($currentCart['item_count'] ?? 0) ?></span> <span class="cart-count"><?= (int) ($currentCart['item_count'] ?? 0) ?></span>
</a> </a>
@@ -129,6 +131,6 @@ $isAdmin = isset($adminPage);
</footer> </footer>
<?php endif; ?> <?php endif; ?>
<script src="/assets/app.js" defer></script> <script src="<?= e(asset('assets/app.js')) ?>" defer></script>
</body> </body>
</html> </html>
+11 -10
View File
@@ -1,20 +1,22 @@
<aside class="admin-sidebar"> <aside class="admin-sidebar">
<a class="admin-brand" href="/admin"> <a class="admin-brand" href="<?= e(url('/admin')) ?>">
<span class="brand-mark">FB</span> <span class="brand-mark">
<img src="<?= e(asset('assets/images/fat-bottom-grille-logo.jpg')) ?>" alt="">
</span>
<span> <span>
<strong><?= e($config->get('business.name')) ?></strong> <strong><?= e($config->get('business.name')) ?></strong>
<small>Operations</small> <small>Operations</small>
</span> </span>
</a> </a>
<nav class="admin-nav" aria-label="Dashboard navigation"> <nav class="admin-nav" aria-label="Dashboard navigation">
<a class="<?= $adminPage === 'dashboard' ? 'is-active' : '' ?>" href="/admin">Overview</a> <a class="<?= $adminPage === 'dashboard' ? 'is-active' : '' ?>" href="<?= e(url('/admin')) ?>">Overview</a>
<a class="<?= $adminPage === 'orders' ? 'is-active' : '' ?>" href="/admin/orders">Orders</a> <a class="<?= $adminPage === 'orders' ? 'is-active' : '' ?>" href="<?= e(url('/admin/orders')) ?>">Orders</a>
<a class="<?= $adminPage === 'menu' ? 'is-active' : '' ?>" href="/admin/menu">Menu</a> <a class="<?= $adminPage === 'menu' ? 'is-active' : '' ?>" href="<?= e(url('/admin/menu')) ?>">Menu</a>
<a class="<?= $adminPage === 'specials' ? 'is-active' : '' ?>" href="/admin/specials">Specials</a> <a class="<?= $adminPage === 'specials' ? 'is-active' : '' ?>" href="<?= e(url('/admin/specials')) ?>">Specials</a>
<a class="<?= $adminPage === 'newsletters' ? 'is-active' : '' ?>" href="/admin/newsletters">Newsletters</a> <a class="<?= $adminPage === 'newsletters' ? 'is-active' : '' ?>" href="<?= e(url('/admin/newsletters')) ?>">Newsletters</a>
<?php if (($currentUser['role'] ?? '') === 'admin'): ?> <?php if (($currentUser['role'] ?? '') === 'admin'): ?>
<a class="<?= $adminPage === 'users' ? 'is-active' : '' ?>" href="/admin/users">Users & Staff</a> <a class="<?= $adminPage === 'users' ? 'is-active' : '' ?>" href="<?= e(url('/admin/users')) ?>">Users & Staff</a>
<a class="<?= $adminPage === 'settings' ? 'is-active' : '' ?>" href="/admin/settings">Settings</a> <a class="<?= $adminPage === 'settings' ? 'is-active' : '' ?>" href="<?= e(url('/admin/settings')) ?>">Settings</a>
<?php endif; ?> <?php endif; ?>
</nav> </nav>
<div class="admin-user"> <div class="admin-user">
@@ -25,4 +27,3 @@
</span> </span>
</div> </div>
</aside> </aside>
+2 -3
View File
@@ -21,10 +21,10 @@ $tags = is_array($tags) ? $tags : [];
<?php foreach ($tags as $tag): ?><span><?= e($tag) ?></span><?php endforeach; ?> <?php foreach ($tags as $tag): ?><span><?= e($tag) ?></span><?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<form class="quick-add" action="/cart/add" method="post"> <form class="quick-add" action="<?= e(url('/cart/add')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="menu_item_id" value="<?= (int) $item['id'] ?>"> <input type="hidden" name="menu_item_id" value="<?= (int) $item['id'] ?>">
<input type="hidden" name="return_to" value="<?= e($_SERVER['REQUEST_URI'] ?? '/menu') ?>"> <input type="hidden" name="return_to" value="<?= e(\FatBottom\Core\Http::path()) ?>">
<label> <label>
<span class="sr-only">Quantity</span> <span class="sr-only">Quantity</span>
<select name="quantity" aria-label="Quantity for <?= e($item['name']) ?>"> <select name="quantity" aria-label="Quantity for <?= e($item['name']) ?>">
@@ -37,4 +37,3 @@ $tags = is_array($tags) ? $tags : [];
</form> </form>
</div> </div>
</article> </article>
+5 -6
View File
@@ -11,10 +11,10 @@
<div class="empty-state"> <div class="empty-state">
<h2>Your order is wide open.</h2> <h2>Your order is wide open.</h2>
<p>Start with a burger, a burrito, or whatever sounds good right now.</p> <p>Start with a burger, a burrito, or whatever sounds good right now.</p>
<a class="button" href="/menu">Browse the Menu</a> <a class="button" href="<?= e(url('/menu')) ?>">Browse the Menu</a>
</div> </div>
<?php else: ?> <?php else: ?>
<form action="/cart/update" method="post"> <form action="<?= e(url('/cart/update')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="cart-list"> <div class="cart-list">
<?php foreach ($cart['items'] as $item): ?> <?php foreach ($cart['items'] as $item): ?>
@@ -30,12 +30,12 @@
<input type="number" min="0" max="20" name="quantity[<?= (int) $item['id'] ?>]" value="<?= (int) $item['quantity'] ?>"> <input type="number" min="0" max="20" name="quantity[<?= (int) $item['id'] ?>]" value="<?= (int) $item['quantity'] ?>">
</label> </label>
<strong class="cart-line-total"><?= money((int) $item['quantity'] * (int) $item['unit_price_cents']) ?></strong> <strong class="cart-line-total"><?= money((int) $item['quantity'] * (int) $item['unit_price_cents']) ?></strong>
<button class="icon-button remove-cart-item" type="submit" formaction="/cart/remove" name="cart_item_id" value="<?= (int) $item['id'] ?>" aria-label="Remove <?= e($item['name']) ?>">&times;</button> <button class="icon-button remove-cart-item" type="submit" formaction="<?= e(url('/cart/remove')) ?>" name="cart_item_id" value="<?= (int) $item['id'] ?>" aria-label="Remove <?= e($item['name']) ?>">&times;</button>
</article> </article>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<div class="cart-actions"> <div class="cart-actions">
<a class="text-link" href="/menu">Add More Items</a> <a class="text-link" href="<?= e(url('/menu')) ?>">Add More Items</a>
<button class="button button-outline" type="submit">Update Order</button> <button class="button button-outline" type="submit">Update Order</button>
</div> </div>
</form> </form>
@@ -50,9 +50,8 @@
<div><dt>Estimated tax</dt><dd><?= money($totals['tax_cents']) ?></dd></div> <div><dt>Estimated tax</dt><dd><?= money($totals['tax_cents']) ?></dd></div>
<div class="summary-total"><dt>Total</dt><dd><?= money($totals['total_cents']) ?></dd></div> <div class="summary-total"><dt>Total</dt><dd><?= money($totals['total_cents']) ?></dd></div>
</dl> </dl>
<a class="button button-block button-large" href="/checkout">Continue to Checkout</a> <a class="button button-block button-large" href="<?= e(url('/checkout')) ?>">Continue to Checkout</a>
<p class="summary-note">Pickup at 410 W Piedmont St, Keyser.</p> <p class="summary-note">Pickup at 410 W Piedmont St, Keyser.</p>
</aside> </aside>
<?php endif; ?> <?php endif; ?>
</div> </div>
+1 -2
View File
@@ -8,7 +8,7 @@
<div class="container checkout-layout section"> <div class="container checkout-layout section">
<section class="checkout-main"> <section class="checkout-main">
<form id="checkout-form" class="panel form-stack" <form id="checkout-form" class="panel form-stack"
action="/checkout" method="post" action="<?= e(url('/checkout')) ?>" method="post"
data-square-enabled="<?= $squareEnabled ? 'true' : 'false' ?>" data-square-enabled="<?= $squareEnabled ? 'true' : 'false' ?>"
data-square-app-id="<?= e($squareAppId) ?>" data-square-app-id="<?= e($squareAppId) ?>"
data-square-location-id="<?= e($squareLocationId) ?>"> data-square-location-id="<?= e($squareLocationId) ?>">
@@ -95,4 +95,3 @@
<p class="summary-note">Estimated ready time: <?= (int) $config->get('ordering.pickup_eta_minutes', 25) ?> minutes.</p> <p class="summary-note">Estimated ready time: <?= (int) $config->get('ordering.pickup_eta_minutes', 25) ?> minutes.</p>
</aside> </aside>
</div> </div>
+12 -16
View File
@@ -6,7 +6,7 @@
<h1>Big flavor.<br><em>No shortcuts.</em></h1> <h1>Big flavor.<br><em>No shortcuts.</em></h1>
<p>Stacked burgers, loaded burritos, and dinner plates that eat like Sunday supper. Order ahead and we will have it hot.</p> <p>Stacked burgers, loaded burritos, and dinner plates that eat like Sunday supper. Order ahead and we will have it hot.</p>
<div class="hero-actions"> <div class="hero-actions">
<a class="button button-large" href="/menu">Start Your Order</a> <a class="button button-large" href="<?= e(url('/menu')) ?>">Start Your Order</a>
<?php if ($config->get('business.phone')): ?> <?php if ($config->get('business.phone')): ?>
<a class="text-link" href="tel:<?= e(preg_replace('/[^0-9+]/', '', (string) $config->get('business.phone'))) ?>"> <a class="text-link" href="tel:<?= e(preg_replace('/[^0-9+]/', '', (string) $config->get('business.phone'))) ?>">
Call <?= e($config->get('business.phone')) ?> Call <?= e($config->get('business.phone')) ?>
@@ -18,19 +18,15 @@
<span><?= (bool) $config->get('ordering.accepting_orders', true) ? 'Ordering now open' : 'Ordering currently paused' ?></span> <span><?= (bool) $config->get('ordering.accepting_orders', true) ? 'Ordering now open' : 'Ordering currently paused' ?></span>
</div> </div>
</div> </div>
<div class="hero-plate" aria-hidden="true"> <div class="hero-plate">
<div class="plate-ring"> <div class="hero-food-photo">
<div class="burger"> <img src="<?= e(asset('assets/images/chicken-sandwich-fries.jpg')) ?>" alt="Chicken sandwich and hand-cut fries from Fat Bottom Grille">
<span class="bun bun-top"></span> <div class="hero-photo-caption">
<span class="lettuce"></span> <span>Made in Keyser</span>
<span class="cheese"></span> <strong>Hot. Fresh. Generous.</strong>
<span class="patty"></span>
<span class="onion"></span>
<span class="patty patty-two"></span>
<span class="bun bun-bottom"></span>
</div> </div>
</div> </div>
<div class="hero-stamp"> <div class="hero-stamp" aria-hidden="true">
<strong>410</strong> <strong>410</strong>
<span>W Piedmont</span> <span>W Piedmont</span>
</div> </div>
@@ -46,7 +42,7 @@
<span class="eyebrow">Happening now</span> <span class="eyebrow">Happening now</span>
<h2>Todays Specials</h2> <h2>Todays Specials</h2>
</div> </div>
<a class="text-link" href="/menu">See the full menu</a> <a class="text-link" href="<?= e(url('/menu')) ?>">See the full menu</a>
</div> </div>
<div class="special-grid special-count-<?= min(count($specials), 4) ?>"> <div class="special-grid special-count-<?= min(count($specials), 4) ?>">
<?php foreach ($specials as $special): ?> <?php foreach ($specials as $special): ?>
@@ -59,7 +55,7 @@
<strong><?= money($special['price_cents']) ?></strong> <strong><?= money($special['price_cents']) ?></strong>
<?php endif; ?> <?php endif; ?>
<?php if ($special['menu_item_id']): ?> <?php if ($special['menu_item_id']): ?>
<form action="/cart/add" method="post"> <form action="<?= e(url('/cart/add')) ?>" method="post">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="menu_item_id" value="<?= (int) $special['menu_item_id'] ?>"> <input type="hidden" name="menu_item_id" value="<?= (int) $special['menu_item_id'] ?>">
<input type="hidden" name="quantity" value="1"> <input type="hidden" name="quantity" value="1">
@@ -97,7 +93,7 @@
<span class="eyebrow">Pick your direction</span> <span class="eyebrow">Pick your direction</span>
<div class="category-links"> <div class="category-links">
<?php foreach ($categories as $category): ?> <?php foreach ($categories as $category): ?>
<a href="/menu#<?= e($category['slug']) ?>"> <a href="<?= e(url('/menu')) ?>#<?= e($category['slug']) ?>">
<strong><?= e($category['name']) ?></strong> <strong><?= e($category['name']) ?></strong>
<span><?= count($category['items']) ?> picks</span> <span><?= count($category['items']) ?> picks</span>
</a> </a>
@@ -116,7 +112,7 @@
<span class="eyebrow">Proudly from Keyser</span> <span class="eyebrow">Proudly from Keyser</span>
<h2>Food that shows up hungry.</h2> <h2>Food that shows up hungry.</h2>
<p>Fat Bottom Grille is the kind of place where the portions are honest and nobody leaves wondering what is for dinner. Find us at 410 W Piedmont Street, right here in Keyser.</p> <p>Fat Bottom Grille is the kind of place where the portions are honest and nobody leaves wondering what is for dinner. Find us at 410 W Piedmont Street, right here in Keyser.</p>
<a class="button button-outline" href="/menu">Order for Pickup</a> <a class="button button-outline" href="<?= e(url('/menu')) ?>">Order for Pickup</a>
</div> </div>
</div> </div>
</section> </section>
+2 -3
View File
@@ -5,7 +5,7 @@
<h1>Order Online</h1> <h1>Order Online</h1>
<p>Choose your favorites and we will get the kitchen moving.</p> <p>Choose your favorites and we will get the kitchen moving.</p>
</div> </div>
<form class="menu-search" action="/menu" method="get"> <form class="menu-search" action="<?= e(url('/menu')) ?>" method="get">
<label for="menu-query">Search the menu</label> <label for="menu-query">Search the menu</label>
<div class="search-row"> <div class="search-row">
<input id="menu-query" type="search" name="q" value="<?= e($query) ?>" placeholder="Burger, chicken, fries..."> <input id="menu-query" type="search" name="q" value="<?= e($query) ?>" placeholder="Burger, chicken, fries...">
@@ -29,7 +29,7 @@
<span class="eyebrow">No match yet</span> <span class="eyebrow">No match yet</span>
<h2>We could not find <?= e($query) ?>”</h2> <h2>We could not find <?= e($query) ?>”</h2>
<p>Try another search or browse the full menu.</p> <p>Try another search or browse the full menu.</p>
<a class="button" href="/menu">Clear Search</a> <a class="button" href="<?= e(url('/menu')) ?>">Clear Search</a>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php foreach ($categories as $category): ?> <?php foreach ($categories as $category): ?>
@@ -49,4 +49,3 @@
</section> </section>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
+2 -3
View File
@@ -15,9 +15,8 @@
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<div class="confirmation-actions"> <div class="confirmation-actions">
<a class="button" href="/menu">Order Something Else</a> <a class="button" href="<?= e(url('/menu')) ?>">Order Something Else</a>
<?php if ($currentUser): ?><a class="button button-outline" href="/account">View My Orders</a><?php endif; ?> <?php if ($currentUser): ?><a class="button button-outline" href="<?= e(url('/account')) ?>">View My Orders</a><?php endif; ?>
</div> </div>
</div> </div>
</section> </section>