Skip to main content

Filament Admin Panels

Profile PS3's dual-panel Filament 4.x architecture for admin and user interfaces.

Panel Architecture Overview

Profile PS3 uses Filament's multi-panel capability to provide two distinct application interfaces:

Admin Panel (/admin)

  • Purpose: Full administrative control and system management
  • Access: Administrators and Super Admins only
  • Features: All CRUD resources, user management, material databases, system settings

App Panel (/app)

  • Purpose: User-facing project management and calculators
  • Access: All approved registered users
  • Features: Personal projects, calculators, profile management

Admin Panel Configuration

Location: app/Providers/Filament/AdminPanelProvider.php

Core Features

public function panel(Panel $panel): Panel
{
return $panel
->default() // Default panel
->id('admin')
->path('admin')
->login(Login::class) // Custom login page
->passwordReset() // Password reset flow
->emailVerification() // Email verification
->databaseNotifications() // Database-backed notifications
->sidebarCollapsibleOnDesktop() // Collapsible sidebar
->profile(Profile::class, false) // Custom profile page
->multiFactorAuthentication( // MFA with app authenticator
AppAuthentication::make()
->recoverable() // Recovery codes
)
->viteTheme('resources/css/filament/admin/theme.css')
->colors([
'primary' => '#9fcf68', // Brand green
])
->brandLogo(asset('images/ps3_final.png'))
->brandName('Profile PS3 Admin')
->favicon(asset('images/favicon.png'))
->globalSearchKeyBindings(['command+k', 'ctrl+k'])
->globalSearchFieldKeyBindingSuffix();
}

Resource Discovery

Auto-discovers resources from multiple directories:

->discoverResources(
in: app_path('Filament/Resources'),
for: 'App\\Filament\\Resources'
)
->discoverResources(
in: app_path('Filament/Shared/Resources'),
for: 'App\\Filament\\Shared\\Resources'
)
->discoverPages(
in: app_path('Filament/Pages'),
for: 'App\\Filament\\Pages'
)
->discoverPages(
in: app_path('Filament/Shared/Pages'),
for: 'App\\Filament\\Shared\\Pages'
)

Installed Plugins

->plugins([
FilamentSpatieRolesPermissionsPlugin::make(),
ImpersonatePlugin::make(),
])

FilamentSpatieRolesPermissionsPlugin:

  • Role management UI
  • Permission management UI
  • User role assignment
  • Shield policies integration

ImpersonatePlugin:

  • Admin user impersonation for support
  • Audit trail of impersonation sessions
  • One-click return to admin account
->navigationGroups([
NavigationGroup::make('Projects')
->icon('heroicon-o-folder')
->collapsed(false),

NavigationGroup::make('Materials')
->icon('heroicon-o-cube')
->collapsed(true),

NavigationGroup::make('Geographic Data')
->icon('heroicon-o-globe-alt')
->collapsed(true),

NavigationGroup::make('Users & Access')
->icon('heroicon-o-users')
->collapsed(true),

NavigationGroup::make('System')
->icon('heroicon-o-cog')
->collapsed(true),
])

Middleware Stack

->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
\Hasnayeen\Themes\Http\Middleware\SetTheme::class,
])
->authMiddleware([
Authenticate::class,
'verified',
])

Admin Panel Resources

User Management Resources

UserResource

Path: app/Filament/Resources/UserResource.php

Features:

  • User CRUD with company details
  • Account approval workflow (pending → approved/denied)
  • Role assignment
  • MFA status display
  • Impersonation action
  • Login count tracking
  • Regional manager assignment

Form Fields:

Forms\Components\TextInput::make('fname')->required(),
Forms\Components\TextInput::make('lname')->required(),
Forms\Components\TextInput::make('email')->email()->unique()->required(),
Forms\Components\TextInput::make('company'),
Forms\Components\TextInput::make('phone')->tel(),
Forms\Components\Textarea::make('address'),
Forms\Components\Select::make('state_province_id')
->relationship('state', 'name')
->searchable(),
Forms\Components\TextInput::make('city'),
Forms\Components\TextInput::make('zip')->maxLength(10),
Forms\Components\Select::make('country')
->options(Country::pluck('name', 'code')),
Forms\Components\Select::make('approved')
->options([
'pending' => 'Pending',
'approved' => 'Approved',
'denied' => 'Denied',
])->required(),
Forms\Components\Toggle::make('view_comparables')
->label('Can View Material Comparisons'),
Forms\Components\Toggle::make('get_product_updates')
->label('Receive Product Updates'),

