activeTab = $this->normalizeTab($this->activeTab); $this->loadData(); } public function updatedActiveTab(): void { $this->activeTab = $this->normalizeTab($this->activeTab); $this->loadData(); $this->resetTable(); } protected function getForms(): array { return ['tabsForm']; } public function tabsForm(Schema $schema): Schema { return $schema->schema([ Tabs::make(__('PostgreSQL Sections')) ->contained() ->livewireProperty('activeTab') ->tabs([ 'databases' => Tab::make(__('Databases')) ->icon('heroicon-o-circle-stack') ->schema([ View::make('filament.jabali.pages.postgresql-tab-table'), ]), 'users' => Tab::make(__('Users')) ->icon('heroicon-o-users') ->schema([ View::make('filament.jabali.pages.postgresql-tab-table'), ]), ]), ]); } protected function normalizeTab(string $tab): string { return in_array($tab, ['databases', 'users'], true) ? $tab : 'databases'; } protected function getAgent(): AgentClient { if ($this->agent === null) { $this->agent = new AgentClient; } return $this->agent; } protected function getUsername(): string { return Auth::user()->username; } protected function loadData(): void { if ($this->activeTab === 'users') { $this->loadUsers(); } else { $this->loadDatabases(); } } protected function loadDatabases(): void { try { $result = $this->getAgent()->postgresListDatabases($this->getUsername()); $this->databases = $result['databases'] ?? []; } catch (Exception) { $this->databases = []; } } protected function loadUsers(): void { try { $result = $this->getAgent()->postgresListUsers($this->getUsername()); $this->users = $result['users'] ?? []; } catch (Exception) { $this->users = []; } } protected function getUserOptions(): array { if (empty($this->users)) { $this->loadUsers(); } $options = []; foreach ($this->users as $user) { $options[$user['username']] = $user['username']; } return $options; } public function table(Table $table): Table { if ($this->activeTab === 'users') { return $table ->records(fn () => $this->users) ->columns([ TextColumn::make('username') ->label(__('User')) ->searchable(), ]) ->recordActions([ Action::make('delete') ->label(__('Delete')) ->icon('heroicon-o-trash') ->color('danger') ->requiresConfirmation() ->action(function (array $record): void { $result = $this->getAgent()->postgresDeleteUser($this->getUsername(), $record['username']); if ($result['success'] ?? false) { Notification::make()->title(__('User deleted'))->success()->send(); $this->loadUsers(); $this->resetTable(); return; } Notification::make()->title(__('Deletion failed'))->body($result['error'] ?? '')->danger()->send(); }), ]) ->emptyStateHeading(__('No PostgreSQL users')) ->emptyStateDescription(__('Create a PostgreSQL user to manage databases')); } return $table ->records(fn () => $this->databases) ->columns([ TextColumn::make('name') ->label(__('Database')) ->searchable(), TextColumn::make('size_bytes') ->label(__('Size')) ->formatStateUsing(fn ($state) => $this->formatBytes((int) $state)) ->color('gray'), ]) ->recordActions([ Action::make('delete') ->label(__('Delete')) ->icon('heroicon-o-trash') ->color('danger') ->requiresConfirmation() ->action(function (array $record): void { $result = $this->getAgent()->postgresDeleteDatabase($this->getUsername(), $record['name']); if ($result['success'] ?? false) { Notification::make()->title(__('Database deleted'))->success()->send(); $this->loadDatabases(); $this->resetTable(); return; } Notification::make()->title(__('Deletion failed'))->body($result['error'] ?? '')->danger()->send(); }), ]) ->emptyStateHeading(__('No PostgreSQL databases')) ->emptyStateDescription(__('Create a PostgreSQL database to get started')); } protected function getHeaderActions(): array { return [ Action::make('createDatabase') ->label(__('Create Database')) ->icon('heroicon-o-circle-stack') ->color('primary') ->visible(fn () => $this->activeTab === 'databases') ->form([ TextInput::make('database') ->label(__('Database Name')) ->helperText(__('Use a name like :prefix_db', ['prefix' => $this->getUsername()])) ->required(), Select::make('owner') ->label(__('Owner User')) ->options($this->getUserOptions()) ->required(), ]) ->action(function (array $data): void { $result = $this->getAgent()->postgresCreateDatabase( $this->getUsername(), $data['database'], $data['owner'] ); if ($result['success'] ?? false) { Notification::make()->title(__('Database created'))->success()->send(); $this->loadDatabases(); $this->resetTable(); return; } Notification::make()->title(__('Creation failed'))->body($result['error'] ?? '')->danger()->send(); }), Action::make('createUser') ->label(__('Create User')) ->icon('heroicon-o-user-plus') ->color('primary') ->visible(fn () => $this->activeTab === 'users') ->form([ TextInput::make('db_user') ->label(__('Username')) ->helperText(__('Use a name like :prefix_user', ['prefix' => $this->getUsername()])) ->required(), TextInput::make('password') ->label(__('Password')) ->password() ->revealable() ->default(fn () => Str::random(16)) ->required(), ]) ->action(function (array $data): void { $result = $this->getAgent()->postgresCreateUser( $this->getUsername(), $data['db_user'], $data['password'] ); if ($result['success'] ?? false) { Notification::make()->title(__('User created'))->success()->send(); $this->loadUsers(); $this->resetTable(); return; } Notification::make()->title(__('Creation failed'))->body($result['error'] ?? '')->danger()->send(); }), ]; } protected function formatBytes(int $bytes, int $precision = 2): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, $precision).' '.$units[$pow]; } }