You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
7.0KB

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\SwimLane;
  5. use App\Repositories\CardRepository;
  6. use App\Repositories\SwimLaneRepository;
  7. use App\Services\AuthService;
  8. use Core\Controller;
  9. use Core\Request;
  10. class SwimLanesController extends Controller
  11. {
  12. private function lanes(): SwimLaneRepository
  13. {
  14. return new SwimLaneRepository(database());
  15. }
  16. private function cards(): CardRepository
  17. {
  18. return new CardRepository(database());
  19. }
  20. public function store(Request $request): mixed
  21. {
  22. if (!AuthService::isLoggedIn()) {
  23. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  24. }
  25. $boardId = (int) $request->input('board_id', 0);
  26. $name = trim((string) $request->input('name', ''));
  27. if ($boardId === 0 || $name === '') {
  28. return $this->json(['ok' => false, 'error' => 'board_id and name are required']);
  29. }
  30. $now = date('Y-m-d H:i:s');
  31. $username = AuthService::getCurrentUsername();
  32. $lane = new SwimLane();
  33. $lane->boardId = $boardId;
  34. $lane->name = $name;
  35. $lane->position = $this->lanes()->maxPosition($boardId) + 1;
  36. $lane->createdAt = $now;
  37. $lane->createdBy = $username;
  38. $lane->updatedAt = $now;
  39. $lane->updatedBy = $username;
  40. $this->lanes()->insert($lane);
  41. return $this->json(['ok' => true, 'id' => $lane->id, 'name' => $lane->name, 'position' => $lane->position]);
  42. }
  43. public function update(Request $request, int $id): mixed
  44. {
  45. if (!AuthService::isLoggedIn()) {
  46. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  47. }
  48. $name = trim((string) $request->input('name', ''));
  49. if ($name === '') {
  50. return $this->json(['ok' => false, 'error' => 'name is required']);
  51. }
  52. $row = $this->lanes()->find($id);
  53. if ($row === null) {
  54. return $this->json(['ok' => false, 'error' => 'Not found'], 404);
  55. }
  56. $lane = SwimLane::fromRow($row);
  57. $lane->name = $name;
  58. $lane->updatedAt = date('Y-m-d H:i:s');
  59. $lane->updatedBy = AuthService::getCurrentUsername();
  60. $this->lanes()->update($lane);
  61. return $this->json(['ok' => true]);
  62. }
  63. public function toggleCount(Request $request, int $id): mixed
  64. {
  65. if (!AuthService::isLoggedIn()) {
  66. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  67. }
  68. $row = $this->lanes()->find($id);
  69. if ($row === null) {
  70. return $this->json(['ok' => false, 'error' => 'Not found'], 404);
  71. }
  72. $show = (string) $request->input('show_card_count', '0') === '1';
  73. $this->lanes()->updateShowCardCount($id, $show, date('Y-m-d H:i:s'), AuthService::getCurrentUsername());
  74. return $this->json(['ok' => true, 'show_card_count' => $show]);
  75. }
  76. public function toggleExport(Request $request, int $id): mixed
  77. {
  78. if (!AuthService::isLoggedIn()) {
  79. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  80. }
  81. $row = $this->lanes()->find($id);
  82. if ($row === null) {
  83. return $this->json(['ok' => false, 'error' => 'Not found'], 404);
  84. }
  85. $show = (string) $request->input('show_export_button', '0') === '1';
  86. $this->lanes()->updateShowExportButton($id, $show, date('Y-m-d H:i:s'), AuthService::getCurrentUsername());
  87. return $this->json(['ok' => true, 'show_export_button' => $show]);
  88. }
  89. public function updateCardAgeSettings(Request $request, int $id): mixed
  90. {
  91. if (!AuthService::isLoggedIn()) {
  92. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  93. }
  94. $row = $this->lanes()->find($id);
  95. if ($row === null) {
  96. return $this->json(['ok' => false, 'error' => 'Not found'], 404);
  97. }
  98. $showCardAge = (string) $request->input('show_card_age', '0') === '1';
  99. $cardAgeWarningDays = max(0, (int) $request->input('card_age_warning_days', 0));
  100. $this->lanes()->updateCardAgeSettings(
  101. $id,
  102. $showCardAge,
  103. $cardAgeWarningDays,
  104. date('Y-m-d H:i:s'),
  105. AuthService::getCurrentUsername()
  106. );
  107. return $this->json([
  108. 'ok' => true,
  109. 'show_card_age' => $showCardAge,
  110. 'card_age_warning_days' => $cardAgeWarningDays,
  111. ]);
  112. }
  113. public function export(int $id): mixed
  114. {
  115. if (!AuthService::isLoggedIn()) {
  116. return $this->redirect('/auth/login');
  117. }
  118. $row = $this->lanes()->find($id);
  119. if ($row === null) {
  120. return new \Core\Response('Not found', 404);
  121. }
  122. $lane = \App\Models\SwimLane::fromRow($row);
  123. $cards = $this->cards()->findBySwimLaneId($id);
  124. $out = fopen('php://memory', 'wb');
  125. fputcsv($out, ['Job #', 'Job Name', 'Customer', 'Delivery Date', 'Quantity', 'Notes'], ',', '"', '\\');
  126. foreach ($cards as $card) {
  127. fputcsv($out, [
  128. $card->jobNumber,
  129. $card->jobName,
  130. $card->customerName,
  131. $card->deliveryDate ?? '',
  132. $card->quantity ?? '',
  133. $card->notes,
  134. ], ',', '"', '\\');
  135. }
  136. rewind($out);
  137. $csv = (string) stream_get_contents($out);
  138. fclose($out);
  139. $slug = trim((string) preg_replace('/[^a-z0-9]+/i', '-', $lane->name), '-');
  140. $filename = ($slug ?: 'lane') . '-' . date('Y-m-d') . '.csv';
  141. return new \Core\Response($csv, 200, [
  142. 'Content-Type' => 'text/csv; charset=utf-8',
  143. 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
  144. ]);
  145. }
  146. public function destroy(int $id): mixed
  147. {
  148. if (!AuthService::isLoggedIn()) {
  149. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  150. }
  151. $this->cards()->deleteBySwimLaneId($id);
  152. $this->lanes()->delete($id);
  153. return $this->json(['ok' => true]);
  154. }
  155. public function reorder(): mixed
  156. {
  157. if (!AuthService::isLoggedIn()) {
  158. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  159. }
  160. $raw = file_get_contents('php://input');
  161. $items = json_decode((string) $raw, true);
  162. if (!is_array($items)) {
  163. return $this->json(['ok' => false, 'error' => 'Invalid JSON payload']);
  164. }
  165. $now = date('Y-m-d H:i:s');
  166. $username = AuthService::getCurrentUsername();
  167. foreach ($items as $item) {
  168. $laneId = (int) ($item['id'] ?? 0);
  169. $position = (int) ($item['position'] ?? 0);
  170. if ($laneId > 0) {
  171. $this->lanes()->updatePosition($laneId, $position, $now, $username);
  172. }
  173. }
  174. return $this->json(['ok' => true]);
  175. }
  176. }

Powered by TurnKey Linux.