WebOrbiton
v1.0.0.1

StocketBase

115 lines · 5.2 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. ​
  14. $isAdmin = Auth::isAdministrative();
  15. ​
  16. $statement = $isAdmin
  17. ? $db->query('SELECT p.*, t.display_name, c.name AS category_name FROM products p LEFT JOIN team_accounts t ON t.id = p.created_by LEFT JOIN categories c ON c.id = p.category_id WHERE p.deleted_at IS NULL ORDER BY p.updated_at DESC')
  18. : $db->prepare('SELECT p.*, t.display_name, c.name AS category_name FROM products p LEFT JOIN team_accounts t ON t.id = p.created_by LEFT JOIN categories c ON c.id = p.category_id WHERE p.created_by = :id AND p.deleted_at IS NULL ORDER BY p.updated_at DESC');
  19. ​
  20. if (!$isAdmin) {
  21. $statement->execute(['id' => $currentUser['id']]);
  22. }
  23. $products = $statement->fetchAll();
  24. ​
  25. $trashStatement = $isAdmin
  26. ? $db->query('SELECT COUNT(*) FROM products WHERE deleted_at IS NOT NULL')
  27. : $db->prepare('SELECT COUNT(*) FROM products WHERE deleted_at IS NOT NULL AND created_by = :id');
  28. if (!$isAdmin) {
  29. $trashStatement->execute(['id' => $currentUser['id']]);
  30. }
  31. $trashCount = (int) $trashStatement->fetchColumn();
  32. ​
  33. $storeCurrency = SiteFront::settings()['store_currency'] ?? 'USD';
  34. ​
  35. ?>
  36. <div class="dash-header-row">
  37. <h1 class="dash-title"><?= Icons::icon("grid", "icon icon-lg") ?>Products</h1>
  38. <a href="dashboard.php?view=trash" class="dash-btn"><?= Icons::icon('trash', 'icon icon-sm') ?>Trash<?= $trashCount > 0 ? ' (' . $trashCount . ')' : '' ?></a>
  39. <a href="dashboard.php?view=product-edit" class="dash-btn dash-btn-primary"><?= Icons::icon('plus', 'icon icon-sm') ?>New product</a>
  40. </div>
  41. ​
  42. <table class="dash-table">
  43. <thead>
  44. <tr>
  45. <th><?= Icons::icon('heading', 'icon icon-sm') ?>Title</th>
  46. <th><?= Icons::icon('categories', 'icon icon-sm') ?>Category</th>
  47. <th><?= Icons::icon('dollar', 'icon icon-sm') ?>Price</th>
  48. <th><?= Icons::icon('flag', 'icon icon-sm') ?>Status</th>
  49. <th><?= Icons::icon('clock', 'icon icon-sm') ?>Updated</th>
  50. <th></th>
  51. </tr>
  52. </thead>
  53. <tbody>
  54. <?php foreach ($products as $product): ?>
  55. <tr>
  56. <td><a href="dashboard.php?view=product-edit&id=<?= (int) $product['id'] ?>"><?= htmlspecialchars($product['title']) ?></a></td>
  57. <td><?= htmlspecialchars($product['category_name'] ?? 'No category') ?></td>
  58. <td>
  59. <?php if (!empty($product['price_cents'])): ?>
  60. <?= htmlspecialchars(Currency::format((int) $product['price_cents'], (string) ($product['currency'] ?: $storeCurrency))) ?><?= $product['billing_interval'] && $product['billing_interval'] !== 'one_time' ? '/' . htmlspecialchars((string) $product['billing_interval']) : '' ?>
  61. <?php else: ?>
  62. Free
  63. <?php endif; ?>
  64. </td>
  65. <td><span class="status-pill status-<?= htmlspecialchars($product['status']) ?>"><?= htmlspecialchars(StatusLabel::get((string) $product['status'])) ?></span></td>
  66. <td><?= htmlspecialchars($product['updated_at']) ?></td>
  67. <td class="dash-table-actions">
  68. ​
  69. <?php if ($isAdmin && $product['status'] === 'pending_review'): ?>
  70. <form method="post" style="display:inline;">
  71. <?= Csrf::field() ?>
  72. <input type="hidden" name="action" value="moderate_product">
  73. <input type="hidden" name="product_id" value="<?= (int) $product['id'] ?>">
  74. <input type="hidden" name="decision" value="approve">
  75. <button type="submit" class="dash-btn-small dash-btn-success"><?= Icons::icon('check', 'icon icon-sm') ?>Approve</button>
  76. </form>
  77. <button type="button" class="dash-btn-small dash-btn-danger js-reject-btn" data-product-id="<?= (int) $product['id'] ?>"><?= Icons::icon('x', 'icon icon-sm') ?>Send back</button>
  78. <?php endif; ?>
  79. ​
  80. <form method="post" style="display:inline;" onsubmit="return confirm('Move this product to the trash? You can bring it back later.');">
  81. <?= Csrf::field() ?>
  82. <input type="hidden" name="action" value="delete_product">
  83. <input type="hidden" name="product_id" value="<?= (int) $product['id'] ?>">
  84. <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete</button>
  85. </form>
  86. <?php if ($product['status'] === 'published' && !empty($product['slug'])): ?>
  87. <a href="<?= htmlspecialchars(SiteFront::productUrl($product['slug'])) ?>" class="dash-btn-small" target="_blank" rel="noopener noreferrer"><?= Icons::icon('eye', 'icon icon-sm') ?>View</a>
  88. <?php else: ?>
  89. <a href="product.php?preview=<?= (int) $product['id'] ?>" class="dash-btn-small" target="_blank" rel="noopener noreferrer"><?= Icons::icon('eye', 'icon icon-sm') ?>Preview</a>
  90. <?php endif; ?>
  91. </td>
  92. </tr>
  93. ​
  94. <?php if ($isAdmin && $product['status'] === 'pending_review'): ?>
  95. <tr class="js-reject-row" id="reject-row-<?= (int) $product['id'] ?>" style="display:none;">
  96. <td colspan="6">
  97. <form method="post">
  98. <?= Csrf::field() ?>
  99. <input type="hidden" name="action" value="moderate_product">
  100. <input type="hidden" name="product_id" value="<?= (int) $product['id'] ?>">
  101. <input type="hidden" name="decision" value="reject">
  102. <input type="text" name="rejection_reason" placeholder="What should be fixed?" class="dash-input-inline">
  103. <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('check', 'icon icon-sm') ?>Send back</button>
  104. </form>
  105. </td>
  106. </tr>
  107. <?php endif; ?>
  108. ​
  109. <?php endforeach; ?>
  110. <?php if (empty($products)): ?>
  111. <tr><td colspan="6">No products yet. Click "New product" to add your first one.</td></tr>
  112. <?php endif; ?>
  113. </tbody>
  114. </table>
  115. ​