PHP code example of coringawc / filament-action-approvals

1. Go to this page and download the library: Download coringawc/filament-action-approvals library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

coringawc / filament-action-approvals example snippets


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,
    ],

    // Intercept native Filament operations when models declare approval operations
    'operations' => [
        'intercept' => false,
        'modal_callout' => true,
    ],

    // 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' => '',
];

use CoringaWc\FilamentActionApprovals\FilamentActionApprovalsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentActionApprovalsPlugin::make()
                ->flowResource()      // Enable the ApprovalFlow 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
                ->interceptOperations() // Intercept EditAction/DeleteAction when an approval flow exists
                ->operationModalCallout(false) // Disable the automatic approval callout in intercepted action modals
                ->navigationGroup('Workflow'),  // Override navigation group
        ]);
}

use CoringaWc\FilamentActionApprovals\Actions\ListApprovalsAction;

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

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

ListApprovalsAction::make()
    ->forApprovable(fn (): Model => $this->getRecord())
    ->hideWhenNoActionableApprovals(false);

use CoringaWc\FilamentActionApprovals\FilamentActionApprovalsPlugin;
use CoringaWc\FilamentActionApprovals\Widgets\ContextualApprovalsTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;

FilamentActionApprovalsPlugin::make()
    ->configureContextualApprovalsTableUsing(
        fn (Table $table, ContextualApprovalsTable $widget): Table => MyApprovalsTable::configure($table),
    )
    ->scopeContextualApprovalsUsing(
        fn (Builder $query, ContextualApprovalsTable $widget): Builder => MyApprovalResource::scopeVisibleApprovals($query),
    );

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

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']);
    }
}

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<string, string>
     */
    public static function approvableActions(): array
    {
        return [
            'submit' => __('Submit for Processing'),
            'cancel' => __('Request Cancellation'),
        ];
    }
}

use CoringaWc\FilamentActionApprovals\Attributes\ApprovableOperation;
use CoringaWc\FilamentActionApprovals\Concerns\HasApprovals;
use CoringaWc\FilamentActionApprovals\Enums\ApprovalOperation;

#[ApprovableOperation(
    operation: ApprovalOperation::Update,
    actionKey: 'purchase-order.edit',
    fields: ['title', 'amount'],
)]
#[ApprovableOperation(
    operation: ApprovalOperation::Delete,
    actionKey: 'purchase-order.delete',
)]
class PurchaseOrder extends Model
{
    use HasApprovals;
}

use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;

EditAction::make();
DeleteAction::make();

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]],
    '

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
        ];
    }
}

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(),
        ];
    }
}

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(),
        ];
    }
}

use CoringaWc\FilamentActionApprovals\RelationManagers\ApprovalsRelationManager;

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

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
        ]);
    }
}

use CoringaWc\FilamentActionApprovals\Infolists\ApprovalStatusSection;

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

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);

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_resolver' => UserResolver::class,
'approver_config' => ['user_ids' => [1, 2, 3]],

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

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();
            },
        ];
    }
}

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

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
    }
}

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

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

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);
    }
}

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,
    };
}

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

// 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
],

// 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
],

FilamentActionApprovalsPlugin::isSuperAdmin($userId);

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',
        };
    }
}

use CoringaWc\FilamentActionApprovals\Attributes\ApprovableActions;

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

use CoringaWc\FilamentActionApprovals\Support\ApprovableActionLabel;

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

// 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
],

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

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;
}

FilamentActionApprovalsPlugin::make()
    ->flowResource(false) // Disable built-in resource
bash
php artisan vendor:publish --tag="filament-action-approvals-migrations"
php artisan migrate
bash
php artisan vendor:publish --tag="filament-action-approvals-config"
bash
php artisan vendor:publish --tag="filament-action-approvals-translations"