Compare commits

...

83 Commits

Author SHA1 Message Date
Felipe Fontana b1f2c93667 correção horarios e horarios option 9 months ago
Felipe Fontana 3f19fee6b5 Correção modal horario e horarios option painel 9 months ago
Felipe Fontana e8513036c0 Merge branch 'Saas' of http://192.168.115.233:3000/awade/simples-painel into Felipe 9 months ago
Felipe Fontana 9abc2f9a87 correção modal graficos, gerar graficos ao abrir a pagina 9 months ago
Felipe Fontana ba1cca8050 correção modal contatos 9 months ago
Felipe Fontana 0dffaef8cd correção função para atualizar os templates 10 months ago
Felipe Fontana 357effe28d Adicionando função de transferir atendimentos na modal de agentes logados na fila 10 months ago
Felipe Fontana def5cfbcd4 Incluindo filtros de data nos graficos do painel 10 months ago
Felipe Fontana ad13702469 Atualização filtro data relatorio historico de atendimento 10 months ago
Felipe Fontana 281c041d9f Att mudar senha usuarios 10 months ago
Felipe Fontana 6cdff35211 Adicionando função de deletar templates 10 months ago
Guilherme Guia 16f8f69b41 Correcao historico de atendimento nao exibindo atendimentos abandonados 10 months ago
Felipe Fontana be6ea95024 Update modal horario e horarios itens 10 months ago
Felipe Fontana 09e9359efb Merge branch 'Saas' of http://192.168.115.233:3000/awade/simples-painel into Felipe 10 months ago
Felipe Fontana e97d1e8a5a att config time out agente indisponivel 10 months ago
Felipe Fontana 607f7419a0 Update modal transferir a fila dos atendimentos em espera 10 months ago
Felipe Fontana d3d4c39356 correção editar grupo usuário 10 months ago
Felipe Fontana d36938614b adicionando modal de horários 10 months ago
Felipe Fontana c6e0bd6c25 Correção relatorio producao agente e correcao graficos 10 months ago
Felipe Fontana 8840958a7d inserindo relatorio de producao agentes 10 months ago
Felipe Fontana a2575df2f8 Relatorio atendimentos abandonados 10 months ago
Felipe Fontana 2e2e43dc50 Correção dashboard atendimentos com evento TIMEOUT_CALLER 10 months ago
Felipe Fontana 4336a8f537 Correção dashboard 10 months ago
Felipe Fontana 1c1b2b7074 correção links e dashboard agentes operantes 10 months ago
Felipe Fontana f2e0bf8645 corrigindo link api 10 months ago
Felipe Fontana 088de5ad1a correção pausa/desconectar/despausar agentes dashboard e correção cadastro redirects 10 months ago
Felipe Fontana 7238b9ff09 corringo conectar agente quando tiver apenas um channel 10 months ago
Felipe Fontana 8e9bbf57ad corrigindo conectar agente 10 months ago
Felipe Fontana 4c0bf1d1ed Merge branch 'Felipe' into Saas 10 months ago
Felipe Fontana ee5be98117 Atualização dashboard 10 months ago
Felipe Fontana 56a3c808a0 Atualização criar e editar usuarios com grupos 10 months ago
Felipe Fontana 90e2ebe86e Merge branch 'Felipe' into Saas 10 months ago
Felipe Fontana 25a4411a76 Crud para number channel, e correção do bug ao passar o number channel para conectar no client 10 months ago
Felipe Fontana f28f6a1891 adicionado env app_url_client no env.example env example 11 months ago
Felipe Fontana f8b1a3949e corrigindo variavel url client chumbada 11 months ago
Felipe Fontana 542de3d95c Alterações para funcionar com o status CANCELADO e alterações para cadastrar o timeout EMESPERA 11 months ago
Felipe Fontana 61c46235f0 Alteração para não deixar ultrapassar o limite de numeros dos campos de configuração 11 months ago
Felipe Fontana 9f4638f49e Atualizando controllers, templates e lógicas 11 months ago
guilherme.guia 2802c1dd7b Alteracao para otimizacao 12 months ago
guilherme.guia 4e2868a945 Habilitando registros de atendimentos atuais para o agente 12 months ago
guilherme.guia 7d74fb1c73 Adicionando registro de atendimentos abandonadas ou perdidos no dashboard 12 months ago
guilherme.guia 7a2c01959c Retirando logs 12 months ago
guilherme.guia 9e43f8e67a Resolvendo bug de renderizacao de filas desativadas 12 months ago
guilherme.guia 3c2fc4cbc6 Arrumando bug de cores no grafico 12 months ago
guilherme.guia f055ccd009 Resolvendo bug ao tentar filtrar elementos 12 months ago
guilherme.guia edc371b8d6 Corrigindo bug de cadastro de redirect e redirect options 12 months ago
guilherme.guia d8963d5f83 Retirando logs 12 months ago
guilherme.guia d88a7c507a Correcao de escrita e permissão de template 12 months ago
guilherme.guia fd86285703 Mudancao na busca e cadastro de agentes 12 months ago
guilherme.guia d7f2b065c3 Correcao em desconectar e pausa do agente 12 months ago
guilherme.guia 273c8de4cd Adicionando campos de websocket no arquivo env 12 months ago
guilherme.guia 47b440305a Retirando variavel indefinida da view 12 months ago
guilherme.guia 21cfb6a281 Ajuste em filtro de relatorio historico de atendimentos 12 months ago
guilherme.guia 87eeb61aa9 Correcao de bug conectar agente 1 year ago
guilherme.guia 84b8af207d Adicionando filtro em relatorio de historico de atendimento 1 year ago
guilherme.guia dc1fcb3311 Funcionalidade de autorizacao ACL via gate 1 year ago
guilherme.guia 5688962f5a Adicionando funcionalidade de relatorio 1 year ago
guilherme.guia 656d55ee7a Adicionado views de relatorio 1 year ago
guilherme.guia 69402d0431 Adicionando array de eventos do chat 1 year ago
guilherme.guia b1c2133ffa Adicionando funcao que retorna o token 1 year ago
guilherme.guia 793e0f36f4 Adicionando imagens para usar no relatorio de historico de conversa 1 year ago
guilherme.guia 492f3f922d Correcao nome de variavel 1 year ago
guilherme.guia 50d12f88b5 Correcao rota de login para controller 1 year ago
guilherme.guia a42f9233c7 Organizacao de pastas Controllers 1 year ago
guilherme.guia a28dc5cea7 Alteracao caminho das views 1 year ago
guilherme.guia b26ea20634 Renomeando nome das pastas para organizacao de templates 1 year ago
guilherme.guia 63eff64cc0 Adicionando o provider Crypt como helper 1 year ago
guilherme.guia 1ffd8ba642 Adicionando Tokens no sistema 1 year ago
guilherme.guia db4c3da1cc Adicionando funcionalidade de conectar chat via portal 1 year ago
guilherme.guia 78476f0828 Adicionando menu flutuante e drop sidebar 1 year ago
guilherme guia 8dcf9dc5dc Correcao de layout 1 year ago
guilherme guia 7b70727d45 Correcao de bug de cadastro de agentes 1 year ago
guilherme guia 0353262f02 Adicionando funcionalidade de atualizacao assincrona de templates via command script 1 year ago
guilherme guia 4210d23d19 Corrigindo tela de templates e alterando regra de negocio de templates 1 year ago
guilherme guia 0673aa78e7 Corrigindo bugs Models 1 year ago
guilherme guia d9b61845f7 Retirando modulo de steps que foi inativado do sistema 1 year ago
guilherme guia 4d826185b5 Correcao de bug modal na tela de dashboard 1 year ago
guilherme guia a4cc8a979b Alteracao em campo de numero para contato 1 year ago
guilherme guia d0cb6418f6 organizacao de pastas das views e scripts js 1 year ago
guilherme guia 26013d5c4e Adicionando tela templates 1 year ago
guilherme guia 587f956aa4 Adicionado tela de contatos 1 year ago
guilherme guia 51c19af996 Adicionando tela de graficos 1 year ago
guilherme guia 59b92d6598 Correção de atualização 1 year ago
  1. 15
      .env.example
  2. 2
      Dockerfile
  3. 60
      app/Console/Commands/AtualizaTemplates.php
  4. 1
      app/Console/Kernel.php
  5. 2
      app/Helpers/Crypt.php
  6. 26
      app/Helpers/Helper.php
  7. 161
      app/Http/Controllers/Admin/AgentesController.php
  8. 77
      app/Http/Controllers/Admin/ConectarAgenteController.php
  9. 29
      app/Http/Controllers/Admin/ConfigAtendimentoController.php
  10. 149
      app/Http/Controllers/Admin/ContatosController.php
  11. 390
      app/Http/Controllers/Admin/DashboardController.php
  12. 70
      app/Http/Controllers/Admin/EmpresaController.php
  13. 48
      app/Http/Controllers/Admin/FilasController.php
  14. 101
      app/Http/Controllers/Admin/GraficosController.php
  15. 205
      app/Http/Controllers/Admin/HorariosController.php
  16. 192
      app/Http/Controllers/Admin/HorariosOptionController.php
  17. 142
      app/Http/Controllers/Admin/NumberChannelController.php
  18. 73
      app/Http/Controllers/Admin/PausasController.php
  19. 57
      app/Http/Controllers/Admin/ProfileController.php
  20. 80
      app/Http/Controllers/Admin/RedirectController.php
  21. 104
      app/Http/Controllers/Admin/RedirectOptionController.php
  22. 49
      app/Http/Controllers/Admin/Relatorios/RelatorioAtendimentosAbandonadosController.php
  23. 62
      app/Http/Controllers/Admin/Relatorios/RelatorioHistoricoAtendimentoController.php
  24. 54
      app/Http/Controllers/Admin/Relatorios/RelatorioProducaoAgenteController.php
  25. 17
      app/Http/Controllers/Admin/RelatoriosController.php
  26. 129
      app/Http/Controllers/Admin/SupervisorController.php
  27. 60
      app/Http/Controllers/Admin/SystemMessageController.php
  28. 83
      app/Http/Controllers/Admin/TemplatesController.php
  29. 193
      app/Http/Controllers/AgentesController.php
  30. 7
      app/Http/Controllers/Auth/AuthenticatedSessionController.php
  31. 210
      app/Http/Controllers/DashboardController.php
  32. 10
      app/Http/Controllers/GruposController.php
  33. 0
      app/Http/Controllers/ProfileController.php
  34. 97
      app/Http/Controllers/SupervisorController.php
  35. 2
      app/Http/Requests/ProfileUpdateRequest.php
  36. 135
      app/Models/Agentes.php
  37. 274
      app/Models/Atendimentos.php
  38. 4
      app/Models/ConfigAtendimento.php
  39. 43
      app/Models/Contatos.php
  40. 43
      app/Models/Filas.php
  41. 41
      app/Models/Grupos.php
  42. 109
      app/Models/Horario.php
  43. 126
      app/Models/HorarioOption.php
  44. 39
      app/Models/Messages.php
  45. 23
      app/Models/NumberChanel.php
  46. 107
      app/Models/NumberChannel.php
  47. 24
      app/Models/Permissoes.php
  48. 59
      app/Models/Redirect.php
  49. 45
      app/Models/RedirectOption.php
  50. 32
      app/Models/Templates.php
  51. 14
      app/Models/Tokens.php
  52. 12
      app/Models/User.php
  53. 9
      app/Providers/AuthServiceProvider.php
  54. 52
      app/Traits/AuthToken.php
  55. 5
      config/constants.php
  56. 23
      config/event.php
  57. BIN
      public/img/filtro-limpo.png
  58. BIN
      public/img/icons/csv-file.png
  59. BIN
      public/img/icons/doc-file.png
  60. BIN
      public/img/icons/notfound-file.png
  61. BIN
      public/img/icons/pdf-file.png
  62. BIN
      public/img/icons/ppt-file.png
  63. BIN
      public/img/icons/txt-file.png
  64. BIN
      public/img/icons/xls-file.png
  65. BIN
      public/img/icons/zip-file.png
  66. 7
      public/js/relatorios/historicoChamadas/accordion.js
  67. 152
      public/js/relatorios/historicoChamadas/index.js
  68. 37
      public/js/views/agentes/atualizaAgente.js
  69. 230
      public/js/views/agentesLogados/index.js
  70. 7
      public/js/views/app/selectTheme.js
  71. 29
      public/js/views/channels/atualizaChannel.js
  72. 31
      public/js/views/contatos/atualizaContato.js
  73. 66
      public/js/views/dashboard/index.js
  74. 21
      public/js/views/empresa/atualizaEmpresa.js
  75. 26
      public/js/views/fila/atualizaFila.js
  76. 204
      public/js/views/graficos/index.js
  77. 59
      public/js/views/horarios/atualizaHorarios.js
  78. 36
      public/js/views/horarios/montaSelect.js
  79. 13
      public/js/views/horarios/requestType.js
  80. 88
      public/js/views/horariosOption/atualizaHorariosOptions.js
  81. 37
      public/js/views/horariosOption/montaSelect.js
  82. 13
      public/js/views/horariosOption/requestType.js
  83. 12
      public/js/views/menu/index.js
  84. 20
      public/js/views/navegation/index.js
  85. 24
      public/js/views/pausa/atualizaPausa.js
  86. 59
      public/js/views/redirect/atualizaRedirect.js
  87. 48
      public/js/views/redirectOption/atualizaRedirectOption.js
  88. 35
      public/js/views/redirectOption/montaSelect.js
  89. 13
      public/js/views/redirectOption/requestType.js
  90. 14
      public/js/views/systemMessages/cadastroSystemMessage.js
  91. 351
      public/js/views/templates/cadastroTemplates.js
  92. 109
      public/js/views/templates/index.js
  93. 79
      resources/css/app.css
  94. 158
      resources/views/admin/agentesLogados.blade.php
  95. 93
      resources/views/admin/cadastros/channels.blade.php
  96. 85
      resources/views/admin/cadastros/contatos.blade.php
  97. 61
      resources/views/admin/cadastros/empresas.blade.php
  98. 52
      resources/views/admin/cadastros/filas.blade.php
  99. 102
      resources/views/admin/cadastros/horarios.blade.php
  100. 140
      resources/views/admin/cadastros/horariosOption.blade.php
  101. Some files were not shown because too many files have changed in this diff Show More

15
.env.example

@ -3,17 +3,20 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=
APP_URL_WEBSOCKET=
APP_URL_WEBSOCKET=
APP_URL_CLIENT=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432
DB_DATABASE=pbx
DB_USERNAME=contacte
DB_PASSWORD=ctepgSQL
BROADCAST_DRIVER=log
CACHE_DRIVER=file

2
Dockerfile

@ -1,4 +1,6 @@
FROM php:8.1-fpm
ENV TZ=America/Cuiaba
SHELL ["/bin/bash", "--login", "-c"]
ARG user=contacte

60
app/Console/Commands/AtualizaTemplates.php

@ -0,0 +1,60 @@
<?php
namespace App\Console\Commands;
use App\Models\Templates;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\DB;
class AtualizaTemplates extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:atualiza-templates';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Requisição assincrona que recupera todos os templates e depois atualiza os que estão cadastrados no banco';
/**
* Execute the console command.
*/
public function handle()
{
$empresas = DB::table('empresa')->orderBy('id', 'desc')->get();
foreach ($empresas as $empresa){
$channels = DB::table('number_channel')->where('id_empresa', $empresa->id)
->where('channel', 'positus')->get();
foreach ($channels as $channel) {
$data = $this->getTemplates($channel);
if ($data) {
foreach ($data as $value) {
$template = Templates::findById($value['id']);
if($template){
Templates::where("id", $value['id'])
->update([
'status' => $value['status']['code'],
]);
}
}
}
}
}
}
private function getTemplates($channel){
$response = Http::withToken($channel->token)->get("https://api.positus.global/v2/workspaces/{$channel->work_space}/message-templates");
if (isset($response->json()['data'])){
return $response->json()['data'];
} else {
return null;
}
}
}

1
app/Console/Kernel.php

@ -13,6 +13,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
$schedule->command('app:atualiza-templates')->everyMinute();
}
/**

2
app/Helpers/Crypt.php

@ -1,5 +1,5 @@
<?php
namespace app\Helpers;
namespace App\Helpers;
/**
* Description of Cripto

26
app/Helpers/Helper.php

@ -27,4 +27,30 @@ class Helper
public static function last_day_in_mouth($month, $year){
return $month == 2 ? ($year % 4 ? 28 : ($year % 100 ? 29 : ($year % 400 ? 28 : 29))) : (($month - 1) % 7 % 2 ? 30 : 31);
}
public static function getIdEmpresa()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
return $id_empresa;
}
public static function getEmpresas()
{
$user = auth()->user();
$empresas = $user->empresa;
if ($empresas->count() > 1) {
return $empresas;
} else {
$empresa = Helper::getIdEmpresa();
return $empresa;
}
}
public static function Crypt()
{
return new Crypt('aes-256-cbc', config('constants.PASSWORD'));
}
}

161
app/Http/Controllers/Admin/AgentesController.php

@ -0,0 +1,161 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Grupos;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class AgentesController extends Controller
{
public function __construct(
protected Agentes $agenteRepository,
protected Grupos $gruposRepository,
) {
}
public function index(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_agentes')){
abort(404);
}
$id_empresa = Helper::getIdEmpresa();
$search = strtolower($request->pesquisa);
$status = $request->status === "desativado" ? false : true;
$grupos = $this->gruposRepository->getPermissoesCriarGrupo($user);
$agentes = $this->agenteRepository->list(["id_empresa" => $id_empresa, "status" => $status, "search" => $search]);
$consulta_matricula = $this->agenteRepository->getMaiorMatricula($id_empresa);
$maior_matricula = $consulta_matricula->maior_matricula + 1;
$grupoId = $this->gruposRepository->getUserGrupoID($user->id);
$changePassword = false;
if ($grupoId == 4 || $grupoId == 1) {
$changePassword = true;
}
return view('admin.cadastros.users', compact('agentes', 'status', 'maior_matricula', 'search', 'grupos', 'changePassword'));
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_agentes')){
abort(404);
}
$request->validate([
'nome' => ['required'],
'email' => ['required', Rule::unique('usuarios')],
'senha' => ['required'],
'matricula' => ['required'],
'status' => ['required'],
'id_grupo' => ['required'],
]);
$status = $request->status === 'on' ? true : false;
$id_empresa = Helper::getIdEmpresa();
$isMatriculaRegister = $this->agenteRepository->get(['matricula' => $request->matricula]);
if ($isMatriculaRegister) {
return redirect()->back()->with('status', ' matricula vinculada com outro agente');
}
$created = $this->agenteRepository->create($request->nome, $request->email, $request->senha, $request->matricula, $status, $id_empresa, $request->id_grupo);
if(!$created){
return redirect()->back()->with('status', 'Erro ao tentar criar agente');
}
return redirect()->back()->with('status', ' Cadastrado com sucesso');
}
public function edit($id)
{
$user = auth()->user();
$grupo_id = $this->gruposRepository->getUserGrupoID($id);
if(Gate::forUser($user)->denies('edit_agentes')){
abort(404);
}
if (empty($id)) {
return redirect('users');
}
$userData = Agentes::find($id);
$userData->grupo_id = $grupo_id;
$response = [
'status' => true,
'data' => $userData
];
return response()->json($response);
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_agentes')){
abort(404);
}
if (empty($id)) {
return redirect('users');
}
Agentes::where('id', $id)->update([
"status" => false
]);
return redirect('users')->with('status', 'desativado com sucesso');
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_agentes')){
abort(404);
}
$request->validate([
"nome" => ['required'],
"status" => ['required'],
"id_grupo" => ['required'],
"email" => ['required'],
"matricula" => ['required']
]);
if (empty($id) || empty($request->nome)) {
return redirect('users');
}
$status = $request->status === "on" ? true : false;
$agente_matricula = Agentes::select('matricula')
->where('id', $id)
->get()
->first();
if ($agente_matricula->matricula != $request->matricula) {
$isMatriculaRegister = Agentes::where('matricula', $request->matricula)->get()->count();
if ($isMatriculaRegister > 0) {
return redirect()->back()->with('status', ' matricula vinculada com outro agente');
}
$this->agenteRepository->edit($id, $request->nome, $request->id_grupo, $status, $request->senha, $request->matricula, $request->email);
} else {
$this->agenteRepository->edit($id, $request->nome, $request->id_grupo, $status, $request->senha, $request->matricula, $request->email);
}
return redirect('users')->with('status', 'atualizado com sucesso');
}
}

77
app/Http/Controllers/Admin/ConectarAgenteController.php

@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Filas;
use App\Models\NumberChannel;
use App\Models\Tokens;
use App\Traits\AuthToken;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ConectarAgenteController extends Controller
{
use AuthToken;
public function __construct(
protected Filas $queueRepository,
protected NumberChannel $numberChannelRepository,
protected Tokens $tokenRepository,
) {
}
public function index(){
$idEmpresa = Helper::getIdEmpresa();
$filas = $this->queueRepository->list(["id_empresa" => $idEmpresa, "status" => true]);
$channels = $this->numberChannelRepository->list(["id_empresa" => $idEmpresa, "status" => true]);
return view("admin.conectarAgente.conectarAgente", compact('filas', 'channels'));
}
public function conectar(Request $request){
$queueId = $request->queue_id;
if(empty($queueId)){
return response()->json("Parametro id é obrigatório", 404);
}
$fila = $this->queueRepository->get(["id" => $queueId]);
if(empty($fila)){
return response()->json("Fila não foi encontrada", 404);
}
$user = Auth::user();
if(empty($user)){
return response()->json("Usuário não foi encontrado", 404);
}
$idEmpresa = Helper::getIdEmpresa();
if ($request->channel_id) {
$numberChannelId = $request->channel_id;
} else {
$numberChannelId = $this->numberChannelRepository->where('id_empresa', $idEmpresa)->where('status', true)->value('id');
}
if(empty($numberChannelId)){
return response()->json("Numberchannel não foi encontrado", 404);
}
$tokenDB = $this->getToken($user->id);
$info = [
"id_empresa" => $idEmpresa,
"id_number" => $numberChannelId,
"fila" => $queueId,
"servidor" => env('APP_URL'),
"websocket" => env('APP_URL_WEBSOCKET'),
"matricula" => $user->matricula,
"token" => $tokenDB->token,
"expire" => $tokenDB->expired_at,
];
$tokenAgente = Helper::Crypt()->encrypt(json_encode($info, true));
return response()->json($tokenAgente);
}
}

29
app/Http/Controllers/ConfigAtendimentoController.php → app/Http/Controllers/Admin/ConfigAtendimentoController.php

@ -1,57 +1,46 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\ConfigAtendimento;
use Illuminate\Http\Request;
class ConfigAtendimentoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$configAtendimento = ConfigAtendimento::where('id_empresa', $id_empresa)->first();
return view('admin.configs', compact('configAtendimento'));
return view('admin.configs.configs', compact('configAtendimento'));
}
/**
* Show the form for creating a new resource.
*/
public function create(Request $request)
{
//
$request->validate([
"qtde_atendimento" => ['required'],
"timeout_cliente" => ['required'],
"timeout_agente" => ['required'],
"timeout_espera" => ['required'],
"timeout_supervisor" => ['required'],
"id_empresa" => ['required']
]);
if (empty($request->qtde_atendimento) || empty($request->timeout_cliente) || empty($request->timeout_agente) || empty($request->id_empresa)) {
if (empty($request->qtde_atendimento) || empty($request->timeout_cliente) || empty($request->timeout_espera) || empty($request->timeout_agente) || empty($request->id_empresa) || empty($request->timeout_supervisor)) {
return redirect('configs');
}
ConfigAtendimento::where('id_empresa', '=', $request->id_empresa)
->update([
"quantidade_simutaneo" => $request->qtde_atendimento,
"timeout_client" => $request->timeout_cliente,
"timeout_agent" => $request->timeout_agente
"timeout_agent" => $request->timeout_agente,
"timeout_espera" => $request->timeout_espera,
"timeout_supervisor" => $request->timeout_supervisor,
]);
return redirect('configs');
}
}

149
app/Http/Controllers/Admin/ContatosController.php

@ -0,0 +1,149 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Contatos;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class ContatosController extends Controller
{
public function index(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_contatos')){
abort(404);
}
$id_empresa = Helper::getIdEmpresa();
$search = strtolower($request->pesquisa);
$status = $request->status === "desativado" ? false : true;
$contatos = Contatos::where(function ($query) use ($search, $id_empresa, $status){
$query->where('status', $status);
$query->where("id_empresa", $id_empresa);
if($search){
$query->where(DB::raw('LOWER(nome)'), 'LIKE', "%{$search}%");
$query->orWhere('email', $search);
}
})->get();
foreach ($contatos as $contato) {
$contato->formated_date = $contato->created_at->format('d M Y');
}
return view("admin.cadastros.contatos", compact('contatos', 'status'));
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_contatos')){
abort(404);
}
$request->validate([
'nome' => ['required'],
'email' => ['nullable', 'email', Rule::unique('contatos')],
'contato' => ['required', Rule::unique('contatos')],
'status' => ['required'],
]);
$status = $request->status === 'on' ? true : false;
$user = auth()->user();
$id_empresa = Helper::getIdEmpresa();
$telefoneDesformatado = preg_replace('/[^0-9]/', '', $request->contato);
Contatos::create([
'nome' => $request->nome,
'email' => $request->email,
'contato' => $telefoneDesformatado,
'status' => $status,
'notes' => $request->descricao,
'id_empresa' => $id_empresa,
'id_agente' => $user->id,
]);
return redirect()->back()->with('status', 'Contato cadastrado');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_contatos')){
abort(404);
}
if (empty($id)) {
return response()->json(['data' => 'Parametro ID_CONTATO é obrigatório']);
}
$contatosData = Contatos::find($id);
$response = [
'data' => [
'status' => true,
'contato_data' => $contatosData
],
];
return response()->json($response);
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_contatos')){
abort(404);
}
$request->validate([
'nome' => ['required'],
'email' => ['nullable', 'email', Rule::unique('contatos')],
'contato' => ['required'],
'status' => ['required'],
]);
$status = $request->status === "on" ? true : false;
$telefoneDesformatado = preg_replace('/[^0-9]/', '', $request->contato);
Contatos::where("id", $id)
->update([
'nome' => $request->nome,
'email' => $request->email,
'contato' => $telefoneDesformatado,
'status' => $status,
'notes' => $request->descricao,
]);
return redirect()->back()->with('status', 'Atualizado com sucesso');
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_contatos')){
abort(404);
}
$contato = Contatos::where('id', $id);
$rowCount = $contato->count();
if ($rowCount > 0) {
$contato->update([
'status' => false
]);
return redirect()->back()->with('status', 'Contato desativado');
}
return redirect()->back();
}
}

