Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
128 lines
5.0 KiB
PHP
128 lines
5.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Providers\Filament;
|
|
|
|
use App\Filament\Pages\Auth\PireonLogin;
|
|
use App\Filament\Widgets\ContentOverview;
|
|
use App\Filament\Widgets\WhatNeedsYou;
|
|
use App\Http\Middleware\SetLocale;
|
|
use Filament\Http\Middleware\Authenticate;
|
|
use Filament\Http\Middleware\AuthenticateSession;
|
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
|
use Filament\Navigation\NavigationItem;
|
|
use Filament\Pages\Dashboard;
|
|
use Filament\Panel;
|
|
use Filament\PanelProvider;
|
|
use Filament\Support\Colors\Color;
|
|
use Filament\Support\Icons\Heroicon;
|
|
use Filament\View\PanelsRenderHook;
|
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
|
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
|
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
|
use Illuminate\Session\Middleware\StartSession;
|
|
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
|
|
|
class AdminPanelProvider extends PanelProvider
|
|
{
|
|
public function panel(Panel $panel): Panel
|
|
{
|
|
return $panel
|
|
->default()
|
|
->id('admin')
|
|
->path('admin')
|
|
->brandName(config('app.name'))
|
|
|
|
// No password form: this page only offers a button that starts the
|
|
// OAuth flow at the identity server.
|
|
->login(PireonLogin::class)
|
|
|
|
->colors([
|
|
'primary' => Color::Indigo,
|
|
])
|
|
->font('Inter')
|
|
->sidebarCollapsibleOnDesktop()
|
|
|
|
// The bell. Submissions from the public site arrive here, because
|
|
// an inbox nobody is told about is an inbox nobody reads.
|
|
->databaseNotifications()
|
|
->databaseNotificationsPolling('60s')
|
|
|
|
->navigationGroups([
|
|
__('panel.groups.collective'),
|
|
__('panel.groups.content'),
|
|
__('panel.groups.administration'),
|
|
])
|
|
|
|
// The API documentation lives outside the panel — it is a plain
|
|
// page, readable without an account — but this is where somebody
|
|
// goes looking for it. A new tab, so opening the reference does
|
|
// not throw away whatever was half-edited in the panel.
|
|
->navigationItems([
|
|
// The label is a closure, not a resolved string: the panel is
|
|
// configured once per process, so a translation evaluated here
|
|
// would freeze in whatever language happened to be active then.
|
|
NavigationItem::make(fn (): string => __('docs.title'))
|
|
->url(fn (): string => route('docs.api'), shouldOpenInNewTab: true)
|
|
->icon(Heroicon::OutlinedCodeBracketSquare)
|
|
->group(__('panel.groups.administration'))
|
|
->sort(95),
|
|
])
|
|
|
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
|
|
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
|
|
->pages([
|
|
Dashboard::class,
|
|
])
|
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
|
->widgets([
|
|
// First on the dashboard: what is waiting for somebody, rather
|
|
// than counters that never change.
|
|
WhatNeedsYou::class,
|
|
ContentOverview::class,
|
|
])
|
|
|
|
// Language switcher, rendered next to the user menu.
|
|
// First render hook to fire on every page, panel and login alike:
|
|
// the one place that has to be true regardless of which screen
|
|
// somebody lands on first.
|
|
->renderHook(
|
|
PanelsRenderHook::BODY_START,
|
|
fn (): string => view('filament.partials.environment-banner')->render(),
|
|
)
|
|
|
|
->renderHook(
|
|
PanelsRenderHook::USER_MENU_BEFORE,
|
|
fn (): string => view('filament.partials.language-switcher')->render(),
|
|
)
|
|
|
|
// And once more under the sign-in form, which is the one screen
|
|
// with no user whose preference we could read.
|
|
->renderHook(
|
|
PanelsRenderHook::AUTH_LOGIN_FORM_AFTER,
|
|
fn (): string => view('filament.partials.login-footer')->render(),
|
|
)
|
|
|
|
->middleware([
|
|
EncryptCookies::class,
|
|
AddQueuedCookiesToResponse::class,
|
|
StartSession::class,
|
|
AuthenticateSession::class,
|
|
ShareErrorsFromSession::class,
|
|
PreventRequestForgery::class,
|
|
SubstituteBindings::class,
|
|
DisableBladeIconComponents::class,
|
|
DispatchServingFilamentEvent::class,
|
|
// Filament panels do not run the "web" middleware group, so
|
|
// locale negotiation has to be listed here explicitly.
|
|
SetLocale::class,
|
|
])
|
|
->authMiddleware([
|
|
Authenticate::class,
|
|
]);
|
|
}
|
|
}
|