Create ElementElement to JSON

Create Element

Element to JSON

Learn how to convert the element into JSON so it can be stored in your backend.


Element to JSON

Since every element must have a JSON representation, we also need to define one for our new element. To do that, we'll update the following file:

// src/shared/kits/workflow/types/workflow-schema.ts

export type WorkflowSchema = {
  start: { message: string };
  elements: ElementSchema[];
  end: { message: string };
};

export type ElementSchema =
  | ActionSchema
  | ConditionSchema
  | SwitchSchema
  | LoopSchema
  | ParallelSchema;

export interface ActionSchema {
  type: "action";
  id: string;
  message: string;
}

export interface ConditionSchema {
  type: "condition";
  id: string;
  if: string;
  then: ElementSchema[];
  else: ElementSchema[];
}

export interface SwitchSchema {
  type: "switch";
  id: string;
  branches: {
    case: string;
    then: ElementSchema[];
  }[];
  default: ElementSchema[];
}

export interface LoopSchema {
  type: "loop";
  id: string;
  while: string;
  into: ElementSchema[];
}

export interface ParallelSchema {
  type: "parallel";
  id: string;
  branches: {
    label: string;
    then: ElementSchema[];
  }[];
}

We'll also update the following file to export the type we created:

// src/shared/kits/workflow/index.tsx

export { useWorkflow, type State, type Action } from "./hooks/use-workflow";

export { WorkflowView } from "./components/workflow-view";

export { toWorkflowSchema } from "./utilities/to-workflow-schema";
export { toWorkflow } from "./utilities/to-workflow";

export type {
  WorkflowSchema,
  ElementSchema,
  ActionSchema,
  ConditionSchema,
  SwitchSchema,
  LoopSchema,
  ParallelSchema,
} from "./types/workflow-schema";

export type {
  Workflow,
  ElementFlow,
  ActionFlow,
  ConditionFlow,
  SwitchFlow,
  LoopFlow,
  ParallelFlow,
} from "./mvc/model/workflow";

export type { WorkflowChange } from "./mvc/model/workflow-change";

We'll also need to update the following function to map the new element to its JSON format:

// src/shared/kits/workflow/utilities/to-workflow-schema.ts

import type {
  Workflow,
  ElementFlow,
  ActionFlow,
  ConditionFlow,
  SwitchFlow,
  LoopFlow,
  ParallelFlow,
} from "@/shared/kits/workflow";

import type {
  WorkflowSchema,
  ElementSchema,
  ActionSchema,
  ConditionSchema,
  SwitchSchema,
  LoopSchema,
  ParallelSchema,
} from "../types/workflow-schema";

export function toWorkflowSchema(workflow: Workflow): WorkflowSchema {
  return {
    start: { message: workflow.flow.start.message },
    elements: workflow.flow.elements.map((element) => elementToSchema(element)),
    end: { message: workflow.flow.end.message },
  };
}

export function elementToSchema(elementFlow: ElementFlow): ElementSchema {
  switch (elementFlow.type) {
    case "action":
      return actionToSchema(elementFlow);
    case "condition":
      return conditionToSchema(elementFlow);
    case "switch":
      return switchToSchema(elementFlow);
    case "loop":
      return loopToSchema(elementFlow);
    case "parallel":
      return parallelToSchema(elementFlow);
  }
}

function actionToSchema(actionFlow: ActionFlow): ActionSchema {
  return {
    type: "action",
    id: actionFlow.id,
    message: actionFlow.message,
  };
}

function conditionToSchema(conditionFlow: ConditionFlow): ConditionSchema {
  return {
    type: "condition",
    id: conditionFlow.id,
    if: conditionFlow.if,
    then: conditionFlow.then.map(elementToSchema),
    else: conditionFlow.else.map(elementToSchema),
  };
}

function switchToSchema(switchFlow: SwitchFlow): SwitchSchema {
  return {
    type: "switch",
    id: switchFlow.id,
    branches: switchFlow.branches.map((branch) => ({
      case: branch.case,
      then: branch.then.map(elementToSchema),
    })),
    default: switchFlow.default.map(elementToSchema),
  };
}

