Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# Crash dumps
erl_crash.dump

# Test scratch space
/tmp/

# Workspace-level generated docs (per-project ignores live in each subdir)
/doc/
/_build/
Expand Down
34 changes: 30 additions & 4 deletions durable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,36 @@ mix durable.cleanup --older-than 7d --status completed # only completed, older
mix durable.cleanup --older-than 24h --dry-run # preview what would be deleted
```

## Dashboard

`durable_dashboard` is a LiveView-first console for monitoring and managing
running workflows. Mount it into your Phoenix router with one line:

```elixir
defmodule MyAppWeb.Router do
use MyAppWeb, :router

# ...your existing pipelines and scopes...

use DurableDashboard.Router, mount: "/dashboard", durable: MyApp.Durable
end
```

The macro adds the dashboard's pipelines, asset routes, and live routes
in-place — no `forward`, no extra endpoint. You get:

- Overview with live status counts and recent executions
- Workflow list with filters, search, and pagination
- Workflow detail with summary, ReactFlow graph, topology, logs, I/O,
and child execution history
- Pending inputs queue
- Schedules list with toggle / trigger actions
- Settings view
- ⌘K command palette

See [`durable_dashboard/README.md`](durable_dashboard/) for the full
option list and design notes.

## Guides

- [Branching](guides/branching.md) - Conditional flow control
Expand All @@ -533,10 +563,6 @@ mix durable.cleanup --older-than 24h --dry-run # preview what would be
- [Waiting](guides/waiting.md) - Sleep, events, human input
- [Orchestration](guides/orchestration.md) - Parent/child workflow composition

## Coming Soon

- Phoenix LiveView dashboard

## License

MIT
18 changes: 1 addition & 17 deletions durable_dashboard/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,23 +498,7 @@ The step→child mapping is sourced from the parent's context:

---

## 12. Dead code

The following exist in the tree but are not reachable from any live route:

- `assets/src/{main.tsx, App.tsx, views/, hooks/, components/{shared,ui,workflow}/}`
— pre-LiveView React SPA. Only `assets/src/v2/main.ts` (+ what it
imports from `lib/` and `v2/react/`) is loaded by `Layouts.root_v2/1`.
- `lib/durable_dashboard/live/dashboard_live.ex` — earlier monolithic
`DashboardLive`; the router mounts the per-page LVs
(`OverviewLive`, `WorkflowsLive`, `WorkflowLive`, `InputsLive`,
`SchedulesLive`, `SettingsLive`) instead.
- `Layouts.root/1` — paired with the v1 SPA above; only `root_v2/1` is
wired.

Audits should skip these files. Removing them cleanly is its own PR.

## 13. Updating this doc
## 12. Updating this doc

When you ship a UI change that establishes new precedent (a new
component, a new color use, a new animation), update this doc in the same
Expand Down
94 changes: 94 additions & 0 deletions durable_dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# DurableDashboard

LiveView-first web console for the [Durable](https://hex.pm/packages/durable)
workflow engine.

The dashboard renders server-side via Phoenix LiveView, with a single
ReactFlow island for the workflow-graph view. No SPA, no separate API —
just one router macro.

## Features

- **Overview** — live status counts and recent executions
- **Workflows** — searchable, filterable list with pagination
- **Workflow detail** — summary, flow graph, topology, logs, I/O, and
child-execution history
- **Pending inputs** — surface and resolve human-in-the-loop steps
- **Schedules** — toggle, trigger, and inspect cron-driven workflows
- **Settings** — inspect Durable's runtime configuration
- **⌘K command palette** — keyboard-driven navigation

## Installation

Add `durable_dashboard` alongside `durable`:

```elixir
def deps do
[
{:durable, "~> 0.0.0-alpha"},
{:durable_dashboard, "~> 0.0.0-alpha"}
]
end
```

## Usage

Mount the dashboard at any path in your host router:

```elixir
defmodule MyAppWeb.Router do
use MyAppWeb, :router

# ...your existing pipelines and scopes...

use DurableDashboard.Router, mount: "/dashboard", durable: MyApp.Durable
end
```

The macro emits the dashboard's pipelines, asset routes, and live routes
inline in your router. It must live at the **top level** — not inside a
`scope` or `pipe_through` block, since it defines its own pipelines.

### Options

- `:mount` — URL prefix the dashboard mounts at (required)
- `:durable` — Durable instance name (default: `Durable`)
- `:live_socket_path` — path your endpoint uses for `Phoenix.LiveView.Socket`
(default: `"/live"`)
- `:on_mount` — list of `on_mount` hooks the host wants to inject for
auth or other concerns (default: `[]`)

### URL surface

The macro emits these routes under your `:mount` prefix:

- `GET /` — Overview
- `GET /workflows` — list
- `GET /workflows/:id[/:tab]` — detail
- `GET /inputs`
- `GET /schedules`
- `GET /settings`
- `GET /__assets__/*` — CSS/JS/font assets

## Authentication

Inject your host app's auth via `:on_mount` hooks — the dashboard does
not ship its own auth layer:

```elixir
use DurableDashboard.Router,
mount: "/dashboard",
durable: MyApp.Durable,
on_mount: [{MyAppWeb.UserAuth, :ensure_authenticated}]
```

## Design

`DESIGN.md` codifies the design language: tokens, typography, spacing,
motion, status semantics, component primitives, and composition
patterns. New visual decisions are made there first, then applied in
code.

## License

MIT
25 changes: 0 additions & 25 deletions durable_dashboard/assets/components.json

This file was deleted.

13 changes: 1 addition & 12 deletions durable_dashboard/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,13 @@
},
"dependencies": {
"@dagrejs/dagre": "^1.1.4",
"@fontsource-variable/geist": "^5.2.8",
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@xyflow/react": "^12.8.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"lucide-react": "^0.577.0",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view",
"radix-ui": "^1.4.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"recharts": "3.8.0",
"shadcn": "^4.0.5",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tw-animate-css": "^1.4.0"
"react-dom": "^19.1.0"
},
"devDependencies": {
"@biomejs/biome": "^2.0.6",
Expand Down
Loading
Loading