DialPlan
Embed the DialStack visual dial-plan editor directly in your app. One component, three display modes: read-only viewer, full editor, or static thumbnail.
Basic Usage
import { DialPlan } from '@dialstack/sdk-react/dial-plan';
function Routing({ dialPlanId }: { dialPlanId: string }) {
return <DialPlan dialPlanId={dialPlanId} />;
}
The default mode is view — a read-only flow diagram with pan/zoom and controls.
Modes
// Read-only diagram (default)
<DialPlan dialPlanId={dialPlanId} mode="view" />
// Full editor: node library, drag-and-drop, config panel, save
<DialPlan dialPlanId={dialPlanId} mode="edit" />
// Static thumbnail: no controls, no background, no interaction
<DialPlan dialPlanId={dialPlanId} mode="preview" />
// Create mode: no dialPlanId + mode="edit" starts a blank dial plan
<DialPlan mode="edit" onSave={(dp) => console.log('Created', dp.id)} />
| Mode | Editable | Pan/zoom | Controls | Use case |
|---|---|---|---|---|
view | No | Yes | Yes | Inspect an existing dial plan |
edit | Yes | Yes | Yes | Build or modify a dial plan |
preview | No | No | No | Thumbnail in a list or card |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
dialPlanId | string | - | Dial plan to load. Omit in edit mode to create one |
mode | 'view' | 'edit' | 'preview' | 'view' | Display mode |
locale | DialPlanLocale | English | i18n strings (labels, exits, descriptions) |
theme | 'light' | 'dark' | 'light' | Color theme |
className | string | - | Container class |
style | React.CSSProperties | - | Container inline style |
onNodeClick | (nodeId, node) => void | - | Node click callback (view mode) |
onLoaderStart | () => void | - | Fired when loading starts |
onLoaderEnd | (dialPlan) => void | - | Fired when loading finishes |
onLoadError | (error) => void | - | Fired on load failure |
onSave | (dialPlan) => void | - | Fired after a successful save (edit mode) |
onDirtyChange | (isDirty) => void | - | Fired when the editor's dirty state changes |
onError | (error) => void | - | Alias for onLoadError, used in edit mode |
onCreateResource | (type) => Promise<{ id, name, ... }> | - | Host hook to open a "create new …" modal |
onOpenResource | (resourceId) => void | - | Host hook to navigate to a resource detail page |
Imperative Handle
Pass a ref to trigger actions programmatically:
import { useRef } from 'react';
import { DialPlan, type DialPlanHandle } from '@dialstack/sdk-react/dial-plan';
function Editor() {
const ref = useRef<DialPlanHandle>(null);
return (
<>
<button onClick={() => ref.current?.save()}>Save</button>
<DialPlan ref={ref} mode="edit" dialPlanId={dialPlanId} />
</>
);
}
| Method | Returns | Description |
|---|---|---|
save() | Promise<void> | Persist pending changes; resolves on success |
Node types
The editor builds the same nodes the API stores. For every node type, its config fields and its exits, see the Dial plans guide.
The editor shows some nodes as their own tiles. Voicemail and Voice App are internal_dial nodes that target a shared voicemail box or a voice app. The stored node type stays internal_dial; only the canvas presentation differs.
Host Integration (edit mode)
The editor lets users pick targets from your directory. Provide two host hooks so the UI can list resources and create new ones on the fly:
<DialPlan
mode="edit"
dialPlanId={id}
onCreateResource={async (type) => {
// type: 'schedule' | 'user' | 'ring_group' | 'dial_plan' | 'voice_app' | 'shared_voicemail'
const created = await openCreateModal(type);
return created; // { id, name, extension_number? }
}}
onOpenResource={(resourceId) => {
router.push(`/resources/${resourceId}`);
}}
/>
onCreateResource(type)— called when the user selects "Create new …" in a target combobox. Return the new resource's{ id, name }to auto-select it.onOpenResource(id)— called when the user clicks the "Open target details" link next to a selected target.
Resource lists themselves come from the DialStack API; nothing extra to wire.
Events
<DialPlan
dialPlanId={dialPlanId}
mode="edit"
onLoaderStart={() => setLoading(true)}
onLoaderEnd={(dp) => {
setLoading(false);
setName(dp.name);
}}
onDirtyChange={setUnsaved}
onSave={(dp) => toast.success(`Saved ${dp.name}`)}
onError={(err) => toast.error(err.message)}
/>
Internationalization
Supply a locale object to override every UI string. All fields are required on the type — see DialPlanLocale for the full shape.
<DialPlan
dialPlanId={dialPlanId}
locale={{
nodeTypes: {
start: 'Démarrer',
schedule: 'Horaire',
internalDial: 'Poste interne',
voicemail: 'Messagerie',
externalDial: 'Numéro externe',
ringAllUsers: 'Sonner tous',
voiceApp: 'Voice App',
},
// exits, nodeDescriptions, targetTypes, resourceGroups, configLabels,
// toolbar, panel, combobox, status — see DialPlanLocale
}}
/>
TypeScript
import type {
DialPlanProps,
DialPlanMode,
DialPlanHandle,
DialPlanNode,
DialPlanNodeType,
} from '@dialstack/sdk-react/dial-plan';
DialPlanNode is a discriminated union — narrow on node.type to get the typed config.
Complete Example
import { useRef, useState } from 'react';
import { DialPlan, type DialPlanHandle } from '@dialstack/sdk-react/dial-plan';
export function DialPlanEditorPage({ id }: { id: string }) {
const ref = useRef<DialPlanHandle>(null);
const [dirty, setDirty] = useState(false);
const [saving, setSaving] = useState(false);
async function handleSave() {
setSaving(true);
try {
await ref.current?.save();
} finally {
setSaving(false);
}
}
return (
<div className="h-screen flex flex-col">
<header className="flex items-center justify-between p-4 border-b">
<h1>Edit dial plan</h1>
<button disabled={!dirty || saving} onClick={handleSave}>
{saving ? 'Saving…' : 'Save'}
</button>
</header>
<div className="flex-1">
<DialPlan
ref={ref}
dialPlanId={id}
mode="edit"
theme="light"
onDirtyChange={setDirty}
onSave={() => setDirty(false)}
onError={(err) => console.error(err)}
onCreateResource={async (type) => openCreateModal(type)}
onOpenResource={(rid) => router.push(`/resources/${rid}`)}
/>
</div>
</div>
);
}
Next Steps
- Dial plans guide — node types and the API
- Component basics — the full component inventory
- Theming — Customize appearance
- i18n — Internationalization