WebOrbiton
v1.0.0.1

StocketBase

138 lines · 7.5 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. if (count(get_included_files()) === 1) {
  6. http_response_code(403);
  7. exit;
  8. }
  9. ​
  10. require_once __DIR__ . '/../includes/currency.php';
  11. ​
  12. $db = Database::site();
  13. $usersDb = Database::users();
  14. $storeCurrency = SiteFront::settings()['store_currency'] ?? 'USD';
  15. ​
  16. $myProductCount = 0;
  17. $pendingCount = 0;
  18. $publishedCount = 0;
  19. ​
  20. if (Auth::isAdministrative()) {
  21. $publishedCount = (int) $db->query("SELECT COUNT(*) AS c FROM products WHERE status = 'published'")->fetch()['c'];
  22. $pendingCount = (int) $db->query("SELECT COUNT(*) AS c FROM products WHERE status = 'pending_review'")->fetch()['c'];
  23. } else {
  24. $statement = $db->prepare("SELECT COUNT(*) AS c FROM products WHERE created_by = :id AND deleted_at IS NULL");
  25. $statement->execute(['id' => $currentUser['id']]);
  26. $myProductCount = (int) $statement->fetch()['c'];
  27. ​
  28. $statement = $db->prepare("SELECT COUNT(*) AS c FROM products WHERE created_by = :id AND status = 'published'");
  29. $statement->execute(['id' => $currentUser['id']]);
  30. $publishedCount = (int) $statement->fetch()['c'];
  31. }
  32. ​
  33. $recentStatement = $db->prepare(
  34. Auth::isAdministrative()
  35. ? 'SELECT p.*, t.display_name FROM products p LEFT JOIN team_accounts t ON t.id = p.created_by WHERE p.deleted_at IS NULL ORDER BY p.updated_at DESC LIMIT 8'
  36. : 'SELECT p.*, t.display_name FROM products p LEFT JOIN team_accounts t ON t.id = p.created_by WHERE p.created_by = :id AND p.deleted_at IS NULL ORDER BY p.updated_at DESC LIMIT 8'
  37. );
  38. if (!Auth::isAdministrative()) {
  39. $recentStatement->execute(['id' => $currentUser['id']]);
  40. } else {
  41. $recentStatement->execute();
  42. }
  43. $recentProducts = $recentStatement->fetchAll();
  44. ​
  45. $isManager = Auth::hasRoleAtLeast(Auth::ROLE_STORE_MANAGER);
  46. $revenueToday = 0;
  47. $revenue7d = 0;
  48. $revenue30d = 0;
  49. $ordersPending = 0;
  50. $recentOrders = [];
  51. $lowStockProducts = [];
  52. ​
  53. if ($isManager) {
  54. $paidStatuses = "('paid','processing','fulfilled','shipped','completed')";
  55. $revenueToday = (int) $usersDb->query("SELECT COALESCE(SUM(total_cents), 0) AS c FROM orders WHERE status IN $paidStatuses AND created_at >= CURDATE()")->fetch()['c'];
  56. $revenue7d = (int) $usersDb->query("SELECT COALESCE(SUM(total_cents), 0) AS c FROM orders WHERE status IN $paidStatuses AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)")->fetch()['c'];
  57. $revenue30d = (int) $usersDb->query("SELECT COALESCE(SUM(total_cents), 0) AS c FROM orders WHERE status IN $paidStatuses AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)")->fetch()['c'];
  58. $ordersPending = (int) $usersDb->query("SELECT COUNT(*) AS c FROM orders WHERE status = 'pending'")->fetch()['c'];
  59. $recentOrders = $usersDb->query('SELECT * FROM orders ORDER BY created_at DESC LIMIT 8')->fetchAll();
  60. $lowStockProducts = $db->query(
  61. "SELECT id, title, stock_quantity FROM products WHERE deleted_at IS NULL AND track_inventory = 1 AND stock_quantity IS NOT NULL AND stock_quantity <= 5 ORDER BY stock_quantity ASC LIMIT 8"
  62. )->fetchAll();
  63. }
  64. ​
  65. ?>
  66. <h1 class="dash-title"><?= Icons::icon("overview", "icon icon-lg") ?>Overview</h1>
  67. ​
  68. <div class="dash-cards">
  69. <?php if ($isManager): ?>
  70. <div class="dash-card"><?= Icons::icon('dollar', 'icon icon-lg') ?><div class="dash-card-value"><?= htmlspecialchars(Currency::format($revenueToday, $storeCurrency)) ?></div><div class="dash-card-label">Sales today</div></div>
  71. <div class="dash-card"><?= Icons::icon('credit-card', 'icon icon-lg') ?><div class="dash-card-value"><?= htmlspecialchars(Currency::format($revenue7d, $storeCurrency)) ?></div><div class="dash-card-label">Sales, last 7 days</div></div>
  72. <div class="dash-card"><?= Icons::icon('stats', 'icon icon-lg') ?><div class="dash-card-value"><?= htmlspecialchars(Currency::format($revenue30d, $storeCurrency)) ?></div><div class="dash-card-label">Sales, last 30 days</div></div>
  73. <div class="dash-card"><?= Icons::icon('checklist', 'icon icon-lg') ?><div class="dash-card-value"><?= $ordersPending ?></div><div class="dash-card-label">Orders not paid yet</div></div>
  74. <?php endif; ?>
  75. <?php if (Auth::isAdministrative()): ?>
  76. <div class="dash-card"><?= Icons::icon('grid', 'icon icon-lg') ?><div class="dash-card-value"><?= $publishedCount ?></div><div class="dash-card-label">Products in your store</div></div>
  77. <div class="dash-card"><?= Icons::icon('clock', 'icon icon-lg') ?><div class="dash-card-value"><?= $pendingCount ?></div><div class="dash-card-label">Waiting for your approval</div></div>
  78. <?php else: ?>
  79. <div class="dash-card"><?= Icons::icon('edit', 'icon icon-lg') ?><div class="dash-card-value"><?= $myProductCount ?></div><div class="dash-card-label">Your products</div></div>
  80. <div class="dash-card"><?= Icons::icon('grid', 'icon icon-lg') ?><div class="dash-card-value"><?= $publishedCount ?></div><div class="dash-card-label">Live in the store</div></div>
  81. <?php endif; ?>
  82. </div>
  83. ​
  84. <?php if ($isManager): ?>
  85. <h2 class="dash-subtitle"><?= Icons::icon('checklist', 'icon icon-sm') ?>Recent orders</h2>
  86. <table class="dash-table">
  87. <thead><tr><th><?= Icons::icon('hash', 'icon icon-sm') ?>Order</th><th><?= Icons::icon('dollar', 'icon icon-sm') ?>Total</th><th><?= Icons::icon('flag', 'icon icon-sm') ?>Status</th><th><?= Icons::icon('clock', 'icon icon-sm') ?>Placed</th><th></th></tr></thead>
  88. <tbody>
  89. <?php foreach ($recentOrders as $order): ?>
  90. <tr>
  91. <td><?= htmlspecialchars($order['order_number']) ?></td>
  92. <td><?= htmlspecialchars(Currency::format((int) $order['total_cents'], $order['currency'])) ?></td>
  93. <td><span class="status-pill status-<?= htmlspecialchars($order['status']) ?>"><?= htmlspecialchars(StatusLabel::get((string) $order['status'])) ?></span></td>
  94. <td><?= htmlspecialchars($order['created_at']) ?></td>
  95. <td><a href="dashboard.php?view=order-detail&id=<?= (int) $order['id'] ?>" class="dash-btn-small"><?= Icons::icon('eye', 'icon icon-sm') ?>View</a></td>
  96. </tr>
  97. <?php endforeach; ?>
  98. <?php if (empty($recentOrders)): ?>
  99. <tr><td colspan="5">No orders yet.</td></tr>
  100. <?php endif; ?>
  101. </tbody>
  102. </table>
  103. ​
  104. <?php if (!empty($lowStockProducts)): ?>
  105. <h2 class="dash-subtitle"><?= Icons::icon('flag', 'icon icon-sm') ?>Running low</h2>
  106. <table class="dash-table">
  107. <thead><tr><th><?= Icons::icon('heading', 'icon icon-sm') ?>Product</th><th><?= Icons::icon('grid', 'icon icon-sm') ?>Stock left</th><th></th></tr></thead>
  108. <tbody>
  109. <?php foreach ($lowStockProducts as $lowStock): ?>
  110. <tr>
  111. <td><?= htmlspecialchars($lowStock['title']) ?></td>
  112. <td><?= (int) $lowStock['stock_quantity'] ?></td>
  113. <td><a href="dashboard.php?view=product-edit&id=<?= (int) $lowStock['id'] ?>" class="dash-btn-small"><?= Icons::icon('edit', 'icon icon-sm') ?>Edit</a></td>
  114. </tr>
  115. <?php endforeach; ?>
  116. </tbody>
  117. </table>
  118. <?php endif; ?>
  119. <?php endif; ?>
  120. ​
  121. <h2 class="dash-subtitle"><?= Icons::icon('activity', 'icon icon-sm') ?>Latest product changes</h2>
  122. <table class="dash-table">
  123. <thead><tr><th><?= Icons::icon('heading', 'icon icon-sm') ?>Title</th><th><?= Icons::icon('user', 'icon icon-sm') ?>Added by</th><th><?= Icons::icon('flag', 'icon icon-sm') ?>Status</th><th><?= Icons::icon('clock', 'icon icon-sm') ?>Updated</th></tr></thead>
  124. <tbody>
  125. <?php foreach ($recentProducts as $product): ?>
  126. <tr>
  127. <td><a href="dashboard.php?view=product-edit&id=<?= (int) $product['id'] ?>"><?= htmlspecialchars($product['title']) ?></a></td>
  128. <td><?= htmlspecialchars((string) ($product['display_name'] ?? 'System')) ?></td>
  129. <td><span class="status-pill status-<?= htmlspecialchars($product['status']) ?>"><?= htmlspecialchars(StatusLabel::get((string) $product['status'])) ?></span></td>
  130. <td><?= htmlspecialchars($product['updated_at']) ?></td>
  131. </tr>
  132. <?php endforeach; ?>
  133. <?php if (empty($recentProducts)): ?>
  134. <tr><td colspan="4">No products yet.</td></tr>
  135. <?php endif; ?>
  136. </tbody>
  137. </table>
  138. ​