PHPackages                             usamamuneerchaudhary/filament-model-states - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. usamamuneerchaudhary/filament-model-states

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

usamamuneerchaudhary/filament-model-states
==========================================

State-machine-aware timeline and Kanban board components for Filament, built on top of spatie/laravel-model-states.

1.0(1mo ago)3100MITPHPPHP ^8.3CI passing

Since Jul 8Pushed 1mo agoCompare

[ Source](https://github.com/usamamuneerchaudhary/filament-model-states)[ Packagist](https://packagist.org/packages/usamamuneerchaudhary/filament-model-states)[ RSS](/packages/usamamuneerchaudhary-filament-model-states/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

Filament Model States
=====================

[](#filament-model-states)

[![Latest Version on Packagist](https://camo.githubusercontent.com/579b7565c2d33048f4438a7826c782b376c22405598a949028176e8ce14503f9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465733f7374796c653d666c61742d7371756172652667)](https://packagist.org/packages/usamamuneerchaudhary/filament-model-states)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/613af30bdffd879b29642eb2ad161515cbd4000a3dade3d6352eb0b8af2c0102/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465732f6261646765732f7175616c6974792d73636f72652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/usamamuneerchaudhary/filament-model-states/?branch=main)[![CodeFactor](https://camo.githubusercontent.com/481c0c54012398096c5c8f8586f732936f58fb3f33f71771a3b36688838cbbef/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465732f6261646765)](https://www.codefactor.io/repository/github/usamamuneerchaudhary/filament-model-states)[![Build Status](https://camo.githubusercontent.com/beb3c3dd76eaa1ceff6c141719d423faf1b4ce103544ca65f153c6350c711a99/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465732f6261646765732f6275696c642e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/usamamuneerchaudhary/filament-model-states/build-status/main)[![Code Intelligence Status](https://camo.githubusercontent.com/6dc3ea6f4bd098e54d9b616d16888d233a13ab421506d3428ba90f0f1fdb797d/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465732f6261646765732f636f64652d696e74656c6c6967656e63652e7376673f623d6d61696e)](https://scrutinizer-ci.com/code-intelligence)[![Total Downloads](https://camo.githubusercontent.com/5da33e718951e41f583ea60e5990de615d721706236f4857788bfa6d6437e437/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465733f7374796c653d666c61742d737175617265)](https://packagist.org/packages/usamamuneerchaudhary/filament-model-states)[![Licence](https://camo.githubusercontent.com/4a64c503ae8ab052cd3462a124f4e33103c64e7f26b627e01105142a7db3a7c9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7573616d616d756e6565726368617564686172792f66696c616d656e742d6d6f64656c2d7374617465733f7374796c653d666c61742d737175617265)](https://github.com/usamamuneerchaudhary/filament-model-states/blob/HEAD/LICENSE.md)

[![filament-model-states-thumbnail.jpg](public/images/filament-model-states-thumbnail.jpg)](public/images/filament-model-states-thumbnail.jpg)

\## In Action !\[kanban.gif\](public/images/kanban.gif) State-aware **Kanban board** and **status timeline** components for Filament v5.

Built to sit on top of:

- [`spatie/laravel-model-status`](https://github.com/spatie/laravel-model-status) &gt; powers the **timeline** (status history log)
- [`spatie/laravel-model-states`](https://github.com/spatie/laravel-model-states) &gt; powers **transition validation** and column discovery on the Kanban board (optional but recommended)

Filament Model States provides a plug and play auto-detection for enums, Spatie Model States, or plain database values.

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

[](#requirements)

- PHP 8.3+
- Laravel 11–13
- Filament v5
- `spatie/laravel-model-status` (required for timeline + optional status logging on Kanban moves)
- `spatie/laravel-model-states` (optional for server-side transition validation and config-driven column order)

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

[](#installation)

```
composer require usamamuneerchaudhary/filament-model-states
```

```
php artisan vendor:publish --tag="filament-model-states-config"

```

```
php artisan filament:assets

```

After upgrading the package, re-run `php artisan filament:assets` and hard-refresh your browser if Kanban styles or drag-and-drop stop working.

---

Quick start
-----------

[](#quick-start)

### 1. Prepare your model

[](#1-prepare-your-model)

**Option A: backed enum (simplest)**

```
use Spatie\ModelStatus\HasStatuses;

class Order extends Model
{
    use HasStatuses;

    protected function casts(): array
    {
        return [
            'workflow_state' => WorkflowState::class,
        ];
    }
}
```

```
enum WorkflowState: string
{
    case Draft = 'draft';
    case Pending = 'pending';
    case Completed = 'completed';

    public function label(): string
    {
        return match ($this) {
            self::Draft => 'Draft',
            self::Pending => 'Pending',
            self::Completed => 'Completed',
        };
    }

    /** @return array */
    public static function transitions(): array
    {
        return [
            'draft' => ['pending'],
            'pending' => ['completed'],
            'completed' => [],
        ];
    }
}
```

**Option B: Spatie Model States**

```
use Spatie\ModelStates\HasStates;
use Spatie\ModelStatus\HasStatuses;

class Booking extends Model
{
    use HasStates;
    use HasStatuses;

    protected function casts(): array
    {
        return [
            'status' => BookingState::class,
        ];
    }
}
```

### 2. Add the Kanban to a resource list page

[](#2-add-the-kanban-to-a-resource-list-page)

```
use Usamamuneerchaudhary\FilamentModelStates\Concerns\HasStateKanbanBoard;

class BookingResource extends Resource
{
    use HasStateKanbanBoard;

    // ...
}
```

```
// Pages/ListBookings.php
protected function getHeaderWidgets(): array
{
    return [
        BookingResource::getStateKanbanBoard(),
    ];
}
```

[![Screenshot](public/images/screenshot.png)](public/images/screenshot.png)That's it. Columns, labels, transitions, and column order are auto-detected.

### 3. Verify detection (optional)

[](#3-verify-detection-optional)

```
php artisan filament-model-states:make-kanban Booking --detect --state-field=status
```

---

Kanban board
------------

[](#kanban-board)

### Plug-and-play widget

[](#plug-and-play-widget)

Point the board at any model, no subclass required:

```
use Usamamuneerchaudhary\FilamentModelStates\Widgets\ModelStateKanbanBoard;

// List page header widgets
ModelStateKanbanBoard::for(Booking::class)

// Non-default state field
ModelStateKanbanBoard::for(Booking::class, stateField: 'status')
```

### Resource trait (recommended)

[](#resource-trait-recommended)

Add `HasStateKanbanBoard` to your Filament resource, then register on the list page:

```
use Usamamuneerchaudhary\FilamentModelStates\Concerns\HasStateKanbanBoard;

class OrderResource extends Resource
{
    use HasStateKanbanBoard;
}
```

```
protected function getHeaderWidgets(): array
{
    return [
        OrderResource::getStateKanbanBoard(),
    ];
}
```

### Generated widget (optional)

[](#generated-widget-optional)

If you prefer a dedicated widget class:

```
php artisan filament-model-states:make-kanban Booking --resource=BookingResource
```

This creates `app/Filament/Widgets/BookingKanbanBoard.php` and prints wiring instructions. Use `--force` to overwrite, `--detect` to inspect without generating.

### Custom widget (full control)

[](#custom-widget-full-control)

Extend `StateKanbanBoard` when you need complete control over columns, queries, card content, or authorization:

```
use Usamamuneerchaudhary\FilamentModelStates\Widgets\StateKanbanBoard;

class OrderStatusBoard extends StateKanbanBoard
{
    protected function getModel(): string
    {
        return Order::class;
    }

    protected function getStateField(): string
    {
        return 'workflow_state';
    }

    protected function getColumns(): array
    {
        return ['draft', 'pending', 'processing', 'completed', 'cancelled'];
    }

    protected function getAllowedTransitions(): ?array
    {
        return WorkflowState::transitions();
    }

    protected function getColumnLabel(string $key): string
    {
        return WorkflowState::from($key)->label();
    }

    protected function getCardTitle(Model $record): string
    {
        return "Order #{$record->reference}";
    }

    protected function getCardSubtitle(Model $record): ?string
    {
        return $record->customer_name;
    }

    protected function getQuery(): Builder
    {
        return Order::query()->where('team_id', filament()->getTenant()->id);
    }
}
```

---

Auto-detection
--------------

[](#auto-detection)

`ModelStateKanbanBoard` resolves everything through `StateConfigurationResolver`:

PrioritySourceColumnsTransitionsLabels1**Backed enum cast**`Enum::cases()``Enum::transitions()` or `Model::getStateTransitions()``Enum::label()`2**Spatie Model States cast**`BookingState::getStateMapping()``BookingState::config()` allowances`State::label()` or headline of `$name`3**Database fallback**Distinct values in the state column`Model::getStateTransitions()` if definedHeadline of stored value### State field detection

[](#state-field-detection)

The resolver looks for, in order:

1. `workflow_state`, `state`, or `status` attributes in `$casts`
2. Any enum cast whose field name contains `state` or `status`
3. Any Spatie state cast on a field containing `state` or `status`
4. Any enum or Spatie state cast

Pass an explicit field when ambiguous:

```
ModelStateKanbanBoard::for(Booking::class, stateField: 'status')
```

---

Spatie Model States
-------------------

[](#spatie-model-states)

### Transitions

[](#transitions)

When your model uses `spatie/laravel-model-states`, the Kanban reads allowances directly from your abstract state's `config()`:

```
abstract class BookingState extends State
{
    public static function config(): StateConfig
    {
        return parent::config()
            ->default(Initiated::class)
            ->allowTransition(Initiated::class, AwaitingPayment::class, ToAwaitingPayment::class)
            ->allowTransition(Initiated::class, Expired::class, ToExpired::class);
    }
}
```

On drag-and-drop:

1. **UI**: only allowed target columns are highlighted; invalid targets are blocked client-side.
2. **Server**: `$currentState->canTransitionTo($targetStateClass)` is called before saving.
3. **Persist**: `$currentState->transitionTo($targetStateClass)` runs your transition classes (`ToAwaitingPayment`, etc.).
4. **History**: if the model uses `HasStatuses`, `setStatus()` is called automatically.

### Column keys and labels

[](#column-keys-and-labels)

PropertyUsed for`public static string $name = 'initiated'`Column key (DB value / morph class)`label()`Column header label (optional)`description()`**Not** used for Kanban headers, keep this for tooltips or detail viewsWithout `label()`, headers are generated from `$name` (e.g. `initiated` → "Initiated").

```
class Initiated extends BookingState
{
    public static string $name = 'initiated';

    public static function label(): string
    {
        return 'Initiated';
    }

    public static function description(): ?string
    {
        return 'Booking has just been created';
    }
}
```

### Column ordering

[](#column-ordering)

Columns are ordered automatically. Priority:

1. **`columnOrder()`** on the abstract state class (explicit override)
2. **`$sortOrder`** or **`sortOrder()`** on each concrete state class
3. **Config registration order**: default state first, then the order `allowTransition()` calls appear in `config()`
4. **Filesystem discovery**: remaining states in directory order

Explicit order example:

```
abstract class BookingState extends State
{
    public static function columnOrder(): array
    {
        return [
            'initiated',
            'awaiting_payment',
            'pending',
            'confirmed',
            'completed',
            'cancelled',
        ];
    }
}
```

Per-state sort order:

```
class Initiated extends BookingState
{
    public static int $sortOrder = 1;
    public static string $name = 'initiated';
}
```

---

Enum-based workflows
--------------------

[](#enum-based-workflows)

Define transitions on the enum:

```
public static function transitions(): array
{
    return [
        'draft' => ['pending', 'cancelled'],
        'pending' => ['processing', 'cancelled'],
        'processing' => ['completed', 'failed'],
        'completed' => [],
        'cancelled' => ['draft'],
        'failed' => ['pending'],
    ];
}
```

Or on the model:

```
public static function getStateTransitions(): array
{
    return WorkflowState::transitions();
}
```

Enum case order in the PHP file is used as column order.

---

Optional model hooks
--------------------

[](#optional-model-hooks)

### Search

[](#search)

Add a query scope to enable the board's search box:

```
public function scopeFilamentKanbanSearch(Builder $query, string $search): Builder
{
    return $query->where(function (Builder $query) use ($search): void {
        $query->where('reference', 'like', "%{$search}%")
            ->orWhere('customer_name', 'like', "%{$search}%");
    });
}
```

### Status history on Kanban moves

[](#status-history-on-kanban-moves)

Use `HasStatuses` on the model. Each successful drag calls:

```
$record->setStatus($targetColumn, 'Moved via Kanban board');
```

Customize the reason by extending `StateKanbanBoard::persistStateChange()`.

### Spatie state class resolution

[](#spatie-state-class-resolution)

If column keys don't map cleanly to state classes, add to your model:

```
public static function getStateClassForColumn(string $column): string
{
    return match ($column) {
        'awaiting_payment' => AwaitingPayment::class,
        default => throw new InvalidArgumentException("Unknown column [{$column}]"),
    };
}
```

---

Status timeline (Infolist)
--------------------------

[](#status-timeline-infolist)

Shows a vertical history of status changes from `spatie/laravel-model-status`.

### Basic usage

[](#basic-usage)

```
use Usamamuneerchaudhary\FilamentModelStates\Infolists\Components\StatusTimelineEntry;

public static function infolist(Schema $schema): Schema
{
    return $schema->components([
        StatusTimelineEntry::make('status_history')
            ->label('Status history')
            ->limit(20),
    ]);
}
```

### Resource trait

[](#resource-trait)

```
use Usamamuneerchaudhary\FilamentModelStates\Concerns\HasStatusTimelineInfolist;

class OrderInfolist
{
    use HasStatusTimelineInfolist;

    public static function configure(Schema $schema): Schema
    {
        return $schema->components([
            Section::make('Status history')
                ->schema([
                    self::statusTimelineEntry(),
                ]),
        ]);
    }
}
```

The timeline reads `$record->statuses()`, shows each state with reason and duration, and highlights the current one. Status labels match the Kanban board (enum `label()`, Spatie state `label()`, or a headline fallback).

### Options

[](#options)

```
StatusTimelineEntry::make('status_history')
    ->limit(20)              // max entries shown (no pagination UI)
    ->hideReason()           // hide the reason text
    ->emptyStateLabel('No changes yet.');
```

---

Kanban features
---------------

[](#kanban-features)

- **Drag-and-drop** between columns with SortableJS (bundled, no CDN)
- **Server-side validation**: invalid moves are rejected with a notification; cards snap back
- **Confirmation modal** before saving a move (configurable)
- **Search**: filters cards when `scopeFilamentKanbanSearch` is defined
- **Column filter**: dropdown to focus on a single column
- **Totals**: SQL counts per column; cards are paginated per column
- **Transition hints**: "Can move to" chips under each column header
- **Authorization**: `Gate::authorize('update', $record)` on every move

---

Artisan command
---------------

[](#artisan-command)

```
# Inspect auto-detected configuration
php artisan filament-model-states:make-kanban Booking --detect

# Specify a non-default state field
php artisan filament-model-states:make-kanban Booking --detect --state-field=status

# Generate a widget class
php artisan filament-model-states:make-kanban Booking --resource=BookingResource

# Overwrite an existing generated widget
php artisan filament-model-states:make-kanban Booking --force
```

Example `--detect` output:

```
 Model              App\Models\Booking
 State field        status
 Detection source   spatie-model-states
 Columns            initiated, awaiting_payment, pending, ...
 Transitions        {"initiated":["awaiting_payment","expired"], ...}

```

---

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

[](#configuration)

Published to `config/filament-model-states.php`:

```
return [
    'timeline' => [
        'per_page' => 25,
        'show_reason' => true,
        'date_format' => 'M j, Y \a\t g:i A',
    ],

    'kanban' => [
        // Filament color tokens per column key
        'column_colors' => [
            'draft' => 'gray',
            'pending' => 'warning',
            'processing' => 'info',
            'completed' => 'success',
            'cancelled' => 'danger',
            'failed' => 'danger',
        ],

        // Show a danger notification when a drag is rejected
        'notify_on_invalid_transition' => true,

        // Max cards rendered per column (totals always use SQL counts)
        'records_per_column' => 25,

        // Confirm in a modal before persisting a move
        'confirm_before_move' => true,
    ],
];
```

Unlisted column keys fall back to `gray`. Override per-widget via `getColumnColor()` on a custom `StateKanbanBoard` subclass.

---

How transition validation works
-------------------------------

[](#how-transition-validation-works)

When a card is dropped on a new column, `moveRecord()` runs **server-side**:

1. Loads the record with a row lock.
2. Authorizes `update` on the record.
3. Checks the transition:
    - **Spatie Model States**: `$state->canTransitionTo($targetStateClass)`
    - **Enum / manual map**: `getAllowedTransitions()` array
    - **No rules**: all moves allowed
4. Persists via `transitionTo()` (Spatie) or direct attribute save (enum/DB).
5. Optionally logs to status history via `setStatus()`.

The browser cannot bypass step 3, enforcement lives in your state machine, not JavaScript.

---

Where to register the board
---------------------------

[](#where-to-register-the-board)

LocationHow**Resource list page** (recommended)`getHeaderWidgets()` → `YourResource::getStateKanbanBoard()`**Custom page**`ModelStateKanbanBoard::for(YourModel::class)` in `getHeaderWidgets()` or `getFooterWidgets()`**Panel widgets**Only if you want a global board, usually prefer resource-scoped---

Compatibility
-------------

[](#compatibility)

- **Filament v5** (`filament/filament ^5.0`)
- **Laravel 11–13**
- **PHP 8.3+**
- `StatusTimelineEntry` uses the Filament v5 schemas/infolists API
- Kanban CSS and JS are bundled via `FilamentAsset`, run `php artisan filament:assets` after install

---

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance90

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/adb088072d2e9f335a0c37e9e5481d8d3c582ccc4b4ca721850b1e32b3a480e8?d=identicon)[usamamuneerchaudhary](/maintainers/usamamuneerchaudhary)

---

Top Contributors

[![usamamuneerchaudhary](https://avatars.githubusercontent.com/u/24513380?v=4)](https://github.com/usamamuneerchaudhary "usamamuneerchaudhary (21 commits)")

---

Tags

phpspatielaraveltimelinefilamentfilament-pluginfilament-v5kanbanmodel-statesmodel-status

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/usamamuneerchaudhary-filament-model-states/health.svg)

```
[![Health](https://phpackages.com/badges/usamamuneerchaudhary-filament-model-states/health.svg)](https://phpackages.com/packages/usamamuneerchaudhary-filament-model-states)
```

###  Alternatives

[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3915.5k](/packages/rawilk-profile-filament-plugin)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

329575.9k35](/packages/codewithdennis-filament-select-tree)[croustibat/filament-jobs-monitor

Background Jobs monitoring like Horizon for all drivers for FilamentPHP

278359.3k12](/packages/croustibat-filament-jobs-monitor)[finity-labs/fin-mail

A powerful email template manager and composer for Filament with dynamic token replacement, template versioning, and inline email sending.

316.8k2](/packages/finity-labs-fin-mail)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17861.9k3](/packages/stephenjude-filament-jetstream)[dotswan/filament-map-picker

Easily pick and retrieve geo-coordinates using a map-based interface in your Filament applications.

128216.0k3](/packages/dotswan-filament-map-picker)

PHPackages © 2026

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