The site now exists in German (at the root, with /impressum and /datenschutz) and English (/en). A request without a language follows the visitor's choice from the language switch, then the browser language, then German; the old English legal addresses move to /en for good. All visible texts are data in src/content/de.ts and en.ts, so components hold none of their own. The header carries a DE/EN switch whose bit curtain covers the page while it is swapped and reopens at the same reading position. Title, description, canonical, hreflang, Open Graph, the share image, the sitemap and the structured data are per language. The privacy notice now describes the cookie that remembers the chosen language. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
245 lines
4.9 KiB
TypeScript
245 lines
4.9 KiB
TypeScript
import type { FlowNodeCopy, NodeKind, WorkflowCopy } from "./types";
|
|
|
|
/**
|
|
* Example workflows for the automation section. They are illustrations of typical
|
|
* cases, not descriptions of specific client projects. This file holds what the
|
|
* drawing needs (nodes, positions, runs); the texts are in en.ts and de.ts.
|
|
*/
|
|
|
|
type NodeLayout = {
|
|
readonly id: string;
|
|
readonly kind: NodeKind;
|
|
/** Position of the node centre on the wide 920 x 400 canvas. */
|
|
readonly x: number;
|
|
readonly y: number;
|
|
/** Position of the node centre on the tall 400 x 540 canvas for phones. */
|
|
readonly nx: number;
|
|
readonly ny: number;
|
|
};
|
|
|
|
export type FlowNode = NodeLayout & FlowNodeCopy;
|
|
|
|
/** One step of a run: the connections travelled at the same time. */
|
|
export type FlowStep = readonly (readonly [from: string, to: string])[];
|
|
|
|
type WorkflowLayout = {
|
|
readonly id: string;
|
|
readonly nodes: readonly NodeLayout[];
|
|
/** Every connection that exists, for drawing. */
|
|
readonly edges: readonly (readonly [from: string, to: string])[];
|
|
/** The runs that play in turn. Each run is a list of steps. */
|
|
readonly runs: readonly (readonly FlowStep[])[];
|
|
};
|
|
|
|
export type Workflow = Omit<WorkflowLayout, "nodes"> &
|
|
Omit<WorkflowCopy, "nodes"> & { readonly nodes: readonly FlowNode[] };
|
|
|
|
const LAYOUTS: readonly WorkflowLayout[] = [
|
|
{
|
|
id: "order",
|
|
nodes: [
|
|
{
|
|
id: "shop",
|
|
kind: "trigger",
|
|
x: 110,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 60,
|
|
},
|
|
{
|
|
id: "stock",
|
|
kind: "rule",
|
|
x: 340,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 190,
|
|
},
|
|
{
|
|
id: "invoice",
|
|
kind: "action",
|
|
x: 580,
|
|
y: 110,
|
|
nx: 105,
|
|
ny: 330,
|
|
},
|
|
{
|
|
id: "crm",
|
|
kind: "action",
|
|
x: 580,
|
|
y: 290,
|
|
nx: 295,
|
|
ny: 330,
|
|
},
|
|
{
|
|
id: "mail",
|
|
kind: "action",
|
|
x: 810,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 470,
|
|
},
|
|
],
|
|
edges: [
|
|
["shop", "stock"],
|
|
["stock", "invoice"],
|
|
["stock", "crm"],
|
|
["invoice", "mail"],
|
|
["crm", "mail"],
|
|
],
|
|
runs: [
|
|
[
|
|
[["shop", "stock"]],
|
|
[
|
|
["stock", "invoice"],
|
|
["stock", "crm"],
|
|
],
|
|
[
|
|
["invoice", "mail"],
|
|
["crm", "mail"],
|
|
],
|
|
],
|
|
],
|
|
},
|
|
{
|
|
id: "documents",
|
|
nodes: [
|
|
{
|
|
id: "inbox",
|
|
kind: "trigger",
|
|
x: 110,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 60,
|
|
},
|
|
{
|
|
id: "read",
|
|
kind: "ai",
|
|
x: 340,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 190,
|
|
},
|
|
{
|
|
id: "check",
|
|
kind: "rule",
|
|
x: 570,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 320,
|
|
},
|
|
{
|
|
id: "book",
|
|
kind: "action",
|
|
x: 810,
|
|
y: 110,
|
|
nx: 105,
|
|
ny: 460,
|
|
},
|
|
{
|
|
id: "ask",
|
|
kind: "human",
|
|
x: 810,
|
|
y: 290,
|
|
nx: 295,
|
|
ny: 460,
|
|
},
|
|
],
|
|
edges: [
|
|
["inbox", "read"],
|
|
["read", "check"],
|
|
["check", "book"],
|
|
["check", "ask"],
|
|
],
|
|
runs: [
|
|
[[["inbox", "read"]], [["read", "check"]], [["check", "book"]]],
|
|
[[["inbox", "read"]], [["read", "check"]], [["check", "book"]]],
|
|
[[["inbox", "read"]], [["read", "check"]], [["check", "ask"]]],
|
|
],
|
|
},
|
|
{
|
|
id: "lead",
|
|
nodes: [
|
|
{
|
|
id: "form",
|
|
kind: "trigger",
|
|
x: 110,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 60,
|
|
},
|
|
{
|
|
id: "contact",
|
|
kind: "action",
|
|
x: 340,
|
|
y: 200,
|
|
nx: 200,
|
|
ny: 190,
|
|
},
|
|
{
|
|
id: "notify",
|
|
kind: "action",
|
|
x: 580,
|
|
y: 110,
|
|
nx: 105,
|
|
ny: 330,
|
|
},
|
|
{
|
|
id: "wait",
|
|
kind: "rule",
|
|
x: 580,
|
|
y: 290,
|
|
nx: 295,
|
|
ny: 330,
|
|
},
|
|
{
|
|
id: "remind",
|
|
kind: "action",
|
|
x: 810,
|
|
y: 290,
|
|
nx: 295,
|
|
ny: 470,
|
|
},
|
|
],
|
|
edges: [
|
|
["form", "contact"],
|
|
["contact", "notify"],
|
|
["contact", "wait"],
|
|
["wait", "remind"],
|
|
],
|
|
runs: [
|
|
[
|
|
[["form", "contact"]],
|
|
[
|
|
["contact", "notify"],
|
|
["contact", "wait"],
|
|
],
|
|
[["wait", "remind"]],
|
|
],
|
|
],
|
|
},
|
|
];
|
|
|
|
const NO_TEXT: FlowNodeCopy = { system: "", label: "", log: "" };
|
|
|
|
/** The example workflows with their texts in one language. */
|
|
export const workflowsFor = (
|
|
copy: Readonly<Record<string, WorkflowCopy>>,
|
|
): readonly Workflow[] =>
|
|
LAYOUTS.flatMap((layout) => {
|
|
const text = copy[layout.id];
|
|
if (text === undefined) return [];
|
|
return [
|
|
{
|
|
...layout,
|
|
name: text.name,
|
|
summary: text.summary,
|
|
byHand: text.byHand,
|
|
automated: text.automated,
|
|
nodes: layout.nodes.map((node) => ({
|
|
...node,
|
|
...(text.nodes[node.id] ?? NO_TEXT),
|
|
})),
|
|
},
|
|
];
|
|
});
|