# @miniorange/flowforge

Schema-driven form renderer and workflow builder for miniOrange IAM. Provides a dynamic form engine (FlowForm) and a node-based workflow canvas (WorkflowCanvas) built on ReactFlow, with BPMN import/export.

📋 **Form schema reference:** [`FORM.md`](./FORM.md) — field types, validation, conditions, ref API, common patterns.

> **Proprietary** — © miniOrange. All rights reserved. Use is restricted to miniOrange products and authorized integrations.

## Installation

```bash
npm install @miniorange/flowforge
```

### Peer dependencies

This package expects the host app to provide React, MUI, and a few UI libraries (so versions stay in sync with your app):

```bash
npm install react react-dom @mui/material @emotion/react @emotion/styled formik react-icons react-select reactflow
```

Supported ranges: React ≥17, MUI ≥5, reactflow ≥11 (see `peerDependencies` in `package.json`).

## Usage

```tsx
import { FlowForgeCanvas } from '@miniorange/flowforge/canvas';
import { NodeTypeRegistry } from '@miniorange/flowforge/nodes';
import { TemplateRegistry } from '@miniorange/flowforge/templates';
import '@miniorange/flowforge/dist/style.css';

const nodeRegistry = new NodeTypeRegistry();
const templateRegistry = new TemplateRegistry();
// register node descriptors + templates...

export function Builder() {
  return (
    <FlowForgeCanvas
      nodeRegistry={nodeRegistry}
      templateRegistry={templateRegistry}
      canvasOptions={{ branchLayout: 'vertical' }}
      onSave={(workflow) => console.log(workflow)}
    />
  );
}
```

## Loading templates from a backend

Instead of hardcoding `templateRegistry.register({...})` calls, fetch templates
from your API and bulk-register them. The package accepts the backend wire
shape directly via `registerFromApi`:

```ts
import { templateRegistry } from '@miniorange/flowforge/templates';
import type { ApiWorkflowTemplate } from '@miniorange/flowforge/canvas';

async function loadTemplates() {
  const res  = await fetch('/api/v1/workflow-template');
  const list: ApiWorkflowTemplate[] = await res.json();
  templateRegistry.clear().registerFromApi(list);
}
```

Expected payload shape per row (the backend's persistence model + a `templateUI`
JSON column for UI fields):

```jsonc
{
  "key": "END_USER_CREATION",
  "name": "New User Registration",
  "description": "…",
  "state": "PUBLISHED",
  "allowedTasks":  ["USER_CREATION", "USER_GROUP_ASSIGNMENT", "USER_ROLE_ASSIGNMENT"],
  "allowedSources": ["SIGNUP_FLOW", "ADMIN_CREATION_FLOW"],
  "configuration":  { /* runtime BPMN — ignored by the canvas */ },
  "templateUI": {
    "triggerCategory": "event",
    "icon": "user-plus",
    "tags": ["onboarding"],
    "category": "User Lifecycle",
    "author": "miniOrange",
    "popularity": 100,
    "source": "system",
    "product": "IAM",
    "maxTasks": 10,
    "taskLabels": { "USER_GROUP_ASSIGNMENT": "Assign Groups" },
    "skeletonState": {
      "slots": [ /* CanvasSlot[] — pre-seeded canvas chain */ ],
      "branches": {}
    }
  }
}
```

`registerFromApi` normalises `allowedTasks → availableTasks` automatically.
Bad rows (missing `key`) are skipped with a DEV-mode warning, so a single
malformed template doesn't blank the picker.

## Entry points

| Import | Contents |
| --- | --- |
| `@miniorange/flowforge` | Everything (form + canvas + registries) |
| `@miniorange/flowforge/form` | FlowForm dynamic form renderer |
| `@miniorange/flowforge/canvas` | WorkflowCanvas builder |
| `@miniorange/flowforge/nodes` | NodeTypeRegistry + built-in node types |
| `@miniorange/flowforge/templates` | TemplateRegistry + skeletons |
| `@miniorange/flowforge/core` | Shared core types/utilities |
| `@miniorange/flowforge/default-ui` | Default drawer/picker UI |

## Development

```bash
npm run demo       # run the demo app (Vite)
npm run build      # typecheck + build dist
npm run typecheck  # tsc --noEmit
npm test           # build + BPMN round-trip tests
```

## License

UNLICENSED — proprietary to miniOrange. Not for redistribution outside authorized use.
