Use Filament tabs for user panel navs

This commit is contained in:
root
2026-02-01 02:49:54 +02:00
parent b2500da7de
commit 6b78df5ed2
11 changed files with 83 additions and 165 deletions

1
.gitignore vendored
View File

@@ -21,3 +21,4 @@ yarn-error.log
CLAUDE.md
/jabali-panel_*.deb
/jabali-deps_*.deb
.git-credentials

View File

@@ -1 +1 @@
VERSION=0.9-rc39
VERSION=0.9-rc40

View File

@@ -29,7 +29,8 @@ use Filament\Pages\Page;
use Filament\Schemas\Components\Actions as FormActions;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\View;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
@@ -115,7 +116,18 @@ class Backups extends Page implements HasActions, HasForms, HasTable
->description(__('Create and manage backups of your account data. Backups include your websites, databases, and mailboxes. You can restore from any backup at any time.'))
->icon('heroicon-o-information-circle')
->iconColor('info'),
View::make('filament.jabali.components.backup-tabs-nav'),
Tabs::make(__('Backup Sections'))
->livewireProperty('activeTab')
->tabs([
'local' => Tab::make(__('My Backups'))
->icon('heroicon-o-archive-box'),
'remote' => Tab::make(__('Server Backups'))
->icon('heroicon-o-cloud'),
'destinations' => Tab::make(__('SFTP Destinations'))
->icon('heroicon-o-server-stack'),
'history' => Tab::make(__('Restore History'))
->icon('heroicon-o-arrow-path'),
]),
]);
}

View File

@@ -29,7 +29,8 @@ use Filament\Infolists\Components\TextEntry;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\View;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
@@ -134,7 +135,22 @@ class Email extends Page implements HasActions, HasForms, HasTable
public function emailForm(Schema $schema): Schema
{
return $schema->schema([
View::make('filament.jabali.components.email-tabs-nav'),
Tabs::make(__('Email Sections'))
->livewireProperty('activeTab')
->tabs([
'mailboxes' => Tab::make(__('Mailboxes'))
->icon('heroicon-o-envelope'),
'forwarders' => Tab::make(__('Forwarders'))
->icon('heroicon-o-arrow-right'),
'autoresponders' => Tab::make(__('Autoresponders'))
->icon('heroicon-o-clock'),
'catchall' => Tab::make(__('Catch-All'))
->icon('heroicon-o-inbox-stack'),
'logs' => Tab::make(__('Logs'))
->icon('heroicon-o-document-text'),
'spam' => Tab::make(__('Spam Settings'))
->icon('heroicon-o-shield-check'),
]),
]);
}

View File

