SupportTicketController.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Concerns\ResolvesActiveUnit;
  4. use App\Enums\NotificationType;
  5. use App\Services\NotificationService;
  6. use App\Services\SupportTicketService;
  7. use App\Http\Requests\SupportTicketRequest;
  8. use App\Http\Resources\SupportTicketResource;
  9. use Illuminate\Http\JsonResponse;
  10. class SupportTicketController extends Controller
  11. {
  12. use ResolvesActiveUnit;
  13. public function __construct(
  14. protected SupportTicketService $service,
  15. protected NotificationService $notifications,
  16. ) {}
  17. public function index(): JsonResponse
  18. {
  19. $user = auth()->user();
  20. $query = \App\Models\SupportTicket::query()
  21. ->with(['applicantUnit', 'targetUnit'])
  22. ->orderBy('created_at', 'desc');
  23. if ($this->isMatriz($user)) {
  24. $query->where(fn($q) => $q->where('origin', '!=', 'unit')->orWhere('scope', '!=', 'internal'));
  25. } else {
  26. $query->visibleToUnit($this->activeUnitId($user));
  27. }
  28. return $this->successResponse(
  29. payload: SupportTicketResource::collection($query->get())
  30. );
  31. }
  32. public function store(SupportTicketRequest $request): JsonResponse
  33. {
  34. $data = $request->validated();
  35. $user = auth()->user();
  36. $isMatriz = $this->isMatriz($user);
  37. $data['origin'] = $isMatriz ? 'matriz' : 'unit';
  38. $data['applicant_user_id'] = $user->id;
  39. $data['responsable_user_id'] = $user->id;
  40. $data['applicant_unit_id'] = $isMatriz ? null : $this->activeUnitId($user);
  41. $data['status'] = 'in_progress';
  42. // Broadcast: Matriz para todas as unidades
  43. if ($isMatriz && ($data['scope'] ?? null) === 'all') {
  44. $batchId = (string) \Illuminate\Support\Str::uuid();
  45. $unitIds = \App\Models\Unit::query()->pluck('id');
  46. $created = [];
  47. foreach ($unitIds as $unitId) {
  48. $ticket = $this->service->create(array_merge($data, [
  49. 'target_unit_id' => $unitId,
  50. 'batch_id' => $batchId,
  51. ]));
  52. $this->notifyTicketCreated($ticket);
  53. $created[] = $ticket;
  54. }
  55. return $this->successResponse(
  56. payload: SupportTicketResource::collection($created),
  57. message: __('messages.created'),
  58. code: 201
  59. );
  60. }
  61. // Resolução de target_unit_id por cenário
  62. if ($isMatriz) {
  63. if (($data['scope'] ?? null) === 'internal') {
  64. $data['target_unit_id'] = null;
  65. }
  66. // scope='specific' → target_unit_id já veio do request
  67. } else {
  68. // Franchisee
  69. $data['target_unit_id'] = (($data['scope'] ?? null) === 'internal')
  70. ? $this->activeUnitId($user)
  71. : null; // 'specific' do Franchisee = "para Matriz"
  72. }
  73. $item = $this->service->create($data);
  74. $this->notifyTicketCreated($item);
  75. return $this->successResponse(
  76. payload: new SupportTicketResource($item),
  77. message: __('messages.created'),
  78. code: 201
  79. );
  80. }
  81. /**
  82. * Notifica o outro lado quando um chamado é criado:
  83. * franqueada → matriz; matriz → unidade alvo.
  84. */
  85. private function notifyTicketCreated(\App\Models\SupportTicket $ticket): void
  86. {
  87. if ($ticket->origin === 'unit') {
  88. $unitId = $ticket->applicant_unit_id;
  89. $recipients = $this->notifications->matrizUserIds();
  90. } else {
  91. $unitId = $ticket->target_unit_id;
  92. $recipients = $this->notifications->unitUserIds($unitId);
  93. }
  94. $this->notifications->dispatch([
  95. 'origin_table' => 'support_tickets',
  96. 'origin_id' => $ticket->id,
  97. 'unit_id' => $unitId,
  98. 'type' => NotificationType::SUPPORT_TICKET_CREATED->value,
  99. 'title' => 'Novo chamado de suporte',
  100. 'message' => $ticket->title,
  101. 'url' => '/support',
  102. 'action_text' => 'Ver chamado',
  103. 'priority' => $ticket->severity === 'high' ? 'high' : 'normal',
  104. 'created_by_user_id' => $ticket->applicant_user_id,
  105. 'recipient_user_ids' => $recipients,
  106. ]);
  107. }
  108. public function show(int $id): JsonResponse
  109. {
  110. $item = $this->service->findById($id);
  111. return $this->successResponse(payload: new SupportTicketResource($item));
  112. }
  113. public function update(SupportTicketRequest $request, int $id): JsonResponse
  114. {
  115. $user = auth()->user();
  116. $item = $this->service->findById($id);
  117. if (!$this->canManage($user, $item)) {
  118. return $this->errorResponse(message: __('messages.unauthorized'), code: 403);
  119. }
  120. $item = $this->service->update($id, $request->validated());
  121. return $this->successResponse(payload: new SupportTicketResource($item), message: __('messages.updated'));
  122. }
  123. public function destroy(int $id): JsonResponse
  124. {
  125. $user = auth()->user();
  126. $item = $this->service->findById($id);
  127. if (!$this->canManage($user, $item)) {
  128. return $this->errorResponse(message: __('messages.unauthorized'), code: 403);
  129. }
  130. $this->service->delete($id);
  131. return $this->successResponse(message: __('messages.deleted'), code: 204);
  132. }
  133. private function isMatriz(\App\Models\User $user): bool
  134. {
  135. return $user->user_type === 'ADMIN';
  136. }
  137. private function canManage(\App\Models\User $user, \App\Models\SupportTicket $ticket): bool
  138. {
  139. if ($this->isMatriz($user)) {
  140. return true;
  141. }
  142. // Franchisee só pode gerenciar tickets internos que ela mesma criou
  143. return $ticket->origin === 'unit'
  144. && $ticket->scope === 'internal'
  145. && $ticket->applicant_unit_id === $this->activeUnitId($user);
  146. }
  147. }