Filament UI

Alpha notice: dbflowlabs/filament is in active alpha. Page slugs, config keys, and permission names may change between tags. Pin exact tags in production (for example 0.1.0-alpha.1@alpha).

DBFlow Filament is the Standard operational UI layer for workflow administration. It runs on top of dbflowlabs/core and gives operators a Filament-native way to work with tasks, instances, and definitions.

DBFlow Filament operates workflows. DBFlow Pro (dbflowlabs/filament-pro) designs workflows visually. Pro is Early Access / Preview — see Pro Visual Builder and Preview Limitations. Standard UI details are below.

Filament UI is only half of an integration. You still need Core runtime setup: migrations, user resolution, workflow definitions (code or UI), assignee resolvers, sync, and host business triggers (DBFlow::start()). Complete the Host Integration Checklist first.

Security: The package ships with AllowAllPermissionChecker by default. Replace permission_checker_class in config/dbflow-filament.php before any shared or production environment — otherwise every authenticated panel user can access workflow pages and approve/reject tasks.

What you get

After DBFlowFilamentPanel::register($panel), the package exposes:

Surface Class Default URL (panel path admin)
My Workflow Tasks MyWorkflowTasks /admin/dbflow/my-workflow-tasks
Workflow Instances WorkflowInstances /admin/dbflow/workflow-instances
Instance detail ViewWorkflowInstance /admin/dbflow/workflow-instances/{record}
Workflow definitions WorkflowResource /admin/workflows

Routing notes:

  • Package pages use {panel_path}/{route_prefix}/…. Default route_prefix is dbflow (config('dbflow-filament.route_prefix')).
  • WorkflowResource is a normal Filament resource registered at the panel root — its slug is workflows, not under the dbflow prefix.
  • Named routes use route_name_prefix (default dbflow.filament.).
  • If your panel path is empty (->path('')), My Tasks becomes /dbflow/my-workflow-tasks.

The package does not auto-register pages during boot(). You must call DBFlowFilamentPanel::register($panel) from your PanelProvider (or register pageClasses() / resourceClasses() manually).

Install

composer require "dbflowlabs/filament:0.1.0-alpha.1@alpha" "dbflowlabs/core:^0.1.0-alpha.1@alpha"

If your project already allows alpha stability, you may omit @alpha.

php artisan vendor:publish --tag=dbflow-filament-config
php artisan migrate

dbflowlabs/core is required and installs automatically as a dependency.

Two configuration files

Config file Primary switch What it controls
config/dbflow.php enabled Core runtime feature flag (provider still boots during alpha)
config/dbflow-filament.php enabled Whether registered Filament pages/resources are exposed

DBFlowFilamentPanel checks dbflow-filament.enabled and panel_registration_mode. It does not read dbflow.enabled.

Hosts that want a single product feature flag should gate both configs (and panel registration) in application code:

if ($this->shouldRegisterDbflow()) {
  return DBFlowFilamentPanel::register($panel);
}

Typical pattern: wrap registration on config('dbflow.enabled') and a host pilot flag, not only dbflow-filament.enabled.

Register the panel

Call DBFlowFilamentPanel::register() inside your PanelProvider. This is the supported integration — do not use a separate Filament plugin class.

<?php

namespace App\Providers\Filament;

use DbflowLabs\Filament\Support\DBFlowFilamentPanel;
use Filament\Panel;
use Filament\PanelProvider;

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        $panel = $panel
            ->id('admin')
            ->path('admin')
            // ... your existing panel configuration
        ;

        if ($this->shouldRegisterDbflow()) {
            return DBFlowFilamentPanel::register($panel);
        }

        return $panel;
    }

    private function shouldRegisterDbflow(): bool
    {
        return (bool) config('dbflow-filament.enabled', true)
            && (bool) config('dbflow.enabled', true);
        // Hosts often add a product pilot flag here as well.
    }
}

DBFlowFilamentPanel reads config/dbflow-filament.php to decide which pages and resources to attach. Set panel_registration_mode to disabled if you prefer manual registration via DBFlowFilamentPanel::pageClasses() and resourceClasses().

Feature toggles