390
app/Http/Controllers/Admin/DashboardController.php

@ -0,0 +1,390 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Filas;
use App\Models\Pausas;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
use App\Models\Tokens;
use App\Traits\AuthToken;
class DashboardController extends Controller
{
use AuthToken;
public function __construct(
protected Filas $queueRepository,
protected Tokens $tokenRepository,
) {
}
public function index()
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_dashboard')){
return redirect('conectarAgente');
}
$id_empresa = Helper::getIdEmpresa();
$pausas = Pausas::where(["id_empresa" => $id_empresa, "is_ativo" => true])->get();
return view('admin.dashboard.dashboard', compact('pausas'));
}
public function getRelatorioDados()
{
$id_empresa = Helper::getIdEmpresa();
$atendimentosData = DB::table('usuarios as u')
->leftJoin('atendimento as a', 'u.id', '=', 'a.id_usuario')
->select('u.nome', DB::raw('count(a.id_usuario) as qtde_atendimento'))
->where('a.id_empresa', $id_empresa)
->groupBy('u.nome')
->get();
$atendimentosStatusData = DB::table('usuarios as u')
->join('usuario_empresa as ue', 'ue.id_usuario', '=', 'u.id')
->join('eventos_atendimento as ea', 'ea.id_usuario', '=', 'u.id')
->select('ea.evento', DB::raw('count(ea.evento) as qtde_usados'))
->where('ue.id_empresa', $id_empresa)
->groupBy('ea.evento')
->get();
$filasMaisUsadas = DB::table('queues as q')
->join('eventos_atendimento as ea', 'ea.id_queue', '=', 'q.id')
->select('q.nome', DB::raw('count(ea.id_queue) as qtde_fila'))
->where('q.id_empresa', $id_empresa)
->groupBy('q.nome')
->get();
$data_inicio = date("Y-m-01");
$data_atual = date('Y-m-d');
$data_fim = Helper::last_date_in_mouth();
$atendimentosPorMes = $this->getAtendimentosMes($data_inicio, $data_fim);
$atendimentosDia = $this->getAtendimentoDia($data_atual);
$totalAgentesOperantes = $this->getAgentesOperantes();
$totalAgentesInoperantes = $this->getAgentesInoperantes();
$relatoriosAtendimento = $this->relatoriosDados();
$total_atendimentos = $this->totalAtendimentos($atendimentosData);
$data = [
'atendimentosData' => $atendimentosData,
'atendimentosStatusData' => $atendimentosStatusData,
'filasMaisUsadas' => $filasMaisUsadas,
'atendimentosPorMes' => $atendimentosPorMes,
'atendimentosDia' => $atendimentosDia,
'totalAgentesOperantes' => $totalAgentesOperantes,
'totalAgentesInoperantes' => $totalAgentesInoperantes,
'relatoriosAtendimento' => $relatoriosAtendimento,
'total_atendimentos' => $total_atendimentos,
];
return response()->json(['status' => true, 'data' => $data]);
}
private function totalAtendimentos($atendimentos)
{
$total_atendimentos = 0;
foreach ($atendimentos as $atendimento) {
$total_atendimentos += $atendimento->qtde_atendimento;
}
return $total_atendimentos;
}
private function relatoriosDados()
{
$id_empresa = Helper::getIdEmpresa();
$relatorios = DB::select("
SELECT
q.id, q.nome,
SUM(CASE WHEN evento = 'EMESPERA' THEN 1 ELSE 0 END) AS em_espera,
SUM(CASE WHEN evento = 'START' THEN 1 ELSE 0 END) AS iniciadas,
SUM(CASE WHEN evento = 'SENDED' THEN 1 ELSE 0 END) AS enviados,
SUM(CASE WHEN evento IN ('COMPLETE_AGENT','COMPLETE_CALLER','TIMEOUT_CALLER') THEN 1 ELSE 0 END) AS finalizadas,
SUM(CASE WHEN evento = 'ABANDON' THEN 1 ELSE 0 END) AS abandonadas,
SUM(CASE WHEN evento = 'CANCELADO' THEN 1 ELSE 0 END) AS cancelado
FROM atendimento a
INNER JOIN eventos_atendimento ea ON ea.uniqueid = a.uniqueid and ea.id = (SELECT MAX(id) FROM eventos_atendimento ee WHERE ee.uniqueid = ea.uniqueid)
INNER JOIN queues q on q.id = ea.id_queue
WHERE a.data_reg::date = CURRENT_DATE
AND a.id_empresa = :id_empresa
GROUP BY q.nome, q.id
ORDER BY q.id
", ['id_empresa' => $id_empresa]);
$filas = DB::select("
SELECT
q.nome, q.id, count(s.nome) as agentes
FROM
queues q
LEFT JOIN supervisor s on s.fila = q.nome
WHERE q.id_empresa = :id_empresa AND q.is_ativa = true
GROUP BY q.nome, q.id
", ['id_empresa' => $id_empresa]);
$relatoriosPorId = [];
foreach ($relatorios as $relatorio) {
$relatoriosPorId[$relatorio->id] = [
'em_espera' => $relatorio->em_espera,
'iniciadas' => $relatorio->iniciadas,
'enviadas' => $relatorio->enviados,
'finalizadas' => $relatorio->finalizadas,
'abandonadas' => $relatorio->abandonadas,
'canceladas' => $relatorio->cancelado,
];
}
foreach ($filas as &$fila) {
$idFila = $fila->id;
if (isset($relatoriosPorId[$idFila])) {
$fila->em_espera = $relatoriosPorId[$idFila]['em_espera'];
$fila->iniciadas = $relatoriosPorId[$idFila]['iniciadas'];
$fila->enviadas = $relatoriosPorId[$idFila]['enviadas'];
$fila->finalizadas = $relatoriosPorId[$idFila]['finalizadas'];
$fila->abandonadas = $relatoriosPorId[$idFila]['abandonadas'];
$fila->canceladas = $relatoriosPorId[$idFila]['canceladas'];
} else {
$fila->em_espera = 0;
$fila->iniciadas = 0;
$fila->enviadas = 0;
$fila->finalizadas = 0;
$fila->abandonadas = 0;
$fila->canceladas = 0;
}
}
return $filas;
}
private function getAtendimentosMes($data_inicio, $data_fim)
{
$id_empresa = Helper::getIdEmpresa();
$atendimentosPorMes = DB::select("
select count (*) as total from atendimento a
inner join eventos_atendimento ea on a.uniqueid = ea.uniqueid
where a.data_reg::date >= :data_inicio and a.data_reg::date <= :data_fim
and ea.id = (select MAX(id) from eventos_atendimento ee where ee.uniqueid = ea.uniqueid)
and evento not in ('EMESPERA', 'LOST_CONNECTION', 'SENDED', 'CANCELADO', 'ABANDON')
and id_empresa = :id_empresa
", ['id_empresa' => $id_empresa, 'data_inicio' => $data_inicio, 'data_fim' => $data_fim]);
return $atendimentosPorMes[0]->total;
}
private function getAtendimentoDia($data_atual)
{
$id_empresa = Helper::getIdEmpresa();
$atendimentoDia = DB::select("
select count (*) as total from atendimento a
inner join eventos_atendimento ea on a.uniqueid = ea.uniqueid
where a.data_reg::date = :data_atual
and ea.id = (select MAX(id) from eventos_atendimento ee where ee.uniqueid = ea.uniqueid)
and evento not in ('EMESPERA', 'LOST_CONNECTION', 'SENDED', 'CANCELADO', 'ABANDON')
and id_empresa = :id_empresa
", ['id_empresa' => $id_empresa, 'data_atual' => $data_atual]);
return $atendimentoDia[0]->total;
}
private function getAgentesOperantes()
{
$id_empresa = Helper::getIdEmpresa();
$totalAgentesOperantes = DB::select("
SELECT COUNT(DISTINCT s.id_usuario) AS total
FROM supervisor s
INNER JOIN atendimento a ON a.matricula = s.matricula
LEFT JOIN eventos_atendimento ea ON ea.uniqueid = a.uniqueid
WHERE ea.id = (SELECT MAX(id) FROM eventos_atendimento ee WHERE ee.uniqueid = ea.uniqueid)
AND (evento = 'START' AND status IN ('INDISPONIVEL') AND s.id_empresa = :id_empresa) OR
(status IN ('LIVRE', 'OCUPADO') AND s.id_empresa = :id_empresa)
", ['id_empresa' => $id_empresa]);
return $totalAgentesOperantes[0]->total;
}
private function getAgentesInoperantes()
{
$id_empresa = Helper::getIdEmpresa();
$totalAgentesInoperantes = DB::select("
SELECT COUNT(s.id_usuario) AS total
FROM supervisor s
WHERE status IN ('INDISPONIVEL', 'PAUSA')
AND NOT EXISTS (
SELECT 1
FROM atendimento a
LEFT JOIN eventos_atendimento ea ON ea.uniqueid = a.uniqueid
WHERE ea.id = (SELECT MAX(id) FROM eventos_atendimento ee WHERE ee.uniqueid = ea.uniqueid)
AND evento = 'START'
AND a.matricula = s.matricula
)
AND s.id_empresa = :id_empresa
", ['id_empresa' => $id_empresa]);
return $totalAgentesInoperantes[0]->total;
}
public function agentesLogados($id)
{
if (empty($id)) {
return redirect()->back();
}
$id_empresa = Helper::getIdEmpresa();
$fila = Filas::where(['id' => $id, 'id_empresa' => $id_empresa])->first();
$pausas = Pausas::where(["id_empresa" => $id_empresa, "is_ativo" => true])->get();
$filas = $this->queueRepository->list(['id_empresa' => $id_empresa]);
$atendimentos_espera = $this->getAtendimentosEmEsperaFila($id);
return view('admin.dashboard.agentesLogados', compact('fila', 'pausas', 'atendimentos_espera', 'filas'));
}
public function relatoriosFilas($id)
{
if (empty($id)) {
return response()->json(['data' => "Parametro ID é obrigatorio"]);
}
$id_empresa = Helper::getIdEmpresa();
$fila = Filas::where('id', $id)->where('id_empresa', $id_empresa)->first();
$agentesNaFila = DB::table("supervisor as s")
->leftJoin("queues as q", "q.nome", "=", "s.fila")
->join("tokens as t", "t.id_usuario", "=", "s.id_usuario")
->select(
"s.id",
"s.nome",
"s.matricula",
"s.motivo_pausa",
"s.fila",
"s.status",
"t.token",
"q.id as id_queue",
DB::raw("(now() - s.tempo_login) AS login"),
DB::raw(
"(
SELECT COUNT(*)
FROM atendimento AS ma
JOIN (
SELECT uniqueid, MAX(id) AS max_id
FROM eventos_atendimento
GROUP BY uniqueid
) AS m2_max
ON ma.uniqueid = m2_max.uniqueid
JOIN eventos_atendimento AS m2
ON ma.uniqueid = m2.uniqueid AND m2.id = m2_max.max_id
WHERE ma.matricula = s.matricula AND m2.evento = 'START'
) as atendimentos_atuais"
)
)
->where([
["s.id_empresa", "=", $id_empresa],
['q.nome', $fila->nome],
])
->groupBy('s.id', 't.token', "q.id")
->get();
return response()->json(['data' => $agentesNaFila]);
}
public function getAtendimentosEmEsperaFila($id_fila)
{
$atendimentos_espera = DB::select("
SELECT
ea.uniqueid, ea.id, a.nome, a.cliente_id, ea.data_evento
FROM atendimento a
INNER JOIN eventos_atendimento ea ON ea.uniqueid = a.uniqueid and ea.id = (SELECT MAX(id) FROM eventos_atendimento ee WHERE ee.uniqueid = ea.uniqueid)
WHERE
ea.id_queue = :id_fila and ea.evento = 'EMESPERA'
ORDER BY ea.id
", ['id_fila' => $id_fila]);
return $atendimentos_espera;
}
public function transferirFilaAtendimento(Request $request)
{
DB::beginTransaction();
try {
DB::table('eventos_atendimento')->where("uniqueid", $request->unique_id)
->update([
"id_queue" => $request->fila
]);
DB::commit();
return redirect()->back()->with('status', 'Atendimento transferido');
} catch (\Throwable $th) {
DB::rollBack();
return redirect()->back()->with('status', 'Erro ao transferir atendimento');
}
}
public function agenteAtendimentos($id, $matricula_agente)
{
if (empty($matricula_agente) || empty($id)) {
return response()->json(['data' => "Parametro ID é obrigatorio"]);
}
$atendimentos_agente = DB::select("
SELECT *
FROM atendimento AS ma
JOIN (
SELECT uniqueid, MAX(id) AS max_id
FROM eventos_atendimento
GROUP BY uniqueid
) AS m2_max
ON ma.uniqueid = m2_max.uniqueid
JOIN eventos_atendimento AS m2
ON ma.uniqueid = m2.uniqueid AND m2.id = m2_max.max_id
WHERE ma.matricula = :matricula_agente AND m2.evento = 'START'
", ['matricula_agente' => $matricula_agente]);
return response()->json(['data' => $atendimentos_agente]);
}
public function transferirAtendimento(Request $request)
{
$user = auth()->user();
$tokenDB = $this->getToken($user->id);
$httpClient = new Client();
$check = true;
$url = env('APP_URL');
$response = $httpClient->post("$url/api/v1/atendimentos/transferir", [
'headers' => [
'Authorization' => "Bearer $tokenDB->token",
],
'json' => [
'matricula_origem' => $request->origem,
'matricula_destino' => $request->destino,
'uniqueid' => $request->atendimento_unique_id
],
]);
if (!($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
$check = false;
}
if (!$check) {
return redirect()->back()->with('status', 'Erro ao tentar transferir o atendimento');
} else {
return redirect()->back()->with('status', 'O atendimento foi transferido com sucesso!');
}
}
}

70
app/Http/Controllers/EmpresaController.php → app/Http/Controllers/Admin/EmpresaController.php

@ -1,7 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Empresa;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@ -9,22 +10,31 @@ use Illuminate\Support\Facades\Gate;
class EmpresaController extends Controller
{
public function index()
public function index(Request $request)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('show_empresas')){
abort(404);
}
$empresas = DB::table('empresa')->orderBy('id', 'desc')->get();
return view('admin.empresas', compact('empresas'));
$search = strtolower($request->pesquisa);
$status = $request->status === "desativado" ? false : true;
$empresas = DB::table('empresa')
->orderBy('id', 'desc')
->when($search, function ($query) use ($search) {
return $query->where(DB::raw('LOWER(nome)'), 'like', '%' . $search . '%');
})
->where('status', $status)
->get();
return view('admin.cadastros.empresas', compact('empresas', 'status'));
}
public function store(Request $request)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('store_empresas')){
abort(404);
}
$request->validate([
@ -32,22 +42,30 @@ class EmpresaController extends Controller
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.Empresa::class],
'token' => ['required', 'string'],
'cnpj' => ['required', 'unique:'.Empresa::class],
'status' => ['required'],
]);
$cnpjSemMascara = preg_replace("/[^0-9]/", "", $request->cnpj);
$status = $request->status === 'on' ? true : false;
Empresa::create([
'nome' => $request->nome,
'token' => $request->token,
'email' => $request->email,
'cnpj' => $cnpjSemMascara,
'status' => $status
]);
return redirect()->back()->with('status', 'Cadastrado com sucesso');
return redirect('empresas')->with('status', 'Cadastrado com sucesso');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_empresas')){
abort(404);
}
if (empty($id)) {
return response()->json(['data' => 'Parametro ID é obrigatório']);
}
@ -63,21 +81,26 @@ class EmpresaController extends Controller
return response()->json($response);
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_empresas')){
abort(404);
}
if (!$id) {
return redirect()->back();
}
$empresa = Empresa::where("id", $id)->first();
$status = $request->status === "on" ? true : false;
$newCnpjOrEmail = $request->cnpj === $empresa->cnpj || $request->email === $empresa->email;
if($newCnpjOrEmail){
Empresa::where("id", $id)
->update([
'nome' => $request->nome,
'status' => $status,
]);
}else{
@ -85,6 +108,7 @@ class EmpresaController extends Controller
'nome' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.Empresa::class],
'cnpj' => ['required', 'unique:'.Empresa::class],
"status" => ['required']
]);
$cnpjSemMascara = preg_replace("/[^0-9]/", "", $request->cnpj);
@ -94,12 +118,28 @@ class EmpresaController extends Controller
'nome' => $request->nome,
'email' => $request->email,
'cnpj' => $cnpjSemMascara,
'status' => $status,
]);
}
return redirect()->back()->with('status', 'Atualizado com sucesso');
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_empresas')){
abort(404);
}
if (empty($id)) {
return redirect('empresas');
}
Empresa::where('id', $id)->update([
"status" => false
]);
return redirect('empresas')->with('status', 'desativado com sucesso');
}
}

48
app/Http/Controllers/FilasController.php → app/Http/Controllers/Admin/FilasController.php

@ -1,20 +1,23 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Filas;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class FilasController extends Controller
{
public function index(Request $request)
{
$selected = (object) ['isSelected' => false, 'isAtivo' => 'ativo'];
$user = auth()->user();
if(Gate::forUser($user)->denies('show_filas')){
abort(404);
}
// $filas = filas::where('is_ativa', true)->get();
$selected = (object) ['isSelected' => false, 'isAtivo' => 'ativo'];
$filas = filas::class;
$user = auth()->user();
@ -31,7 +34,6 @@ class FilasController extends Controller
$filas = $filas::where('is_ativa', true);
}
if ($request->pesquisa) {
$pesquisa = strtolower($request->pesquisa);
@ -40,18 +42,21 @@ class FilasController extends Controller
$filas = $filas->where('id_empresa', $id_empresa)->OrderBy('nome', 'asc')->get();
return view('admin.filas', compact('filas', 'selected'));
return view('admin.cadastros.filas', compact('filas', 'selected'));
}
public function create(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_filas')){
abort(404);
}
$request->validate([
'nome' => ['string', 'required'],
'motivo' => ['required']
]);
$is_ativa = $request->motivo === 'on' ? true : false;
if (empty($request->nome)) {
@ -62,8 +67,6 @@ class FilasController extends Controller
return redirect('filas')->with('status', 'Fila já cadastrada');
}
// se tornar reativo
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
@ -74,20 +77,21 @@ class FilasController extends Controller
'is_ativa' => $is_ativa
]);
return redirect('filas')->with('status', 'Fila cadastrada');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_filas')){
abort(404);
}
if (empty($id)) {
return redirect('filas');
}
$setor = Filas::find($id);
$response = [
'data' => $setor,
];
@ -97,8 +101,11 @@ class FilasController extends Controller
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_filas')){
abort(404);
}
if (empty($id)) {
return redirect('filas');
}
@ -112,13 +119,11 @@ class FilasController extends Controller
'status' => ['required']
]);
$isFilaRegister = Filas::select('nome')
->where('id', $id)
->get()
->first();
$is_ativa = $request->status === 'on' ? true : false;
if ($isFilaRegister->nome != strtoupper($request->nome)) {
@ -141,24 +146,23 @@ class FilasController extends Controller
return redirect('filas')->with('status', 'Fila Editada');
}
public function destroy($id)
{
$pesquisa = Filas::where('id', '=', $id);
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_filas')){
abort(404);
}
$pesquisa = Filas::where('id', '=', $id);
$setor = $pesquisa->get();
$rowCount = $setor->count();
if ($rowCount > 0) {
$pesquisa->update([
'is_ativa' => false
]);
}
// filas::where('id', '=', $id)->delete();
return redirect('filas')->with('status', 'Fila desativada');
}
}

101
app/Http/Controllers/Admin/GraficosController.php

@ -0,0 +1,101 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
class GraficosController extends Controller
{
public function index()
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_analytics')){
abort(404);
}
return view("admin.graficos.graficos");
}
public function show(Request $request)
{
$data = $this->getRelatorioDados($request->dataInicio, $request->dataFim);
return response()->json(['status' => true, 'data' => $data]);
}
private function getRelatorioDados($dataInicio, $dataFim)
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$atendimentosData = DB::table('usuarios as u')
->leftJoin('atendimento as a', 'u.id', '=', 'a.id_usuario')
->select('u.nome', DB::raw('count(a.id_usuario) as qtde_atendimento'))
->where('a.id_empresa', $id_empresa);
if ($dataInicio && $dataFim) {
$atendimentosData->whereBetween(DB::raw('a.data_reg::DATE'), [$dataInicio, $dataFim]);
} elseif ($dataInicio) {
$atendimentosData->where(DB::raw('a.data_reg::DATE'), '>=', $dataInicio);
} elseif ($dataFim) {
$atendimentosData->where(DB::raw('a.data_reg::DATE'), '<=', $dataFim);
}
$atendimentosData = $atendimentosData
->groupBy('u.nome')
->get();
$atendimentosStatusData = DB::table('usuarios as u')
->join('usuario_empresa as ue', 'ue.id_usuario', '=', 'u.id')
->join('eventos_atendimento as ea', 'ea.id_usuario', '=', 'u.id')
->select('ea.evento', DB::raw('count(ea.evento) as qtde_usados'))
->where([
['ue.id_empresa', '=', $id_empresa],
['ea.evento', '<>', config('event.CONF_EVENT_ESPERA')],
]);
if ($dataInicio && $dataFim) {
$atendimentosStatusData->whereBetween(DB::raw('ea.data_reg::DATE'), [$dataInicio, $dataFim]);
} elseif ($dataInicio) {
$atendimentosStatusData->where(DB::raw('ea.data_reg::DATE'), '>=', $dataInicio);
} elseif ($dataFim) {
$atendimentosStatusData->where(DB::raw('ea.data_reg::DATE'), '<=', $dataFim);
}
$atendimentosStatusData = $atendimentosStatusData
->groupBy('ea.evento')
->get();
$filasMaisUsadas = DB::table('queues as q')
->join('eventos_atendimento as ea', 'ea.id_queue', '=', 'q.id')
->select('q.nome', DB::raw('count(ea.id_queue) as qtde_fila'))
->where('q.id_empresa', $id_empresa);
if ($dataInicio && $dataFim) {
$filasMaisUsadas->whereBetween(DB::raw('ea.data_reg::DATE'), [$dataInicio, $dataFim]);
} elseif ($dataInicio) {
$filasMaisUsadas->where(DB::raw('ea.data_reg::DATE'), '>=', $dataInicio);
} elseif ($dataFim) {
$filasMaisUsadas->where(DB::raw('ea.data_reg::DATE'), '<=', $dataFim);
}
$filasMaisUsadas = $filasMaisUsadas
->groupBy('q.nome')
->get();
$data = [
'atendimentosData' => $atendimentosData,
'atendimentosStatusData' => $atendimentosStatusData,
'filasMaisUsadas' => $filasMaisUsadas,
];
return $data;
}
}

205
app/Http/Controllers/Admin/HorariosController.php

@ -0,0 +1,205 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\Types;
use App\Models\NumberChannel;
use App\Models\Horario;
use App\Models\Agentes;
use App\Models\Filas;
use App\Models\Redirect;
use App\Models\HorarioOption;
use Illuminate\Support\Facades\DB;
class HorariosController extends Controller
{
public function __construct(
protected Horario $horarioRepository,
protected Agentes $agenteRepository,
protected Filas $queueRepository,
protected Redirect $redirectRepository,
protected HorarioOption $horarioOptionRepository,
) {
}
public function index(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_redirect')){
abort(404);
}
$id_empresa = Helper::getEmpresas();
if (!is_int($id_empresa)){
$empresasIds = $id_empresa->pluck('id')->toArray();
} else {
$empresasIds = $id_empresa;
}
$types = Types::all();
$horarios = $this->horarioRepository->list(["id_empresa" => $empresasIds]);
$numberChannels = NumberChannel::whereIn('id_empresa', $empresasIds)->get();
return view('admin.cadastros.horarios', compact('horarios', 'numberChannels', 'types'));
}
public function show(Request $request)
{
$data = $request->all();
$id_type = $request->segment(2);
if (empty($id_type)) {
return response()->json(['data' => 'Parametro ID_TYPE é obrigatório']);
}
$type = DB::table('types')->where([['id', $id_type], ['status', true]])->first();
$typeName = $type->name;
$id_empresa = Helper::getIdEmpresa();
$data = null;
switch ($typeName) {
case 'queue':
$data = $this->queueRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'redirect':
$data = $this->redirectRepository->list(["id_empresa" => $id_empresa]);
break;
case 'agent':
$data = $this->agenteRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'horario':
$data = $this->horarioRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
default:
$data = null;
break;
}
$response = [
'data' => [
'status' => true,
'data' => $data,
],
];
return response()->json($response);
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_redirect')){
abort(404);
}
$request->validate([
'id_number' => ['required'],
'nome' => ['required', 'string'],
'opcao' => ['required'],
'acao' => ['required'],
'status' => ['required']
]);
$status = $request->status === "on" ? true : false;
$id = $request->acao;
if ($request->opcao == 3) {
$id = $this->agenteRepository->get(["id" => $request->acao])->matricula;
}
$created = $this->horarioRepository->create($request->id_number, strtoupper($request->nome), $request->opcao, $id, $status);
if(!$created){
redirect()->back()->with('status', 'Erro ao tentar cadastrar horário');
}
return redirect()->back()->with('status', 'Cadastrado com sucesso!');
}
public function edit(Request $request)
{
$id = $request->segment(2);
if (empty($id)) {
return response()->json(['data' => 'Parametro ID é obrigatório']);
}
$optionData = Horario::find($id);
$types = Types::all();
$id_empresa = Helper::getEmpresas();
if (!is_int($id_empresa)){
$empresasIds = $id_empresa->pluck('id')->toArray();
} else {
$empresasIds = $id_empresa;
}
$numberChannels = NumberChannel::whereIn('id_empresa', $empresasIds)->get();
$response = [
'data' => [
'status' => true,
'option_data' => $optionData,
'types' => $types,
'numberChannels' => $numberChannels
],
];
return response()->json($response);
}
public function update(Request $request)
{
$id = $request->segment(2);
if (!$id) {
return redirect()->back();
}
$request->validate([
'id_number' => ['required'],
'nome' => ['required', 'string'],
'opcao' => ['required'],
'acao' => ['required'],
'status' => ['required']
]);
$status = $request->status === "on" ? true : false;
$acao = $request->acao;
if ($request->opcao == 3) {
$acao = $this->agenteRepository->get(["id" => $request->acao])->matricula;
}
$created = $this->horarioRepository->atualizar($id, $request->id_number, $request->nome, $request->opcao, $acao, $status);
if(!$created){
redirect()->back()->with('status', 'Erro ao tentar atualizar Horario');
}
return redirect()->back()->with('status', 'Atualizado com sucesso');
}
public function destroy($horario_id)
{
if (!$horario_id) {
return redirect()->back();
}
$horariosOptions = $this->horarioOptionRepository->list(['id_horario' => $horario_id]);
if (count($horariosOptions) > 0) {
return redirect()->back()->with('status', 'É necessário deletar todos os subitens primeiro!');
}
$deleted = $this->horarioRepository->deletar($horario_id);
if(!$deleted){
return redirect()->back()->with('status', 'Erro ao tentar deletar horário');
}
return redirect()->back()->with('status', 'Deletado com sucesso!');
}
}

