Initial commit: Pireon Admin — headless admin panel and public API
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>
This commit is contained in:
127
app/Providers/Filament/AdminPanelProvider.php
Normal file
127
app/Providers/Filament/AdminPanelProvider.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user