PHPackages                             coringawc/filament-action-approvals - 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. coringawc/filament-action-approvals

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

coringawc/filament-action-approvals
===================================

Action-based approval flows for Filament with spatie/laravel-model-states integration.

v2.3.6(1mo ago)014MITPHPPHP ^8.3CI passing

Since Apr 18Pushed 1mo agoCompare

[ Source](https://github.com/CoringaWc/filament-action-approvals)[ Packagist](https://packagist.org/packages/coringawc/filament-action-approvals)[ Docs](https://github.com/CoringaWc/filament-action-approvals)[ RSS](/packages/coringawc-filament-action-approvals/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (9)Dependencies (14)Versions (13)Used By (0)

Filament Action Approvals
=========================

[](#filament-action-approvals)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a882619d95be4bac01830b5f8d6f52e0d590548a6616cb1c386781f50723a22a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636f72696e676177632f66696c616d656e742d616374696f6e2d617070726f76616c732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/coringawc/filament-action-approvals)[![GitHub Tests Action Status](https://camo.githubusercontent.com/cf1e555608962b7a3532de476081e573e860e7341d4baa953154d6725147bae4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f636f72696e676177632f66696c616d656e742d616374696f6e2d617070726f76616c732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/coringawc/filament-action-approvals/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/b8e0be8434ace93c297e364e7c5bc4dbf59e1bc949a770ecc70374507af91bc6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f636f72696e676177632f66696c616d656e742d616374696f6e2d617070726f76616c732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/coringawc/filament-action-approvals/actions?query=workflow%3A%22Fix+PHP+code+styling%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/641f135fd627c8085dad402096734ee9d576ef27a8a01a0553268b01130a678b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636f72696e676177632f66696c616d656e742d616374696f6e2d617070726f76616c732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/coringawc/filament-action-approvals)

Action-based approval workflows for [Filament v5](https://filamentphp.com). Define multi-step approval flows with configurable approver resolvers, SLA enforcement, delegation, and lifecycle callbacks — all integrated into the Filament admin panel.

Features
--------

[](#features)

- **Multi-step approval flows** — sequential steps with configurable approver resolution
- **Polymorphic** — any Eloquent model can be approvable via the `HasApprovals` trait
- **Pluggable approver resolvers** — `UserResolver`, `RoleResolver`, `CustomRuleResolver`, or create your own
- **Approvable actions** — define domain-specific actions (submit, cancel, etc.) on your model, each with its own approval flow
- **Per-action submit buttons** — create dedicated `SubmitForApprovalAction` instances locked to a specific `actionKey`
- **Delegation** — approvers can delegate their step to another user
- **SLA enforcement** — per-step SLA deadlines with warning notifications and configurable escalation (notify, auto-approve, reject, reassign)
- **Lifecycle callbacks** — hook into `onApprovalSubmitted`, `onApprovalApproved`, `onApprovalRejected`, etc. directly on your model
- **Resubmission policy** — control whether models can be resubmitted after approval/rejection
- **User display name** — uses Filament's `getFilamentName()` when available, falls back to `name` attribute
- **Built-in Filament components**:
    - `ApprovalFlowResource` — CRUD for managing approval flow definitions
        - `ApprovalResource` — operational approvals listing with status tabs, filters, overview stats, and slide-over detail view
        - `ListApprovalsAction` — contextual action that opens a slide-over table for approvals scoped to a model or record
        - `ApprovalsDashboard` — opt-in global dashboard with period filters and operational widgets
    - `ApprovalsRelationManager` — shows approval history with slide-over details
    - `ApprovalStatusColumn` — ready-to-use status badge column
    - `ApprovalStatusSection` — infolist section with approval details and timeline
        - Grouped approval actions: Submit, Approve, Reject, Comment, Delegate (usable individually or through the `Aprovações` dropdown)
    - Widgets: Pending Approvals, Approval Analytics
- **Multi-tenancy support** — scope flows and approvers per tenant
- **Event-driven** — fires events for submitted, completed, rejected, step-completed, and escalated
- **Notification system** — notifies approvers, submitters, and escalation targets
- **ACL integration** — optional integration with [`coringawc/filament-acl`](https://github.com/CoringaWc/filament-acl) for permission-aware resources

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

[](#requirements)

- PHP 8.3+
- Laravel 12+
- Filament 5.x

### Optional

[](#optional)

- [`coringawc/filament-acl`](https://github.com/CoringaWc/filament-acl) ^1.0 — enables `HasResourcePermissions` and `PermissionSubject` integration
- [`spatie/laravel-permission`](https://github.com/spatie/laravel-permission) ^7.0 — required for `RoleResolver`

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

[](#installation)

```
composer require coringawc/filament-action-approvals
```

Publish and run the migrations:

```
php artisan vendor:publish --tag="filament-action-approvals-migrations"
php artisan migrate
```

Optionally publish the config file:

```
php artisan vendor:publish --tag="filament-action-approvals-config"
```

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

[](#configuration)

The config file (`config/filament-action-approvals.php`) contains:

```
return [
    // The user model used for approver relationships
    'user_model' => App\Models\User::class,

    // Registered resolver classes available in the flow builder
    'approver_resolvers' => [
        \CoringaWc\FilamentActionApprovals\ApproverResolvers\UserResolver::class,
        \CoringaWc\FilamentActionApprovals\ApproverResolvers\RoleResolver::class,
        \CoringaWc\FilamentActionApprovals\ApproverResolvers\CustomRuleResolver::class,
    ],

    // Multi-tenancy settings
    'multi_tenancy' => [
        'enabled' => false,
        'column' => 'company_id',
        'scope_approvers' => true,
    ],

    // SLA warning threshold (0.75 = 75% of SLA time elapsed)
    'sla_warning_threshold' => 0.75,

    // Date display settings
    'date' => [
        'display_format' => 'd/m/Y H:i',
        'use_since' => true,
    ],

    // Auto-register the SLA processing command to run every minute
    'schedule_sla_command' => true,

    // Navigation group for the ApprovalFlow resource
    'navigation_group' => null,

    // Toggle built-in operational actions in package UIs
    'actions' => [
        'approve' => true,
        'reject' => true,
        'comment' => false,
        'delegate' => false,
    ],

    // Broadcasting — opt-in per event (all disabled by default)
    'broadcasting' => [
        'events' => [
            'submitted' => false,
            'approved' => false,
            'rejected' => false,
            'cancelled' => false,
            'commented' => false,
            'delegated' => false,
            'step_completed' => false,
            'escalated' => false,
        ],
        'queue' => null,
    ],

    // Super admin bypass — see and act on all approvals
    'super_admin' => [
        'enabled' => false,
        'roles' => ['super_admin'],
        'user_ids' => [],
    ],

    // Resource customization
    'resource' => [
        'enabled' => true,
        'cluster' => null,
        'navigation_sort' => null,
        'navigation_icon' => null,
        'show_widgets' => true,
    ],

    // ApprovalResource customization
    'approvals_resource' => [
        'enabled' => true,
        'navigation_sort' => null,
        'navigation_icon' => null,
    ],

    // Opt-in operational dashboard
    'dashboard' => [
        'enabled' => false,
        'route_path' => 'approvals-dashboard',
        'navigation_sort' => null,
        'navigation_icon' => null,
    ],

    // Database table prefix (empty = no prefix)
    'table_prefix' => '',
];
```

Plugin Registration
-------------------

[](#plugin-registration)

Register the plugin in your Filament panel provider:

```
use CoringaWc\FilamentActionApprovals\FilamentActionApprovalsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentActionApprovalsPlugin::make()
                ->flowResource()      // Enable the ApprovalFlow CRUD resource (default: true)
                ->approvalResource()  // Enable the operational ApprovalResource (default: true)
                ->dashboard()         // Enable the ApprovalsDashboard page (default: false / opt-in)
                ->widgets()           // Enable global panel widgets explicitly when also using the dedicated dashboard
                ->navigationGroup('Workflow'),  // Override navigation group
        ]);
}
```

### Plugin Methods

[](#plugin-methods)

MethodDescription`flowResource(bool $enabled = true)`Enable/disable the built-in ApprovalFlow resource`approvalResource(bool $enabled = true)`Enable/disable the built-in ApprovalResource`dashboard(bool $enabled = true)`Enable/disable the opt-in ApprovalsDashboard page`widgets(bool $enabled = true)`Enable/disable PendingApprovals and Analytics widgets`resolvers(array $resolvers)`Override approver resolvers for this panel`userModel(string $model)`Override the user model for this panel`navigationGroup(string $group)`Override the navigation group labelIf the dedicated `ApprovalsDashboard` page is enabled, global panel widgets are suppressed by default so the package dashboard stays independent from the panel's primary dashboard. Call `->widgets()` explicitly if you want both.

### Contextual approvals navigation

[](#contextual-approvals-navigation)

Use `ListApprovalsAction` when you want to open a slide-over table already scoped to a model type or a specific record:

```
use CoringaWc\FilamentActionApprovals\Actions\ListApprovalsAction;

protected function getHeaderActions(): array
{
    return [
        ListApprovalsAction::make()
            ->forApprovableType(PurchaseOrderResource::getModel()),
    ];
}
```

For relation managers or nested contexts, pass the owner record instead:

```
ListApprovalsAction::make()
    ->forApprovable(fn (): Model => $this->getOwnerRecord())
```

The action opens a slide-over containing the approvals table already filtered to the relevant model or record. Use it on resource list pages or on headers of relation managers/nested resources that belong to an approvable model. Do not add it to `ApprovalsRelationManager` itself.

### Dashboard opt-in

[](#dashboard-opt-in)

`ApprovalsDashboard` is disabled by default. Enable it explicitly when your panel needs a global operational view:

```
FilamentActionApprovalsPlugin::make()
    ->flowResource()
    ->approvalResource()
    ->dashboard()
```

The dashboard includes quick period actions (`5d`, `15d`, `30d`, `All`), an advanced filter slideover, status distribution, bottleneck visibility, stale pending approvals, and filtered analytics.

When this dedicated dashboard is enabled, the package no longer injects its global widgets into the panel's main dashboard unless you opt in with `->widgets()`.

Usage
-----

[](#usage)

### 1. Make Your Model Approvable

[](#1-make-your-model-approvable)

Add the `HasApprovals` trait to any Eloquent model:

```
use CoringaWc\FilamentActionApprovals\Concerns\HasApprovals;
use CoringaWc\FilamentActionApprovals\Models\Approval;

class PurchaseOrder extends Model
{
    use HasApprovals;

    // React to approval lifecycle events
    public function onApprovalApproved(Approval $approval): void
    {
        $this->update(['status' => 'approved']);
    }

    public function onApprovalRejected(Approval $approval): void
    {
        $this->update(['status' => 'rejected']);
    }
}
```

### 2. Define Approvable Actions (Optional)

[](#2-define-approvable-actions-optional)

If your model has multiple domain actions that require approval (e.g., submit, cancel, reimburse), define them via `approvableActions()`:

```
class PurchaseOrder extends Model
{
    use HasApprovals;

    /**
     * Define domain-specific actions that can be submitted for approval.
     * Each key is an action identifier, each value is a human-readable label.
     *
     * @return array
     */
    public static function approvableActions(): array
    {
        return [
            'submit' => __('Submit for Processing'),
            'cancel' => __('Request Cancellation'),
        ];
    }
}
```

When `approvableActions()` is defined, the `SubmitForApprovalAction` will show a selector for the user to pick which action they are requesting. You can also create dedicated buttons per action — see [Per-Action Submit Buttons](#per-action-submit-buttons).

### 3. Create an Approval Flow

[](#3-create-an-approval-flow)

Approval flows can be created via the admin panel UI (ApprovalFlow resource) or programmatically:

```
use CoringaWc\FilamentActionApprovals\ApproverResolvers\UserResolver;
use CoringaWc\FilamentActionApprovals\ApproverResolvers\RoleResolver;
use CoringaWc\FilamentActionApprovals\Enums\EscalationAction;
use CoringaWc\FilamentActionApprovals\Enums\StepType;
use CoringaWc\FilamentActionApprovals\Models\ApprovalFlow;

$flow = ApprovalFlow::create([
    'name' => 'Purchase Order Approval',
    'approvable_type' => PurchaseOrder::class,
    'action_key' => 'submit',  // Optional: tie this flow to a specific action
    'is_active' => true,
]);

// Step 1: Manager approval
$flow->steps()->create([
    'name' => 'Manager Review',
    'order' => 1,
    'type' => StepType::Single,
    'approver_resolver' => UserResolver::class,
    'approver_config' => ['user_ids' => [1, 2]],
    'required_approvals' => 1,
    'sla_hours' => 24,
    'escalation_action' => EscalationAction::Notify,
]);

// Step 2: Director approval
$flow->steps()->create([
    'name' => 'Director Sign-off',
    'order' => 2,
    'type' => StepType::Single,
    'approver_resolver' => RoleResolver::class,
    'approver_config' => ['role' => 'director'],
    'required_approvals' => 1,
    'sla_hours' => 48,
    'escalation_action' => EscalationAction::AutoApprove,
]);
```

### 4. Add Approval Actions to Your Resource

[](#4-add-approval-actions-to-your-resource)

#### Option A: All actions at once (quickstart)

[](#option-a-all-actions-at-once-quickstart)

Use the `HasApprovalsResource` trait to add the grouped approval menu as header actions:

```
use CoringaWc\FilamentActionApprovals\Concerns\HasApprovalsResource;

class PurchaseOrderResource extends Resource
{
    use HasApprovalsResource;
    // ...
}

// Edit or View Page
class EditPurchaseOrder extends EditRecord
{
    protected function getHeaderActions(): array
    {
        return [
            ...static::getResource()::getApprovalHeaderActions(),
            // Returns: a single ActionGroup labeled "Aprovações"
            // containing SubmitForApprovalAction, ApproveAction,
            // RejectAction, CommentAction, and DelegateAction
        ];
    }
}
```

The default grouped trigger uses the vertical ellipsis icon and the `Aprovações` label so it can be reused consistently in resource headers and in approval detail infolists.

#### Option B: Individual actions (fine-grained control)

[](#option-b-individual-actions-fine-grained-control)

Use each action individually when you need to customize visibility, labels, or only show specific actions:

```
use CoringaWc\FilamentActionApprovals\Actions\ApproveAction;
use CoringaWc\FilamentActionApprovals\Actions\CommentAction;
use CoringaWc\FilamentActionApprovals\Actions\DelegateAction;
use CoringaWc\FilamentActionApprovals\Actions\RejectAction;
use CoringaWc\FilamentActionApprovals\Actions\SubmitForApprovalAction;

class ViewInvoice extends ViewRecord
{
    protected function getHeaderActions(): array
    {
        return [
            // Custom domain actions first
            AdvanceStatusAction::make(),
            CancelAction::make(),

            // Only the approval response actions — submitter uses a different flow
            ApproveAction::make(),
            RejectAction::make(),
            CommentAction::make(),
            DelegateAction::make(),
        ];
    }
}
```

Each action manages its own visibility automatically:

ActionVisible When`SubmitForApprovalAction`Record can be submitted for the current action context (no pending approval, flows exist, model policy allows it)`ApproveAction`User is an assigned approver and hasn't acted yet`RejectAction`User is an assigned approver and hasn't acted yet`CommentAction`A pending approval exists and user can act on it`DelegateAction`User is an assigned approver (original, not delegate)#### Per-Action Submit Buttons

[](#per-action-submit-buttons)

When your model defines `approvableActions()`, you can create dedicated submit buttons for each action using `actionKey()`:

```
use CoringaWc\FilamentActionApprovals\Actions\SubmitForApprovalAction;
use Filament\Support\Icons\Heroicon;

class EditPurchaseOrder extends EditRecord
{
    protected function getHeaderActions(): array
    {
        return [
            // Dedicated button for "submit" action — skips the action selector modal
            SubmitForApprovalAction::make('submitPO')
                ->actionKey('submit')
                ->label(__('Submit for Approval'))
                ->icon(Heroicon::OutlinedPaperAirplane),

            // Dedicated button for "cancel" action
            SubmitForApprovalAction::make('cancelPO')
                ->actionKey('cancel')
                ->label(__('Request Cancellation'))
                ->icon(Heroicon::OutlinedXMark)
                ->color('danger'),

            // Approval response actions
            ApproveAction::make(),
            RejectAction::make(),
            CommentAction::make(),
            DelegateAction::make(),
        ];
    }
}
```

When `actionKey()` is set:

- The action key selector modal is **skipped** entirely
- The action is only visible when there are matching flows for that specific `actionKey`
- If there's exactly one matching flow, submission happens with just a confirmation dialog (no form)
- If there are multiple matching flows, only the flow selector is shown (without the action selector)

This gives you full control to place specific approval submission buttons anywhere in your UI — in the header, in a custom action group, or even as table row actions.

### 5. Add the Relation Manager

[](#5-add-the-relation-manager)

Add the `ApprovalsRelationManager` to show approval history on any resource:

```
use CoringaWc\FilamentActionApprovals\RelationManagers\ApprovalsRelationManager;

class PurchaseOrderResource extends Resource
{
    public static function getRelations(): array
    {
        return [
            ApprovalsRelationManager::class,
        ];
    }
}
```

The relation manager shows a table of all approvals for the record. Each row has a "View" action that opens a **slide-over** with:

- Approval details (flow, status, submitter, dates)
- Grouped approval actions at the top of the infolist when the current user can act
- Step-by-step progress with approver names and received/required counts
- Full audit trail (submitted, approved, rejected, commented, delegated, escalated)

### 6. Add the Status Column

[](#6-add-the-status-column)

Show the latest approval status in any table:

```
use CoringaWc\FilamentActionApprovals\Columns\ApprovalStatusColumn;

class PurchaseOrderResource extends Resource
{
    public static function table(Table $table): Table
    {
        return $table->columns([
            TextColumn::make('title'),
            ApprovalStatusColumn::make(), // Badge: Pending, Approved, Rejected, Cancelled
        ]);
    }
}
```

### 7. Add the Status Section to Infolists (Optional)

[](#7-add-the-status-section-to-infolists-optional)

Use `ApprovalStatusSection` to display approval details inline on a View or Edit page. This is an alternative to the relation manager — useful when you want the approval details embedded directly in the page's infolist rather than in a separate tab or slide-over.

```
use CoringaWc\FilamentActionApprovals\Infolists\ApprovalStatusSection;

class ViewPurchaseOrder extends ViewRecord
{
    public function infolist(Infolist $infolist): Infolist
    {
        return $infolist->schema([
            // ... your other infolist sections ...
            ApprovalStatusSection::make(),
        ]);
    }
}
```

> **Note:** When using `ApprovalsRelationManager`, the `ApprovalStatusSection` may be redundant since the RM's slide-over already shows full approval details.

### 8. Programmatic Usage

[](#8-programmatic-usage)

Interact with the approval engine directly:

```
use CoringaWc\FilamentActionApprovals\Services\ApprovalEngine;

$engine = app(ApprovalEngine::class);

// Submit for approval
$approval = $engine->submit($purchaseOrder, $flow, auth()->id());

// Or via the model
$approval = $purchaseOrder->submitForApproval($flow);

// Approve a step
$stepInstance = $approval->currentStepInstance();
$engine->approve($stepInstance, auth()->id(), 'Approved — budget ok');

// Reject
$engine->reject($stepInstance, auth()->id(), 'Budget exceeded');

// Add a comment
$engine->comment($approval, auth()->id(), 'Please provide receipts');

// Delegate to another user
$engine->delegate($stepInstance, auth()->id(), $delegateUserId, 'On vacation');

// Cancel the approval
$engine->cancel($approval);
```

User Display Names
------------------

[](#user-display-names)

The package uses `UserDisplayName::resolve()` to display user names throughout the UI (infolists, columns, selects, audit trail). The resolution order is:

1. If the user model implements `Filament\Models\Contracts\HasName`, calls `getFilamentName()`
2. Falls back to the `name` attribute

This means if your User model uses Filament's `HasName` interface (which provides `getFilamentName()`), the package will automatically use the display name defined there (e.g., full name, username, or any custom format).

```
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasName;

class User extends Authenticatable implements FilamentUser, HasName
{
    public function getFilamentName(): string
    {
        return "{$this->first_name} {$this->last_name}";
    }
}
```

Approver Resolvers
------------------

[](#approver-resolvers)

### UserResolver

[](#userresolver)

Resolves specific user IDs as approvers:

```
'approver_resolver' => UserResolver::class,
'approver_config' => ['user_ids' => [1, 2, 3]],
```

### RoleResolver

[](#roleresolver)

Resolves all users with a given role (requires `spatie/laravel-permission`):

```
'approver_resolver' => RoleResolver::class,
'approver_config' => ['role' => 'manager'],
```

### CustomRuleResolver

[](#customruleresolver)

Define model-scoped custom rules directly on your model via `approvalCustomRules()`:

```
class PurchaseOrder extends Model
{
    use HasApprovals;

    public static function approvalCustomRules(): array
    {
        return [
            'department_head' => function (PurchaseOrder $model): array {
                return [$model->department->head_id];
            },
            'finance_team' => function (PurchaseOrder $model): array {
                return User::where('department', 'finance')->pluck('id')->all();
            },
        ];
    }
}
```

Use in a step:

```
'approver_resolver' => CustomRuleResolver::class,
'approver_config' => ['custom_rule' => 'department_head'],
```

The resolver is only available in the flow builder when the selected model defines `approvalCustomRules()`. Each key becomes a selectable option.

### Custom Resolver

[](#custom-resolver)

Implement the `ApproverResolver` contract:

```
use CoringaWc\FilamentActionApprovals\Contracts\ApproverResolver;

class HierarchyResolver implements ApproverResolver
{
    public function resolve(array $config, Model $approvable): array
    {
        return [$approvable->user->manager_id];
    }

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

    public static function configSchema(): array
    {
        return []; // Filament form components for the flow builder
    }

    public static function isAvailable(?string $modelClass = null): bool
    {
        return true; // Available for all models
    }
}
```

Register it in the config or plugin:

```
// config
'approver_resolvers' => [
    UserResolver::class,
    RoleResolver::class,
    HierarchyResolver::class,
],

// or per-panel
FilamentActionApprovalsPlugin::make()
    ->resolvers([UserResolver::class, HierarchyResolver::class])
```

Lifecycle Callbacks
-------------------

[](#lifecycle-callbacks)

Override these methods on your model to react to approval events:

MethodCalled When`onApprovalSubmitted(Approval $approval)`Model is submitted for approval`onApprovalApproved(Approval $approval)`All steps approved (approval complete)`onApprovalRejected(Approval $approval)`Approval rejected at any step`onApprovalCancelled(Approval $approval)`Approval is cancelled`onApprovalCommented(ApprovalAction $action)`Comment added`onApprovalDelegated(ApprovalStepInstance $si, $from, $to)`Step delegated`onApprovalStepCompleted(ApprovalStepInstance $si)`Individual step approved`onApprovalEscalated(ApprovalStepInstance $si)`SLA breach triggers escalationResubmission Policy
-------------------

[](#resubmission-policy)

Control whether a model can be resubmitted after a completed approval:

```
class Expense extends Model
{
    use HasApprovals {
        canSubmitForApproval as protected canSubmitForApprovalThroughFlow;
    }

    // By default, approved and cancelled approvals cannot be resubmitted.
    // Override when your domain should also block or allow other outcomes.
    public function allowsApprovalResubmission(): bool
    {
        $latest = $this->latestApproval();

        return ! $latest || $latest->status === ApprovalStatus::Rejected;
    }

    // Add project-specific submit rules on top of the default flow-approver policy
    public function canSubmitForApproval(?string $actionKey = null, int|string|null $userId = null): bool
    {
        return $this->user_id === ($userId ?? auth()->id())
            || $this->canSubmitForApprovalThroughFlow($actionKey, $userId);
    }
}
```

When you need different submit policies per approvable action, inspect the first parameter:

```
public function canSubmitForApproval(?string $actionKey = null, int|string|null $userId = null): bool
{
    return match ($actionKey) {
        'submit' => $this->user_id === ($userId ?? auth()->id())
            || $this->canSubmitForApprovalThroughFlow($actionKey, $userId),
        'cancel' => auth()->user()?->can('cancelPurchaseOrders') ?? false,
        default => false,
    };
}
```

If you do not override `canSubmitForApproval()`, the package default allows submission only for users resolved as approvers in a matching approval flow. If you do not override `allowsApprovalResubmission()`, the package default blocks a new submission after the latest approval ends as `approved` or `cancelled`, while still allowing retries after `rejected`.

SLA Enforcement
---------------

[](#sla-enforcement)

Configure per-step SLA deadlines:

```
$flow->steps()->create([
    'name' => 'Urgent Review',
    'sla_hours' => 4,
    'escalation_action' => EscalationAction::AutoApprove,
    // ...
]);
```

### Escalation Actions

[](#escalation-actions)

ActionBehavior`Notify`Sends an `ApprovalEscalatedNotification``AutoApprove`Automatically approves the overdue step`Reject`Automatically rejects the approval`Reassign`Reassigns to new approvers using the step's resolverThe SLA processor runs via the `approval:process-sla` command, automatically scheduled every minute when `schedule_sla_command` is `true`.

Events
------

[](#events)

EventPayload`ApprovalSubmitted``Approval $approval``ApprovalCompleted``Approval $approval``ApprovalRejected``Approval $approval``ApprovalCancelled``Approval $approval``ApprovalCommented``ApprovalAction $action``ApprovalDelegated``ApprovalStepInstance $stepInstance, int $fromUserId, int $toUserId``ApprovalStepCompleted``ApprovalStepInstance $stepInstance``ApprovalEscalated``ApprovalStepInstance $stepInstance`### Broadcasting

[](#broadcasting)

All events implement `ShouldBroadcast` but are **disabled by default**. Enable per-event broadcasting via config:

```
// config/filament-action-approvals.php
'broadcasting' => [
    'events' => [
        'submitted' => true,      // Enable ApprovalSubmitted broadcasting
        'approved' => true,       // Enable ApprovalCompleted broadcasting
        'rejected' => false,
        'cancelled' => false,
        'commented' => false,
        'delegated' => false,
        'step_completed' => false,
        'escalated' => false,
    ],
    'queue' => 'broadcasting',    // null = default queue
],
```

Events broadcast on the `approval-events` channel. The `BroadcastsConditionally` trait provides `broadcastWhen()` that checks the per-event config — zero overhead when disabled.

Super Admin Bypass
------------------

[](#super-admin-bypass)

When enabled, super admins can see and act on **all** approval actions (Approve, Reject, Comment, Delegate) regardless of being an assigned approver:

```
// config/filament-action-approvals.php
'super_admin' => [
    'enabled' => true,
    'roles' => ['super_admin'],    // spatie/laravel-permission roles
    'user_ids' => [1],             // explicit user IDs (always works)
    'hide_from_selects' => true,   // hide super admins from resolver selects
],
```

Check programmatically:

```
FilamentActionApprovalsPlugin::isSuperAdmin($userId);
```

When `spatie/laravel-permission` is not installed, only `user_ids` matching works. Role-based matching requires the package.

Enum-Based Approvable Actions
-----------------------------

[](#enum-based-approvable-actions)

Instead of returning a plain array from `approvableActions()`, you can use a backed enum with `HasLabel`:

```
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum PurchaseOrderAction: string implements HasLabel, HasColor, HasIcon
{
    case Submit = 'submit';
    case Cancel = 'cancel';

    public function getLabel(): string
    {
        return match ($this) {
            self::Submit => __('Submit for Processing'),
            self::Cancel => __('Request Cancellation'),
        };
    }

    public function getColor(): string
    {
        return match ($this) {
            self::Submit => 'success',
            self::Cancel => 'danger',
        };
    }

    public function getIcon(): string
    {
        return match ($this) {
            self::Submit => 'heroicon-o-paper-airplane',
            self::Cancel => 'heroicon-o-x-mark',
        };
    }
}
```

Then configure on your model using the `#[ApprovableActions]` attribute:

```
use CoringaWc\FilamentActionApprovals\Attributes\ApprovableActions;

#[ApprovableActions(PurchaseOrderAction::class)]
class PurchaseOrder extends Model
{
    use HasApprovals;
}
```

> **PHP 8.4 safety:** The `#[ApprovableActions]` attribute replaces the old `$approvableActionsEnum` property approach. PHP 8.4 fatals on trait property redefinition with different default values. Attributes are on the class, not the trait, avoiding this issue.

When an enum is configured, `approvableActions()` resolves labels from `getLabel()` automatically. You can also extract icon/color per action key:

```
use CoringaWc\FilamentActionApprovals\Support\ApprovableActionLabel;

ApprovableActionLabel::iconFor($model, 'submit');  // 'heroicon-o-paper-airplane'
ApprovableActionLabel::colorFor($model, 'submit'); // 'success'
```

Resource Customization
----------------------

[](#resource-customization)

Customize the built-in `ApprovalFlowResource` appearance via config:

```
// config/filament-action-approvals.php
'resource' => [
    'enabled' => true,
    'cluster' => \App\Filament\Admin\Clusters\Settings::class,
    'navigation_sort' => 5,
    'navigation_icon' => 'heroicon-o-cog',
    'show_widgets' => true,  // Show analytics widgets on the list page
],
```

Multi-Tenancy
-------------

[](#multi-tenancy)

Enable tenant-scoped approval flows:

```
// config/filament-action-approvals.php
'multi_tenancy' => [
    'enabled' => true,
    'column' => 'company_id',      // Your tenant foreign key
    'scope_approvers' => true,     // Also scope role-based resolvers
],
```

When enabled, `ApprovalFlow::forModel()` automatically scopes queries by the model's tenant column.

Database Schema
---------------

[](#database-schema)

The package creates the following tables:

TablePurpose`approval_flows`Flow definitions (name, approvable\_type, action\_key, active status)`approval_steps`Step definitions within a flow (order, resolver, SLA, escalation)`approvals`Runtime approval instances (polymorphic to approvable model)`approval_step_instances`Runtime step instances (status, assigned approvers, SLA tracking)`approval_actions`Audit trail of all actions (submit, approve, reject, comment, delegate, escalate)`approval_delegations`Delegation records (from\_user to\_user per step instance)Integration with filament-acl (Optional)
----------------------------------------

[](#integration-with-filament-acl-optional)

If you use [`coringawc/filament-acl`](https://github.com/CoringaWc/filament-acl), you can extend the built-in `ApprovalFlowResource` to add permission-aware access control:

```
composer require coringawc/filament-acl
```

Create a custom resource that extends the package resource:

```
namespace App\Filament\Admin\Resources;

use CoringaWc\FilamentAcl\Attributes\PermissionSubject;
use CoringaWc\FilamentAcl\Resources\Concerns\HasResourcePermissions;
use CoringaWc\FilamentActionApprovals\Resources\ApprovalFlows\ApprovalFlowResource as BaseResource;

#[PermissionSubject('ApprovalFlow')]
class ApprovalFlowResource extends BaseResource
{
    use HasResourcePermissions;
}
```

Then disable the built-in resource on the plugin and register your own:

```
FilamentActionApprovalsPlugin::make()
    ->flowResource(false) // Disable built-in resource
```

Translations
------------

[](#translations)

The package uses `filament-action-approvals::approval.*` keys for all user-facing strings. Translation files are in `resources/lang/{locale}/approval.php`.

Key groups:

GroupDescription`status.*`Approval status labels (pending, approved, etc.)`action_type.*`Action type labels (submitted, approved, etc.)`step_type.*`Step type labels (single, sequential, parallel)`step_status.*`Step instance status labels`escalation.*`Escalation action labels`actions.*`UI action button labels and messagesTo publish translations for customization:

```
php artisan vendor:publish --tag="filament-action-approvals-translations"
```

Testing
-------

[](#testing)

The package includes a workbench environment for development and testing:

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance90

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

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

Every ~0 days

Total

12

Last Release

47d ago

Major Versions

v1.0.0 → v2.0.02026-04-19

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/22261717?v=4)[Argemiro Dias](/maintainers/CoringaWc)[@CoringaWc](https://github.com/CoringaWc)

---

Top Contributors

[![CoringaWc](https://avatars.githubusercontent.com/u/22261717?v=4)](https://github.com/CoringaWc "CoringaWc (29 commits)")

---

Tags

laravelworkflowfilamentfilament-pluginapprovalsCoringaWc

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/coringawc-filament-action-approvals/health.svg)

```
[![Health](https://phpackages.com/badges/coringawc-filament-action-approvals/health.svg)](https://phpackages.com/packages/coringawc-filament-action-approvals)
```

###  Alternatives

[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[wsmallnews/filament-nestedset

Filament nestedset tree builder powered by kalnoy/nestedset with Filament v4 and v5 support

196.5k14](/packages/wsmallnews-filament-nestedset)[bezhansalleh/filament-google-analytics

Google Analytics integration for FilamentPHP

208175.5k8](/packages/bezhansalleh-filament-google-analytics)[dotswan/filament-map-picker

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

127173.7k3](/packages/dotswan-filament-map-picker)[jibaymcs/filament-tour

Bring the power of DriverJs to your Filament panels and start a tour !

12351.0k](/packages/jibaymcs-filament-tour)[aymanalhattami/filament-context-menu

context menu (right click menu) for filament

9939.0k](/packages/aymanalhattami-filament-context-menu)

PHPackages © 2026

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