From 28faa727b151eaa870a2379e73b4a495a186b807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20P=C3=A9rez?= Date: Thu, 21 May 2026 23:21:41 -0600 Subject: [PATCH] Fix: socket permissions para nginx --- app/Events/NuevoMensajeWhatsApp.php | 41 +++ app/Http/Controllers/PromocionController.php | 190 ++++++++++++ .../WhatsAppAnalyticsController.php | 109 +++++++ .../Controllers/WhatsAppChatController.php | 127 ++++++++ .../Controllers/WhatsAppWebhookController.php | 116 ++++++++ app/Jobs/EnviarPromocionJob.php | 84 ++++++ app/Models/Alumno.php | 23 ++ app/Models/Promocion.php | 39 +++ app/Models/WhatsappLog.php | 24 ++ app/Services/WhatsAppService.php | 189 ++++++++++++ config/services.php | 7 + ..._05_20_000001_create_promociones_table.php | 28 ++ ...0_000002_create_alumno_promocion_table.php | 25 ++ ...5_20_000003_create_whatsapp_logs_table.php | 28 ++ ...20_000004_add_opt_out_to_alumnos_table.php | 23 ++ ...05_refactor_promocion_plantel_to_pivot.php | 34 +++ database/seeders/RoleSeeder.php | 8 + php-fpm-pool.conf | 6 +- .../{app-DAN06vjJ.js => app-DUTi2uSh.js} | 2 +- public/build/manifest.json | 2 +- .../views/layouts/_partials/header.blade.php | 2 + .../views/layouts/_partials/menu.blade.php | 24 ++ resources/views/promocion/analytics.blade.php | 202 +++++++++++++ resources/views/promocion/chat.blade.php | 281 ++++++++++++++++++ resources/views/promocion/create.blade.php | 148 +++++++++ resources/views/promocion/edit.blade.php | 168 +++++++++++ resources/views/promocion/index.blade.php | 162 ++++++++++ resources/views/user/create.blade.php | 1 + resources/views/user/edit.blade.php | 2 +- routes/api.php | 12 + routes/channels.php | 14 + routes/web.php | 29 ++ 32 files changed, 2144 insertions(+), 6 deletions(-) create mode 100644 app/Events/NuevoMensajeWhatsApp.php create mode 100644 app/Http/Controllers/PromocionController.php create mode 100644 app/Http/Controllers/WhatsAppAnalyticsController.php create mode 100644 app/Http/Controllers/WhatsAppChatController.php create mode 100644 app/Http/Controllers/WhatsAppWebhookController.php create mode 100644 app/Jobs/EnviarPromocionJob.php create mode 100644 app/Models/Promocion.php create mode 100644 app/Models/WhatsappLog.php create mode 100644 app/Services/WhatsAppService.php create mode 100644 database/migrations/2026_05_20_000001_create_promociones_table.php create mode 100644 database/migrations/2026_05_20_000002_create_alumno_promocion_table.php create mode 100644 database/migrations/2026_05_20_000003_create_whatsapp_logs_table.php create mode 100644 database/migrations/2026_05_20_000004_add_opt_out_to_alumnos_table.php create mode 100644 database/migrations/2026_05_20_000005_refactor_promocion_plantel_to_pivot.php rename public/build/assets/{app-DAN06vjJ.js => app-DUTi2uSh.js} (99%) create mode 100644 resources/views/promocion/analytics.blade.php create mode 100644 resources/views/promocion/chat.blade.php create mode 100644 resources/views/promocion/create.blade.php create mode 100644 resources/views/promocion/edit.blade.php create mode 100644 resources/views/promocion/index.blade.php diff --git a/app/Events/NuevoMensajeWhatsApp.php b/app/Events/NuevoMensajeWhatsApp.php new file mode 100644 index 00000000..c18a87c5 --- /dev/null +++ b/app/Events/NuevoMensajeWhatsApp.php @@ -0,0 +1,41 @@ +log->alumno_id) { + return []; + } + + return [ + new PrivateChannel('whatsapp-chat.' . $this->log->alumno_id), + ]; + } + + public function broadcastWith(): array + { + return [ + 'id' => $this->log->id, + 'tipo' => $this->log->tipo, + 'mensaje' => $this->log->mensaje, + 'media_type' => $this->log->media_type, + 'alumno_id' => $this->log->alumno_id, + 'telefono' => $this->log->telefono, + 'created_at' => $this->log->created_at?->format('H:i'), + ]; + } +} diff --git a/app/Http/Controllers/PromocionController.php b/app/Http/Controllers/PromocionController.php new file mode 100644 index 00000000..04fc3abd --- /dev/null +++ b/app/Http/Controllers/PromocionController.php @@ -0,0 +1,190 @@ +plantelesDelUsuario()->pluck('id'); + + $promociones = Promocion::with('planteles') + ->whereHas('planteles', fn($q) => $q->whereIn('plantels.id', $plantelesIds)) + ->latest() + ->paginate(15); + + return view('promocion.index', compact('promociones')); + } + + public function create() + { + $planteles = $this->plantelesDelUsuario(); + return view('promocion.create', compact('planteles')); + } + + public function store(Request $request) + { + $data = $request->validate([ + 'titulo' => 'required|string|max:200', + 'descripcion' => 'nullable|string|max:3000', + 'imagen' => 'nullable|image|max:10240', + 'fecha_inicio' => 'required|date', + 'fecha_fin' => 'required|date|after_or_equal:fecha_inicio', + 'status' => 'required|in:activo,inactivo', + 'plantels' => 'required|array|min:1', + 'plantels.*' => 'exists:plantels,id', + ]); + + $this->validarAccesoPlanteles($request->plantels ?? []); + + if ($request->hasFile('imagen')) { + $data['imagen'] = $request->file('imagen')->store('promociones', 'public'); + } + + unset($data['plantels']); + $promocion = Promocion::create($data); + $promocion->planteles()->sync($request->plantels); + + return redirect()->route('promocion.index')->with('success', 'Promoción creada correctamente.'); + } + + public function edit(Promocion $promocion) + { + $this->autorizarPlantel($promocion); + $planteles = $this->plantelesDelUsuario(); + $plantelesSeleccionados = $promocion->planteles->pluck('id')->toArray(); + return view('promocion.edit', compact('promocion', 'planteles', 'plantelesSeleccionados')); + } + + public function update(Request $request, Promocion $promocion) + { + $this->autorizarPlantel($promocion); + + $data = $request->validate([ + 'titulo' => 'required|string|max:200', + 'descripcion' => 'nullable|string|max:3000', + 'imagen' => 'nullable|image|max:10240', + 'fecha_inicio' => 'required|date', + 'fecha_fin' => 'required|date|after_or_equal:fecha_inicio', + 'status' => 'required|in:activo,inactivo', + 'plantels' => 'required|array|min:1', + 'plantels.*' => 'exists:plantels,id', + ]); + + $this->validarAccesoPlanteles($request->plantels ?? []); + + if ($request->hasFile('imagen')) { + if ($promocion->imagen) { + Storage::disk('public')->delete($promocion->imagen); + } + $data['imagen'] = $request->file('imagen')->store('promociones', 'public'); + } elseif ($request->boolean('remove_imagen') && $promocion->imagen) { + Storage::disk('public')->delete($promocion->imagen); + $data['imagen'] = null; + } + + unset($data['plantels']); + $promocion->update($data); + $promocion->planteles()->sync($request->plantels); + + return redirect()->route('promocion.index')->with('success', 'Promoción actualizada.'); + } + + public function destroy(Promocion $promocion) + { + $this->autorizarPlantel($promocion); + + if ($promocion->imagen) { + Storage::disk('public')->delete($promocion->imagen); + } + + $promocion->delete(); + + return back()->with('success', 'Promoción eliminada.'); + } + + public function toggleStatus(Promocion $promocion) + { + $this->autorizarPlantel($promocion); + + $nuevoStatus = $promocion->status === 'activo' ? 'inactivo' : 'activo'; + $promocion->update(['status' => $nuevoStatus]); + + return back()->with('success', $nuevoStatus === 'activo' ? 'Promoción activada.' : 'Promoción desactivada.'); + } + + public function enviar(Promocion $promocion) + { + $this->autorizarPlantel($promocion); + + $plantelesIds = $promocion->planteles->pluck('id'); + + $prospectos = Alumno::where('status', 1) + ->where('opt_out', false) + ->whereHas('alumnos.plantelUsuarios', function ($q) use ($plantelesIds) { + $q->whereIn('plantels.id', $plantelesIds); + }) + ->get(); + + $despachados = 0; + + foreach ($prospectos as $i => $alumno) { + if (!$promocion->alumnos()->where('alumno_id', $alumno->id)->exists()) { + $promocion->alumnos()->attach($alumno->id, ['status_envio' => 'pendiente']); + } + + EnviarPromocionJob::dispatch($promocion, $alumno) + ->delay(now()->addSeconds($i * 2)); + + $despachados++; + } + + if ($despachados === 0) { + return back()->with('swal', [ + 'icon' => 'warning', + 'title' => 'Sin destinatarios', + 'text' => 'No se encontraron prospectos activos para los planteles de esta promoción.', + ]); + } + + return back()->with('swal', [ + 'icon' => 'success', + 'title' => '¡Enviado!', + 'text' => "Se programó el envío para {$despachados} prospecto(s).", + ]); + } + + private function plantelesDelUsuario() + { + return Auth::user()->plantelUsuarios; + } + + private function autorizarPlantel(Promocion $promocion): void + { + $ids = $this->plantelesDelUsuario()->pluck('id'); + $tieneAcceso = $promocion->planteles->pluck('id')->intersect($ids)->isNotEmpty(); + + if (!$tieneAcceso) { + abort(403); + } + } + + private function validarAccesoPlanteles(array $plantelIds): void + { + $permitidos = $this->plantelesDelUsuario()->pluck('id'); + + foreach ($plantelIds as $id) { + if (!$permitidos->contains($id)) { + abort(403, 'No tienes acceso a uno de los planteles seleccionados.'); + } + } + } +} diff --git a/app/Http/Controllers/WhatsAppAnalyticsController.php b/app/Http/Controllers/WhatsAppAnalyticsController.php new file mode 100644 index 00000000..b986d00d --- /dev/null +++ b/app/Http/Controllers/WhatsAppAnalyticsController.php @@ -0,0 +1,109 @@ +plantelUsuarios->pluck('id'); + $planteles = Auth::user()->plantelUsuarios; + + $query = WhatsappLog::query(); + + if ($request->filled('plantel_id')) { + $plantelId = (int) $request->plantel_id; + $query->whereNotNull('alumno_id') + ->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) { + $q->where('plantels.id', $plantelId); + }); + } + + if ($request->filled('promocion_id')) { + $query->whereJsonContains('payload->promocion_id', (int) $request->promocion_id); + } + + if ($request->filled('fecha_desde')) { + $query->where('created_at', '>=', $request->fecha_desde . ' 00:00:00'); + } + + if ($request->filled('fecha_hasta')) { + $query->where('created_at', '<=', $request->fecha_hasta . ' 23:59:59'); + } + + $enviados = (clone $query)->where('tipo', 'enviado')->count(); + $fallidos = (clone $query)->where('tipo', 'error')->count(); + $optOuts = (clone $query)->where('tipo', 'opt_out')->count(); + $recibidos = (clone $query)->where('tipo', 'recibido')->count(); + + $logsRecientes = (clone $query) + ->with('alumno.alumnos') + ->latest('created_at') + ->paginate(20); + + $promociones = Promocion::whereHas('planteles', function ($q) use ($plantelesIds) { + $q->whereIn('plantels.id', $plantelesIds); + })->latest()->get(); + + return view('promocion.analytics', compact( + 'enviados', 'fallidos', 'optOuts', 'recibidos', + 'logsRecientes', 'planteles', 'promociones' + )); + } + + public function stats(Request $request) + { + $query = WhatsappLog::query(); + + if ($request->filled('plantel_id')) { + $plantelId = (int) $request->plantel_id; + $query->whereNotNull('alumno_id') + ->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) { + $q->where('plantels.id', $plantelId); + }); + } + + return response()->json([ + 'enviados' => (clone $query)->where('tipo', 'enviado')->count(), + 'recibidos' => (clone $query)->where('tipo', 'recibido')->count(), + 'fallidos' => (clone $query)->where('tipo', 'error')->count(), + 'opt_outs' => (clone $query)->where('tipo', 'opt_out')->count(), + ]); + } + + public function logs(Request $request) + { + $query = WhatsappLog::with('alumno.alumnos') + ->orderByDesc('created_at'); + + if ($request->filled('fecha_desde')) { + $query->where('created_at', '>=', $request->fecha_desde); + } + + if ($request->filled('fecha_hasta')) { + $query->where('created_at', '<=', $request->fecha_hasta . ' 23:59:59'); + } + + if ($request->filled('plantel_id')) { + $plantelId = (int) $request->plantel_id; + $query->whereNotNull('alumno_id') + ->whereHas('alumno.alumnos.plantelUsuarios', function ($q) use ($plantelId) { + $q->where('plantels.id', $plantelId); + }); + } + + if ($request->filled('tipo')) { + $query->where('tipo', $request->tipo); + } + + $logs = $query->paginate(50); + + return response()->json($logs); + } +} diff --git a/app/Http/Controllers/WhatsAppChatController.php b/app/Http/Controllers/WhatsAppChatController.php new file mode 100644 index 00000000..981f0961 --- /dev/null +++ b/app/Http/Controllers/WhatsAppChatController.php @@ -0,0 +1,127 @@ +plantelUsuarios->pluck('id'); + + // Prospectos del plantel que tienen historial de WhatsApp + $prospectos = Alumno::whereHas('alumnos.plantelUsuarios', function ($q) use ($plantelesIds) { + $q->whereIn('plantels.id', $plantelesIds); + }) + ->whereHas('whatsappLogs') + ->with(['alumnos' => fn($q) => $q->select('users.id', 'users.name', 'users.apellidoPaterno', 'users.telefono', 'users.profile_photo_path')]) + ->get() + ->map(function (Alumno $alumno) { + $ultimoMensaje = $alumno->whatsappLogs()->latest('created_at')->first(); + $noLeidos = $alumno->whatsappLogs()->where('leido', false)->where('tipo', 'recibido')->count(); + + return [ + 'alumno' => $alumno, + 'user' => $alumno->alumnos->first(), + 'ultimo' => $ultimoMensaje, + 'no_leidos' => $noLeidos, + ]; + }) + ->sortByDesc(fn($item) => optional($item['ultimo'])->created_at); + + $totalNoLeidos = WhatsappLog::whereIn('alumno_id', $prospectos->pluck('alumno.id')) + ->where('leido', false) + ->where('tipo', 'recibido') + ->count(); + + return view('promocion.chat', compact('prospectos', 'totalNoLeidos')); + } + + public function show(Alumno $alumno) + { + $this->autorizarAlumno($alumno); + + $logs = WhatsappLog::where('alumno_id', $alumno->id) + ->orderBy('created_at') + ->get(); + + // Marcar como leídos + WhatsappLog::where('alumno_id', $alumno->id) + ->where('leido', false) + ->update(['leido' => true]); + + $user = $alumno->alumnos()->first(); + + return response()->json([ + 'alumno' => [ + 'id' => $alumno->id, + 'nombre' => $user ? "{$user->name} {$user->apellidoPaterno}" : "Alumno #{$alumno->id}", + 'avatar' => $user?->profile_photo_url ?? null, + 'tel' => $alumno->telefonoWhatsapp(), + ], + 'logs' => $logs->map(fn($log) => [ + 'id' => $log->id, + 'tipo' => $log->tipo, + 'mensaje' => $log->mensaje, + 'media_type' => $log->media_type, + 'created_at' => $log->created_at?->format('d/m/Y H:i'), + ]), + ]); + } + + public function send(Request $request, WhatsAppService $whatsapp) + { + $request->validate([ + 'alumno_id' => 'required|exists:alumnos,id', + 'mensaje' => 'required|string|max:4096', + ]); + + $alumno = Alumno::findOrFail($request->alumno_id); + $this->autorizarAlumno($alumno); + + if ($alumno->opt_out) { + return response()->json(['error' => 'El prospecto solicitó no recibir mensajes (opt-out).'], 422); + } + + $telefono = $alumno->telefonoWhatsapp(); + + if (!$telefono) { + return response()->json(['error' => 'El prospecto no tiene teléfono registrado.'], 422); + } + + $mensajeId = $whatsapp->enviarTexto($telefono, $request->mensaje); + + $log = WhatsappLog::create([ + 'alumno_id' => $alumno->id, + 'telefono' => $telefono, + 'tipo' => 'enviado', + 'mensaje' => $request->mensaje, + 'payload' => ['enviado_por' => Auth::id(), 'whatsapp_id' => $mensajeId], + ]); + + return response()->json([ + 'id' => $log->id, + 'tipo' => $log->tipo, + 'mensaje' => $log->mensaje, + 'created_at' => $log->created_at->format('d/m/Y H:i'), + ]); + } + + private function autorizarAlumno(Alumno $alumno): void + { + $plantelesIds = Auth::user()->plantelUsuarios->pluck('id'); + + $tiene = $alumno->alumnos() + ->whereHas('plantelUsuarios', fn($q) => $q->whereIn('plantels.id', $plantelesIds)) + ->exists(); + + if (!$tiene) { + abort(403); + } + } +} diff --git a/app/Http/Controllers/WhatsAppWebhookController.php b/app/Http/Controllers/WhatsAppWebhookController.php new file mode 100644 index 00000000..81b4fc5b --- /dev/null +++ b/app/Http/Controllers/WhatsAppWebhookController.php @@ -0,0 +1,116 @@ +get('hub_mode') === 'subscribe' && + $request->get('hub_verify_token') === $verifyToken + ) { + return response($request->get('hub_challenge'), 200); + } + + return response('Forbidden', 403); + } + + public function handle(Request $request, WhatsAppService $whatsapp) + { + $payload = $request->all(); + + try { + $entry = $payload['entry'][0] ?? null; + $changes = $entry['changes'][0] ?? null; + $value = $changes['value'] ?? null; + $message = $value['messages'][0] ?? null; + + if (!$message) { + return response()->json(['status' => 'ok']); + } + + $telefono = $message['from'] ?? null; + $tipo = $message['type'] ?? 'text'; + $alumno = $this->alumnoPorTelefono($telefono); + $alumnoId = $alumno?->id; + + $logData = [ + 'alumno_id' => $alumnoId, + 'telefono' => $telefono, + 'tipo' => 'recibido', + 'payload' => $payload, + ]; + + if ($tipo === 'text') { + $texto = $message['text']['body'] ?? ''; + $logData['mensaje'] = $texto; + $logData['media_type'] = null; + + if ($this->esOptOut($texto)) { + $whatsapp->procesarOptOut($telefono); + $logData['tipo'] = 'opt_out'; + } + } elseif (in_array($tipo, ['image', 'video', 'audio', 'document'])) { + $mediaId = $message[$tipo]['id'] ?? null; + + if ($mediaId) { + $ruta = $whatsapp->descargarMedia($mediaId); + $logData['mensaje'] = $ruta; + $logData['media_type'] = $tipo; + } + } + + $log = WhatsappLog::create($logData); + + if ($alumnoId) { + broadcast(new NuevoMensajeWhatsApp($log)); + + // Marcar como no leído para el badge + WhatsappLog::where('id', $log->id)->update(['leido' => false]); + } + } catch (\Throwable $e) { + Log::error('WhatsApp webhook error', ['error' => $e->getMessage(), 'payload' => $payload]); + } + + return response()->json(['status' => 'ok']); + } + + private function alumnoPorTelefono(?string $telefono): ?Alumno + { + if (!$telefono) { + return null; + } + + $limpio = preg_replace('/\D/', '', $telefono); + $sinCodigo = strlen($limpio) === 12 ? substr($limpio, 2) : $limpio; + + return Alumno::whereHas('alumnos', function ($q) use ($limpio, $sinCodigo) { + $q->where('telefono', $limpio)->orWhere('telefono', $sinCodigo); + })->first(); + } + + private function esOptOut(string $texto): bool + { + $normalizado = strtolower(trim($texto)); + + foreach ($this->optOutKeywords as $keyword) { + if ($normalizado === $keyword || str_contains($normalizado, $keyword)) { + return true; + } + } + + return false; + } +} diff --git a/app/Jobs/EnviarPromocionJob.php b/app/Jobs/EnviarPromocionJob.php new file mode 100644 index 00000000..bf066379 --- /dev/null +++ b/app/Jobs/EnviarPromocionJob.php @@ -0,0 +1,84 @@ +alumno->refresh(); + + if ($this->alumno->opt_out) { + return; + } + + $telefono = $this->alumno->telefonoWhatsapp(); + + if (!$telefono) { + WhatsappLog::create([ + 'alumno_id' => $this->alumno->id, + 'telefono' => 'sin_telefono', + 'tipo' => 'error', + 'mensaje' => 'Alumno sin teléfono registrado', + 'payload' => ['promocion_id' => $this->promocion->id], + ]); + + $this->actualizarPivot('fallido', null); + return; + } + + try { + $mensajeId = $whatsapp->enviarPromocion($telefono, $this->promocion); + } catch (\RuntimeException $e) { + WhatsappLog::create([ + 'alumno_id' => $this->alumno->id, + 'telefono' => $telefono, + 'tipo' => 'error', + 'mensaje' => $e->getMessage(), + 'payload' => ['promocion_id' => $this->promocion->id], + ]); + + $this->actualizarPivot('fallido', null); + return; + } + + WhatsappLog::create([ + 'alumno_id' => $this->alumno->id, + 'telefono' => $telefono, + 'tipo' => 'enviado', + 'mensaje' => $this->promocion->titulo, + 'payload' => ['promocion_id' => $this->promocion->id, 'whatsapp_id' => $mensajeId], + ]); + + $this->actualizarPivot('enviado', $mensajeId); + } + + private function actualizarPivot(string $status, ?string $mensajeId): void + { + $this->promocion->alumnos()->updateExistingPivot($this->alumno->id, [ + 'status_envio' => $status, + 'enviado_at' => now(), + 'whatsapp_mensaje_id' => $mensajeId, + ]); + } +} diff --git a/app/Models/Alumno.php b/app/Models/Alumno.php index 2580910a..b5567a1b 100644 --- a/app/Models/Alumno.php +++ b/app/Models/Alumno.php @@ -5,11 +5,17 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; class Alumno extends Model { protected $guarded = []; + protected $casts = [ + 'opt_out' => 'boolean', + 'opt_out_at' => 'datetime', + ]; + public function existe($plantelId): bool { return $this->plantelUsuarios() @@ -52,4 +58,21 @@ class Alumno extends Model return $this->hasMany(Documento::class); } + public function promociones(): BelongsToMany + { + return $this->belongsToMany(Promocion::class, 'alumno_promocion') + ->withPivot(['enviado_at', 'status_envio', 'whatsapp_mensaje_id']); + } + + public function whatsappLogs(): HasMany + { + return $this->hasMany(WhatsappLog::class); + } + + public function telefonoWhatsapp(): ?string + { + $user = $this->alumnos()->first(); + return $user?->telefono; + } + } diff --git a/app/Models/Promocion.php b/app/Models/Promocion.php new file mode 100644 index 00000000..e13b13ad --- /dev/null +++ b/app/Models/Promocion.php @@ -0,0 +1,39 @@ + 'date', + 'fecha_fin' => 'date', + ]; + + public function planteles(): BelongsToMany + { + return $this->belongsToMany(Plantel::class, 'plantel_promocion'); + } + + public function alumnos(): BelongsToMany + { + return $this->belongsToMany(Alumno::class, 'alumno_promocion') + ->withPivot(['enviado_at', 'status_envio', 'whatsapp_mensaje_id']); + } + + public function isActiva(): bool + { + return $this->status === 'activo'; + } + + public function imagenUrl(): ?string + { + return $this->imagen ? asset('storage/' . $this->imagen) : null; + } +} diff --git a/app/Models/WhatsappLog.php b/app/Models/WhatsappLog.php new file mode 100644 index 00000000..fed5e9a2 --- /dev/null +++ b/app/Models/WhatsappLog.php @@ -0,0 +1,24 @@ + 'array', + 'leido' => 'boolean', + 'created_at' => 'datetime', + ]; + + public function alumno(): BelongsTo + { + return $this->belongsTo(Alumno::class); + } +} diff --git a/app/Services/WhatsAppService.php b/app/Services/WhatsAppService.php new file mode 100644 index 00000000..c19ba1b6 --- /dev/null +++ b/app/Services/WhatsAppService.php @@ -0,0 +1,189 @@ +token = $token; + $this->phoneId = $phoneId; + $this->apiUrl = "https://graph.facebook.com/v19.0/{$this->phoneId}/messages"; + } + + public function enviarTemplate(string $telefono, string $templateName, array $parametros = []): ?string + { + $components = []; + + if (!empty($parametros)) { + $components[] = [ + 'type' => 'body', + 'parameters' => array_map(fn($p) => ['type' => 'text', 'text' => $p], $parametros), + ]; + } + + $response = Http::withToken($this->token)->post($this->apiUrl, [ + 'messaging_product' => 'whatsapp', + 'to' => $this->normalizarTelefono($telefono), + 'type' => 'template', + 'template' => [ + 'name' => $templateName, + 'language' => ['code' => 'es_MX'], + 'components' => $components, + ], + ]); + + if ($response->failed()) { + Log::error('WhatsApp template error', ['telefono' => $telefono, 'response' => $response->json()]); + return null; + } + + return $response->json('messages.0.id'); + } + + public function enviarPromocion(string $telefono, Promocion $promocion): string + { + $payload = [ + 'messaging_product' => 'whatsapp', + 'to' => $this->normalizarTelefono($telefono), + ]; + + if ($promocion->imagen) { + $payload['type'] = 'image'; + $payload['image'] = [ + 'link' => asset('storage/' . $promocion->imagen), + 'caption' => "{$promocion->titulo}\n\n{$promocion->descripcion}", + ]; + } else { + $payload['type'] = 'text'; + $payload['text'] = ['body' => "{$promocion->titulo}\n\n{$promocion->descripcion}"]; + } + + $response = Http::withToken($this->token)->post($this->apiUrl, $payload); + + if ($response->failed()) { + Log::error('WhatsApp promocion error', [ + 'telefono' => $telefono, + 'status' => $response->status(), + 'body' => $response->body(), + ]); + + throw new \RuntimeException($response->body()); + } + + return $response->json('messages.0.id'); + } + + public function procesarOptOut(string $telefono): void + { + $normalizado = $this->normalizarTelefono($telefono); + + Alumno::whereHas('alumnos', function ($q) use ($normalizado) { + $q->where('telefono', $normalizado) + ->orWhere('telefono', ltrim($normalizado, '52')); + })->each(function (Alumno $alumno) { + $alumno->update([ + 'opt_out' => true, + 'opt_out_at' => now(), + ]); + }); + + WhatsappLog::create([ + 'telefono' => $normalizado, + 'tipo' => 'opt_out', + 'mensaje' => 'Usuario solicitó opt-out', + ]); + } + + public function descargarMedia(string $mediaId): ?string + { + $infoResponse = Http::withToken($this->token) + ->get("https://graph.facebook.com/v19.0/{$mediaId}"); + + if ($infoResponse->failed()) { + Log::error('WhatsApp media info error', ['mediaId' => $mediaId]); + return null; + } + + $mediaUrl = $infoResponse->json('url'); + $mimeType = $infoResponse->json('mime_type', 'application/octet-stream'); + $extension = $this->extensionFromMime($mimeType); + + $fileResponse = Http::withToken($this->token)->get($mediaUrl); + + if ($fileResponse->failed()) { + return null; + } + + $filename = 'whatsapp_media/' . $mediaId . '.' . $extension; + Storage::disk('public')->put($filename, $fileResponse->body()); + + return $filename; + } + + public function enviarTexto(string $telefono, string $texto): ?string + { + $response = Http::withToken($this->token)->post($this->apiUrl, [ + 'messaging_product' => 'whatsapp', + 'to' => $this->normalizarTelefono($telefono), + 'type' => 'text', + 'text' => ['body' => $texto], + ]); + + if ($response->failed()) { + return null; + } + + return $response->json('messages.0.id'); + } + + private function normalizarTelefono(string $telefono): string + { + $limpio = preg_replace('/\D/', '', $telefono); + + // Si ya tiene el prefijo 52 (12 dígitos), quítalo para re-agregarlo limpio + if (str_starts_with($limpio, '52') && strlen($limpio) === 12) { + $limpio = substr($limpio, 2); + } + + // Siempre anteponer el código de país México + return '52' . $limpio; + } + + private function extensionFromMime(string $mimeType): string + { + return match (true) { + str_contains($mimeType, 'jpeg') => 'jpg', + str_contains($mimeType, 'png') => 'png', + str_contains($mimeType, 'gif') => 'gif', + str_contains($mimeType, 'webp') => 'webp', + str_contains($mimeType, 'mp4') => 'mp4', + str_contains($mimeType, 'ogg') => 'ogg', + str_contains($mimeType, 'mpeg') => 'mp3', + str_contains($mimeType, 'pdf') => 'pdf', + str_contains($mimeType, 'msword') => 'doc', + str_contains($mimeType, 'spreadsheet')=> 'xlsx', + default => 'bin', + }; + } +} diff --git a/config/services.php b/config/services.php index 2fd4952f..6f24e95f 100644 --- a/config/services.php +++ b/config/services.php @@ -45,4 +45,11 @@ return [ 'key' => env('GOOGLE_MAPS_API_KEY'), ], + 'whatsapp' => [ + 'token' => env('WHATSAPP_TOKEN'), + 'phone_id' => env('WHATSAPP_PHONE_ID'), + 'business_id' => env('WHATSAPP_BUSINESS_ID'), + 'verify_token' => env('WHATSAPP_VERIFY_TOKEN'), + ], + ]; diff --git a/database/migrations/2026_05_20_000001_create_promociones_table.php b/database/migrations/2026_05_20_000001_create_promociones_table.php new file mode 100644 index 00000000..57e10698 --- /dev/null +++ b/database/migrations/2026_05_20_000001_create_promociones_table.php @@ -0,0 +1,28 @@ +id(); + $table->string('titulo'); + $table->text('descripcion')->nullable(); + $table->string('imagen')->nullable(); + $table->date('fecha_inicio'); + $table->date('fecha_fin'); + $table->enum('status', ['activo', 'inactivo'])->default('activo'); + $table->foreignId('plantel_id')->constrained('plantels')->cascadeOnDelete(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('promociones'); + } +}; diff --git a/database/migrations/2026_05_20_000002_create_alumno_promocion_table.php b/database/migrations/2026_05_20_000002_create_alumno_promocion_table.php new file mode 100644 index 00000000..ec10dce9 --- /dev/null +++ b/database/migrations/2026_05_20_000002_create_alumno_promocion_table.php @@ -0,0 +1,25 @@ +id(); + $table->foreignId('alumno_id')->constrained('alumnos')->cascadeOnDelete(); + $table->foreignId('promocion_id')->constrained('promociones')->cascadeOnDelete(); + $table->timestamp('enviado_at')->nullable(); + $table->enum('status_envio', ['pendiente', 'enviado', 'fallido'])->default('pendiente'); + $table->string('whatsapp_mensaje_id')->nullable(); + }); + } + + public function down(): void + { + Schema::dropIfExists('alumno_promocion'); + } +}; diff --git a/database/migrations/2026_05_20_000003_create_whatsapp_logs_table.php b/database/migrations/2026_05_20_000003_create_whatsapp_logs_table.php new file mode 100644 index 00000000..e67a084d --- /dev/null +++ b/database/migrations/2026_05_20_000003_create_whatsapp_logs_table.php @@ -0,0 +1,28 @@ +id(); + $table->foreignId('alumno_id')->nullable()->constrained('alumnos')->nullOnDelete(); + $table->string('telefono', 30); + $table->enum('tipo', ['enviado', 'recibido', 'opt_out', 'error']); + $table->text('mensaje')->nullable(); + $table->string('media_type')->nullable(); + $table->json('payload')->nullable(); + $table->boolean('leido')->default(false); + $table->timestamp('created_at')->useCurrent(); + }); + } + + public function down(): void + { + Schema::dropIfExists('whatsapp_logs'); + } +}; diff --git a/database/migrations/2026_05_20_000004_add_opt_out_to_alumnos_table.php b/database/migrations/2026_05_20_000004_add_opt_out_to_alumnos_table.php new file mode 100644 index 00000000..671b3e5c --- /dev/null +++ b/database/migrations/2026_05_20_000004_add_opt_out_to_alumnos_table.php @@ -0,0 +1,23 @@ +boolean('opt_out')->default(false)->after('status'); + $table->timestamp('opt_out_at')->nullable()->after('opt_out'); + }); + } + + public function down(): void + { + Schema::table('alumnos', function (Blueprint $table) { + $table->dropColumn(['opt_out', 'opt_out_at']); + }); + } +}; diff --git a/database/migrations/2026_05_20_000005_refactor_promocion_plantel_to_pivot.php b/database/migrations/2026_05_20_000005_refactor_promocion_plantel_to_pivot.php new file mode 100644 index 00000000..fdb4d666 --- /dev/null +++ b/database/migrations/2026_05_20_000005_refactor_promocion_plantel_to_pivot.php @@ -0,0 +1,34 @@ +dropForeign(['plantel_id']); + $table->dropColumn('plantel_id'); + }); + + Schema::create('plantel_promocion', function (Blueprint $table) { + $table->unsignedBigInteger('plantel_id'); + $table->foreign('plantel_id')->references('id')->on('plantels')->cascadeOnDelete(); + $table->unsignedBigInteger('promocion_id'); + $table->foreign('promocion_id')->references('id')->on('promociones')->cascadeOnDelete(); + $table->primary(['plantel_id', 'promocion_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('plantel_promocion'); + + Schema::table('promociones', function (Blueprint $table) { + $table->foreignId('plantel_id')->constrained('plantels')->cascadeOnDelete(); + }); + } +}; diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 67c7ef31..a89e9c7b 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -167,5 +167,13 @@ class RoleSeeder extends Seeder Permission::create(['name'=>'comunidad','description'=>'Comunidad'])->syncRoles([$role1]); Permission::create(['name'=>'amigos','description'=>'Amistades'])->syncRoles([$role1]); Permission::create(['name'=>'mensajes','description'=>'Mensajería'])->syncRoles([$role1]); + + Permission::create(['name'=>'promocion.index','description'=>'promocion index'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.create','description'=>'promocion crear'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.edit','description'=>'promocion editar'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.destroy','description'=>'promocion destuir'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.enviar','description'=>'promocion enviar'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.chat','description'=>'promocion chat'])->syncRoles([$role1]); + Permission::create(['name'=>'promocion.analytics','description'=>'promocion analytics'])->syncRoles([$role1]); } } diff --git a/php-fpm-pool.conf b/php-fpm-pool.conf index 20d1ef6a..d2326dca 100644 --- a/php-fpm-pool.conf +++ b/php-fpm-pool.conf @@ -2,11 +2,11 @@ user = nobody group = nobody listen = /var/run/php-fpm.sock -listen.owner = nobody -listen.group = nobody -listen.mode = 0660 pm = dynamic pm.max_children = 10 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 +listen.owner = nginx +listen.group = nginx +listen.mode = 0660 diff --git a/public/build/assets/app-DAN06vjJ.js b/public/build/assets/app-DUTi2uSh.js similarity index 99% rename from public/build/assets/app-DAN06vjJ.js rename to public/build/assets/app-DUTi2uSh.js index 149f3faf..8fee47bc 100644 --- a/public/build/assets/app-DAN06vjJ.js +++ b/public/build/assets/app-DUTi2uSh.js @@ -4,4 +4,4 @@ function bn(s,t){return function(){return s.apply(t,arguments)}}const{toString:V `+l.map(hn).join(` `):" "+hn(l[0]):"as no adapter specified";throw new A("There is no suitable adapter to dispatch the request "+p,"ERR_NOT_SUPPORT")}return c}const Dn={getAdapter:vo,adapters:St};function dt(s){if(s.cancelToken&&s.cancelToken.throwIfRequested(),s.signal&&s.signal.aborted)throw new ye(null,s)}function dn(s){return dt(s),s.headers=K.from(s.headers),s.data=ht.call(s,s.transformRequest),["post","put","patch"].indexOf(s.method)!==-1&&s.headers.setContentType("application/x-www-form-urlencoded",!1),Dn.getAdapter(s.adapter||Oe.adapter,s)(s).then(function(o){return dt(s),o.data=ht.call(s,s.transformResponse,o),o.headers=K.from(o.headers),o},function(o){return Ln(o)||(dt(s),o&&o.response&&(o.response.data=ht.call(s,s.transformResponse,o.response),o.response.headers=K.from(o.response.headers))),Promise.reject(o)})}const Fn="1.13.2",Ke={};["object","boolean","number","function","string","symbol"].forEach((s,t)=>{Ke[s]=function(o){return typeof o===s||"a"+(t<1?"n ":" ")+s}});const fn={};Ke.transitional=function(t,i,o){function c(h,l){return"[Axios v"+Fn+"] Transitional option '"+h+"'"+l+(o?". "+o:"")}return(h,l,p)=>{if(t===!1)throw new A(c(l," has been removed"+(i?" in "+i:"")),A.ERR_DEPRECATED);return i&&!fn[l]&&(fn[l]=!0,console.warn(c(l," has been deprecated since v"+i+" and will be removed in the near future"))),t?t(h,l,p):!0}};Ke.spelling=function(t){return(i,o)=>(console.warn(`${o} is likely a misspelling of ${t}`),!0)};function wo(s,t,i){if(typeof s!="object")throw new A("options must be an object",A.ERR_BAD_OPTION_VALUE);const o=Object.keys(s);let c=o.length;for(;c-- >0;){const h=o[c],l=t[h];if(l){const p=s[h],y=p===void 0||l(p,h,s);if(y!==!0)throw new A("option "+h+" must be "+y,A.ERR_BAD_OPTION_VALUE);continue}if(i!==!0)throw new A("Unknown option "+h,A.ERR_BAD_OPTION)}}const Me={assertOptions:wo,validators:Ke},re=Me.validators;let de=class{constructor(t){this.defaults=t||{},this.interceptors={request:new en,response:new en}}async request(t,i){try{return await this._request(t,i)}catch(o){if(o instanceof Error){let c={};Error.captureStackTrace?Error.captureStackTrace(c):c=new Error;const h=c.stack?c.stack.replace(/^.+\n/,""):"";try{o.stack?h&&!String(o.stack).endsWith(h.replace(/^.+\n.+\n/,""))&&(o.stack+=` `+h):o.stack=h}catch{}}throw o}}_request(t,i){typeof t=="string"?(i=i||{},i.url=t):i=t||{},i=fe(this.defaults,i);const{transitional:o,paramsSerializer:c,headers:h}=i;o!==void 0&&Me.assertOptions(o,{silentJSONParsing:re.transitional(re.boolean),forcedJSONParsing:re.transitional(re.boolean),clarifyTimeoutError:re.transitional(re.boolean)},!1),c!=null&&(d.isFunction(c)?i.paramsSerializer={serialize:c}:Me.assertOptions(c,{encode:re.function,serialize:re.function},!0)),i.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?i.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:i.allowAbsoluteUrls=!0),Me.assertOptions(i,{baseUrl:re.spelling("baseURL"),withXsrfToken:re.spelling("withXSRFToken")},!0),i.method=(i.method||this.defaults.method||"get").toLowerCase();let l=h&&d.merge(h.common,h[i.method]);h&&d.forEach(["delete","get","head","post","put","patch","common"],b=>{delete h[b]}),i.headers=K.concat(l,h);const p=[];let y=!0;this.interceptors.request.forEach(function(v){typeof v.runWhen=="function"&&v.runWhen(i)===!1||(y=y&&v.synchronous,p.unshift(v.fulfilled,v.rejected))});const w=[];this.interceptors.response.forEach(function(v){w.push(v.fulfilled,v.rejected)});let g,C=0,T;if(!y){const b=[dn.bind(this),void 0];for(b.unshift(...p),b.push(...w),T=b.length,g=Promise.resolve(i);C{if(!o._listeners)return;let h=o._listeners.length;for(;h-- >0;)o._listeners[h](c);o._listeners=null}),this.promise.then=c=>{let h;const l=new Promise(p=>{o.subscribe(p),h=p}).then(c);return l.cancel=function(){o.unsubscribe(h)},l},t(function(h,l,p){o.reason||(o.reason=new ye(h,l,p),i(o.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const i=this._listeners.indexOf(t);i!==-1&&this._listeners.splice(i,1)}toAbortSignal(){const t=new AbortController,i=o=>{t.abort(o)};return this.subscribe(i),t.signal.unsubscribe=()=>this.unsubscribe(i),t.signal}static source(){let t;return{token:new qn(function(c){t=c}),cancel:t}}};function _o(s){return function(i){return s.apply(null,i)}}function Co(s){return d.isObject(s)&&s.isAxiosError===!0}const bt={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(bt).forEach(([s,t])=>{bt[t]=s});function Bn(s){const t=new de(s),i=bn(de.prototype.request,t);return d.extend(i,de.prototype,t,{allOwnKeys:!0}),d.extend(i,t,null,{allOwnKeys:!0}),i.create=function(c){return Bn(fe(s,c))},i}const F=Bn(Oe);F.Axios=de;F.CanceledError=ye;F.CancelToken=So;F.isCancel=Ln;F.VERSION=Fn;F.toFormData=Ve;F.AxiosError=A;F.Cancel=F.CanceledError;F.all=function(t){return Promise.all(t)};F.spread=_o;F.isAxiosError=Co;F.mergeConfig=fe;F.AxiosHeaders=K;F.formToJSON=s=>An(d.isHTMLForm(s)?new FormData(s):s);F.getAdapter=Dn.getAdapter;F.HttpStatusCode=bt;F.default=F;const{Axios:Bo,AxiosError:Ho,CanceledError:Mo,isCancel:zo,CancelToken:$o,VERSION:Jo,all:Xo,Cancel:Wo,isAxiosError:Vo,spread:Ko,toFormData:Go,AxiosHeaders:Qo,HttpStatusCode:Yo,formToJSON:Zo,getAdapter:ea,mergeConfig:ta}=F;class _t{constructor(){this.notificationCreatedEvent=".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"}listenForWhisper(t,i){return this.listen(".client-"+t,i)}notification(t){return this.listen(this.notificationCreatedEvent,t)}stopListeningForNotification(t){return this.stopListening(this.notificationCreatedEvent,t)}stopListeningForWhisper(t,i){return this.stopListening(".client-"+t,i)}}class Hn{constructor(t){this.namespace=t}format(t){return[".","\\"].includes(t.charAt(0))?t.substring(1):(this.namespace&&(t=this.namespace+"."+t),t.replace(/\./g,"\\"))}setNamespace(t){this.namespace=t}}function To(s){try{new s}catch(t){if(t instanceof Error&&t.message.includes("is not a constructor"))return!1}return!0}class Ct extends _t{constructor(t,i,o){super(),this.name=i,this.pusher=t,this.options=o,this.eventFormatter=new Hn(this.options.namespace),this.subscribe()}subscribe(){this.subscription=this.pusher.subscribe(this.name)}unsubscribe(){this.pusher.unsubscribe(this.name)}listen(t,i){return this.on(this.eventFormatter.format(t),i),this}listenToAll(t){return this.subscription.bind_global((i,o)=>{if(i.startsWith("pusher:"))return;let c=String(this.options.namespace??"").replace(/\./g,"\\"),h=i.startsWith(c)?i.substring(c.length+1):"."+i;t(h,o)}),this}stopListening(t,i){return i?this.subscription.unbind(this.eventFormatter.format(t),i):this.subscription.unbind(this.eventFormatter.format(t)),this}stopListeningToAll(t){return t?this.subscription.unbind_global(t):this.subscription.unbind_global(),this}subscribed(t){return this.on("pusher:subscription_succeeded",()=>{t()}),this}error(t){return this.on("pusher:subscription_error",i=>{t(i)}),this}on(t,i){return this.subscription.bind(t,i),this}}class Mn extends Ct{whisper(t,i){return this.pusher.channels.channels[this.name].trigger(`client-${t}`,i),this}}class ko extends Ct{whisper(t,i){return this.pusher.channels.channels[this.name].trigger(`client-${t}`,i),this}}class Eo extends Mn{here(t){return this.on("pusher:subscription_succeeded",i=>{t(Object.keys(i.members).map(o=>i.members[o]))}),this}joining(t){return this.on("pusher:member_added",i=>{t(i.info)}),this}whisper(t,i){return this.pusher.channels.channels[this.name].trigger(`client-${t}`,i),this}leaving(t){return this.on("pusher:member_removed",i=>{t(i.info)}),this}}class zn extends _t{constructor(t,i,o){super(),this.events={},this.listeners={},this.name=i,this.socket=t,this.options=o,this.eventFormatter=new Hn(this.options.namespace),this.subscribe()}subscribe(){this.socket.emit("subscribe",{channel:this.name,auth:this.options.auth||{}})}unsubscribe(){this.unbind(),this.socket.emit("unsubscribe",{channel:this.name,auth:this.options.auth||{}})}listen(t,i){return this.on(this.eventFormatter.format(t),i),this}stopListening(t,i){return this.unbindEvent(this.eventFormatter.format(t),i),this}subscribed(t){return this.on("connect",i=>{t(i)}),this}error(t){return this}on(t,i){return this.listeners[t]=this.listeners[t]||[],this.events[t]||(this.events[t]=(o,c)=>{this.name===o&&this.listeners[t]&&this.listeners[t].forEach(h=>h(c))},this.socket.on(t,this.events[t])),this.listeners[t].push(i),this}unbind(){Object.keys(this.events).forEach(t=>{this.unbindEvent(t)})}unbindEvent(t,i){this.listeners[t]=this.listeners[t]||[],i&&(this.listeners[t]=this.listeners[t].filter(o=>o!==i)),(!i||this.listeners[t].length===0)&&(this.events[t]&&(this.socket.removeListener(t,this.events[t]),delete this.events[t]),delete this.listeners[t])}}class $n extends zn{whisper(t,i){return this.socket.emit("client event",{channel:this.name,event:`client-${t}`,data:i}),this}}class xo extends $n{here(t){return this.on("presence:subscribed",i=>{t(i.map(o=>o.user_info))}),this}joining(t){return this.on("presence:joining",i=>t(i.user_info)),this}whisper(t,i){return this.socket.emit("client event",{channel:this.name,event:`client-${t}`,data:i}),this}leaving(t){return this.on("presence:leaving",i=>t(i.user_info)),this}}class $e extends _t{subscribe(){}unsubscribe(){}listen(t,i){return this}listenToAll(t){return this}stopListening(t,i){return this}subscribed(t){return this}error(t){return this}on(t,i){return this}}class Jn extends $e{whisper(t,i){return this}}class Ro extends $e{whisper(t,i){return this}}class Oo extends Jn{here(t){return this}joining(t){return this}whisper(t,i){return this}leaving(t){return this}}const Xn=class Wn{constructor(t){this.setOptions(t),this.connect()}setOptions(t){this.options={...Wn._defaultOptions,...t,broadcaster:t.broadcaster};let i=this.csrfToken();i&&(this.options.auth.headers["X-CSRF-TOKEN"]=i,this.options.userAuthentication.headers["X-CSRF-TOKEN"]=i),i=this.options.bearerToken,i&&(this.options.auth.headers.Authorization="Bearer "+i,this.options.userAuthentication.headers.Authorization="Bearer "+i)}csrfToken(){var t,i;return typeof window<"u"&&(t=window.Laravel)!=null&&t.csrfToken?window.Laravel.csrfToken:this.options.csrfToken?this.options.csrfToken:typeof document<"u"&&typeof document.querySelector=="function"?((i=document.querySelector('meta[name="csrf-token"]'))==null?void 0:i.getAttribute("content"))??null:null}};Xn._defaultOptions={auth:{headers:{}},authEndpoint:"/broadcasting/auth",userAuthentication:{endpoint:"/broadcasting/user-auth",headers:{}},csrfToken:null,bearerToken:null,host:null,key:null,namespace:"App.Events"};let Tt=Xn;class qe extends Tt{constructor(){super(...arguments),this.channels={}}connect(){if(typeof this.options.client<"u")this.pusher=this.options.client;else if(this.options.Pusher)this.pusher=new this.options.Pusher(this.options.key,this.options);else if(typeof window<"u"&&typeof window.Pusher<"u")this.pusher=new window.Pusher(this.options.key,this.options);else throw new Error("Pusher client not found. Should be globally available or passed via options.client")}signin(){this.pusher.signin()}listen(t,i,o){return this.channel(t).listen(i,o)}channel(t){return this.channels[t]||(this.channels[t]=new Ct(this.pusher,t,this.options)),this.channels[t]}privateChannel(t){return this.channels["private-"+t]||(this.channels["private-"+t]=new Mn(this.pusher,"private-"+t,this.options)),this.channels["private-"+t]}encryptedPrivateChannel(t){return this.channels["private-encrypted-"+t]||(this.channels["private-encrypted-"+t]=new ko(this.pusher,"private-encrypted-"+t,this.options)),this.channels["private-encrypted-"+t]}presenceChannel(t){return this.channels["presence-"+t]||(this.channels["presence-"+t]=new Eo(this.pusher,"presence-"+t,this.options)),this.channels["presence-"+t]}leave(t){[t,"private-"+t,"private-encrypted-"+t,"presence-"+t].forEach(i=>{this.leaveChannel(i)})}leaveChannel(t){this.channels[t]&&(this.channels[t].unsubscribe(),delete this.channels[t])}socketId(){return this.pusher.connection.socket_id}connectionStatus(){const t=this.pusher.connection.state;switch(t){case"connected":case"connecting":return t;case"failed":case"unavailable":return"failed";default:return"disconnected"}}onConnectionChange(t){const i=()=>{t(this.connectionStatus())},o=["state_change","connected","disconnected"];return o.forEach(c=>{this.pusher.connection.bind(c,i)}),()=>{o.forEach(c=>{this.pusher.connection.unbind(c,i)})}}disconnect(){this.pusher.disconnect()}}class Po extends Tt{constructor(){super(...arguments),this.channels={}}connect(){let t=this.getSocketIO();this.socket=t(this.options.host??void 0,this.options),this.socket.io.on("reconnect",()=>{Object.values(this.channels).forEach(i=>{i.subscribe()})})}getSocketIO(){if(typeof this.options.client<"u")return this.options.client;if(typeof window<"u"&&typeof window.io<"u")return window.io;throw new Error("Socket.io client not found. Should be globally available or passed via options.client")}listen(t,i,o){return this.channel(t).listen(i,o)}channel(t){return this.channels[t]||(this.channels[t]=new zn(this.socket,t,this.options)),this.channels[t]}privateChannel(t){return this.channels["private-"+t]||(this.channels["private-"+t]=new $n(this.socket,"private-"+t,this.options)),this.channels["private-"+t]}presenceChannel(t){return this.channels["presence-"+t]||(this.channels["presence-"+t]=new xo(this.socket,"presence-"+t,this.options)),this.channels["presence-"+t]}leave(t){[t,"private-"+t,"presence-"+t].forEach(i=>{this.leaveChannel(i)})}leaveChannel(t){this.channels[t]&&(this.channels[t].unsubscribe(),delete this.channels[t])}socketId(){return this.socket.id}connectionStatus(){return this.socket.connected?"connected":this.socket.io._reconnecting?"reconnecting":this.socket.id!==void 0?"disconnected":"connecting"}onConnectionChange(t){const i=()=>{t(this.connectionStatus())},o=["connect","disconnect","connect_error","reconnect_attempt","reconnect","reconnect_error","reconnect_failed"];return o.forEach(c=>{this.socket.on(c,i)}),()=>{o.forEach(c=>{this.socket.off(c,i)})}}disconnect(){this.socket.disconnect()}}class pn extends Tt{constructor(){super(...arguments),this.channels={}}connect(){}listen(t,i,o){return new $e}channel(t){return new $e}privateChannel(t){return new Jn}encryptedPrivateChannel(t){return new Ro}presenceChannel(t){return new Oo}leave(t){}leaveChannel(t){}socketId(){return"fake-socket-id"}connectionStatus(){return"connected"}onConnectionChange(t){return()=>{}}disconnect(){}}class Ao{constructor(t){this.options=t,this.connect(),this.options.withoutInterceptors||this.registerInterceptors()}channel(t){return this.connector.channel(t)}connect(){if(this.options.broadcaster==="reverb")this.connector=new qe({...this.options,cluster:""});else if(this.options.broadcaster==="pusher")this.connector=new qe(this.options);else if(this.options.broadcaster==="ably")this.connector=new qe({...this.options,cluster:"",broadcaster:"pusher"});else if(this.options.broadcaster==="socket.io")this.connector=new Po(this.options);else if(this.options.broadcaster==="null")this.connector=new pn(this.options);else if(typeof this.options.broadcaster=="function"&&To(this.options.broadcaster))this.connector=new this.options.broadcaster(this.options);else throw new Error(`Broadcaster ${typeof this.options.broadcaster} ${String(this.options.broadcaster)} is not supported.`)}disconnect(){this.connector.disconnect()}join(t){return this.connector.presenceChannel(t)}leave(t){this.connector.leave(t)}leaveChannel(t){this.connector.leaveChannel(t)}leaveAllChannels(){for(const t in this.connector.channels)this.leaveChannel(t)}listen(t,i,o){return this.connector.listen(t,i,o)}private(t){return this.connector.privateChannel(t)}encryptedPrivate(t){if(this.connectorSupportsEncryptedPrivateChannels(this.connector))return this.connector.encryptedPrivateChannel(t);throw new Error(`Broadcaster ${typeof this.options.broadcaster} ${String(this.options.broadcaster)} does not support encrypted private channels.`)}connectorSupportsEncryptedPrivateChannels(t){return t instanceof qe||t instanceof pn}socketId(){return this.connector.socketId()}connectionStatus(){return this.connector.connectionStatus()}registerInterceptors(){typeof Vue<"u"&&Vue!=null&&Vue.http&&this.registerVueRequestInterceptor(),typeof axios=="function"&&this.registerAxiosRequestInterceptor(),typeof jQuery=="function"&&this.registerjQueryAjaxSetup(),typeof Turbo=="object"&&this.registerTurboRequestInterceptor()}registerVueRequestInterceptor(){Vue.http.interceptors.push((t,i)=>{this.socketId()&&t.headers.set("X-Socket-ID",this.socketId()),i()})}registerAxiosRequestInterceptor(){axios.interceptors.request.use(t=>(this.socketId()&&(t.headers["X-Socket-Id"]=this.socketId()),t))}registerjQueryAjaxSetup(){typeof jQuery.ajax<"u"&&jQuery.ajaxPrefilter((t,i,o)=>{this.socketId()&&o.setRequestHeader("X-Socket-Id",this.socketId())})}registerTurboRequestInterceptor(){document.addEventListener("turbo:before-fetch-request",t=>{t.detail.fetchOptions.headers["X-Socket-Id"]=this.socketId()})}}function Lo(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}var ft={exports:{}};var mn;function No(){return mn||(mn=1,(function(s,t){(function(o,c){s.exports=c()})(window,function(){return(function(i){var o={};function c(h){if(o[h])return o[h].exports;var l=o[h]={i:h,l:!1,exports:{}};return i[h].call(l.exports,l,l.exports,c),l.l=!0,l.exports}return c.m=i,c.c=o,c.d=function(h,l,p){c.o(h,l)||Object.defineProperty(h,l,{enumerable:!0,get:p})},c.r=function(h){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(h,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(h,"__esModule",{value:!0})},c.t=function(h,l){if(l&1&&(h=c(h)),l&8||l&4&&typeof h=="object"&&h&&h.__esModule)return h;var p=Object.create(null);if(c.r(p),Object.defineProperty(p,"default",{enumerable:!0,value:h}),l&2&&typeof h!="string")for(var y in h)c.d(p,y,(function(w){return h[w]}).bind(null,y));return p},c.n=function(h){var l=h&&h.__esModule?function(){return h.default}:function(){return h};return c.d(l,"a",l),l},c.o=function(h,l){return Object.prototype.hasOwnProperty.call(h,l)},c.p="",c(c.s=2)})([(function(i,o,c){var h=this&&this.__extends||(function(){var v=function(f,S){return v=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(E,R){E.__proto__=R}||function(E,R){for(var N in R)R.hasOwnProperty(N)&&(E[N]=R[N])},v(f,S)};return function(f,S){v(f,S);function E(){this.constructor=f}f.prototype=S===null?Object.create(S):(E.prototype=S.prototype,new E)}})();Object.defineProperty(o,"__esModule",{value:!0});var l=256,p=(function(){function v(f){f===void 0&&(f="="),this._paddingCharacter=f}return v.prototype.encodedLength=function(f){return this._paddingCharacter?(f+2)/3*4|0:(f*8+5)/6|0},v.prototype.encode=function(f){for(var S="",E=0;E>>18&63),S+=this._encodeByte(R>>>12&63),S+=this._encodeByte(R>>>6&63),S+=this._encodeByte(R>>>0&63)}var N=f.length-E;if(N>0){var R=f[E]<<16|(N===2?f[E+1]<<8:0);S+=this._encodeByte(R>>>18&63),S+=this._encodeByte(R>>>12&63),N===2?S+=this._encodeByte(R>>>6&63):S+=this._paddingCharacter||"",S+=this._paddingCharacter||""}return S},v.prototype.maxDecodedLength=function(f){return this._paddingCharacter?f/4*3|0:(f*6+7)/8|0},v.prototype.decodedLength=function(f){return this.maxDecodedLength(f.length-this._getPaddingLength(f))},v.prototype.decode=function(f){if(f.length===0)return new Uint8Array(0);for(var S=this._getPaddingLength(f),E=f.length-S,R=new Uint8Array(this.maxDecodedLength(E)),N=0,j=0,B=0,D=0,z=0,J=0,Q=0;j>>4,R[N++]=z<<4|J>>>2,R[N++]=J<<6|Q,B|=D&l,B|=z&l,B|=J&l,B|=Q&l;if(j>>4,B|=D&l,B|=z&l),j>>2,B|=J&l),j>>8&6,S+=51-f>>>8&-75,S+=61-f>>>8&-15,S+=62-f>>>8&3,String.fromCharCode(S)},v.prototype._decodeChar=function(f){var S=l;return S+=(42-f&f-44)>>>8&-l+f-43+62,S+=(46-f&f-48)>>>8&-l+f-47+63,S+=(47-f&f-58)>>>8&-l+f-48+52,S+=(64-f&f-91)>>>8&-l+f-65+0,S+=(96-f&f-123)>>>8&-l+f-97+26,S},v.prototype._getPaddingLength=function(f){var S=0;if(this._paddingCharacter){for(var E=f.length-1;E>=0&&f[E]===this._paddingCharacter;E--)S++;if(f.length<4||S>2)throw new Error("Base64Coder: incorrect padding")}return S},v})();o.Coder=p;var y=new p;function w(v){return y.encode(v)}o.encode=w;function g(v){return y.decode(v)}o.decode=g;var C=(function(v){h(f,v);function f(){return v!==null&&v.apply(this,arguments)||this}return f.prototype._encodeByte=function(S){var E=S;return E+=65,E+=25-S>>>8&6,E+=51-S>>>8&-75,E+=61-S>>>8&-13,E+=62-S>>>8&49,String.fromCharCode(E)},f.prototype._decodeChar=function(S){var E=l;return E+=(44-S&S-46)>>>8&-l+S-45+62,E+=(94-S&S-96)>>>8&-l+S-95+63,E+=(47-S&S-58)>>>8&-l+S-48+52,E+=(64-S&S-91)>>>8&-l+S-65+0,E+=(96-S&S-123)>>>8&-l+S-97+26,E},f})(p);o.URLSafeCoder=C;var T=new C;function x(v){return T.encode(v)}o.encodeURLSafe=x;function b(v){return T.decode(v)}o.decodeURLSafe=b,o.encodedLength=function(v){return y.encodedLength(v)},o.maxDecodedLength=function(v){return y.maxDecodedLength(v)},o.decodedLength=function(v){return y.decodedLength(v)}}),(function(i,o,c){Object.defineProperty(o,"__esModule",{value:!0});var h="utf8: invalid string",l="utf8: invalid source encoding";function p(g){for(var C=new Uint8Array(y(g)),T=0,x=0;x>6,C[T++]=128|b&63):b<55296?(C[T++]=224|b>>12,C[T++]=128|b>>6&63,C[T++]=128|b&63):(x++,b=(b&1023)<<10,b|=g.charCodeAt(x)&1023,b+=65536,C[T++]=240|b>>18,C[T++]=128|b>>12&63,C[T++]=128|b>>6&63,C[T++]=128|b&63)}return C}o.encode=p;function y(g){for(var C=0,T=0;T=g.length-1)throw new Error(h);T++,C+=4}else throw new Error(h)}return C}o.encodedLength=y;function w(g){for(var C=[],T=0;T=g.length)throw new Error(l);var v=g[++T];if((v&192)!==128)throw new Error(l);x=(x&31)<<6|v&63,b=128}else if(x<240){if(T>=g.length-1)throw new Error(l);var v=g[++T],f=g[++T];if((v&192)!==128||(f&192)!==128)throw new Error(l);x=(x&15)<<12|(v&63)<<6|f&63,b=2048}else if(x<248){if(T>=g.length-2)throw new Error(l);var v=g[++T],f=g[++T],S=g[++T];if((v&192)!==128||(f&192)!==128||(S&192)!==128)throw new Error(l);x=(x&15)<<18|(v&63)<<12|(f&63)<<6|S&63,b=65536}else throw new Error(l);if(x=55296&&x<=57343)throw new Error(l);if(x>=65536){if(x>1114111)throw new Error(l);x-=65536,C.push(String.fromCharCode(55296|x>>10)),x=56320|x&1023}}C.push(String.fromCharCode(x))}return C.join("")}o.decode=w}),(function(i,o,c){i.exports=c(3).default}),(function(i,o,c){c.r(o);class h{constructor(e,n){this.lastId=0,this.prefix=e,this.name=n}create(e){this.lastId++;var n=this.lastId,a=this.prefix+n,u=this.name+"["+n+"]",m=!1,_=function(){m||(e.apply(null,arguments),m=!0)};return this[n]=_,{number:n,id:a,name:u,callback:_}}remove(e){delete this[e.number]}}var l=new h("_pusher_script_","Pusher.ScriptReceivers"),p={VERSION:"8.4.0",PROTOCOL:7,wsPort:80,wssPort:443,wsPath:"",httpHost:"sockjs.pusher.com",httpPort:80,httpsPort:443,httpPath:"/pusher",stats_host:"stats.pusher.com",authEndpoint:"/pusher/auth",authTransport:"ajax",activityTimeout:12e4,pongTimeout:3e4,unavailableTimeout:1e4,userAuthentication:{endpoint:"/pusher/user-auth",transport:"ajax"},channelAuthorization:{endpoint:"/pusher/auth",transport:"ajax"},cdn_http:"http://js.pusher.com",cdn_https:"https://js.pusher.com",dependency_suffix:""},y=p;class w{constructor(e){this.options=e,this.receivers=e.receivers||l,this.loading={}}load(e,n,a){var u=this;if(u.loading[e]&&u.loading[e].length>0)u.loading[e].push(a);else{u.loading[e]=[a];var m=P.createScriptRequest(u.getPath(e,n)),_=u.receivers.create(function(k){if(u.receivers.remove(_),u.loading[e]){var O=u.loading[e];delete u.loading[e];for(var L=function(q){q||m.cleanup()},I=0;I>>6)+Z(128|e&63):Z(224|e>>>12&15)+Z(128|e>>>6&63)+Z(128|e&63)},Ae=function(r){return r.replace(/[^\x00-\x7F]/g,se)},G=function(r){var e=[0,2,1][r.length%3],n=r.charCodeAt(0)<<16|(r.length>1?r.charCodeAt(1):0)<<8|(r.length>2?r.charCodeAt(2):0),a=[ee.charAt(n>>>18),ee.charAt(n>>>12&63),e>=2?"=":ee.charAt(n>>>6&63),e>=1?"=":ee.charAt(n&63)];return a.join("")},Le=window.btoa||function(r){return r.replace(/[\s\S]{1,3}/g,G)};class te{constructor(e,n,a,u){this.clear=n,this.timer=e(()=>{this.timer&&(this.timer=u(this.timer))},a)}isRunning(){return this.timer!==null}ensureAborted(){this.timer&&(this.clear(this.timer),this.timer=null)}}var ve=te;function Ge(r){window.clearTimeout(r)}function ne(r){window.clearInterval(r)}class X extends ve{constructor(e,n){super(setTimeout,Ge,e,function(a){return n(),null})}}class we extends ve{constructor(e,n){super(setInterval,ne,e,function(a){return n(),a})}}var pe={now(){return Date.now?Date.now():new Date().valueOf()},defer(r){return new X(0,r)},method(r,...e){var n=Array.prototype.slice.call(arguments,1);return function(a){return a[r].apply(a,n.concat(arguments))}}},H=pe;function W(r,...e){for(var n=0;n{window.console&&window.console.log&&window.console.log(e)}}debug(...e){this.log(this.globalLog,e)}warn(...e){this.log(this.globalLogWarn,e)}error(...e){this.log(this.globalLogError,e)}globalLogWarn(e){window.console&&window.console.warn?window.console.warn(e):this.globalLog(e)}globalLogError(e){window.console&&window.console.error?window.console.error(e):this.globalLogWarn(e)}log(e,...n){var a=Vn.apply(this,arguments);at.log?at.log(a):at.logToConsole&&e.bind(this)(a)}}var U=new nr,rr=function(r,e,n,a,u){(n.headers!==void 0||n.headersProvider!=null)&&U.warn(`To send headers with the ${a.toString()} request, you must use AJAX, rather than JSONP.`);var m=r.nextAuthCallbackID.toString();r.nextAuthCallbackID++;var _=r.getDocument(),k=_.createElement("script");r.auth_callbacks[m]=function(I){u(null,I)};var O="Pusher.auth_callbacks['"+m+"']";k.src=n.endpoint+"?callback="+encodeURIComponent(O)+"&"+e;var L=_.getElementsByTagName("head")[0]||_.documentElement;L.insertBefore(k,L.firstChild)},sr=rr;class ir{constructor(e){this.src=e}send(e){var n=this,a="Error loading "+n.src;n.script=document.createElement("script"),n.script.id=e.id,n.script.src=n.src,n.script.type="text/javascript",n.script.charset="UTF-8",n.script.addEventListener?(n.script.onerror=function(){e.callback(a)},n.script.onload=function(){e.callback(null)}):n.script.onreadystatechange=function(){(n.script.readyState==="loaded"||n.script.readyState==="complete")&&e.callback(null)},n.script.async===void 0&&document.attachEvent&&/opera/i.test(navigator.userAgent)?(n.errorScript=document.createElement("script"),n.errorScript.id=e.id+"_error",n.errorScript.text=e.name+"('"+a+"');",n.script.async=n.errorScript.async=!1):n.script.async=!0;var u=document.getElementsByTagName("head")[0];u.insertBefore(n.script,u.firstChild),n.errorScript&&u.insertBefore(n.errorScript,n.script.nextSibling)}cleanup(){this.script&&(this.script.onload=this.script.onerror=null,this.script.onreadystatechange=null),this.script&&this.script.parentNode&&this.script.parentNode.removeChild(this.script),this.errorScript&&this.errorScript.parentNode&&this.errorScript.parentNode.removeChild(this.errorScript),this.script=null,this.errorScript=null}}class or{constructor(e,n){this.url=e,this.data=n}send(e){if(!this.request){var n=er(this.data),a=this.url+"/"+e.number+"?"+n;this.request=P.createScriptRequest(a),this.request.send(e)}}cleanup(){this.request&&this.request.cleanup()}}var ar=function(r,e){return function(n,a){var u="http"+(e?"s":"")+"://",m=u+(r.host||r.options.host)+r.options.path,_=P.createJSONPRequest(m,n),k=P.ScriptReceivers.create(function(O,L){l.remove(k),_.cleanup(),L&&L.host&&(r.host=L.host),a&&a(O,L)});_.send(k)}},cr={name:"jsonp",getAgent:ar},ur=cr;function Qe(r,e,n){var a=r+(e.useTLS?"s":""),u=e.useTLS?e.hostTLS:e.hostNonTLS;return a+"://"+u+n}function Ye(r,e){var n="/app/"+r,a="?protocol="+y.PROTOCOL+"&client=js&version="+y.VERSION+(e?"&"+e:"");return n+a}var lr={getInitial:function(r,e){var n=(e.httpPath||"")+Ye(r,"flash=false");return Qe("ws",e,n)}},hr={getInitial:function(r,e){var n=(e.httpPath||"/pusher")+Ye(r);return Qe("http",e,n)}},dr={getInitial:function(r,e){return Qe("http",e,e.httpPath||"/pusher")},getPath:function(r,e){return Ye(r)}};class fr{constructor(){this._callbacks={}}get(e){return this._callbacks[Ze(e)]}add(e,n,a){var u=Ze(e);this._callbacks[u]=this._callbacks[u]||[],this._callbacks[u].push({fn:n,context:a})}remove(e,n,a){if(!e&&!n&&!a){this._callbacks={};return}var u=e?[Ze(e)]:Et(this._callbacks);n||a?this.removeCallback(u,n,a):this.removeAllCallbacks(u)}removeCallback(e,n,a){Se(e,function(u){this._callbacks[u]=Rt(this._callbacks[u]||[],function(m){return n&&n!==m.fn||a&&a!==m.context}),this._callbacks[u].length===0&&delete this._callbacks[u]},this)}removeAllCallbacks(e){Se(e,function(n){delete this._callbacks[n]},this)}}function Ze(r){return"_"+r}class oe{constructor(e){this.callbacks=new fr,this.global_callbacks=[],this.failThrough=e}bind(e,n,a){return this.callbacks.add(e,n,a),this}bind_global(e){return this.global_callbacks.push(e),this}unbind(e,n,a){return this.callbacks.remove(e,n,a),this}unbind_global(e){return e?(this.global_callbacks=Rt(this.global_callbacks||[],n=>n!==e),this):(this.global_callbacks=[],this)}unbind_all(){return this.unbind(),this.unbind_global(),this}emit(e,n,a){for(var u=0;u0)for(var u=0;u{this.onError(n),this.changeState("closed")}),!1}return this.bindListeners(),U.debug("Connecting",{transport:this.name,url:e}),this.changeState("connecting"),!0}close(){return this.socket?(this.socket.close(),!0):!1}send(e){return this.state==="open"?(H.defer(()=>{this.socket&&this.socket.send(e)}),!0):!1}ping(){this.state==="open"&&this.supportsPing()&&this.socket.ping()}onOpen(){this.hooks.beforeOpen&&this.hooks.beforeOpen(this.socket,this.hooks.urls.getPath(this.key,this.options)),this.changeState("open"),this.socket.onopen=void 0}onError(e){this.emit("error",{type:"WebSocketError",error:e}),this.timeline.error(this.buildTimelineMessage({error:e.toString()}))}onClose(e){e?this.changeState("closed",{code:e.code,reason:e.reason,wasClean:e.wasClean}):this.changeState("closed"),this.unbindListeners(),this.socket=void 0}onMessage(e){this.emit("message",e)}onActivity(){this.emit("activity")}bindListeners(){this.socket.onopen=()=>{this.onOpen()},this.socket.onerror=e=>{this.onError(e)},this.socket.onclose=e=>{this.onClose(e)},this.socket.onmessage=e=>{this.onMessage(e)},this.supportsPing()&&(this.socket.onactivity=()=>{this.onActivity()})}unbindListeners(){this.socket&&(this.socket.onopen=void 0,this.socket.onerror=void 0,this.socket.onclose=void 0,this.socket.onmessage=void 0,this.supportsPing()&&(this.socket.onactivity=void 0))}changeState(e,n){this.state=e,this.timeline.info(this.buildTimelineMessage({state:e,params:n})),this.emit(e,n)}buildTimelineMessage(e){return W({cid:this.id},e)}}class me{constructor(e){this.hooks=e}isSupported(e){return this.hooks.isSupported(e)}createConnection(e,n,a,u){return new pr(this.hooks,e,n,a,u)}}var mr=new me({urls:lr,handlesActivityChecks:!1,supportsPing:!1,isInitialized:function(){return!!P.getWebSocketAPI()},isSupported:function(){return!!P.getWebSocketAPI()},getSocket:function(r){return P.createWebSocket(r)}}),At={urls:hr,handlesActivityChecks:!1,supportsPing:!0,isInitialized:function(){return!0}},Lt=W({getSocket:function(r){return P.HTTPFactory.createStreamingSocket(r)}},At),Nt=W({getSocket:function(r){return P.HTTPFactory.createPollingSocket(r)}},At),It={isSupported:function(){return P.isXHRSupported()}},gr=new me(W({},Lt,It)),br=new me(W({},Nt,It)),yr={ws:mr,xhr_streaming:gr,xhr_polling:br},Ie=yr,vr=new me({file:"sockjs",urls:dr,handlesActivityChecks:!0,supportsPing:!1,isSupported:function(){return!0},isInitialized:function(){return window.SockJS!==void 0},getSocket:function(r,e){return new window.SockJS(r,null,{js_path:C.getPath("sockjs",{useTLS:e.useTLS}),ignore_null_origin:e.ignoreNullOrigin})},beforeOpen:function(r,e){r.send(JSON.stringify({path:e}))}}),jt={isSupported:function(r){var e=P.isXDRSupported(r.useTLS);return e}},wr=new me(W({},Lt,jt)),Sr=new me(W({},Nt,jt));Ie.xdr_streaming=wr,Ie.xdr_polling=Sr,Ie.sockjs=vr;var _r=Ie;class Cr extends oe{constructor(){super();var e=this;window.addEventListener!==void 0&&(window.addEventListener("online",function(){e.emit("online")},!1),window.addEventListener("offline",function(){e.emit("offline")},!1))}isOnline(){return window.navigator.onLine===void 0?!0:window.navigator.onLine}}var Tr=new Cr;class kr{constructor(e,n,a){this.manager=e,this.transport=n,this.minPingDelay=a.minPingDelay,this.maxPingDelay=a.maxPingDelay,this.pingDelay=void 0}createConnection(e,n,a,u){u=W({},u,{activityTimeout:this.pingDelay});var m=this.transport.createConnection(e,n,a,u),_=null,k=function(){m.unbind("open",k),m.bind("closed",O),_=H.now()},O=L=>{if(m.unbind("closed",O),L.code===1002||L.code===1003)this.manager.reportDeath();else if(!L.wasClean&&_){var I=H.now()-_;I<2*this.maxPingDelay&&(this.manager.reportDeath(),this.pingDelay=Math.max(I/2,this.minPingDelay))}};return m.bind("open",k),m}isSupported(e){return this.manager.isAlive()&&this.transport.isSupported(e)}}const Ut={decodeMessage:function(r){try{var e=JSON.parse(r.data),n=e.data;if(typeof n=="string")try{n=JSON.parse(e.data)}catch{}var a={event:e.event,channel:e.channel,data:n};return e.user_id&&(a.user_id=e.user_id),a}catch(u){throw{type:"MessageParseError",error:u,data:r.data}}},encodeMessage:function(r){return JSON.stringify(r)},processHandshake:function(r){var e=Ut.decodeMessage(r);if(e.event==="pusher:connection_established"){if(!e.data.activity_timeout)throw"No activity timeout specified in handshake";return{action:"connected",id:e.data.socket_id,activityTimeout:e.data.activity_timeout*1e3}}else{if(e.event==="pusher:error")return{action:this.getCloseAction(e.data),error:this.getCloseError(e.data)};throw"Invalid handshake"}},getCloseAction:function(r){return r.code<4e3?r.code>=1002&&r.code<=1004?"backoff":null:r.code===4e3?"tls_only":r.code<4100?"refused":r.code<4200?"backoff":r.code<4300?"retry":"refused"},getCloseError:function(r){return r.code!==1e3&&r.code!==1001?{type:"PusherError",data:{code:r.code,message:r.reason||r.message}}:null}};var ce=Ut;class Er extends oe{constructor(e,n){super(),this.id=e,this.transport=n,this.activityTimeout=n.activityTimeout,this.bindListeners()}handlesActivityChecks(){return this.transport.handlesActivityChecks()}send(e){return this.transport.send(e)}send_event(e,n,a){var u={event:e,data:n};return a&&(u.channel=a),U.debug("Event sent",u),this.send(ce.encodeMessage(u))}ping(){this.transport.supportsPing()?this.transport.ping():this.send_event("pusher:ping",{})}close(){this.transport.close()}bindListeners(){var e={message:a=>{var u;try{u=ce.decodeMessage(a)}catch(m){this.emit("error",{type:"MessageParseError",error:m,data:a.data})}if(u!==void 0){switch(U.debug("Event recd",u),u.event){case"pusher:error":this.emit("error",{type:"PusherError",data:u.data});break;case"pusher:ping":this.emit("ping");break;case"pusher:pong":this.emit("pong");break}this.emit("message",u)}},activity:()=>{this.emit("activity")},error:a=>{this.emit("error",a)},closed:a=>{n(),a&&a.code&&this.handleCloseEvent(a),this.transport=null,this.emit("closed")}},n=()=>{ie(e,(a,u)=>{this.transport.unbind(u,a)})};ie(e,(a,u)=>{this.transport.bind(u,a)})}handleCloseEvent(e){var n=ce.getCloseAction(e),a=ce.getCloseError(e);a&&this.emit("error",a),n&&this.emit(n,{action:n,error:a})}}class xr{constructor(e,n){this.transport=e,this.callback=n,this.bindListeners()}close(){this.unbindListeners(),this.transport.close()}bindListeners(){this.onMessage=e=>{this.unbindListeners();var n;try{n=ce.processHandshake(e)}catch(a){this.finish("error",{error:a}),this.transport.close();return}n.action==="connected"?this.finish("connected",{connection:new Er(n.id,this.transport),activityTimeout:n.activityTimeout}):(this.finish(n.action,{error:n.error}),this.transport.close())},this.onClosed=e=>{this.unbindListeners();var n=ce.getCloseAction(e)||"backoff",a=ce.getCloseError(e);this.finish(n,{error:a})},this.transport.bind("message",this.onMessage),this.transport.bind("closed",this.onClosed)}unbindListeners(){this.transport.unbind("message",this.onMessage),this.transport.unbind("closed",this.onClosed)}finish(e,n){this.callback(W({transport:this.transport,action:e},n))}}class Rr{constructor(e,n){this.timeline=e,this.options=n||{}}send(e,n){this.timeline.isEmpty()||this.timeline.send(P.TimelineTransport.getAgent(this,e),n)}}class et extends oe{constructor(e,n){super(function(a,u){U.debug("No callbacks on "+e+" for "+a)}),this.name=e,this.pusher=n,this.subscribed=!1,this.subscriptionPending=!1,this.subscriptionCancelled=!1}authorize(e,n){return n(null,{auth:""})}trigger(e,n){if(e.indexOf("client-")!==0)throw new f("Event '"+e+"' does not start with 'client-'");if(!this.subscribed){var a=b.buildLogSuffix("triggeringClientEvents");U.warn(`Client event triggered before channel 'subscription_succeeded' event . ${a}`)}return this.pusher.send_event(e,n,this.name)}disconnect(){this.subscribed=!1,this.subscriptionPending=!1}handleEvent(e){var n=e.event,a=e.data;if(n==="pusher_internal:subscription_succeeded")this.handleSubscriptionSucceededEvent(e);else if(n==="pusher_internal:subscription_count")this.handleSubscriptionCountEvent(e);else if(n.indexOf("pusher_internal:")!==0){var u={};this.emit(n,a,u)}}handleSubscriptionSucceededEvent(e){this.subscriptionPending=!1,this.subscribed=!0,this.subscriptionCancelled?this.pusher.unsubscribe(this.name):this.emit("pusher:subscription_succeeded",e.data)}handleSubscriptionCountEvent(e){e.data.subscription_count&&(this.subscriptionCount=e.data.subscription_count),this.emit("pusher:subscription_count",e.data)}subscribe(){this.subscribed||(this.subscriptionPending=!0,this.subscriptionCancelled=!1,this.authorize(this.pusher.connection.socket_id,(e,n)=>{e?(this.subscriptionPending=!1,U.error(e.toString()),this.emit("pusher:subscription_error",Object.assign({},{type:"AuthError",error:e.message},e instanceof z?{status:e.status}:{}))):this.pusher.send_event("pusher:subscribe",{auth:n.auth,channel_data:n.channel_data,channel:this.name})}))}unsubscribe(){this.subscribed=!1,this.pusher.send_event("pusher:unsubscribe",{channel:this.name})}cancelSubscription(){this.subscriptionCancelled=!0}reinstateSubscription(){this.subscriptionCancelled=!1}}class tt extends et{authorize(e,n){return this.pusher.config.channelAuthorizer({channelName:this.name,socketId:e},n)}}class Or{constructor(){this.reset()}get(e){return Object.prototype.hasOwnProperty.call(this.members,e)?{id:e,info:this.members[e]}:null}each(e){ie(this.members,(n,a)=>{e(this.get(a))})}setMyID(e){this.myID=e}onSubscription(e){this.members=e.presence.hash,this.count=e.presence.count,this.me=this.get(this.myID)}addMember(e){return this.get(e.user_id)===null&&this.count++,this.members[e.user_id]=e.user_info,this.get(e.user_id)}removeMember(e){var n=this.get(e.user_id);return n&&(delete this.members[e.user_id],this.count--),n}reset(){this.members={},this.count=0,this.myID=null,this.me=null}}var Pr=function(r,e,n,a){function u(m){return m instanceof n?m:new n(function(_){_(m)})}return new(n||(n=Promise))(function(m,_){function k(I){try{L(a.next(I))}catch(q){_(q)}}function O(I){try{L(a.throw(I))}catch(q){_(q)}}function L(I){I.done?m(I.value):u(I.value).then(k,O)}L((a=a.apply(r,e||[])).next())})};class Ar extends tt{constructor(e,n){super(e,n),this.members=new Or}authorize(e,n){super.authorize(e,(a,u)=>Pr(this,void 0,void 0,function*(){if(!a)if(u=u,u.channel_data!=null){var m=JSON.parse(u.channel_data);this.members.setMyID(m.user_id)}else if(yield this.pusher.user.signinDonePromise,this.pusher.user.user_data!=null)this.members.setMyID(this.pusher.user.user_data.id);else{let _=b.buildLogSuffix("authorizationEndpoint");U.error(`Invalid auth response for channel '${this.name}', expected 'channel_data' field. ${_}, or the user should be signed in.`),n("Invalid auth response");return}n(a,u)}))}handleEvent(e){var n=e.event;if(n.indexOf("pusher_internal:")===0)this.handleInternalEvent(e);else{var a=e.data,u={};e.user_id&&(u.user_id=e.user_id),this.emit(n,a,u)}}handleInternalEvent(e){var n=e.event,a=e.data;switch(n){case"pusher_internal:subscription_succeeded":this.handleSubscriptionSucceededEvent(e);break;case"pusher_internal:subscription_count":this.handleSubscriptionCountEvent(e);break;case"pusher_internal:member_added":var u=this.members.addMember(a);this.emit("pusher:member_added",u);break;case"pusher_internal:member_removed":var m=this.members.removeMember(a);m&&this.emit("pusher:member_removed",m);break}}handleSubscriptionSucceededEvent(e){this.subscriptionPending=!1,this.subscribed=!0,this.subscriptionCancelled?this.pusher.unsubscribe(this.name):(this.members.onSubscription(e.data),this.emit("pusher:subscription_succeeded",this.members))}disconnect(){this.members.reset(),super.disconnect()}}var Lr=c(1),nt=c(0);class Nr extends tt{constructor(e,n,a){super(e,n),this.key=null,this.nacl=a}authorize(e,n){super.authorize(e,(a,u)=>{if(a){n(a,u);return}let m=u.shared_secret;if(!m){n(new Error(`No shared_secret key in auth payload for encrypted channel: ${this.name}`),null);return}this.key=Object(nt.decode)(m),delete u.shared_secret,n(null,u)})}trigger(e,n){throw new j("Client events are not currently supported for encrypted channels")}handleEvent(e){var n=e.event,a=e.data;if(n.indexOf("pusher_internal:")===0||n.indexOf("pusher:")===0){super.handleEvent(e);return}this.handleEncryptedEvent(n,a)}handleEncryptedEvent(e,n){if(!this.key){U.debug("Received encrypted event before key has been retrieved from the authEndpoint");return}if(!n.ciphertext||!n.nonce){U.error("Unexpected format for encrypted event, expected object with `ciphertext` and `nonce` fields, got: "+n);return}let a=Object(nt.decode)(n.ciphertext);if(a.length{if(_){U.error(`Failed to make a request to the authEndpoint: ${k}. Unable to fetch new key, so dropping encrypted event`);return}if(m=this.nacl.secretbox.open(a,u,this.key),m===null){U.error("Failed to decrypt event with new key. Dropping encrypted event");return}this.emit(e,this.getDataToEmit(m))});return}this.emit(e,this.getDataToEmit(m))}getDataToEmit(e){let n=Object(Lr.decode)(e);try{return JSON.parse(n)}catch{return n}}}class Ir extends oe{constructor(e,n){super(),this.state="initialized",this.connection=null,this.key=e,this.options=n,this.timeline=this.options.timeline,this.usingTLS=this.options.useTLS,this.errorCallbacks=this.buildErrorCallbacks(),this.connectionCallbacks=this.buildConnectionCallbacks(this.errorCallbacks),this.handshakeCallbacks=this.buildHandshakeCallbacks(this.errorCallbacks);var a=P.getNetwork();a.bind("online",()=>{this.timeline.info({netinfo:"online"}),(this.state==="connecting"||this.state==="unavailable")&&this.retryIn(0)}),a.bind("offline",()=>{this.timeline.info({netinfo:"offline"}),this.connection&&this.sendActivityCheck()}),this.updateStrategy()}connect(){if(!(this.connection||this.runner)){if(!this.strategy.isSupported()){this.updateState("failed");return}this.updateState("connecting"),this.startConnecting(),this.setUnavailableTimer()}}send(e){return this.connection?this.connection.send(e):!1}send_event(e,n,a){return this.connection?this.connection.send_event(e,n,a):!1}disconnect(){this.disconnectInternally(),this.updateState("disconnected")}isUsingTLS(){return this.usingTLS}startConnecting(){var e=(n,a)=>{n?this.runner=this.strategy.connect(0,e):a.action==="error"?(this.emit("error",{type:"HandshakeError",error:a.error}),this.timeline.error({handshakeError:a.error})):(this.abortConnecting(),this.handshakeCallbacks[a.action](a))};this.runner=this.strategy.connect(0,e)}abortConnecting(){this.runner&&(this.runner.abort(),this.runner=null)}disconnectInternally(){if(this.abortConnecting(),this.clearRetryTimer(),this.clearUnavailableTimer(),this.connection){var e=this.abandonConnection();e.close()}}updateStrategy(){this.strategy=this.options.getStrategy({key:this.key,timeline:this.timeline,useTLS:this.usingTLS})}retryIn(e){this.timeline.info({action:"retry",delay:e}),e>0&&this.emit("connecting_in",Math.round(e/1e3)),this.retryTimer=new X(e||0,()=>{this.disconnectInternally(),this.connect()})}clearRetryTimer(){this.retryTimer&&(this.retryTimer.ensureAborted(),this.retryTimer=null)}setUnavailableTimer(){this.unavailableTimer=new X(this.options.unavailableTimeout,()=>{this.updateState("unavailable")})}clearUnavailableTimer(){this.unavailableTimer&&this.unavailableTimer.ensureAborted()}sendActivityCheck(){this.stopActivityCheck(),this.connection.ping(),this.activityTimer=new X(this.options.pongTimeout,()=>{this.timeline.error({pong_timed_out:this.options.pongTimeout}),this.retryIn(0)})}resetActivityCheck(){this.stopActivityCheck(),this.connection&&!this.connection.handlesActivityChecks()&&(this.activityTimer=new X(this.activityTimeout,()=>{this.sendActivityCheck()}))}stopActivityCheck(){this.activityTimer&&this.activityTimer.ensureAborted()}buildConnectionCallbacks(e){return W({},e,{message:n=>{this.resetActivityCheck(),this.emit("message",n)},ping:()=>{this.send_event("pusher:pong",{})},activity:()=>{this.resetActivityCheck()},error:n=>{this.emit("error",n)},closed:()=>{this.abandonConnection(),this.shouldRetry()&&this.retryIn(1e3)}})}buildHandshakeCallbacks(e){return W({},e,{connected:n=>{this.activityTimeout=Math.min(this.options.activityTimeout,n.activityTimeout,n.connection.activityTimeout||1/0),this.clearUnavailableTimer(),this.setConnection(n.connection),this.socket_id=this.connection.id,this.updateState("connected",{socket_id:this.socket_id})}})}buildErrorCallbacks(){let e=n=>a=>{a.error&&this.emit("error",{type:"WebSocketError",error:a.error}),n(a)};return{tls_only:e(()=>{this.usingTLS=!0,this.updateStrategy(),this.retryIn(0)}),refused:e(()=>{this.disconnect()}),backoff:e(()=>{this.retryIn(1e3)}),retry:e(()=>{this.retryIn(0)})}}setConnection(e){this.connection=e;for(var n in this.connectionCallbacks)this.connection.bind(n,this.connectionCallbacks[n]);this.resetActivityCheck()}abandonConnection(){if(this.connection){this.stopActivityCheck();for(var e in this.connectionCallbacks)this.connection.unbind(e,this.connectionCallbacks[e]);var n=this.connection;return this.connection=null,n}}updateState(e,n){var a=this.state;if(this.state=e,a!==e){var u=e;u==="connected"&&(u+=" with new socket ID "+n.socket_id),U.debug("State changed",a+" -> "+u),this.timeline.info({state:e,params:n}),this.emit("state_change",{previous:a,current:e}),this.emit(e,n)}}shouldRetry(){return this.state==="connecting"||this.state==="connected"}}class jr{constructor(){this.channels={}}add(e,n){return this.channels[e]||(this.channels[e]=Ur(e,n)),this.channels[e]}all(){return Kn(this.channels)}find(e){return this.channels[e]}remove(e){var n=this.channels[e];return delete this.channels[e],n}disconnect(){ie(this.channels,function(e){e.disconnect()})}}function Ur(r,e){if(r.indexOf("private-encrypted-")===0){if(e.config.nacl)return ae.createEncryptedChannel(r,e,e.config.nacl);let n="Tried to subscribe to a private-encrypted- channel but no nacl implementation available",a=b.buildLogSuffix("encryptedChannelSupport");throw new j(`${n}. ${a}`)}else{if(r.indexOf("private-")===0)return ae.createPrivateChannel(r,e);if(r.indexOf("presence-")===0)return ae.createPresenceChannel(r,e);if(r.indexOf("#")===0)throw new S('Cannot create a channel with name "'+r+'".');return ae.createChannel(r,e)}}var Dr={createChannels(){return new jr},createConnectionManager(r,e){return new Ir(r,e)},createChannel(r,e){return new et(r,e)},createPrivateChannel(r,e){return new tt(r,e)},createPresenceChannel(r,e){return new Ar(r,e)},createEncryptedChannel(r,e,n){return new Nr(r,e,n)},createTimelineSender(r,e){return new Rr(r,e)},createHandshake(r,e){return new xr(r,e)},createAssistantToTheTransportManager(r,e,n){return new kr(r,e,n)}},ae=Dr;class Dt{constructor(e){this.options=e||{},this.livesLeft=this.options.lives||1/0}getAssistant(e){return ae.createAssistantToTheTransportManager(this,e,{minPingDelay:this.options.minPingDelay,maxPingDelay:this.options.maxPingDelay})}isAlive(){return this.livesLeft>0}reportDeath(){this.livesLeft-=1}}class ue{constructor(e,n){this.strategies=e,this.loop=!!n.loop,this.failFast=!!n.failFast,this.timeout=n.timeout,this.timeoutLimit=n.timeoutLimit}isSupported(){return Pt(this.strategies,H.method("isSupported"))}connect(e,n){var a=this.strategies,u=0,m=this.timeout,_=null,k=(O,L)=>{L?n(null,L):(u=u+1,this.loop&&(u=u%a.length),u0&&(m=new X(a.timeout,function(){_.abort(),u(!0)})),_=e.connect(n,function(k,O){k&&m&&m.isRunning()&&!a.failFast||(m&&m.ensureAborted(),u(k,O))}),{abort:function(){m&&m.ensureAborted(),_.abort()},forceMinPriority:function(k){_.forceMinPriority(k)}}}}class rt{constructor(e){this.strategies=e}isSupported(){return Pt(this.strategies,H.method("isSupported"))}connect(e,n){return Fr(this.strategies,e,function(a,u){return function(m,_){if(u[a].error=m,m){qr(u)&&n(!0);return}Se(u,function(k){k.forceMinPriority(_.transport.priority)}),n(null,_)}})}}function Fr(r,e,n){var a=xt(r,function(u,m,_,k){return u.connect(e,n(m,k))});return{abort:function(){Se(a,Br)},forceMinPriority:function(u){Se(a,function(m){m.forceMinPriority(u)})}}}function qr(r){return Yn(r,function(e){return!!e.error})}function Br(r){!r.error&&!r.aborted&&(r.abort(),r.aborted=!0)}class Hr{constructor(e,n,a){this.strategy=e,this.transports=n,this.ttl=a.ttl||1800*1e3,this.usingTLS=a.useTLS,this.timeline=a.timeline}isSupported(){return this.strategy.isSupported()}connect(e,n){var a=this.usingTLS,u=Mr(a),m=u&&u.cacheSkipCount?u.cacheSkipCount:0,_=[this.strategy];if(u&&u.timestamp+this.ttl>=H.now()){var k=this.transports[u.transport];k&&(["ws","wss"].includes(u.transport)||m>3?(this.timeline.info({cached:!0,transport:u.transport,latency:u.latency}),_.push(new ue([k],{timeout:u.latency*2+1e3,failFast:!0}))):m++)}var O=H.now(),L=_.pop().connect(e,function I(q,De){q?(Ft(a),_.length>0?(O=H.now(),L=_.pop().connect(e,I)):n(q)):(zr(a,De.transport.name,H.now()-O,m),n(null,De))});return{abort:function(){L.abort()},forceMinPriority:function(I){e=I,L&&L.forceMinPriority(I)}}}}function st(r){return"pusherTransport"+(r?"TLS":"NonTLS")}function Mr(r){var e=P.getLocalStorage();if(e)try{var n=e[st(r)];if(n)return JSON.parse(n)}catch{Ft(r)}return null}function zr(r,e,n,a){var u=P.getLocalStorage();if(u)try{u[st(r)]=Ne({timestamp:H.now(),transport:e,latency:n,cacheSkipCount:a})}catch{}}function Ft(r){var e=P.getLocalStorage();if(e)try{delete e[st(r)]}catch{}}class je{constructor(e,{delay:n}){this.strategy=e,this.options={delay:n}}isSupported(){return this.strategy.isSupported()}connect(e,n){var a=this.strategy,u,m=new X(this.options.delay,function(){u=a.connect(e,n)});return{abort:function(){m.ensureAborted(),u&&u.abort()},forceMinPriority:function(_){e=_,u&&u.forceMinPriority(_)}}}}class _e{constructor(e,n,a){this.test=e,this.trueBranch=n,this.falseBranch=a}isSupported(){var e=this.test()?this.trueBranch:this.falseBranch;return e.isSupported()}connect(e,n){var a=this.test()?this.trueBranch:this.falseBranch;return a.connect(e,n)}}class $r{constructor(e){this.strategy=e}isSupported(){return this.strategy.isSupported()}connect(e,n){var a=this.strategy.connect(e,function(u,m){m&&a.abort(),n(u,m)});return a}}function Ce(r){return function(){return r.isSupported()}}var Jr=function(r,e,n){var a={};function u(Kt,$s,Js,Xs,Ws){var Gt=n(r,Kt,$s,Js,Xs,Ws);return a[Kt]=Gt,Gt}var m=Object.assign({},e,{hostNonTLS:r.wsHost+":"+r.wsPort,hostTLS:r.wsHost+":"+r.wssPort,httpPath:r.wsPath}),_=Object.assign({},m,{useTLS:!0}),k=Object.assign({},e,{hostNonTLS:r.httpHost+":"+r.httpPort,hostTLS:r.httpHost+":"+r.httpsPort,httpPath:r.httpPath}),O={loop:!0,timeout:15e3,timeoutLimit:6e4},L=new Dt({minPingDelay:1e4,maxPingDelay:r.activityTimeout}),I=new Dt({lives:2,minPingDelay:1e4,maxPingDelay:r.activityTimeout}),q=u("ws","ws",3,m,L),De=u("wss","ws",3,_,L),qs=u("sockjs","sockjs",1,k),zt=u("xhr_streaming","xhr_streaming",1,k,I),Bs=u("xdr_streaming","xdr_streaming",1,k,I),$t=u("xhr_polling","xhr_polling",1,k),Hs=u("xdr_polling","xdr_polling",1,k),Jt=new ue([q],O),Ms=new ue([De],O),zs=new ue([qs],O),Xt=new ue([new _e(Ce(zt),zt,Bs)],O),Wt=new ue([new _e(Ce($t),$t,Hs)],O),Vt=new ue([new _e(Ce(Xt),new rt([Xt,new je(Wt,{delay:4e3})]),Wt)],O),ct=new _e(Ce(Vt),Vt,zs),ut;return e.useTLS?ut=new rt([Jt,new je(ct,{delay:2e3})]):ut=new rt([Jt,new je(Ms,{delay:2e3}),new je(ct,{delay:5e3})]),new Hr(new $r(new _e(Ce(q),ut,ct)),a,{ttl:18e5,timeline:e.timeline,useTLS:e.useTLS})},Xr=Jr,Wr=(function(){var r=this;r.timeline.info(r.buildTimelineMessage({transport:r.name+(r.options.useTLS?"s":"")})),r.hooks.isInitialized()?r.changeState("initialized"):r.hooks.file?(r.changeState("initializing"),C.load(r.hooks.file,{useTLS:r.options.useTLS},function(e,n){r.hooks.isInitialized()?(r.changeState("initialized"),n(!0)):(e&&r.onError(e),r.onClose(),n(!1))})):r.onClose()}),Vr={getRequest:function(r){var e=new window.XDomainRequest;return e.ontimeout=function(){r.emit("error",new E),r.close()},e.onerror=function(n){r.emit("error",n),r.close()},e.onprogress=function(){e.responseText&&e.responseText.length>0&&r.onChunk(200,e.responseText)},e.onload=function(){e.responseText&&e.responseText.length>0&&r.onChunk(200,e.responseText),r.emit("finished",200),r.close()},e},abortRequest:function(r){r.ontimeout=r.onerror=r.onprogress=r.onload=null,r.abort()}},Kr=Vr;const Gr=256*1024;class Qr extends oe{constructor(e,n,a){super(),this.hooks=e,this.method=n,this.url=a}start(e){this.position=0,this.xhr=this.hooks.getRequest(this),this.unloader=()=>{this.close()},P.addUnloadListener(this.unloader),this.xhr.open(this.method,this.url,!0),this.xhr.setRequestHeader&&this.xhr.setRequestHeader("Content-Type","application/json"),this.xhr.send(e)}close(){this.unloader&&(P.removeUnloadListener(this.unloader),this.unloader=null),this.xhr&&(this.hooks.abortRequest(this.xhr),this.xhr=null)}onChunk(e,n){for(;;){var a=this.advanceBuffer(n);if(a)this.emit("chunk",{status:e,data:a});else break}this.isBufferTooLong(n)&&this.emit("buffer_too_long")}advanceBuffer(e){var n=e.slice(this.position),a=n.indexOf(` -`);return a!==-1?(this.position+=a+1,n.slice(0,a)):null}isBufferTooLong(e){return this.position===e.length&&e.length>Gr}}var it;(function(r){r[r.CONNECTING=0]="CONNECTING",r[r.OPEN=1]="OPEN",r[r.CLOSED=3]="CLOSED"})(it||(it={}));var le=it,Yr=1;class Zr{constructor(e,n){this.hooks=e,this.session=Bt(1e3)+"/"+rs(8),this.location=es(n),this.readyState=le.CONNECTING,this.openStream()}send(e){return this.sendRaw(JSON.stringify([e]))}ping(){this.hooks.sendHeartbeat(this)}close(e,n){this.onClose(e,n,!0)}sendRaw(e){if(this.readyState===le.OPEN)try{return P.createSocketRequest("POST",qt(ts(this.location,this.session))).start(e),!0}catch{return!1}else return!1}reconnect(){this.closeStream(),this.openStream()}onClose(e,n,a){this.closeStream(),this.readyState=le.CLOSED,this.onclose&&this.onclose({code:e,reason:n,wasClean:a})}onChunk(e){if(e.status===200){this.readyState===le.OPEN&&this.onActivity();var n,a=e.data.slice(0,1);switch(a){case"o":n=JSON.parse(e.data.slice(1)||"{}"),this.onOpen(n);break;case"a":n=JSON.parse(e.data.slice(1)||"[]");for(var u=0;u{this.onChunk(e)}),this.stream.bind("finished",e=>{this.hooks.onFinished(this,e)}),this.stream.bind("buffer_too_long",()=>{this.reconnect()});try{this.stream.start()}catch(e){H.defer(()=>{this.onError(e),this.onClose(1006,"Could not start streaming",!1)})}}closeStream(){this.stream&&(this.stream.unbind_all(),this.stream.close(),this.stream=null)}}function es(r){var e=/([^\?]*)\/*(\??.*)/.exec(r);return{base:e[1],queryString:e[2]}}function ts(r,e){return r.base+"/"+e+"/xhr_send"}function qt(r){var e=r.indexOf("?")===-1?"?":"&";return r+e+"t="+ +new Date+"&n="+Yr++}function ns(r,e){var n=/(https?:\/\/)([^\/:]+)((\/|:)?.*)/.exec(r);return n[1]+e+n[3]}function Bt(r){return P.randomInt(r)}function rs(r){for(var e=[],n=0;n0&&r.onChunk(n.status,n.responseText);break;case 4:n.responseText&&n.responseText.length>0&&r.onChunk(n.status,n.responseText),r.emit("finished",n.status),r.close();break}},n},abortRequest:function(r){r.onreadystatechange=null,r.abort()}},ls=us,hs={createStreamingSocket(r){return this.createSocket(os,r)},createPollingSocket(r){return this.createSocket(cs,r)},createSocket(r,e){return new ss(r,e)},createXHR(r,e){return this.createRequest(ls,r,e)},createRequest(r,e,n){return new Qr(r,e,n)}},Ht=hs;Ht.createXDR=function(r,e){return this.createRequest(Kr,r,e)};var ds=Ht,fs={nextAuthCallbackID:1,auth_callbacks:{},ScriptReceivers:l,DependenciesReceivers:g,getDefaultStrategy:Xr,Transports:_r,transportConnectionInitializer:Wr,HTTPFactory:ds,TimelineTransport:ur,getXHRAPI(){return window.XMLHttpRequest},getWebSocketAPI(){return window.WebSocket||window.MozWebSocket},setup(r){window.Pusher=r;var e=()=>{this.onDocumentBody(r.ready)};window.JSON?e():C.load("json2",{},e)},getDocument(){return document},getProtocol(){return this.getDocument().location.protocol},getAuthorizers(){return{ajax:Q,jsonp:sr}},onDocumentBody(r){document.body?r():setTimeout(()=>{this.onDocumentBody(r)},0)},createJSONPRequest(r,e){return new or(r,e)},createScriptRequest(r){return new ir(r)},getLocalStorage(){try{return window.localStorage}catch{return}},createXHR(){return this.getXHRAPI()?this.createXMLHttpRequest():this.createMicrosoftXHR()},createXMLHttpRequest(){var r=this.getXHRAPI();return new r},createMicrosoftXHR(){return new ActiveXObject("Microsoft.XMLHTTP")},getNetwork(){return Tr},createWebSocket(r){var e=this.getWebSocketAPI();return new e(r)},createSocketRequest(r,e){if(this.isXHRSupported())return this.HTTPFactory.createXHR(r,e);if(this.isXDRSupported(e.indexOf("https:")===0))return this.HTTPFactory.createXDR(r,e);throw"Cross-origin HTTP requests are not supported"},isXHRSupported(){var r=this.getXHRAPI();return!!r&&new r().withCredentials!==void 0},isXDRSupported(r){var e=r?"https:":"http:",n=this.getProtocol();return!!window.XDomainRequest&&n===e},addUnloadListener(r){window.addEventListener!==void 0?window.addEventListener("unload",r,!1):window.attachEvent!==void 0&&window.attachEvent("onunload",r)},removeUnloadListener(r){window.addEventListener!==void 0?window.removeEventListener("unload",r,!1):window.detachEvent!==void 0&&window.detachEvent("onunload",r)},randomInt(r){return Math.floor(function(){return(window.crypto||window.msCrypto).getRandomValues(new Uint32Array(1))[0]/Math.pow(2,32)}()*r)}},P=fs,ot;(function(r){r[r.ERROR=3]="ERROR",r[r.INFO=6]="INFO",r[r.DEBUG=7]="DEBUG"})(ot||(ot={}));var Ue=ot;class ps{constructor(e,n,a){this.key=e,this.session=n,this.events=[],this.options=a||{},this.sent=0,this.uniqueID=0}log(e,n){e<=this.options.level&&(this.events.push(W({},n,{timestamp:H.now()})),this.options.limit&&this.events.length>this.options.limit&&this.events.shift())}error(e){this.log(Ue.ERROR,e)}info(e){this.log(Ue.INFO,e)}debug(e){this.log(Ue.DEBUG,e)}isEmpty(){return this.events.length===0}send(e,n){var a=W({session:this.session,bundle:this.sent+1,key:this.key,lib:"js",version:this.options.version,cluster:this.options.cluster,features:this.options.features,timeline:this.events},this.options.params);return this.events=[],e(a,(u,m)=>{u||this.sent++,n&&n(u,m)}),!0}generateUniqueID(){return this.uniqueID++,this.uniqueID}}class ms{constructor(e,n,a,u){this.name=e,this.priority=n,this.transport=a,this.options=u||{}}isSupported(){return this.transport.isSupported({useTLS:this.options.useTLS})}connect(e,n){if(this.isSupported()){if(this.priority{a||(I(),m?m.close():u.close())},forceMinPriority:q=>{a||this.priority{var n="socket_id="+encodeURIComponent(r.socketId);for(var a in e.params)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(e.params[a]);if(e.paramsProvider!=null){let u=e.paramsProvider();for(var a in u)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(u[a])}return n};var Ss=r=>{if(typeof P.getAuthorizers()[r.transport]>"u")throw`'${r.transport}' is not a recognized auth transport`;return(e,n)=>{const a=ws(e,r);P.getAuthorizers()[r.transport](P,a,r,v.UserAuthentication,n)}};const _s=(r,e)=>{var n="socket_id="+encodeURIComponent(r.socketId);n+="&channel_name="+encodeURIComponent(r.channelName);for(var a in e.params)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(e.params[a]);if(e.paramsProvider!=null){let u=e.paramsProvider();for(var a in u)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(u[a])}return n};var Cs=r=>{if(typeof P.getAuthorizers()[r.transport]>"u")throw`'${r.transport}' is not a recognized auth transport`;return(e,n)=>{const a=_s(e,r);P.getAuthorizers()[r.transport](P,a,r,v.ChannelAuthorization,n)}};const Ts=(r,e,n)=>{const a={authTransport:e.transport,authEndpoint:e.endpoint,auth:{params:e.params,headers:e.headers}};return(u,m)=>{const _=r.channel(u.channelName);n(_,a).authorize(u.socketId,m)}};function ks(r,e){let n={activityTimeout:r.activityTimeout||y.activityTimeout,cluster:r.cluster,httpPath:r.httpPath||y.httpPath,httpPort:r.httpPort||y.httpPort,httpsPort:r.httpsPort||y.httpsPort,pongTimeout:r.pongTimeout||y.pongTimeout,statsHost:r.statsHost||y.stats_host,unavailableTimeout:r.unavailableTimeout||y.unavailableTimeout,wsPath:r.wsPath||y.wsPath,wsPort:r.wsPort||y.wsPort,wssPort:r.wssPort||y.wssPort,enableStats:Ps(r),httpHost:Es(r),useTLS:Os(r),wsHost:xs(r),userAuthenticator:As(r),channelAuthorizer:Ns(r,e)};return"disabledTransports"in r&&(n.disabledTransports=r.disabledTransports),"enabledTransports"in r&&(n.enabledTransports=r.enabledTransports),"ignoreNullOrigin"in r&&(n.ignoreNullOrigin=r.ignoreNullOrigin),"timelineParams"in r&&(n.timelineParams=r.timelineParams),"nacl"in r&&(n.nacl=r.nacl),n}function Es(r){return r.httpHost?r.httpHost:r.cluster?`sockjs-${r.cluster}.pusher.com`:y.httpHost}function xs(r){return r.wsHost?r.wsHost:Rs(r.cluster)}function Rs(r){return`ws-${r}.pusher.com`}function Os(r){return P.getProtocol()==="https:"?!0:r.forceTLS!==!1}function Ps(r){return"enableStats"in r?r.enableStats:"disableStats"in r?!r.disableStats:!1}function As(r){const e=Object.assign(Object.assign({},y.userAuthentication),r.userAuthentication);return"customHandler"in e&&e.customHandler!=null?e.customHandler:Ss(e)}function Ls(r,e){let n;return"channelAuthorization"in r?n=Object.assign(Object.assign({},y.channelAuthorization),r.channelAuthorization):(n={transport:r.authTransport||y.authTransport,endpoint:r.authEndpoint||y.authEndpoint},"auth"in r&&("params"in r.auth&&(n.params=r.auth.params),"headers"in r.auth&&(n.headers=r.auth.headers)),"authorizer"in r&&(n.customHandler=Ts(e,n,r.authorizer))),n}function Ns(r,e){const n=Ls(r,e);return"customHandler"in n&&n.customHandler!=null?n.customHandler:Cs(n)}class Is extends oe{constructor(e){super(function(n,a){U.debug(`No callbacks on watchlist events for ${n}`)}),this.pusher=e,this.bindWatchlistInternalEvent()}handleEvent(e){e.data.events.forEach(n=>{this.emit(n.name,n)})}bindWatchlistInternalEvent(){this.pusher.connection.bind("message",e=>{var n=e.event;n==="pusher_internal:watchlist_events"&&this.handleEvent(e)})}}function js(){let r,e;return{promise:new Promise((a,u)=>{r=a,e=u}),resolve:r,reject:e}}var Us=js;class Ds extends oe{constructor(e){super(function(n,a){U.debug("No callbacks on user for "+n)}),this.signin_requested=!1,this.user_data=null,this.serverToUserChannel=null,this.signinDonePromise=null,this._signinDoneResolve=null,this._onAuthorize=(n,a)=>{if(n){U.warn(`Error during signin: ${n}`),this._cleanup();return}this.pusher.send_event("pusher:signin",{auth:a.auth,user_data:a.user_data})},this.pusher=e,this.pusher.connection.bind("state_change",({previous:n,current:a})=>{n!=="connected"&&a==="connected"&&this._signin(),n==="connected"&&a!=="connected"&&(this._cleanup(),this._newSigninPromiseIfNeeded())}),this.watchlist=new Is(e),this.pusher.connection.bind("message",n=>{var a=n.event;a==="pusher:signin_success"&&this._onSigninSuccess(n.data),this.serverToUserChannel&&this.serverToUserChannel.name===n.channel&&this.serverToUserChannel.handleEvent(n)})}signin(){this.signin_requested||(this.signin_requested=!0,this._signin())}_signin(){this.signin_requested&&(this._newSigninPromiseIfNeeded(),this.pusher.connection.state==="connected"&&this.pusher.config.userAuthenticator({socketId:this.pusher.connection.socket_id},this._onAuthorize))}_onSigninSuccess(e){try{this.user_data=JSON.parse(e.user_data)}catch{U.error(`Failed parsing user data after signin: ${e.user_data}`),this._cleanup();return}if(typeof this.user_data.id!="string"||this.user_data.id===""){U.error(`user_data doesn't contain an id. user_data: ${this.user_data}`),this._cleanup();return}this._signinDoneResolve(),this._subscribeChannels()}_subscribeChannels(){const e=n=>{n.subscriptionPending&&n.subscriptionCancelled?n.reinstateSubscription():!n.subscriptionPending&&this.pusher.connection.state==="connected"&&n.subscribe()};this.serverToUserChannel=new et(`#server-to-user-${this.user_data.id}`,this.pusher),this.serverToUserChannel.bind_global((n,a)=>{n.indexOf("pusher_internal:")===0||n.indexOf("pusher:")===0||this.emit(n,a)}),e(this.serverToUserChannel)}_cleanup(){this.user_data=null,this.serverToUserChannel&&(this.serverToUserChannel.unbind_all(),this.serverToUserChannel.disconnect(),this.serverToUserChannel=null),this.signin_requested&&this._signinDoneResolve()}_newSigninPromiseIfNeeded(){if(!this.signin_requested||this.signinDonePromise&&!this.signinDonePromise.done)return;const{promise:e,resolve:n}=Us();e.done=!1;const a=()=>{e.done=!0};e.then(a).catch(a),this.signinDonePromise=e,this._signinDoneResolve=n}}class M{static ready(){M.isReady=!0;for(var e=0,n=M.instances.length;eP.getDefaultStrategy(this.config,u,bs);this.connection=ae.createConnectionManager(this.key,{getStrategy:a,timeline:this.timeline,activityTimeout:this.config.activityTimeout,pongTimeout:this.config.pongTimeout,unavailableTimeout:this.config.unavailableTimeout,useTLS:!!this.config.useTLS}),this.connection.bind("connected",()=>{this.subscribeAll(),this.timelineSender&&this.timelineSender.send(this.connection.isUsingTLS())}),this.connection.bind("message",u=>{var m=u.event,_=m.indexOf("pusher_internal:")===0;if(u.channel){var k=this.channel(u.channel);k&&k.handleEvent(u)}_||this.global_emitter.emit(u.event,u.data)}),this.connection.bind("connecting",()=>{this.channels.disconnect()}),this.connection.bind("disconnected",()=>{this.channels.disconnect()}),this.connection.bind("error",u=>{U.warn(u)}),M.instances.push(this),this.timeline.info({instances:M.instances.length}),this.user=new Ds(this),M.isReady&&this.connect()}channel(e){return this.channels.find(e)}allChannels(){return this.channels.all()}connect(){if(this.connection.connect(),this.timelineSender&&!this.timelineSenderTimer){var e=this.connection.isUsingTLS(),n=this.timelineSender;this.timelineSenderTimer=new we(6e4,function(){n.send(e)})}}disconnect(){this.connection.disconnect(),this.timelineSenderTimer&&(this.timelineSenderTimer.ensureAborted(),this.timelineSenderTimer=null)}bind(e,n,a){return this.global_emitter.bind(e,n,a),this}unbind(e,n,a){return this.global_emitter.unbind(e,n,a),this}bind_global(e){return this.global_emitter.bind_global(e),this}unbind_global(e){return this.global_emitter.unbind_global(e),this}unbind_all(e){return this.global_emitter.unbind_all(),this}subscribeAll(){var e;for(e in this.channels.channels)this.channels.channels.hasOwnProperty(e)&&this.subscribe(e)}subscribe(e){var n=this.channels.add(e,this);return n.subscriptionPending&&n.subscriptionCancelled?n.reinstateSubscription():!n.subscriptionPending&&this.connection.state==="connected"&&n.subscribe(),n}unsubscribe(e){var n=this.channels.find(e);n&&n.subscriptionPending?n.cancelSubscription():(n=this.channels.remove(e),n&&n.subscribed&&n.unsubscribe())}send_event(e,n,a){return this.connection.send_event(e,n,a)}shouldUseTLS(){return this.config.useTLS}signin(){this.user.signin()}}M.instances=[],M.isReady=!1,M.logToConsole=!1,M.Runtime=P,M.ScriptReceivers=P.ScriptReceivers,M.DependenciesReceivers=P.DependenciesReceivers,M.auth_callbacks=P.auth_callbacks;var at=o.default=M;function Fs(r){if(r==null)throw"You must pass your app key when you instantiate Pusher."}P.setup(M)})])})})(ft)),ft.exports}var Io=No();const jo=Lo(Io);window.Pusher=jo;const ke=window.reverbConfig??{};window.Echo=new Ao({broadcaster:"reverb",key:ke.key??"my-app-key",wsHost:ke.host??"127.0.0.1",wsPort:ke.port??"8080"??80,wssPort:ke.port??"8080"??443,forceTLS:ke.forceTLS??!1,enabledTransports:["ws","wss"]});window.axios=F;window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest";const gn=document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");gn&&(window.axios.defaults.headers.common["X-CSRF-TOKEN"]=gn); +`);return a!==-1?(this.position+=a+1,n.slice(0,a)):null}isBufferTooLong(e){return this.position===e.length&&e.length>Gr}}var it;(function(r){r[r.CONNECTING=0]="CONNECTING",r[r.OPEN=1]="OPEN",r[r.CLOSED=3]="CLOSED"})(it||(it={}));var le=it,Yr=1;class Zr{constructor(e,n){this.hooks=e,this.session=Bt(1e3)+"/"+rs(8),this.location=es(n),this.readyState=le.CONNECTING,this.openStream()}send(e){return this.sendRaw(JSON.stringify([e]))}ping(){this.hooks.sendHeartbeat(this)}close(e,n){this.onClose(e,n,!0)}sendRaw(e){if(this.readyState===le.OPEN)try{return P.createSocketRequest("POST",qt(ts(this.location,this.session))).start(e),!0}catch{return!1}else return!1}reconnect(){this.closeStream(),this.openStream()}onClose(e,n,a){this.closeStream(),this.readyState=le.CLOSED,this.onclose&&this.onclose({code:e,reason:n,wasClean:a})}onChunk(e){if(e.status===200){this.readyState===le.OPEN&&this.onActivity();var n,a=e.data.slice(0,1);switch(a){case"o":n=JSON.parse(e.data.slice(1)||"{}"),this.onOpen(n);break;case"a":n=JSON.parse(e.data.slice(1)||"[]");for(var u=0;u{this.onChunk(e)}),this.stream.bind("finished",e=>{this.hooks.onFinished(this,e)}),this.stream.bind("buffer_too_long",()=>{this.reconnect()});try{this.stream.start()}catch(e){H.defer(()=>{this.onError(e),this.onClose(1006,"Could not start streaming",!1)})}}closeStream(){this.stream&&(this.stream.unbind_all(),this.stream.close(),this.stream=null)}}function es(r){var e=/([^\?]*)\/*(\??.*)/.exec(r);return{base:e[1],queryString:e[2]}}function ts(r,e){return r.base+"/"+e+"/xhr_send"}function qt(r){var e=r.indexOf("?")===-1?"?":"&";return r+e+"t="+ +new Date+"&n="+Yr++}function ns(r,e){var n=/(https?:\/\/)([^\/:]+)((\/|:)?.*)/.exec(r);return n[1]+e+n[3]}function Bt(r){return P.randomInt(r)}function rs(r){for(var e=[],n=0;n0&&r.onChunk(n.status,n.responseText);break;case 4:n.responseText&&n.responseText.length>0&&r.onChunk(n.status,n.responseText),r.emit("finished",n.status),r.close();break}},n},abortRequest:function(r){r.onreadystatechange=null,r.abort()}},ls=us,hs={createStreamingSocket(r){return this.createSocket(os,r)},createPollingSocket(r){return this.createSocket(cs,r)},createSocket(r,e){return new ss(r,e)},createXHR(r,e){return this.createRequest(ls,r,e)},createRequest(r,e,n){return new Qr(r,e,n)}},Ht=hs;Ht.createXDR=function(r,e){return this.createRequest(Kr,r,e)};var ds=Ht,fs={nextAuthCallbackID:1,auth_callbacks:{},ScriptReceivers:l,DependenciesReceivers:g,getDefaultStrategy:Xr,Transports:_r,transportConnectionInitializer:Wr,HTTPFactory:ds,TimelineTransport:ur,getXHRAPI(){return window.XMLHttpRequest},getWebSocketAPI(){return window.WebSocket||window.MozWebSocket},setup(r){window.Pusher=r;var e=()=>{this.onDocumentBody(r.ready)};window.JSON?e():C.load("json2",{},e)},getDocument(){return document},getProtocol(){return this.getDocument().location.protocol},getAuthorizers(){return{ajax:Q,jsonp:sr}},onDocumentBody(r){document.body?r():setTimeout(()=>{this.onDocumentBody(r)},0)},createJSONPRequest(r,e){return new or(r,e)},createScriptRequest(r){return new ir(r)},getLocalStorage(){try{return window.localStorage}catch{return}},createXHR(){return this.getXHRAPI()?this.createXMLHttpRequest():this.createMicrosoftXHR()},createXMLHttpRequest(){var r=this.getXHRAPI();return new r},createMicrosoftXHR(){return new ActiveXObject("Microsoft.XMLHTTP")},getNetwork(){return Tr},createWebSocket(r){var e=this.getWebSocketAPI();return new e(r)},createSocketRequest(r,e){if(this.isXHRSupported())return this.HTTPFactory.createXHR(r,e);if(this.isXDRSupported(e.indexOf("https:")===0))return this.HTTPFactory.createXDR(r,e);throw"Cross-origin HTTP requests are not supported"},isXHRSupported(){var r=this.getXHRAPI();return!!r&&new r().withCredentials!==void 0},isXDRSupported(r){var e=r?"https:":"http:",n=this.getProtocol();return!!window.XDomainRequest&&n===e},addUnloadListener(r){window.addEventListener!==void 0?window.addEventListener("unload",r,!1):window.attachEvent!==void 0&&window.attachEvent("onunload",r)},removeUnloadListener(r){window.addEventListener!==void 0?window.removeEventListener("unload",r,!1):window.detachEvent!==void 0&&window.detachEvent("onunload",r)},randomInt(r){return Math.floor(function(){return(window.crypto||window.msCrypto).getRandomValues(new Uint32Array(1))[0]/Math.pow(2,32)}()*r)}},P=fs,ot;(function(r){r[r.ERROR=3]="ERROR",r[r.INFO=6]="INFO",r[r.DEBUG=7]="DEBUG"})(ot||(ot={}));var Ue=ot;class ps{constructor(e,n,a){this.key=e,this.session=n,this.events=[],this.options=a||{},this.sent=0,this.uniqueID=0}log(e,n){e<=this.options.level&&(this.events.push(W({},n,{timestamp:H.now()})),this.options.limit&&this.events.length>this.options.limit&&this.events.shift())}error(e){this.log(Ue.ERROR,e)}info(e){this.log(Ue.INFO,e)}debug(e){this.log(Ue.DEBUG,e)}isEmpty(){return this.events.length===0}send(e,n){var a=W({session:this.session,bundle:this.sent+1,key:this.key,lib:"js",version:this.options.version,cluster:this.options.cluster,features:this.options.features,timeline:this.events},this.options.params);return this.events=[],e(a,(u,m)=>{u||this.sent++,n&&n(u,m)}),!0}generateUniqueID(){return this.uniqueID++,this.uniqueID}}class ms{constructor(e,n,a,u){this.name=e,this.priority=n,this.transport=a,this.options=u||{}}isSupported(){return this.transport.isSupported({useTLS:this.options.useTLS})}connect(e,n){if(this.isSupported()){if(this.priority{a||(I(),m?m.close():u.close())},forceMinPriority:q=>{a||this.priority{var n="socket_id="+encodeURIComponent(r.socketId);for(var a in e.params)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(e.params[a]);if(e.paramsProvider!=null){let u=e.paramsProvider();for(var a in u)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(u[a])}return n};var Ss=r=>{if(typeof P.getAuthorizers()[r.transport]>"u")throw`'${r.transport}' is not a recognized auth transport`;return(e,n)=>{const a=ws(e,r);P.getAuthorizers()[r.transport](P,a,r,v.UserAuthentication,n)}};const _s=(r,e)=>{var n="socket_id="+encodeURIComponent(r.socketId);n+="&channel_name="+encodeURIComponent(r.channelName);for(var a in e.params)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(e.params[a]);if(e.paramsProvider!=null){let u=e.paramsProvider();for(var a in u)n+="&"+encodeURIComponent(a)+"="+encodeURIComponent(u[a])}return n};var Cs=r=>{if(typeof P.getAuthorizers()[r.transport]>"u")throw`'${r.transport}' is not a recognized auth transport`;return(e,n)=>{const a=_s(e,r);P.getAuthorizers()[r.transport](P,a,r,v.ChannelAuthorization,n)}};const Ts=(r,e,n)=>{const a={authTransport:e.transport,authEndpoint:e.endpoint,auth:{params:e.params,headers:e.headers}};return(u,m)=>{const _=r.channel(u.channelName);n(_,a).authorize(u.socketId,m)}};function ks(r,e){let n={activityTimeout:r.activityTimeout||y.activityTimeout,cluster:r.cluster,httpPath:r.httpPath||y.httpPath,httpPort:r.httpPort||y.httpPort,httpsPort:r.httpsPort||y.httpsPort,pongTimeout:r.pongTimeout||y.pongTimeout,statsHost:r.statsHost||y.stats_host,unavailableTimeout:r.unavailableTimeout||y.unavailableTimeout,wsPath:r.wsPath||y.wsPath,wsPort:r.wsPort||y.wsPort,wssPort:r.wssPort||y.wssPort,enableStats:Ps(r),httpHost:Es(r),useTLS:Os(r),wsHost:xs(r),userAuthenticator:As(r),channelAuthorizer:Ns(r,e)};return"disabledTransports"in r&&(n.disabledTransports=r.disabledTransports),"enabledTransports"in r&&(n.enabledTransports=r.enabledTransports),"ignoreNullOrigin"in r&&(n.ignoreNullOrigin=r.ignoreNullOrigin),"timelineParams"in r&&(n.timelineParams=r.timelineParams),"nacl"in r&&(n.nacl=r.nacl),n}function Es(r){return r.httpHost?r.httpHost:r.cluster?`sockjs-${r.cluster}.pusher.com`:y.httpHost}function xs(r){return r.wsHost?r.wsHost:Rs(r.cluster)}function Rs(r){return`ws-${r}.pusher.com`}function Os(r){return P.getProtocol()==="https:"?!0:r.forceTLS!==!1}function Ps(r){return"enableStats"in r?r.enableStats:"disableStats"in r?!r.disableStats:!1}function As(r){const e=Object.assign(Object.assign({},y.userAuthentication),r.userAuthentication);return"customHandler"in e&&e.customHandler!=null?e.customHandler:Ss(e)}function Ls(r,e){let n;return"channelAuthorization"in r?n=Object.assign(Object.assign({},y.channelAuthorization),r.channelAuthorization):(n={transport:r.authTransport||y.authTransport,endpoint:r.authEndpoint||y.authEndpoint},"auth"in r&&("params"in r.auth&&(n.params=r.auth.params),"headers"in r.auth&&(n.headers=r.auth.headers)),"authorizer"in r&&(n.customHandler=Ts(e,n,r.authorizer))),n}function Ns(r,e){const n=Ls(r,e);return"customHandler"in n&&n.customHandler!=null?n.customHandler:Cs(n)}class Is extends oe{constructor(e){super(function(n,a){U.debug(`No callbacks on watchlist events for ${n}`)}),this.pusher=e,this.bindWatchlistInternalEvent()}handleEvent(e){e.data.events.forEach(n=>{this.emit(n.name,n)})}bindWatchlistInternalEvent(){this.pusher.connection.bind("message",e=>{var n=e.event;n==="pusher_internal:watchlist_events"&&this.handleEvent(e)})}}function js(){let r,e;return{promise:new Promise((a,u)=>{r=a,e=u}),resolve:r,reject:e}}var Us=js;class Ds extends oe{constructor(e){super(function(n,a){U.debug("No callbacks on user for "+n)}),this.signin_requested=!1,this.user_data=null,this.serverToUserChannel=null,this.signinDonePromise=null,this._signinDoneResolve=null,this._onAuthorize=(n,a)=>{if(n){U.warn(`Error during signin: ${n}`),this._cleanup();return}this.pusher.send_event("pusher:signin",{auth:a.auth,user_data:a.user_data})},this.pusher=e,this.pusher.connection.bind("state_change",({previous:n,current:a})=>{n!=="connected"&&a==="connected"&&this._signin(),n==="connected"&&a!=="connected"&&(this._cleanup(),this._newSigninPromiseIfNeeded())}),this.watchlist=new Is(e),this.pusher.connection.bind("message",n=>{var a=n.event;a==="pusher:signin_success"&&this._onSigninSuccess(n.data),this.serverToUserChannel&&this.serverToUserChannel.name===n.channel&&this.serverToUserChannel.handleEvent(n)})}signin(){this.signin_requested||(this.signin_requested=!0,this._signin())}_signin(){this.signin_requested&&(this._newSigninPromiseIfNeeded(),this.pusher.connection.state==="connected"&&this.pusher.config.userAuthenticator({socketId:this.pusher.connection.socket_id},this._onAuthorize))}_onSigninSuccess(e){try{this.user_data=JSON.parse(e.user_data)}catch{U.error(`Failed parsing user data after signin: ${e.user_data}`),this._cleanup();return}if(typeof this.user_data.id!="string"||this.user_data.id===""){U.error(`user_data doesn't contain an id. user_data: ${this.user_data}`),this._cleanup();return}this._signinDoneResolve(),this._subscribeChannels()}_subscribeChannels(){const e=n=>{n.subscriptionPending&&n.subscriptionCancelled?n.reinstateSubscription():!n.subscriptionPending&&this.pusher.connection.state==="connected"&&n.subscribe()};this.serverToUserChannel=new et(`#server-to-user-${this.user_data.id}`,this.pusher),this.serverToUserChannel.bind_global((n,a)=>{n.indexOf("pusher_internal:")===0||n.indexOf("pusher:")===0||this.emit(n,a)}),e(this.serverToUserChannel)}_cleanup(){this.user_data=null,this.serverToUserChannel&&(this.serverToUserChannel.unbind_all(),this.serverToUserChannel.disconnect(),this.serverToUserChannel=null),this.signin_requested&&this._signinDoneResolve()}_newSigninPromiseIfNeeded(){if(!this.signin_requested||this.signinDonePromise&&!this.signinDonePromise.done)return;const{promise:e,resolve:n}=Us();e.done=!1;const a=()=>{e.done=!0};e.then(a).catch(a),this.signinDonePromise=e,this._signinDoneResolve=n}}class M{static ready(){M.isReady=!0;for(var e=0,n=M.instances.length;eP.getDefaultStrategy(this.config,u,bs);this.connection=ae.createConnectionManager(this.key,{getStrategy:a,timeline:this.timeline,activityTimeout:this.config.activityTimeout,pongTimeout:this.config.pongTimeout,unavailableTimeout:this.config.unavailableTimeout,useTLS:!!this.config.useTLS}),this.connection.bind("connected",()=>{this.subscribeAll(),this.timelineSender&&this.timelineSender.send(this.connection.isUsingTLS())}),this.connection.bind("message",u=>{var m=u.event,_=m.indexOf("pusher_internal:")===0;if(u.channel){var k=this.channel(u.channel);k&&k.handleEvent(u)}_||this.global_emitter.emit(u.event,u.data)}),this.connection.bind("connecting",()=>{this.channels.disconnect()}),this.connection.bind("disconnected",()=>{this.channels.disconnect()}),this.connection.bind("error",u=>{U.warn(u)}),M.instances.push(this),this.timeline.info({instances:M.instances.length}),this.user=new Ds(this),M.isReady&&this.connect()}channel(e){return this.channels.find(e)}allChannels(){return this.channels.all()}connect(){if(this.connection.connect(),this.timelineSender&&!this.timelineSenderTimer){var e=this.connection.isUsingTLS(),n=this.timelineSender;this.timelineSenderTimer=new we(6e4,function(){n.send(e)})}}disconnect(){this.connection.disconnect(),this.timelineSenderTimer&&(this.timelineSenderTimer.ensureAborted(),this.timelineSenderTimer=null)}bind(e,n,a){return this.global_emitter.bind(e,n,a),this}unbind(e,n,a){return this.global_emitter.unbind(e,n,a),this}bind_global(e){return this.global_emitter.bind_global(e),this}unbind_global(e){return this.global_emitter.unbind_global(e),this}unbind_all(e){return this.global_emitter.unbind_all(),this}subscribeAll(){var e;for(e in this.channels.channels)this.channels.channels.hasOwnProperty(e)&&this.subscribe(e)}subscribe(e){var n=this.channels.add(e,this);return n.subscriptionPending&&n.subscriptionCancelled?n.reinstateSubscription():!n.subscriptionPending&&this.connection.state==="connected"&&n.subscribe(),n}unsubscribe(e){var n=this.channels.find(e);n&&n.subscriptionPending?n.cancelSubscription():(n=this.channels.remove(e),n&&n.subscribed&&n.unsubscribe())}send_event(e,n,a){return this.connection.send_event(e,n,a)}shouldUseTLS(){return this.config.useTLS}signin(){this.user.signin()}}M.instances=[],M.isReady=!1,M.logToConsole=!1,M.Runtime=P,M.ScriptReceivers=P.ScriptReceivers,M.DependenciesReceivers=P.DependenciesReceivers,M.auth_callbacks=P.auth_callbacks;var at=o.default=M;function Fs(r){if(r==null)throw"You must pass your app key when you instantiate Pusher."}P.setup(M)})])})})(ft)),ft.exports}var Io=No();const jo=Lo(Io);window.Pusher=jo;const ke=window.reverbConfig??{};window.Echo=new Ao({broadcaster:"reverb",key:ke.key??"fmoknd2swp3clsqfbdpg",wsHost:ke.host??"127.0.0.1",wsPort:ke.port??"8080"??80,wssPort:ke.port??"8080"??443,forceTLS:ke.forceTLS??!1,enabledTransports:["ws","wss"]});window.axios=F;window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest";const gn=document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");gn&&(window.axios.defaults.headers.common["X-CSRF-TOKEN"]=gn); diff --git a/public/build/manifest.json b/public/build/manifest.json index 65ac414e..917ee12b 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -9,7 +9,7 @@ ] }, "resources/js/app.js": { - "file": "assets/app-DAN06vjJ.js", + "file": "assets/app-DUTi2uSh.js", "name": "app", "src": "resources/js/app.js", "isEntry": true diff --git a/resources/views/layouts/_partials/header.blade.php b/resources/views/layouts/_partials/header.blade.php index f7dafcbf..4c72319f 100644 --- a/resources/views/layouts/_partials/header.blade.php +++ b/resources/views/layouts/_partials/header.blade.php @@ -30,6 +30,7 @@