PHPackages                             codenzia/filament-panel-base - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. codenzia/filament-panel-base

ActiveLibrary[Admin Panels](/categories/admin)

codenzia/filament-panel-base
============================

Multi-panel architecture support with shared branding, dynamic colors, localization middleware, user moderation, and country/currency components for Filament v4.

01PHP

Since Feb 28Pushed 2mo agoCompare

[ Source](https://github.com/Codenzia/filament-panel-base)[ Packagist](https://packagist.org/packages/codenzia/filament-panel-base)[ RSS](/packages/codenzia-filament-panel-base/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Filament Panel Base
===================

[](#filament-panel-base)

Multi-panel architecture support for [Filament v4](https://filamentphp.com) with shared branding, dynamic theme colors, CSS custom property injection, localization middleware, user moderation, and country/currency components.

Installation
------------

[](#installation)

```
composer require codenzia/filament-panel-base
```

Publish the config file:

```
php artisan vendor:publish --tag="filament-panel-base-config"
```

Setup
-----

[](#setup)

### 1. Register the plugin

[](#1-register-the-plugin)

```
use Codenzia\FilamentPanelBase\FilamentPanelBasePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentPanelBasePlugin::make()
                ->settingsUsing(fn () => app(\App\Settings\GeneralSettings::class)),
        ]);
}
```

### 2. Extend BasePanelProvider

[](#2-extend-basepanelprovider)

```
use Codenzia\FilamentPanelBase\Providers\BasePanelProvider;

class AdminPanelProvider extends BasePanelProvider
{
    public function panel(Panel $panel): Panel
    {
        $this->configureSharedSettings(
            $panel->default()->id('admin')->path('admin')->login()
        );

        return $panel
            ->middleware($this->getSharedMiddleware())
            ->authMiddleware([Authenticate::class]);
    }
}
```

### 3. Register middleware

[](#3-register-middleware)

In `bootstrap/app.php`:

```
use Codenzia\FilamentPanelBase\Middleware\SetCountry;
use Codenzia\FilamentPanelBase\Middleware\SetCurrency;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web([
        SetCountry::class,
        SetCurrency::class,
    ]);
})
```

### 4. Custom Theme (Tailwind v4)

[](#4-custom-theme-tailwind-v4)

If your panel uses a custom Filament theme with `->viteTheme()`, you must add `@source` directives so Tailwind compiles the classes used by this package (panel badge, visit website button, blade components, etc.):

```
/* resources/css/filament/admin/theme.css */
@source '../../../../vendor/codenzia/filament-panel-base/src/**/*.php';
@source '../../../../vendor/codenzia/filament-panel-base/resources/views/**/*.blade.php';
```

Then rebuild your assets with `npm run build`.

### 5. Frontend Theme (Optional)

[](#5-frontend-theme-optional)

The package includes a built-in theme system with 17 color presets and runtime CSS variable injection. This enables Tailwind utility classes like `bg-brand-500` that update dynamically when the theme changes — no rebuild required.

**Step 1: Add components to your layout ``:**

```

@vite(['resources/css/app.css', 'resources/js/app.js'])
```

`` prevents a flash of unstyled content by applying the dark class before first paint. `` injects CSS custom properties (`--site-brand-*`, `--site-primary`, etc.) into `:root`.

**Step 2: Import the theme CSS in your `resources/css/app.css`:**

```
@import "../../vendor/codenzia/filament-panel-base/resources/css/theme.css";
@import "tailwindcss";
```

This maps `--color-brand-*` to the runtime CSS variables via Tailwind v4's `@theme` directive, giving you utility classes like `bg-brand-500`, `text-brand-600`, etc.

Or publish the theme CSS for customization:

```
php artisan vendor:publish --tag=filament-panel-base-theme
```

**Step 3: Implement `ProvidesThemeColors` on your settings class (optional):**

```
use Codenzia\FilamentPanelBase\Contracts\ProvidesThemeColors;
use Codenzia\FilamentPanelBase\Support\ThemePresets;

class GeneralSettings extends Settings implements ProvidesThemeColors
{
    public string $theme_preset = 'ocean_blue';
    public string $primary_color = '#3b82f6';
    // ... other color properties

    public function getThemeColors(): array
    {
        if ($this->theme_preset !== 'custom') {
            $preset = ThemePresets::get($this->theme_preset);
            if ($preset) {
                unset($preset['label']);
                return $preset;
            }
        }

        return [
            'primary_color' => $this->primary_color,
            // ... map all 15 color keys
        ];
    }
}
```

When no settings class implements `ProvidesThemeColors`, the package falls back to `config('filament-panel-base.theme.preset')` (default: `ocean_blue`).

Features
--------

[](#features)

### BasePanelProvider

[](#basepanelprovider)

Abstract panel provider that applies shared configuration to all panels:

- **Brand name** — resolved from settings class or `config('app.name')`
- **Logo &amp; favicon** — resolved from settings via `getAppLogoUrl()` / `getAppFaviconUrl()`
- **Dynamic colors** — reads hex colors from settings (or `ProvidesThemeColors` contract) and converts via `Color::hex()`
- **User menu** — profile slideOver, role display, phone, email, cross-panel navigation
- **Panel badge** — "Administration" / "My Account" badge after the logo
- **Visit Website** button in the topbar
- **Shared middleware stack** — session, CSRF, Filament essentials

#### Panel configuration API

[](#panel-configuration-api)

Call these methods on your `BasePanelProvider` subclass **before** `configureSharedSettings()`:

```
class AdminPanelProvider extends BasePanelProvider
{
    public function panel(Panel $panel): Panel
    {
        $this
            ->addTitleBadge('Administration', 'heroicon-o-shield-check', 'primary', showOnAuthForm: true)
            ->showVisitWebsite(label: 'Back to site')
            ->showLanguageDropdown()
            ->sidebarCollapseButtonPosition('right')
            ->sidebarIcon('heroicon-o-bars-3')
            ->sidebarSlideover()
            ->sidebarSearchable();

        $this->configureSharedSettings(
            $panel->default()->id('admin')->path('admin')->login()
        );

        return $panel->authMiddleware([Authenticate::class]);
    }
}
```

**Topbar**

MethodDefaultDescription`showLanguageDropdown(bool $show = true)``true`Show or hide the locale switcher dropdown in the topbar.`showVisitWebsite(bool $show = true, ?string $label = null)``true`Show or hide the "Visit Website" link button. Pass `$label` to override the translated default.`addTitleBadge(string $label, ?string $icon = null, string $color = 'primary', bool $showOnAuthForm = true)`—Render a small colour-coded badge next to the logo. Accepts `'primary'`, `'success'`, `'warning'`, `'danger'`, `'info'`, or `'gray'`. When `$showOnAuthForm` is `true` (default), the badge is also shown centred above the login and register forms.**Sidebar**

MethodDefaultDescription`sidebarCollapseButtonPosition(string $position)``'left'``'left'` keeps Filament's default topbar button. `'right'` replaces it with a pill button on the right edge of the sidebar.`sidebarIcon(string $icon)`—Replace the default chevron with any Filament icon string (e.g. `'heroicon-o-bars-3'`). Applies to both left and right button positions.`sidebarSlideover(bool $enabled = true)``true`When enabled, the sidebar overlays the main content on desktop instead of pushing it. A dim backdrop is shown, matching Filament's mobile drawer behaviour. Call `->sidebarSlideover(false)` to restore the default push layout.`sidebarCollapseToIcons(bool $enabled = true)``true`When slideover is enabled, keep Filament's icon-only narrow bar when the sidebar is closed instead of sliding it fully off-screen. Users can still click nav icons without opening the full drawer. Call `->sidebarCollapseToIcons(false)` to slide the sidebar fully off-screen instead.`sidebarSearchable(bool $enabled = true)``true`Show a search input at the top of the sidebar navigation. Typing filters items client-side by matching labels; groups with no visible items are hidden automatically. The input is hidden when the sidebar is collapsed to icon-only mode.> **Note:** Slideover mode is **on by default**. When it is active and no custom icon is set, the left-position button automatically uses `heroicon-o-bars-3` (the mobile drawer icon) to signal drawer behaviour. The right-position pill button always uses the chevron SVG by default.

### Theme System

[](#theme-system)

The package ships 17 predefined color presets plus a `custom` option. Each preset defines 15 color keys covering primary, secondary, background, surface, text, status, border, and shadow colors.

**Available presets:** Ocean Blue, Forest Green, Sunset Orange, Royal Purple, Rose Garden, Modern Dark, Teal Breeze, Amber Gold, Slate Steel, Crimson Fire, Sky Light, Emerald Fresh, Indigo Classic, Pink Blossom, Warm Earth, Midnight Blue, Charcoal Noir.

**ThemePresets API:**

```
use Codenzia\FilamentPanelBase\Support\ThemePresets;

ThemePresets::all();         // All 18 presets (17 + custom)
ThemePresets::labels();      // ['ocean_blue' => 'Ocean Blue', ...] — for Select dropdowns
ThemePresets::get('ocean_blue'); // Single preset array or null
ThemePresets::defaults();    // Ocean Blue colors (the default)
ThemePresets::colorKeys();   // ['primary_color', 'danger_color', ...] — all 15 keys
```

**Blade components:**

ComponentPurpose``Injects CSS custom properties into `:root` using `color-mix()` for brand scale generation``FOUC prevention — applies `dark` class before first paintThe `theme-styles` component accepts an optional `:colors` prop. When omitted, it resolves colors automatically via `FilamentPanelBasePlugin::make()->getThemeColors()`.

**Color resolution order:**

1. Settings class implementing `ProvidesThemeColors` interface
2. Config preset (`filament-panel-base.theme.preset`) + color overrides
3. Ocean Blue defaults

**CSS variables injected by ``:**

VariableSource`--site-primary`Primary brand color`--site-primary-hover`Primary hover state`--site-brand-50` to `--site-brand-900`Generated via `color-mix()` from primary`--site-secondary`, `--site-background`, `--site-surface`Semantic colors`--site-text-primary`, `--site-text-secondary`, `--site-text-on-primary`Text colors`--site-success`, `--site-warning`, `--site-danger`, `--site-info`Status colors`--site-border`, `--site-shadow`UI element colors### Middleware

[](#middleware)

MiddlewareDescription`SetLocale`Detects locale from session/cookie, validates against `ProvidesLocales` provider`SetCountry`Auto-detects country from IP using geo API, stores in session`SetCurrency`Sets active currency from country relationship or session`EnsureUserApproved`Blocks suspended/pending users (requires `HasModerationStatus` contract)### Registration Settings

[](#registration-settings)

The package provides a shared `RegistrationSettings` class (`spatie/laravel-settings`) with two fields that complement the `EnsureUserApproved` middleware and `HasModerationStatus` contract:

- `registration_mode` — `'open'` (default) or `'moderated'`
- `require_email_verification` — `true` (default) or `false`

The settings migration is auto-registered and idempotent. Run `php artisan migrate` to create the entries.

**Using the settings in your registration flow:**

```
use Codenzia\FilamentPanelBase\Settings\RegistrationSettings;

$settings = app(RegistrationSettings::class);

$user = User::create([
    'name' => $name,
    'email' => $email,
    'password' => $password,
    'status' => $settings->registration_mode === 'moderated' ? 'pending' : 'approved',
]);

if ($settings->require_email_verification) {
    // redirect to email verification
}
```

The package does **not** ship an admin page for these settings — consuming projects should build their own UI tailored to their needs.

### Contracts

[](#contracts)

Implement these interfaces on your models/settings to integrate with the package:

```
use Codenzia\FilamentPanelBase\Contracts\ProvidesThemeColors;

class GeneralSettings extends Settings implements ProvidesThemeColors
{
    public function getThemeColors(): array
    {
        // Return array with keys like 'primary_color', 'danger_color', etc.
    }
}
```

```
use Codenzia\FilamentPanelBase\Contracts\HasModerationStatus;

class User extends Authenticatable implements HasModerationStatus
{
    public function isSuspended(): bool { /* ... */ }
    public function isPending(): bool { /* ... */ }
}
```

```
use Codenzia\FilamentPanelBase\Contracts\ProvidesLocales;

class Language extends Model implements ProvidesLocales
{
    public static function getActive(): array { /* ... */ }
}
```

```
use Codenzia\FilamentPanelBase\Contracts\ProvidesCountries;

class Country extends Model implements ProvidesCountries
{
    public function scopePublished(Builder $query): Builder { /* ... */ }
    public function currency() { /* ... */ }
}
```

```
use Codenzia\FilamentPanelBase\Contracts\ProvidesCurrencies;

class Currency extends Model implements ProvidesCurrencies
{
    public function getCodeAttribute(): string { /* ... */ }
    public function getSymbolAttribute(): string { /* ... */ }
}
```

### Traits

[](#traits)

TraitDescription`HasProfileSlideOver`Profile-editing slideOver action in the user menu with vertical tabs (Personal Info + Change Password)`NotifiesAdmins`Sends notifications to admin-role users and optionally the content author`HasContactValidation`Shared validation rules for lead capture forms (name, phone, email, WhatsApp)#### HasProfileSlideOver

[](#hasprofileslideover)

Used by `BasePanelProvider` to add a profile-editing slideOver to the Filament user menu. Includes name, email, and password fields out of the box. Override methods in your panel provider to add project-specific fields:

MethodPurpose`getProfilePersonalInfoComponents()`Form fields for the "Personal Information" tab`getProfilePasswordComponents()`Form fields for the "Change Password" tab`getProfileFormTabs()`Customise tabs (add new ones, reorder, etc.)`getProfileFormData()`Data to fill the form (override to include relationships)`saveProfileData(array $data)`Persist form data (override to handle media sync, etc.)**Example — adding an avatar and phone field:**

```
use Codenzia\FilamentMedia\Forms\MediaPickerField;
use Codenzia\FilamentPanelBase\Forms\Components\PhoneInput;

class UserPanelProvider extends BasePanelProvider
{
    protected function getProfileFormData(): array
    {
        $data = parent::getProfileFormData();
        $data['media_avatar'] = filament()->auth()->user()->images()->first()?->getKey();

        return $data;
    }

    protected function getProfilePersonalInfoComponents(): array
    {
        return [
            ...parent::getProfilePersonalInfoComponents(),
            PhoneInput::make('phone')->label(__('Phone'))->countries(/* ... */),
            MediaPickerField::make('media_avatar')->label(__('Avatar'))->imageOnly(),
        ];
    }

    protected function saveProfileData(array $data): void
    {
        $user = filament()->auth()->user();

        if (array_key_exists('media_avatar', $data)) {
            $user->syncMediaByIds($data['media_avatar'] ? [$data['media_avatar']] : []);
            unset($data['media_avatar']);
        }

        parent::saveProfileData($data);
    }
}
```

### Blade Components

[](#blade-components)

**Form fields** (Livewire-bound via `@entangle`):

```

```

`phone-input` combines a country code dropdown and phone number input into a single bordered group with a searchable dropdown. It accepts the same country collection as the other components and binds to two separate Livewire properties.

PropDefaultDescription`:countries`*(required)*Eloquent collection of Country models (must have `code`, `phone_code`, `name`)`country-code-model``'country_code'`Livewire property for the selected dial code`phone-model``'whatsapp'`Livewire property for the phone number`:default``null`Fallback country code (e.g. `'+962'`)`placeholder``'501234567'`Input placeholder**Navbar switchers** (pair with `SetCountry`/`SetCurrency`/`SetLocale` middleware):

```

```

The switchers read view-shared data from middleware (`$availableCountries`, `$currentCountry`, `$availableCurrencies`, `$currentCurrency`, `$currentCurrencyMode`) and require routes named `country.switch`, `currency.switch`, and `locale.switch` in the consuming app.

### Filament Form Components

[](#filament-form-components)

Reusable form fields for Filament v4 panels. Both use Filament's native CSS classes (`fi-input-wrp`, `fi-input`) for full theme compatibility — any custom panel styling automatically applies.

#### CountrySelect

[](#countryselect)

A Select field with flag icons beside each country name. Extends `Filament\Forms\Components\Select` with `allowHtml()`, `searchable()`, and `preload()` pre-configured.

**From relationship** (stores the country ID):

```
use Codenzia\FilamentPanelBase\Forms\Components\CountrySelect;

CountrySelect::make('country_id')
    ->relationship('country', 'name')
    ->required()
    ->live()
```

The related model must have a `code` column with the ISO country code (e.g. `jo`, `sa`). To use a different column:

```
CountrySelect::make('country_id')
    ->codeAttribute('iso_code')
    ->relationship('country', 'name')
```

**From array** (keys are ISO codes, stored as value):

```
CountrySelect::make('country')
    ->countries(['jo' => 'Jordan', 'sa' => 'Saudi Arabia'])
```

**From array** (keys are IDs, with explicit code):

```
CountrySelect::make('country_id')
    ->countries([
        1 => ['name' => 'Jordan', 'code' => 'jo'],
        2 => ['name' => 'Saudi Arabia', 'code' => 'sa'],
    ])
```

**From closure** (lazy-loaded):

```
CountrySelect::make('country_id')
    ->countries(fn () => Country::published()
        ->get()
        ->mapWithKeys(fn ($c) => [$c->id => ['name' => $c->name, 'code' => strtolower($c->code)]])
        ->toArray())
```

#### PhoneInput

[](#phoneinput)

A compound field with a country code dropdown (flags + dial code) and a phone number input. Stores the combined value as a single string (e.g. `+962501234567`). Uses Filament's `fi-input-wrp` wrapper with a non-inline prefix for the country code section.

```
use Codenzia\FilamentPanelBase\Forms\Components\PhoneInput;

PhoneInput::make('phone')
    ->label(__('Phone'))
    ->countries(fn (): array => Country::published()
        ->whereNotNull('phone_code')
        ->orderBy('order')
        ->get()
        ->map(fn (Country $c): array => [
            'code' => strtolower($c->code),
            'phone_code' => $c->phone_code,
            'name' => $c->name,
        ])
        ->toArray())
```

Each country in the array must have `code` (ISO, lowercase), `phone_code` (e.g. `+962`), and `name`.

**Default country code:**

```
PhoneInput::make('phone')
    ->countries($countries)
    ->defaultCountryCode('+962')
```

**Placeholder &amp; validation:**

```
PhoneInput::make('phone')
    ->countries($countries)
    ->placeholder('7XXXXXXXX')
    ->required()
    ->unique(ignoreRecord: true)
```

The field supports `disabled()`, `readOnly()`, `live()`, and standard Filament validation rules.

### Flag Icons

[](#flag-icons)

This package bundles [flag-icons](https://github.com/lipis/flag-icons) CSS and SVGs for country flag display. On Filament panels, the CSS is auto-injected via `@filamentStyles`. For frontend layouts, add a `` tag:

```

```

Publish the SVG assets:

```
php artisan filament:assets
php artisan vendor:publish --tag=filament-panel-base-assets
```

**Note:** The bundled `flag-icons.css` is minified with very long lines. If your IDE's spell checker warns about it (e.g. cSpell's "line length greater than 20000"), add it to your ignore list in `.vscode/settings.json`:

```
{ "cSpell.ignorePaths": ["**/flag-icons.css"] }
```

**Important:** The CSS class prefix is `flag` (not the upstream `fi`) to avoid collision with Filament's own `.fi-*` class namespace. Usage:

```

```

### ColorUtils

[](#colorutils)

Static utility class for color manipulation:

```
use Codenzia\FilamentPanelBase\Support\ColorUtils;

ColorUtils::hexToRgb('#3b82f6');        // [59, 130, 246]
ColorUtils::hexToRgbString('#3b82f6');  // 'rgb(59, 130, 246)'
ColorUtils::hexToRgba('#3b82f6', 0.5);  // 'rgba(59, 130, 246, 0.5)'
ColorUtils::isLightColor('#ffffff');     // true
ColorUtils::getContrastColor('#3b82f6'); // '#ffffff'
```

Configuration
-------------

[](#configuration)

```
// config/filament-panel-base.php

return [
    'panels' => ['admin', 'dashboard'],
    'admin_role' => 'super_admin',
    'user_model' => \App\Models\User::class,

    'locale' => [
        'provider' => null,        // class implementing ProvidesLocales
        'available' => ['en'],
        'detection_order' => ['session', 'cookie', 'config'],
    ],

    'country' => [
        'auto_detect' => true,
        'default_id' => null,
        'model' => null,           // class implementing ProvidesCountries
        'geo_api' => 'https://ipapi.co/{ip}/json/',
        'cache_ttl' => 1440,
    ],

    'currency' => [
        'model' => null,           // class implementing ProvidesCurrencies
        'virtual_usd' => true,
    ],

    'contact_validation' => [
        'require_whatsapp' => false,
        'allow_email_alternative' => true,
    ],

    'settings_class' => null,      // class with branding properties

    'colors' => [
        'primary'   => '#3b82f6',
        'secondary' => '#6366f1',
        'danger'    => '#ef4444',
        'warning'   => '#f59e0b',
        'success'   => '#10b981',
        'info'      => '#06b6d4',
    ],

    'theme' => [
        'preset' => 'ocean_blue',  // any ThemePresets key
        'colors' => [],            // override individual color keys
    ],
];
```

Translatable Content (Optional)
-------------------------------

[](#translatable-content-optional)

The package provides automatic integration with [spatie/laravel-translatable](https://github.com/spatie/laravel-translatable) and [lara-zeus/spatie-translatable](https://github.com/lara-zeus/spatie-translatable) for Filament v4. When both packages are installed, `BasePanelProvider` auto-registers the `SpatieTranslatablePlugin` on every panel — no manual plugin registration needed.

### When to use this

[](#when-to-use-this)

Use this integration when your project has **translatable database content** (e.g. product names, category descriptions, page content stored in multiple languages). This is different from Laravel's built-in `__()` / `trans()` localization which translates static UI strings.

**Good fit:** A real estate site where property names, descriptions, and locations are stored in both English and Arabic.

**Not needed:** A single-language app, or an app that only translates UI labels via language files.

### Step 1: Install the packages

[](#step-1-install-the-packages)

```
composer require spatie/laravel-translatable lara-zeus/spatie-translatable
```

### Step 2: Configure available locales

[](#step-2-configure-available-locales)

In your `config/filament-panel-base.php`, set the locales your content supports:

```
'locale' => [
    'provider' => \App\Models\Language::class,
    'available' => ['en', 'ar'],  // ← used by the translatable plugin
    'detection_order' => ['session', 'cookie', 'config'],
],
```

The `locale.available` array is passed to `SpatieTranslatablePlugin::defaultLocales()` automatically. That's all the panel-level setup required — no need to register the plugin yourself.

### Step 3: Make your models translatable

[](#step-3-make-your-models-translatable)

Add the `HasTranslations` trait and declare which columns are translatable:

```
use Spatie\Translatable\HasTranslations;

class Category extends Model
{
    use HasTranslations;

    public array $translatable = ['name', 'description'];
}
```

Translatable columns must be `json` type in the database. Create a migration if converting existing columns:

```
Schema::table('categories', function (Blueprint $table) {
    $table->json('name')->change();
    $table->json('description')->nullable()->change();
});
```

### Step 4: Make your Filament resources translatable

[](#step-4-make-your-filament-resources-translatable)

Add the `Translatable` concern to each resource class:

```
use LaraZeus\SpatieTranslatable\Resources\Concerns\Translatable;

class CategoryResource extends Resource
{
    use Translatable;

    protected static ?string $model = Category::class;
    // ...
}
```

### Step 5: Add the locale switcher to resource pages

[](#step-5-add-the-locale-switcher-to-resource-pages)

Add the `Translatable` concern and `LocaleSwitcher` action to each resource page:

**For ManageRecords pages:**

```
use LaraZeus\SpatieTranslatable\Resources\Pages\ManageRecords\Concerns\Translatable;
use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher;

class ManageCategories extends ManageRecords
{
    use Translatable;

    protected static string $resource = CategoryResource::class;

    protected function getHeaderActions(): array
    {
        return [
            LocaleSwitcher::make(),
            Actions\CreateAction::make()->slideOver(),
        ];
    }
}
```

**For ListRecords pages:**

```
use LaraZeus\SpatieTranslatable\Resources\Pages\ListRecords\Concerns\Translatable;
use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher;

class ListCategories extends ListRecords
{
    use Translatable;

    protected static string $resource = CategoryResource::class;

    protected function getHeaderActions(): array
    {
        return [
            LocaleSwitcher::make(),
            Actions\CreateAction::make(),
        ];
    }
}
```

### Customising the integration

[](#customising-the-integration)

Override `registerTranslatablePlugin()` in your panel provider to customise the behaviour:

```
class AdminPanelProvider extends BasePanelProvider
{
    protected function registerTranslatablePlugin(Panel $panel): void
    {
        // Custom locales per panel
        $panel->plugin(
            \LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin::make()
                ->defaultLocales(['en', 'ar', 'fr'])
                ->persist()
        );
    }
}
```

To disable the integration for a specific panel, override with an empty method:

```
protected function registerTranslatablePlugin(Panel $panel): void
{
    // This panel does not need translatable content
}
```

Translation Manager UI (Optional)
---------------------------------

[](#translation-manager-ui-optional)

The package includes a built-in Translation Manager that lets you view, edit, and scan all `__()` / `trans()` language strings from the admin panel — no file editing required. Translations are stored in the database via [spatie/laravel-translation-loader](https://github.com/spatie/laravel-translation-loader) and override file-based translations at runtime.

### When to use this

[](#when-to-use-this-1)

Use this when you need a **non-developer-friendly UI** to manage static language files (e.g. `lang/en/messages.php`, `lang/ar.json`). This is different from `spatie/laravel-translatable` which handles database content.

**Key features:**

- **Codebase scanner** — automatically finds all `__()`, `trans()`, `@lang()`, `Lang::get()` calls
- **Dynamic locales** — reads available languages from your `ProvidesLocales` provider (no hardcoded config)
- **Per-language workflow** — access translations from your Language resource's action group, scoped to a single locale
- **Configurable scanner** — scan extra file types (`js`, `ts`, `vue`) and functions (`$t`, `i18n.t`)
- **DB overrides** — database translations take precedence over file translations, with caching

### Step 1: Publish migrations and config

[](#step-1-publish-migrations-and-config)

```
php artisan panel-base:enable-translations
```

This publishes the `spatie/laravel-translation-loader` migration and config.

### Step 2: Run migrations

[](#step-2-run-migrations)

```
php artisan migrate
```

### Step 3: Configure the translation model

[](#step-3-configure-the-translation-model)

In `config/translation-loader.php`, point the model to the panel-base Translation model:

```
'model' => Codenzia\FilamentPanelBase\Models\Translation::class,
```

### Step 4: Opt in per panel

[](#step-4-opt-in-per-panel)

Add `->withTranslations()` to `FilamentPanelBasePlugin::make()` in the panel(s) where you want the translation routes registered:

```
->plugins([
    FilamentPanelBasePlugin::make()
        ->withTranslations()
        ->settingsUsing(fn () => app(\App\Settings\GeneralSettings::class)),
])
```

The Translations resource is hidden from sidebar navigation by default. It is designed to be accessed from a Language resource (see Step 5). Panels without `->withTranslations()` are unaffected.

### Step 5: Add to your Language resource

[](#step-5-add-to-your-language-resource)

Add the **Manage Translations** action to your Language resource's record actions and optionally the **Scan** action to the page header:

```
use Codenzia\FilamentPanelBase\Filament\Resources\TranslationResource;

// In your LanguageResource table():
->recordActions([
    Actions\ActionGroup::make([
        Actions\EditAction::make()->slideOver(),
        TranslationResource::manageAction(), // opens translations scoped to this language
        Actions\DeleteAction::make(),
    ]),
])

// In your ManageLanguages page getHeaderActions() (optional):
TranslationResource::scanHeaderAction(),
```

When the user clicks **Manage Translations** on a language, the translations page opens scoped to that locale — the table shows that language's text and the edit form only shows the relevant textarea.

### Step 6: Scan your codebase for translation keys

[](#step-6-scan-your-codebase-for-translation-keys)

```
php artisan translations:scan
```

This scans your project for all `__()`, `trans()`, `@lang()` calls and populates the database with initial values from your existing language files. Re-run whenever you add new translation keys.

You can also scan from the admin UI using the **Scan** button in the Translations page header.

### Customising the scanner

[](#customising-the-scanner)

Override scan paths, file extensions, and translation functions via config:

```
// config/filament-panel-base.php
'translations' => [
    'navigation_group' => 'Settings',
    'navigation_sort' => 11,
    'navigation_icon' => 'heroicon-o-language',
    'scan_paths' => null,           // null = [app_path(), resource_path('views')]
    'scan_extensions' => ['php'],   // add 'js', 'ts', 'vue' for frontend files
    'scan_functions' => [],         // extra function names, e.g. ['$t', 'i18n.t']
],
```

The scanner always matches `__()` plus the PHP-specific grouped functions (`trans()`, `@lang()`, `Lang::get()`, etc.). The `scan_functions` config adds extra function names for JSON-style translation calls in other languages.

Plugin API
----------

[](#plugin-api)

```
FilamentPanelBasePlugin::make()
    // Resolve settings via closure
    ->settingsUsing(fn () => app(GeneralSettings::class))
    // Or by class name
    ->settingsClass(GeneralSettings::class)
    // Enable the translation manager UI for this panel (opt-in)
    ->withTranslations()

// Get resolved theme colors (used internally by )
FilamentPanelBasePlugin::make()->getThemeColors();
// Returns: ['primary_color' => '#3b82f6', 'danger_color' => '#ef4444', ...]
```

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 12+
- Filament v4
- `spatie/laravel-settings` ^3.0 (required, for `RegistrationSettings`)
- `spatie/laravel-permission` (optional, for `NotifiesAdmins` trait)
- `spatie/laravel-translatable` + `lara-zeus/spatie-translatable` (optional, for translatable database content)
- `spatie/laravel-translation-loader` ^2.8 (bundled — activate with `->withTranslations()` for translation manager UI)

License
-------

[](#license)

This package is dual-licensed:

- **MIT License** — Free for open source projects under an OSI-approved license.
- **Commercial License** — Required for proprietary/commercial projects. Visit [codenzia.com](https://codenzia.com) for details.

See [LICENSE.md](LICENSE.md) for full terms.

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance56

Moderate activity, may be stable

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/7c09a47187ca823dff0650b985b6b1d0632bf550fffbd692005cb12ffae5e8ac?d=identicon)[mh2x](/maintainers/mh2x)

---

Top Contributors

[![mh2x](https://avatars.githubusercontent.com/u/10361843?v=4)](https://github.com/mh2x "mh2x (25 commits)")

### Embed Badge

![Health badge](/badges/codenzia-filament-panel-base/health.svg)

```
[![Health](https://phpackages.com/badges/codenzia-filament-panel-base/health.svg)](https://phpackages.com/packages/codenzia-filament-panel-base)
```

###  Alternatives

[jeroennoten/laravel-adminlte

Easy AdminLTE integration with Laravel

4.0k4.8M43](/packages/jeroennoten-laravel-adminlte)[dmstr/yii2-adminlte-asset

AdminLTE backend theme asset bundle for Yii 2.0 Framework

1.1k1.8M67](/packages/dmstr-yii2-adminlte-asset)[dwij/laraadmin

LaraAdmin is a Open source Laravel Admin Panel / CMS which can be used as Admin Backend, Data Management Tool or CRM boilerplate for Laravel with features like CRUD Generation, Module Manager, Media, Menus, Backups and much more

1.6k68.7k](/packages/dwij-laraadmin)[filament/spatie-laravel-media-library-plugin

Filament support for `spatie/laravel-medialibrary`.

1764.8M125](/packages/filament-spatie-laravel-media-library-plugin)[bezhansalleh/filament-exceptions

A Simple &amp; Beautiful Pluggable Exception Viewer for FilamentPHP's Admin Panel

193195.9k13](/packages/bezhansalleh-filament-exceptions)[filament/infolists

Easily add beautiful read-only infolists to any Livewire component.

1220.8M36](/packages/filament-infolists)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
