Files
filament-whatsapp-conector/resources/views/livewire/qr-code-display.blade.php
Wallace Martins 3bf496e8a9 feat: initial release v0.1.0
- Evolution API v2 integration with full HTTP client
- WhatsApp instance management (Create, Connect, Delete, LogOut, Restart)
- Real-time QR Code display with Alpine.js countdown timer
- Pairing code support for WhatsApp Web linking
- Webhook endpoint for receiving Evolution API events
- Complete instance settings (reject calls, always online, read messages, etc.)
- Filament v4 Resource with modal QR Code after instance creation
- Table actions for Connect, View, and Edit
- Status badges with Filament's native components
- Full translations support (English and Portuguese)
- Native Filament multi-tenancy support
- DTOs with Spatie Laravel Data for type safety
- Laravel Events for extensibility
- Background job processing for webhooks and messages
- Comprehensive configuration file
2025-12-07 10:14:40 -03:00

163 lines
7.4 KiB
PHP

<div
x-data="{
countdown: {{ $qrCodeTtl }},
ttl: {{ $qrCodeTtl }},
timer: null,
init() {
this.startCountdown();
// Listen for QR code refresh to reset countdown
Livewire.on('qrCodeRefreshed', () => {
this.countdown = this.ttl;
this.startCountdown();
});
},
startCountdown() {
if (this.timer) clearInterval(this.timer);
this.timer = setInterval(() => {
if (this.countdown > 0) {
this.countdown--;
} else {
clearInterval(this.timer);
$wire.fetchQrCode();
}
}, 1000);
}
}"
x-init="init()"
@instance-connected.window="clearInterval(timer)"
wire:poll.5s="checkConnection"
class="w-full"
>
{{-- Loading State --}}
@if($isLoading)
<div class="flex flex-col items-center justify-center py-16">
<div class="relative">
<div class="w-16 h-16 border-4 border-gray-200 dark:border-gray-700 rounded-full"></div>
<div class="absolute top-0 left-0 w-16 h-16 border-4 border-primary-500 border-t-transparent rounded-full animate-spin"></div>
</div>
<p class="mt-6 text-sm font-medium text-gray-500 dark:text-gray-400">
{{ __('filament-evolution::qrcode.loading') }}
</p>
</div>
@elseif($isConnected)
{{-- Connected State --}}
<div class="flex flex-col items-center justify-center py-12">
<div class="relative">
<div class="w-20 h-20 bg-green-500 rounded-full flex items-center justify-center">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"></path>
</svg>
</div>
</div>
<h3 class="mt-6 text-lg font-semibold text-gray-900 dark:text-white">
{{ __('filament-evolution::qrcode.connected_title') }}
</h3>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400 text-center">
{{ __('filament-evolution::qrcode.connected_description') }}
</p>
</div>
@elseif($error)
{{-- Error State --}}
<div class="flex flex-col items-center justify-center py-12">
<div class="w-20 h-20 bg-red-500 rounded-full flex items-center justify-center">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
<h3 class="mt-6 text-lg font-semibold text-gray-900 dark:text-white">
{{ __('filament-evolution::qrcode.error_title') }}
</h3>
<p class="mt-2 text-sm text-red-500 text-center">
{{ $error }}
</p>
<button
wire:click="fetchQrCode"
class="mt-4 px-4 py-2 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700"
>
{{ __('filament-evolution::qrcode.try_again') }}
</button>
</div>
@elseif($qrCode)
{{-- QR Code Display --}}
<div class="flex flex-col items-center">
{{-- Status --}}
<div class="flex items-center gap-2 mb-4">
<span class="relative flex h-3 w-3">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-amber-500"></span>
</span>
<span class="text-sm font-medium text-gray-600 dark:text-gray-300">
{{ __('filament-evolution::qrcode.waiting_scan') }}
</span>
</div>
{{-- QR Code --}}
<div class="bg-white p-3 rounded-xl shadow-lg">
<img src="{{ $qrCode }}" alt="QR Code" class="w-56 h-56" />
</div>
{{-- Countdown Timer with Alpine --}}
<div class="mt-3 flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>
{{ __('filament-evolution::qrcode.expires_in') }}:
<strong class="text-gray-700 dark:text-gray-200" x-text="countdown + 's'"></strong>
</span>
</div>
{{-- Instructions --}}
<p class="mt-4 text-sm text-gray-600 dark:text-gray-400 text-center max-w-xs">
{{ __('filament-evolution::qrcode.scan_instructions') }}
</p>
{{-- Pairing Code --}}
@if($pairingCode)
<div class="mt-4 text-center">
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">
{{ __('filament-evolution::qrcode.or_use_code') }}
</p>
<div class="inline-flex items-center gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-800 rounded-lg">
<span class="font-mono text-xl font-bold tracking-widest text-gray-900 dark:text-white">
{{ $pairingCode }}
</span>
</div>
</div>
@endif
{{-- Refresh Button --}}
<button
wire:click="refreshQrCode"
wire:loading.attr="disabled"
class="mt-4 flex items-center gap-2 text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700"
>
<svg wire:loading.class="animate-spin" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
{{ __('filament-evolution::qrcode.refresh') }}
</button>
</div>
@else
{{-- No QR Code - Generate --}}
<div class="flex flex-col items-center justify-center py-12">
<button
wire:click="fetchQrCode"
wire:loading.attr="disabled"
class="px-6 py-3 bg-primary-600 text-white text-sm font-semibold rounded-lg hover:bg-primary-700 flex items-center gap-2"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"></path>
</svg>
{{ __('filament-evolution::qrcode.generate') }}
</button>
</div>
@endif
</div>