From 80a4731cd0fdddcf9a08c23eeb1ba9650fbcc8eb Mon Sep 17 00:00:00 2001 From: lukibeg Date: Mon, 15 Dec 2025 21:56:42 -0300 Subject: [PATCH] =?UTF-8?q?feat|fix:=20Timezone=20em=20America/Sao=5FPaulo?= =?UTF-8?q?|Laravel=20Reverb=20+=20Pusher=20+=20Echo|Atualiza=C3=A7=C3=B5e?= =?UTF-8?q?s=20visuais.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 25 ++++++- app/Events/DashboardUpdate.php | 37 ++++++++++ .../Controllers/Api/AmiEventController.php | 30 +++----- config/app.php | 2 +- resources/js/Pages/Dashboard.vue | 52 ++++++++++++-- resources/js/Pages/Profile/Edit.vue | 2 +- .../Pages/Profile/Partials/DeleteUserForm.vue | 50 +++++-------- .../Profile/Partials/UpdatePasswordForm.vue | 71 +++++-------------- .../Partials/UpdateProfileInformationForm.vue | 68 +++++------------- resources/js/bootstrap.js | 15 ++++ routes/channels.php | 5 +- 11 files changed, 193 insertions(+), 164 deletions(-) create mode 100644 app/Events/DashboardUpdate.php diff --git a/.env.example b/.env.example index 79ed088..b7bad90 100644 --- a/.env.example +++ b/.env.example @@ -24,8 +24,15 @@ DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=omniboard -DB_USERNAME=root -DB_PASSWORD= +DB_USERNAME=omniboard_user +DB_PASSWORD="*Ingline.Sys#9420%SECURITY#" + +REVERB_APP_ID=test +REVERB_APP_KEY=test +REVERB_APP_SECRET=test +REVERB_HOST="localhost" +REVERB_PORT=8081 +REVERB_SCHEME=http SESSION_DRIVER=database SESSION_LIFETIME=120 @@ -63,3 +70,17 @@ AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID=348643 +REVERB_APP_KEY=v40nrhrty4rxpsv9mnt1 +REVERB_APP_SECRET=vf0mesdcuze3dob3nage +REVERB_HOST="localhost" +REVERB_PORT=8081 +REVERB_SCHEME=http + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" \ No newline at end of file diff --git a/app/Events/DashboardUpdate.php b/app/Events/DashboardUpdate.php new file mode 100644 index 0000000..c0d2831 --- /dev/null +++ b/app/Events/DashboardUpdate.php @@ -0,0 +1,37 @@ +tenantId = $tenantId; + } + + // Define o canal privado: "dashboard.{tenant_id}" + public function broadcastOn(): array + { + return [ + new PrivateChannel('dashboard.' . $this->tenantId), + ]; + } + + // Opcional: Nome do evento no Front (padrão é o nome da classe) + public function broadcastAs() + { + return 'metrics.updated'; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/AmiEventController.php b/app/Http/Controllers/Api/AmiEventController.php index 6bbba44..91094f9 100644 --- a/app/Http/Controllers/Api/AmiEventController.php +++ b/app/Http/Controllers/Api/AmiEventController.php @@ -9,12 +9,12 @@ use App\Models\WaitingList; use App\Models\DailyMetric; use Carbon\Carbon; +use App\Events\DashboardUpdate; class AmiEventController extends Controller { public function handle(Request $request) { - // 1. Autenticação via Token (Header: X-Tenant-Token) $token = $request->header('X-Tenant-Token'); $tenant = Tenant::where('api_key', $token)->first(); @@ -22,7 +22,6 @@ public function handle(Request $request) return response()->json(['error' => 'Unauthorized'], 401); } - // 2. Dados do Evento $data = $request->all(); $eventName = $data['Event'] ?? null; $queueName = $data['Queue'] ?? null; @@ -31,17 +30,14 @@ public function handle(Request $request) return response()->json(['status' => 'ignored']); } - // DEPOIS (Correto) $queue = Queue::where('tenant_id', $tenant->id) ->where('source_id', $queueName) // Procura "08000" na coluna source_id ->first();; - // Se a fila não existe no banco, ignoramos ou criamos automaticamente (opcional) if (!$queue) { return response()->json(['error' => 'Fila não encontrada'], 404); } - // 4. Processar Lógica de Negócio switch ($eventName) { case 'QueueCallerJoin': $this->handleJoin($queue, $data); @@ -60,11 +56,9 @@ public function handle(Request $request) return response()->json(['status' => 'success']); } - // LÓGICA DE NEGÓCIO (A mesma do script anterior, mas agora com Eloquent) private function handleJoin($queue, $data) { - // Adiciona na Lista de Espera COM TENANT_ID WaitingList::create([ 'tenant_id' => $queue->tenant_id, // <--- ADICIONE ESTA LINHA 'queue_id' => $queue->id, @@ -73,28 +67,25 @@ private function handleJoin($queue, $data) 'entered_at' => now(), ]); - // Incrementa Total do Dia $this->updateMetric($queue, 'received_count', 1); + $this->broadcastUpdate($queue); } private function handleConnect($queue, $data) { - // Remove da Lista de Espera WaitingList::where('queue_id', $queue->id) ->where('caller_number', $data['CallerIDNum']) ->delete(); - // Atualiza TME (Média Ponderada) - // Nota: Isso é simplificado. Em produção real, calcular média ponderada em SQL é melhor. $metric = $this->getTodayMetric($queue); $holdTime = intval($data['HoldTime'] ?? 0); - // Novo TME = ((Atual * Qtd) + Novo) / (Qtd + 1) $newAvg = (($metric->avg_wait_time * $metric->answered_count) + $holdTime) / ($metric->answered_count + 1); $metric->avg_wait_time = $newAvg; $metric->answered_count += 1; $metric->save(); + $this->broadcastUpdate($queue); } private function handleComplete($queue, $data) @@ -102,28 +93,24 @@ private function handleComplete($queue, $data) $talkTime = intval($data['TalkTime'] ?? 0); $metric = $this->getTodayMetric($queue); - // Evita divisão por zero se o connect falhou if ($metric->answered_count > 0) { - // Novo TMA = ((Atual * (Qtd-1)) + Novo) / Qtd - // Nota: Usamos Qtd (answered_count) como divisor pois a chamada já foi contada no Connect $newAvg = (($metric->avg_talk_time * ($metric->answered_count - 1)) + $talkTime) / $metric->answered_count; $metric->avg_talk_time = $newAvg; $metric->save(); } + $this->broadcastUpdate($queue); } private function handleAbandon($queue, $data) { - // Remove da Espera WaitingList::where('queue_id', $queue->id) ->where('caller_number', $data['CallerIDNum']) ->delete(); - // Incrementa Abandonos $this->updateMetric($queue, 'abandoned_count', 1); + $this->broadcastUpdate($queue); } - // Helpers private function getTodayMetric($queue) { return DailyMetric::firstOrCreate( @@ -132,7 +119,7 @@ private function getTodayMetric($queue) 'date' => Carbon::today() ], [ - 'tenant_id' => $queue->tenant_id, // <--- O PULO DO GATO: Herda o ID da Fila + 'tenant_id' => $queue->tenant_id, 'received_count' => 0, 'answered_count' => 0, 'abandoned_count' => 0 @@ -145,4 +132,9 @@ private function updateMetric($queue, $field, $value) $metric = $this->getTodayMetric($queue); $metric->increment($field, $value); } + private function broadcastUpdate($queue) + { + // Dispara o evento apenas para o Tenant dono da fila + broadcast(new DashboardUpdate($queue->tenant_id)); + } } diff --git a/config/app.php b/config/app.php index 423eed5..28be821 100644 --- a/config/app.php +++ b/config/app.php @@ -65,7 +65,7 @@ | */ - 'timezone' => 'UTC', + 'timezone' => env('APP_TIMEZONE', 'UTC'), /* |-------------------------------------------------------------------------- diff --git a/resources/js/Pages/Dashboard.vue b/resources/js/Pages/Dashboard.vue index ec8d50f..61a0492 100644 --- a/resources/js/Pages/Dashboard.vue +++ b/resources/js/Pages/Dashboard.vue @@ -1,12 +1,57 @@