03348cad79
Key areas: [OrderStatusService.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Services/OrderStatusService.php), [AdminController.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Controllers/AdminController.php), and [Database.php](/Users/tyemeclifford/Documents/GH/fatbottomgrille/app/Core/Database.php). SQLite migration applied successfully. PHP/JS syntax, integration workflows, rendered pages, database integrity, and tracker authorization all passed. Invalid tracker tokens correctly return 403.
144 lines
13 KiB
PHP
144 lines
13 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">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>
|