- Implemented the full order lifecycle, secure customer tracker, automatic acceptance emails, configurable status templates, and guest/account CRM management.

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.
This commit is contained in:
Ty Clifford
2026-06-14 13:35:31 -04:00
parent 3417589a7c
commit 03348cad79
24 changed files with 1218 additions and 72 deletions
+73
View File
@@ -0,0 +1,73 @@
<div class="detail-heading">
<div>
<a class="back-link" href="<?= e(url('/admin/customers')) ?>">&larr; Back to customers</a>
<div class="detail-title-row">
<h2><?= e($customer['full_name']) ?></h2>
<span class="status-dot <?= $customer['active'] ? 'is-on' : '' ?>"><?= $customer['active'] ? 'Active' : 'Deactivated' ?></span>
</div>
<p>Customer since <?= e(store_datetime((string) $customer['created_at'], 'F j, Y')) ?></p>
</div>
<strong class="detail-total"><?= money($customer['lifetime_value_cents']) ?></strong>
</div>
<div class="crm-stat-grid">
<div><span>Orders</span><strong><?= (int) $customer['order_count'] ?></strong></div>
<div><span>Lifetime value</span><strong><?= money($customer['lifetime_value_cents']) ?></strong></div>
<div><span>First order</span><strong><?= $customer['first_order_at'] ? e(store_datetime((string) $customer['first_order_at'], 'M j, Y')) : '—' ?></strong></div>
<div><span>Account</span><strong><?= $customer['user_id'] ? 'Registered' : 'Guest' ?></strong></div>
</div>
<div class="admin-detail-grid">
<section class="admin-panel">
<div class="admin-panel-heading"><h2>Order History</h2></div>
<div class="table-wrap">
<table>
<thead><tr><th>Order</th><th>Placed</th><th>Status</th><th>Total</th><th></th></tr></thead>
<tbody>
<?php if (!$orders): ?><tr><td colspan="5" class="empty-cell">No linked orders.</td></tr><?php endif; ?>
<?php foreach ($orders as $order): ?>
<tr>
<td><strong><?= e($order['order_number']) ?></strong></td>
<td><?= e(store_datetime((string) $order['placed_at'], 'M j, Y g:i A')) ?></td>
<td><span class="status-pill status-<?= e($order['status']) ?>"><?= e(order_status_label((string) $order['status'])) ?></span></td>
<td><?= money($order['total_cents']) ?></td>
<td><a href="<?= e(url('/admin/order')) ?>?id=<?= (int) $order['id'] ?>">Open</a></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
<aside>
<section class="admin-panel">
<div class="admin-panel-heading"><h2>CRM Profile</h2></div>
<form class="form-stack" action="<?= e(url('/admin/customer/save')) ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" name="customer_id" value="<?= (int) $customer['id'] ?>">
<label>Full name<input type="text" name="full_name" required value="<?= e($customer['full_name']) ?>"></label>
<label>Email<input type="email" value="<?= e($customer['email']) ?>" disabled></label>
<label>Phone<input type="tel" name="phone" value="<?= e($customer['phone']) ?>"></label>
<label>Internal CRM notes<textarea name="internal_notes" rows="5" placeholder="Preferences, service notes, follow-up details..."><?= e($customer['internal_notes']) ?></textarea></label>
<label class="check-control compact"><input type="checkbox" name="active" value="1" <?= checked($customer['active']) ?>><span><strong>Active customer</strong><small>Deactivate without removing order history.</small></span></label>
<button class="button button-block" type="submit">Save Customer</button>
</form>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><h2>Contact & Account</h2></div>
<div class="customer-card">
<a href="mailto:<?= e($customer['email']) ?>"><?= e($customer['email']) ?></a>
<?php if ($customer['phone']): ?><a href="tel:<?= e(preg_replace('/[^0-9+]/', '', (string) $customer['phone'])) ?>"><?= e($customer['phone']) ?></a><?php endif; ?>
<span><?= $customer['user_id'] ? 'Customer account: ' . e($customer['account_active'] ? 'active' : 'disabled') : 'No customer account' ?></span>
</div>
</section>
<section class="admin-panel">
<div class="admin-panel-heading"><h2>Delete CRM Record</h2></div>
<p class="muted">This removes the CRM profile but keeps every historical order. A future order with this email will create a new profile.</p>
<form action="<?= e(url('/admin/customer/delete')) ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" name="customer_id" value="<?= (int) $customer['id'] ?>">
<button class="button button-danger button-block" type="submit" data-confirm="Delete this customer CRM record? Historical orders will remain.">Delete Customer</button>
</form>
</section>
</aside>
</div>