Guided flow
Steps with typed fields, conditions as data, live checks, a review page and one proposal handed to the host.
7 demos
Steps with typed fields, conditions as data, live checks, a review page and one proposal handed to the host.
A four-step flow with choice cards, a password that never echoes, options that appear only when they apply, and a review page.
Report cards, grouped multiple choice, weekdays that appear only for a weekly schedule, durations and recipients.
Starts from the current values; the review shows only what changed as was and now, and the proposal carries a JSON patch.
Connection and sign-in tests, host-listed options, records, presets, a session draft, deep links and the equivalent command.
Opens in a dialog, picks servers from a grid with facets, and asks the reader to type RETIRE before the host acts.
One step, no review page, compact density; also written in Polish and right to left.
Build one like it
The smallest complete version, in each language CanopyX supports for this view. The demo above is the same API with more data.
using CanopyX;
using CanopyX.Html;
var flow = new CanopyFlow("service-setup", "Set up the reporting service") {
Description = "Finish the tasks in any order. "
+ "The service starts once every required task is complete.",
Mode = CanopyFlowMode.Tasks,
Submit = new CanopyFlowSubmit("Start the service")
};
flow.Step("database", "Connect the database", step => step
.Group("Connect")
.Choice("engine", "Database engine", f => f
.Option("sql", "SQL Server")
.Option("pg", "PostgreSQL")
.Required()
.Default("sql"))
.Text("server", "Server", f => f
.Required()
.Help("A host name, with a port when it is not the default."))
.Text("name", "Database name", f => f.Required().Default("reports").Half()));
flow.Step("mail", "Choose who gets notified", step => step
.Group("Connect")
.List("admins", "Administrators", f => f
.Required()
.Items(CanopyFlowItemRule.Email)
.Count(1, 10))
.Toggle("digest", "Send a daily digest", f => f.Default(true)));
flow.Step("branding", "Add your branding", step => step
.Group("Content")
.Optional()
.Text("company", "Company name")
.Choice("accent", "Accent colour", f => f
.Option("teal", "Teal")
.Option("blue", "Blue")
.Default("teal")));
flow.Review(review => review
.Declaration("Starting the service runs the first schedules today."));
flow.SaveHtmlPage("flow.html");Run this snippet (guided flow, C#)Its output, generated with the site.
import { createFlow } from "@evotecit/canopyx/flow";
import "@evotecit/canopyx/flow-entry.css";
const flow = createFlow(document.getElementById("flow"), {
flow: {
kind: "flow", version: 1, id: "service-setup",
title: "Set up the reporting service",
description: "Finish the tasks in any order. "
+ "The service starts once every required task is complete.",
mode: "tasks",
steps: [
{
id: "database", title: "Connect the database", group: "Connect",
fields: [
{
id: "engine", type: "choice", label: "Database engine",
required: true, default: "sql",
options: [
{ value: "sql", label: "SQL Server" },
{ value: "pg", label: "PostgreSQL" }
]
},
{
id: "server", type: "text", label: "Server", required: true,
help: "A host name, with a port when it is not the default."
},
{
id: "name", type: "text", label: "Database name",
required: true, default: "reports", width: "half"
}
]
},
{
id: "mail", title: "Choose who gets notified", group: "Connect",
fields: [
{
id: "admins", type: "list", label: "Administrators", required: true,
validation: { minItems: 1, maxItems: 10, item: "email" }
},
{
id: "digest", type: "toggle", label: "Send a daily digest",
default: true
}
]
},
{
id: "branding", title: "Add your branding", group: "Content",
optional: true,
fields: [
{ id: "company", type: "text", label: "Company name" },
{
id: "accent", type: "choice", label: "Accent colour", default: "teal",
options: [
{ value: "teal", label: "Teal" },
{ value: "blue", label: "Blue" }
]
}
]
},
{
id: "review", type: "review",
declaration: "Starting the service runs the first schedules today."
}
],
submit: { label: "Start the service" }
},
// CanopyX never writes: post the answers to your service and say what happened.
onSubmit: async detail => {
const response = await fetch("/api/reporting/setup", {
method: "POST",
body: JSON.stringify(detail.values)
});
return response.ok
? { status: "saved", message: "The service is starting." }
: { status: "error", message: "Not saved." };
}
});Run this snippet (guided flow, JavaScript)Its output, generated with the site.
Try this in the demo
- Tasks in any order
Open Connect the database.
Each task opens on its own; the hub keeps its state and the host's summary line.
- Fields from the host
Switch the engine between SQL Server and PostgreSQL.
The engine options are fields the host returns, so the flow asks only what that engine needs.
- Checks
Type a server name containing fail and test the connection.
The host's check fails with its message; a passing check lists what it found.
- One proposal
Finish the required tasks, then review and submit.
The flow hands the host one document; secrets are read once and never echo back.