---
title: "Three-step workflow | Customer&#x27;s Canvas Hub"
canonical: "https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/tutorials/three-step-workflow/"
---

# Three-step workflow

> Let's consider the example that your product is business cards, and you want customers to personalize it. Customers can select horizontal, vertical, or square format, and two types of style.

For this, you may create a three-step workflow. First, customers select a product variant, edit a design in Design Editor, and then approve the result.

Let's see how this workflow looks.

![Three-step-workflow.](./images/three-step-workflow.gif)

In this tutorial, you will learn how to create a three-step PIM workflow.

## Planning

Let's start from planning your workflow. For this, you need to decide the quantity of steps and what widgets you need.

### Steps

This workflow contains three steps:

- The `Variant Selector` step. Here, a customer selects a product variant.
- The `Editor` step. In this step, a customer edits a design in the Design Editor.
- The `Approval` step. A customer approves the personalization result.

```json
{
  "steps": [
    {
      "name": "Variant Selector"
    },
    {
      "name": "Editor"
    },
    {
      "name": "Approval"
    }
  ]
}
```

### Widgets

Let's consider what widgets you need for this workflow:

* `design-editor` for customizing a design.
* `variant-selector` for selecting a product variant.
* `order` for creating an order.
* `finish-group` for defining buttons and checkboxes at the `Approval` step.
* `steps` for hiding the `Finish` button in the Navigation panel.
* `slider` for representing a personalization result.

```json
"widgets": [
  {
    "name": "editor",
    "type": "design-editor",
    "params": {...}
  },
  {
    "name": "variant-selector",
    "type": "variant-selector",
    "params": {...}
  },
  {
    "name": "order",
    "type": "order",
    "params": {...}
  },
  {
    "name": "finish",
    "type": "finish-group",
    "params": {...}
  },
  {
    "name": "steps",
    "type": "steps",
    "params": {...}
  },
  {
    "name": "preview",
    "type": "slider",
    "params": {...}
  }
]
```

## Vars

Let's define variables for your workflow. The following syntax allows you to get a product ID, a product version ID, and a product filter ID from BackOffice.

```json
"vars": {
  "pimProductId": "{| Product.id |}",
  "pimProductVersionId": "{| Product.productVersionId |}",
  "pimProductFilterId": "{| Product.productFilterId |}"
}
```

## Defining steps

### Variant selector

The `Variant Selector` step is the first step in this workflow. At this step, customers select a product variant by filters. For this, use the `variant-selector` widget and embed it in the `mainPanel`.

![The Variant selector step.](./images/three-step-workflow-variant-selector.png)

The `variant-selector` widget shows both product variants and design variants. This widget uses the defined `vars` and obtains the data about a product, variants, and filters.

```json
{
  "name": "variant-selector",
  "type": "variant-selector",
  "params": {
    "productId": "{{ vars.pimProductId }}",
    "productVersionId": "{{ vars.pimProductVersionId }}",
    "productFilterId": "{{ vars.pimProductFilterId }}"
  }
}
```

### Editor

The `Editor` step is the second step in this workflow file. At this step, customers personalize a design in the `design-editor` widget. It is embedded in the `mainPanel`.

Add the `onActivate` event to show the preloader while loading the `design-editor` and the design selected at the `Variant selector` step.

![The Editor step.](./images/three-step-workflow-editor.png)

```json
{
  "name": "Editor",
  "mainPanel": {
    "name": "editor"
  },
  "onActivate": [
    "{{ #function main.showPreloader(true) }}",
    "{{ #function $['editor'].commands.initial.execute($['editor'].params.initial) }}",
    "{{ #function main.showPreloader(false) }}"
  ]
}
```

#### Design Editor

The `design-editor` widget allows customers to personalize a design. Let's consider the basic settings.

The `initial` command gets a selected design in the `productDefinition`property from the `variant-selector`widget. The `editorConfig` property configures the editor and gets fonts from your tenant. In the `rendering` object, you will define the output resolution, file format, and color space and enable safety lines.

