Files
Ty Clifford b14f4c1796 - Implemented the Lakeside Orders demo rebrand:
New licensed lake, picnic, dining, server, burger, and food photography.
Rebuilt homepage gallery, logo, colors, copy, menu, specials, and 
exports.
Added dashboard-configurable branding in [settings.php (line 
18)](/Users/tyemeclifford/Documents/GH/fatbottomgrille/views/admin/settings.php:18).
Added a public attribution page and full [image 
credits](/Users/tyemeclifford/Documents/GH/fatbottomgrille/public/assets/images/CREDITS.md).
Migrated existing SQLite menu data and admin email to 
admin@lakesideorders.test.
Verified 51 PHP files, JSON/JavaScript, storefront pages, database 
migration, admin settings, and menu PDF. All passed.
2026-06-14 14:26:09 -04:00

165 lines
16 KiB
PHP

<form class="admin-stack" action="<?= e(url('/admin/settings')) ?>" method="post">
<?= csrf_field() ?>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Public identity</span><h2>Business Information</h2></div></div>
<div class="form-grid form-grid-three">
<label>Public site URL<input type="url" name="app[url]" required value="<?= e($config->get('app.url')) ?>"></label>
<label>Business name<input type="text" name="business[name]" required value="<?= e($config->get('business.name')) ?>"></label>
<label>Phone<input type="tel" name="business[phone]" value="<?= e($config->get('business.phone')) ?>"></label>
<label>Order email<input type="email" name="business[email]" value="<?= e($config->get('business.email')) ?>"></label>
<label>Street<input type="text" name="business[street]" value="<?= e($config->get('business.street')) ?>"></label>
<label>City<input type="text" name="business[city]" value="<?= e($config->get('business.city')) ?>"></label>
<label>State<input type="text" name="business[state]" value="<?= e($config->get('business.state')) ?>"></label>
<label>ZIP code<input type="text" name="business[postal_code]" value="<?= e($config->get('business.postal_code')) ?>"></label>
<label>Published hours<input type="text" name="business[hours]" value="<?= e($config->get('business.hours')) ?>"></label>
<label class="field-wide">Announcement bar<input type="text" name="business[announcement]" value="<?= e($config->get('business.announcement')) ?>"></label>
</div>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Storefront presentation</span><h2>Branding & Homepage</h2></div></div>
<div class="notice">Edit the demo message and active image paths here. Image files live in <code>public/assets/images</code>.</div>
<div class="form-grid">
<label>Location label<input type="text" name="branding[location_label]" value="<?= e($config->get('branding.location_label')) ?>"></label>
<label>Hero eyebrow<input type="text" name="branding[hero_eyebrow]" value="<?= e($config->get('branding.hero_eyebrow')) ?>"></label>
<label>Hero heading<input type="text" name="branding[hero_heading]" value="<?= e($config->get('branding.hero_heading')) ?>"></label>
<label>Hero emphasized line<input type="text" name="branding[hero_emphasis]" value="<?= e($config->get('branding.hero_emphasis')) ?>"></label>
<label class="field-wide">Search description<textarea name="branding[meta_description]" rows="2"><?= e($config->get('branding.meta_description')) ?></textarea></label>
<label class="field-wide">Hero introduction<textarea name="branding[hero_intro]" rows="3"><?= e($config->get('branding.hero_intro')) ?></textarea></label>
<label>Hero food image<input type="text" name="branding[hero_image]" value="<?= e($config->get('branding.hero_image')) ?>"></label>
<label>Picnic gallery image<input type="text" name="branding[gallery_picnic_image]" value="<?= e($config->get('branding.gallery_picnic_image')) ?>"></label>
<label>Patio gallery image<input type="text" name="branding[gallery_patio_image]" value="<?= e($config->get('branding.gallery_patio_image')) ?>"></label>
<label>Service gallery image<input type="text" name="branding[gallery_service_image]" value="<?= e($config->get('branding.gallery_service_image')) ?>"></label>
<label>Story image<input type="text" name="branding[story_image]" value="<?= e($config->get('branding.story_image')) ?>"></label>
<label>Story eyebrow<input type="text" name="branding[story_eyebrow]" value="<?= e($config->get('branding.story_eyebrow')) ?>"></label>
<label>Story heading<input type="text" name="branding[story_heading]" value="<?= e($config->get('branding.story_heading')) ?>"></label>
<label class="field-wide">Story text<textarea name="branding[story_text]" rows="3"><?= e($config->get('branding.story_text')) ?></textarea></label>
<label class="field-wide">Footer tagline<input type="text" name="branding[footer_tagline]" value="<?= e($config->get('branding.footer_tagline')) ?>"></label>
</div>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Kitchen controls</span><h2>Online Ordering</h2></div></div>
<div class="settings-row">
<label class="check-control"><input type="checkbox" name="ordering[accepting_orders]" value="1" <?= checked($config->get('ordering.accepting_orders')) ?>><span><strong>Accept online orders</strong><small>Turn this off to pause checkout without hiding the menu.</small></span></label>
<label>Estimated pickup time, minutes<input type="number" min="5" max="180" name="ordering[pickup_eta_minutes]" value="<?= (int) $config->get('ordering.pickup_eta_minutes') ?>"></label>
</div>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Card processing</span><h2>Square</h2></div><span class="status-dot <?= $config->get('square.enabled') ? 'is-on' : '' ?>"><?= $config->get('square.enabled') ? 'Enabled' : 'Demo mode' ?></span></div>
<div class="notice">When disabled, checkout runs in demo mode and never charges a card. Enable only after entering matching Square application, location, and access credentials.</div>
<div class="form-grid">
<label>Environment<select name="square[environment]"><option value="sandbox" <?= selected($config->get('square.environment'), 'sandbox') ?>>Sandbox</option><option value="production" <?= selected($config->get('square.environment'), 'production') ?>>Production</option></select></label>
<label>Application ID<input type="text" name="square[application_id]" value="<?= e($config->get('square.application_id')) ?>" autocomplete="off"></label>
<label>Location ID<input type="text" name="square[location_id]" value="<?= e($config->get('square.location_id')) ?>" autocomplete="off"></label>
<label>Access token<input type="password" name="square[access_token]" value="" placeholder="<?= $config->get('square.access_token') ? 'Saved credential - leave blank to keep' : 'Enter access token' ?>" autocomplete="new-password"></label>
</div>
<label class="check-control"><input type="checkbox" name="square[enabled]" value="1" <?= checked($config->get('square.enabled')) ?>><span><strong>Enable Square payments</strong><small>Live checkout will require a valid card token.</small></span></label>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Transactional email</span><h2>Mailgun</h2></div><span class="status-dot <?= $config->get('mailgun.enabled') ? 'is-on' : '' ?>"><?= $config->get('mailgun.enabled') ? 'Enabled' : 'Local log mode' ?></span></div>
<div class="notice">When disabled, verification and newsletter messages are written to <code>storage/logs/mail.log</code> for local testing.</div>
<div class="form-grid">
<label>Mailgun domain<input type="text" name="mailgun[domain]" value="<?= e($config->get('mailgun.domain')) ?>"></label>
<label>API key<input type="password" name="mailgun[api_key]" value="" placeholder="<?= $config->get('mailgun.api_key') ? 'Saved credential - leave blank to keep' : 'Enter API key' ?>" autocomplete="new-password"></label>
<label>From name<input type="text" name="mailgun[from_name]" value="<?= e($config->get('mailgun.from_name')) ?>"></label>
<label>From email<input type="email" name="mailgun[from_email]" value="<?= e($config->get('mailgun.from_email')) ?>"></label>
</div>
<label class="check-control"><input type="checkbox" name="mailgun[enabled]" value="1" <?= checked($config->get('mailgun.enabled')) ?>><span><strong>Enable Mailgun delivery</strong></span></label>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Customer notifications</span><h2>Order Status Email Templates</h2></div></div>
<div class="notice">
Available placeholders: <code>{{customer_name}}</code>, <code>{{order_number}}</code>,
<code>{{status_label}}</code>, <code>{{note}}</code>, <code>{{rejection_reason}}</code>,
<code>{{tracking_url}}</code>, and <code>{{business_name}}</code>.
</div>
<div class="email-template-list">
<?php foreach (['pending', 'accepted', 'declined', 'processing', 'ready', 'completed'] as $emailStatus): ?>
<details <?= $emailStatus === 'accepted' ? 'open' : '' ?>>
<summary>
<strong><?= e(order_status_label($emailStatus)) ?></strong>
<span><?= $emailStatus === 'pending' ? 'Order receipt' : 'Status update' ?></span>
</summary>
<div class="inline-editor form-stack">
<label>Subject
<input type="text" name="order_emails[<?= e($emailStatus) ?>][subject]" required
value="<?= e($config->get('order_emails.' . $emailStatus . '.subject')) ?>">
</label>
<label>Message
<textarea name="order_emails[<?= e($emailStatus) ?>][body]" rows="6" required><?= e($config->get('order_emails.' . $emailStatus . '.body')) ?></textarea>
</label>
</div>
</details>
<?php endforeach; ?>
</div>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><div><span class="eyebrow">Optional database server</span><h2>MySQL / MariaDB Connection</h2></div></div>
<div class="notice">These values are only used by the migration tool below. The application continues using SQLite unless a migration succeeds and you explicitly switch drivers.</div>
<div class="form-grid form-grid-three">
<label>Host<input type="text" name="database[mysql_host]" value="<?= e($config->get('database.mysql_host')) ?>"></label>
<label>Port<input type="number" min="1" max="65535" name="database[mysql_port]" value="<?= (int) $config->get('database.mysql_port') ?>"></label>
<label>Database<input type="text" name="database[mysql_database]" value="<?= e($config->get('database.mysql_database')) ?>"></label>
<label>Username<input type="text" name="database[mysql_username]" value="<?= e($config->get('database.mysql_username')) ?>" autocomplete="off"></label>
<label>Password<input type="password" name="database[mysql_password]" value="" placeholder="<?= $config->get('database.mysql_password') ? 'Saved credential - leave blank to keep' : 'Enter database password' ?>" autocomplete="new-password"></label>
</div>
</section>
<div class="sticky-save"><button class="button button-large" type="submit">Save Store Settings</button></div>
</form>
<section class="admin-panel settings-followup">
<div class="admin-panel-heading"><div><span class="eyebrow">State and city</span><h2>Tax Rates</h2></div></div>
<div class="editable-list">
<?php foreach ($taxRates as $tax): ?>
<details>
<summary>
<span class="item-avatar">%</span>
<span><strong><?= e($tax['name']) ?></strong><small><?= e(ucfirst((string) $tax['jurisdiction'])) ?> jurisdiction</small></span>
<strong><?= number_format((int) $tax['rate_basis_points'] / 100, 2) ?>%</strong>
<span class="status-dot <?= $tax['active'] ? 'is-on' : '' ?>"><?= $tax['active'] ? 'Active' : 'Disabled' ?></span>
<span class="edit-label">Edit</span>
</summary>
<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'] ?>">
<div class="form-grid">
<label>Name<input type="text" name="name" value="<?= e($tax['name']) ?>" required></label>
<label>Jurisdiction<select name="jurisdiction"><option value="state" <?= selected($tax['jurisdiction'], 'state') ?>>State</option><option value="city" <?= selected($tax['jurisdiction'], 'city') ?>>City</option><option value="county" <?= selected($tax['jurisdiction'], 'county') ?>>County</option></select></label>
<label>Rate, percent<input type="number" min="0" step="0.01" name="rate" value="<?= number_format((int) $tax['rate_basis_points'] / 100, 2, '.', '') ?>"></label>
<label class="check-control compact"><input type="checkbox" name="active" value="1" <?= checked($tax['active']) ?>><span><strong>Active</strong></span></label>
</div>
<button class="button button-small" type="submit">Save Tax</button>
</form>
</details>
<?php endforeach; ?>
</div>
<details class="new-record">
<summary>+ Add Tax Rate</summary>
<form class="inline-editor form-stack" action="<?= e(url('/admin/tax/save')) ?>" method="post">
<?= csrf_field() ?>
<div class="form-grid">
<label>Name<input type="text" name="name" required></label>
<label>Jurisdiction<select name="jurisdiction"><option value="state">State</option><option value="city">City</option><option value="county">County</option></select></label>
<label>Rate, percent<input type="number" min="0" step="0.01" name="rate" required></label>
<label class="check-control compact"><input type="checkbox" name="active" value="1" checked><span><strong>Active</strong></span></label>
</div>
<button class="button button-small" type="submit">Create Tax Rate</button>
</form>
</details>
</section>
<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>
<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="<?= e(url('/admin/migrate/mysql')) ?>" method="post">
<?= csrf_field() ?>
<div class="form-grid form-grid-three">
<label>Host<input type="text" value="<?= e($config->get('database.mysql_host')) ?>" disabled></label>
<label>Port<input type="text" value="<?= e($config->get('database.mysql_port')) ?>" disabled></label>
<label>Database<input type="text" value="<?= e($config->get('database.mysql_database')) ?>" disabled></label>
<label>Username<input type="text" value="<?= e($config->get('database.mysql_username')) ?>" disabled></label>
</div>
<p class="form-footnote">Connection values are saved in the Store Settings form above before running migration.</p>
<label class="check-control"><input type="checkbox" name="switch_driver" value="1"><span><strong>Switch the application to MySQL after a successful copy</strong><small>Do this only when the MySQL service will remain available.</small></span></label>
<button class="button button-outline" type="submit" data-confirm="Copy the SQLite database to the configured MySQL server?">Run Database Migration</button>
</form>
</section>