192
app/Http/Controllers/Admin/HorariosOptionController.php

@ -0,0 +1,192 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Filas;
use App\Models\Horario;
use App\Models\HorarioOption;
use App\Models\Types;
use App\Models\Redirect;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class HorariosOptionController extends Controller
{
public function __construct(
protected Agentes $agenteRepository,
protected Filas $queueRepository,
protected Horario $horarioRepository,
protected HorarioOption $horariosOptionsRepository,
protected Redirect $redirectRepository,
) {
}
public function index($id)
{
if (empty($id)) {
return redirect('horarios');
}
$options = $this->horariosOptionsRepository->list(["id_horario" => $id]);
$types = Types::all();
$id_horario = $id;
return view('admin.cadastros.horariosOption', compact('options', 'id_horario', 'types'));
}
public function show(Request $request)
{
$id_type = $request->segment(4);
if (empty($id_type)) {
return response()->json(['data' => 'Parametro ID_TYPE é obrigatório']);
}
$type = DB::table('types')->where([['id', $id_type], ['status', true]])->first();
$typeName = $type->name;
$id_empresa = Helper::getIdEmpresa();
$data = null;
switch ($typeName) {
case 'queue':
$data = $this->queueRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'redirect':
$data = $this->redirectRepository->list(["id_empresa" => $id_empresa]);
break;
case 'agent':
$data = $this->agenteRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'horario':
$data = $this->horarioRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
default:
$data = null;
break;
}
$response = [
'data' => [
'status' => true,
'data' => $data,
],
];
return response()->json($response);
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_redirect')){
abort(404);
}
$request->validate([
'id_horario' => ['required'],
'horario_inicio' => ['required'],
'horario_fim' => ['required'],
'feriado' => ['required'],
'todos_dias_semana' => ['required'],
'todos_meses' => ['required'],
'todos_dias_mes' => ['required'],
]);
$id = $request->destino;
if ($request->type == 3) {
$id = $this->agenteRepository->get(["id" => $request->destino])->matricula;
}
$todos_dias_semana = $request->todos_dias_semana === "Sim" ? true : false;
$todos_meses = $request->todos_meses === "Sim" ? true : false;
$todos_dias_mes = $request->todos_dias_mes === "Sim" ? true : false;
$feriado = $request->feriado === "Sim" ? true : false;
$created = $this->horariosOptionsRepository->create($request->id_horario, $request->horario_inicio, $request->horario_fim, $todos_dias_semana, $request->dias_semana,
$request->dias_semana_fim, $todos_dias_mes, $request->dias_mes, $request->dias_mes_fim, $todos_meses, $request->meses, $request->meses_fim,
$request->type, $id, $feriado);
if(!$created){
redirect()->back()->with('status', 'Erro ao tentar cadastrar regra');
}
return redirect()->back()->with('status', 'Cadastrado com sucesso');
}
public function destroy($id_horario, $id_horario_option)
{
if (!$id_horario_option) {
return redirect()->back();
}
$deleted = $this->horariosOptionsRepository->deletar($id_horario_option);
if(!$deleted){
redirect()->back()->with('status', 'Erro ao tentar deletar horário');
}
return redirect()->back()->with('status', 'Deletado com sucesso!');
}
public function edit(Request $request)
{
$id = $request->segment(4);
if (empty($id)) {
return response()->json(['data' => 'Parametro ID é obrigatório']);
}
$optionData = HorarioOption::find($id);
$types = Types::all();
$response = [
'data' => [
'status' => true,
'option_data' => $optionData,
'types' => $types,
],
];
return response()->json($response);
}
public function update(Request $request)
{
$id = $request->segment(4);
if (!$id) {
return redirect()->back();
}
$request->validate([
'horario_inicio' => ['required'],
'horario_fim' => ['required'],
'feriado' => ['required'],
'todos_dias_semana' => ['required'],
'todos_meses' => ['required'],
'todos_dias_mes' => ['required'],
]);
$destino = $request->destino;
if ($request->type == 3) {
$destino = $this->agenteRepository->get(["id" => $request->destino])->matricula;
}
$todos_dias_semana = $request->todos_dias_semana === "Sim" ? true : false;
$todos_meses = $request->todos_meses === "Sim" ? true : false;
$todos_dias_mes = $request->todos_dias_mes === "Sim" ? true : false;
$feriado = $request->feriado === "Sim" ? true : false;
$created = $this->horariosOptionsRepository->atualizar($id, $request->horario_inicio, $request->horario_fim, $todos_dias_semana, $request->dias_semana,
$request->dias_semana_fim, $todos_dias_mes, $request->dias_mes, $request->dias_mes_fim, $todos_meses, $request->meses, $request->meses_fim,
$request->type, $destino, $feriado);
if(!$created){
redirect()->back()->with('status', 'Erro ao tentar atualizar regra');
}
return redirect()->back()->with('status', 'Atualizado com sucesso');
}
}

142
app/Http/Controllers/Admin/NumberChannelController.php

@ -0,0 +1,142 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\NumberChannel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class NumberChannelController extends Controller
{
public function __construct(
protected NumberChannel $numberChannelRepository,
) {
}
public function index(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_channels')){
abort(404);
}
$empresas = Helper::getEmpresas();
if (!is_int($empresas)){
$empresasIds = $empresas->pluck('id')->toArray();
} else {
$empresasIds = $empresas;
}
if ($request->empresa) {
$empresasIds = $request->empresa;
}
$search = strtolower($request->pesquisa);
$status = $request->status === "desativado" ? false : true;
$channels = $this->numberChannelRepository->list(["id_empresa" => $empresasIds, "status" => $status, "search" => $search]);
return view('admin.cadastros.channels', compact('channels', 'status', 'search'));
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_channels')){
abort(404);
}
$request->validate([
'name' => ['required'],
'id_empresa' => ['required'],
'number' => ['required'],
'token' => ['required'],
'channel' => ['required'],
'work_space' => ['required'],
'title' => ['required'],
]);
$status = $request->status === 'on' ? true : false;
$created = $this->numberChannelRepository->create($request->name, $request->id_empresa, $request->number, $request->token, $request->channel, $request->work_space, $request->title, $status);
if(!$created){
return redirect()->back()->with('status', 'Erro ao tentar criar channel');
}
return redirect()->back()->with('status', ' Cadastrado com sucesso');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_channels')){
abort(404);
}
if (empty($id)) {
return redirect('channels');
}
$channelData = NumberChannel::find($id);
$response = [
'status' => true,
'data' => $channelData
];
return response()->json($response);
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_channels')){
abort(404);
}
if (empty($id)) {
return redirect('channels');
}
NumberChannel::where('id', $id)->update([
"status" => false
]);
return redirect('channels')->with('status', 'desativado com sucesso');
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_channels')){
abort(404);
}
$request->validate([
'name' => ['required'],
'id_empresa' => ['required'],
'number' => ['required'],
'token' => ['required'],
'channel' => ['required'],
'work_space' => ['required'],
'title' => ['required'],
]);
$status = $request->status === "on" ? true : false;
NumberChannel::where("id", $id)->update([
'name' => $request->name,
'id_empresa' => $request->id_empresa,
'number' => $request->number,
'token' => $request->token,
'channel' => $request->channel,
'work_space' => $request->work_space,
'title' => $request->title,
'status' => $status,
]);
return redirect('channels')->with('status', 'atualizado com sucesso');
}
}

73
app/Http/Controllers/PausasController.php → app/Http/Controllers/Admin/PausasController.php

@ -1,70 +1,60 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Pausas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\VarDumper\Caster\RedisCaster;
class PausasController extends Controller
{
//
public function index(Request $request)
{
$selected = (object) ['isSelected' => false, 'isAtivo' => 'ativo'];
$user = auth()->user();
if(Gate::forUser($user)->denies('show_pausas')){
abort(404);
}
$selected = (object) ['isSelected' => false, 'isAtivo' => 'ativo'];
$pausas = Pausas::class;
$is_ativo = $request->situacao === 'on' ? true : false;
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
if ($request->status) {
$status = $request->status == 'ativo' ? true : false;
$selected = (object) ['isSelected' => true, 'isAtivo' => $request->status];
$pausas = $pausas::where('is_ativo', $status);
} else {
$pausas = $pausas::where('is_ativo', true);
}
if ($request->pesquisa) {
$pesquisa = strtolower($request->pesquisa);
$pausas->where(DB::raw('LOWER(motivos_pausas.motivo)'), 'like', "%$pesquisa%");
}
$pausas = $pausas->where('id_empresa', $id_empresa)->OrderBy('motivo', 'asc')->get();
return view('admin.pausas', compact('pausas', 'selected'));
return view('admin.cadastros.pausas', compact('pausas', 'selected'));
}
public function create(Request $request)
{
if (Gate::denies('is_permission')) {
return redirect('users');
$user = auth()->user();
if(Gate::forUser($user)->denies('store_pausas')){
abort(404);
}
if (empty($request->motivo)) {
return redirect('pausas');
}
$request->validate([
'motivo' => ['string', 'required'],
'situacao' => ['required'],
@ -74,7 +64,6 @@ class PausasController extends Controller
$id_empresa = $user->empresa->first()->id;
$isPausaExists = DB::table('motivos_pausas')->where([
['motivo', strtoupper($request->motivo)],
['id_empresa', $id_empresa]
@ -84,50 +73,51 @@ class PausasController extends Controller
return redirect('pausas')->with('status', 'Pausa já cadastrada');
}
$is_ativo = $request->situacao === 'on' ? true : false;
Pausas::create([
'motivo' => strtoupper($request->motivo),
'id_empresa' => $id_empresa,
'is_ativo' => $is_ativo
]);
return redirect('pausas')->with('status', 'Pausa cadastrada');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_pausas')){
abort(404);
}
if (empty($id)) {
return redirect('pausas');
}
$pausa = Pausas::find($id);
$response = [
'data' => $pausa
];
return response()->json($response);
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_pausas')){
abort(404);
}
if (empty($id)) {
return redirect('pausas');
}
if (empty($request->motivo) || empty($request->status)) {
return redirect('pausas');
}
$request->validate([
'motivo' => ['string', 'required'],
@ -140,24 +130,16 @@ class PausasController extends Controller
->first();
$is_ativo = $request->status === 'on' ? true : false;
if ($isPausaRegister->motivo != $request->motivo) {
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$isPausaExists = DB::table('motivos_pausas')->where([
['motivo', strtoupper($request->motivo)],
['id_empresa', $id_empresa]
])->exists();
if ($isPausaExists) {
return redirect('pausas')->with('status', 'Pausa já cadastrada');
}
@ -173,23 +155,20 @@ class PausasController extends Controller
]);
}
return redirect('pausas')->with('status', 'Pausa atualizada');
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_pausas')){
abort(404);
}
$pesquisa = Pausas::where('id', '=', $id);
$setor = $pesquisa->get();
$rowCount = $setor->count();
if ($rowCount > 0) {
$pesquisa->update([
'is_ativo' => false

57
app/Http/Controllers/Admin/ProfileController.php

@ -0,0 +1,57 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;
class ProfileController extends Controller
{
/**
* Display the user's profile form.
*/
public function edit(Request $request): View
{
return view('profile.edit', [
'user' => $request->user(),
]);
}
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
/**
* Delete the user's account.
*/
public function destroy(Request $request): RedirectResponse
{
$request->validateWithBag('userDeletion', [
'password' => ['required', 'current_password'],
]);
$user = $request->user();
Auth::logout();
$user->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return Redirect::to('/');
}
}

80
app/Http/Controllers/RedirectController.php → app/Http/Controllers/Admin/RedirectController.php

@ -1,35 +1,49 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Models\numberChanel;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\NumberChannel;
use App\Models\Redirect;
use App\Models\RedirectOption;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class RedirectController extends Controller
{
public function __construct(
protected Redirect $redirectRepository,
protected RedirectOption $redirectOptionRepository,
) {
}
public function index()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
if(Gate::forUser($user)->denies('show_redirect')){
abort(404);
}
$redirects = DB::table('redirect as r')
->join('number_channel as nc', 'nc.id', '=', 'r.id_number')
->select('r.*')
->where('nc.id_empresa', $id_empresa)
->get();
$empresas = Helper::getEmpresas();
$numberChannels = numberChanel::where('id_empresa', $id_empresa)->get();
if (!is_int($empresas)){
$empresasIds = $empresas->pluck('id')->toArray();
} else {
$empresasIds = $empresas;
}
$redirects = $this->redirectRepository->list(["id_empresa" => $empresasIds]);
$numberChannels = NumberChannel::whereIn('id_empresa', $empresasIds)->get();
return view('admin.redirects', compact('redirects', 'numberChannels'));
return view('admin.cadastros.redirects', compact('redirects', 'numberChannels'));
}
public function store(Request $request)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('store_redirect')){
abort(404);
}
$request->validate([
@ -43,23 +57,16 @@ class RedirectController extends Controller
$status = $request->status === "on" ? true : false;
$initial = $request->initial === "on" ? true : false;
Redirect::create([
'name' => strtoupper($request->nome),
'description' => $request->descricao,
'status' => $status,
'initial' => $initial,
'id_number' => $request->channel,
'data_reg' => date('Y-m-d')
]);
$this->redirectRepository->create(strtoupper($request->nome),$request->descricao, $status, $initial, $request->channel, date('Y-m-d'));
return redirect()->back()->with('status', 'Cadastrado com sucesso');
}
public function edit($id)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_redirect')){
abort(404);
}
if (empty($id)) {
@ -69,7 +76,7 @@ class RedirectController extends Controller
$redirectData = Redirect::find($id);
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$numberChannels = numberChanel::where('id_empresa', $id_empresa)->get();
$numberChannels = NumberChannel::where('id_empresa', $id_empresa)->get();
$response = [
'data' => [
'status' => true,
@ -84,8 +91,9 @@ class RedirectController extends Controller
public function update(Request $request, $id)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('update_redirect')){
abort(404);
}
$request->validate([
@ -95,7 +103,6 @@ class RedirectController extends Controller
'initial' => ['required'],
'channel' => ['required'],
]);
$status = $request->status === "on" ? true : false;
$initial = $request->initial === "on" ? true : false;
@ -115,17 +122,20 @@ class RedirectController extends Controller
public function destroy($id)
{
if (Gate::denies('is_permission')) {
return redirect()->back();
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_redirect')){
abort(404);
}
$pesquisa = Redirect::where('id', '=', $id);
$Redirect = $pesquisa->get();
$rowCount = $Redirect->count();
if ($rowCount > 0) {
$redirectOptions = $this->redirectOptionRepository->list(['id_redirect' => $id]);
$pesquisa->delete();
if (count($redirectOptions) > 0) {
return redirect('redirects')->with('status', 'Primeiramente é necessário deletar as opções do Redirect!');
}
Redirect::where("id", $id)
->delete();
return redirect('redirects')->with('status', 'deletado com sucesso');
}
}

104
app/Http/Controllers/RedirectOptionController.php → app/Http/Controllers/Admin/RedirectOptionController.php

@ -1,26 +1,39 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Filas;
use App\Models\Redirect;
use App\Models\RedirectOption;
use App\Models\Types;
use App\Models\Horario;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class RedirectOptionController extends Controller
{
public function __construct(
protected Agentes $agenteRepository,
protected Filas $queueRepository,
protected Redirect $redirectRepository,
protected RedirectOption $redirectOptionsRepository,
protected Horario $horarioRepository,
) {
}
public function index($id)
{
if (empty($id)) {
return redirect('redirects');
}
$options = RedirectOption::where('id_redirect', '=', $id)->OrderBy('sequence', 'asc')->get();
$options = $this->redirectOptionsRepository->list(["id_redirect" => $id]);
$types = Types::all();
$id_redirect = $id;
return view('admin.redirectOption', compact('options', 'id_redirect', 'types'));
return view('admin.cadastros.redirectOption', compact('options', 'id_redirect', 'types'));
}
public function show(Request $request)
@ -33,17 +46,20 @@ class RedirectOptionController extends Controller
$type = DB::table('types')->where([['id', $id_type], ['status', true]])->first();
$typeName = $type->name;
$id_empresa = Helper::getIdEmpresa();
$data = null;
switch ($typeName) {
case 'queue':
$data = $this->queue();
$data = $this->queueRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'redirect':
$data = $this->redirect();
$data = $this->redirectRepository->list(["id_empresa" => $id_empresa]);
break;
case 'agent':
$data = $this->agent();
$data = $this->agenteRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
case 'horario':
$data = $this->horarioRepository->list(["id_empresa" => $id_empresa, "status" => true]);
break;
default:
$data = null;
@ -60,51 +76,6 @@ class RedirectOptionController extends Controller
return response()->json($response);
}
private function queue()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$filas = DB::table('queues as q')->select("q.id", "q.nome as name")->where([['id_empresa', $id_empresa], ['is_ativa', true]])->get();
return $filas;
}
private function redirect()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$redirects = DB::table('redirect as r')
->join('number_channel as nc', 'nc.id', '=', 'r.id_number')
->select('r.id', 'r.name')
->where('nc.id_empresa', $id_empresa)
->get();
return $redirects;
}
private function agent()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$agentes = DB::table('grupo_usuario as gu')
->join('usuarios as u', 'u.id', '=', 'gu.id_usuario')
->join('grupo as g', 'g.id', '=', 'gu.id_grupo')
->join('usuario_empresa as ue', 'u.id', '=', 'ue.id_usuario')
->join('empresa as e', 'e.id', '=', 'ue.id_empresa')
->select('u.id', "u.nome as name", 'u.matricula')
->where('e.id', $id_empresa)
->where('u.status', true)
->get();
return $agentes;
}
public function store(Request $request)
{
$request->validate([
@ -115,35 +86,26 @@ class RedirectOptionController extends Controller
]);
$id = $request->destino;
if ($request->type == 3) {
$id = Agentes::where('id', $request->destino)->first()->matricula;
$id = $this->agenteRepository->get(["id" => $request->destino])->matricula;
}
$hidden = $request->status === "on" ? false : true;
RedirectOption::create([
'sequence' => $request->sequencia,
'description' => $request->descricao,
'id_redirect' => $request->id_redirect,
'id_type' => $request->type,
'code_id' => $id,
'hide' => $hidden,
'data_reg' => date("Y-m-d"),
]);
$created = $this->redirectOptionsRepository->create($request->sequencia, $request->descricao, $request->id_redirect, $request->type, $id, $hidden, date("Y-m-d"));
if(!$created){
redirect()->back()->with('status', 'Erro ao tentar cadastrar opção');
}
return redirect()->back()->with('status', 'Cadastrado com sucesso');
}
public function sequence($id)
{
if (empty($id)) {
return response()->json(['data' => 'Parametro ID_REDIRECT é obrigatório']);
}
$data = DB::table('redirect_option')->where([['id_redirect', $id]])->orderBy('sequence')->get();
$data = $this->redirectOptionsRepository->list(["id_redirect" => $id]);
$response = [
'data' => [
'status' => true,
@ -157,13 +119,11 @@ class RedirectOptionController extends Controller
public function destroy(Request $request)
{
$id = $request->segment(4);
if (empty($id)) {
return redirect()->back();
}
$pesquisa = RedirectOption::where('id', '=', $id);
$RedirectOption = $pesquisa->get();
$rowCount = $RedirectOption->count();
@ -171,14 +131,12 @@ class RedirectOptionController extends Controller
$pesquisa->delete();
}
return redirect()->back()->with('status', 'Deletado com sucesso');
}
public function edit(Request $request)
{
$id = $request->segment(4);
if (empty($id)) {
return response()->json(['data' => 'Parametro ID é obrigatório']);
}
@ -200,7 +158,6 @@ class RedirectOptionController extends Controller
public function update(Request $request)
{
$id = $request->segment(4);
if (!$id) {
return redirect()->back();
}
@ -213,7 +170,6 @@ class RedirectOptionController extends Controller
]);
$code_id = $request->destino;
if ($request->type == 3) {
$code_id = Agentes::where('id', $request->destino)->first()->matricula;
}

49
app/Http/Controllers/Admin/Relatorios/RelatorioAtendimentosAbandonadosController.php

@ -0,0 +1,49 @@
<?php
namespace App\Http\Controllers\Admin\Relatorios;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Atendimentos;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\Filas;
class RelatorioAtendimentosAbandonadosController extends Controller
{
public function __construct(
protected Atendimentos $atendimentosRepository,
protected Agentes $agenteRepository,
protected Filas $filasRepository,
) {
}
public function index(Request $request): View
{
$user = auth()->user();
if (Gate::forUser($user)->denies('show_relatorios')) {
abort(404);
}
$selected = (object) $request->all();
$empresas = Helper::getEmpresas();
if (!is_int($empresas)){
$empresasIds = $empresas->pluck('id')->toArray();
} else {
$empresasIds = $empresas;
}
if ($request->empresa) {
$empresasIds = $request->empresa;
}
$filas = $this->filasRepository->list(["id_empresa" => $empresasIds]);
$agentes = $this->agenteRepository->list(["id_empresa" => $empresasIds]);
$atendimentos = $this->atendimentosRepository->getAbandonAtends(["empresa" => $empresasIds, "fila" => $request->fila, "atendente" => $request->atendente, "usuario" => $request->usuario, "dataInicio" => $request->dataInicio, "dataFim" => $request->dataFim]);
return view('admin.relatorios.abandonAtendimentos.abandonAtendimentos', compact('atendimentos', 'selected', 'empresas', 'filas', 'agentes'));
}
}

62
app/Http/Controllers/Admin/Relatorios/RelatorioHistoricoAtendimentoController.php

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Admin\Relatorios;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Atendimentos;
use App\Models\Contatos;
use App\Models\Messages;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class RelatorioHistoricoAtendimentoController extends Controller
{
public function __construct(
protected Atendimentos $atendimentosRepository,
protected Messages $messageRepository,
protected Contatos $contatosRepository,
protected Agentes $agenteRepository
) {
}
public function index(Request $request): View
{
$user = auth()->user();
if (Gate::forUser($user)->denies('show_relatorio_historico_atendimento')) {
abort(404);
}
$selected = (object) $request->all();
$idEmpresa = Helper::getIdEmpresa();
$atendimentos = $this->atendimentosRepository->getCompletAtend(["id_empresa" => $idEmpresa, "protocolo" => $request->protocolo,
"atendente" => $request->atendente, "usuario" => $request->usuario, "dataInicio" => $request->dataInicio,
"dataFim" => $request->dataFim]);
return view('admin.relatorios.historicoAtendimentos.historicoAtendimentos', compact('atendimentos', 'selected'));
}
public function show($uniqueid): View
{
if (empty($uniqueid)) {
return redirect()->back();
}
$idEmpresa = Helper::getIdEmpresa();
$messages = $this->messageRepository->get($uniqueid);
$atendimento = $this->atendimentosRepository->get(["uniqueid" => $uniqueid]);
$contato = $this->contatosRepository->get(["id_empresa" => $idEmpresa, "contato" => $atendimento->cliente_id]);
if (empty($contato)) {
$contato = (object) [
"nome" => $atendimento->nome,
"contato" => $atendimento->cliente_id,
];
}
$atendentes = $this->atendimentosRepository->findAgentByAtend($uniqueid);
return view('admin.relatorios.historicoAtendimentos.historicoAtendimento', compact('messages', 'contato', 'atendentes'));
}
}

54
app/Http/Controllers/Admin/Relatorios/RelatorioProducaoAgenteController.php

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Admin\Relatorios;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Agentes;
use App\Models\Atendimentos;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\Filas;
class RelatorioProducaoAgenteController extends Controller
{
public function __construct(
protected Atendimentos $atendimentosRepository,
protected Agentes $agenteRepository,
protected Filas $filasRepository,
) {
}
public function index(Request $request): View
{
$user = auth()->user();
if (Gate::forUser($user)->denies('show_relatorios')) {
abort(404);
}
$selected = (object) $request->all();
$empresas = Helper::getEmpresas();
if (!is_int($empresas)){
$empresasIds = $empresas->pluck('id')->toArray();
} else {
$empresasIds = $empresas;
}
if ($request->empresa) {
$empresasIds = $request->empresa;
}
$filas = $this->filasRepository->list(["id_empresa" => $empresasIds]);
$agentes = $this->agenteRepository->list(["id_empresa" => $empresasIds]);
if (empty($request->all())) {
$atendimentos = [];
} else {
$atendimentos = $this->atendimentosRepository->getProducaoAgente(["fila" => $request->fila, "atendente" => $request->atendente, "dataInicio" => $request->dataInicio, "dataFim" => $request->dataFim]);
}
return view('admin.relatorios.producaoAgentes.producaoAgentes', compact('atendimentos', 'selected', 'filas', 'agentes'));
}
}

17
app/Http/Controllers/RelatoriosController.php → app/Http/Controllers/Admin/RelatoriosController.php

@ -11,11 +11,8 @@ use PhpParser\Node\Stmt\Nop;
class RelatoriosController extends Controller
{
//
public function index(Request $request)
{
$filas = Filas::where('is_ativa', true);
$user = auth()->user();
@ -31,27 +28,15 @@ class RelatoriosController extends Controller
return view('admin.relatorios', compact('filas'));
}
public function agentesLogados($id_fila)
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$fila = Filas::where('id', $id_fila)->where('id_empresa', $id_empresa)->first();
$agentesNaFila = Supervisor::where('fila', $fila->nome)->get();
// SELECT s.nome, COALESCE(COUNT(a.id), 0) AS total_atendimentos
// FROM supervisor as s
// LEFT JOIN atendimento as a ON s.id_usuario = a.id_usuario AND date(a.data_reg) = '2023-04-11'
// where s.fila = 'FALSIFICADA'
// GROUP BY s.id
$agentesNaFila = DB::table('supervisor as s')
->leftJoin('atendimento as a', function ($join) {
$data_atual = date('Y-m-d');
@ -64,6 +49,6 @@ class RelatoriosController extends Controller
->groupBy('s.id')
->get();
return view('admin.agentesLogados', compact('agentesNaFila', 'fila'));
return view('admin.dashboard.agentesLogados', compact('agentesNaFila', 'fila'));
}
}

129
app/Http/Controllers/Admin/SupervisorController.php

@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Filas;
use App\Models\Pausas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use GuzzleHttp\Client;
use App\Traits\AuthToken;
use App\Models\Tokens;
class SupervisorController extends Controller
{
use AuthToken;
public function __construct(
protected Filas $queueRepository,
protected Tokens $tokenRepository,
) {
}
public function desconectarAgente(Request $request)
{
$request->validate([
'supervisor_id' => ['required'],
]);
$deleted = DB::table("supervisor as s")->where("s.id", $request->supervisor_id)->delete();
if(!$deleted){
return redirect()->back()->with('status', 'Erro ao desconectar agente');
}
return redirect()->back()->with('status', 'Desconectado com sucesso');
}
public function pausaAgente(Request $request)
{
$request->validate([
'pausa' => ['string', 'required'],
'supervisor_id' => ['required'],
]);
$pausa = Pausas::where("id", $request->pausa)->first();
$user = auth()->user();
$tokenDB = $this->getToken($user->id);
$httpClient = new Client();
$agente = DB::table("supervisor as s")
->where("s.id", $request->supervisor_id)
->first();
$url = env('APP_URL');
$response = $httpClient->post("$url/api/v1/agente/entrarPausa", [
'headers' => [
'Authorization' => "Bearer $tokenDB->token",
],
'json' => [
'id_pausa' => $pausa->id,
'matricula' => $agente->matricula,
],
]);
if (!($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
return redirect()->back()->with('status', 'Erro ao tentar pausar o agente');
} else {
return redirect()->back()->with('status', 'O agente foi colocado em pausa');
}
}
public function retirarPausaAgente(Request $request)
{
$request->validate([
'supervisor_id' => ['required'],
]);
$updated = DB::table("supervisor as s")->where("s.id", $request->supervisor_id)->update([
'motivo_pausa' => null,
'status' => "LIVRE",
]);
if(!$updated){
return redirect()->back()->with('status', 'Erro ao retirar pausa do agente');
}
return redirect()->back()->with('status', 'Pausa retirada com sucesso');
}
public function pausarTodosAgentes(Request $request, $id)
{
$user = auth()->user();
$tokenDB = $this->getToken($user->id);
$httpClient = new Client();
$check = true;
$fila = Filas::where("id", $id)->first();
$pausa = Pausas::where("id", $request->pausa)->first();
$agentes = DB::table("supervisor as s")
->where("s.fila", $fila->nome)
->get();
foreach ($agentes as $agente) {
$url = env('APP_URL');
$response = $httpClient->post("$url/api/v1/agente/entrarPausa", [
'headers' => [
'Authorization' => "Bearer $tokenDB->token",
],
'json' => [
'id_pausa' => $pausa->id,
'matricula' => $agente->matricula,
],
]);
if (!($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
$check = false;
}
if (!$check) {
return redirect()->back()->with('status', 'Erro ao tentar pausar todos os agentes');
} else {
return redirect()->back()->with('status', 'Todos os agentes foram colocados em pausa');
}
}
}
}

60
app/Http/Controllers/SystemMessageController.php → app/Http/Controllers/Admin/SystemMessageController.php

@ -1,39 +1,36 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\SystemMessage;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class SystemMessageController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
// , compact('steps', 'numberChannels')
$user = auth()->user();
if(Gate::forUser($user)->denies('show_system_message')){
abort(404);
}
$id_empresa = $user->empresa->first()->id;
$systemMessages = SystemMessage::where('id_empresa', $id_empresa)->get();
$id_empresa = Helper::getIdEmpresa();
$systemMessages = SystemMessage::where('id_empresa', $id_empresa)->orderBy('id', 'asc')->get();
return view('admin.systemMessage', compact('systemMessages'));
return view('admin.cadastros.systemMessage', compact('systemMessages'));
}
/**
* Show the form for creating a new resource.
*/
public function create(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_system_message')){
abort(404);
}
$id_empresa = $user->empresa->first()->id;
$id_empresa = Helper::getIdEmpresa();
$request->validate([
'texto' => ['required'],
@ -41,14 +38,10 @@ class SystemMessageController extends Controller
'ordem' => ['required']
]);
if (empty($request->texto) || empty($request->momento)) {
return redirect('systemMessage')->with('status', 'erro ao cadastrar system message');
}
SystemMessage::create([
'texto' => $request->texto,
'momento' => $request->momento,
@ -56,12 +49,16 @@ class SystemMessageController extends Controller
'id_empresa' => $id_empresa,
]);
return redirect('systemMessage');
}
public function edit($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('edit_system_message')){
abort(404);
}
if (empty($id)) {
return redirect('systemMessage');
}
@ -76,25 +73,24 @@ class SystemMessageController extends Controller
];
// dd($response);
// echo json_encode($response);
return response()->json($response);
}
public function update(Request $request, $id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('update_system_message')){
abort(404);
}
$request->validate([
"texto" => ['required'],
"momento" => ['required'],
"ordem" => ['required'],
]);
if (empty($id) || empty($request->texto) || empty($request->momento)) {
return redirect('systemMessage');
}
SystemMessage::where("id", $id)
->update([
@ -103,20 +99,20 @@ class SystemMessageController extends Controller
'momento' => $request->momento
]);
return redirect('systemMessage')->with('status', 'atualizado com sucesso');
}
public function destroy($id)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('destroy_system_message')){
abort(404);
}
$pesquisa = SystemMessage::where('id', '=', $id);
$systemMessage = $pesquisa->get();
$rowCount = $systemMessage->count();
if ($rowCount > 0) {
$pesquisa->delete();
}

83
app/Http/Controllers/Admin/TemplatesController.php

@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Templates;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\NumberChannel;
use Illuminate\Support\Facades\DB;
class TemplatesController extends Controller
{
public function index()
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_templates')){
abort(404);
}
$channel_token = session('channel_token');
$channel_number = session('channel_number');
$workspace = session('workspace');
return view("admin.cadastros.templates", ["channel_token" => $channel_token, "channel_number" => $channel_number, "workspace" => $workspace]);
}
public function show()
{
$user = auth()->user();
if(Gate::forUser($user)->denies('show_templates')){
abort(404);
}
$id_empresa = Helper::getIdEmpresa();
$templates = DB::select("
SELECT *
FROM templates
WHERE id_empresa = :id_empresa
", ['id_empresa' => $id_empresa]);
return response()->json(["data" => $templates]);
}
public function store(Request $request)
{
$user = auth()->user();
if(Gate::forUser($user)->denies('store_templates')){
abort(404);
}
$data = $request->all();
$id_empresa = Helper::getIdEmpresa();
$componentsJson = json_encode($data["components"]);
Templates::create([
"id" => $data["id"],
"name" => $data["name"],
"category" => $data["category"]["description"],
"language" => $data["language"]["name"],
"status" => $data["status"]["code"],
"components" => $componentsJson,
"id_empresa" => $id_empresa,
]);
return response()->json(["message" => "Template foi cadastrado com sucesso"]);
}
public function deletar($id)
{
if (empty($id)) {
return redirect()->back();
}
Templates::where("id", $id)
->delete();
return redirect()->back()->with('status', 'Deletado com sucesso');
}
}

193
app/Http/Controllers/AgentesController.php

@ -1,193 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Agentes;
use App\Models\GrupoUsuario;
use App\Models\UsuarioEmpresa;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class AgentesController extends Controller
{
public function index(Request $request)
{
$selected = (object) ['isSelected' => false, 'isAtivo' => 'ativo'];
$maior_matricula = null;
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$agentes = DB::table('grupo_usuario as gu')
->join('usuarios as u', 'u.id', '=', 'gu.id_usuario')
->join('grupo as g', 'g.id', '=', 'gu.id_grupo')
->join('usuario_empresa as ue', 'u.id', '=', 'ue.id_usuario')
->join('empresa as e', 'e.id', '=', 'ue.id_empresa')
->select('u.*', 'g.name')
->where('e.id', $id_empresa);
$consulta_matricula = DB::table('usuarios as u')
->join('usuario_empresa as ue', 'ue.id_usuario', '=', 'u.id')
->select(DB::raw('MAX(matricula) as maior_matricula'))
->where('ue.id_empresa', $id_empresa)
->first();
if ($request->status) {
$status = $request->status == 'ativo' ? true : false;
$selected = (object) ['isSelected' => true, 'isAtivo' => $request->status];
$agentes->where('u.status', $status);
} else {
$agentes->where('u.status', true);
}
if ($request->pesquisa) {
$pesquisa = strtolower($request->pesquisa);
$agentes->where(DB::raw('LOWER(u.nome)'), 'like', "%$pesquisa%");
}
$agentes = $agentes->orderBy('u.nome', 'asc')->get();
$maior_matricula = $consulta_matricula->maior_matricula + 1;
return view('admin.users', compact('agentes', 'selected', 'maior_matricula'));
}
public function store(Request $request)
{
$request->validate([
'nome' => ['required'],
'email' => ['required', 'unique:'.User::class],
'matricula' => ['required'],
'status' => ['required'],
]);
$status = $request->status === 'on' ? true : false;
$isMatriculaRegister = Agentes::where('matricula', $request->matricula)->get()->count();
if ($isMatriculaRegister > 0) {
return redirect('/users')->with('status', ' matricula vinculada com outro agente');
}
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
DB::beginTransaction();
try {
$agente = Agentes::create([
"nome" => $request->nome,
"email" => $request->email,
"matricula" => $request->matricula,
"status" => $status,
"password" => Hash::make('123'),
]);
GrupoUsuario::create([
'id_usuario' => $agente->id,
'id_grupo' => 3
]);
UsuarioEmpresa::create([
'id_usuario' => $agente->id,
'id_empresa' => $id_empresa,
'is_ativo' => true
]);
DB::commit();
return redirect('users')->with('status', 'Agente cadastrado');
} catch (\Throwable $th) {
DB::rollBack();
dd($th->getMessage());
return redirect('users')->with('status', 'Erro ao tentar cadastrar agente');
}
}
public function edit($id)
{
if (empty($id)) {
return redirect('users');
}
$userData = Agentes::find($id);
$response = [
'status' => true,
'data' => $userData
];
return response()->json($response);
}
public function destroy($id)
{
if (empty($id)) {
return redirect('users');
}
Agentes::where('id', $id)->update([
"status" => false
]);
return redirect('users')->with('status', 'desativado com sucesso');
}
public function update(Request $request, $id)
{
$request->validate([
"nome" => ['required'],
"status" => ['required'],
]);
if (empty($id) || empty($request->nome)) {
return redirect('users');
}
$status = $request->status === "on" ? true : false;
$agente_matricula = Agentes::select('matricula')
->where('id', $id)
->get()
->first();
if ($agente_matricula->matricula != $request->matricula) {
$isMatriculaRegister = Agentes::where('matricula', $request->matricula)->get()->count();
if ($isMatriculaRegister > 0) {
return redirect('/users/editar/' . $id)->with('status', ' matricula vinculada com outro agente');
}
Agentes::where("id", $id)
->update([
"nome" => $request->nome,
"status" => $status,
]);
} else {
Agentes::where("id", $id)
->update([
"nome" => $request->nome,
"status" => $status,
]);
}
return redirect('users')->with('status', 'atualizado com sucesso');
}
}

7
app/Http/Controllers/Auth/AuthenticatedSessionController.php

@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use App\Traits\AuthToken;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -12,6 +13,7 @@ use Illuminate\View\View;
class AuthenticatedSessionController extends Controller
{
use AuthToken;
/**
* Display the login view.
*/
@ -25,8 +27,11 @@ class AuthenticatedSessionController extends Controller
*/
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();
$request->authenticate();
$this->generateToken($request);
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);

210
app/Http/Controllers/DashboardController.php

@ -1,210 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Atendimentos;
use App\Models\Filas;
use App\Models\Pausas;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
public function index()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$pausas = Pausas::where(["id_empresa" => $id_empresa, "is_ativo" => true])->get();
return view('admin.dashboard', compact('pausas'));
}
public function getRelatorioDados()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$atendimentosData = DB::table('usuarios as u')
->leftJoin('atendimento as a', 'u.id', '=', 'a.id_usuario')
->select('u.nome', DB::raw('count(a.id_usuario) as qtde_atendimento'))
->where('a.id_empresa', $id_empresa)
->groupBy('u.nome')
->get();
$atendimentosStatusData = DB::table('usuarios as u')
->join('usuario_empresa as ue', 'ue.id_usuario', '=', 'u.id')
->join('eventos_atendimento as ea', 'ea.id_usuario', '=', 'u.id')
->select('b.evento', DB::raw('count(b.evento) as qtde_usados'))
->where('a.id_empresa', $id_empresa)
->groupBy('b.evento')
->get();
$filasMaisUsadas = DB::table('queues as q')
->join('eventos_atendimento as ea', 'ea.id_queue', '=', 'q.id')
->select('q.nome', DB::raw('count(ea.id_queue) as qtde_fila'))
->where('q.id_empresa', $id_empresa)
->groupBy('q.nome')
->get();
$data_inicio = date("Y-m-01");
$data_atual = date('Y-m-d');
$data_fim = Helper::last_date_in_mouth();
$atendimentosPorMes = $this->getAtendimentosMes($data_inicio, $data_fim);
$atendimentosDia = $this->getAtendimentoDia($data_atual);
$totalAgentesAtivos = $this->getAgentesAtivos();
$totalAgentesPausa = $this->getAgentesPausa();
$relatoriosAtendimento = $this->relatoriosDados();
$total_atendimentos = $this->totalAtendimentos($atendimentosData);
$data = [
'atendimentosData' => $atendimentosData,
'atendimentosStatusData' => $atendimentosStatusData,
'filasMaisUsadas' => $filasMaisUsadas,
'atendimentosPorMes' => $atendimentosPorMes,
'atendimentosDia' => $atendimentosDia,
'totalAgentesAtivos' => $totalAgentesAtivos,
'totalAgentesPausa' => $totalAgentesPausa,
'relatoriosAtendimento' => $relatoriosAtendimento,
'total_atendimentos' => $total_atendimentos,
];
return response()->json(['status' => true, 'data' => $data]);
}
private function totalAtendimentos($atendimentos)
{
$total_atendimentos = 0;
foreach ($atendimentos as $atendimento) {
$total_atendimentos += $atendimento->qtde_atendimento;
}
return $total_atendimentos;
}
private function relatoriosDados()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$relatorio = DB::table('queues as q')
->leftJoin('supervisor as s', 's.fila', '=', 'q.nome')
->leftJoin('atendimento as a', 'a.id_usuario', '=', 's.id_usuario')
->select('q.id as fila_id', 'q.nome as fila', DB::raw('count( DISTINCT s.id) as quantidade_supervisores'), DB::raw('COALESCE(COUNT(a.id), 0) AS quantidade_atendimentos'))
->where([
['q.id_empresa', $id_empresa],
['q.is_ativa', true],
])
->groupBy('q.nome', 'q.id')
->orderBy('q.nome', 'asc')
->get();
return $relatorio;
}
private function getAtendimentosMes($data_inicio, $data_fim)
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$atendimentosPorMes = Atendimentos::whereDate('data_reg', '>=', $data_inicio)
->whereDate('data_reg', '<=', $data_fim)
->where('id_empresa', $id_empresa)
->count();
return $atendimentosPorMes;
}
private function getAtendimentoDia($data_atual)
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$atendimentoDia = Atendimentos::whereDate('data_reg', '=', $data_atual)
->where('id_empresa', $id_empresa)
->count();
return $atendimentoDia;
}
private function getAgentesAtivos()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$totalAgentesAtivos = DB::table('supervisor as s')
->where([
['s.id_empresa', $id_empresa],
])
->count();
return $totalAgentesAtivos;
}
private function getAgentesPausa()
{
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$totalAgentesPausa = DB::table('supervisor as s')
->where([
['s.id_empresa', $id_empresa],
['s.status', 'PAUSA'],
])
->count();
return $totalAgentesPausa;
}
public function agentesLogados($id)
{
if(empty($id))
{
return redirect()->back();
}
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$fila = Filas::where(['id' => $id, 'id_empresa' => $id_empresa])->first();
$pausas = Pausas::where(["id_empresa" => $id_empresa, "is_ativo" => true])->get();
return view('admin.agentesLogados', compact('fila', 'pausas'));
}
public function relatoriosFilas($id)
{
if(empty($id)){
return response()->json(['data' => "Parametro ID é obrigatorio"]);
}
$user = auth()->user();
$id_empresa = $user->empresa->first()->id;
$fila = Filas::where('id', $id)->where('id_empresa', $id_empresa)->first();
$agentesNaFila = DB::table('supervisor as s')
->leftJoin('atendimento as a', function ($join) {
$data_atual = date('Y-m-d h:i:s');
$join->on('s.id_usuario', '=', 'a.id_usuario')
->where(DB::raw('DATE(a.data_reg)'), $data_atual);
})
->select(DB::raw("(now() - s.tempo_login) AS login"), DB::raw('COALESCE(COUNT(a.id), 0) AS qtde_atendimento'), 's.*')
->where([
['s.id_empresa', $id_empresa],
['fila', $fila->nome],
])
->groupBy('s.id')
->get();
return response()->json(['data' => $agentesNaFila]);
}
}