Config key Env (optional) Default Purpose
enabled DBFLOW_FILAMENT_ENABLED true Master Filament package toggle
panel_registration_mode DBFLOW_FILAMENT_PANEL_REGISTRATION explicit explicit or disabled
enable_my_tasks_page DBFLOW_FILAMENT_MY_TASKS true My Workflow Tasks page
enable_my_task_actions DBFLOW_FILAMENT_MY_TASK_ACTIONS true Approve/reject on tasks page
enable_workflow_instances_page DBFLOW_FILAMENT_INSTANCES true Instance list + detail
enable_workflow_definition_resource DBFLOW_FILAMENT_DEFINITIONS true Workflow Definition resource
enable_logs_timeline DBFLOW_FILAMENT_LOGS_TIMELINE true Audit timeline on instance detail
require_reject_note DBFLOW_FILAMENT_REQUIRE_REJECT_NOTE true Require note when rejecting from tasks UI
reject_strategy DBFLOW_FILAMENT_REJECT_STRATEGY end Core RejectTask strategy from My Tasks

Example .env:

DBFLOW_FILAMENT_ENABLED=true
DBFLOW_FILAMENT_MY_TASKS=true
DBFLOW_FILAMENT_INSTANCES=true
DBFLOW_FILAMENT_DEFINITIONS=true
DBFLOW_FILAMENT_LOGS_TIMELINE=true
DBFLOW_FILAMENT_REJECT_STRATEGY=end

You can substitute custom page classes — for example view_workflow_instance_page_class — when you need host-specific layouts. The dbflow-demo project uses DemoViewWorkflowInstance for a friendlier instance detail page.

Extension contracts

Hosts customize presentation and authorization by binding class strings in config/dbflow-filament.php (*_class keys). Prefer class bindings over legacy callables (permission_checker, workflowable_label_resolver, status_badge_mapper).

Contract Config key Purpose
PermissionChecker permission_checker_class Gate package pages, resources, and table actions
WorkflowableLabelResolver workflowable_label_resolver_class Human-readable labels for workflowable records
UserDisplayResolver user_display_resolver_class Actor names in tables and timelines
UserAssigneeOptionsResolver user_assignee_options_resolver_class User picker options in definition editors
PermissionAssigneeOptionsResolver permission_assignee_options_resolver_class Labels for permission/callback assignee keys in editors
StatusBadgeMapper status_badge_mapper_class Badge color/label for workflow statuses
WorkflowDefinitionEditorResolver workflow_definition_editor_resolver Replace the standard linear editor (Pro uses this hook)

Default permission_checker_class is DbflowLabs\Filament\Support\AllowAllPermissionChecker — replace it in every non-local environment.

Permission abilities

Ability strings are configured under permissions in config/dbflow-filament.php. WorkflowFilamentPermissions resolves them before calling PermissionChecker::can().

Default ability names:

dbflow.tasks.view
dbflow.tasks.approve
dbflow.tasks.reject
dbflow.workflow_instances.view
dbflow.workflow_instances.view_any
dbflow.definitions.view
dbflow.definitions.create
dbflow.definitions.update
dbflow.definitions.delete
dbflow.definitions.validate
dbflow.definitions.publish
dbflow.definitions.disable
dbflow.definitions.enable
dbflow.definitions.archive
dbflow.definitions.copy

dbflow.tasks.view is required for the My Workflow Tasks navigation item. If navigation is missing but direct URLs work, check your PermissionChecker mapping first.

Permission assignees vs Core resolvers

Layer API Purpose
Filament definition editor PermissionAssigneeOptionsResolver Human-readable labels for assignee keys authors can pick
Core runtime DBFlow::registerAssigneeResolver($key, …) Resolves user IDs when a workflow runs

For assignees.type: permission (or callback), the JSON value / callback must match the same registry key you register in Core — it is not a Spatie permission string or Laravel Gate ability. See Code-defined Workflows.

Runtime approval identity still flows through Core's UserResolver (config/dbflow.php). Filament contracts are display and admin UX only.

Host model links

Implement DbflowLabs\Core\Contracts\WorkflowRouteResolvable on workflowable models so instance and task tables can link back to your Filament resource:

