feat: add WhatsappMessageResource and WhatsappWebhookResource
- Add WhatsappMessageResource to view message history - List view with filters by instance, direction, type, status - View page with message details and raw payload - Add WhatsappWebhookResource to view webhook logs - List view with filters by instance, event, processed status, errors - View page with webhook details, error info and payload - Update FilamentEvolutionPlugin with optional resource loading: - viewMessageHistory() - enable message history resource (default: false) - viewWebhookLogs() - enable webhook logs resource (default: false) - whatsappInstanceResource() - show/hide instances (default: true) - Add translations for new resources (en/pt_BR) - Update README with plugin options documentation - Fix Filament v4 infolist signature (Schema instead of Infolist)
This commit is contained in:
25
README.md
25
README.md
@@ -64,6 +64,31 @@ public function panel(Panel $panel): Panel
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Options
|
||||
|
||||
You can customize which resources are available in the panel:
|
||||
|
||||
```php
|
||||
FilamentEvolutionPlugin::make()
|
||||
->viewMessageHistory() // Enable message history resource
|
||||
->viewWebhookLogs() // Enable webhook logs resource
|
||||
```
|
||||
|
||||
| Method | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `whatsappInstanceResource(bool)` | `true` | Show/hide the WhatsApp Instances resource |
|
||||
| `viewMessageHistory(bool)` | `false` | Show/hide the Message History resource |
|
||||
| `viewWebhookLogs(bool)` | `false` | Show/hide the Webhook Logs resource |
|
||||
|
||||
#### Example: Full Configuration
|
||||
|
||||
```php
|
||||
FilamentEvolutionPlugin::make()
|
||||
->whatsappInstanceResource() // Show instances (default: true)
|
||||
->viewMessageHistory() // Show message history
|
||||
->viewWebhookLogs() // Show webhook logs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
29
resources/lang/en/message.php
Normal file
29
resources/lang/en/message.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'navigation_label' => 'Messages',
|
||||
'model_label' => 'Message',
|
||||
'plural_model_label' => 'Messages',
|
||||
|
||||
'sections' => [
|
||||
'message_info' => 'Message Information',
|
||||
'content' => 'Content',
|
||||
'timestamps' => 'Timestamps',
|
||||
'raw_payload' => 'Raw Payload',
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
'instance' => 'Instance',
|
||||
'direction' => 'Direction',
|
||||
'phone' => 'Phone',
|
||||
'type' => 'Type',
|
||||
'content' => 'Content',
|
||||
'status' => 'Status',
|
||||
'message_id' => 'Message ID',
|
||||
'media' => 'Media',
|
||||
'sent_at' => 'Sent At',
|
||||
'delivered_at' => 'Delivered At',
|
||||
'read_at' => 'Read At',
|
||||
'created_at' => 'Created At',
|
||||
],
|
||||
];
|
||||
29
resources/lang/en/webhook.php
Normal file
29
resources/lang/en/webhook.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'navigation_label' => 'Webhook Logs',
|
||||
'model_label' => 'Webhook Log',
|
||||
'plural_model_label' => 'Webhook Logs',
|
||||
|
||||
'sections' => [
|
||||
'webhook_info' => 'Webhook Information',
|
||||
'payload' => 'Payload',
|
||||
'error' => 'Error',
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
'instance' => 'Instance',
|
||||
'event' => 'Event',
|
||||
'processed' => 'Processed',
|
||||
'has_error' => 'Has Error',
|
||||
'error' => 'Error',
|
||||
'processing_time' => 'Processing Time',
|
||||
'created_at' => 'Created At',
|
||||
'updated_at' => 'Updated At',
|
||||
],
|
||||
|
||||
'status' => [
|
||||
'yes' => 'Yes',
|
||||
'no' => 'No',
|
||||
],
|
||||
];
|
||||
29
resources/lang/pt_BR/message.php
Normal file
29
resources/lang/pt_BR/message.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'navigation_label' => 'Mensagens',
|
||||
'model_label' => 'Mensagem',
|
||||
'plural_model_label' => 'Mensagens',
|
||||
|
||||
'sections' => [
|
||||
'message_info' => 'Informações da Mensagem',
|
||||
'content' => 'Conteúdo',
|
||||
'timestamps' => 'Datas',
|
||||
'raw_payload' => 'Payload Original',
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
'instance' => 'Instância',
|
||||
'direction' => 'Direção',
|
||||
'phone' => 'Telefone',
|
||||
'type' => 'Tipo',
|
||||
'content' => 'Conteúdo',
|
||||
'status' => 'Status',
|
||||
'message_id' => 'ID da Mensagem',
|
||||
'media' => 'Mídia',
|
||||
'sent_at' => 'Enviado em',
|
||||
'delivered_at' => 'Entregue em',
|
||||
'read_at' => 'Lido em',
|
||||
'created_at' => 'Criado em',
|
||||
],
|
||||
];
|
||||
29
resources/lang/pt_BR/webhook.php
Normal file
29
resources/lang/pt_BR/webhook.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'navigation_label' => 'Logs de Webhook',
|
||||
'model_label' => 'Log de Webhook',
|
||||
'plural_model_label' => 'Logs de Webhook',
|
||||
|
||||
'sections' => [
|
||||
'webhook_info' => 'Informações do Webhook',
|
||||
'payload' => 'Payload',
|
||||
'error' => 'Erro',
|
||||
],
|
||||
|
||||
'fields' => [
|
||||
'instance' => 'Instância',
|
||||
'event' => 'Evento',
|
||||
'processed' => 'Processado',
|
||||
'has_error' => 'Tem Erro',
|
||||
'error' => 'Erro',
|
||||
'processing_time' => 'Tempo de Processamento',
|
||||
'created_at' => 'Criado em',
|
||||
'updated_at' => 'Atualizado em',
|
||||
],
|
||||
|
||||
'status' => [
|
||||
'yes' => 'Sim',
|
||||
'no' => 'Não',
|
||||
],
|
||||
];
|
||||
@@ -52,7 +52,7 @@ class CreateWhatsappInstance extends CreateRecord
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title(__('filament-evolution::resource.messages.created'))
|
||||
->body('Instance saved locally. API sync failed: ' . $e->getMessage())
|
||||
->body('Instance saved locally. API sync failed: '.$e->getMessage())
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class ViewWhatsappInstance extends ViewRecord
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(__('filament-evolution::resource.fields.status') . ': ' . $status->getLabel())
|
||||
->title(__('filament-evolution::resource.fields.status').': '.$status->getLabel())
|
||||
->send();
|
||||
|
||||
} catch (EvolutionApiException $e) {
|
||||
|
||||
149
src/Filament/Resources/WhatsappMessageResource.php
Normal file
149
src/Filament/Resources/WhatsappMessageResource.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources;
|
||||
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use WallaceMartinss\FilamentEvolution\Enums\MessageDirectionEnum;
|
||||
use WallaceMartinss\FilamentEvolution\Enums\MessageStatusEnum;
|
||||
use WallaceMartinss\FilamentEvolution\Enums\MessageTypeEnum;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource\Pages;
|
||||
use WallaceMartinss\FilamentEvolution\Models\WhatsappMessage;
|
||||
|
||||
class WhatsappMessageResource extends Resource
|
||||
{
|
||||
protected static ?string $model = WhatsappMessage::class;
|
||||
|
||||
public static function getNavigationSort(): ?int
|
||||
{
|
||||
return config('filament-evolution.filament.navigation_sort', 100) + 1;
|
||||
}
|
||||
|
||||
public static function getNavigationIcon(): string|Heroicon|null
|
||||
{
|
||||
return Heroicon::ChatBubbleBottomCenterText;
|
||||
}
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return __('filament-evolution::resource.navigation_group');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament-evolution::message.navigation_label');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('filament-evolution::message.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('filament-evolution::message.plural_model_label');
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function canEdit($record): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function canDelete($record): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function form(Schema $form): Schema
|
||||
{
|
||||
return $form->schema([]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('instance.name')
|
||||
->label(__('filament-evolution::message.fields.instance'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('direction')
|
||||
->label(__('filament-evolution::message.fields.direction'))
|
||||
->badge()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('phone')
|
||||
->label(__('filament-evolution::message.fields.phone'))
|
||||
->searchable()
|
||||
->copyable(),
|
||||
|
||||
TextColumn::make('type')
|
||||
->label(__('filament-evolution::message.fields.type'))
|
||||
->badge()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('content')
|
||||
->label(__('filament-evolution::message.fields.content'))
|
||||
->limit(50)
|
||||
->wrap()
|
||||
->searchable(),
|
||||
|
||||
TextColumn::make('status')
|
||||
->label(__('filament-evolution::message.fields.status'))
|
||||
->badge()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('sent_at')
|
||||
->label(__('filament-evolution::message.fields.sent_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('filament-evolution::message.fields.created_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->defaultSort('created_at', 'desc')
|
||||
->filters([
|
||||
SelectFilter::make('instance')
|
||||
->relationship('instance', 'name')
|
||||
->label(__('filament-evolution::message.fields.instance'))
|
||||
->preload(),
|
||||
|
||||
SelectFilter::make('direction')
|
||||
->options(MessageDirectionEnum::class)
|
||||
->label(__('filament-evolution::message.fields.direction')),
|
||||
|
||||
SelectFilter::make('type')
|
||||
->options(MessageTypeEnum::class)
|
||||
->label(__('filament-evolution::message.fields.type')),
|
||||
|
||||
SelectFilter::make('status')
|
||||
->options(MessageStatusEnum::class)
|
||||
->label(__('filament-evolution::message.fields.status')),
|
||||
])
|
||||
->actions([])
|
||||
->bulkActions([]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListWhatsappMessages::route('/'),
|
||||
'view' => Pages\ViewWhatsappMessage::route('/{record}'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource\Pages;
|
||||
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource;
|
||||
|
||||
class ListWhatsappMessages extends ListRecords
|
||||
{
|
||||
protected static string $resource = WhatsappMessageResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource\Pages;
|
||||
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource;
|
||||
|
||||
class ViewWhatsappMessage extends ViewRecord
|
||||
{
|
||||
protected static string $resource = WhatsappMessageResource::class;
|
||||
|
||||
public function infolist(Schema $infolist): Schema
|
||||
{
|
||||
return $infolist
|
||||
->schema([
|
||||
Section::make(__('filament-evolution::message.sections.message_info'))
|
||||
->schema([
|
||||
TextEntry::make('instance.name')
|
||||
->label(__('filament-evolution::message.fields.instance')),
|
||||
|
||||
TextEntry::make('direction')
|
||||
->label(__('filament-evolution::message.fields.direction'))
|
||||
->badge(),
|
||||
|
||||
TextEntry::make('phone')
|
||||
->label(__('filament-evolution::message.fields.phone'))
|
||||
->copyable(),
|
||||
|
||||
TextEntry::make('type')
|
||||
->label(__('filament-evolution::message.fields.type'))
|
||||
->badge(),
|
||||
|
||||
TextEntry::make('status')
|
||||
->label(__('filament-evolution::message.fields.status'))
|
||||
->badge(),
|
||||
|
||||
TextEntry::make('message_id')
|
||||
->label(__('filament-evolution::message.fields.message_id'))
|
||||
->copyable(),
|
||||
])
|
||||
->columns(3),
|
||||
|
||||
Section::make(__('filament-evolution::message.sections.content'))
|
||||
->schema([
|
||||
TextEntry::make('content')
|
||||
->label(__('filament-evolution::message.fields.content'))
|
||||
->columnSpanFull()
|
||||
->prose(),
|
||||
|
||||
TextEntry::make('media')
|
||||
->label(__('filament-evolution::message.fields.media'))
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn ($state) => $state ? json_encode($state, JSON_PRETTY_PRINT) : '-')
|
||||
->visible(fn ($record) => ! empty($record->media)),
|
||||
]),
|
||||
|
||||
Section::make(__('filament-evolution::message.sections.timestamps'))
|
||||
->schema([
|
||||
TextEntry::make('sent_at')
|
||||
->label(__('filament-evolution::message.fields.sent_at'))
|
||||
->dateTime(),
|
||||
|
||||
TextEntry::make('delivered_at')
|
||||
->label(__('filament-evolution::message.fields.delivered_at'))
|
||||
->dateTime(),
|
||||
|
||||
TextEntry::make('read_at')
|
||||
->label(__('filament-evolution::message.fields.read_at'))
|
||||
->dateTime(),
|
||||
|
||||
TextEntry::make('created_at')
|
||||
->label(__('filament-evolution::message.fields.created_at'))
|
||||
->dateTime(),
|
||||
])
|
||||
->columns(4),
|
||||
|
||||
Section::make(__('filament-evolution::message.sections.raw_payload'))
|
||||
->schema([
|
||||
TextEntry::make('raw_payload')
|
||||
->label('')
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn ($state) => $state ? json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) : '-')
|
||||
->prose(),
|
||||
])
|
||||
->collapsible()
|
||||
->collapsed(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
149
src/Filament/Resources/WhatsappWebhookResource.php
Normal file
149
src/Filament/Resources/WhatsappWebhookResource.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources;
|
||||
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Filters\TernaryFilter;
|
||||
use Filament\Tables\Table;
|
||||
use WallaceMartinss\FilamentEvolution\Enums\WebhookEventEnum;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource\Pages;
|
||||
use WallaceMartinss\FilamentEvolution\Models\WhatsappWebhook;
|
||||
|
||||
class WhatsappWebhookResource extends Resource
|
||||
{
|
||||
protected static ?string $model = WhatsappWebhook::class;
|
||||
|
||||
public static function getNavigationSort(): ?int
|
||||
{
|
||||
return config('filament-evolution.filament.navigation_sort', 100) + 2;
|
||||
}
|
||||
|
||||
public static function getNavigationIcon(): string|Heroicon|null
|
||||
{
|
||||
return Heroicon::QueueList;
|
||||
}
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return __('filament-evolution::resource.navigation_group');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament-evolution::webhook.navigation_label');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('filament-evolution::webhook.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('filament-evolution::webhook.plural_model_label');
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function canEdit($record): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function canDelete($record): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function form(Schema $form): Schema
|
||||
{
|
||||
return $form->schema([]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('instance.name')
|
||||
->label(__('filament-evolution::webhook.fields.instance'))
|
||||
->searchable()
|
||||
->sortable()
|
||||
->placeholder('-'),
|
||||
|
||||
TextColumn::make('event')
|
||||
->label(__('filament-evolution::webhook.fields.event'))
|
||||
->badge()
|
||||
->sortable(),
|
||||
|
||||
IconColumn::make('processed')
|
||||
->label(__('filament-evolution::webhook.fields.processed'))
|
||||
->boolean()
|
||||
->trueIcon('heroicon-o-check-circle')
|
||||
->falseIcon('heroicon-o-clock')
|
||||
->trueColor('success')
|
||||
->falseColor('warning')
|
||||
->sortable(),
|
||||
|
||||
IconColumn::make('error')
|
||||
->label(__('filament-evolution::webhook.fields.has_error'))
|
||||
->boolean()
|
||||
->getStateUsing(fn ($record) => ! empty($record->error))
|
||||
->trueIcon('heroicon-o-x-circle')
|
||||
->falseIcon('heroicon-o-check')
|
||||
->trueColor('danger')
|
||||
->falseColor('success'),
|
||||
|
||||
TextColumn::make('processing_time_ms')
|
||||
->label(__('filament-evolution::webhook.fields.processing_time'))
|
||||
->suffix(' ms')
|
||||
->sortable()
|
||||
->placeholder('-'),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('filament-evolution::webhook.fields.created_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->defaultSort('created_at', 'desc')
|
||||
->filters([
|
||||
SelectFilter::make('instance')
|
||||
->relationship('instance', 'name')
|
||||
->label(__('filament-evolution::webhook.fields.instance'))
|
||||
->preload(),
|
||||
|
||||
SelectFilter::make('event')
|
||||
->options(WebhookEventEnum::class)
|
||||
->label(__('filament-evolution::webhook.fields.event')),
|
||||
|
||||
TernaryFilter::make('processed')
|
||||
->label(__('filament-evolution::webhook.fields.processed')),
|
||||
|
||||
TernaryFilter::make('has_error')
|
||||
->label(__('filament-evolution::webhook.fields.has_error'))
|
||||
->queries(
|
||||
true: fn ($query) => $query->whereNotNull('error'),
|
||||
false: fn ($query) => $query->whereNull('error'),
|
||||
),
|
||||
])
|
||||
->actions([])
|
||||
->bulkActions([]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListWhatsappWebhooks::route('/'),
|
||||
'view' => Pages\ViewWhatsappWebhook::route('/{record}'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource\Pages;
|
||||
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource;
|
||||
|
||||
class ListWhatsappWebhooks extends ListRecords
|
||||
{
|
||||
protected static string $resource = WhatsappWebhookResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource\Pages;
|
||||
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource;
|
||||
|
||||
class ViewWhatsappWebhook extends ViewRecord
|
||||
{
|
||||
protected static string $resource = WhatsappWebhookResource::class;
|
||||
|
||||
public function infolist(Schema $infolist): Schema
|
||||
{
|
||||
return $infolist
|
||||
->schema([
|
||||
Section::make(__('filament-evolution::webhook.sections.webhook_info'))
|
||||
->schema([
|
||||
TextEntry::make('instance.name')
|
||||
->label(__('filament-evolution::webhook.fields.instance'))
|
||||
->placeholder('-'),
|
||||
|
||||
TextEntry::make('event')
|
||||
->label(__('filament-evolution::webhook.fields.event'))
|
||||
->badge(),
|
||||
|
||||
TextEntry::make('processed')
|
||||
->label(__('filament-evolution::webhook.fields.processed'))
|
||||
->badge()
|
||||
->formatStateUsing(fn ($state) => $state ? __('filament-evolution::webhook.status.yes') : __('filament-evolution::webhook.status.no'))
|
||||
->color(fn ($state) => $state ? 'success' : 'warning'),
|
||||
|
||||
TextEntry::make('processing_time_ms')
|
||||
->label(__('filament-evolution::webhook.fields.processing_time'))
|
||||
->suffix(' ms')
|
||||
->placeholder('-'),
|
||||
|
||||
TextEntry::make('created_at')
|
||||
->label(__('filament-evolution::webhook.fields.created_at'))
|
||||
->dateTime(),
|
||||
|
||||
TextEntry::make('updated_at')
|
||||
->label(__('filament-evolution::webhook.fields.updated_at'))
|
||||
->dateTime(),
|
||||
])
|
||||
->columns(3),
|
||||
|
||||
Section::make(__('filament-evolution::webhook.sections.error'))
|
||||
->schema([
|
||||
TextEntry::make('error')
|
||||
->label('')
|
||||
->columnSpanFull()
|
||||
->prose()
|
||||
->color('danger'),
|
||||
])
|
||||
->visible(fn ($record) => ! empty($record->error)),
|
||||
|
||||
Section::make(__('filament-evolution::webhook.sections.payload'))
|
||||
->schema([
|
||||
TextEntry::make('payload')
|
||||
->label('')
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn ($state) => $state ? json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) : '-')
|
||||
->prose(),
|
||||
])
|
||||
->collapsible(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,17 @@ namespace WallaceMartinss\FilamentEvolution;
|
||||
use Filament\Contracts\Plugin;
|
||||
use Filament\Panel;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappInstanceResource;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappMessageResource;
|
||||
use WallaceMartinss\FilamentEvolution\Filament\Resources\WhatsappWebhookResource;
|
||||
|
||||
class FilamentEvolutionPlugin implements Plugin
|
||||
{
|
||||
protected bool $hasWhatsappInstanceResource = true;
|
||||
|
||||
protected bool $hasWhatsappMessageResource = false;
|
||||
|
||||
protected bool $hasWhatsappWebhookResource = false;
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
return app(static::class);
|
||||
@@ -32,10 +38,22 @@ class FilamentEvolutionPlugin implements Plugin
|
||||
|
||||
public function register(Panel $panel): void
|
||||
{
|
||||
$resources = [];
|
||||
|
||||
if ($this->hasWhatsappInstanceResource) {
|
||||
$panel->resources([
|
||||
WhatsappInstanceResource::class,
|
||||
]);
|
||||
$resources[] = WhatsappInstanceResource::class;
|
||||
}
|
||||
|
||||
if ($this->hasWhatsappMessageResource) {
|
||||
$resources[] = WhatsappMessageResource::class;
|
||||
}
|
||||
|
||||
if ($this->hasWhatsappWebhookResource) {
|
||||
$resources[] = WhatsappWebhookResource::class;
|
||||
}
|
||||
|
||||
if (! empty($resources)) {
|
||||
$panel->resources($resources);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +62,33 @@ class FilamentEvolutionPlugin implements Plugin
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the WhatsApp Instance resource.
|
||||
*/
|
||||
public function whatsappInstanceResource(bool $condition = true): static
|
||||
{
|
||||
$this->hasWhatsappInstanceResource = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the Message History resource to view all messages.
|
||||
*/
|
||||
public function viewMessageHistory(bool $condition = true): static
|
||||
{
|
||||
$this->hasWhatsappMessageResource = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the Webhook Logs resource to view all webhook events.
|
||||
*/
|
||||
public function viewWebhookLogs(bool $condition = true): static
|
||||
{
|
||||
$this->hasWhatsappWebhookResource = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class FilamentEvolutionServiceProvider extends PackageServiceProvider
|
||||
public function packageRegistered(): void
|
||||
{
|
||||
$this->app->singleton(EvolutionClient::class, function () {
|
||||
return new EvolutionClient();
|
||||
return new EvolutionClient;
|
||||
});
|
||||
|
||||
$this->app->singleton(WhatsappService::class, function ($app) {
|
||||
|
||||
@@ -51,7 +51,7 @@ class WhatsappWebhook extends Model
|
||||
return ! empty($this->error);
|
||||
}
|
||||
|
||||
public function markAsProcessed(int $processingTimeMs = null): void
|
||||
public function markAsProcessed(?int $processingTimeMs = null): void
|
||||
{
|
||||
$this->update([
|
||||
'processed' => true,
|
||||
@@ -59,7 +59,7 @@ class WhatsappWebhook extends Model
|
||||
]);
|
||||
}
|
||||
|
||||
public function markAsFailed(string $error, int $processingTimeMs = null): void
|
||||
public function markAsFailed(string $error, ?int $processingTimeMs = null): void
|
||||
{
|
||||
$this->update([
|
||||
'processed' => false,
|
||||
|
||||
@@ -184,7 +184,7 @@ class EvolutionClient
|
||||
*/
|
||||
public function fetchInstance(string $instanceName): array
|
||||
{
|
||||
return $this->request('GET', "/instance/fetchInstances", [
|
||||
return $this->request('GET', '/instance/fetchInstances', [
|
||||
'instanceName' => $instanceName,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class WhatsappService
|
||||
|
||||
// If Brazilian number without country code, add it
|
||||
if (strlen($number) === 10 || strlen($number) === 11) {
|
||||
$number = '55' . $number;
|
||||
$number = '55'.$number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
@@ -68,18 +68,19 @@ class WhatsappService
|
||||
|
||||
// For local files, convert to base64 (Evolution API expects raw base64 without data: prefix)
|
||||
$storage = Storage::disk($disk);
|
||||
|
||||
|
||||
if (! $storage->exists($path)) {
|
||||
throw new EvolutionApiException("File not found: {$path}");
|
||||
}
|
||||
|
||||
$contents = $storage->get($path);
|
||||
|
||||
|
||||
return base64_encode($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public URL for a file (supports local and S3).
|
||||
*
|
||||
* @deprecated Use getMediaContent() instead for Evolution API
|
||||
*/
|
||||
public function getFileUrl(string $path, ?string $disk = null): string
|
||||
|
||||
@@ -11,21 +11,21 @@ class EvolutionClientTest extends TestCase
|
||||
{
|
||||
public function test_client_can_be_instantiated(): void
|
||||
{
|
||||
$client = new EvolutionClient();
|
||||
$client = new EvolutionClient;
|
||||
|
||||
$this->assertInstanceOf(EvolutionClient::class, $client);
|
||||
}
|
||||
|
||||
public function test_client_is_configured_when_has_url_and_key(): void
|
||||
{
|
||||
$client = new EvolutionClient();
|
||||
$client = new EvolutionClient;
|
||||
|
||||
$this->assertTrue($client->isConfigured());
|
||||
}
|
||||
|
||||
public function test_client_returns_configured_base_url(): void
|
||||
{
|
||||
$client = new EvolutionClient();
|
||||
$client = new EvolutionClient;
|
||||
|
||||
$this->assertSame('https://api.evolution.test', $client->getBaseUrl());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user