mirror of
https://github.com/myronblair/tomsjavajive-app
synced 2026-06-30 17:50:56 -05:00
392 lines
17 KiB
PHP
392 lines
17 KiB
PHP
<?php
|
|
ob_start();
|
|
/**
|
|
* Tom's Java Jive - Admin Customers Management
|
|
*/
|
|
|
|
$pageTitle = 'Customers';
|
|
require_once __DIR__ . '/includes/header.php';
|
|
|
|
// Handle actions
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
if ($action === 'create') {
|
|
$email = strtolower(trim($_POST['email'] ?? ''));
|
|
$name = trim($_POST['name'] ?? '');
|
|
$phone = trim($_POST['phone'] ?? '');
|
|
$walletBalance = floatval($_POST['wallet_balance'] ?? 0);
|
|
$rewardPoints = intval($_POST['reward_points'] ?? 0);
|
|
|
|
if (empty($email)) {
|
|
setFlash('error', 'Email is required');
|
|
} else {
|
|
$existing = db()->fetch("SELECT id FROM customers WHERE email = :email", ['email' => $email]);
|
|
if ($existing) {
|
|
setFlash('error', 'Customer with this email already exists');
|
|
} else {
|
|
db()->insert('customers', [
|
|
'customer_id' => generateId('cust_'),
|
|
'email' => $email,
|
|
'name' => $name ?: null,
|
|
'phone' => $phone ?: null,
|
|
'wallet_balance' => $walletBalance,
|
|
'reward_points' => $rewardPoints,
|
|
'is_active' => 1
|
|
]);
|
|
setFlash('success', 'Customer created successfully');
|
|
}
|
|
}
|
|
header('Location: /admin/customers.php');
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'update' && !empty($_POST['customer_id'])) {
|
|
$customerId = $_POST['customer_id'];
|
|
$name = trim($_POST['name'] ?? '');
|
|
$phone = trim($_POST['phone'] ?? '');
|
|
$walletBalance = floatval($_POST['wallet_balance'] ?? 0);
|
|
$rewardPoints = intval($_POST['reward_points'] ?? 0);
|
|
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
|
|
|
db()->update('customers', [
|
|
'name' => $name ?: null,
|
|
'phone' => $phone ?: null,
|
|
'wallet_balance' => $walletBalance,
|
|
'reward_points' => $rewardPoints,
|
|
'is_active' => $isActive
|
|
], 'customer_id = :id', ['id' => $customerId]);
|
|
|
|
setFlash('success', 'Customer updated successfully');
|
|
header('Location: /admin/customers.php');
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'delete' && !empty($_POST['customer_id'])) {
|
|
db()->delete('customers', 'customer_id = :id', ['id' => $_POST['customer_id']]);
|
|
setFlash('success', 'Customer deleted');
|
|
header('Location: /admin/customers.php');
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'adjust_wallet' && !empty($_POST['customer_id'])) {
|
|
$amount = floatval($_POST['amount'] ?? 0);
|
|
$reason = trim($_POST['reason'] ?? '');
|
|
|
|
if ($amount != 0) {
|
|
db()->query(
|
|
"UPDATE customers SET wallet_balance = wallet_balance + :amt WHERE customer_id = :id",
|
|
['amt' => $amount, 'id' => $_POST['customer_id']]
|
|
);
|
|
|
|
// Log transaction
|
|
db()->insert('wallet_transactions', [
|
|
'transaction_id' => generateId('wt_'),
|
|
'customer_id' => $_POST['customer_id'],
|
|
'amount' => $amount,
|
|
'type' => $amount > 0 ? 'credit' : 'debit',
|
|
'description' => $reason ?: 'Admin adjustment',
|
|
'balance_after' => db()->fetch("SELECT wallet_balance FROM customers WHERE customer_id = :id", ['id' => $_POST['customer_id']])['wallet_balance'] ?? 0
|
|
]);
|
|
|
|
setFlash('success', 'Wallet adjusted by $' . number_format($amount, 2));
|
|
}
|
|
header('Location: /admin/customers.php');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Filters
|
|
$search = $_GET['search'] ?? '';
|
|
$status = $_GET['status'] ?? '';
|
|
$page = max(1, intval($_GET['page'] ?? 1));
|
|
|
|
$where = ['1=1'];
|
|
$params = [];
|
|
|
|
if ($search) {
|
|
$where[] = '(email LIKE :search OR name LIKE :search OR phone LIKE :search)';
|
|
$params['search'] = '%' . $search . '%';
|
|
}
|
|
|
|
if ($status === 'active') {
|
|
$where[] = 'is_active = 1';
|
|
} elseif ($status === 'inactive') {
|
|
$where[] = 'is_active = 0';
|
|
}
|
|
|
|
$whereClause = implode(' AND ', $where);
|
|
$total = db()->count('customers', $whereClause, $params);
|
|
$pagination = paginate($total, $page, ADMIN_ITEMS_PER_PAGE);
|
|
|
|
$customers = db()->fetchAll(
|
|
"SELECT c.*,
|
|
(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.customer_id) as order_count,
|
|
(SELECT SUM(total) FROM orders o WHERE o.customer_id = c.customer_id AND o.payment_status = 'paid') as total_spent
|
|
FROM customers c
|
|
WHERE {$whereClause}
|
|
ORDER BY c.created_at DESC
|
|
LIMIT :limit OFFSET :offset",
|
|
array_merge($params, ['limit' => $pagination['per_page'], 'offset' => $pagination['offset']])
|
|
);
|
|
|
|
// Stats
|
|
$totalCustomers = db()->count('customers');
|
|
$activeCustomers = db()->count('customers', 'is_active = 1');
|
|
$totalWalletBalance = (float)(db()->fetch("SELECT COALESCE(SUM(wallet_balance),0) as total FROM customers")['total'] ?? 0);
|
|
?>
|
|
|
|
<div class="page-header">
|
|
<h1 class="page-title">Customers</h1>
|
|
<button class="btn btn-primary" onclick="openCustomerModal()">
|
|
<i class="fas fa-plus"></i> Add Customer
|
|
</button>
|
|
</div>
|
|
|
|
<?php if (hasFlash('success')): ?>
|
|
<div class="alert alert-success"><i class="fas fa-check-circle"></i> <?= getFlash('success') ?></div>
|
|
<?php endif; ?>
|
|
<?php if (hasFlash('error')): ?>
|
|
<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> <?= getFlash('error') ?></div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Stats -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon primary"><i class="fas fa-users"></i></div>
|
|
<div>
|
|
<div class="stat-card-value"><?= $totalCustomers ?></div>
|
|
<div class="stat-card-label">Total Customers</div>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon success"><i class="fas fa-user-check"></i></div>
|
|
<div>
|
|
<div class="stat-card-value"><?= $activeCustomers ?></div>
|
|
<div class="stat-card-label">Active</div>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon warning"><i class="fas fa-wallet"></i></div>
|
|
<div>
|
|
<div class="stat-card-value"><?= formatCurrency($totalWalletBalance) ?></div>
|
|
<div class="stat-card-label">Total Wallet Balance</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="admin-card">
|
|
<div class="admin-card-body">
|
|
<form method="GET" style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: end;">
|
|
<div class="form-group mb-0" style="flex: 1; min-width: 200px;">
|
|
<label class="form-label">Search</label>
|
|
<input type="text" name="search" class="form-input" placeholder="Email, name, phone..." value="<?= htmlspecialchars($search) ?>">
|
|
</div>
|
|
<div class="form-group mb-0">
|
|
<label class="form-label">Status</label>
|
|
<select name="status" class="form-select">
|
|
<option value="">All</option>
|
|
<option value="active" <?= $status === 'active' ? 'selected' : '' ?>>Active</option>
|
|
<option value="inactive" <?= $status === 'inactive' ? 'selected' : '' ?>>Inactive</option>
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="btn btn-secondary"><i class="fas fa-filter"></i> Filter</button>
|
|
<?php if ($search || $status): ?>
|
|
<a href="/admin/customers.php" class="btn btn-secondary">Clear</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="admin-card">
|
|
<div class="admin-card-header">
|
|
<span><?= $total ?> customers found</span>
|
|
</div>
|
|
<div class="admin-card-body" style="padding: 0;">
|
|
<table class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Customer</th>
|
|
<th>Phone</th>
|
|
<th>Orders</th>
|
|
<th>Total Spent</th>
|
|
<th>Wallet</th>
|
|
<th>Points</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($customers)): ?>
|
|
<tr><td colspan="8" class="text-muted" style="text-align: center; padding: 2rem;">No customers found</td></tr>
|
|
<?php else: ?>
|
|
<?php foreach ($customers as $customer): ?>
|
|
<tr>
|
|
<td>
|
|
<strong><?= htmlspecialchars($customer['name'] ?? 'No Name') ?></strong><br>
|
|
<small class="text-muted"><?= htmlspecialchars($customer['email']) ?></small>
|
|
</td>
|
|
<td><?= htmlspecialchars($customer['phone'] ?? '-') ?></td>
|
|
<td><?= $customer['order_count'] ?? 0 ?></td>
|
|
<td><?= formatCurrency($customer['total_spent'] ?? 0) ?></td>
|
|
<td>
|
|
<strong class="<?= ($customer['wallet_balance'] ?? 0) > 0 ? 'text-success' : '' ?>">
|
|
<?= formatCurrency($customer['wallet_balance'] ?? 0) ?>
|
|
</strong>
|
|
</td>
|
|
<td><?= $customer['reward_points'] ?? 0 ?></td>
|
|
<td>
|
|
<?php if ($customer['is_active']): ?>
|
|
<span class="badge badge-success">Active</span>
|
|
<?php else: ?>
|
|
<span class="badge badge-error">Inactive</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-secondary" onclick='openCustomerModal(<?= json_encode($customer) ?>)' title="Edit">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-secondary" onclick='openWalletModal("<?= $customer['customer_id'] ?>", "<?= htmlspecialchars($customer['name'] ?? $customer['email']) ?>", <?= $customer['wallet_balance'] ?? 0 ?>)' title="Adjust Wallet">
|
|
<i class="fas fa-wallet"></i>
|
|
</button>
|
|
<form method="POST" style="display: inline;">
|
|
<input type="hidden" name="action" value="delete">
|
|
<input type="hidden" name="customer_id" value="<?= $customer['customer_id'] ?>">
|
|
<button type="submit" class="btn btn-sm btn-danger" data-confirm="Delete this customer?">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($pagination['total_pages'] > 1): ?>
|
|
<div style="display: flex; justify-content: center; margin-top: 1rem;">
|
|
<?= renderPagination($pagination, '/admin/customers.php?search=' . urlencode($search) . '&status=' . $status) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Customer Modal -->
|
|
<div class="modal-overlay" id="customerModal">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3 class="modal-title" id="customerModalTitle">Add Customer</h3>
|
|
<button type="button" class="modal-close" onclick="Modal.close('customerModal')">×</button>
|
|
</div>
|
|
<form method="POST" id="customerForm">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" id="customerAction" value="create">
|
|
<input type="hidden" name="customer_id" id="customerId">
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Email *</label>
|
|
<input type="email" name="email" id="customerEmail" class="form-input" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" name="name" id="customerName" class="form-input">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Phone</label>
|
|
<input type="tel" name="phone" id="customerPhone" class="form-input">
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label class="form-label">Wallet Balance</label>
|
|
<input type="number" name="wallet_balance" id="customerWallet" class="form-input" step="0.01" min="0" value="0">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Reward Points</label>
|
|
<input type="number" name="reward_points" id="customerPoints" class="form-input" min="0" value="0">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group mb-0" id="statusGroup" style="display: none;">
|
|
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
|
|
<input type="checkbox" name="is_active" id="customerActive" checked>
|
|
Active
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="Modal.close('customerModal')">Cancel</button>
|
|
<button type="submit" class="btn btn-primary" id="customerSubmitBtn">Add Customer</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Wallet Adjustment Modal -->
|
|
<div class="modal-overlay" id="walletModal">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3 class="modal-title">Adjust Wallet Balance</h3>
|
|
<button type="button" class="modal-close" onclick="Modal.close('walletModal')">×</button>
|
|
</div>
|
|
<form method="POST">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" value="adjust_wallet">
|
|
<input type="hidden" name="customer_id" id="walletCustomerId">
|
|
|
|
<p>Customer: <strong id="walletCustomerName"></strong></p>
|
|
<p>Current Balance: <strong id="walletCurrentBalance"></strong></p>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Adjustment Amount</label>
|
|
<input type="number" name="amount" class="form-input" step="0.01" required placeholder="Use negative to deduct">
|
|
<small class="text-muted">Positive to add, negative to subtract</small>
|
|
</div>
|
|
|
|
<div class="form-group mb-0">
|
|
<label class="form-label">Reason</label>
|
|
<input type="text" name="reason" class="form-input" placeholder="e.g., Refund, Bonus, Correction">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="Modal.close('walletModal')">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Apply Adjustment</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function openCustomerModal(customer = null) {
|
|
const isEdit = !!customer;
|
|
|
|
document.getElementById('customerModalTitle').textContent = isEdit ? 'Edit Customer' : 'Add Customer';
|
|
document.getElementById('customerSubmitBtn').textContent = isEdit ? 'Update Customer' : 'Add Customer';
|
|
document.getElementById('customerAction').value = isEdit ? 'update' : 'create';
|
|
document.getElementById('customerId').value = isEdit ? customer.customer_id : '';
|
|
document.getElementById('customerEmail').value = isEdit ? customer.email : '';
|
|
document.getElementById('customerEmail').readOnly = isEdit;
|
|
document.getElementById('customerName').value = isEdit ? (customer.name || '') : '';
|
|
document.getElementById('customerPhone').value = isEdit ? (customer.phone || '') : '';
|
|
document.getElementById('customerWallet').value = isEdit ? (customer.wallet_balance || 0) : 0;
|
|
document.getElementById('customerPoints').value = isEdit ? (customer.reward_points || 0) : 0;
|
|
document.getElementById('customerActive').checked = isEdit ? customer.is_active : true;
|
|
document.getElementById('statusGroup').style.display = isEdit ? 'block' : 'none';
|
|
|
|
Modal.open('customerModal');
|
|
}
|
|
|
|
function openWalletModal(customerId, name, balance) {
|
|
document.getElementById('walletCustomerId').value = customerId;
|
|
document.getElementById('walletCustomerName').textContent = name;
|
|
document.getElementById('walletCurrentBalance').textContent = '$' + parseFloat(balance).toFixed(2);
|
|
Modal.open('walletModal');
|
|
}
|
|
</script>
|
|
|
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|