public function getWorkflowShowUrl(): ?string
{
    return PurchaseOrderResource::getUrl('edit', ['record' => $this]);
}

My Tasks actions

The My Tasks table uses MyWorkflowTaskTableActions, which delegates to MyWorkflowTaskActionRunner. That runner calls Core's ApproveTask and RejectTask actions — the same runtime entrypoints as DBFlow::approve() and DBFlow::reject().

Reject strategy on the My Tasks page defaults to config('dbflow-filament.reject_strategy') (commonly endRejectStrategy::End).

Workflow definitions resource

WorkflowResource manages draft definitions stored in dbflow_workflows / dbflow_workflow_versions. The Standard linear editor ships with the package. Pro may replace the editor through workflow_definition_editor_resolver when installed.

Code-synced workflows and the definition editor

WorkflowResource::definitionEditorSection() is visible only when Workflow::hasDraft() is true.

How the workflow was created Draft state after sync
Created via WorkflowResource → Create Draft exists (CreateWorkflowDraft) — editor shows
Synced from WorkflowDefinitionProvider Published version exists, no draft — edit page metadata only, no node editor

SyncWorkflowDefinitions writes published versions; it does not populate draft_definition. This is a common “sync succeeded but the edit form is empty” surprise.

Host options:

  1. Seed a draft after sync (recommended for code-first pilots that still want the Standard editor):
use DbflowLabs\Core\Actions\SaveWorkflowDraft;
use DbflowLabs\Core\Models\Workflow;

$workflow = Workflow::query()->where('key', 'your_workflow_key')->first();

if ($workflow && ! $workflow->hasDraft() && $workflow->hasPublishedVersion()) {
    $definition = $workflow->currentDefinition();
    $definition['key'] = $workflow->key;
    $definition['name'] = $workflow->name;

    app(SaveWorkflowDraft::class)->handle($workflow, $definition);
}

Run this after SyncWorkflowDefinitions::handle() in boot, deploy, or your own Artisan command. Skip workflows with source = ui or workflows that already have a draft.

  1. Disable the definition resource (enable_workflow_definition_resource = false) when definitions are owned entirely by code or Pro.

  2. Treat UI edits as ops-only — publishing from Filament creates a new version; code sync behaviour for source = code vs source = ui differs. Document ownership for your team before operators publish from the UI.

Customize navigation

// config/dbflow-filament.php
'navigation_group' => 'Workflow',
'navigation_sort' => [
    'my_tasks' => 10,
    'workflow_instances' => 20,
    'workflow_definitions' => 25,
],

Optional should_register_navigation callable gates all package navigation items for the current user.

Troubleshooting

Symptom Likely cause What to check
No Workflow navigation group Panel not registered or Filament disabled DBFlowFilamentPanel::register(), dbflow-filament.enabled, panel_registration_mode !== disabled
Navigation missing but routes work Permission mapping PermissionChecker must allow dbflow.tasks.view
Empty task list after submit Assignee resolution Resolver registered? Returns user IDs? Current user in assignee set?
Unknown assignee resolver on start Missing Core registration DBFlow::registerAssigneeResolver() for each permission / callback key
404 on package page URLs Panel path mismatch Compare {panel_path}/{route_prefix}/… with Panel::path()
Definition edit page has no node form No draft after code sync Seed draft with SaveWorkflowDraft or create via WorkflowResource
Everyone can approve Default permission checker Replace AllowAllPermissionChecker
UI works but actions fail Core auth / runtime DBFLOW_AUTH_MODEL, assignee IDs; Filament does not gate on dbflow.enabled

End-to-end Filament checklist

  1. Install Core + Filament alpha packages; run migrations.
  2. Publish dbflow-filament-config; set permission_checker_class (not allow-all).
  3. Register DBFlowFilamentPanel::register($panel) behind host feature flags.
  4. Register providers, resolvers, hooks; run SyncWorkflowDefinitions.
  5. If using the definition editor for code workflows, seed drafts after sync (see above).
  6. Call DBFlow::start() from host business actions; guard downstream operations.
  7. Log in as assignee → My Workflow Tasks → approve or reject.

What's next

Something wrong? Open an issue on GitHub