10
app/Http/Controllers/GruposController.php

@ -1,10 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class GruposController extends Controller
{
//
}

0
app/Http/Controllers/ProfileController.php

97
app/Http/Controllers/SupervisorController.php

@ -1,97 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Filas;
use App\Models\Pausas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class SupervisorController extends Controller
{
public function desconectarAgente(Request $request)
{
$request->validate([
'supervisor_id' => ['required'],
]);
$deleted = DB::table("supervisor as s")->where("s.id", $request->supervisor_id)->delete();
if(!$deleted){
return redirect()->back()->with('status', 'Erro ao desconectar agente');
}
return redirect()->back()->with('status', 'Desconectado com sucesso');
}
public function pausaAgente(Request $request)
{
$request->validate([
'pausa' => ['string', 'required'],
'supervisor_id' => ['required'],
]);
$pausa = Pausas::where("id", $request->pausa)->first();
$updated = DB::table("supervisor as s")->where("s.id", $request->supervisor_id)->update([
'motivo_pausa' => strtoupper($pausa->motivo),
'status' => "PAUSA",
]);
if(!$updated){
return redirect()->back()->with('status', 'Erro ao pausar agente');
}
return redirect()->back()->with('status', 'Pausado com sucesso');
}
public function retirarPausaAgente(Request $request)
{
$request->validate([
'supervisor_id' => ['required'],
]);
$updated = DB::table("supervisor as s")->where("s.id", $request->supervisor_id)->update([
'motivo_pausa' => null,
'status' => "LIVRE",
]);
if(!$updated){
return redirect()->back()->with('status', 'Erro ao retirar pausa do agente');
}
return redirect()->back()->with('status', 'Pausa retirada com sucesso');
}
public function pausarTodosAgentes(Request $request, $id)
{
$fila = Filas::where("id", $id)->first();
$pausa = Pausas::where("id", $request->pausa)->first();
$agentes = DB::table("supervisor as s")
->where("s.fila", $fila->nome)
->get();
DB::beginTransaction();
try {
foreach ($agentes as $agente) {
DB::table("supervisor as s")
->where('s.id', $agente->id)
->update([
'motivo_pausa' => strtoupper($pausa->motivo),
'status' => "PAUSA",
]);
}
DB::commit();
return redirect()->back()->with('status', 'Todos os agentes foram colocados em pausa');
} catch (\Throwable $th) {
DB::rollBack();
dd($th->getMessage());
return redirect('users')->with('status', 'Erro ao tentar pausar todos os agentes');
}
}
}

2
app/Http/Requests/ProfileUpdateRequest.php