Table Columns:

Tables\Columns\TextColumn::make('fname')->searchable(),
Tables\Columns\TextColumn::make('lname')->searchable(),
Tables\Columns\TextColumn::make('email')->searchable(),
Tables\Columns\TextColumn::make('company')->searchable(),
Tables\Columns\BadgeColumn::make('approved')
->colors([
'warning' => 'pending',
'success' => 'approved',
'danger' => 'denied',
]),
Tables\Columns\IconColumn::make('hasEnabledTwoFactorAuthentication')
->label('MFA')
->boolean(),
Tables\Columns\TextColumn::make('login_count')->sortable(),
Tables\Columns\TextColumn::make('created_at')->dateTime()->sortable(),

Actions:

  • Impersonate user
  • Approve/deny account
  • Reset password
  • Disable MFA
  • Send notification email

RoleResource & PermissionResource

Provided by: althinect/filament-spatie-roles-permissions

Features:

  • Role CRUD (super-admin, admin, manager, user)
  • Permission CRUD and assignment
  • Guard-aware permissions
  • User role assignment

Project Management Resources

ProjectResource

Path: app/Filament/Resources/ProjectResource.php

Features:

  • Project CRUD with location
  • Relationship management (slopes, channels, soil tests, attachments)
  • Project type/stage/energy classification
  • GPS coordinate mapping
  • Project status management (active/archived/deleted)
  • Auto-generated project numbers

Relation Managers:

  • SlopesRelationManager — Slope protection calculations
  • ChannelsRelationManager — Channel protection calculations
  • SoilTestsRelationManager — Soil analysis data
  • AttachmentsRelationManager — File uploads with S3 integration

SlopeResource

Path: app/Filament/Resources/SlopeResource.php

Features:

  • Slope calculation CRUD
  • Dimension inputs (length, width, gradient)
  • Soil type and vegetation selection
  • Recommended product calculations
  • Project association

ChannelResource

Path: app/Filament/Resources/ChannelResource.php

Features:

  • Channel calculation CRUD
  • Dimensions and flow rate inputs
  • Soil and vegetation parameters
  • Product recommendations
  • Project association

Material Database Resources

EcbResource (Erosion Control Blankets)

Path: app/Filament/Resources/EcbResource.php

Features:

  • ECB product CRUD
  • Specifications (material, thickness, weight, longevity)
  • Coverage rates and pricing
  • Performance ratings (slope, flow velocity)

HecpResource (Hydraulically Applied Erosion Control Products)

Path: app/Filament/Resources/HecpResource.php

Features:

  • HECP product CRUD
  • Application rates and coverage
  • Material specifications
  • Pricing data

TrmResource (Turf Reinforcement Mats)

Path: app/Filament/Resources/TrmResource.php

Features:

  • TRM product CRUD
  • Tensile strength and load ratings
  • Coverage and pricing
  • Material specifications

Geographic Resources

CountryResource

Path: app/Filament/Resources/CountryResource.php

Features:

  • Country CRUD with ISO codes
  • State relationship management

StateResource

Path: app/Filament/Resources/StateResource.php

Features:

  • State/province CRUD
  • Country association
  • City relationship management

CityResource

Path: app/Filament/Resources/CityResource.php

Features:

  • City CRUD
  • State association
  • Project count display

SalesRegionResource

Path: app/Filament/Resources/SalesRegionResource.php

Features:

  • Sales territory CRUD
  • State selection (multi-select)
  • Regional manager assignment
  • User notification preferences

System Resources

BdoExportResource

Path: app/Filament/Resources/BdoExportResource.php

Features:

  • Export history display
  • Export status tracking (pending/success/failed)
  • Record count and error messages
  • Manual export triggering
  • Checkpoint management

App Panel Configuration

Location: app/Providers/Filament/AppPanelProvider.php

Core Features

public function panel(Panel $panel): Panel
{
return $panel
->id('app')
->path('app')
->login()
->registration() // User registration
->passwordReset()
->emailVerification()
->profile(EditProfile::class)
->colors([
'primary' => '#9fcf68',
])
->brandLogo(asset('images/ps3_final.png'))
->brandName('Profile PS3')
->favicon(asset('images/favicon.png'))
->sidebarCollapsibleOnDesktop()
->discoverResources(
in: app_path('Filament/App/Resources'),
for: 'App\\Filament\\App\\Resources'
)
->discoverPages(
in: app_path('Filament/App/Pages'),
for: 'App\\Filament\\App\\Pages'
);
}

