- Implemented the full Fat Bottom Grille storefront and staff dashboard.
Highlights include configurable menus, specials, cart/checkout, taxes, customer accounts with email 2FA, newsletters, analytics, refunds, PDF/CSV exports, Square/Mailgun adapters, and optional MySQL migration. See README.md for setup and production configuration. Verified PHP/JavaScript syntax, responsive layouts, registration, 2FA, checkout, dashboard workflows, settings persistence, and PDF generation. Real Square, Mailgun, and MySQL connections require credentials/server access. Square implementation follows the official Payments API and Refunds API.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<div class="admin-page-actions">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="admin-stack">
|
||||
<section class="admin-panel">
|
||||
<div class="admin-panel-heading">
|
||||
<div><span class="eyebrow">Organize the storefront</span><h2>Menu Categories</h2></div>
|
||||
<span class="count-badge"><?= count($categories) ?> categories</span>
|
||||
</div>
|
||||
<div class="editable-list">
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<details>
|
||||
<summary>
|
||||
<span class="drag-index"><?= (int) $category['display_order'] ?></span>
|
||||
<span><strong><?= e($category['name']) ?></strong><small><?= e($category['description']) ?></small></span>
|
||||
<span class="status-dot <?= $category['active'] ? 'is-on' : '' ?>"><?= $category['active'] ? 'Active' : 'Hidden' ?></span>
|
||||
<span class="edit-label">Edit</span>
|
||||
</summary>
|
||||
<form class="inline-editor form-stack" action="/admin/category/save" method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $category['id'] ?>">
|
||||
<div class="form-grid">
|
||||
<label>Name<input type="text" name="name" required value="<?= e($category['name']) ?>"></label>
|
||||
<label>URL slug<input type="text" name="slug" required value="<?= e($category['slug']) ?>"></label>
|
||||
<label class="field-wide">Description<input type="text" name="description" value="<?= e($category['description']) ?>"></label>
|
||||
<label>Display order<input type="number" name="display_order" value="<?= (int) $category['display_order'] ?>"></label>
|
||||
<label class="check-control compact"><input type="checkbox" name="active" value="1" <?= checked($category['active']) ?>><span><strong>Visible</strong></span></label>
|
||||
</div>
|
||||
<button class="button button-small" type="submit">Save Category</button>
|
||||
</form>
|
||||
</details>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<details class="new-record">
|
||||
<summary>+ Add Category</summary>
|
||||
<form class="inline-editor form-stack" action="/admin/category/save" method="post">
|
||||
<?= csrf_field() ?>
|
||||
<div class="form-grid">
|
||||
<label>Name<input type="text" name="name" required></label>
|
||||
<label>URL slug<input type="text" name="slug" placeholder="Generated from name if blank"></label>
|
||||
<label class="field-wide">Description<input type="text" name="description"></label>
|
||||
<label>Display order<input type="number" name="display_order" value="60"></label>
|
||||
<label class="check-control compact"><input type="checkbox" name="active" value="1" checked><span><strong>Visible</strong></span></label>
|
||||
</div>
|
||||
<button class="button button-small" type="submit">Create Category</button>
|
||||
</form>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="admin-panel">
|
||||
<div class="admin-panel-heading">
|
||||
<div><span class="eyebrow">Price and availability</span><h2>Menu Items</h2></div>
|
||||
<span class="count-badge"><?= count($items) ?> items</span>
|
||||
</div>
|
||||
<div class="editable-list item-editable-list">
|
||||
<?php foreach ($items as $item): ?>
|
||||
<?php $tags = json_decode((string) $item['dietary_tags'], true); ?>
|
||||
<details>
|
||||
<summary>
|
||||
<span class="item-avatar"><?= e(strtoupper(substr((string) $item['name'], 0, 2))) ?></span>
|
||||
<span><strong><?= e($item['name']) ?></strong><small><?= e($item['category_name']) ?></small></span>
|
||||
<strong class="item-price"><?= money($item['price_cents']) ?></strong>
|
||||
<span class="status-dot <?= $item['active'] ? 'is-on' : '' ?>"><?= $item['active'] ? 'Active' : 'Hidden' ?></span>
|
||||
<span class="edit-label">Edit</span>
|
||||
</summary>
|
||||
<form class="inline-editor form-stack" action="/admin/item/save" method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $item['id'] ?>">
|
||||
<div class="form-grid form-grid-three">
|
||||
<label>Name<input type="text" name="name" required value="<?= e($item['name']) ?>"></label>
|
||||
<label>Category
|
||||
<select name="category_id" required>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<option value="<?= (int) $category['id'] ?>" <?= selected($item['category_id'], $category['id']) ?>><?= e($category['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>Price<input type="number" name="price" min="0" step="0.01" required value="<?= number_format((int) $item['price_cents'] / 100, 2, '.', '') ?>"></label>
|
||||
<label>Compare-at price<input type="number" name="compare_at_price" min="0" step="0.01" value="<?= $item['compare_at_cents'] !== null ? number_format((int) $item['compare_at_cents'] / 100, 2, '.', '') : '' ?>"></label>
|
||||
<label>URL slug<input type="text" name="slug" value="<?= e($item['slug']) ?>"></label>
|
||||
<label>Display order<input type="number" name="display_order" value="<?= (int) $item['display_order'] ?>"></label>
|
||||
<label class="field-wide">Description<textarea name="description" rows="2"><?= e($item['description']) ?></textarea></label>
|
||||
<label class="field-wide">Image URL<input type="url" name="image_url" value="<?= e($item['image_url']) ?>" placeholder="Optional HTTPS image"></label>
|
||||
<label class="field-wide">Tags, comma separated<input type="text" name="dietary_tags" value="<?= e(is_array($tags) ? implode(', ', $tags) : '') ?>"></label>
|
||||
</div>
|
||||
<div class="check-row">
|
||||
<label class="check-control compact"><input type="checkbox" name="active" value="1" <?= checked($item['active']) ?>><span><strong>Available</strong></span></label>
|
||||
<label class="check-control compact"><input type="checkbox" name="featured" value="1" <?= checked($item['featured']) ?>><span><strong>Featured</strong></span></label>
|
||||
</div>
|
||||
<button class="button button-small" type="submit">Save Item</button>
|
||||
</form>
|
||||
</details>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<details class="new-record">
|
||||
<summary>+ Add Menu Item</summary>
|
||||
<form class="inline-editor form-stack" action="/admin/item/save" method="post">
|
||||
<?= csrf_field() ?>
|
||||
<div class="form-grid form-grid-three">
|
||||
<label>Name<input type="text" name="name" required></label>
|
||||
<label>Category
|
||||
<select name="category_id" required>
|
||||
<?php foreach ($categories as $category): ?><option value="<?= (int) $category['id'] ?>"><?= e($category['name']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>Price<input type="number" name="price" min="0" step="0.01" required></label>
|
||||
<label>Compare-at price<input type="number" name="compare_at_price" min="0" step="0.01"></label>
|
||||
<label>URL slug<input type="text" name="slug" placeholder="Generated from name"></label>
|
||||
<label>Display order<input type="number" name="display_order" value="0"></label>
|
||||
<label class="field-wide">Description<textarea name="description" rows="2"></textarea></label>
|
||||
<label class="field-wide">Image URL<input type="url" name="image_url"></label>
|
||||
<label class="field-wide">Tags, comma separated<input type="text" name="dietary_tags"></label>
|
||||
</div>
|
||||
<div class="check-row">
|
||||
<label class="check-control compact"><input type="checkbox" name="active" value="1" checked><span><strong>Available</strong></span></label>
|
||||
<label class="check-control compact"><input type="checkbox" name="featured" value="1"><span><strong>Featured</strong></span></label>
|
||||
</div>
|
||||
<button class="button button-small" type="submit">Create Item</button>
|
||||
</form>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user