@@ -14,6 +14,9 @@ use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Url;
@@ -90,6 +93,27 @@ class Logs extends Page implements HasActions, HasForms
$this->activeTab = $this->normalizeTab($tab);
}
protected function getForms(): array
{
return ['tabsForm'];
}
public function tabsForm(Schema $schema): Schema
{
return $schema->schema([
Tabs::make(__('Log Sections'))
->livewireProperty('activeTab')
->tabs([
'logs' => Tab::make(__('Logs'))
->icon('heroicon-o-document-text'),
'stats' => Tab::make(__('Statistics'))
->icon('heroicon-o-chart-bar'),
'activity' => Tab::make(__('Activity Log'))
->icon('heroicon-o-clipboard-document-list'),
]),
]);
}
protected function normalizeTab(?string $tab): string
{
return match ($tab) {

View File

@@ -16,6 +16,9 @@ use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
@@ -71,6 +74,25 @@ class PostgreSQL extends Page implements HasActions, HasForms, HasTable
$this->resetTable();
}
protected function getForms(): array
{
return ['tabsForm'];
}
public function tabsForm(Schema $schema): Schema
{
return $schema->schema([
Tabs::make(__('PostgreSQL Sections'))
->livewireProperty('activeTab')
->tabs([
'databases' => Tab::make(__('Databases'))
->icon('heroicon-o-circle-stack'),
'users' => Tab::make(__('Users'))
->icon('heroicon-o-users'),
]),
]);
}
protected function normalizeTab(string $tab): string
{
return in_array($tab, ['databases', 'users'], true) ? $tab : 'databases';

View File

@@ -16,7 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
JABALI_VERSION="$(sed -n 's/^VERSION=//p' "$SCRIPT_DIR/VERSION")"
fi
JABALI_VERSION="${JABALI_VERSION:-0.9-rc39}"
JABALI_VERSION="${JABALI_VERSION:-0.9-rc40}"
# Colors
RED='\033[0;31m'

View File

@@ -1,40 +0,0 @@
@php
$tabs = [
'local' => ['label' => __('My Backups'), 'icon' => 'heroicon-o-archive-box'],
'remote' => ['label' => __('Server Backups'), 'icon' => 'heroicon-o-cloud'],
'destinations' => ['label' => __('SFTP Destinations'), 'icon' => 'heroicon-o-server-stack'],
'history' => ['label' => __('Restore History'), 'icon' => 'heroicon-o-arrow-path'],
];
@endphp
<nav class="fi-tabs flex max-w-full gap-x-1 overflow-x-auto mx-auto rounded-xl bg-white p-2 shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10" role="tablist">
@foreach($tabs as $key => $tab)
<button
type="button"
role="tab"
aria-selected="{{ $this->activeTab === $key ? 'true' : 'false' }}"
wire:click="setTab('{{ $key }}')"
@class([
'fi-tabs-item group flex items-center gap-x-2 rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75',
'fi-active bg-gray-50 dark:bg-white/5' => $this->activeTab === $key,
'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5' => $this->activeTab !== $key,
])
>
<x-filament::icon
:icon="$tab['icon']"
@class([
'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75',
'text-primary-600 dark:text-primary-400' => $this->activeTab === $key,
'text-gray-400 group-hover:text-gray-500 group-focus-visible:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-400 dark:group-focus-visible:text-gray-400' => $this->activeTab !== $key,
])
/>
<span @class([
'fi-tabs-item-label transition duration-75',
'text-primary-600 dark:text-primary-400' => $this->activeTab === $key,
'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200' => $this->activeTab !== $key,
])>
{{ $tab['label'] }}
</span>
</button>
@endforeach
</nav>

View File

@@ -1,42 +0,0 @@
@php
$tabs = [
'mailboxes' => ['label' => __('Mailboxes'), 'icon' => 'heroicon-o-envelope'],
'forwarders' => ['label' => __('Forwarders'), 'icon' => 'heroicon-o-arrow-right'],
'autoresponders' => ['label' => __('Autoresponders'), 'icon' => 'heroicon-o-clock'],
'catchall' => ['label' => __('Catch-All'), 'icon' => 'heroicon-o-inbox-stack'],
'logs' => ['label' => __('Logs'), 'icon' => 'heroicon-o-document-text'],
'spam' => ['label' => __('Spam Settings'), 'icon' => 'heroicon-o-shield-check'],
];
@endphp
<nav class="fi-tabs flex max-w-full gap-x-1 overflow-x-auto mx-auto rounded-xl bg-white p-2 shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10" role="tablist">
@foreach($tabs as $key => $tab)
<button
type="button"
role="tab"
aria-selected="{{ $this->activeTab === $key ? 'true' : 'false' }}"
wire:click="setTab('{{ $key }}')"
@class([
'fi-tabs-item group flex items-center gap-x-2 rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75',
'fi-active bg-gray-50 dark:bg-white/5' => $this->activeTab === $key,
'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5' => $this->activeTab !== $key,
])
>
<x-filament::icon
:icon="$tab['icon']"
@class([
'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75',
'text-primary-600 dark:text-primary-400' => $this->activeTab === $key,
'text-gray-400 group-hover:text-gray-500 group-focus-visible:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-400 dark:group-focus-visible:text-gray-400' => $this->activeTab !== $key,
])
/>
<span @class([
'fi-tabs-item-label transition duration-75',
'text-primary-600 dark:text-primary-400' => $this->activeTab === $key,
'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200' => $this->activeTab !== $key,
])>
{{ $tab['label'] }}
</span>
</button>
@endforeach
</nav>

View File

@@ -1,43 +1,5 @@
<x-filament-panels::page>
@php
$tabs = [
'logs' => ['label' => __('Logs'), 'icon' => 'heroicon-o-document-text'],
'stats' => ['label' => __('Statistics'), 'icon' => 'heroicon-o-chart-bar'],
'activity' => ['label' => __('Activity Log'), 'icon' => 'heroicon-o-clipboard-document-list'],
];
@endphp
<nav class="fi-tabs flex max-w-full gap-x-1 overflow-x-auto mx-auto rounded-xl bg-white p-2 shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10" role="tablist">
@foreach($tabs as $key => $tab)
<button
type="button"
role="tab"
aria-selected="{{ $activeTab === $key ? 'true' : 'false' }}"
wire:click="setTab('{{ $key }}')"
@class([
'fi-tabs-item group flex items-center gap-x-2 rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75',
'fi-active bg-gray-50 dark:bg-white/5' => $activeTab === $key,
'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5' => $activeTab !== $key,
])
>
<x-filament::icon
:icon="$tab['icon']"
@class([
'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75',
'text-primary-600 dark:text-primary-400' => $activeTab === $key,
'text-gray-400 group-hover:text-gray-500 group-focus-visible:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-400 dark:group-focus-visible:text-gray-400' => $activeTab !== $key,
])
/>
<span @class([
'fi-tabs-item-label transition duration-75',
'text-primary-600 dark:text-primary-400' => $activeTab === $key,
'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200' => $activeTab !== $key,
])>
{{ $tab['label'] }}
</span>
</button>
@endforeach
</nav>
{{ $this->tabsForm }}
@if(in_array($activeTab, ['logs', 'stats'], true))
@if(count($this->getDomainOptions()) > 0)

View File

@@ -1,42 +1,5 @@
<x-filament-panels::page>
@php
$tabs = [
'databases' => ['label' => __('Databases'), 'icon' => 'heroicon-o-circle-stack'],
'users' => ['label' => __('Users'), 'icon' => 'heroicon-o-users'],
];
@endphp
<nav class="fi-tabs flex max-w-full gap-x-1 overflow-x-auto mx-auto rounded-xl bg-white p-2 shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10" role="tablist">
@foreach($tabs as $key => $tab)
<button
type="button"
role="tab"
aria-selected="{{ $activeTab === $key ? 'true' : 'false' }}"
wire:click="$set('activeTab', '{{ $key }}')"
@class([
'fi-tabs-item group flex items-center gap-x-2 rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75',
'fi-active bg-gray-50 dark:bg-white/5' => $activeTab === $key,
'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5' => $activeTab !== $key,
])
>
<x-filament::icon
:icon="$tab['icon']"
@class([
'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75',
'text-primary-600 dark:text-primary-400' => $activeTab === $key,
'text-gray-400 group-hover:text-gray-500 group-focus-visible:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-400 dark:group-focus-visible:text-gray-400' => $activeTab !== $key,
])
/>
<span @class([
'fi-tabs-item-label transition duration-75',
'text-primary-600 dark:text-primary-400' => $activeTab === $key,
'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200' => $activeTab !== $key,
])>
{{ $tab['label'] }}
</span>
</button>
@endforeach
</nav>
{{ $this->tabsForm }}
<div class="mt-4">
{{ $this->table }}