function loopToSchema(loopFlow: LoopFlow): LoopSchema {
  return {
    type: "loop",
    id: loopFlow.id,
    while: loopFlow.while,
    into: loopFlow.into.map(elementToSchema),
  };
}

function parallelToSchema(parallelFlow: ParallelFlow): ParallelSchema {
  return {
    type: "parallel",
    id: parallelFlow.id,
    branches: parallelFlow.branches.map((branch) => ({
      label: branch.label,
      then: branch.then.map(elementToSchema),
    })),
  };
}

We'll also need to update the following function to map the JSON format to the new element:

// src/shared/kits/workflow/utilities/to-workflow.ts

import type {
  Workflow,
  ElementFlow,
  ActionFlow,
  ConditionFlow,
  SwitchFlow,
  LoopFlow,
  ParallelFlow,
} from "@/shared/kits/workflow";

import type {
  WorkflowSchema,
  ElementSchema,
  ActionSchema,
  ConditionSchema,
  SwitchSchema,
  LoopSchema,
  ParallelSchema,
} from "../types/workflow-schema";

export function toWorkflow(schema: WorkflowSchema): Workflow {
  return {
    flow: {
      start: { message: schema.start.message },
      dropTop: new Set<string>(),
      elements: schema.elements.map((element) => elementToWorkflow(element)),
      end: { message: schema.end.message },
    },
    widget: null,
    active: null,
    failure: null,
  };
}

export function elementToWorkflow(elementSchema: ElementSchema): ElementFlow {
  switch (elementSchema.type) {
    case "action":
      return actionToWorkflow(elementSchema);
    case "condition":
      return conditionToWorkflow(elementSchema);
    case "switch":
      return switchToWorkflow(elementSchema);
    case "loop":
      return loopToWorkflow(elementSchema);
    case "parallel":
      return parallelToWorkflow(elementSchema);
  }
}

function actionToWorkflow(actionSchema: ActionSchema): ActionFlow {
  return {
    type: "action",
    id: actionSchema.id,
    message: actionSchema.message,
    dragging: null,
    dropNext: new Set<string>(),
  };
}

function conditionToWorkflow(conditionSchema: ConditionSchema): ConditionFlow {
  return {
    type: "condition",
    id: conditionSchema.id,
    if: conditionSchema.if,
    then: conditionSchema.then.map(elementToWorkflow),
    else: conditionSchema.else.map(elementToWorkflow),
    dragging: null,
    dropNext: new Set<string>(),
    dropThen: new Set<string>(),
    dropElse: new Set<string>(),
  };
}

function switchToWorkflow(switchSchema: SwitchSchema): SwitchFlow {
  return {
    type: "switch",
    id: switchSchema.id,
    branches: switchSchema.branches.map((branch) => ({
      case: branch.case,
      then: branch.then.map(elementToWorkflow),
    })),
    default: switchSchema.default.map(elementToWorkflow),
    dragging: null,
    dropNext: new Set<string>(),
    dropBranches: switchSchema.branches.map(() => new Set<string>()),
    dropDefault: new Set<string>(),
  };
}

function loopToWorkflow(loopSchema: LoopSchema): LoopFlow {
  return {
    type: "loop",
    id: loopSchema.id,
    while: loopSchema.while,
    into: loopSchema.into.map(elementToWorkflow),
    dragging: null,
    dropNext: new Set<string>(),
    dropInto: new Set<string>(),
  };
}

function parallelToWorkflow(parallelSchema: ParallelSchema): ParallelFlow {
  return {
    type: "parallel",
    id: parallelSchema.id,
    branches: parallelSchema.branches.map((branch) => ({
      label: branch.label,
      then: branch.then.map(elementToWorkflow),
    })),
    dragging: null,
    dropNext: new Set<string>(),
    dropBranches: parallelSchema.branches.map(() => new Set<string>()),
  };
}