# Workflow network

> Read-only process diagram of step cards joined by measured connectors, with decision, human and result kinds, that wraps by width and follows the reading direction.

Source: https://docs.nasaqui.com/components/workflow-network

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/workflow-network.json
```

A process explained as cards joined by connectors: who does what, where a decision branches, where it ends. It is for showing a workflow, not editing one, so it is light (no graph library), prints well and reads right to left in Arabic. Use it in documentation, onboarding, approval explainers and presentations.

## When to use

- Explaining how a process works to the people who take part in it.
- A workflow summary next to a run or a policy.

## When not to use

- Building or configuring a workflow: use [`WorkflowCanvas`](https://docs.nasaqui.com/components/workflow-canvas).
- A strictly linear list of steps to walk through: use [`Stepper`](https://docs.nasaqui.com/components/stepper).

## Import

```tsx
import { WorkflowNetwork } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { WorkflowNetwork } from "@fadymondy/nasaq/web";

export function Refunds() {
  return (
    <WorkflowNetwork
      title="Refund requests"
      steps={[
        { id: "ask", title: "Customer asks", owner: "Customer" },
        { id: "check", title: "Within 14 days?", kind: "decision" },
        { id: "review", title: "Manager reviews", kind: "human", owner: "Support" },
        { id: "pay", title: "Refund issued", kind: "output" },
      ]}
      links={[
        { from: "ask", to: "check" },
        { from: "check", to: "pay", label: "Yes" },
        { from: "check", to: "review", label: "No" },
        { from: "review", to: "pay", label: "Approved" },
      ]}
    />
  );
}
```

## Anatomy

```
WorkflowNetwork                 data-slot="workflow-network"   <figure>
├─ heading, caption
├─ svg                          connectors (aria-hidden), arrowheads, optional draw-in
├─ rows                         flex rows of cards, wrapped and balanced
│  └─ card                      data-step="id" data-kind="decision"   <div>, or <button> with onStepClick
└─ connector labels             pills on the connectors
```

## API

### `WorkflowNetwork`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `steps` | `WorkflowNetworkStep[]` | required | `{ id, title, description?, icon?, owner?, kind? }`. `kind` is `step`, `decision`, `human`, `system` or `output`. |
| `links?` | `{ from; to; label? }[]` | steps in order | Connectors by step id. Unknown ids are ignored. |
| `layout?` | `"horizontal" \| "vertical" \| "auto"` | `"auto"` | `auto` becomes one column when the container is narrow (a phone, a sidebar). |
| `highlight?` | `string` | none | A step id: its card gets a ring and its connectors the brand colour. |
| `onStepClick?` | `(step) => void` | none | Makes cards buttons. |
| `animate?` | `boolean` | `false` | Draws the connectors in once. Ignored with reduced motion. |
| `title?`, `caption?` | `string` | none | Heading above and note below. |
| `labels?` | `Partial<WorkflowNetworkLabels>` | en/ar | Kind names and the screen-reader summary. |

## Examples

### Jumping links

A link that skips over cards on its row arcs above them; in a single column it is routed round the side. No configuration is needed.

### Clickable steps

```tsx
<WorkflowNetwork steps={steps} onStepClick={(s) => openDetails(s.id)} highlight={current} />
```

## Accessibility

- Connectors are decorative; the order is the DOM order of the cards, and a screen-reader line states the step count.
- With `onStepClick` each card is a real button with a visible focus ring.
- Kinds are not colour only: each has its own icon shape and a text badge.
- Motion is off with `prefers-reduced-motion`.

## RTL & i18n

- Rows are ordinary flex rows, so in Arabic the first step is on the right and the flow runs right to left. Connectors are measured from the rendered cards and always meet them.
- The default step arrow mirrors; kind icons do not. Kind badge text is localised (en and ar) and can be overridden with `labels`.
- Step titles and owners are yours to localise.

## Styling & tokens

- Cards `bg-card` with `border-border`; decision `border-nq-accent/60`, human `border-dashed`, system `bg-secondary`, output `border-nq-brand/50`. Highlight uses `nq-brand`.
- Target `[data-slot=workflow-network] [data-kind=decision]`. Never use raw hex.

## Do / Don't

- **Do** keep titles to a few words and put detail in `description`.
- **Do** keep `steps` in reading order; layout follows the array.
- **Don't** use it for more than about a dozen steps; split the process instead.

## Related

- [WorkflowCanvas](https://docs.nasaqui.com/components/workflow-canvas) · [Stepper](https://docs.nasaqui.com/components/stepper) · [Timeline](https://docs.nasaqui.com/components/timeline)

## Lab

https://docs.nasaqui.com/?path=/docs/components-workflow-workflow-network--docs

## Code

### React

```tsx
import { WorkflowNetwork } from "@fadymondy/nasaq/web";