App Panel Resources

MyProjectsResource

Path: app/Filament/App/Resources/MyProjectsResource.php

Features:

  • User's own projects only (scoped by UserProjectScope)
  • Project CRUD
  • Slope, channel, soil test management
  • Attachment uploads

ApplicationCalculatorPage

Path: app/Filament/App/Pages/ApplicationCalculatorPage.php

Features:

  • Application rate calculator form
  • Area and dimension inputs
  • Soil type and vegetation selection
  • Product recommendation engine
  • Cost estimation
  • Save calculation to project

ProganicsCalculatorPage

Path: app/Filament/App/Pages/ProganicsCalculatorPage.php

Features:

  • ProGanics BSM calculator form
  • Biotic Soil Media calculations
  • Application rate recommendations
  • Save calculation to project

Custom Filament Components

Custom Actions

ApproveUserAction:

Tables\Actions\Action::make('approve')
->icon('heroicon-o-check-circle')
->color('success')
->requiresConfirmation()
->action(fn (User $record) => $record->update(['approved' => 'approved']))
->visible(fn (User $record) => $record->approved === 'pending')
->after(function (User $record) {
// Send approval email
Notification::make()
->title('Account Approved')
->success()
->send();
});

ImpersonateAction:

Tables\Actions\Action::make('impersonate')
->icon('heroicon-o-user')
->url(fn (User $record) => route('filament.admin.impersonate', $record))
->visible(fn () => auth()->user()->hasRole('super-admin'));

Custom Widgets

ProjectStatsWidget:

  • Total projects count
  • Active vs archived breakdown
  • Projects by type chart
  • Recent activity timeline

MaterialStatsWidget:

  • ECB/HECP/TRM product counts
  • Coverage rate averages
  • Price range displays

UserStatsWidget:

  • Total users
  • Pending approval count
  • MFA adoption rate
  • Login activity

Filament Forms Best Practices

Repeater Fields:

Forms\Components\Repeater::make('slopes')
->relationship()
->schema([
Forms\Components\TextInput::make('slopeDescription')->required(),
Forms\Components\TextInput::make('slopeLength')->numeric()->required(),
Forms\Components\TextInput::make('slopeWidth')->numeric()->required(),
Forms\Components\TextInput::make('slopeGradient')->numeric()->required(),
])
->collapsible()
->itemLabel(fn (array $state): ?string => $state['slopeDescription'] ?? null)
->defaultItems(0)
->addActionLabel('Add Slope');

Dependent Selects:

Forms\Components\Select::make('country_id')
->relationship('country', 'name')
->reactive()
->afterStateUpdated(fn (callable $set) => $set('state_id', null)),

Forms\Components\Select::make('state_id')
->relationship('state', 'name')
->options(function (callable $get) {
$countryId = $get('country_id');
if (!$countryId) return [];
return State::where('country_id', $countryId)->pluck('name', 'id');
})
->reactive()
->afterStateUpdated(fn (callable $set) => $set('city', null)),

Performance Optimization

Eager Loading:

// In Resource table() method
->modifyQueryUsing(fn ($query) => $query->with([
'user', 'state', 'country', 'type', 'stage'
]))

Caching:

// Cache expensive queries
Forms\Components\Select::make('state_id')
->options(Cache::remember('states', 3600, function () {
return State::pluck('name', 'id');
}))

Custom Themes

Admin Theme: resources/css/filament/admin/theme.css

Tailwind Configuration:

// tailwind.config.js for admin panel
export default {
content: [
'./resources/views/filament/admin/**/*.blade.php',
'./app/Filament/**/*.php',
],
theme: {
extend: {
colors: {
primary: {
50: '#f5faf0',
100: '#e8f4d8',
500: '#9fcf68',
700: '#6fa43f',
900: '#3e5a22',
},
},
},
},
};

Testing Filament Panels

Feature Tests:

use function Pest\Livewire\livewire;

test('admin can access user resource', function () {
$admin = User::factory()->create();
$admin->assignRole('admin');

$this->actingAs($admin);

livewire(UserResource\Pages\ListUsers::class)
->assertSuccessful();
});

test('user cannot access admin panel', function () {
$user = User::factory()->create(['approved' => 'approved']);

$this->actingAs($user)
->get('/admin')
->assertForbidden();
});