getLivewire(); return $form ->schema([ Forms\Components\Section::make() ->schema([ TextInput::make('name') ->label(__('filament.resource.api_provider.form.name')) ->required() ->maxLength(255), Toggle::make('enabled') ->label(__('filament.resource.api_provider.form.enabled')) ->default(true), TextInput::make('api_url') ->label(__('filament.resource.api_provider.form.api_url')) ->required() ->url() ->maxLength(255), TextInput::make('username') ->label(__('filament.resource.api_provider.form.username')) ->nullable() ->maxLength(255), TextInput::make('password') ->label(__('filament.resource.api_provider.form.password')) ->password() ->nullable() ->maxLength(255), TextInput::make('token') ->label(__('filament.resource.api_provider.form.token')) ->nullable() ->maxLength(255), Select::make('plugin') ->options($plugins) ->nullable() ->label(__('filament.resource.api_provider.form.plugin')), Actions::make([ Action::make('test_connection') ->label(__('filament.resource.api_provider.action.test_connection')) ->icon(function (\Livewire\Component $livewire) { return match ($livewire->testResultState) { 'success' => 'heroicon-o-check-circle', 'failed' => 'heroicon-o-x-circle', default => 'heroicon-o-link', }; }) ->color(function (\Livewire\Component $livewire) { return match ($livewire->testResultState) { 'success' => 'success', 'failed' => 'danger', default => 'gray', }; }) ->action(function (array $data, Forms\Components\Component $component, \Livewire\Component $livewire) { $formData = $component->getLivewire()->form->getState(); $apiUrl = str_replace('127.0.0.1', 'localhost', $formData['api_url'] ?? null); $pluginName = $formData['plugin'] ?? null; $token = $formData['token'] ?? null; if (!$pluginName) { Notification::make() ->title(__('filament.resource.api_provider.notification.connection_failed')) ->body('Please select a plugin first.') ->danger() ->send(); $component->getLivewire()->dispatch('testConnectionFinished', result: 'failed'); return; } try { // Create a dummy ApiProvider model for the test $dummyApiProvider = new \App\Models\ApiProvider(); $dummyApiProvider->api_url = $apiUrl; $dummyApiProvider->token = $token; // Load the specific plugin using the PluginLoader $pluginInstance = PluginLoader::getPlugin($pluginName, $dummyApiProvider); // Call the testConnection method of the plugin $testResult = $pluginInstance->testConnection([ 'api_url' => $apiUrl, 'token' => $token, 'username' => $formData['username'] ?? null, 'password' => $formData['password'] ?? null, ]); if ($testResult) { Notification::make() ->title(__('filament.resource.api_provider.notification.connection_successful')) ->success() ->send(); $component->getLivewire()->dispatch('testConnectionFinished', result: 'success'); } else { Notification::make() ->title(__('filament.resource.api_provider.notification.connection_failed')) ->body('Plugin reported connection failed. Check logs for details.') ->danger() ->send(); $component->getLivewire()->dispatch('testConnectionFinished', result: 'failed'); } } catch (\Exception $e) { Log::error('Plugin test connection failed: An unexpected error occurred.', [ 'api_url' => $apiUrl, 'plugin' => $pluginName, 'error_message' => $e->getMessage(), ]); Notification::make() ->title(__('filament.resource.api_provider.notification.connection_failed')) ->body('An unexpected error occurred during plugin test: ' . $e->getMessage()) ->danger() ->send(); $component->getLivewire()->dispatch('testConnectionFinished', result: 'failed'); } }), ]), ]) ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name')->label(__('filament.resource.api_provider.table.name'))->searchable()->sortable(), IconColumn::make('enabled') ->label(__('filament.resource.api_provider.table.enabled')) ->boolean(), TextColumn::make('api_url')->label(__('filament.resource.api_provider.table.api_url'))->searchable(), TextColumn::make('plugin')->label(__('filament.resource.api_provider.table.plugin'))->searchable()->sortable(), ]) ->filters([ // ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), Tables\Actions\BulkAction::make('enable') ->label(__('filament.resource.api_provider.action.enable_selected')) ->icon('heroicon-o-check-circle') ->action(function (\Illuminate\Support\Collection $records) { $records->each->update(['enabled' => true]); }), Tables\Actions\BulkAction::make('disable') ->label(__('filament.resource.api_provider.action.disable_selected')) ->icon('heroicon-o-x-circle') ->action(function (\Illuminate\Support\Collection $records) { $records->each->update(['enabled' => false]); }), ]), ]) ->emptyStateActions([ Tables\Actions\CreateAction::make(), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListApiProviders::route('/'), 'create' => Pages\CreateApiProvider::route('/create'), 'edit' => Pages\EditApiProvider::route('/{record}/edit'), ]; } protected static function getAvailablePlugins(): array { $plugins = []; $path = app_path('Api/Plugins'); $files = File::files($path); foreach ($files as $file) { $filename = $file->getFilenameWithoutExtension(); if (in_array($filename, ['ApiPluginInterface', 'PluginLoader'])) { continue; } $class = "App\Api\Plugins\\" . $filename; if (class_exists($class) && in_array(ApiPluginInterface::class, class_implements($class))) { // Do not instantiate here, just get identifier and name if possible statically or by convention // For now, we'll use filename as identifier and name $plugins[$filename] = $filename; } } return $plugins; } }