padosoft / laravel-flow-admin
Professional UI/UX admin panel for padosoft/laravel-flow — pixel-perfect Blade + Alpine.js dashboard with KPIs, run timeline, approvals, outbox, definitions and ⌘K palette.
Requires
- php: ^8.3
- illuminate/config: ^13.0
- illuminate/http: ^13.0
- illuminate/routing: ^13.0
- illuminate/support: ^13.0
- illuminate/view: ^13.0
- padosoft/laravel-flow: ^1.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^11.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
README
Laravel Flow Admin
Pixel-perfect, dark-first admin panel for padosoft/laravel-flow — runs, approvals, outbox & definitions in one Blade + Alpine cockpit.
🚀 Quick Start · 📸 Screenshots · ⚙️ Configuration · 🔒 Authorization · 🤝 Contributing
📚 Table of Contents
- ✨ Why this package
- 🎯 Features
- 📸 Screenshots
- 📦 Requirements
- 🚀 Quick Start (5 minutes)
- 📖 Step-by-Step Setup
- ⚙️ Configuration
- 🔒 Authorization (mutations)
- 🎨 Customization
- 🧪 Demo Mode (no DB needed)
- 🗺️ Routes
- 🏛️ Architecture
- 🤖 AI Vibe Coding Pack
- ⚖️ Comparison
- 🛣️ Roadmap
- ✅ Quality Gates
- 🤝 Contributing
- 🔐 Security
- 📜 License
- 💜 Credits
✨ Why this package
padosoft/laravel-flow is intentionally headless — a deterministic, queue-driven workflow engine you can drop into any Laravel app.
laravel-flow-admin is the operator console for it. A production-style control plane for runs, approvals, outbox webhooks and configuration — without leaking the engine's internal namespaces into your app.
Think Horizon for queues, Pulse for metrics — and Flow Admin for the lifecycle of long-running, multi-step business workflows.
🎯 Features
- 📊 Overview dashboard — KPI tiles, sparklines, recent runs, queue health, error rate.
- 🏃 Runs index & detail — filterable list, full timeline (timeline / Gantt / DAG), payload diff, retry/cancel actions.
- ✅ Approvals inbox — pending decisions with one-click approve / reject through your own authorizer.
- 📤 Webhook outbox — delivery state, replay failed jobs, inspect headers/payloads.
- 📋 Flow definitions — registered workflows, version, last activity at a glance.
- ⚡ ⌘K command palette — jump anywhere in two keystrokes.
- 🎨 Pixel-perfect dark + light themes — persisted in cookie, switchable per user.
- 🛡️ Deny-by-default authorizer — every mutation goes through your
ActionAuthorizer. No accidents. - 🔁 Auto-refreshing pages — configurable polling (
/flow/api/live). - 🧱 Adapter pattern —
eloquentfor prod,arrayfor demos / E2E (deterministic seed-42 fixtures). - 🧪 Battle-tested — 101 PHPUnit tests, 18 Playwright scenarios across Chromium / Firefox / WebKit.
- 📦 Zero-coupling — built on a public
Contracts\*surface; engine internals stay@internal.
📸 Screenshots
📦 Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.3 (also tested on 8.4) |
| Laravel | ^13.0 |
padosoft/laravel-flow |
^1.0 |
| Node.js (only if you want to rebuild assets) | >=20 |
| Database | any Laravel-supported driver (or array adapter for demos) |
💡 You do not need Node.js to use this package. Pre-built assets ship inside the package and are publishable via
vendor:publish.
🚀 Quick Start (5 minutes)
# 1. Install both packages composer require padosoft/laravel-flow-admin # 2. Publish config + assets php artisan vendor:publish --tag=flow-admin-config php artisan vendor:publish --tag=flow-admin-assets # 3. Run flow-engine migrations (from padosoft/laravel-flow) php artisan migrate # 4. Visit the admin panel php artisan serve # 👉 http://localhost:8000/flow
That's it. The panel is read-only by default (deny-all authorizer) so you can safely browse production data on day 1, then opt-in to mutations when you've wired your permission rules.
📖 Step-by-Step Setup
1. Install the underlying engine
laravel-flow-admin is a UI on top of padosoft/laravel-flow. If you don't already have it, install it first:
composer require padosoft/laravel-flow php artisan vendor:publish --tag=flow-config php artisan vendor:publish --tag=flow-migrations php artisan migrate
This creates the flow_runs, flow_steps, flow_approvals, flow_webhook_outbox and related tables that this admin panel reads from.
📖 Full engine docs: github.com/padosoft/laravel-flow
2. Install the admin panel
composer require padosoft/laravel-flow-admin
The service provider is auto-discovered. Routes, views, config and migrations are loaded out-of-the-box from the package.
3. Publish assets and config
# Publish the config (config/flow-admin.php) — recommended php artisan vendor:publish --tag=flow-admin-config # Publish compiled CSS/JS to public/vendor/flow-admin (required for styling) php artisan vendor:publish --tag=flow-admin-assets # Optional: publish Blade views to resources/views/vendor/flow-admin (for customization) php artisan vendor:publish --tag=flow-admin-views
⚠️ Don't skip
flow-admin-assetsin production. The panel relies on the published CSS/JS bundle.
4. Configure middleware & routes
By default the panel mounts at /flow and runs through web,auth middleware. Override with environment variables:
FLOW_ADMIN_PREFIX=ops/flow FLOW_ADMIN_MIDDLEWARE="web,auth,verified,can:access-flow-admin" FLOW_ADMIN_THEME=dark FLOW_ADMIN_STEP_VIZ=timeline FLOW_ADMIN_POLLING_MS=4000
🛡️ If you set
FLOW_ADMIN_MIDDLEWARE=""we fall back to['web']instead of leaving the panel unauthenticated. Setting it empty was a known footgun, so we close it explicitly.
5. Wire your authorizer
The panel ships with DenyAllAuthorizer so every mutation (resume, reject, replay, cancel, retry-webhook) is blocked by default. To enable mutations, implement your own:
// app/Flow/AdminAuthorizer.php namespace App\Flow; use Padosoft\LaravelFlowAdmin\Contracts\ActionAuthorizer; final class AdminAuthorizer implements ActionAuthorizer { public function canResume(string $runId): bool { return auth()->user()?->can('flow.runs.resume') ?? false; } public function canCancel(string $runId): bool { return auth()->user()?->can('flow.runs.cancel') ?? false; } public function canApprove(string $approvalId): bool { return auth()->user()?->can('flow.approvals.act') ?? false; } public function canReject(string $approvalId): bool { return auth()->user()?->can('flow.approvals.act') ?? false; } public function canReplayWebhook(string $outboxId): bool { return auth()->user()?->can('flow.outbox.replay') ?? false; } }
Bind it in AppServiceProvider::register():
use Padosoft\LaravelFlowAdmin\Contracts\ActionAuthorizer; use App\Flow\AdminAuthorizer; $this->app->bind(ActionAuthorizer::class, AdminAuthorizer::class);
…or set 'authorizer' => App\Flow\AdminAuthorizer::class in config/flow-admin.php.
6. Visit the panel
php artisan serve
Open http://localhost:8000/flow and you should see the dashboard. Press ⌘K (or Ctrl+K) anywhere to open the command palette.
⚙️ Configuration
All keys live in config/flow-admin.php. They are also overridable via environment variables.
| Key | Env | Default | Description |
|---|---|---|---|
prefix |
FLOW_ADMIN_PREFIX |
flow |
URI prefix for all routes (/flow, /flow/runs, …). |
middleware |
FLOW_ADMIN_MIDDLEWARE |
web,auth |
Comma-separated middleware stack. Empty/whitespace falls back to ['web']. |
adapter |
FLOW_ADMIN_ADAPTER |
eloquent |
eloquent (prod) or array (deterministic demo fixtures). |
authorizer |
— | DenyAllAuthorizer |
FQCN of your ActionAuthorizer implementation. |
polling_interval_ms |
FLOW_ADMIN_POLLING_MS |
4000 |
Auto-refresh interval for live pages. |
theme_default |
FLOW_ADMIN_THEME |
dark |
dark or light. Per-user override stored in flow_admin_theme cookie. |
step_viz_default |
FLOW_ADMIN_STEP_VIZ |
timeline |
Default visualization on run detail: timeline, gantt, or dag. |
🔒 Authorization (mutations)
Every mutation route (resume, reject, replay, cancel, retry-webhook) consults your ActionAuthorizer before the controller runs. This is non-negotiable: there is no "global admin" bypass and no way to short-circuit the gate from a Blade view.
Public extension surface (semver-stable from v0.1.0 →):
Padosoft\LaravelFlowAdmin\Contracts\ActionAuthorizerPadosoft\LaravelFlowAdmin\Contracts\ReadModelAdapterPadosoft\LaravelFlowAdmin\Contracts\ViewModelFactory(and family)config/flow-admin.phpkeys- Publish tags:
flow-admin-config,flow-admin-views,flow-admin-assets - Route names:
flow-admin.*
Everything under Adapters\, Http\Controllers\, Support\, ViewModels\ is internal and may change between minor versions until v1.0.
🎨 Customization
Override Blade views
php artisan vendor:publish --tag=flow-admin-views
Edit anything under resources/views/vendor/flow-admin/. Component slots and named layout sections are preserved across upgrades.
Override CSS tokens
The published bundle exposes CSS custom properties for colors, radii, spacing and font sizing. Wrap the panel in a custom theme by overriding tokens:
:root[data-flow-admin-theme="dark"] { --flow-admin-color-accent: #6366f1; --flow-admin-color-bg: #0b0d12; }
Switch theme programmatically
POST /flow/theme X-CSRF-TOKEN: … theme=dark|light
🧪 Demo Mode (no DB needed)
For showcases, screenshots, or end-to-end tests you can bypass the database entirely:
FLOW_ADMIN_ADAPTER=array
The ArrayReadModelAdapter produces deterministic fixtures (seed=42) so KPI numbers, run IDs and timelines are reproducible across screenshots and Playwright runs.
🗺️ Routes
All routes live under the configured prefix (default /flow) and the flow-admin.* route-name namespace.
| Method | URI | Name | Purpose |
|---|---|---|---|
GET |
/ |
flow-admin.overview |
Dashboard |
GET |
/runs |
flow-admin.runs.index |
Runs list |
GET |
/runs/{id} |
flow-admin.runs.show |
Run detail + timeline |
GET |
/approvals |
flow-admin.approvals.index |
Approvals inbox |
GET |
/outbox |
flow-admin.outbox.index |
Webhook outbox |
GET |
/definitions |
flow-admin.definitions.index |
Registered flows |
GET |
/settings |
flow-admin.settings.index |
Effective configuration |
GET |
/api/search |
flow-admin.api.search |
⌘K palette backend |
GET |
/api/live |
flow-admin.api.live |
Live polling JSON |
POST |
/theme |
flow-admin.theme.toggle |
Persist theme cookie |
🏛️ Architecture
HTTP request
│
├─► routes/flow-admin.php (prefix + middleware + name)
│
├─► Http/Controllers/*Controller ◄── thin: request → DTO → factory → view
│ │
│ ├─► Http/Requests/*Request (validation, sorting, filtering DTOs)
│ ├─► ViewModels/*Factory (read-side view assembly)
│ └─► Contracts/ActionAuthorizer (gate for any mutation)
│
├─► Adapters/Eloquent | Array (ReadModelAdapter implementations)
│ └─► reads flow_* tables OR seed-42 fixtures
│
└─► resources/views/* + Alpine stores + Vite bundle
Design source-of-truth lives under .design-source/project/ (pixel reference) and is enforced through Playwright visual regression on chromium / firefox / webkit.
🤖 AI Vibe Coding Pack
This repository ships a contributor pack under .claude/:
- Rules — Laravel 13 defaults, admin-panel UX, query optimization, naming conventions, exception handling, logging security.
- Skills — orchestrators for
create-admin-interface,playwright-enterprise-tester,copilot-pr-review-loop,pre-push-self-review,test-count-readme-sync. - Runbooks — macro/subtask branch workflow used during initial implementation.
If you build with Claude Code or another agent, copy .claude/ into your downstream project for a head start.
⚖️ Comparison
| Tool | Workflow runs lifecycle | Approvals UI | Webhook outbox | Drop-in for Laravel Flow |
|---|---|---|---|---|
| Laravel Flow Admin | ✅ | ✅ | ✅ | ✅ |
| Laravel Horizon | ⚠️ queue/job only | ❌ | ❌ | ❌ |
| Laravel Pulse | ⚠️ app metrics | ❌ | ❌ | ❌ |
| Custom dashboard | depends | depends | depends | ⏳ slow to bootstrap |
| Temporal UI | ✅ (for Temporal) | ✅ | ⚠️ | ❌ |
🛣️ Roadmap
- v0.1 — core pages, eloquent + array adapters, theme cookie, ⌘K palette, Playwright matrix.
- v0.1.1 — public release hardening, README polish, GitHub release artifacts.
- v0.2 — bulk actions on runs, saved filter presets, CSV/JSON export.
- v0.3 — Pulse-style sparkline cards, alerting hooks.
- v1.0 — frozen public surface, SemVer guarantees, downstream-stable Adapters.
✅ Quality Gates
Every push runs through this gate (matrix php: 8.3, 8.4 × laravel: 13):
composer validate --strict --no-check-publish composer format:test # Laravel Pint composer analyse # PHPStan / Larastan level 8 composer test # PHPUnit — 101 tests, 584 assertions npm run lint # ESLint flat config npm run build # Vite build verification npm run test:e2e # Playwright on chromium + firefox + webkit
Latest local run: 101 tests / 584 assertions / 18 E2E scenarios passed.
🤝 Contributing
PRs welcome! Please:
- Open an issue first for non-trivial changes.
- Branch from
mainastask/<short-name>orsubtask/<short-name>. - Run the full local gate (above) before pushing.
- Add tests — unit, feature, or Playwright depending on the change.
- Update
docs/PROGRESS.mdif your PR closes a roadmap item, anddocs/LESSON.mdif you discover a reusable insight.
See CONTRIBUTING.md for the full workflow and CODE_OF_CONDUCT.md.
🔐 Security
If you discover a security vulnerability, please do not open a public issue. Email hello@padosoft.com directly. See SECURITY.md for our disclosure policy.
📜 License
Apache-2.0 © Padosoft. See LICENSE for the full text.
💜 Credits
- Padosoft — package author and maintainer.
padosoft/laravel-flow— the headless workflow engine this panel operates.- Laravel, Alpine.js, Vite, Playwright — the giants whose shoulders this is built on.
Made with ❤️ by Padosoft — workflows are hard, the UI shouldn't be.