@ -16,7 +16,7 @@ class ProfileUpdateRequest extends FormRequest
public function rules(): array
{
return [
'name' => ['string', 'max:255'],
'nome' => ['string', 'max:255'],
'email' => ['email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)],
];
}

135
app/Models/Agentes.php

@ -4,6 +4,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class Agentes extends Model
{
@ -19,4 +21,137 @@ class Agentes extends Model
'status',
'password',
];
public function get(array $params)
{
$agentes = DB::table('grupo_usuario as gu')
->join('usuarios as u', 'u.id', '=', 'gu.id_usuario')
->join('grupo as g', 'g.id', '=', 'gu.id_grupo')
->join('usuario_empresa as ue', 'u.id', '=', 'ue.id_usuario')
->join('empresa as e', 'e.id', '=', 'ue.id_empresa')
->select('u.*', 'g.name');
if (isset($params['id_empresa'])) {
$agentes->where('ue.id_empresa', $params['id_empresa']);
}
if (isset($params['matricula'])) {
$agentes->where('u.matricula', $params['matricula']);
}
if (isset($params['search'])) {
$agentes->where(DB::raw('LOWER(u.nome)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['status'])) {
$agentes->where('u.status', $params['status']);
}
if (isset($params['id'])) {
$agentes->where('u.id', $params['id']);
}
return $agentes->orderBy('u.nome', 'asc')->first();
}
public function list(array $params)
{
$agentes = DB::table('grupo_usuario as gu')
->join('usuarios as u', 'u.id', '=', 'gu.id_usuario')
->join('grupo as g', 'g.id', '=', 'gu.id_grupo')
->join('usuario_empresa as ue', 'u.id', '=', 'ue.id_usuario')
->join('empresa as e', 'e.id', '=', 'ue.id_empresa')
->select('u.*', 'g.name as cargo');
if (is_array($params['id_empresa'])) {
$agentes->whereIn('ue.id_empresa', $params['id_empresa']);
} else {
$agentes->where('ue.id_empresa', $params['id_empresa']);
}
if (isset($params['matricula'])) {
$agentes->where('u.matricula', $params['matricula']);
}
if (isset($params['search'])) {
$agentes->where(DB::raw('LOWER(u.nome)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['status'])) {
$agentes->where('u.status', $params['status']);
}
return $agentes->orderBy('u.nome', 'asc')->get();
}
public function create(string $nome, string $email, string $password, string|int $matricula, bool $status, int $id_empresa, int $id_grupo)
{
DB::beginTransaction();
try {
DB::table('usuarios')->insert([
"nome" => $nome,
"email" => $email,
"matricula" => $matricula,
"status" => $status,
"password" => Hash::make($password),
]);
$agente = DB::table('usuarios')->where('matricula', $matricula)->first();
DB::table('grupo_usuario')->insert([
'id_usuario' => $agente->id,
'id_grupo' => $id_grupo
]);
DB::table('usuario_empresa')->insert([
'id_usuario' => $agente->id,
'id_empresa' => $id_empresa,
'is_ativo' => true
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public function getMaiorMatricula(int $id_empresa)
{
$consulta_matricula = DB::table('usuarios as u')
->join('usuario_empresa as ue', 'ue.id_usuario', '=', 'u.id')
->select(DB::raw('MAX(matricula) as maior_matricula'))
->where('ue.id_empresa', $id_empresa);
return $consulta_matricula->first();
}
public function edit(int $id, string $nome, int $id_grupo, bool $status, ?string $password, ?string $matricula, ?string $email)
{
DB::beginTransaction();
try {
DB::table('usuarios')->where("id", $id)
->update([
"nome" => $nome,
"status" => $status,
"password" => Hash::make($password),
"matricula" => $matricula,
"email" => $email
]);
DB::table('grupo_usuario')->where("id_usuario", $id)
->update([
"id_grupo" => $id_grupo
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
}

274
app/Models/Atendimentos.php

@ -2,8 +2,10 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Atendimentos extends Model
{
@ -23,4 +25,276 @@ class Atendimentos extends Model
'id_number_channel',
'nome',
];
public function getCompletAtend(array $params = [], int $quantidade = 15)
{
$atendimentos = DB::table('atendimento as a')
->join('usuarios as u', 'u.matricula', '=', 'a.matricula')
->join('eventos_atendimento as ea', 'ea.uniqueid', '=', 'a.uniqueid')
->join('protocolo_reg as r', "r.uniqueid", "=", "a.uniqueid")
->leftJoin('contatos as c', "c.contato", "=", "a.cliente_id")
->select(DB::raw('DISTINCT a.uniqueid'), DB::raw('COALESCE(c.nome, a.nome) AS nome_client'), 'u.nome as nome_atendente', 'r.protocolo as protocolo', 'a.data_reg as data')
->whereIn('ea.evento', [config('event.CONF_EVENT_TIMERMINO_CLIENTE'), config('event.CONF_EVENT_TIMERMINO_AGENTE'), config('event.CONF_EVENT_TIMEOUT_CLIENTE'), config('event.CONF_EVENT_TIMEOUT_AGENTE'), config('event.CONF_EVENT_ABANDON')]);
if (isset($params['id_empresa'])) {
$atendimentos->where('a.id_empresa', $params['id_empresa']);
}
if (isset($params['protocolo'])) {
$atendimentos->where(DB::raw('r.protocolo'), 'LIKE', "%{$params['protocolo']}%");
}
if (isset($params['usuario'])) {
$atendimentos->where(DB::raw('lower(c.nome)'), 'like', '%' . strtolower($params['usuario']) . '%');
}
if (isset($params['atendente'])) {
$atendimentos->where(DB::raw('lower(u.nome)'), 'like', '%' . strtolower($params['atendente']) . '%');
}
if (isset($params['dataInicio']) && isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], $params['dataFim']]);
}
if (isset($params['dataInicio'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], DB::raw('CURRENT_DATE')]);
}
if (isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), ['0001-01-01', $params['dataFim']]);
}
return $atendimentos->orderByDesc('r.protocolo')->paginate($quantidade);
}
public function get(array $params = [])
{
$atendimento = DB::table('atendimento as ma')
->leftJoin('eventos_atendimento as me', "ma.uniqueid", "=", "me.uniqueid")
->leftJoin("queues as q", "q.id", "=", "me.id_queue")
->select("ma.*", "q.id as id_queue");
if (!empty($params['uniqueid'])) {
$atendimento->where('ma.uniqueid', $params['uniqueid']);
}
if (!empty($params['id'])) {
$atendimento->where('ma.id', $params['id']);
}
return $atendimento->first();
}
public function list(array $params = [])
{
$atendimento = DB::table('atendimento as ma')
->leftJoin('eventos_atendimento as me', "ma.uniqueid", "=", "me.uniqueid")
->leftJoin("queues as q", "q.id", "=", "me.id_queue")
->select("ma.*", "q.id as id_queue", "me.id_usuario");
if (!empty($params['uniqueid'])) {
$atendimento->where('ma.uniqueid', $params['uniqueid']);
}
if (!empty($params['id'])) {
$atendimento->where('ma.id', $params['id']);
}
return $atendimento->get();
}
public function findAgentByAtend($uniqueid)
{
$atendimento = DB::table('atendimento as ma')
->join('eventos_atendimento as me', "ma.uniqueid", "=", "me.uniqueid")
->join("usuarios as u", "u.id", "=", "me.id_usuario")
->join("queues as q", "q.id", "=", "me.id_queue")
->join("protocolo_reg as r", "r.uniqueid", "=", "ma.uniqueid")
->select("u.*", "q.nome as nome_fila", "r.protocolo")
->selectRaw("MIN(me.data_evento) as data_atendimento")
->where([
['ma.uniqueid', $uniqueid],
["me.evento", config("event.CONF_EVENT_START")]
])
->groupBy("me.id_usuario", "u.id", "q.nome", "r.protocolo");
return $atendimento->get();
}
public function getAbandonAtends(array $params = [])
{
$atendimentos = DB::table('atendimento as a')
->join('eventos_atendimento as ea', function ($join) {
$join->on('ea.uniqueid', '=', 'a.uniqueid')
->where('ea.id', '=', function ($query) {
$query->select(DB::raw('MAX(id)'))
->from('eventos_atendimento as ee')
->whereRaw('ee.uniqueid = ea.uniqueid');
});
})
->join('queues as q', 'q.id', '=', 'ea.id_queue')
->leftJoin('usuarios as u', 'u.id', '=', 'a.id_usuario')
->leftJoin('protocolo_reg as pr','pr.uniqueid','=','a.uniqueid')
->select(
'a.uniqueid',
'a.matricula',
'a.cliente_id',
'u.nome as atendente',
'a.nome',
'q.nome as fila',
'a.data_reg',
'data_evento',
'pr.protocolo',
DB::raw('(SELECT count(*) FROM message m WHERE m.uniqueid = a.uniqueid AND type NOT IN (\'alert\') AND src <> cliente_id) AS interacao')
)
->whereIn('evento', ['ABANDON'])
->orderBy('a.id');
if (isset($params['dataInicio']) && isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], $params['dataFim']]);
}
if (isset($params['dataInicio'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], DB::raw('CURRENT_DATE')]);
}
if (isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), ['0001-01-01', $params['dataFim']]);
}
if (isset($params['atendente'])) {
$atendimentos->where('u.id', '=', $params['atendente']);
}
if (isset($params['fila'])) {
$atendimentos->where('q.id', '=', $params['fila']);
}
if (isset($params['usuario'])) {
$atendimentos->where(DB::raw('lower(a.nome)'), 'like', '%' . strtolower($params['usuario']) . '%');
}
if (isset($params['empresa'])) {
$atendimentos->where('a.id_empresa', '=', $params['empresa']);
}
return $atendimentos->get();
}
public function getProducaoAgente(array $params = [])
{
$tempoTrabalho = DB::table('eventos_usuarios as eu')
->join('usuarios as u', 'u.id', '=', 'eu.id_usuario')
->join('queues as q', 'q.id', '=', 'eu.id_dac')
->select(
'q.id as id_dac',
'q.nome as fila',
'u.nome',
'u.matricula',
DB::raw('CAST(eu.duracao AS date) AS data'),
DB::raw('MIN(eu.login) AS login'),
DB::raw('MAX(eu.logoff) AS logoff'),
DB::raw("TO_CHAR(MAX(eu.logoff) - MIN(eu.login), 'HH24:MI:SS') AS tempoLogin")
)
->groupBy('u.id', 'q.id', 'q.nome', 'u.nome', 'u.matricula', DB::raw('CAST(eu.duracao AS date)'))
->orderBy('u.id');
if (isset($params['dataInicio']) && isset($params['dataFim'])) {
$tempoTrabalho->whereBetween(DB::raw('CAST(eu.duracao AS date)'), [$params['dataInicio'], $params['dataFim']]);
}
if (isset($params['dataInicio'])) {
$tempoTrabalho->whereBetween(DB::raw('CAST(eu.duracao AS date)'), [$params['dataInicio'], DB::raw('CURRENT_DATE')]);
}
if (isset($params['dataFim'])) {
$tempoTrabalho->whereBetween(DB::raw('CAST(eu.duracao AS date)'), ['0001-01-01', $params['dataFim']]);
}
if (isset($params['atendente'])) {
$tempoTrabalho->where('u.id', '=', $params['atendente']);
}
if (isset($params['fila'])) {
$tempoTrabalho->where('q.id', '=', $params['fila']);
}
$tempoTrabalho->get();
$atendimentos = DB::table('usuarios as u')
->select(
'u.matricula',
'u.nome',
DB::raw('a.data_reg::DATE AS data_atendimento'),
DB::raw('SUM(CASE WHEN evento = \'SENDED\' THEN 1 ELSE 0 END) AS enviados'),
DB::raw('SUM(CASE WHEN evento IN (\'COMPLETE_AGENT\', \'COMPLETE_CALLER\', \'TIMEOUT_CALLER\') THEN 1 ELSE 0 END) AS finalizadas'),
DB::raw('SUM(CASE WHEN evento = \'ABANDON\' THEN 1 ELSE 0 END) AS abandonadas'),
DB::raw('SUM(CASE WHEN evento = \'CANCELADO\' THEN 1 ELSE 0 END) AS cancelado')
)
->join('atendimento as a', 'a.id_usuario', '=', 'u.id')
->join('eventos_atendimento as ea', function ($join) {
$join->on('ea.uniqueid', '=', 'a.uniqueid')
->where('ea.id', '=', function ($query) {
$query->select(DB::raw('MAX(id)'))
->from('eventos_atendimento as ee')
->whereRaw('ee.uniqueid = ea.uniqueid');
});
})
->join('queues as q', 'q.id', '=', 'ea.id_queue')
->groupBy('u.id', 'u.matricula', 'u.nome', DB::raw('a.data_reg::DATE'))
->orderBy('u.id')
->orderBy(DB::raw('a.data_reg::DATE'));
if (isset($params['dataInicio']) && isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], $params['dataFim']]);
}
if (isset($params['dataInicio'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), [$params['dataInicio'], DB::raw('CURRENT_DATE')]);
}
if (isset($params['dataFim'])) {
$atendimentos->whereBetween(DB::raw('a.data_reg::DATE'), ['0001-01-01', $params['dataFim']]);
}
if (isset($params['atendente'])) {
$atendimentos->where('u.id', '=', $params['atendente']);
}
if (isset($params['fila'])) {
$atendimentos->where('q.id', '=', $params['fila']);
}
$atendimentos->get();
$tempoTrabalhoResult = $tempoTrabalho->get();
$atendimentosResult = $atendimentos->get();
// Inicializar o array para armazenar os resultados combinados
$resultadoFinal = [];
// Iterar sobre os resultados de tempoTrabalho
foreach ($tempoTrabalhoResult as $tempoTrabalhoItem) {
// Verificar se há uma correspondência em atendimentos
$correspondenciaAtendimento = $atendimentosResult->first(function ($atendimento) use ($tempoTrabalhoItem) {
return $atendimento->matricula == $tempoTrabalhoItem->matricula
&& $atendimento->data_atendimento == $tempoTrabalhoItem->data;
});
// Se houver correspondência, combinar as informações
if ($correspondenciaAtendimento) {
$resultadoFinal[] = (object) array_merge((array) $tempoTrabalhoItem, (array) $correspondenciaAtendimento);
} else {
// Se não houver correspondência, preencher com 0
$tempoTrabalhoItem->enviados = 0;
$tempoTrabalhoItem->finalizadas = 0;
$tempoTrabalhoItem->abandonadas = 0;
$tempoTrabalhoItem->cancelado = 0;
$resultadoFinal[] = $tempoTrabalhoItem;
}
}
return $resultadoFinal;
}
}

4
app/Models/ConfigAtendimento.php

@ -16,7 +16,9 @@ class ConfigAtendimento extends Model
"id_empresa",
"timeout_client",
"timeout_agent",
"quantidade_simutaneo"
"quantidade_simutaneo",
"timeout_supervisor",
"timeout_espera",
];
}

43
app/Models/Contatos.php

@ -0,0 +1,43 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Contatos extends Model
{
use HasFactory;
protected $table = "contatos";
public $timestamps = true;
protected $fillable = [
"nome",
"email",
"contato",
"id_empresa",
"id_agente",
"status",
"notes",
];
public function get(array $params = []){
$contato = DB::table('contatos as c');
if(!empty($params['id_empresa'])){
$contato->where('id_empresa', $params['id_empresa']);
}
if (!empty($params['contato'])) {
$contato->where('contato', $params['contato']);
}
if (!empty($params['id'])) {
$contato->where('id', $params['id']);
}
return $contato->first();
}
}

43
app/Models/Filas.php

