Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

222 рядки
8.0KB

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\Card;
  5. use App\Models\CardMovement;
  6. use App\Repositories\BoardColumnRepository;
  7. use App\Repositories\CardMovementRepository;
  8. use App\Repositories\CardRepository;
  9. use App\Repositories\SwimLaneRepository;
  10. use App\Services\AuthService;
  11. use Core\Controller;
  12. use Core\Request;
  13. use Core\Response;
  14. class CardsController extends Controller
  15. {
  16. private function cards(): CardRepository
  17. {
  18. return new CardRepository(database());
  19. }
  20. private function cardMovements(): CardMovementRepository
  21. {
  22. return new CardMovementRepository(database());
  23. }
  24. private function columns(): BoardColumnRepository
  25. {
  26. return new BoardColumnRepository(database());
  27. }
  28. private function lanes(): SwimLaneRepository
  29. {
  30. return new SwimLaneRepository(database());
  31. }
  32. public function store(Request $request): mixed
  33. {
  34. if (!AuthService::isLoggedIn()) {
  35. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  36. }
  37. $boardId = (int) $request->input('board_id', 0);
  38. $columnId = (int) $request->input('column_id', 0);
  39. $swimLaneId = (int) $request->input('swim_lane_id', 0);
  40. if ($boardId === 0 || $columnId === 0 || $swimLaneId === 0) {
  41. return $this->json(['ok' => false, 'error' => 'board_id, column_id, and swim_lane_id are required']);
  42. }
  43. $now = date('Y-m-d H:i:s');
  44. $username = AuthService::getCurrentUsername();
  45. $nextPos = $this->cards()->maxPosition($columnId, $swimLaneId) + 1;
  46. $card = new Card();
  47. $card->boardId = $boardId;
  48. $card->columnId = $columnId;
  49. $card->swimLaneId = $swimLaneId;
  50. $card->jobNumber = trim((string) $request->input('job_number', ''));
  51. $card->jobName = trim((string) $request->input('job_name', ''));
  52. $card->customerName = trim((string) $request->input('customer_name', ''));
  53. $card->deliveryDate = trim((string) $request->input('delivery_date', '')) ?: null;
  54. $card->quantity = trim((string) $request->input('quantity', ''));
  55. $card->notes = trim((string) $request->input('notes', ''));
  56. $card->fullNote = (string) $request->input('full_note', '');
  57. $card->position = $nextPos;
  58. $card->cellEnteredAt = $now;
  59. $card->createdAt = $now;
  60. $card->createdBy = $username;
  61. $card->updatedAt = $now;
  62. $card->updatedBy = $username;
  63. try {
  64. $this->cards()->insert($card);
  65. } catch (\Throwable $e) {
  66. return $this->json(['ok' => false, 'error' => $e->getMessage()]);
  67. }
  68. return $this->json(['ok' => true] + $card->toJsonArray());
  69. }
  70. public function update(Request $request, int $id): mixed
  71. {
  72. if (!AuthService::isLoggedIn()) {
  73. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  74. }
  75. $card = $this->cards()->findById($id);
  76. if ($card === null) {
  77. return $this->json(['ok' => false, 'error' => 'Not found'], 404);
  78. }
  79. $card->jobNumber = trim((string) $request->input('job_number', ''));
  80. $card->jobName = trim((string) $request->input('job_name', ''));
  81. $card->customerName = trim((string) $request->input('customer_name', ''));
  82. $card->deliveryDate = trim((string) $request->input('delivery_date', '')) ?: null;
  83. $card->quantity = trim((string) $request->input('quantity', ''));
  84. $card->notes = trim((string) $request->input('notes', ''));
  85. $card->fullNote = (string) $request->input('full_note', '');
  86. $card->updatedAt = date('Y-m-d H:i:s');
  87. $card->updatedBy = AuthService::getCurrentUsername();
  88. try {
  89. $this->cards()->update($card);
  90. } catch (\Throwable $e) {
  91. return $this->json(['ok' => false, 'error' => $e->getMessage()]);
  92. }
  93. return $this->json(['ok' => true] + $card->toJsonArray());
  94. }
  95. public function move(Request $request, int $id): mixed
  96. {
  97. if (!AuthService::isLoggedIn()) {
  98. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  99. }
  100. $card = $this->cards()->findById($id);
  101. if ($card === null) {
  102. return Response::json(['ok' => false, 'error' => 'Not found'], 404);
  103. }
  104. $columnId = (int) $request->input('column_id', 0);
  105. $swimLaneId = (int) $request->input('swim_lane_id', 0);
  106. $position = (int) $request->input('position', 0);
  107. $toColumn = $columnId > 0 ? $this->columns()->find($columnId) : null;
  108. $toLane = $swimLaneId > 0 ? $this->lanes()->find($swimLaneId) : null;
  109. if ($toColumn === null || $toLane === null ||
  110. (int) $toColumn['board_id'] !== $card->boardId ||
  111. (int) $toLane['board_id'] !== $card->boardId || $position < 0) {
  112. return Response::json(['ok' => false, 'error' => 'Invalid destination'], 422);
  113. }
  114. $now = gmdate('Y-m-d H:i:s');
  115. $username = AuthService::getCurrentUsername();
  116. $siblings = trim((string) $request->input('sibling_ids', ''));
  117. try {
  118. database()->transaction(function () use (
  119. $card,
  120. $id,
  121. $columnId,
  122. $swimLaneId,
  123. $position,
  124. $toColumn,
  125. $toLane,
  126. $now,
  127. $username,
  128. $siblings
  129. ): void {
  130. $this->cards()->move($id, $columnId, $swimLaneId, $position, $now, $username);
  131. if ($columnId !== $card->columnId || $swimLaneId !== $card->swimLaneId) {
  132. $fromColumn = $this->columns()->find($card->columnId);
  133. $fromLane = $this->lanes()->find($card->swimLaneId);
  134. $movement = new CardMovement();
  135. $movement->cardId = $id;
  136. $movement->fromColumnId = $card->columnId;
  137. $movement->fromColumnName = $fromColumn['name'] ?? null;
  138. $movement->toColumnId = $columnId;
  139. $movement->toColumnName = $toColumn['name'];
  140. $movement->fromSwimLaneId = $card->swimLaneId;
  141. $movement->fromSwimLaneName = $fromLane['name'] ?? null;
  142. $movement->toSwimLaneId = $swimLaneId;
  143. $movement->toSwimLaneName = $toLane['name'];
  144. $movement->movedAt = $now;
  145. $movement->movedBy = $username;
  146. $this->cardMovements()->insert($movement);
  147. }
  148. if ($siblings !== '') {
  149. foreach (explode(',', $siblings) as $idx => $sibId) {
  150. $sibId = (int) trim($sibId);
  151. if ($sibId > 0) {
  152. $this->cards()->updatePosition($sibId, $idx, $now, $username);
  153. }
  154. }
  155. }
  156. });
  157. } catch (\Throwable $e) {
  158. error_log('Card move failed: ' . $e->getMessage());
  159. return Response::json(['ok' => false, 'error' => 'Could not move card'], 500);
  160. }
  161. return $this->json(['ok' => true]);
  162. }
  163. public function destroy(int $id): mixed
  164. {
  165. if (!AuthService::isLoggedIn()) {
  166. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  167. }
  168. $this->cardMovements()->deleteByCardId($id);
  169. $this->cards()->delete($id);
  170. return $this->json(['ok' => true]);
  171. }
  172. public function history(int $id): mixed
  173. {
  174. if (!AuthService::isLoggedIn()) {
  175. return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401);
  176. }
  177. $movements = array_map(
  178. fn($m) => $m->toJsonArray(),
  179. $this->cardMovements()->findByCardId($id)
  180. );
  181. return $this->json(['ok' => true, 'movements' => $movements]);
  182. }
  183. }

Powered by TurnKey Linux.