diff --git a/app/Filament/Resources/PageResource.php b/app/Filament/Admin/Resources/Resources/PageResource.php similarity index 100% rename from app/Filament/Resources/PageResource.php rename to app/Filament/Admin/Resources/Resources/PageResource.php diff --git a/app/Filament/App/Pages/.gitignore b/app/Filament/App/Pages/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/app/Filament/App/Resources/.gitignore b/app/Filament/App/Resources/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/app/Filament/App/Widgets/.gitignore b/app/Filament/App/Widgets/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 24a3ef1..ae5660e 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -2,10 +2,20 @@ namespace App\Providers\Filament; +use App\Filament\Admin\Pages; +use App\Filament\Admin\Pages\EditProfile; +use App\Http\Middleware\TeamsPermission; +use App\Listeners\CreatePersonalTeam; +use App\Listeners\SwitchTeam; +use App\Models\Team; +use Filament\Events\Auth\Registered; +use Filament\Events\TenantSet; +use Filament\Facades\Filament; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DispatchServingFilamentEvent; -use Filament\Pages; +use Filament\Navigation\MenuItem; +use Filament\Pages as FilamentPage; use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Colors\Color; @@ -16,29 +26,45 @@ use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Session\Middleware\AuthenticateSession; use Illuminate\Session\Middleware\StartSession; +use Illuminate\Support\Facades\Event; use Illuminate\View\Middleware\ShareErrorsFromSession; +use Laravel\Fortify\Fortify; +use Laravel\Fortify\Http\Controllers\AuthenticatedSessionController; +use Laravel\Fortify\Http\Controllers\RegisteredUserController; +use Laravel\Jetstream\Features; +use Laravel\Jetstream\Jetstream; class AdminPanelProvider extends PanelProvider { public function panel(Panel $panel): Panel { - return $panel + $panel + ->default() ->id('admin') ->path('admin') - ->login() - ->registration() + ->login([AuthenticatedSessionController::class, 'create']) + ->registration([RegisteredUserController::class, 'create']) ->passwordReset() ->emailVerification() - ->profile() + ->viteTheme('resources/css/Filament/Admin/theme.css') ->colors([ - 'primary' => Color::Amber, + 'primary' => Color::Gray, + ]) + ->userMenuItems([ + MenuItem::make() + ->label('Profile') + ->icon('heroicon-o-user-circle') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(EditProfile::getUrl()) + : url($panel->getPath())), ]) ->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources') ->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages') ->pages([ - Pages\Dashboard::class, + FilamentPage\Dashboard::class, + Pages\EditProfile::class, ]) - ->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets') + ->discoverWidgets(in: app_path('Filament/Admin/Widgets/Home'), for: 'App\\Filament\\Admin\\Widgets\\Home') ->widgets([ Widgets\AccountWidget::class, Widgets\FilamentInfoWidget::class, @@ -56,9 +82,72 @@ public function panel(Panel $panel): Panel ]) ->authMiddleware([ Authenticate::class, + TeamsPermission::class, ]) ->plugins([ \BezhanSalleh\FilamentShield\FilamentShieldPlugin::make() ]); + + if (Features::hasApiFeatures()) { + $panel->userMenuItems([ + MenuItem::make() + ->label('API Tokens') + ->icon('heroicon-o-key') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(Pages\ApiTokenManagerPage::getUrl()) + : url($panel->getPath())), + ]); + } + + if (Features::hasTeamFeatures()) { + $panel + ->tenant(Team::class, ownershipRelationship: 'team') + ->tenantRegistration(Pages\CreateTeam::class) + ->tenantProfile(Pages\EditTeam::class) + ->userMenuItems([ + MenuItem::make() + ->label('Team Settings') + ->icon('heroicon-o-cog-6-tooth') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(Pages\EditTeam::getUrl()) + : url($panel->getPath())), + ]); + } + + return $panel; + } + + public function boot() + { + /** + * Disable Fortify routes. + */ + Fortify::$registersRoutes = false; + + /** + * Disable Jetstream routes. + */ + Jetstream::$registersRoutes = false; + + /** + * Listen and create personal team for new accounts. + */ + Event::listen( + Registered::class, + CreatePersonalTeam::class, + ); + + /** + * Listen and switch team if tenant was changed. + */ + Event::listen( + TenantSet::class, + SwitchTeam::class, + ); + } + + public function shouldRegisterMenuItem(): bool + { + return true; //auth()->user()?->hasVerifiedEmail() && Filament::hasTenancy() && Filament::getTenant(); } } diff --git a/app/Providers/Filament/AppPanelProvider.php b/app/Providers/Filament/AppPanelProvider.php new file mode 100644 index 0000000..f59fbda --- /dev/null +++ b/app/Providers/Filament/AppPanelProvider.php @@ -0,0 +1,153 @@ +default() + ->id('app') + ->path('app') + ->login([AuthenticatedSessionController::class, 'create']) + ->registration([RegisteredUserController::class, 'create']) + ->passwordReset() + ->emailVerification() + ->viteTheme('resources/css/Filament/App/admin/theme.css') + ->colors([ + 'primary' => Color::Gray, + ]) + ->userMenuItems([ + MenuItem::make() + ->label('Profile') + ->icon('heroicon-o-user-circle') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(EditProfile::getUrl()) + : url($panel->getPath())), + ]) + ->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources') + ->discoverPages(in: app_path('Filament/App/Pages'), for: 'App\\Filament\\App\\Pages') + ->pages([ + FilamentPage\Dashboard::class, + Pages\EditProfile::class, + ]) + ->discoverWidgets(in: app_path('Filament/App/Widgets/Home'), for: 'App\\Filament\\App\\Widgets\\Home') + ->widgets([ + Widgets\AccountWidget::class, + Widgets\FilamentInfoWidget::class, + ]) + ->middleware([ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + AuthenticateSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + DisableBladeIconComponents::class, + DispatchServingFilamentEvent::class, + ]) + ->authMiddleware([ + Authenticate::class, + TeamsPermission::class, + ]) + ->plugins([ + \BezhanSalleh\FilamentShield\FilamentShieldPlugin::make() + ]); + + if (Features::hasApiFeatures()) { + $panel->userMenuItems([ + MenuItem::make() + ->label('API Tokens') + ->icon('heroicon-o-key') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(Pages\ApiTokenManagerPage::getUrl()) + : url($panel->getPath())), + ]); + } + + if (Features::hasTeamFeatures()) { + $panel + ->tenant(Team::class, ownershipRelationship: 'team') + ->tenantRegistration(Pages\CreateTeam::class) + ->tenantProfile(Pages\EditTeam::class) + ->userMenuItems([ + MenuItem::make() + ->label('Team Settings') + ->icon('heroicon-o-cog-6-tooth') + ->url(fn () => $this->shouldRegisterMenuItem() + ? url(Pages\EditTeam::getUrl()) + : url($panel->getPath())), + ]); + } + + return $panel; + } + + public function boot() + { + /** + * Disable Fortify routes. + */ + Fortify::$registersRoutes = false; + + /** + * Disable Jetstream routes. + */ + Jetstream::$registersRoutes = false; + + /** + * Listen and create personal team for new accounts. + */ + Event::listen( + Registered::class, + CreatePersonalTeam::class, + ); + + /** + * Listen and switch team if tenant was changed. + */ + Event::listen( + TenantSet::class, + SwitchTeam::class, + ); + } + + public function shouldRegisterMenuItem(): bool + { + return true; //auth()->user()?->hasVerifiedEmail() && Filament::hasTenancy() && Filament::getTenant(); + } +}