diff --git a/.env.example b/.env.example
index 79ed088..4545621 100644
--- a/.env.example
+++ b/.env.example
@@ -3,6 +3,7 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
+APP_TIMEZONE=America/Sao_Paulo
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
@@ -24,8 +25,15 @@ DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=omniboard
-DB_USERNAME=root
-DB_PASSWORD=
+DB_USERNAME=omniboard_user
+DB_PASSWORD="*Ingline.Sys#9420%SECURITY#"
+
+REVERB_APP_ID=test
+REVERB_APP_KEY=test
+REVERB_APP_SECRET=test
+REVERB_HOST="localhost"
+REVERB_PORT=8081
+REVERB_SCHEME=http
SESSION_DRIVER=database
SESSION_LIFETIME=120
@@ -35,7 +43,7 @@ SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
-QUEUE_CONNECTION=database
+QUEUE_CONNECTION=redis
CACHE_STORE=database
# CACHE_PREFIX=
@@ -63,3 +71,17 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
+
+BROADCAST_CONNECTION=reverb
+
+REVERB_APP_ID=348643
+REVERB_APP_KEY=v40nrhrty4rxpsv9mnt1
+REVERB_APP_SECRET=vf0mesdcuze3dob3nage
+REVERB_HOST="localhost"
+REVERB_PORT=8081
+REVERB_SCHEME=http
+
+VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
+VITE_REVERB_HOST="${REVERB_HOST}"
+VITE_REVERB_PORT="${REVERB_PORT}"
+VITE_REVERB_SCHEME="${REVERB_SCHEME}"
\ No newline at end of file
diff --git a/app/Events/DashboardUpdate.php b/app/Events/DashboardUpdate.php
new file mode 100644
index 0000000..c0d2831
--- /dev/null
+++ b/app/Events/DashboardUpdate.php
@@ -0,0 +1,37 @@
+tenantId = $tenantId;
+ }
+
+ // Define o canal privado: "dashboard.{tenant_id}"
+ public function broadcastOn(): array
+ {
+ return [
+ new PrivateChannel('dashboard.' . $this->tenantId),
+ ];
+ }
+
+ // Opcional: Nome do evento no Front (padrão é o nome da classe)
+ public function broadcastAs()
+ {
+ return 'metrics.updated';
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/AmiEventController.php b/app/Http/Controllers/Api/AmiEventController.php
index 6bbba44..91094f9 100644
--- a/app/Http/Controllers/Api/AmiEventController.php
+++ b/app/Http/Controllers/Api/AmiEventController.php
@@ -9,12 +9,12 @@
use App\Models\WaitingList;
use App\Models\DailyMetric;
use Carbon\Carbon;
+use App\Events\DashboardUpdate;
class AmiEventController extends Controller
{
public function handle(Request $request)
{
- // 1. Autenticação via Token (Header: X-Tenant-Token)
$token = $request->header('X-Tenant-Token');
$tenant = Tenant::where('api_key', $token)->first();
@@ -22,7 +22,6 @@ public function handle(Request $request)
return response()->json(['error' => 'Unauthorized'], 401);
}
- // 2. Dados do Evento
$data = $request->all();
$eventName = $data['Event'] ?? null;
$queueName = $data['Queue'] ?? null;
@@ -31,17 +30,14 @@ public function handle(Request $request)
return response()->json(['status' => 'ignored']);
}
- // DEPOIS (Correto)
$queue = Queue::where('tenant_id', $tenant->id)
->where('source_id', $queueName) // Procura "08000" na coluna source_id
->first();;
- // Se a fila não existe no banco, ignoramos ou criamos automaticamente (opcional)
if (!$queue) {
return response()->json(['error' => 'Fila não encontrada'], 404);
}
- // 4. Processar Lógica de Negócio
switch ($eventName) {
case 'QueueCallerJoin':
$this->handleJoin($queue, $data);
@@ -60,11 +56,9 @@ public function handle(Request $request)
return response()->json(['status' => 'success']);
}
- // LÓGICA DE NEGÓCIO (A mesma do script anterior, mas agora com Eloquent)
private function handleJoin($queue, $data)
{
- // Adiciona na Lista de Espera COM TENANT_ID
WaitingList::create([
'tenant_id' => $queue->tenant_id, // <--- ADICIONE ESTA LINHA
'queue_id' => $queue->id,
@@ -73,28 +67,25 @@ private function handleJoin($queue, $data)
'entered_at' => now(),
]);
- // Incrementa Total do Dia
$this->updateMetric($queue, 'received_count', 1);
+ $this->broadcastUpdate($queue);
}
private function handleConnect($queue, $data)
{
- // Remove da Lista de Espera
WaitingList::where('queue_id', $queue->id)
->where('caller_number', $data['CallerIDNum'])
->delete();
- // Atualiza TME (Média Ponderada)
- // Nota: Isso é simplificado. Em produção real, calcular média ponderada em SQL é melhor.
$metric = $this->getTodayMetric($queue);
$holdTime = intval($data['HoldTime'] ?? 0);
- // Novo TME = ((Atual * Qtd) + Novo) / (Qtd + 1)
$newAvg = (($metric->avg_wait_time * $metric->answered_count) + $holdTime) / ($metric->answered_count + 1);
$metric->avg_wait_time = $newAvg;
$metric->answered_count += 1;
$metric->save();
+ $this->broadcastUpdate($queue);
}
private function handleComplete($queue, $data)
@@ -102,28 +93,24 @@ private function handleComplete($queue, $data)
$talkTime = intval($data['TalkTime'] ?? 0);
$metric = $this->getTodayMetric($queue);
- // Evita divisão por zero se o connect falhou
if ($metric->answered_count > 0) {
- // Novo TMA = ((Atual * (Qtd-1)) + Novo) / Qtd
- // Nota: Usamos Qtd (answered_count) como divisor pois a chamada já foi contada no Connect
$newAvg = (($metric->avg_talk_time * ($metric->answered_count - 1)) + $talkTime) / $metric->answered_count;
$metric->avg_talk_time = $newAvg;
$metric->save();
}
+ $this->broadcastUpdate($queue);
}
private function handleAbandon($queue, $data)
{
- // Remove da Espera
WaitingList::where('queue_id', $queue->id)
->where('caller_number', $data['CallerIDNum'])
->delete();
- // Incrementa Abandonos
$this->updateMetric($queue, 'abandoned_count', 1);
+ $this->broadcastUpdate($queue);
}
- // Helpers
private function getTodayMetric($queue)
{
return DailyMetric::firstOrCreate(
@@ -132,7 +119,7 @@ private function getTodayMetric($queue)
'date' => Carbon::today()
],
[
- 'tenant_id' => $queue->tenant_id, // <--- O PULO DO GATO: Herda o ID da Fila
+ 'tenant_id' => $queue->tenant_id,
'received_count' => 0,
'answered_count' => 0,
'abandoned_count' => 0
@@ -145,4 +132,9 @@ private function updateMetric($queue, $field, $value)
$metric = $this->getTodayMetric($queue);
$metric->increment($field, $value);
}
+ private function broadcastUpdate($queue)
+ {
+ // Dispara o evento apenas para o Tenant dono da fila
+ broadcast(new DashboardUpdate($queue->tenant_id));
+ }
}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 0e21cf2..19c38cc 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -9,6 +9,7 @@
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
+ channels: __DIR__.'/../routes/channels.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
diff --git a/composer.json b/composer.json
index 336521e..d9d1ecb 100644
--- a/composer.json
+++ b/composer.json
@@ -9,6 +9,7 @@
"php": "^8.2",
"inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^12.0",
+ "laravel/reverb": "^1.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.10.1",
"tightenco/ziggy": "^2.0"
diff --git a/composer.lock b/composer.lock
index 1f48237..163bf07 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "aa715b4183fb30d40dbf87a9f5da20c6",
+ "content-hash": "1653c1a4f4f30f9313278604a331d838",
"packages": [
{
"name": "brick/math",
@@ -135,6 +135,136 @@
],
"time": "2024-02-09T16:56:22+00:00"
},
+ {
+ "name": "clue/redis-protocol",
+ "version": "v0.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/redis-protocol.git",
+ "reference": "6f565332f5531b7722d1e9c445314b91862f6d6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/redis-protocol/zipball/6f565332f5531b7722d1e9c445314b91862f6d6c",
+ "reference": "6f565332f5531b7722d1e9c445314b91862f6d6c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\Redis\\Protocol\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "A streaming Redis protocol (RESP) parser and serializer written in pure PHP.",
+ "homepage": "https://github.com/clue/redis-protocol",
+ "keywords": [
+ "parser",
+ "protocol",
+ "redis",
+ "resp",
+ "serializer",
+ "streaming"
+ ],
+ "support": {
+ "issues": "https://github.com/clue/redis-protocol/issues",
+ "source": "https://github.com/clue/redis-protocol/tree/v0.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-07T11:06:28+00:00"
+ },
+ {
+ "name": "clue/redis-react",
+ "version": "v2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/reactphp-redis.git",
+ "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/reactphp-redis/zipball/84569198dfd5564977d2ae6a32de4beb5a24bdca",
+ "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca",
+ "shasum": ""
+ },
+ "require": {
+ "clue/redis-protocol": "^0.3.2",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.0 || ^1.1",
+ "react/promise-timer": "^1.11",
+ "react/socket": "^1.16"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.5",
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\React\\Redis\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
+ "description": "Async Redis client implementation, built on top of ReactPHP.",
+ "homepage": "https://github.com/clue/reactphp-redis",
+ "keywords": [
+ "async",
+ "client",
+ "database",
+ "reactphp",
+ "redis"
+ ],
+ "support": {
+ "issues": "https://github.com/clue/reactphp-redis/issues",
+ "source": "https://github.com/clue/reactphp-redis/tree/v2.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-03T16:18:33+00:00"
+ },
{
"name": "dflydev/dot-access-data",
"version": "v3.0.3",
@@ -508,6 +638,53 @@
],
"time": "2025-03-06T22:45:56+00:00"
},
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9 || ^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Evenement\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "support": {
+ "issues": "https://github.com/igorw/evenement/issues",
+ "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+ },
+ "time": "2023-08-08T05:53:35+00:00"
+ },
{
"name": "fruitcake/php-cors",
"version": "v1.4.0",
@@ -1403,6 +1580,88 @@
},
"time": "2025-11-21T20:52:52+00:00"
},
+ {
+ "name": "laravel/reverb",
+ "version": "v1.6.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/reverb.git",
+ "reference": "b97d21650bcfaa462dfa4735048dbc33359514e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/reverb/zipball/b97d21650bcfaa462dfa4735048dbc33359514e1",
+ "reference": "b97d21650bcfaa462dfa4735048dbc33359514e1",
+ "shasum": ""
+ },
+ "require": {
+ "clue/redis-react": "^2.6",
+ "guzzlehttp/psr7": "^2.6",
+ "illuminate/console": "^10.47|^11.0|^12.0",
+ "illuminate/contracts": "^10.47|^11.0|^12.0",
+ "illuminate/http": "^10.47|^11.0|^12.0",
+ "illuminate/support": "^10.47|^11.0|^12.0",
+ "laravel/prompts": "^0.1.15|^0.2.0|^0.3.0",
+ "php": "^8.2",
+ "pusher/pusher-php-server": "^7.2",
+ "ratchet/rfc6455": "^0.4",
+ "react/promise-timer": "^1.10",
+ "react/socket": "^1.14",
+ "symfony/console": "^6.0|^7.0",
+ "symfony/http-foundation": "^6.3|^7.0"
+ },
+ "require-dev": {
+ "orchestra/testbench": "^8.36|^9.15|^10.8",
+ "pestphp/pest": "^2.0|^3.0|^4.0",
+ "phpstan/phpstan": "^1.10",
+ "ratchet/pawl": "^0.4.1",
+ "react/async": "^4.2",
+ "react/http": "^1.9"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "aliases": {
+ "Output": "Laravel\\Reverb\\Output"
+ },
+ "providers": [
+ "Laravel\\Reverb\\ApplicationManagerServiceProvider",
+ "Laravel\\Reverb\\ReverbServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Reverb\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Joe Dixon",
+ "email": "joe@laravel.com"
+ }
+ ],
+ "description": "Laravel Reverb provides a real-time WebSocket communication backend for Laravel applications.",
+ "keywords": [
+ "WebSockets",
+ "laravel",
+ "real-time",
+ "websocket"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/reverb/issues",
+ "source": "https://github.com/laravel/reverb/tree/v1.6.3"
+ },
+ "time": "2025-11-28T20:12:49+00:00"
+ },
{
"name": "laravel/sanctum",
"version": "v4.2.1",
@@ -2659,6 +2918,102 @@
],
"time": "2025-11-20T02:34:59+00:00"
},
+ {
+ "name": "paragonie/sodium_compat",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/sodium_compat.git",
+ "reference": "547e2dc4d45107440e76c17ab5a46e4252460158"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/547e2dc4d45107440e76c17ab5a46e4252460158",
+ "reference": "547e2dc4d45107440e76c17ab5a46e4252460158",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1",
+ "php-64bit": "*"
+ },
+ "require-dev": {
+ "infection/infection": "^0",
+ "nikic/php-fuzzer": "^0",
+ "phpunit/phpunit": "^7|^8|^9|^10|^11",
+ "vimeo/psalm": "^4|^5|^6"
+ },
+ "suggest": {
+ "ext-sodium": "Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "ParagonIE\\Sodium\\": "namespaced/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "ISC"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com"
+ },
+ {
+ "name": "Frank Denis",
+ "email": "jedisct1@pureftpd.org"
+ }
+ ],
+ "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists",
+ "keywords": [
+ "Authentication",
+ "BLAKE2b",
+ "ChaCha20",
+ "ChaCha20-Poly1305",
+ "Chapoly",
+ "Curve25519",
+ "Ed25519",
+ "EdDSA",
+ "Edwards-curve Digital Signature Algorithm",
+ "Elliptic Curve Diffie-Hellman",
+ "Poly1305",
+ "Pure-PHP cryptography",
+ "RFC 7748",
+ "RFC 8032",
+ "Salpoly",
+ "Salsa20",
+ "X25519",
+ "XChaCha20-Poly1305",
+ "XSalsa20-Poly1305",
+ "Xchacha20",
+ "Xsalsa20",
+ "aead",
+ "cryptography",
+ "ecdh",
+ "elliptic curve",
+ "elliptic curve cryptography",
+ "encryption",
+ "libsodium",
+ "php",
+ "public-key cryptography",
+ "secret-key cryptography",
+ "side-channel resistant"
+ ],
+ "support": {
+ "issues": "https://github.com/paragonie/sodium_compat/issues",
+ "source": "https://github.com/paragonie/sodium_compat/tree/v2.4.0"
+ },
+ "time": "2025-10-06T08:47:40+00:00"
+ },
{
"name": "phpoption/phpoption",
"version": "1.9.4",
@@ -3225,6 +3580,67 @@
},
"time": "2025-12-07T03:39:01+00:00"
},
+ {
+ "name": "pusher/pusher-php-server",
+ "version": "7.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pusher/pusher-http-php.git",
+ "reference": "148b0b5100d000ed57195acdf548a2b1b38ee3f7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/148b0b5100d000ed57195acdf548a2b1b38ee3f7",
+ "reference": "148b0b5100d000ed57195acdf548a2b1b38ee3f7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "guzzlehttp/guzzle": "^7.2",
+ "paragonie/sodium_compat": "^1.6|^2.0",
+ "php": "^7.3|^8.0",
+ "psr/log": "^1.0|^2.0|^3.0"
+ },
+ "require-dev": {
+ "overtrue/phplint": "^2.3",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Pusher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Library for interacting with the Pusher REST API",
+ "keywords": [
+ "events",
+ "messaging",
+ "php-pusher-server",
+ "publish",
+ "push",
+ "pusher",
+ "real time",
+ "real-time",
+ "realtime",
+ "rest",
+ "trigger"
+ ],
+ "support": {
+ "issues": "https://github.com/pusher/pusher-http-php/issues",
+ "source": "https://github.com/pusher/pusher-http-php/tree/7.2.7"
+ },
+ "time": "2025-01-06T10:56:20+00:00"
+ },
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
@@ -3423,6 +3839,595 @@
},
"time": "2025-12-14T04:43:48+00:00"
},
+ {
+ "name": "ratchet/rfc6455",
+ "version": "v0.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ratchetphp/RFC6455.git",
+ "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/859d95f85dda0912c6d5b936d036d044e3af47ef",
+ "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4",
+ "psr/http-factory-implementation": "^1.0",
+ "symfony/polyfill-php80": "^1.15"
+ },
+ "require-dev": {
+ "guzzlehttp/psr7": "^2.7",
+ "phpunit/phpunit": "^9.5",
+ "react/socket": "^1.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\RFC6455\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Matt Bonneau",
+ "role": "Developer"
+ }
+ ],
+ "description": "RFC6455 WebSocket protocol handler",
+ "homepage": "http://socketo.me",
+ "keywords": [
+ "WebSockets",
+ "rfc6455",
+ "websocket"
+ ],
+ "support": {
+ "chat": "https://gitter.im/reactphp/reactphp",
+ "issues": "https://github.com/ratchetphp/RFC6455/issues",
+ "source": "https://github.com/ratchetphp/RFC6455/tree/v0.4.0"
+ },
+ "time": "2025-02-24T01:18:22+00:00"
+ },
+ {
+ "name": "react/cache",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "^3.0 || ^2.0 || ^1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/cache/issues",
+ "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-11-30T15:59:55+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v1.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "^1.0 || ^0.6 || ^0.5",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3 || ^2",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/dns/issues",
+ "source": "https://github.com/reactphp/dns/tree/v1.14.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-18T19:34:28+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "suggest": {
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/event-loop/issues",
+ "source": "https://github.com/reactphp/event-loop/tree/v1.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-17T20:46:25+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "1.12.28 || 1.4.10",
+ "phpunit/phpunit": "^9.6 || ^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v3.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-08-19T18:57:03+00:00"
+ },
+ {
+ "name": "react/promise-timer",
+ "version": "v1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise-timer.git",
+ "reference": "4f70306ed66b8b44768941ca7f142092600fafc1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/4f70306ed66b8b44768941ca7f142092600fafc1",
+ "reference": "4f70306ed66b8b44768941ca7f142092600fafc1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.7.0 || ^1.2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\Timer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
+ "homepage": "https://github.com/reactphp/promise-timer",
+ "keywords": [
+ "async",
+ "event-loop",
+ "promise",
+ "reactphp",
+ "timeout",
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise-timer/issues",
+ "source": "https://github.com/reactphp/promise-timer/tree/v1.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-04T14:27:45+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v1.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^1.13",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3.3 || ^2",
+ "react/promise-stream": "^1.4",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/socket/issues",
+ "source": "https://github.com/reactphp/socket/tree/v1.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-11-19T20:47:34+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.2"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/stream/issues",
+ "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-11T12:45:25+00:00"
+ },
{
"name": "symfony/clock",
"version": "v7.4.0",
diff --git a/config/app.php b/config/app.php
index 423eed5..28be821 100644
--- a/config/app.php
+++ b/config/app.php
@@ -65,7 +65,7 @@
|
*/
- 'timezone' => 'UTC',
+ 'timezone' => env('APP_TIMEZONE', 'UTC'),
/*
|--------------------------------------------------------------------------
diff --git a/config/broadcasting.php b/config/broadcasting.php
new file mode 100644
index 0000000..ebc3fb9
--- /dev/null
+++ b/config/broadcasting.php
@@ -0,0 +1,82 @@
+ env('BROADCAST_CONNECTION', 'null'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Broadcast Connections
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define all of the broadcast connections that will be used
+ | to broadcast events to other systems or over WebSockets. Samples of
+ | each available type of connection are provided inside this array.
+ |
+ */
+
+ 'connections' => [
+
+ 'reverb' => [
+ 'driver' => 'reverb',
+ 'key' => env('REVERB_APP_KEY'),
+ 'secret' => env('REVERB_APP_SECRET'),
+ 'app_id' => env('REVERB_APP_ID'),
+ 'options' => [
+ 'host' => env('REVERB_HOST'),
+ 'port' => env('REVERB_PORT', 443),
+ 'scheme' => env('REVERB_SCHEME', 'https'),
+ 'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
+ ],
+ 'client_options' => [
+ // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
+ ],
+ ],
+
+ 'pusher' => [
+ 'driver' => 'pusher',
+ 'key' => env('PUSHER_APP_KEY'),
+ 'secret' => env('PUSHER_APP_SECRET'),
+ 'app_id' => env('PUSHER_APP_ID'),
+ 'options' => [
+ 'cluster' => env('PUSHER_APP_CLUSTER'),
+ 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
+ 'port' => env('PUSHER_PORT', 443),
+ 'scheme' => env('PUSHER_SCHEME', 'https'),
+ 'encrypted' => true,
+ 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
+ ],
+ 'client_options' => [
+ // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
+ ],
+ ],
+
+ 'ably' => [
+ 'driver' => 'ably',
+ 'key' => env('ABLY_KEY'),
+ ],
+
+ 'log' => [
+ 'driver' => 'log',
+ ],
+
+ 'null' => [
+ 'driver' => 'null',
+ ],
+
+ ],
+
+];
diff --git a/config/reverb.php b/config/reverb.php
new file mode 100644
index 0000000..d7fc88e
--- /dev/null
+++ b/config/reverb.php
@@ -0,0 +1,95 @@
+ env('REVERB_SERVER', 'reverb'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Reverb Servers
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define details for each of the supported Reverb servers.
+ | Each server has its own configuration options that are defined in
+ | the array below. You should ensure all the options are present.
+ |
+ */
+
+ 'servers' => [
+
+ 'reverb' => [
+ 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
+ 'port' => env('REVERB_SERVER_PORT', 8080),
+ 'path' => env('REVERB_SERVER_PATH', ''),
+ 'hostname' => env('REVERB_HOST'),
+ 'options' => [
+ 'tls' => [],
+ ],
+ 'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
+ 'scaling' => [
+ 'enabled' => env('REVERB_SCALING_ENABLED', false),
+ 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
+ 'server' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'port' => env('REDIS_PORT', '6379'),
+ 'username' => env('REDIS_USERNAME'),
+ 'password' => env('REDIS_PASSWORD'),
+ 'database' => env('REDIS_DB', '0'),
+ 'timeout' => env('REDIS_TIMEOUT', 60),
+ ],
+ ],
+ 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
+ 'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
+ ],
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Reverb Applications
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define how Reverb applications are managed. If you choose
+ | to use the "config" provider, you may define an array of apps which
+ | your server will support, including their connection credentials.
+ |
+ */
+
+ 'apps' => [
+
+ 'provider' => 'config',
+
+ 'apps' => [
+ [
+ 'key' => env('REVERB_APP_KEY'),
+ 'secret' => env('REVERB_APP_SECRET'),
+ 'app_id' => env('REVERB_APP_ID'),
+ 'options' => [
+ 'host' => env('REVERB_HOST'),
+ 'port' => env('REVERB_PORT', 443),
+ 'scheme' => env('REVERB_SCHEME', 'https'),
+ 'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
+ ],
+ 'allowed_origins' => ['*'],
+ 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
+ 'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
+ 'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'),
+ 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
+ ],
+ ],
+
+ ],
+
+];
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 8de12ae..2ade2dd 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -23,11 +23,5 @@ public function run(): void
'email' => 'admin@omniboard.com',
'password' => bcrypt('password'), // A senha será 'password'
]);
-
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
- $this->call([OmniBoardSeeder::class]);
}
}
diff --git a/package-lock.json b/package-lock.json
index 116ae5f..4034ad3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,14 +6,17 @@
"": {
"devDependencies": {
"@inertiajs/vue3": "^2.0.0",
+ "@laravel/echo-vue": "^2.2.6",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/vite": "^4.0.0",
"@vitejs/plugin-vue": "^6.0.0",
"autoprefixer": "^10.4.12",
"axios": "^1.7.4",
"concurrently": "^9.0.1",
+ "laravel-echo": "^2.2.6",
"laravel-vite-plugin": "^2.0.0",
"postcss": "^8.4.31",
+ "pusher-js": "^8.4.0",
"tailwindcss": "^3.2.1",
"vite": "^7.0.0",
"vue": "^3.5.0"
@@ -604,6 +607,21 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@laravel/echo-vue": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@laravel/echo-vue/-/echo-vue-2.2.6.tgz",
+ "integrity": "sha512-nznzN1BYVYR+DQ/JLHvl4qKLioBhzQjGsscWeXKLLDNUgOkbZNpx8/tF+ycUtzEPB4fR88BNqBMXs5P60TvBrA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "pusher-js": "*",
+ "socket.io-client": "*",
+ "vue": "^3.0.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -957,6 +975,14 @@
"win32"
]
},
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/@tailwindcss/forms": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
@@ -1819,6 +1845,25 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1882,6 +1927,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/engine.io-client": {
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
+ "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.17.1",
+ "xmlhttprequest-ssl": "~2.1.1"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.4",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
@@ -2363,6 +2434,20 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/laravel-echo": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.2.6.tgz",
+ "integrity": "sha512-KuCldOrE8qbm0CVDBgc6FiX3VuReDu1C1xaS891KqwEUg9NT/Op03iiZqTWeVd0/WJ4H95q2pe9QEDJlwb/FPw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "pusher-js": "*",
+ "socket.io-client": "*"
+ }
+ },
"node_modules/laravel-precognition": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-1.0.0.tgz",
@@ -2759,6 +2844,14 @@
"mini-svg-data-uri": "cli.js"
}
},
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@@ -3057,6 +3150,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pusher-js": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0.tgz",
+ "integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tweetnacl": "^1.0.3"
+ }
+ },
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@@ -3324,6 +3427,38 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/socket.io-client": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
+ "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.2",
+ "engine.io-client": "~6.6.1",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -3584,6 +3719,13 @@
"dev": true,
"license": "0BSD"
},
+ "node_modules/tweetnacl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
+ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
+ "dev": true,
+ "license": "Unlicense"
+ },
"node_modules/update-browserslist-db": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
@@ -3779,6 +3921,39 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/ws": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xmlhttprequest-ssl": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+ "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 64f49c3..2e56e76 100644
--- a/package.json
+++ b/package.json
@@ -8,14 +8,17 @@
},
"devDependencies": {
"@inertiajs/vue3": "^2.0.0",
+ "@laravel/echo-vue": "^2.2.6",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/vite": "^4.0.0",
"@vitejs/plugin-vue": "^6.0.0",
"autoprefixer": "^10.4.12",
"axios": "^1.7.4",
"concurrently": "^9.0.1",
+ "laravel-echo": "^2.2.6",
"laravel-vite-plugin": "^2.0.0",
"postcss": "^8.4.31",
+ "pusher-js": "^8.4.0",
"tailwindcss": "^3.2.1",
"vite": "^7.0.0",
"vue": "^3.5.0"
diff --git a/resources/css/app.css b/resources/css/app.css
index b5c61c9..eba5c64 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -1,3 +1,53 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+
+/* Animações para a página de welcome */
+@keyframes float {
+ 0% {
+ transform: translateY(0px);
+ }
+
+ 50% {
+ transform: translateY(-15px);
+ }
+
+ 100% {
+ transform: translateY(0px);
+ }
+}
+
+@keyframes slide-up {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-float {
+ animation: float 6s ease-in-out infinite;
+}
+
+.animate-slide-up {
+ animation: slide-up 0.8s ease-out forwards;
+ opacity: 0;
+}
+
+.delay-100 {
+ animation-delay: 0.1s;
+}
+
+.delay-200 {
+ animation-delay: 0.2s;
+}
+
+.delay-300 {
+ animation-delay: 0.3s;
+}
+/* Fim das animações */
\ No newline at end of file
diff --git a/resources/js/Pages/Auth/Login.vue b/resources/js/Pages/Auth/Login.vue
index 7244b58..2b722c9 100644
--- a/resources/js/Pages/Auth/Login.vue
+++ b/resources/js/Pages/Auth/Login.vue
@@ -31,7 +31,7 @@