export function Refunds() {
  return (
    <WorkflowNetwork
      title="Refund requests"
      steps={[
        { id: "ask", title: "Customer asks", owner: "Customer" },
        { id: "check", title: "Within 14 days?", kind: "decision" },
        { id: "review", title: "Manager reviews", kind: "human", owner: "Support" },
        { id: "pay", title: "Refund issued", kind: "output" },
      ]}
      links={[
        { from: "ask", to: "check" },
        { from: "check", to: "pay", label: "Yes" },
        { from: "check", to: "review", label: "No" },
        { from: "review", to: "pay", label: "Approved" },
      ]}
    />
  );
}
```

### shadcn

```tsx
import { WorkflowNetwork } from "@/components/ui/workflow-network";

export function Refunds() {
  return (
    <WorkflowNetwork
      title="Refund requests"
      steps={[
        { id: "ask", title: "Customer asks", owner: "Customer" },
        { id: "check", title: "Within 14 days?", kind: "decision" },
        { id: "review", title: "Manager reviews", kind: "human", owner: "Support" },
        { id: "pay", title: "Refund issued", kind: "output" },
      ]}
      links={[
        { from: "ask", to: "check" },
        { from: "check", to: "pay", label: "Yes" },
        { from: "check", to: "review", label: "No" },
        { from: "review", to: "pay", label: "Approved" },
      ]}
    />
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqWorkflowNetwork } from "@fadymondy/nasaq/vue";
</script>

<template>
  <NqWorkflowNetwork
    title="Refund requests"
    :steps="[
      { id: 'ask', title: 'Customer asks', owner: 'Customer' },
      { id: 'check', title: 'Within 14 days?', kind: 'decision' },
      { id: 'review', title: 'Manager reviews', kind: 'human', owner: 'Support' },
      { id: 'pay', title: 'Refund issued', kind: 'output' },
    ]"
    :links="[
      { from: 'ask', to: 'check' },
      { from: 'check', to: 'pay', label: 'Yes' },
      { from: 'check', to: 'review', label: 'No' },
      { from: 'review', to: 'pay', label: 'Approved' },
    ]"
  />
</template>
```

### Blade

```blade
<x-nq::workflow-network
    title="Refund requests"
    :steps="[
        ['id' => 'ask', 'title' => 'Customer asks', 'owner' => 'Customer'],
        ['id' => 'check', 'title' => 'Within 14 days?', 'kind' => 'decision'],
        ['id' => 'review', 'title' => 'Manager reviews', 'kind' => 'human', 'owner' => 'Support'],
        ['id' => 'pay', 'title' => 'Refund issued', 'kind' => 'output'],
    ]"
    :links="[
        ['from' => 'ask', 'to' => 'check'],
        ['from' => 'check', 'to' => 'pay', 'label' => 'Yes'],
        ['from' => 'check', 'to' => 'review', 'label' => 'No'],
        ['from' => 'review', 'to' => 'pay', 'label' => 'Approved'],
    ]" />
```

### HTML + Alpine

```html
<figure data-slot="workflow-network" data-layout="horizontal" x-data="nqWorkflowNetwork(JSON.parse('{\u0022ids\u0022:[\u0022ask\u0022,\u0022check\u0022,\u0022review\u0022,\u0022pay\u0022],\u0022links\u0022:[{\u0022from\u0022:\u0022ask\u0022,\u0022to\u0022:\u0022check\u0022},{\u0022from\u0022:\u0022check\u0022,\u0022to\u0022:\u0022pay\u0022,\u0022label\u0022:\u0022Yes\u0022},{\u0022from\u0022:\u0022check\u0022,\u0022to\u0022:\u0022review\u0022,\u0022label\u0022:\u0022No\u0022},{\u0022from\u0022:\u0022review\u0022,\u0022to\u0022:\u0022pay\u0022,\u0022label\u0022:\u0022Approved\u0022}],\u0022layout\u0022:\u0022auto\u0022,\u0022highlight\u0022:null,\u0022animate\u0022:false,\u0022uid\u0022:\u0022nq-net-b027f686\u0022}'))" class="m-0 w-full">
            <h3 class="mb-2 text-h3 text-foreground">Refund requests</h3>
        <p class="sr-only">4 steps</p>
    <div data-net="flow" class="relative">
        <svg data-net="edges" aria-hidden="true" class="pointer-events-none absolute inset-0 overflow-visible text-muted-foreground" width="0" height="0" viewBox="0 0 1 1">
            <defs>
                <marker id="nq-net-b027f686" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" class="fill-muted-foreground" />
                </marker>
                <marker id="nq-net-b027f686-hot" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" class="fill-nq-brand" />
                </marker>
            </defs>
        </svg>
        <div data-net="rows" class="relative flex flex-col p-2 gap-10">
                            <div data-net="row" class="flex items-stretch justify-center gap-10">
                                                                    <div data-net="cell" class="flex min-w-0 max-w-64 flex-1 basis-0">
                            <div  data-step="ask" data-kind="step" class="relative flex w-full min-w-0 flex-col items-stretch gap-1.5 rounded-card border border-border bg-card p-3 text-start shadow-xs">
                                <span class="flex items-center gap-2">
                                    <span class="flex size-8 shrink-0 items-center justify-center rounded-control border border-border bg-secondary text-foreground [&amp;_svg]:size-4">
                                        <span class="flex">
                                            <svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M5 12h14"/>
  <path d="m12 5 7 7-7 7"/>