```json
{
  "name": "editor",
  "type": "design-editor",
  "params": {
    "initial": {
      "autoCompile": false,
      "showPreloader": true,
      "productDefinition": "{{ $['variant-selector'].selectedVariant.designId}}",
      "editorConfig": {
        "initialMode": "Advanced",
        "restoreProductOnReloadEnabled": false,
        "fontList": {
          "appFonts": [
            "*"
          ]
        },
        "rendering": {
          "hiResOutputDpi": 300,
          "hiResOutputFileFormat": "pdf",
          "hiResOutputColorSpace": "cmyk",
          "proofImageSafetyLinesEnabled": false
        }
      }
    }
  }
}
```

### Approval

At the `Approval` step, customers approve the personalization result, and then create an order. For this, define the `slider` and `finish-group` widgets. Then, embed `slider` in the `mainPanel` and `finish group` in the `bottomPanel`.

![The Approval step.](./images/three-step-workflow-approval.png)

```json
{
  "name": "3. Approval",
  "mainPanel": {
    "name": "preview"
  },
  "bottomPanel": {
    "name": "finish"
  }
}
```

#### Slider

The `Slider` widget displays the personalization result. For this, `slider` gets proof images from the `design-editor` widget, and then creates columns to show design pages. You can also define style settings for this widget, for example, the background color and paddings.

```json
{
  "name": "preview",
  "type": "slider",
  "params": {
    "style": {
      "--au-widget-background": "#eee",
      "--au-widget-padding": "8px"
    },
    "direction": "tile",
    "rows": 1,
    "columns": "{{Math.min(2,$['editor'].proofImageUrls.length)}}",
    "containerColor": "#eee",
    "images": {
      "{{#each $['editor'].proofImageUrls.map(s=>s[0]) as imageUrl }}": {
        "url": "{{imageUrl}}"
      }
    }
  }
}
```

#### Finish group

This `finish-group` widget represents the confirmation checkbox and the **Add to cart** button. When the user clicks this button, the widget saves the personalization result and renders hi-res images.

```json
{
  "name": "finish",
  "type": "finish-group",
  "params": {
    "checkboxPrompt": "I have reviewed and approve my design.",
    "value": false,
    "checkboxEnabled": true,
    "checkboxVisible": true,
    "buttonText": "Add to cart",
    "buttonClassStyle": "primary",
    "onClick": [
      "{{#function main.showPreloader(true, 'Creating print files...')}}",
      "{{#function $['editor'].getHiResImages()}}",
      "{{#function new Promise(res=> setTimeout(res,1000)) }}",
      "{{#function main.showPreloader(false)}}"
    ]
  }
}
```

#### Steps widget

The `steps` widget allows you to hide the **Finish** button in the **Navigation** panel, which appears by default.

```json
{
  "name": "steps",
  "type": "steps",
  "params": {
    "finishButton": {
      "visible": "false"
    }
  }
}
```

## Connecting steps

When you go from the `Editor` step to the `Approval` step, the workflow will render proof images and clear the confirmation checkbox. It is used for the situations when you decide to go back to the `Editor` step to correct the result.

```json
"onActivate": [
  "{{#function $['editor'].getProofImages(800,800)}}",
  "{{#function $['finish'].value = false}}"
]
```

## Creating order

The `order` widget creates an order in the e-commerce system, creates a project in Customer's Canvas, and finishes the personalization process. This widget passes high resolution images, SKU, and other related information.

```json
{
  "name": "order",
  "type": "order",
  "params": {
    "data": {},
    "sku": "{{ $['variant-selector'].selectedVariant?.storefrontProductVariantId || '' }}",
    "props": {
      "_stateId": [
        "{{ $['editor'].stateId }}"
      ],
      "_userId": "{{ $['editor'].userId }}",
      "_hidden": {
        "snapshot": "{{ main.editorState }}",
        "images": "{{ $['editor'].proofImageUrls.map(u=>u[0]) }}",
        "downloadUrls": "{{ $['editor'].hiResUrls }}",
        "sku": "{{ $['variant-selector'].selectedVariant?.storefrontProductVariantId || '' }}",
        "originalProductId": "{{ product.id.toString() }}",
        "productVersionId": "{{ vars.pimProductVersionId }}"
      }
    }
  }
}
```

## Result

Let's consider how this three steps workflow looks.