@ -11,19 +11,58 @@ class Filas extends Model
{
use HasFactory;
public $timestamps = false;
protected $table = "queues";
protected $fillable = [
'nome',
'id_empresa',
'is_ativa',
];
public function list(array $params)
{
$filas = DB::table('queues as q')->select("q.id", "q.nome as name");
if (is_array($params['id_empresa'])) {
// Se $params['id_empresa'] for um array, use a condição whereIn
$filas->whereIn('q.id_empresa', $params['id_empresa']);
} else {
// Se for um único valor, use a condição normal
$filas->where('q.id_empresa', $params['id_empresa']);
}
if (isset($params['search'])) {
$filas->where(DB::raw('LOWER(u.nome)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['status'])) {
$filas->where('q.is_ativa', $params['status']);
}
return $filas->orderBy('q.nome', 'asc')->get();
}
public function get(array $params)
{
$filas = DB::table('queues as q')->select("q.id", "q.nome as name");
if (isset($params['id'])) {
$filas->where('q.id', $params['id']);
}
if (isset($params['id_empresa'])) {
$filas->where('q.id_empresa', $params['id_empresa']);
}
if (isset($params['search'])) {
$filas->where(DB::raw('LOWER(u.nome)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['status'])) {
$filas->where('q.is_ativa', $params['status']);
}
return $filas->orderBy('q.nome', 'asc')->first();
}
}

41
app/Models/Grupos.php

@ -5,6 +5,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class Grupos extends Model
{
@ -20,9 +22,44 @@ class Grupos extends Model
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'grupo_usuario','id_usuario', 'id_grupo')->first();
return $this->belongsToMany(User::class, 'grupo_usuario','id_usuario', 'id_grupo');
}
public function permissoes(): BelongsToMany
{
return $this->belongsToMany(Permissoes::class, 'grupo_permissoes', 'id_grupo', 'id_permissao');
}
public function getPermissoesCriarGrupo($user)
{
$grupos = [];
if (Gate::forUser($user)->allows('store_master')) {
$grupos = DB::table('grupo')->get();
} else {
$roles = [];
if (Gate::forUser($user)->allows('store_administrador')) {
$roles[] = 'ADMINISTRADOR';
$roles[] = 'SUPERVISOR';
$roles[] = 'AGENTE';
} elseif (Gate::forUser($user)->allows('store_supervisor')) {
$roles[] = 'SUPERVISOR';
$roles[] = 'AGENTE';
} else {
$roles[] = 'AGENTE';
}
$grupos = DB::table('grupo')->whereIn('name', $roles)->get();
}
return $grupos;
}
public function getUserGrupoID($user_id)
{
$grupo_id = DB::table('grupo_usuario')->where('id_usuario', $user_id)->value('id_grupo');
return $grupo_id;
}
}

109
app/Models/Horario.php

@ -0,0 +1,109 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Horario extends Model
{
use HasFactory;
protected $table = "horarios";
public $timestamps = false;
protected $fillable = [
'id_number',
'nome',
'opcao',
'acao',
'status',
];
public function list(array $params)
{
$horarios = DB::table('horarios as h')
->join('number_channel as nc', 'nc.id', '=', 'h.id_number')
->select('h.*');
if (is_array($params['id_empresa'])) {
$horarios->whereIn('nc.id_empresa', $params['id_empresa']);
} else {
$horarios->where('nc.id_empresa', $params['id_empresa']);
}
if(isset($params['id_number'])){
$horarios->where('h.id_number', $params['id_number']);
}
if(isset($params['nome'])){
$horarios->where('h.nome', $params['nome']);
}
if(isset($params['status'])){
$horarios->where('h.status', $params['status']);
}
return $horarios->get();
}
public function create(int $id_number, string $nome, string $opcao, string $acao, bool $status)
{
DB::beginTransaction();
try {
DB::table('horarios')->insert([
'id_number' => $id_number,
'nome' => strtoupper($nome),
'opcao' => $opcao,
'acao' => $acao,
'status' => $status
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public function deletar($id_horario)
{
DB::beginTransaction();
try {
DB::table('horarios')->delete([
'id' => $id_horario
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public function atualizar(int $id, int $id_number, string $nome, string $opcao, string $acao, bool $status)
{
DB::beginTransaction();
try {
DB::table("horarios")->where("id", $id)->update([
'id_number' => $id_number,
'nome' => strtoupper($nome),
'opcao' => $opcao,
'acao' => $acao,
'status' => $status
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
}

126
app/Models/HorarioOption.php

@ -0,0 +1,126 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class HorarioOption extends Model
{
use HasFactory;
protected $table = "horarios_itens";
public $timestamps = false;
protected $fillable = [
'id_horario',
'horario_inicio',
'horario_fim',
'todos_dias_semana',
'semana',
'semana_fim',
'todos_dias_mes',
'dias_mes',
'dias_mes_fim',
'todos_mes',
'mes',
'mes_fim',
'opcao',
'acao',
'feriado'
];
public function list(array $params)
{
$options = DB::table('horarios_itens as hi')
->join("horarios as h", "h.id", "=", "hi.id_horario")
->select("hi.*", "h.nome as horario_nome");
if (isset($params['id_horario'])) {
$options->where('hi.id_horario', $params['id_horario']);
}
return $options->orderBy('id', 'asc')->get();
}
public function create(int $id_horario, string $horario_inicio, string $horario_fim, bool $todos_dias_semana, ?string $semana, ?string $semana_fim, bool $todos_dias_mes,
?int $dias_mes, ?int $dias_mes_fim, bool $todos_mes, ?string $mes, ?string $mes_fim, string $opcao, string $acao, bool $feriado)
{
DB::beginTransaction();
try {
DB::table("horarios_itens")->insert([
'id_horario' => $id_horario,
'horario_inicio'=> $horario_inicio,
'horario_fim' => $horario_fim,
'todos_dias_semana' => $todos_dias_semana,
'semana' => $semana,
'semana_fim'=> $semana_fim,
'todos_dias_mes'=> $todos_dias_mes,
'dias_mes' => $dias_mes,
'dias_mes_fim' => $dias_mes_fim,
'todos_mes' => $todos_mes,
'mes' => $mes,
'mes_fim'=> $mes_fim,
'opcao' => $opcao,
'acao' => $acao,
'feriado' => $feriado
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public function deletar($id_horario_option)
{
DB::beginTransaction();
try {
DB::table('horarios_itens')->delete([
'id' => $id_horario_option
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public function atualizar(int $id, string $horario_inicio, string $horario_fim, bool $todos_dias_semana, ?string $semana, ?string $semana_fim, bool $todos_dias_mes,
?int $dias_mes, ?int $dias_mes_fim, bool $todos_mes, ?string $mes, ?string $mes_fim, string $opcao, string $acao, bool $feriado)
{
DB::beginTransaction();
try {
DB::table("horarios_itens")->where("id", $id)->update([
'horario_inicio'=> $horario_inicio,
'horario_fim' => $horario_fim,
'todos_dias_semana' => $todos_dias_semana,
'semana' => $semana,
'semana_fim'=> $semana_fim,
'todos_dias_mes'=> $todos_dias_mes,
'dias_mes' => $dias_mes,
'dias_mes_fim' => $dias_mes_fim,
'todos_mes' => $todos_mes,
'mes' => $mes,
'mes_fim'=> $mes_fim,
'opcao' => $opcao,
'acao' => $acao,
'feriado' => $feriado
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
}

39
app/Models/Messages.php

@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Messages extends Model
{
use HasFactory;
public $timestamps = false;
protected $table = "message";
protected $fillable = [
'uniquedid',
'src',
'dst',
'type',
'content',
'profile_name',
'msg_date',
'media',
'status',
'file_name',
'eventos_atendimento',
'id_provedor',
'mimetype',
];
public function get(string $uniqueid)
{
$messages = Messages::where('uniqueid', $uniqueid)
->orderBy('id')
->get();
return $messages;
}
}

23
app/Models/NumberChanel.php

@ -1,23 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class numberChanel extends Model
{
use HasFactory;
public $timestamps = false;
protected $table = "number_channel";
protected $fillable = [
'id_empresa',
'number',
'token',
'channel',
'work_space',
'title'
];
}

107
app/Models/NumberChannel.php

@ -0,0 +1,107 @@
<?php
namespace App\Models;
use App\Helpers\Helper;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class NumberChannel extends Model
{
use HasFactory;
public $timestamps = false;
protected $table = "number_channel";
protected $fillable = [
'id_empresa',
'number',
'token',
'channel',
'work_space',
'title'
];
public function get(array $params)
{
$channels = DB::table('number_channel as nc')
->join('empresa as e', 'e.id', '=', 'nc.id_empresa')
->select('nc.*');
if (isset($params['id_empresa'])) {
$channels->where('nc.id_empresa', $params['id_empresa']);
}
return $channels->orderBy('nc.name', 'asc')->first();
}
public function list(array $params)
{
$channels = DB::table('number_channel as nc')
->join('empresa as e', 'e.id', '=', 'nc.id_empresa')
->select('nc.*');
if (is_array($params['id_empresa'])) {
// Se $params['id_empresa'] for um array, use a condição whereIn
$channels->whereIn('nc.id_empresa', $params['id_empresa']);
} else {
// Se for um único valor, use a condição normal
$channels->where('nc.id_empresa', $params['id_empresa']);
}
if (isset($params['search'])) {
$channels->where(DB::raw('LOWER(nc.name)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['status'])) {
$channels->where('nc.status', $params['status']);
}
return $channels->orderBy('nc.name', 'asc')->get();
}
public function create(string $name, int $id_empresa, string $number, string $token, string $chanel, string $work_space, string $title, bool $status)
{
DB::beginTransaction();
try {
DB::table('number_channel')->insert([
'name' => $name,
'id_empresa' => $id_empresa,
'number' => $number,
'token' => $token,
'channel' => $chanel,
'work_space' => $work_space,
'title' => $title,
'status' => $status,
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
public static function getToken(){
$id_empresa = Helper::getIdEmpresa();
$token = NumberChannel::where("id_empresa", $id_empresa)->first()->token;
return $token;
}
public static function getNumber(){
$id_empresa = Helper::getIdEmpresa();
$number = NumberChannel::where("id_empresa", $id_empresa)->first()->number;
return $number;
}
public static function getWorkspace(){
$id_empresa = Helper::getIdEmpresa();
$workspace = NumberChannel::where("id_empresa", $id_empresa)->first()->work_space;
return $workspace;
}
}

24
app/Models/Permissoes.php

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Permissoes extends Model
{
protected $table = "permissoes";
use HasFactory;
protected $fillable = [
'nome',
'status',
];
public function grupos(): BelongsToMany
{
return $this->BelongsToMany(Grupos::class,'grupo_permissoes', 'id_grupo', 'id_permissao');
}
}

59
app/Models/Redirect.php

@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Redirect extends Model
{
@ -20,4 +21,62 @@ class Redirect extends Model
'status',
'data_reg',
];
public function get(array $params)
{
$redirects = DB::table('redirect as r')
->join('number_channel as nc', 'nc.id', '=', 'r.id_number')
->select('r.*');
if(isset($params['id_empresa'])){
$redirects->where('nc.id_empresa', $params['id_empresa']);
}
if(isset($params['id_number'])){
$redirects->where('r.id_number', $params['id_number']);
}
if(isset($params['name'])){
$redirects->where('r.name', $params['name']);
}
return $redirects->first();
}
public function list(array $params)
{
$redirects = DB::table('redirect as r')
->join('number_channel as nc', 'nc.id', '=', 'r.id_number')
->select('r.*');
if (is_array($params['id_empresa'])) {
// Se $params['id_empresa'] for um array, use a condição whereIn
$redirects->whereIn('nc.id_empresa', $params['id_empresa']);
} else {
// Se for um único valor, use a condição normal
$redirects->where('nc.id_empresa', $params['id_empresa']);
}
if(isset($params['id_number'])){
$redirects->where('r.id_number', $params['id_number']);
}
if(isset($params['name'])){
$redirects->where('r.name', $params['name']);
}
return $redirects->get();
}
public function create(string $nome, string $description, bool $status, bool $initial, string|int $id_number, string $data_reg)
{
DB::table('redirect')->insert([
'name' => strtoupper($nome),
'description' => $description,
'status' => $status,
'initial' => $initial,
'id_number' => $id_number,
'data_reg' => date('Y-m-d')
]);
}
}

45
app/Models/RedirectOption.php

@ -4,7 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB;
class RedirectOption extends Model
{
@ -33,4 +33,47 @@ class RedirectOption extends Model
return $this->belongsTo(Types::class, 'id_type')->first();
}
public function list(array $params)
{
$options = DB::table('redirect_option as ro')
->join("redirect as r", "r.id", "=", "ro.id_redirect")
->join("types as t", "t.id", "=", "ro.id_type")
->select("ro.*", "r.name as redirect_name", "t.name as type_name");
if (isset($params['sequence'])) {
$options->where('ro.sequence', $params['sequence']);
}
if (isset($params['search'])) {
$options->where(DB::raw('LOWER(ro.nome)'), 'LIKE', "%{$params['search']}%");
}
if (isset($params['id_redirect'])) {
$options->where('ro.id_redirect', $params['id_redirect']);
}
return $options->orderBy('sequence', 'asc')->get();
}
public function create(string|int $sequence, string $description, string|int $id_redirect, string|int $id_type, string|int $code_id, bool $hide, string $data_reg)
{
DB::beginTransaction();
try {
DB::table("redirect_option")->insert([
'sequence' => $sequence,
'description' => $description,
'id_redirect' => $id_redirect,
'id_type' => $id_type,
'code_id' => $code_id,
'hide' => $hide,
'data_reg' => $data_reg
]);
DB::commit();
return true;
} catch (\Throwable $th) {
DB::rollBack();
return false;
}
}
}

32
app/Models/Templates.php

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Templates extends Model
{
use HasFactory;
protected $table = "templates";
public $timestamps = true;
protected $fillable = [
"id",
'name',
'status',
'category',
'language',
'components',
'id_empresa',
];
public static function findById($id)
{
$template = Templates::where('id', $id)->first();
return $template;
}
}

14
app/Models/GrupoUsuario.php → app/Models/Tokens.php

@ -5,14 +5,18 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class GrupoUsuario extends Model
class Tokens extends Model
{
use HasFactory;
protected $table = "grupo_usuario";
public $timestamps = true;
protected $table = "tokens";
protected $fillable = [
"id",
'token',
'id_usuario',
'id_grupo',
'refresh_token',
'expired_at',
];
}
}

12
app/Models/User.php

@ -48,17 +48,23 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
];
public function grupos(): BelongsToMany
{
return $this->BelongsToMany(Grupos::class,'grupo_usuario','id_usuario', 'id_grupo');
}
public function empresa(): BelongsToMany
{
return $this->belongsToMany(Empresa::class, 'usuario_empresa', 'id_usuario', 'id_empresa');
}
public function permissoes()
{
return $this->grupos->map->permissoes->flatten()->pluck('nome');
}
public function hasGrupo($grupo)
{
return $this->grupos()->contains($grupo);
}
}

9
app/Providers/AuthServiceProvider.php

@ -4,6 +4,7 @@ namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Models\User;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
@ -23,10 +24,10 @@ class AuthServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->registerPolicies();
Gate::define('is_permission', function($user){
return $user->grupos->first()->name == "ADMINISTRADOR" ? true : false;
Gate::before(function (User $user, $permission) {
if ($user->permissoes()->contains($permission)) {
return true;
}
});
}
}

52
app/Traits/AuthToken.php

@ -0,0 +1,52 @@
<?php
namespace App\Traits;
use App\Helpers\Helper;
use Illuminate\Support\Facades\Http;
use App\Models\NumberChannel;
trait AuthToken
{
public function generateToken($user)
{
$data = [
"email" => $user->email,
"senha" => $user->password ?? $user->senha,
];
session()->put('user_data', Helper::Crypt()->encrypt(json_encode($data, true)));
$response = Http::post("" . env('APP_URL') . "/access/login", $data);
$responseDecode = json_decode($response);
if($responseDecode->status === "error"){
return false;
}
$token = NumberChannel::getToken();
$number = NumberChannel::getNumber();
$workspace = NumberChannel::getWorkspace();
session()->put('channel_token', $token);
session()->put('channel_number', $number);
session()->put('workspace', $workspace);
return $responseDecode;
}
public function getToken($user_id)
{
$tokenDB = $this->tokenRepository->where('id_usuario', $user_id)->first();
if (empty($tokenDB)) {
$encrypt = session('user_data');
$userData = Helper::Crypt()->decrypt($encrypt);
$user = json_decode($userData);
$this->generateToken($user);
$tokenDB = $this->tokenRepository->where('id_usuario', $user_id)->first();
if (empty($tokenDB)) {
return response()->json("Não foi possivel gerar o token", 404);
}
}
return $tokenDB;
}
}

5
config/constants.php

@ -0,0 +1,5 @@
<?php
return [
"PASSWORD" => '#S1mpl3S_C0nn3ct@R00t',
];

23
config/event.php

@ -0,0 +1,23 @@
<?php
/*
|--------------------------------------------------------------------------
| Eventos
|--------------------------------------------------------------------------
|
| Eventos utilizados pelo gerenciamento dos atendimentos.
|
*/
return [
"CONF_EVENT_ESPERA" => "EMESPERA",
"CONF_EVENT_TIMEOUT_CLIENTE" => "TIMEOUT_CALLER",
"CONF_EVENT_TIMEOUT_AGENTE" => "TIMEOUT_AGENT",
"CONF_EVENT_TIMERMINO_CLIENTE" => "COMPLETE_CALLER",
"CONF_EVENT_TIMERMINO_AGENTE" => "COMPLETE_AGENT",
"CONF_EVENT_CANCELADO" => "CANCELADO",
"CONF_EVENT_TRANSFER" => "TRANSFER",
"CONF_EVENT_START" => "START",
"CONF_EVENT_ERRO_ATEND" => "LOST_CONNECTION",
"CONF_EVENT_SEND_HSM" => "SENDED",
"CONF_EVENT_ABANDON" => "ABANDON",
];

BIN
public/img/filtro-limpo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
public/img/icons/csv-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/img/icons/doc-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
public/img/icons/notfound-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/img/icons/pdf-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/img/icons/ppt-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
public/img/icons/txt-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
public/img/icons/xls-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/img/icons/zip-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

7
public/js/relatorios/historicoChamadas/accordion.js

@ -0,0 +1,7 @@
function clickedAccordion(event){
const target = event.currentTarget;
target.querySelector('.accordion-body').classList.toggle('hidden');
console.log(target.querySelector('.icon'));
target.querySelector('.icon').classList.toggle('rotate-90');
}

152
public/js/relatorios/historicoChamadas/index.js

@ -0,0 +1,152 @@
const chat = document.querySelector('.chat');
const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt']
function montaChat(messages, cliente_id) {
messages.forEach((message) => {
const datesend = converdata(new Date(message.msg_date).getTime());
let typesend = cliente_id == message.src ? "sender" : "receiver";
let statusMessage = message.status != 'error' ? datesend : '<span class="message-error" style="color:red">Mensagem não foi enviada!</span>';
if (message.type == "text") {
chat.innerHTML += `
<div class="${typesend}">
<span class="${typesend}-message">${message.profile_name}: ${message.content.replace(/\r?\n/g, "")}</span>
<br/>
<span class="message-time">${statusMessage}</span>
</div>
<br/>
`;
}
if (message.type == "transfer") {
chat.innerHTML += `
<div class="events">
<span class="events-message">${message.content} por ${message.profile_name}, ${datesend}.</span>
</div>
<br/>
`;
}
if (message.type == "finish" || message.type == "re_start" || message.type == "recover") {
chat.innerHTML += `
<div class="events">
<span class="events-message">${message.content}</span>
</div>
<br/>
`;
}
if (message.type != "text") {
const sendobj = {
filename: message.file_name,
id_provedor: message.id_provedor,
type: message.type,
mimetype: message.mimetype,
from: typesend,
profile_name: message.profile_name
};
messageTypeMedia(sendobj);
}
});
}
function addZero(time) {
if (time < 10) {
time = "0" + time
}
return time
}
function converdata(timestamp, horario_server = false) {
if (horario_server) {
timestamp = timestamp * 1000
}
let date = new Date(timestamp);
let day = addZero(date.getDate())
let month = addZero(date.getMonth() + 1)
let hours = date.getHours();
let minutes = date.getMinutes();
let formattedTime = `${day}/${month} ` + addZero(hours) + ':' + addZero(minutes)
return formattedTime
}
/**
* ENVIA AS MENSAGEM DO TIPO MIDIA (RECEPTIVO)
* @param {*} id_provedor
* @param {*} type
* @param {*} mimetype
* @param {*} delivery
* @returns
*/
const messageTypeMedia = (obj) => {
const fileDownload =
"http://" + server_api + "/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype);
if (obj.type == "voice" || obj.type == "audio") {
chat.innerHTML += `
<div class="${obj.from} ">
<span>${obj.profile_name}: </span>
<span class="${obj.from}-message audio">
<audio controls><source src="${fileDownload}" type="${obj.mimetype}"></audio>
</span>
<span class="message-time">${converdata(Date.now())}</span>
</div>
<br/>
`;
return;
}
if (obj.type == "video") {
chat.innerHTML += `
<div class="${obj.from}">
<span>${obj.profile_name}: </span>
<span class="${obj.from}-message">
<video controls>
<source src="${fileDownload}" type="${obj.mimetype}">
</video>
</span>
<br/>
<span class="message-time">${converdata(Date.now())}</span>
</div>
<br/>
`;
return;
}
if (obj.type == "document") {
const typefile = obj.filename.split(".")[1];
let icon;
if (icontypes.indexOf(typefile) >= 0) {
icon = `<img src="${path}img/icons/${typefile}-file.png" style="max-width: 60px"></img>`;
} else {
icon = `<img src="${path}img/icons/notfound-file.png" style="max-width: 60px"></img>`;
}
chat.innerHTML += `
<div class="${obj.from}">
<span>${obj.profile_name}: </span>
<span class="${obj.from}-message message-column">
<a href="${fileDownload}" target="_blank">
${icon}
</a>
</span>
<span class="fz-12">${obj.filename}</span>
<br/>
<span class="message-time">${converdata(Date.now())}</span>
</div>
<br/>
`;
return;
}
if (obj.type == "image" || obj.type == "sticker") {
chat.innerHTML += `
<div class="${obj.from}">
<span>${obj.profile_name}: </span>
<span class="${obj.from}-message message-column">
<img src="${fileDownload}" style="max-width: 200px; max-height: 150px" " >
</span>
<span class="message-time">${converdata(Date.now())}</span>
</div>
<br/>
`;
return;
}
};

37
public/js/views/agentes/atualizaAgente.js

@ -0,0 +1,37 @@
function atualizaUsuario(id_user) {
$.ajax({
type: "get",
url: `users/editar/${id_user}`,
success: function ({
data
}) {
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `/users/editar/${data.id}`;
modal.querySelector("input[name='nome']").value = data.nome;
modal.querySelector("input[name='email']").value = data.email;
modal.querySelector("input[name='matricula']").value = data.matricula;
// Seleciona o elemento <select>
const selectGrupo = modal.querySelector("select[name='id_grupo']");
// Define o valor do <select> com base no data.grupo_id
if (data.grupo_id && selectGrupo.querySelector(`option[value='${data.grupo_id}']`)) {
selectGrupo.value = data.grupo_id;
}
// Desativa o <select> se data.grupo_id não existir
selectGrupo.disabled = !data.grupo_id || !selectGrupo.querySelector(`option[value='${data.grupo_id}']`);
const containerCheckbox = modal.querySelector(".container-checkbox");
if (!data.status) {
containerCheckbox.innerHTML = `<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`;
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

230
public/js/views/agentesLogados/index.js

@ -0,0 +1,230 @@
const colorStatus = {
LIVRE: "green-500",
OCUPADO: "red-500",
PAUSA: "red-500",
INDISPONIVEL: "orange-400",
CHAMANDO: "orange-400",
}
function getFilaId() {
const url = window.location.href;
const splitUrl = url.split('/');
const id = splitUrl[splitUrl.length - 1];
return id;
}
function getDadosRelatorioDB(fila_id) {
return new Promise(function (resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `/dashboard/agentes/${fila_id}/fila`,
success: function (response) {
const data = response.data;
resolve(data);
}
});
})
}
async function montarRelatorios() {
const id = getFilaId();
const data = await getDadosRelatorioDB(id);
let elementsTable = "";
data.forEach(supervisor => {
const tempoFormatado = supervisor.login.split(".")[0];
const btnPlay = `
<button class="bg-blue-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer" onclick="retirarPausa(${supervisor.matricula}, '${supervisor.token}')">
<i class="fas fa-play-circle"></i>
</button>
`
const btnAtend = `
<button class="bg-blue-600 text-white flex-1 rounded text-xl text-center py-2 px-3" onclick="atendimentosAgente(${id}, ${supervisor.matricula}, '${supervisor.token}')">
<i class="fas fa-exchange-alt"></i>
</button>
`
const btnPausa = `<a class="bg-blue-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer" title="Colocar em Pausa?" onclick="selecionaAgente(${supervisor.id})"><i class="fas fa-pause"></i></a>`;
const agenteIsPausa = supervisor.status === "PAUSA" ? btnPlay : btnPausa;
const agenteAtendimentos = supervisor.atendimentos_atuais > 0 ? btnAtend : '';
elementsTable += `
<tr class="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 text-center">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.id}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.nome}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.matricula}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${tempoFormatado}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.motivo_pausa ?? "Sem Pausa"}</td>
<td class="p-3 text-sm text-${colorStatus[supervisor.status]}">${supervisor.status}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.atendimentos_atuais}</td>
<td class="p-3 text-sm font-bold flex items-center gap-2 flex-wrap">
${agenteAtendimentos}
${agenteIsPausa}
<button class="bg-red-600 text-white flex-1 rounded text-xl text-center py-2 px-3" onclick="desconectarAgente(${supervisor.matricula}, '${supervisor.token}')">
<i class="fas fa-power-off"></i>
</button>
</td>
</tr>
`
});
$(".table-relatorio").html(elementsTable);
}
async function desconectarAgente(matricula, token) {
if (confirm("Deseja realmente desconectar do sistema?")) {
logoff(matricula, token).then(() => {
montarRelatorios();
})
}
}
async function retirarPausa(matricula, token) {
if (confirm("Deseja retirar a pausa do agente?")) {
sairPausa(matricula, token).then(() => {
montarRelatorios();
})
}
}
const sairPausa = (matricula, token) => new Promise((resolve) => {
$.ajax({
url: `${server_api}/api/v1/agente/sairPausa`,
type: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
"ngrok-skip-browser-warning":"any"
},
data: JSON.stringify({
matricula
}),
success: function (res) {
alert('Pausa removida do Agente!')
resolve(res)
},
error: function (res) {
alert('Nao foi possivel retirar de pausa.')
}
});
})
const logoff = (matricula, token) => new Promise((resolve) => {
$.ajax({
type: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
"ngrok-skip-browser-warning":"any"
},
url: `${server_api}/access/logoff`,
data: JSON.stringify({
matricula
}),
success: function (res) {
alert('Desconectado com sucesso');
resolve(res)
},
error: function (res) {
alert('Não foi possivel deslogar do sistema.')
}
});
})
async function atendimentosAgente(fila_id, supervisor_matricula, token) {
const modal = document.querySelector(".modal3");
data = await getAtendimentos(fila_id, supervisor_matricula);
const tabelaAtendimentos = document.querySelector('.table-atendimentos');
tabelaAtendimentos.innerHTML = ''; // Limpa o conteúdo atual da tabela
let elementsTable = "";
// Itera sobre os dados e cria as linhas da tabela
data.forEach((item) => {
// Adiciona as células com os dados
elementsTable += `
<tr class="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 text-center">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${item.id}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${item.nome}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">+${item.cliente_id}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${item.data_reg.split('.')[0]}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">
<button class="bg-blue-600 text-white flex-1 rounded text-xl text-center py-2 px-3" title="Transferir de fila?" onclick="transferirAtendimento('${item.uniqueid}', '${supervisor_matricula}', '${token}')">
<i class="fas fa-exchange-alt"></i>
</button>
</td>
</tr>
`;
});
$(".table-atendimentos").html(elementsTable);
modal.style.display = "block";
}
function selecionaAgente(supervisor_id) {
const modal = document.querySelector(".modal");
modal.style.display = "block";
modal.querySelector("form").action = `/dashboard/agentes/{{$fila->nome}}/pausar`;
modal.querySelector("input[name='supervisor_id']").value = supervisor_id;
}
function selecionaFila(unique_id) {
const modal = document.querySelector(".modal2");
modal.style.display = "block";
modal.querySelector("input[name='unique_id']").value = unique_id;
}
function getAtendimentos(fila_id, supervisor_matricula) {
return new Promise(function (resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `/dashboard/agentes/${fila_id}/fila/${supervisor_matricula}`,
success: function (response) {
const data = response.data;
resolve(data);
}
});
})
}
async function transferirAtendimento(unique_id, origem, token) {
agentesLivres = await getAtendentesLivres(token);
const selectAgente = document.querySelector('select[name="destino"]');
// Limpa as opções existentes (caso haja alguma)
selectAgente.innerHTML = '';
// Preenche as opções com os dados recebidos
agentesLivres.forEach((agente) => {
const option = document.createElement('option');
option.value = agente.matricula; // Substitua com o campo apropriado do objeto agente
option.textContent = agente.nome; // Substitua com o campo apropriado do objeto agente
selectAgente.appendChild(option);
});
const modal = document.querySelector(".modal4");
modal.style.display = "block";
modal.querySelector("input[name='origem']").value = origem;
modal.querySelector("input[name='atendimento_unique_id']").value = unique_id;
}
function getAtendentesLivres(token) {
return new Promise((resolve) => {
$.ajax({
url: `${server_api}/api/v1/supervisor/agentes/status/livre`,
type: "GET",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
"ngrok-skip-browser-warning":"any"
},
success: function (response) {
const data = response.data;
resolve(data);
},
error: function (response) {
alert('Nao foi possivel carregar as listas de agentes.')
}
});
})
}

7
public/js/views/app/selectTheme.js

@ -0,0 +1,7 @@
function selectTheme() {
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}

29
public/js/views/channels/atualizaChannel.js

@ -0,0 +1,29 @@
function atualizaChannel(id_channel) {
$.ajax({
type: "get",
url: `channels/editar/${id_channel}`,
success: function ({
data
}) {
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `/channels/editar/${data.id}`;
modal.querySelector("input[name='name']").value = data.name;
modal.querySelector("input[name='id_empresa']").value = data.id_empresa;
modal.querySelector("input[name='number']").value = data.number;
modal.querySelector("input[name='token']").value = data.token;
modal.querySelector("select[name='channel']").value = data.channel;
modal.querySelector("input[name='work_space']").value = data.work_space;
modal.querySelector("input[name='title']").value = data.title;
const containerCheckbox = modal.querySelector(".container-checkbox");
if (!data.status) {
containerCheckbox.innerHTML = `<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

31
public/js/views/contatos/atualizaContato.js

@ -0,0 +1,31 @@
function atualizaContato(id_contato) {
$.ajax({
type: "get",
url: `contatos/${id_contato}/edit`,
success: function({
data
}) {
const contato_data = data.contato_data;
$(".modal-edit").show();
$(".modal-edit").find('form').attr('action', `contatos/${id_contato}`);
$(".modal-edit").find("input[name='nome']").val(contato_data.nome);
$(".modal-edit").find("input[name='email']").val(contato_data.email);
$(".modal-edit").find("textarea[name='descricao']").val(contato_data.notes);
const numeroFormatado = $(".modal-edit").find("input[name='contato']").masked(contato_data.contato);
$(".modal-edit").find("input[name='contato']").val(numeroFormatado);
if (contato_data.status) {
$(".modal-edit").find(".container-radio").
html(`<input type="radio" name="status" value="on" checked> <span class="mr-3 dark:text-gray-100">Sim</span><input type="radio" name="status" value="off" > <span class="dark:text-gray-100">Não</span>`)
} else {
$(".modal-edit").find(".container-radio").
html(`<input type="radio" name="status" value="on" > <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`)
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

66
public/js/views/dashboard/index.js

@ -0,0 +1,66 @@
const eventos = {
EMESPERA: 'Em espera',
TRANSFER: 'Transferidos',
COMPLETE_CALLER: 'Finalizadas pelo cliente',
LOST_CONNECTION: 'Desconectadas',
COMPLETE_AGENT: 'Finalizas pelo Agente',
START: 'Iniciadas',
}
function getDadosRelatorioDB() {
return new Promise(function(resolve, reject){
$.ajax({
type: "get",
dataType: 'json',
url: `dashboard/getRelatorioDados`,
success: function(response) {
const data = response.data;
resolve(data);
}
});
})
}
async function montarRelatorios()
{
const data = await getDadosRelatorioDB();
$(".text-operantes").text(data.totalAgentesOperantes);
$(".text-inoperantes").text(data.totalAgentesInoperantes);
$(".text-atendimentoDia").text(data.atendimentosDia);
$(".text-atendimentosMes").text(data.atendimentosPorMes);
let elementsTable = "";
data.relatoriosAtendimento.forEach(relatorio => {
elementsTable += `
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="text-center" title="Ativar essa fila">
<a href="dashboard/agentes/${relatorio.id}" class="text-xl p-2 hover:bg-gray-700 rounded-full">
<i class="fas fa-play-circle text-gray-900 dark:text-gray-100">
</i>
</a>
</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-left">${relatorio.nome}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.agentes}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.em_espera}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.iniciadas}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.finalizadas}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.abandonadas}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.enviadas}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 text-center">${relatorio.canceladas}</td>
<td class="text-center" title="Colocar fila em pausa">
<button onclick="selecionaFila(${relatorio.id})" class="text-xl p-2 hover:bg-gray-700 rounded-full">
<i class="fas fa-pause-circle text-gray-900 dark:text-gray-100">
</i>
</button>
</td>
</tr>
`
});
$(".table-relatorio").html(elementsTable);
}
function selecionaFila(fila_id){
const modal = document.querySelector(".modal");
modal.style.display = "block";
modal.querySelector("form").action = `/dashboard/agentes/${fila_id}/pausarAgentesFila`;
}

21
public/js/views/empresa/atualizaEmpresa.js

@ -0,0 +1,21 @@
function atualizaEmpresa(empresa_id) {
$.ajax({
type: "get",
url: `empresas/${empresa_id}/edit`,
success: function({data}) {
const empresa_data = data.empresa_data;
$(".modal-edit").show();
$(".modal-edit").find('form').attr('action', `empresas/${empresa_id}`);
$(".modal-edit").find("input[name='nome']").val(empresa_data.nome);
$(".modal-edit").find("input[name='token']").val(empresa_data.token);
$(".modal-edit").find("input[name='email']").val(empresa_data.email);
const cnpjFormatado = $(".modal-edit").find("input[name='cnpj']").masked(empresa_data.cnpj);
$(".modal-edit").find("input[name='cnpj']").val(cnpjFormatado);
if (empresa_data.status) {
$(".modal-edit").find("input[name='status'][value='on']").prop("checked", true);
} else {
$(".modal-edit").find("input[name='status'][value='off']").prop("checked", true);
}
}
});
}

26
public/js/views/fila/atualizaFila.js

@ -0,0 +1,26 @@
function atualizarFila(id_fila) {
$.ajax({
type: "get",
url: `filas/editar/${id_fila}`,
success: function({
data
}) {
console.log(data)
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `/filas/editar/${data.id}`;
modal.querySelector("input[name='nome']").value = data.nome;
const containerCheckbox = modal.querySelector(".container-checkbox");
if (!data.is_ativa) {
containerCheckbox.innerHTML = `<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

204
public/js/views/graficos/index.js

@ -0,0 +1,204 @@
const eventos = {
EMESPERA: 'Em espera',
TRANSFER: 'Transferidos',
COMPLETE_CALLER: 'Finalizadas pelo cliente',
LOST_CONNECTION: 'Desconectadas',
COMPLETE_AGENT: 'Finalizas pelo Agente',
START: 'Iniciadas',
ABANDON: 'Abandonadas',
SENDED: 'Enviadas pelo agente',
TIMEOUT_ESPERA: 'Tempo de espera excedido',
TIMEOUT_CALLER: 'Tempo de resposta excedido',
CANCELADO: 'Cancelado pelo cliente',
}
function getDadosRelatorioDB(dataInicio, dataFim) {
return new Promise(function(resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `graficos/getDados`,
data: {
dataInicio: dataInicio,
dataFim: dataFim
},
success: function(response) {
const data = response.data;
resolve(data);
}
});
})
}
function montaGrafico1(dataDB) {
let labels = dataDB.map((value) => value.nome)
let data = dataDB.map((value) => value.qtde_atendimento)
const ctx = document.getElementById('myChart');
let chartStatus = Chart.getChart("myChart");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
data: data,
label: "Atendimento Por Agente",
font: 90,
borderWidth: 1,
backgroundColor: [
'rgba(255, 159, 64, 0.7)',
'rgba(255, 99, 132, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(255, 205, 86, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(201, 203, 207, 0.7)',
'rgba(153, 102, 255, 0.7)',
],
borderColor: [
'rgb(255, 159, 64)',
'rgb(255, 99, 132)',
'rgb(75, 192, 192)',
'rgb(255, 205, 86)',
'rgb(54, 162, 235)',
'rgb(201, 203, 207)',
'rgb(153, 102, 255)',
],
}],
},
options: {
color: '#fff',
plugins: {
legend: {
labels: {
font: {
size: 15
}
}
}
},
scales: {
y: {
beginAtZero: true,
},
x: {
ticks: {
color: '#91a0a7',
font: {
size: 13
},
}
}
},
},
});
}
function montaGrafico2(dataDB) {
const ctx2 = document.getElementById('myChart2');
const somaPorEvento = {};
dataDB.forEach(element => {
if (somaPorEvento[element.evento]) {
somaPorEvento[element.evento] += element.qtde_usados;
} else {
somaPorEvento[element.evento] = element.qtde_usados;
}
});
let labels = Object.keys(somaPorEvento).map(label => eventos[label]);
let data = Object.values(somaPorEvento);
let chartStatus = Chart.getChart("myChart2");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart2 = new Chart(ctx2, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: data,
borderWidth: 1,
backgroundColor: [
'rgba(255, 159, 64, 0.7)',
'rgba(255, 99, 132, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(255, 205, 86, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(201, 203, 207, 0.7)',
'rgba(153, 102, 255, 0.7)',
],
borderColor: [
'rgb(255, 159, 64)',
'rgb(255, 99, 132)',
'rgb(75, 192, 192)',
'rgb(255, 205, 86)',
'rgb(54, 162, 235)',
'rgb(201, 203, 207)',
'rgb(153, 102, 255)',
],
}]
},
options: {
color: '#91a0a7',
scales: {
y: {
beginAtZero: true
}
}
}
});
}
function montaGrafico3(dataDB) {
const ctx3 = document.getElementById('myChart3');
let labels3 = dataDB.map((value) => value.nome)
let data3 = dataDB.map((value) => value.qtde_fila)
let chartStatus = Chart.getChart("myChart3");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart3= new Chart(ctx3, {
type: 'pie',
data: {
labels: labels3,
datasets: [{
label: 'Quantidade de atendimento',
data: data3,
borderWidth: 1,
backgroundColor: [
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 205, 86)'
],
fontColor: '#fff',
}]
},
options: {
color: '#91a0a7',
scales: {
y: {
beginAtZero: true
}
}
}
});
}
async function graficos() {
document.getElementById('graficos').hidden = false;
var dataInicio = document.getElementById('dataInicio').value;
var dataFim = document.getElementById('dataFim').value;
const data = await getDadosRelatorioDB(dataInicio, dataFim);
montaGrafico1(data.atendimentosData);
montaGrafico2(data.atendimentosStatusData);
montaGrafico3(data.filasMaisUsadas);
}

59
public/js/views/horarios/atualizaHorarios.js

@ -0,0 +1,59 @@
function atualizaHorarios(horario_id) {
$.ajax({
type: "get",
url: `horarios/${horario_id}/edit`,
success: async function({
data
}) {
const option_data = data.option_data;
const types = data.types;
const channels = data.numberChannels;
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector("input[name='nome']").value = option_data.nome;
modal.querySelector('form').action = `horarios/${horario_id}`;
if (option_data.status) {
modal.querySelector(".container-radio").innerHTML =
`<input type='radio' name='status' checked value='on'> <span class='mr-3 dark:text-gray-100'>Sim</span>
<input type='radio' name='status' value='off'> <span class='mr-3 dark:text-gray-100'>Não</span>`
} else {
modal.querySelector(".container-radio").innerHTML =
`<input type='radio' name='status' value='on'> <span class='mr-3 dark:text-gray-100'>Sim</span>
<input type='radio' name='status' checked value='off'> <span class='mr-3 dark:text-gray-100'>Não</span>`;
}
let containerTypes = '';
types.forEach(type => {
if (option_data.opcao_nao == type.id) {
containerTypes +=
` <option value="${type.id}" selected>${type.name}</option>`
} else {
containerTypes +=
` <option value="${type.id}">${type.name}</option>`
}
});
modal.querySelector(".select-types").innerHTML = containerTypes;
let containerChannels = '';
channels.forEach(channel => {
if (option_data.id_number === channel.id) {
containerChannels +=
` <option value="${channel.id}" selected>${channel.name}</option>`
} else {
containerChannels +=
` <option value="${channel.id}">${channel.name}</option>`
}
});
modal.querySelector(".select-channels").innerHTML = containerChannels;
montaSelectDestino(option_data.opcao, modal, option_data.acao);
}
});
}
document.querySelectorAll('.select-types').forEach(el => {
el.addEventListener('change', (e) => {
const select = e.target;
const modal = select.closest('.modal') ?? select.closest('.modal-edit');
const id_redirect = select.value;
montaSelectDestino(id_redirect, modal);
})
})

36
public/js/views/horarios/montaSelect.js

@ -0,0 +1,36 @@
async function montaSelectDestino(id_type, modal, code_id = null) {
const data = await requestType(id_type);
const selectDestino = modal.querySelector(".select-destino");
const inputDestino = modal.querySelector(".input-destino");
if (!data) {
selectDestino.style.display = 'none';
selectDestino.disabled = true;
inputDestino.disabled = false;
inputDestino.style.display = 'block';
return;
}
selectDestino.style.display = 'block';
selectDestino.disabled = false;
inputDestino.disabled = true;
inputDestino.style.display = 'none';
let selectElements = "";
if (code_id) {
data.forEach(element => {
let matriculaOrId = element.matricula ?? element.id;
if (matriculaOrId.toString() === code_id.toString()) {
selectElements +=
` <option value="${element.id}" selected>${element.name ?? element.nome}</option>`
} else {
selectElements +=
` <option value="${element.id}">${element.name ?? element.nome}</option>`
}
})
} else {
data.forEach(element => {
selectElements += `<option value="${element.id}">${element.name ?? element.nome}</option>`
});
}
selectDestino.innerHTML = selectElements;
}

13
public/js/views/horarios/requestType.js

@ -0,0 +1,13 @@
function requestType(id_type) {
return new Promise(function(resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `horarios/${id_type}`,
success: function(response) {
const data = response.data.data;
resolve(data);
}
});
});
}

88
public/js/views/horariosOption/atualizaHorariosOptions.js

@ -0,0 +1,88 @@
function atualizaHorariosOptions(horario_option_id) {
$.ajax({
type: "get",
url: `options/${horario_option_id}/edit`,
success: async function({
data
}) {
const option_data = data.option_data;
const types = data.types;
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector("input[name='horario_inicio']").value = option_data.horario_inicio;
modal.querySelector("input[name='horario_fim']").value = option_data.horario_fim;
var selectSemana = document.getElementById("dias_semana2");
var selectSemanaFim = document.getElementById("dias_semana_fim2");
var selectMes = document.getElementById("meses2");
var selectMesFim = document.getElementById("meses_fim2");
var selectDia = document.getElementById("dias_mes2");
var selectDiaFim = document.getElementById("dias_mes_fim2");
modal.querySelector('form').action = `options/${horario_option_id}`;
if (option_data.feriado) {
modal.querySelector("input[id='feriado_sim']").checked = true;
} else {
modal.querySelector("input[id='feriado_nao']").checked = true;
}
if (option_data.todos_mes) {
modal.querySelector("input[id='todos_mes']").checked = true;
selectMes.disabled = true;
selectMesFim.disabled = true;
selectMes.selectedIndex = -1;
selectMesFim.selectedIndex = -1;
} else {
modal.querySelector("input[id='todos_mes_nao']").checked = true;
selectMes.selectedIndex = option_data.mes - 1;
selectMesFim.selectedIndex = option_data.mes_fim - 1;
}
if (option_data.todos_dias_semana) {
modal.querySelector("input[id='todos_dias_semana']").checked = true;
selectSemana.disabled = true;
selectSemanaFim.disabled = true;
selectSemana.selectedIndex = -1;
selectSemanaFim.selectedIndex = -1;
} else {
modal.querySelector("input[id='todos_dias_semana_nao']").checked = true;
selectSemana.selectedIndex = option_data.semana - 1;
selectSemanaFim.selectedIndex = option_data.semana_fim - 1;
}
if (option_data.todos_dias_mes) {
modal.querySelector("input[id='todos_dias_mes']").checked = true;
selectDia.disabled = true;
selectDiaFim.disabled = true;
selectDia.selectedIndex = -1;
selectDiaFim.selectedIndex = -1;
} else {
modal.querySelector("input[id='todos_dias_mes_nao']").checked = true;
selectDia.selectedIndex = option_data.dias_mes - 1;
selectDiaFim.selectedIndex = option_data.dias_mes_fim - 1;
}
let containerTypes = '';
types.forEach(type => {
if (option_data.opcao == type.id) {
containerTypes +=
` <option value="${type.id}" selected>${type.name}</option>`
} else {
containerTypes +=
` <option value="${type.id}">${type.name}</option>`
}
});
modal.querySelector(".select-types").innerHTML = containerTypes;
montaSelectDestino(option_data.opcao, modal, option_data.acao);
}
});
}
document.querySelectorAll('.select-types').forEach(el => {
el.addEventListener('change', (e) => {
const select = e.target;
const modal = select.closest('.modal') ?? select.closest('.modal-edit');
const id_redirect = select.value;
montaSelectDestino(id_redirect, modal);
})
})

37
public/js/views/horariosOption/montaSelect.js

@ -0,0 +1,37 @@
async function montaSelectDestino(id_type, modal, code_id=null) {
const data = await requestType(id_type);
const selectDestino = modal.querySelector(".select-destino");
const inputDestino = modal.querySelector(".input-destino");
if (!data) {
selectDestino.style.display = 'none';
selectDestino.disabled = true;
inputDestino.disabled = false;
inputDestino.style.display = 'block';
return;
}
selectDestino.style.display = 'block';
selectDestino.disabled = false;
inputDestino.disabled = true;
inputDestino.style.display = 'none';
let selectElements = "";
if (code_id) {
data.forEach(element => {
let matriculaOrId = element.matricula ?? element.id;
if (matriculaOrId.toString() === code_id.toString()) {
selectElements +=
` <option value="${element.id}" selected>${element.name ?? element.nome}</option>`
} else {
selectElements +=
` <option value="${element.id}">${element.name ?? element.nome}</option>`
}
})
} else {
data.forEach(element => {
selectElements += `<option value="${element.id}">${element.name ?? element.nome}</option>`
});
}
selectDestino.innerHTML = selectElements;
}

13
public/js/views/horariosOption/requestType.js

@ -0,0 +1,13 @@
function requestType(id_type) {
return new Promise(function(resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `options/${id_type}`,
success: function(response) {
const data = response.data.data;
resolve(data);
}
});
});
}

12
public/js/views/menu/index.js

@ -0,0 +1,12 @@
function clickedMenu(event) {
const parent = event.currentTarget;
parent.querySelector(".submenu").classList.toggle('hidden');
parent.querySelector(".dropIcon").classList.toggle('selected');
}
function responsiveSidebar(event) {
const shadow = event.currentTarget;
const sidebar = document.querySelector('.sidebar');
sidebar.classList.remove('show');
shadow.classList.add('hidden');
}

20
public/js/views/navegation/index.js

@ -0,0 +1,20 @@
const sidebar = document.querySelector('.sidebar');
const shadow = document.querySelector('.shadowSidebar');
function toogleTheme() {
let value = localStorage.theme === 'dark' ? 'light' : 'dark';
document.querySelector('.btn-theme').innerHTML = value === 'dark' ? '<i class="fas fa-moon text-xl"></i>' : '<i class="fas fa-sun text-xl" ></i>';
localStorage.setItem('theme', value);
selectTheme();
}
function showSidebar() {
const larguraJanela = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
if (larguraJanela < 650) {
sidebar.classList.add('show');
shadow.classList.toggle('hidden');
} else {
sidebar.classList.toggle('hidden');
sidebar.classList.remove('show');
}
}

24
public/js/views/pausa/atualizaPausa.js

@ -0,0 +1,24 @@
function atualizarPausa(id_user) {
$.ajax({
type: "get",
url: `pausas/editar/${id_user}`,
success: function({
data
}) {
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `/pausas/editar/${data.id}`;
modal.querySelector("input[name='motivo']").value = data.motivo;
const containerCheckbox = modal.querySelector(".container-checkbox");
if (!data.is_ativo) {
containerCheckbox.innerHTML = `<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

59
public/js/views/redirect/atualizaRedirect.js

@ -0,0 +1,59 @@
function atualizaRedirect(id_redirect) {
$.ajax({
type: "get",
url: `redirects/${id_redirect}/edit`,
success: function({
data
}) {
const redirect_data = data.redirect_data;
const numbers_channels = data.numbers;
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `redirects/${id_redirect}`;
modal.querySelector("input[name='nome']").value = redirect_data.name;
modal.querySelector("textarea[name='descricao']").value = redirect_data.description;
const containerRadioStatus = document.querySelector(".container-radio-status");
const containerRadioInitial = document.querySelector(".container-radio-initial");
if (redirect_data.status) {
containerRadioStatus.innerHTML =
`<input type="radio" name="status" value="on" checked> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" > <span class="dark:text-gray-100">Não</span>`
} else {
containerRadioStatus.innerHTML =
`<input type="radio" name="status" value="on" > <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
if (redirect_data.initial) {
containerRadioInitial.innerHTML =
`<input type="radio" name="initial" value="on" checked> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="initial" value="off" > <span class="dark:text-gray-100">Não</span>`
} else {
containerRadioInitial.innerHTML =
`<input type="radio" name="initial" value="on" > <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="initial" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
const containerChannels = modal.querySelector("select[name='channel']");
numbers_channels.forEach(channel => {
if (redirect_data.id_number === channel.id) {
containerChannels.innerHTML =
` <option value="${channel.id}" selected>${channel.title}</option>`
} else {
containerChannels.innerHTML =
` <option value="${channel.id}">${channel.title}</option>`
}
})
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}

48
public/js/views/redirectOption/atualizaRedirectOption.js

@ -0,0 +1,48 @@
function atualizaRedirectOption(option_id) {
$.ajax({
type: "get",
url: `options/${option_id}/edit`,
success: async function({
data
}) {
const option_data = data.option_data;
const types = data.types
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.setAttribute('data-sequenceDB', option_data.sequence);
modal.querySelector("input[name='sequencia']").value = option_data.sequence;
modal.querySelector("textarea[name='descricao']").value = option_data.description;
modal.querySelector('form').action = `options/${option_id}`;
if (option_data.hide) {
modal.querySelector(".container-radio").innerHTML =
`<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100" >Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`;
} else {
modal.querySelector(".container-radio").innerHTML =
`<input type="radio" name="status" value="on" checked> <span class="mr-3 dark:text-gray-100" >Sim</span>
<input type="radio" name="status" value="off" > <span class="dark:text-gray-100">Não</span>`;
}
let containerTypes = '';
types.forEach(type => {
if (option_data.id_type === type.id) {
containerTypes +=
` <option value="${type.id}" selected>${type.name}</option>`
} else {
containerTypes +=
` <option value="${type.id}">${type.name}</option>`
}
})
modal.querySelector(".select-types").innerHTML = containerTypes;
montaSelectDestino(option_data.id_type, modal, option_data.code_id);
}
});
}
document.querySelectorAll('.select-types').forEach(el => {
el.addEventListener('change', (e) => {
const select = e.target;
const modal = select.closest('.modal') ?? select.closest('.modal-edit');
const id_redirect = select.value;
montaSelectDestino(id_redirect, modal);
})
})

35
public/js/views/redirectOption/montaSelect.js

@ -0,0 +1,35 @@
async function montaSelectDestino(id_type, modal, code_id = null) {
const data = await requestType(id_type);
const selectDestino = modal.querySelector(".select-destino");
const inputDestino = modal.querySelector(".input-destino");
if (!data) {
selectDestino.style.display = 'none';
selectDestino.disabled = true;
inputDestino.disabled = false;
inputDestino.style.display = 'block';
return;
}
selectDestino.style.display = 'block';
selectDestino.disabled = false;
inputDestino.disabled = true;
inputDestino.style.display = 'none';
let selectElements = "";
if (code_id) {
data.forEach(element => {
let matriculaOrId = element.matricula ?? element.id;
if (matriculaOrId.toString() === code_id.toString()) {
selectElements +=
` <option value="${element.id}" selected>${element.name ?? element.nome}</option>`
} else {
selectElements +=
` <option value="${element.id}">${element.name ?? element.nome}</option>`
}
})
} else {
data.forEach(element => {
selectElements += `<option value="${element.id}">${element.name ?? element.nome}</option>`
});
}
selectDestino.innerHTML = selectElements;
}

13
public/js/views/redirectOption/requestType.js

@ -0,0 +1,13 @@
function requestType(id_type) {
return new Promise(function(resolve, reject) {
$.ajax({
type: "get",
dataType: 'json',
url: `options/${id_type}`,
success: function(response) {
const data = response.data.data;
resolve(data);
}
});
});
}

14
public/js/cadastroSystemMessage.js → public/js/views/systemMessages/cadastroSystemMessage.js

@ -16,28 +16,24 @@ const autoCompleteMessages = [{
},
];
const listItemFormatValue = 3;
const valueEspacamento = 50;
const inputs = document.querySelectorAll('.autoCompleteInput');
inputs.forEach(input => {
input.addEventListener('input', (e) =>{
input.addEventListener('input', (e) => {
let cont = 0;
fechaBox();
let selectedTexts = '';
let inputValue = e.target.value;
let isAtSign = inputValue.indexOf("@") >= 0;
// valor padrão para formatação de caixa de itens
const listItemFormatValue = 3;
if (isAtSign) {
let inputText = '@' + inputValue.split("@").pop(); // Obtem o texto depois do arroba
let inputText = '@' + inputValue.split("@").pop(); // Pega o valor depois do arroba
autoCompleteMessages.forEach(message => {
let messageValue = message.value;
const textContainsMessage = messageValue.includes(inputText.toLowerCase())
if (textContainsMessage) {
selectedTexts += `<li class="p-2 dark:text-gray-100 hover:bg-gray-600 cursor-pointer rounded-md mb-3" onclick="selecionaMessage(event)" data-id="${message.id}">
${message.value}
</li>`
@ -46,9 +42,7 @@ inputs.forEach(input => {
};
}
})
if (selectedTexts) {
const alturaBox = calcularAlturaBox(valueEspacamento, cont);
$('.autoCompleteBox').css('bottom', `-${alturaBox}px`);
$(".autoCompleteBox").html(selectedTexts);
@ -60,7 +54,6 @@ inputs.forEach(input => {
function selecionaMessage({ target }) {
const id = parseInt(target.getAttribute("data-id"));
const item = autoCompleteMessages.find(message => message.id === id);
const parent = target.parentNode.parentNode;
const input = parent.querySelector(".autoCompleteInput");
@ -68,7 +61,6 @@ function selecionaMessage({ target }) {
const indexLastAtSign = inputValue.lastIndexOf("@");
const formatedInputValue = inputValue.substring(0, indexLastAtSign);
const formatedValue = formatedInputValue + item.value;
input.value = formatedValue;
input.focus();
fechaBox();

351
public/js/views/templates/cadastroTemplates.js

@ -0,0 +1,351 @@
const modal = document.querySelector('.modal');
const form = document.querySelector("#cadastro-template");
const steps = modal.querySelectorAll('.step');
const channel_token = document.currentScript.getAttribute('channel_token');
const channel_number = document.currentScript.getAttribute('channel_number');
const workspace = document.currentScript.getAttribute('workspace');
function deletarTemplate(id) {
if(!confirm('Deseja realmente deletar esse template?')) return false;
const csrfToken = document.querySelector("input[name='_token']").value;
$.ajax({
type: "delete",
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + channel_token)
},
url: `https://api.positus.global/v2/workspaces/${workspace}/message-templates/${id}`,
error: function (error) {
console.log(error);
}
});
$.ajax({
type: "delete",
headers: {
"X-CSRF-Token": csrfToken
},
url: `templates/deletar/${id}`,
error: function (error) {
console.log(error);
}
});
}
/* Events */
function nextStep() {
const currentStep = getCurrentStep() === 1;
const isValidated = currentStep ? validationInput() : validationTextArea();
if (isValidated) {
toogleNextStep();
}
}
function backStep() {
toogleBackStep();
}
function showInput(e) {
const target = e.target;
const labelSwitch = target.parentNode;
const containerToogle = labelSwitch.parentNode;
labelSwitch.querySelector('span').classList.toggle('hidden');
containerToogle.querySelector('p').classList.toggle('hidden');
containerToogle.querySelector('.input-text').classList.toggle('hidden');
}
function showButtons(e) {
const target = e.target;
const labelSwitch = target.parentNode;
const btnAdd = document.querySelector('.btn-add');
const containerInputs = document.querySelector('.container-inputs');
containerInputs.classList.toggle('hidden');
labelSwitch.querySelector('span').classList.toggle('hidden');
btnAdd.classList.toggle('hidden');
}
function cadastroTemplate(e) {
e.preventDefault();
const components = montaComponents();
const data = createFormData(components);
handleBtnCadastro();
fetchCadastroPositus(data, channel_token);
}
function addInput() {
const containerInputs = document.querySelector('.container-inputs');
const inputs = [...containerInputs.querySelectorAll('.input')];
const countInputs = inputs.length;
if (countInputs > 2) {
return;
}
const input = `<div class="flex-col gap-2 border border-slate-500 p-3 mb-2 input relative">
<i class="fas fa-times dark:text-gray-100 absolute right-[-22px] top-7 cursor-pointer p-1" onclick="removeInput(event)"></i>
<input type="text"
class="w-full dark:bg-gray-800 placeholder:text-gray-800 dark:placeholder:text-gray-100 dark:text-gray-100 input-text"
placeholder="texto do botão " name="footer" oninput="caracteLimit(event)">
<p class="mt-2 dark:bg-gray-80 dark:text-gray-100 text-sm flex justify-end" data-limit="25">Limite de caracteres 0/60</p>
</div>
`;
containerInputs.insertAdjacentHTML("beforeend", input);
}
function removeInput(e) {
const target = e.target;
const containerInputClicked = target.parentNode;
const containerInputs = document.querySelector('.container-inputs');
const inputs = containerInputs.querySelectorAll('.input');
const countInputs = inputs.length;
if (countInputs <= 1) {
return;
}
containerInputClicked.remove();
}
function caracteLimit(e) {
const target = e.target;
const containerName = target.parentNode;
let inputContent = target.value;
const contentCount = inputContent.length;
const paragraph = containerName.querySelector('p');
const limit = paragraph.getAttribute("data-limit");
if (contentCount > limit) {
target.value = inputContent.slice(0, limit);
return;
}
atualizaContador(contentCount, limit, paragraph);
}
/* Functions */
function toogleNextStep() {
let selected;
let next;
for (let i = 0; i < steps.length; i++) {
const isHidden = steps[i].classList.contains('hidden');
if (!isHidden) {
selected = steps[i];
next = steps[i + 1];
}
}
selected.classList.toggle('hidden');
next.classList.toggle('hidden');
const [btnStep, btnBackStep, btnCadastro] = getButtons();
/* True adiciona Classe | false remove Classe*/
const isStep3 = next.classList.contains('step-3');
btnCadastro.classList.toggle("hidden", !isStep3);
btnStep.classList.toggle("hidden", isStep3);
const isHidden = btnCadastro.classList.contains('hidden');
if (isHidden) {
btnBackStep.classList.remove('hidden')
}
}
function toogleBackStep() {
let selected;
let back;
for (let i = 0; i < steps.length; i++) {
const isHidden = steps[i].classList.contains('hidden');
if (!isHidden) {
selected = steps[i];
back = steps[i - 1];
}
}
selected.classList.toggle('hidden');
back.classList.toggle('hidden');
const [btnStep, btnBackStep, btnCadastro] = getButtons();
const isHidden = btnCadastro.classList.contains('hidden');
/* True adiciona Classe | false remove Classe*/
const isStep3 = back.classList.contains('step-3');
btnCadastro.classList.toggle("hidden", !isStep3);
btnStep.classList.remove("hidden", isStep3);
if (isHidden) {
btnBackStep.classList.toggle('hidden')
}
}
function atualizaContador(count, limit, p) {
p.innerText = `Limite de caracteres ${count}/${limit}`;
}
function toogleInputAction(input, status = false) {
input.disabled = status;
}
function validationInput() {
const input = modal.querySelector("input[name='name']");
if (input.value.length < 1) {
input.classList.add('border-2', 'border-red-500');
return false;
}
input.classList.remove('border-2', 'border-red-500');
return true;
}
function validationTextArea() {
const textArea = modal.querySelector("textarea[name='body']");
if (textArea.value.length < 1) {
textArea.classList.add('border-2', 'border-red-500');
return false;
}
textArea.classList.remove('border-2', 'border-red-500');
return true;
}
function getButtons() {
const btnStep = modal.querySelector('.btn-step');
const btnBackStep = modal.querySelector('.btn-back-step');
const btnCadastro = modal.querySelector('.btn-cadastro');
return [btnStep, btnBackStep, btnCadastro];
}
function getCurrentStep() {
let selected;
for (let i = 0; i < steps.length; i++) {
const isHidden = steps[i].classList.contains('hidden');
if (!isHidden) {
selected = i + 1;
}
}
return selected;
}
function fetchCadastroPositus(data, token) {
$.ajax({
type: "post",
data: data,
processData: false,
contentType: false,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + token)
},
url: `https://api.positus.global/v2/workspaces/${workspace}/message-templates`,
success: function (data) {
handleBtnCadastro();
fetchCadastroDB(data.data);
},
error: function (error) {
handleBtnCadastro();
console.log(error);
}
});
}
function fetchCadastroDB(data) {
const csrfToken = document.querySelector("input[name='_token']").value;
$.ajax({
type: "post",
data: data,
headers: {
"X-CSRF-Token": csrfToken
},
url: `/templates`,
success: function (data) {
cadastroSucess(data.message);
},
error: function (error) {
console.log(error);
}
});
}
function cadastroSucess(message) {
fechaModal();
montaTemplates();
const header = document.querySelector(".header");
const div = document.createElement('div');
div.classList.add("w-full", "p-5", "bg-blue-600", "mb-5", "text-white", "rounded", "uppercase", "font-bold", "text-lg");
div.innerHTML = message;
header.parentNode.insertBefore(div, header);
}
function fechaModal() {
modal.style.display = 'none';
}
function montaComponents() {
const components = []
const checkedHeader = document.querySelector('.header-checkbox').checked;
if (checkedHeader) {
components.push({
type: 'HEADER',
format: 'TEXT',
text: document.querySelector("input[name='header']").value,
});
}
components.push(
{
type: 'BODY',
text: document.querySelector("textarea[name='body']").value,
},
);
const checkedFooter = document.querySelector('.footer-checkbox').checked;
if (checkedFooter) {
components.push({
type: 'FOOTER',
text: document.querySelector("input[name='footer']"),
});
}
const buttonCheckbox = document.querySelector('.button-checkbox').checked;
if (buttonCheckbox) {
const inputs = [...document.querySelectorAll('.input')];
const formatedArray = inputs.map(input => {
const inputValue = input.querySelector('.input-text').value;
return {
text: inputValue,
type: "QUICK_REPLY"
}
});
components.push(
{
type: "BUTTONS",
buttons: formatedArray
}
)
}
return components;
}
function createFormData(formDataArray) {
const formData = new FormData();
const category = form.querySelector("select[name='category']").value;
const name = form.querySelector("input[name='name']").value;
const language = form.querySelector("select[name='language']").value;
formData.append('category', category);
formData.append('name', name);
formData.append('language', language);
formDataArray.forEach((obj, index) => {
for (const key in obj) {
if (Array.isArray(obj[key])) {
obj[key].forEach((item, subIndex) => {
for (const subKey in item) {
formData.append(`components[${index}][${key}][${subIndex}][${subKey}]`, item[subKey]);
}
});
} else {
formData.append(`components[${index}][${key}]`, obj[key]);
}
}
});
return formData;
}
function handleBtnCadastro(){
const btn = modal.querySelector('.btn-cadastro');
const text = btn.querySelectorAll('div');
text.forEach(text => {
text.classList.toggle('hidden');
});
}

109
public/js/views/templates/index.js

@ -0,0 +1,109 @@
function getTemplates() {
return new Promise(function (resolve, reject) {
$.ajax({
type: "get",
url: `/templates/buscarTemplates`,
success: function (response) {
const data = response.data;
resolve(data);
}
});
})
}
const colors = {
APPROVED: "green-500",
REJECTED: "red-500",
PENDING: "orange-500",
}
function montaBody(components) {
let body = '';
components.forEach(component => {
if (component.type === 'BODY') {
body = component.text.replace(/\r?\n/g, "<br>");
}
});
return body;
}
function montaHeader(components) {
let header = '';
components.forEach(component => {
if (component.type === 'HEADER') {
header = component.text.replace(/\r?\n/g, "<br>");
}
});
return header;
}
function montaFooter(components) {
let footer = '';
components.forEach(component => {
if (component.type === 'FOOTER') {
footer = component.text.replace(/\r?\n/g, "<br>");
}
});
return footer;
}
function montaButtons(components) {
let buttons = ``;
components.forEach(component => {
if (component.type === 'BUTTONS') {
component.buttons.forEach(button => {
buttons += `<span class="bg-blue-600 p-2 rounded flex-1">${button.text}</span>`
})
}
});
return buttons;
}
async function montaTemplates() {
const templates = await getTemplates();
let elementsTable = "";
templates.forEach(template => {
if(template.status === "PENDING_DELETION") return;
var dataAtualizada = new Date(template.created_at);
function adicionarZero(numero) {
return numero < 10 ? '0' + numero : numero;
}
var dataFormatada = `${dataAtualizada.getFullYear()}-${adicionarZero(dataAtualizada.getMonth() + 1)}-${adicionarZero(dataAtualizada.getDate())} ${adicionarZero(dataAtualizada.getHours())}:${adicionarZero(dataAtualizada.getMinutes())}:${adicionarZero(dataAtualizada.getSeconds())}`;
let decodeComponents = JSON.parse(template.components);
elementsTable += `
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 pl-5">${template.name}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${template.category}</td>
<td class="p-3 text-sm text-gray-100 w-[150px]">
<div class="bg-blue-600 p-3 rounded">
<div class="header">
${montaHeader(decodeComponents)}
</div>
<div class="body">
${montaBody(decodeComponents)}
</div>
<div class="footer">
${montaFooter(decodeComponents)}
</div>
</div>
<div class="buttons flex flex-wrap gap-2 mt-3 text-center">
${montaButtons(decodeComponents)}
</div>
</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100"> ${template.language}</td>
<td class="p-3 text-sm text-${colors[template.status]}"> ${template.status}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100"> ${dataFormatada}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">
<button onclick="deletarTemplate('${template.id}')" class="bg-red-600 text-white p-2 rounded text-lg w-full">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>`
});
$(".table-templates").html(elementsTable);
}

79
resources/css/app.css

@ -1,6 +1,81 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.dropIcon {
@apply text-gray-300;
}
.dropIcon.selected {
@apply text-gray-100 rotate-90;
}
@media (max-width:640px) {
.sidebar {
@apply hidden;
}
}
@media (min-width:640px) {
.shadowSidebar {
@apply hidden
}
}
.sidebar.show {
@apply block !important;
}
@layer components {
.tooltip {
@apply bg-gray-700 absolute p-3 rounded-md left-8 bottom-[-20px] w-[300px] text-center hidden
}
.info-box:hover .tooltip {
@apply block
}
}
.sender,
.receiver,
.events {
border-radius: 1rem;
padding: 0.8rem;
margin-bottom: 1rem;
/* word-break: break-all; */
word-wrap: break-word;
position: relative;
max-width: 80%;
}
.sender {
border-bottom-left-radius: 0px;
@apply bg-gray-700 text-gray-100 mr-auto;
}
.receiver {
border-bottom-right-radius: 0px;
@apply text-gray-100 ml-auto bg-indigo-500;
}
.events {
@apply bg-gray-700 text-gray-100 self-center;
}
.sender-message,
.receiver-message {
font-size: 1rem;
margin-right: 2rem;
display: block;
/* word-wrap: break-word; */
}
.scrollbar::-webkit-scrollbar {
width: 0.6rem;
height: 5rem;
}
.scrollbar::-webkit-scrollbar-thumb {
@apply bg-gray-600
}

158
resources/views/admin/agentesLogados.blade.php

@ -1,158 +0,0 @@
<x-app-layout>
<div class="py-8 px-8">
<div class="header flex flex-col items-center gap-4">
<a href="{{route('dashboard')}}" style="margin-right: auto;" class="text-blue-500 hover:underline">Voltar</a>
@if(session('status'))
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg">
{{session('status')}}
</div>
@endif
<h1 class="mb-5 text-gray-900 dark:text-gray-100 text-3xl font-bold text-center">
Agentes na Fila - {{$fila->nome}}
</h1>
<form action="{{route('filas')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
<div class="search bg-white dark:bg-gray-800 rounded px-3 py-1 overflow-hidden">
<i class="fas fa-search dark:text-gray-100"></i>
<input type="text" name="pesquisa" class="border-none bg-transparent focus:ring-transparent
placeholder:text-gray-800 dark:placeholder:text-gray-100 dark:text-gray-100 " placeholder="Pesquise filas">
</div>
<select class="border-none rounded overflow-hidden dark:bg-gray-800 dark:text-gray-100" name="status">
<option value="ativo">Livres</option>
<option value="desativado" selected>Ocupados</option>
</select>
<button class="lg:ml-auto ml-0 bg-blue-900 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-lg overflow-hidden">Pesquisar</button>
</form>
</div>
<div class="body mt-8 overflow-auto rounded-lg shadow bg-gray-50 dark:bg-gray-800">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100">
<tr>
<th class="p-3 text-sm font-semibold tracking-wide text-center">ID</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">NOME</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">MATRICULA</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">LOGIN</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">MOTIVO PAUSA</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">STATUS</th>
<th class="p-3 text-sm font-semibold tracking-wide text-center">ATENDIMENTOS</th>
{{-- <th class="p-3 text-sm font-semibold tracking-wide text-center">TMA</th> --}}
<th class="p-3 text-sm font-semibold tracking-wide text-center">AÇÕES</th>
</tr>
</thead>
<tbody class="table-relatorio">
</tbody>
</table>
</div>
</div>
@push('agentesLogados')
<script>
const colorStatus = {
LIVRE: "green-500",
OCUPADO: "red-500",
PAUSA: "red-500",
INDISPONIVEL: "orange-400",
CHAMANDO: "orange-400",
}
function getFilaId()
{
const url = window.location.href;
const splitUrl = url.split('/');
const id = splitUrl[splitUrl.length - 1];
return id;
}
function getDadosRelatorioDB(fila_id) {
return new Promise(function(resolve, reject){
$.ajax({
type: "get",
dataType: 'json',
url: `/dashboard/agentes/${fila_id}/fila`,
success: function(response) {
const data = response.data;
resolve(data);
}
});
})
}
async function montarRelatorios()
{
const id = getFilaId();
const data = await getDadosRelatorioDB(id);
let elementsTable = "";
data.forEach(supervisor => {
const tempoFormatado = supervisor.login.split(".")[0];
const btnPlay = `
<form method="POST" action="/dashboard/agentes/{{$fila->id}}/retirarPausa" title="Retirar Pausa?" class="bg-blue-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer" >
@csrf
<input type="hidden" name="supervisor_id" value="${supervisor.id}">
<button >
<i class="fas fa-play-circle"></i>
</button>
</form>
`
const btnPausa = `<a class="bg-blue-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer" title="Colocar em Pausa?" onclick="selecionaAgente(${supervisor.id})"><i class="fas fa-pause"></i></a>`;
const agenteIsPausa = supervisor.status === "PAUSA" ? btnPlay : btnPausa;
elementsTable += `
<tr class="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 text-center">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.id}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.nome}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.matricula}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${tempoFormatado}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.motivo_pausa ?? "Sem Pausa"}</td>
<td class="p-3 text-sm text-${colorStatus[supervisor.status]}">${supervisor.status}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">${supervisor.qtde_atendimento}</td>
<td class="p-3 text-sm font-bold flex items-center gap-2 flex-wrap">
${agenteIsPausa}
<form method="POST" action="/dashboard/agentes/{{$fila->id}}/desconectar" title="Desconectar Pausa?" class="bg-red-600 text-white flex-1 rounded text-xl text-center py-2 px-3" >
@csrf
<input type="hidden" name="supervisor_id" value="${supervisor.id}">
<button >
<i class="fas fa-power-off"></i>
</button>
</form>
</td>
</tr>
`
});
$(".table-relatorio").html(elementsTable);
}
function selecionaAgente(supervisor_id){
console.log(supervisor_id);
const modal = document.querySelector(".modal");
modal.style.display = "block";
modal.querySelector("form").action = `/dashboard/agentes/{{$fila->nome}}/pausar`;
modal.querySelector("input[name='supervisor_id']").value = supervisor_id;
}
montarRelatorios();
setInterval(() => {
montarRelatorios();
}, 10000);
</script>
@endpush
<x-modalPausarAgente :pausas="$pausas"></x-modalPausarAgente>
</x-app-layout>

93
resources/views/admin/cadastros/channels.blade.php

@ -0,0 +1,93 @@
<x-app-layout>
<div class="py-8 px-8">
@if(session('status'))
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg">
{{session('status')}}
</div>
@endif
<div class="header flex flex-col items-center gap-4 ">
<div class="flex justify-between items-center w-full mb-5">
<h1 class=" text-gray-900 dark:text-gray-100 text-3xl font-bold text-center">
Channels
</h1>
@can('store_channels')
<button class="bg-blue-500 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-base overflow-hidden" @click="modal = !modal" type="button">Cadastrar Channel</button>
@endcan
</div>
<form action="{{route('channels')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
<div class="search bg-white dark:bg-gray-800 rounded px-3 py-1 overflow-hidden">
<i class="fas fa-search dark:text-gray-100"></i>
<input type="text" name="pesquisa" class="border-none bg-transparent focus:ring-transparent placeholder:text-gray-800 dark:placeholder:text-gray-100 dark:text-gray-100 " placeholder="Pesquise channels" value="{{$search}}">
</div>
<select class="border-none rounded overflow-hidden dark:bg-gray-800 dark:text-gray-100" name="status">
@if($status)
<option value="ativo" selected>Ativos</option>
<option value="desativado">Desativados</option>
@else
<option value="ativo">Ativos</option>
<option value="desativado" selected>Desativados</option>
@endif
</select>
<button class="lg:ml-auto ml-0 bg-blue-900 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-lg overflow-hidden">Pesquisar</button>
</form>
</div>
<div class="body mt-4 overflow-auto rounded-lg shadow">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100">
<tr>
<th class="p-3 text-sm font-semibold tracking-wide text-left pl-5">ID</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Nome</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left pl-5">ID Empresa</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Número</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Channel</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Workspace</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Título</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Status</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Ações</th>
</tr>
</thead>
<tbody>
@foreach($channels as $channel)
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 pl-5">{{$channel->id}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$channel->name}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 pl-5">{{$channel->id_empresa}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$channel->number}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$channel->channel}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$channel->work_space}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$channel->title}}</td>
@if($channel->status)
<td class="p-3 text-sm text-green-500 font-bold">ATIVO</td>
@else
<td class="p-3 text-sm text-red-500 font-bold">DESATIVADO</td>
@endif
<td class="p-3 text-sm font-bold w-32">
@can('edit_channels')
<button class="bg-blue-600 text-white p-2 rounded text-lg text-center w-full mb-2" onclick="atualizaChannel(<?= $channel->id ?>)" title="editar channel"><i class="fas fa-edit"></i></button>
@endcan
@if($channel->status)
@can('destroy_channels')
<form method="POST" id="formulario" action="channels/deletar/{{$channel->id}}" title="desativar channel" onclick="javascript: if(!confirm('Deseja desativar esse channel?')) return false;">
@csrf
@method('delete')
<button class="bg-red-600 text-white p-2 rounded text-lg w-full">
<i class="fas fa-ban"></i>
</button>
</form>
@endcan
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<x-modal.insert.modalChannels></x-modal>
<x-modal.edit.modalEditChannels></x-modalEditChannels>
<script src="{{ asset('js/views/channels/atualizaChannel.js') }}"></script>
</x-app-layout>

85
resources/views/admin/cadastros/contatos.blade.php

@ -0,0 +1,85 @@
<x-app-layout>
<div class="py-8 px-8">
@if(session('status'))
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg">
{{session('status')}}
</div>
@endif
<div class="header flex flex-col items-center gap-4 ">
<div class="flex justify-between items-center w-full mb-5">
<h1 class=" text-gray-900 dark:text-gray-100 text-3xl font-bold text-center">
Contatos
</h1>
@can('store_contatos')
<button class="bg-blue-500 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-base overflow-hidden" @click="modal = !modal" type="button">Cadastrar Contato</button>
@endcan
</div>
<form action="{{route('contatos.index')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
<div class="search bg-white dark:bg-gray-800 rounded px-3 py-1 overflow-hidden">
<i class="fas fa-search dark:text-gray-100"></i>
<input type="text" name="pesquisa" class="border-none bg-transparent focus:ring-transparent placeholder:text-gray-800 dark:placeholder:text-gray-100 dark:text-gray-100 " placeholder="Pesquise contatos">
</div>
<select class="border-none rounded overflow-hidden dark:bg-gray-800 dark:text-gray-100" name="status">
@if($status)
<option value="ativo" selected>Ativos</option>
<option value="desativado">Desativados</option>
@else
<option value="ativo">Ativos</option>
<option value="desativado" selected>Desativados</option>
@endif
</select>
<button class="lg:ml-auto ml-0 bg-blue-900 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-lg overflow-hidden">Pesquisar</button>
</form>
</div>
<div class="body mt-4 overflow-auto rounded-lg shadow">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100">
<tr>
<th class="p-3 text-sm font-semibold tracking-wide text-left pl-5">Nome</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Numero</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Email</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Data Cadastro</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Status</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Ações</th>
</tr>
</thead>
<tbody>
@foreach($contatos as $contato)
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 pl-5">{{$contato->nome}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100" data-mask="+00 00 0000-0000">{{$contato->contato}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$contato->email}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $contato->formated_date }}</td>
@if($contato->status)
<td class="p-3 text-sm text-green-500 font-bold">ATIVO</td>
@else
<td class="p-3 text-sm text-red-500 font-bold">DESATIVADO</td>
@endif
<td class="p-3 text-sm font-bold w-28">
@can('store_contatos')
<button onclick="atualizaContato({{ $contato->id }})" class="bg-blue-600 text-white p-2 rounded text-lg text-center w-full mb-2" title="editar contato"><i class="fas fa-edit"></i></button>
@endcan
@can('store_contatos')
<form action="{{ route('contatos.destroy', ['contato' => $contato->id]) }}" method="POST" title="desativar contato" onclick="javascript: if(!confirm('Deseja desativar esse contato?'))
return false;">
@method('delete')
@csrf
<button class="bg-red-600 dark:bg-red-700 text-white p-2 rounded text-lg w-full">
<i class="fas fa-ban"></i>
</button>
</form>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<x-modal.insert.modalContatos></x-modalContatos>
<x-modal.edit.modalEditContatos></x-modalEditContatos>
<script src="{{ asset('js/views/contatos/atualizaContato.js') }}"></script>
</x-app-layout>

61
resources/views/admin/empresas.blade.php → resources/views/admin/cadastros/empresas.blade.php

@ -15,14 +15,21 @@
<button class="bg-blue-500 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-base overflow-hidden" @click="modal = !modal" type="button">Cadastrar Empresa</button>
</div>
<form action="{{route('users')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
<form action="{{route('empresas.index')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
<div class="search bg-white dark:bg-gray-800 rounded px-3 py-1 overflow-hidden">
<i class="fas fa-search dark:text-gray-100"></i>
<input type="text" name="pesquisa" class="border-none bg-transparent focus:ring-transparent placeholder:text-gray-800 dark:placeholder:text-gray-100 dark:text-gray-100 " placeholder="Pesquise empresas">
</div>
<select class="border-none rounded overflow-hidden dark:bg-gray-800 dark:text-gray-100" name="status">
@if($status)
<option value="ativo" selected>Ativas</option>
<option value="desativado">Desativadas</option>
@else
<option value="ativo">Ativas</option>
<option value="desativado" selected>Desativadas</option>
@endif
</select>
<button class="lg:ml-auto ml-0 bg-blue-900 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-lg overflow-hidden">Pesquisar</button>
</form>
@ -49,18 +56,20 @@
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$empresa->token}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{$empresa->email}}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100" data-mask="000.000.000-00">{{$empresa->cnpj}}</td>
<td class="p-3 text-sm font-bold flex items-center gap-2 flex-wrap">
<button class="bg-blue-600 text-white p-2 rounded flex-1 text-lg text-center" onclick="atualizaRedirectOption(<?= $empresa->id ?>)"><i class="fas fa-edit"></i></button>
{{-- @if($empresa->status)
<form method="POST" id="formulario" action="users/deletar/{{$empresa->id}}" class="flex-1 flex" title="desativar conta?" onclick="javascript: if(!confirm('Deseja desativar esse usuário?')) return false;">
@csrf
<td class="p-3 text-sm font-bold w-28">
@can('store_empresas')
<button onclick="atualizaEmpresa({{ $empresa->id }})" class="bg-blue-600 text-white p-2 rounded text-lg text-center w-full mb-2" title="editar empresa"><i class="fas fa-edit"></i></button>
@endcan
@can('store_empresas')
<form action="{{ route('empresas.destroy', ['empresa' => $empresa->id]) }}" method="POST" title="desativar empresa" onclick="javascript: if(!confirm('Deseja desativar essa empresa?'))
return false;">
@method('delete')
<button class="bg-red-600 dark:bg-red-700 text-white p-2 rounded flex-1 text-lg">
@csrf
<button class="bg-red-600 dark:bg-red-700 text-white p-2 rounded text-lg w-full">
<i class="fas fa-ban"></i>
</button>
</form>
@endif --}}
@endcan
</td>
</tr>
@endforeach
@ -69,32 +78,8 @@
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.mask/1.14.16/jquery.mask.js"></script>
<script>
function atualizaRedirectOption(empresa_id) {
$.ajax({
type: "get",
url: `empresas/${empresa_id}/edit`,
success: function({data}) {
const empresa_data = data.empresa_data;
console.log(empresa_data);
$(".modal-edit").show();
$(".modal-edit").find('form').attr('action', `empresas/${empresa_id}`);
$(".modal-edit").find("input[name='nome']").val(empresa_data.nome);
$(".modal-edit").find("input[name='token']").val(empresa_data.token);
$(".modal-edit").find("input[name='email']").val(empresa_data.email);
const cnpjFormatado = $(".modal-edit").find("input[name='cnpj']").masked(empresa_data.cnpj);
$(".modal-edit").find("input[name='cnpj']").val(cnpjFormatado);
}
});
}
</script>
<x-modalEmpresa></x-modalEmpresa>
<x-modalEditEmpresa></x-modalEditEmpresa>
<x-modal.insert.modalEmpresa></x-modalEmpresa>
<x-modal.edit.modalEditEmpresa></x-modalEditEmpresa>
<script src="{{ asset('js/views/empresa/atualizaEmpresa.js') }}"></script>
</x-app-layout>

52
resources/views/admin/filas.blade.php → resources/views/admin/cadastros/filas.blade.php

@ -7,13 +7,14 @@
</div>
@endif
<div class="header flex flex-col items-center gap-4 ">
<div class="flex justify-between items-center w-full mb-5">
<h1 class=" text-gray-900 dark:text-gray-100 text-3xl font-bold text-center">
Filas
</h1>
@can('store_filas')
<button class="bg-blue-500 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-base overflow-hidden" @click="modal = !modal" type="button">Cadastrar Fila</button>
@endcan
</div>
<form action="{{route('filas')}}" method="GET" class="flex w-full gap-3 flex-col lg:flex-row ">
@ -64,15 +65,19 @@
@endif
<td class="p-3 text-sm font-bold w-28">
<button class="bg-blue-600 text-white p-2 rounded text-lg text-center w-full mb-2" onclick="atualizarFila(<?= $fila->id ?>)"><i class="fas fa-edit"></i></button>
@can('edit_filas')
<button class="bg-blue-600 text-white p-2 rounded text-lg text-center w-full mb-2" onclick="atualizarFila(<?= $fila->id ?>)" title="editar fila"><i class="fas fa-edit"></i></button>
@endcan
@if($fila->is_ativa)
<form method="POST" action="filas/deletar/{{$fila->id}}" title="desativar fila?" onclick="javascript: if(!confirm('Deseja desativar essa fila?')) return false;">
@can('destroy_filas')
<form method="POST" action="filas/deletar/{{$fila->id}}" title="desativar fila" onclick="javascript: if(!confirm('Deseja desativar essa fila?')) return false;">
@csrf
@method('delete')
<button class="bg-red-600 dark:bg-red-700 text-white p-2 rounded text-lg w-full">
<i class="fas fa-ban"></i>
</button>
</form>
@endcan
@endif
</td>
</tr>
@ -82,43 +87,8 @@
</div>
</div>
<x-modalFila></x-modalFila>
<x-modalEditFila></x-modalEditFila>
@push('updateFila')
<script src="https://code.jquery.com/jquery-3.6.4.js" integrity="sha256-a9jBBRygX1Bh5lt8GZjXDzyOB+bWve9EiO7tROUtj/E=" crossorigin="anonymous"></script>
<script>
function atualizarFila(id_fila) {
$.ajax({
type: "get",
url: `filas/editar/${id_fila}`,
success: function({
data
}) {
console.log(data)
const modal = document.querySelector('.modal-edit');
modal.style.display = 'block';
modal.querySelector('form').action = `/filas/editar/${data.id}`;
modal.querySelector("input[name='nome']").value = data.nome;
const containerCheckbox = modal.querySelector(".container-checkbox");
if (!data.is_ativa) {
containerCheckbox.innerHTML = `<input type="radio" name="status" value="on"> <span class="mr-3 dark:text-gray-100">Sim</span>
<input type="radio" name="status" value="off" checked> <span class="dark:text-gray-100">Não</span>`
}
}
});
}
function fecharModal() {
document.querySelector('.modal-edit').style.display = 'none';
}
</script>
@endpush
<script src="{{ asset('js/views/fila/atualizaFila.js') }}"></script>
<x-modal.insert.modalFila></x-modalFila>
<x-modal.edit.modalEditFila></x-modalEditFila>
</x-app-layout>

102
resources/views/admin/cadastros/horarios.blade.php

@ -0,0 +1,102 @@
<?php
$tipos = [
1 => 'QUEUE',
2 => 'REDIRECT',
3 => 'AGENT',
4 => 'TEXT',
5 => 'HORARIO'
];
?>
<x-app-layout>
<div class="py-8 px-8">
@if (session('status'))
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg">
{{ session('status') }}
</div>
@endif
<div class="header flex flex-col items-center gap-4 mt-2 mb-8">
<div class="flex justify-between items-center w-full mb-5">
<h1 class=" text-gray-900 dark:text-gray-100 text-3xl font-bold text-center">
Horários
</h1>
@can('store_redirect')
<button
class="bg-blue-500 hover:bg-opacity-90 transition-all text-white py-2 px-6 rounded text-base overflow-hidden"
@click="modal = !modal" type="button">Cadastrar Horários</button>
@endcan
</div>
</div>
<div class="body mt-4 overflow-auto rounded-lg shadow">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100">
<tr>
<th class="p-3 text-sm font-semibold tracking-wide text-left ">ID</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Nome</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">ID Canal</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Opção Fora de Horário</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Ação</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Status</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Opções</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left">Ações</th>
</tr>
</thead>
<tbody>
@foreach ($horarios as $horario)
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $horario->id }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $horario->nome }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $horario->id_number }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $tipos[$horario->opcao] }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">{{ $horario->acao }}</td>
@if ($horario->status)
<td class="p-3 text-sm text-green-500 font-bold">ATIVO</td>
@else
<td class="p-3 text-sm text-red-500 font-bold">DESATIVADO</td>
@endif
<td class="p-3 text-sm text-gray-700 dark:text-gray-100">
@can('show_redirect_option')
<a
href="{{ route('horarios.options.index', ['horario' => $horario->id]) }}"
class="text-blue-500 hover:underline">Editar Horários Disponíveis</a>
@endcan
</td>
<td class="p-3 text-sm font-bold flex-wrap w-32">
@can('edit_redirect')
<button class="bg-blue-600 text-white p-2 rounded text-lg w-full mb-2" title="editar horário"
onclick="atualizaHorarios( {{ $horario->id }})"><i
class="fas fa-edit"></i></button>
@endcan
@can('destroy_redirect')
<form method="POST"
action="{{ route('horarios.destroy', ['horario' => $horario->id]) }}"
class="flex-1 flex" title="deletar horário"
onclick="javascript: if(!confirm('Deseja deletar esses dados?')) return false;">
@csrf
@method('delete')
<button class="bg-red-600 text-white p-2 rounded text-lg w-full">
<i class="fas fa-trash"></i>
</button>
</form>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<x-modal.insert.modalHorarios :types="$types" :numberChannels="$numberChannels"></x-modalHorarios>
<x-modal.edit.modalEditHorarios :types="$types" :numberChannels="$numberChannels"></x-modalEditHorarios>
<script src="{{ asset('js/views/horarios/montaSelect.js') }}"></script>
<script src="{{ asset('js/views/horarios/requestType.js') }}"></script>
<script src="{{ asset('js/views/horarios/atualizaHorarios.js') }}"></script>
<script>
const modal = document.querySelector(".modal");
const id_type = 1;
montaSelectDestino(id_type, modal);
</script>
</x-app-layout>

140
resources/views/admin/cadastros/horariosOption.blade.php

@ -0,0 +1,140 @@
<?php
$tipos = [
1 => 'QUEUE',
2 => 'REDIRECT',
3 => 'AGENT',
4 => 'TEXT',
5 => 'HORARIO'
];
$semana = [
'' => '',
1 => 'Segunda',
2 => 'Terça',
3 => 'Quarta',
4 => 'Quinta',
5 => 'Sexta',
6 => 'Sábado',
7 => 'Domingo',
];
$meses = ['' => '', 1 => 'Janeiro', 2 => 'Fevereiro', 3 => 'Março', 4 => 'Abril', 5 => 'Maio', 6 => 'Junho', 7 => 'Julho', 8 => 'Agosto', 9 => 'Setembro', 10 => 'Outubro', 11 => 'Novembro', 12 => 'Dezembro'];
?>
<x-app-layout>
<div class="py-8 px-8">
<a href="{{ route('horarios.index') }}" style="margin-right: auto;"
class="text-blue-500 hover:underline mb-5 block">Voltar</a>
@if (session('status'))
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg alert">
{{ session('status') }}
</div>
@endif
<div class="w-full p-5 bg-blue-600 mb-5 text-white rounded uppercase font-bold text-lg alert hidden act">
</div>
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg ">
<div class="p-6 text-gray-900 dark:text-gray-100">
<div class="flex items-center">
<div>
<h1 class="text-2xl font-bold">Horários Disponíveis</h1>
</div>
@can('store_redirect_option')
<button class="ml-auto mt-2 bg-blue-600 text-white py-2 px-6 rounded-md text-lg"
@click="modal = !modal">Adicionar</button>
@endcan
</div>
<div class="body mt-4 overflow-auto rounded-lg shadow">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100">
<tr>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center">Feriado</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center" colspan="2">Horário</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center" colspan="3">Dia da Semana</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center" colspan="3">Dia do Mês</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center" colspan="3">Meses</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center" colspan="2">Caso Combine</th>
<th class="p-3 text-sm font-semibold tracking-wide text-left border text-center">Ações</th>
</tr>
</thead>
<tbody>
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td></td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Início</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Fim</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Todos</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Inicio</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Fim</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Todos</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Inicio</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Fim</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Todos</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Inicio</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Fim</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Opção</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">Destino</td>
<td></td>
</tr>
</tbody>
@foreach ($options as $option)
<tbody>
<tr class="bg-white dark:bg-gray-800 dark:border-gray-600 border-b-2">
<td class="p-3 text-sm {{ $option->feriado == '1' ? 'text-green-500' : 'text-red-500' }} font-bold text-center">{{ $option->feriado == '1' ? 'SIM' : 'NÃO' }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $option->horario_inicio }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $option->horario_fim }}</td>
<td class="p-3 text-sm {{ $option->todos_dias_semana == '1' ? 'text-green-500' : 'text-red-500' }} font-bold border text-center">{{ $option->todos_dias_semana == '1' ? 'SIM' : 'NÃO' }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $semana[$option->semana] }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $semana[$option->semana_fim] }}</td>
<td class="p-3 text-sm {{ $option->todos_dias_mes == '1' ? 'text-green-500' : 'text-red-500' }} font-bold border text-center">{{ $option->todos_dias_mes == '1' ? 'SIM' : 'NÃO' }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $option->dias_mes }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $option->dias_mes_fim }}</td>
<td class="p-3 text-sm {{ $option->todos_mes == '1' ? 'text-green-500' : 'text-red-500' }} font-bold border text-center">{{ $option->todos_mes == '1' ? 'SIM' : 'NÃO' }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $meses[$option->mes] }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $meses[$option->mes_fim] }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 uppercase border text-center">{{ $tipos[$option->opcao] }}</td>
<td class="p-3 text-sm text-gray-700 dark:text-gray-100 border text-center">{{ $option->acao }}</td>
<td class="p-3 text-sm font-bold flex items-center gap-2 flex-wrap">
<button class="bg-blue-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer title="editar horário"
onclick="atualizaHorariosOptions( {{ $option->id }})">
<i class="fas fa-edit"></i>
</button>
@can('destroy_redirect')
<form method="POST"
action="{{ route('horarios.options.destroy', ['horario' => $id_horario, 'option' => $option->id]) }}"
class="flex-1 flex" title="deletar horário"
onclick="javascript: if(!confirm('Deseja deletar esses dados?')) return false;">
@csrf
@method('delete')
<button class="bg-red-600 text-white rounded flex-1 text-xl text-center py-2 px-3 cursor-pointer">
<i class="fas fa-trash"></i>
</button>
</form>
@endcan
</td>
</tr>
</tbody>
@endforeach
</table>
</div>
</div>
</div>
</div>
<x-modal.insert.modalHorariosOption :id_horario="$id_horario" :types="$types"></x-modalHorarioOption>
<x-modal.edit.modalEditHorariosOptions :types="$types"></x-modalEditHorariosOptions>
<script src="{{ asset('js/views/horariosOption/atualizaHorariosOptions.js') }}"></script>
<script src="{{ asset('js/views/horariosOption/montaSelect.js') }}"></script>
<script src="{{ asset('js/views/horariosOption/requestType.js') }}"></script>
<script>
const modal = document.querySelector(".modal");
const id_type = 1;
montaSelectDestino(id_type, modal);
</script>
</x-app-layout>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save