</svg>                                        </span>
                                    </span>
                                    <span class="min-w-0 flex-1 text-label leading-tight text-foreground">Customer asks</span>
                                </span>
                                                                                                    <span class="flex flex-wrap items-center gap-1.5">
                                                                                    <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border bg-secondary text-foreground">Customer</span>
                                                                                                                    </span>
                                                            </div>
                        </div>
                                                                    <div data-net="cell" class="flex min-w-0 max-w-64 flex-1 basis-0">
                            <div  data-step="check" data-kind="decision" class="relative flex w-full min-w-0 flex-col items-stretch gap-1.5 rounded-card border bg-card p-3 text-start shadow-xs border-nq-accent/60">
                                <span class="flex items-center gap-2">
                                    <span class="flex size-8 shrink-0 items-center justify-center border border-border bg-secondary text-foreground [&amp;_svg]:size-4 rotate-45 rounded-[8px]">
                                        <span class="flex -rotate-45">
                                            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M15 6a9 9 0 0 0-9 9V3"/>
  <circle cx="18" cy="6" r="3"/>
  <circle cx="6" cy="18" r="3"/>
</svg>                                        </span>
                                    </span>
                                    <span class="min-w-0 flex-1 text-label leading-tight text-foreground">Within 14 days?</span>
                                </span>
                                                                                                    <span class="flex flex-wrap items-center gap-1.5">
                                                                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Decision</span>
                                                                            </span>
                                                            </div>
                        </div>
                                                                    <div data-net="cell" class="flex min-w-0 max-w-64 flex-1 basis-0">
                            <div  data-step="review" data-kind="human" class="relative flex w-full min-w-0 flex-col items-stretch gap-1.5 rounded-card border border-border bg-card p-3 text-start shadow-xs border-dashed">
                                <span class="flex items-center gap-2">
                                    <span class="flex size-8 shrink-0 items-center justify-center rounded-control border border-border bg-secondary text-foreground [&amp;_svg]:size-4">
                                        <span class="flex">
                                            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m16 11 2 2 4-4"/>
  <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
  <circle cx="9" cy="7" r="4"/>
</svg>                                        </span>
                                    </span>
                                    <span class="min-w-0 flex-1 text-label leading-tight text-foreground">Manager reviews</span>
                                </span>
                                                                                                    <span class="flex flex-wrap items-center gap-1.5">
                                                                                    <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border bg-secondary text-foreground">Support</span>
                                                                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Human review</span>
                                                                            </span>
                                                            </div>
                        </div>
                                                                    <div data-net="cell" class="flex min-w-0 max-w-64 flex-1 basis-0">
                            <div  data-step="pay" data-kind="output" class="relative flex w-full min-w-0 flex-col items-stretch gap-1.5 rounded-card border bg-card p-3 text-start shadow-xs border-nq-brand/50">
                                <span class="flex items-center gap-2">
                                    <span class="flex size-8 shrink-0 items-center justify-center rounded-control border [&amp;_svg]:size-4 border-nq-brand/40 bg-nq-brand text-white">
                                        <span class="flex">
                                            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528"/>
</svg>                                        </span>
                                    </span>
                                    <span class="min-w-0 flex-1 text-label leading-tight text-foreground">Refund issued</span>
                                </span>
                                                                                                    <span class="flex flex-wrap items-center gap-1.5">
                                                                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Result</span>
                                                                            </span>
                                                            </div>
                        </div>
                                    </div>
                    </div>
    </div>
    </figure>
```