```json
{
  "showSaveProjectButton": true,
  "showOpenSavedProjectsButton": true,
  "vars": {
    "pimProductId": "{| Product.id |}",
    "pimProductVersionId": "{| Product.productVersionId |}",
    "pimProductFilterId": "{| Product.productFilterId |}"
  },
  "showSteps": true,
  "widgets": [
    {
      "name": "editor",
      "type": "design-editor",
      "params": {
        "initial": {
          "autoCompile": false,
          "showPreloader": true,
          "productDefinition": "{{ $['variant-selector'].selectedVariant.designId}}",
          "editorConfig": {
            "initialMode": "Advanced",
            "restoreProductOnReloadEnabled": false,
            "fontList": {
              "appFonts": [
                "*"
              ]
            },
            "rendering": {
              "hiResOutputDpi": 300,
              "hiResOutputFileFormat": "pdf",
              "hiResOutputColorSpace": "cmyk",
              "proofImageSafetyLinesEnabled": false
            }
          }
        }
      }
    },
    {
      "name": "steps",
      "type": "steps",
      "params": {
        "finishButton": {
          "visible": "false"
        }
      }
    },
    {
      "name": "variant-selector",
      "type": "variant-selector",
      "params": {
        "productId": "{{ vars.pimProductId }}",
        "productVersionId": "{{ vars.pimProductVersionId }}",
        "productFilterId": "{{ vars.pimProductFilterId }}"
      }
    },
    {
      "name": "order",
      "type": "order",
      "params": {
        "data": {},
        "sku": "{{ $['variant-selector'].selectedVariant?.storefrontProductVariantId || '' }}",
        "props": {
          "_stateId": [
            "{{ $['editor'].stateId }}"
          ],
          "_userId": "{{ $['editor'].userId }}",
          "_hidden": {
            "snapshot": "{{ main.editorState }}",
            "images": "{{ $['editor'].proofImageUrls.map(u=>u[0]) }}",
            "downloadUrls": "{{ $['editor'].hiResUrls }}",
            "sku": "{{ $['variant-selector'].selectedVariant?.storefrontProductVariantId || '' }}",
            "originalProductId": "{{ product.id.toString() }}",
            "productVersionId": "{{ vars.pimProductVersionId }}"
          }
        }
      }
    },
    {
      "name": "finish",
      "type": "finish-group",
      "params": {
        "checkboxPrompt": "I have reviewed and approve my design.",
        "value": false,
        "checkboxEnabled": true,
        "checkboxVisible": true,
        "buttonText": "Add to cart",
        "buttonClassStyle": "primary",
        "onClick": [
          "{{#function main.showPreloader(true, 'Creating print files...')}}",
          "{{#function $['editor'].getHiResImages()}}",
          "{{#function new Promise(res=> setTimeout(res,1000)) }}",
          "{{#function main.showPreloader(false)}}"
        ]
      }
    },
    {
      "name": "preview",
      "type": "slider",
      "params": {
        "style": {
          "--au-widget-background": "#eee",
          "--au-widget-padding": "8px"
        },
        "direction": "tile",
        "rows": 1,
        "columns": "{{Math.min(2,$['editor'].proofImageUrls.length)}}",
        "containerColor": "#eee",
        "images": {
          "{{#each $['editor'].proofImageUrls.map(s=>s[0]) as imageUrl }}": {
            "url": "{{imageUrl}}"
          }
        }
      }
    }
  ],
  "steps": [
    {
      "name": "1. Variant Selector",
      "mainPanel": {
        "name": "variant-selector"
      }
    },
    {
      "name": "2. Editor",
      "mainPanel": {
        "name": "editor"
      },
      "onActivate": [
        "{{ #function main.showPreloader(true) }}",
        "{{ #function $['editor'].commands.initial.execute($['editor'].params.initial) }}",
        "{{ #function main.showPreloader(false) }}"
      ]
    },
    {
      "name": "3. Approval",
      "mainPanel": {
        "name": "preview"
      },
      "bottomPanel": {
        "name": "finish"
      },
      "onActivate": [
        "{{#function $['editor'].getProofImages(800,800)}}",
        "{{#function $['finish'].value = false}}"
      ]
    }
  ]
}
```
