- 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:
Ty Clifford
2026-06-10 13:27:42 -04:00
parent d46b0be488
commit 16235369cb
49 changed files with 7685 additions and 2 deletions
+36
View File
@@ -0,0 +1,36 @@
<section class="auth-section section">
<div class="container auth-grid">
<div class="auth-intro">
<span class="eyebrow">Welcome back</span>
<h1>Your usual is waiting.</h1>
<p>Sign in to check past orders, save your pickup information, and manage restaurant news.</p>
<div class="auth-callout">
<strong>Staff member?</strong>
<span>Your staff account uses the same secure sign-in.</span>
</div>
</div>
<form class="auth-card form-stack" action="/login" method="post">
<?= csrf_field() ?>
<div>
<span class="eyebrow">Account access</span>
<h2>Sign In</h2>
</div>
<label>Email address
<input type="email" name="email" required autocomplete="email" value="<?= e($old['email'] ?? '') ?>">
</label>
<label>Password
<input type="password" name="password" required autocomplete="current-password">
</label>
<div class="honeypot" aria-hidden="true">
<label>Website <input type="text" name="website" tabindex="-1" autocomplete="off"></label>
</div>
<label>Quick check: what is <?= (int) $captcha[0] ?> + <?= (int) $captcha[1] ?>?
<input type="number" name="captcha" required inputmode="numeric" autocomplete="off">
</label>
<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="auth-switch">New here? <a href="/register">Create an account</a></p>
</form>
</div>
</section>
+61
View File
@@ -0,0 +1,61 @@
<section class="auth-section section">
<div class="container auth-grid auth-grid-wide">
<div class="auth-intro">
<span class="eyebrow">Join the table</span>
<h1>Faster pickup starts here.</h1>
<p>Create an account to keep your contact details, see order history, and hear about new specials.</p>
<ul class="check-list">
<li>Email verification protects your account</li>
<li>Newsletter preferences stay in your control</li>
<li>Your payment details are handled by Square, not stored here</li>
</ul>
</div>
<form class="auth-card form-stack" action="/register" method="post">
<?= csrf_field() ?>
<div>
<span class="eyebrow">Customer account</span>
<h2>Create Account</h2>
</div>
<div class="form-grid">
<label>Full name
<input type="text" name="full_name" required autocomplete="name" value="<?= e($old['full_name'] ?? '') ?>">
</label>
<label>Phone number
<input type="tel" name="phone" required autocomplete="tel" value="<?= e($old['phone'] ?? '') ?>">
</label>
<label class="field-wide">Email address
<input type="email" name="email" required autocomplete="email" value="<?= e($old['email'] ?? '') ?>">
</label>
<label class="field-wide">Street address
<input type="text" name="street_address" required autocomplete="street-address" value="<?= e($old['street_address'] ?? '') ?>">
</label>
<label>City
<input type="text" name="city" required autocomplete="address-level2" value="<?= e($old['city'] ?? 'Keyser') ?>">
</label>
<label>State
<input type="text" name="state" required maxlength="2" autocomplete="address-level1" value="<?= e($old['state'] ?? 'WV') ?>">
</label>
<label>ZIP code
<input type="text" name="postal_code" required autocomplete="postal-code" value="<?= e($old['postal_code'] ?? '26726') ?>">
</label>
</div>
<div class="form-grid">
<label>Password
<input type="password" name="password" minlength="10" required autocomplete="new-password">
</label>
<label>Confirm password
<input type="password" name="password_confirmation" minlength="10" required autocomplete="new-password">
</label>
</div>
<div class="honeypot" aria-hidden="true">
<label>Website <input type="text" name="website" tabindex="-1" autocomplete="off"></label>
</div>
<label>Quick check: what is <?= (int) $captcha[0] ?> + <?= (int) $captcha[1] ?>?
<input type="number" name="captcha" required inputmode="numeric" autocomplete="off">
</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>
<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>
</form>
</div>
</section>
+19
View File
@@ -0,0 +1,19 @@
<section class="auth-section section">
<div class="container narrow-container">
<form class="auth-card form-stack verify-card" action="/verify" method="post">
<?= csrf_field() ?>
<div class="verification-icon">6</div>
<div>
<span class="eyebrow">One more step</span>
<h1>Check your email</h1>
<p>Enter the six-digit code we sent you. It is valid for 10 minutes.</p>
</div>
<label>Verification code
<input class="code-input" type="text" name="code" required inputmode="numeric" maxlength="6" pattern="[0-9]{6}" autocomplete="one-time-code" autofocus>
</label>
<button class="button button-large button-block" type="submit">Verify & Sign In</button>
<a class="text-link text-center" href="/login">Start over</a>
</form>
</div>
</section>