Fluent Commerce Logo
Docs
Sign In

Putting a User Action on a Fluent App page

How-to Guide

Changed on:

12 June 2024

Key Points

  • As a developer, you can add user actions to a Fluent App page using the OMX orchestration engine. The user action enables users to interact with the Orchestration engine via the UI.
  • This article covers user actions, how the UX Framework understands our workflows, and mechanisms that enable us to leverage that to the user via the UI.
  • The process involves creating a ruleset in OMX, setting the module and subtype, modifying data queries, and enabling actions in the component properties.
  • Ensure appropriate user permissions are set to view objects and refer to the guide's debugging tips if issues arise.

Steps


Step arrow right iconIntroduction

This guide explains the steps necessary for a user action to appear on a Fluent App page. 

This article is primarily aimed at developers to explain how the Fluent App captures user actions from the OMX orchestration engine. User actions on a webpage can sometimes be unclear. This article clarifies how these actions are implemented and what happens behind the scenes. This guide is best used if you:

  • Have a Fluent account with at least one App page configured.
  • Want to put a custom user action onto a page, or a component on the page (on either an existing page or a page made through a Framework plugin).

In-Depth guide

Step arrow right iconCreating the Ruleset

All user actions are associated with a ruleset. This can be configured through the OMX engine. To setup a ruleset with a user action, you can refer to existing documentation for the most part. However, there are a few additional steps to watch out for, such as setting the module and subtype.

If you use the orchestration modeler UI to modify your ruleset, you may notice that the only options for setting the module is “Servicepoint” and “Console” (in this context, module means app, rather than the Fluent module packs like the Order and Inventory modules). These refer to the Fluent Store app and the Fluent OMS app respectively. But if you are configuring an app other than those two, there are some additional steps that are needed to be able to set your module for your user action.

Setting the Module

METHOD 1 (RECOMMENDED) - Register your module under the fc.mystique.apps manifest.

Putting the Store module under the fc.mystique.apps manifest becomes an option when selecting the module for a ruleset. See the link above for details.

METHOD 2 - Edit the ruleset JSON directly and add your app name into its list of modules.

It is possible to add your module to a ruleset even if it doesn't appear in the list of options. You can do this by opening the JSON, finding your ruleset, and add the name of your app to the list of modules. 

The name of your module is the name you used to set up the base manifest e.g. for the Store app, the base manifest name is fc.mystique.apps.store, so the name of the app is "store". See the example below to see how the store app can be added to the list of configurations

1"userActions": [
2                {
3                    "context": [
4                        {
5                            "label": "Reserve Stock",
6                            "type": "PRIMARY",
7                            "modules": [
8                                "adminconsole",
9                                "store",  <------------------ Here
10                                "servicepoint"
11                            ],
12                            "confirm": false
13                        }
14                    ],
15                    "attributes": [
16                        {
17                            "name": "RESERVE_STOCK_DETAILS",
18                            "label": "Reserve Stock Details",
19                          ...

Language: json

Name: Example Configuration

Description:

[Warning: empty required content area]


Setting the Subtype

This step is technically not related to the user actions, but this can sometimes be the cause behind why a user action not appearing.

All rulesets must specify the subtype as well as the entity type. For example, an order could have a CC subtype or HD subtype, and a product could have the subtypes 'Standard', 'Variant' or 'Group'. The rulesets for an order or product should also specify what subtype the ruleset applies to. This is necessary for a Fluent App to find the user actions related to the entity on a page. 

If you are configuring a ruleset through the orchestration modeller UI, entities at the top level will automatically get the subtype of the workflow e.g. rulesets for orders in the Order::CC workflow will automatically get the subtype CC. 

However, for entities that aren't top level, the subtype needs to be specified specifically. For example, products in the Product Catalogue aren't top level, so product rulesets need their subtype specified by the user. The only way to do this is by editing the JSON. The way to do this would be to navigate to the ruleset, enter a field named “subtype,” and then the value is either STANDARD, VARIANT, or GROUP. See the example below for details. 

1        "name": "ReserveStock",
2        "description": "Reserve some stock for this product",
3        "type": "PRODUCT",
4        "subtype": "VARIANT",
5        "eventType": "NORMAL",

Language: json

Name: Example

Description:

[Warning: empty required content area]

Step arrow right iconEditing the Data Query

At the page level, user actions are sourced from the first object returned in the graphql query for your component. 

For example, if the first query in the component’s data.query field is a

`articleById()`
call, then it will retrieve a list of user actions available for the article returned by that query. The state of the article is taken into account when retrieving user actions. For example, if it is in the ‘Awaiting collection’ status, it will retrieve user actions from rules that live under the DELIVERY category, as that status lives under that category. 

Basically, this means that you should ensure that in the manifest the first object you retrieve is also the entity the user action is based on. Any other statements in the query will be ignored.

For user actions appearing in lists (e.g. bulk actions), the first entity that would show in the list will be used to find user actions for all items in the list, so consider designing queries that only return entities for which that user action is valid.

Step arrow right iconEnabling actions in the component properties

 User actions are only supported by a specific subset of components. For pages and lists, these components won’t show user actions unless they are specifically configured to in their properties. 

The specific property and values used to enable and configure the user action varies depending on the component, however, they are well documented on their component page. For the purpose of completeness, they are also mentioned here, but it is recommended to refer to the documentation page for the latest up to date information.


Page Component

In a page component, a list of user actions can be placed onto the page, including both primary and secondary user actions. 

Even though the orchestration UI does not allow more than one primary user action, this component is capable of displaying more than one primary user action at a time. 

User actions are enabled through the “actions” field which can take in either a boolean or a UserActionConfig. For simple user actions, it would be enough to specify “actions” : “true” to enable them. Putting a UserActionConfig allows you to have more control over the user actions, giving the ability to only show a subset of user actions, allowing you to redirect to another page after clicking the button, or calling a different function after the action is done.

1"props": {
2     "actions": {
3        "primary": [{
4            "type": "userAction",
5            "label": "i18n:acme.wave.create.label",
6            "name":"Create Custom Wave",
7            "roles":["STORE_ADMIN"]
8        }]
9    }
10}

Language: plain_text

Name: Page actions configuration example

Description:

[Warning: empty required content area]

Current pages that places user actions on the page component:

  • The waves page
  • Specific articles in the arrivals, customer collections, courier collections and uncollected articles pages. 

Wizard Component

In a wizard component, a user action can be specified per step by specifying its name. In this case, only one user action is allowed. If there is more than one user action with the same name, the first one returned in the user action query result is used. 

1 "steps": [
2    {
3        "title": "Step 1 of 3: Pick",
4        "subTitle": "Wave #${waveById.id}",
5        "action": {
6            "name": "PickConfirm",
7            "config": {
8                "overrides": {
9                    "actionedBy": {
10                        "value":"{{me.username}}"
11                    }
12                }
13            }
14        }
15    }
16]

Language: json

Name: Wizard configuration example

Description:

[Warning: empty required content area]

List Component

In a list component, a user action can appear after a specific item on the list is selected.

To do this, the “actions” field is used to specify a specific user action to appear. 

Only one user action can be used in this component. 

1"props": {
2    "actions": [
3        { "type": "UserAction", "name": "FLAG_AS_URGENT" }
4    ]
5}
6   

Language: json

Name: List Component configuration example

Description:

[Warning: empty required content area]

Inline User Action Component

An inline user action places a user action directly onto the page itself, instead of having it open up the drawer on the right hand side. 

The same prerequisites are needed as other components (graphql query, permissions etc.) so it is not a magical component to make any user action show up. 

This inline user action only takes one user action, which is specified through its name. 

Deprecated Components

V1 Components were also capable of showing user actions. To enable this, some V1 components had the property “showUserActions”. Since V1 components are deprecated, the showUserActions property is now no longer relevant. 

Debugging tips

Step arrow right iconGuidelines for debugging

Since there’s no obvious feedback about why the retrieval of a user action failed, it helps to have a few areas to check in order to figure out what went wrong.

Most of these will involve using the developer tools available on a browser to investigate.

The api/v4.1/transition call

Reference: https://lingo.fluentcommerce.com/apis/rest/v4.1/user-action-api/#operations

This is the rest API call to get the list of User Actions available. 

To find this in the developer tools in Chrome, head to the Network tab and refresh the page where you expect the user action to appear. Then filter for “transition”. You should find the api call to this url easily.

If you are not seeing a user action appear in its response, double check the details of your user action in rubix. Specifically check to make sure that if there is a subtype, that it matches the one on your user action. This is very relevant for Product type user actions. 

If your query specifies a subtype, your user action must also have that subtype. This is true even for “subtype” : “default”. A value of default does not let it match against anything - it has to be on the user action exactly as it appears in the REST API Call. 

Also, try specifying a flexVersion. Although the docs claim that leaving it out retrieves the latest workflow version, I suspect this doesn’t work properly, as it didn’t work for me in the past. Specifying just the major number (the part before the decimal point) seems to retrieve the latest version of that major number properly. You can also specify the exact workflow version which also works well.

Sample

Here you can see the user action field is properly populated. 

1{
2  "response": [
3    {
4      "trigger": {
5        "name": null,
6        "type": "VIRTUAL_CATALOGUE",
7        "subtype": "ATS_CC",
8        "status": "ACTIVE",
9        "module": "store",
10        "flexType": "VIRTUAL_CATALOGUE::ATS_CC",
11        "flexVersion": "1",
12        "retailerId": "1"
13      },
14      "userActions": [
15        {
16          "eventName": "ReserveStock",
17          "context": [
18            {
19              "label": "Reserve Stock",
20              "type": "PRIMARY",
21              "modules": [
22                "adminconsole",
23                "servicepoint",
24                "store"
25              ],
26              "confirm": true
27            }
28          ],
29          "attributes": [
30            {
31              "name": "RESERVE_STOCK_DETAILS",
32              "label": "Stock Reservation Details",
33              "type": "STRING",
34              "source": "",
35              "defaultValue": "",
36              "mandatory": false
37            }
38          ]
39        }
40      ],
41      "transitions": [
42        {
43          "eventName": "ReserveStock",
44          "context": [
45            {
46              "label": "Reserve Stock",
47              "type": "PRIMARY",
48              "modules": [
49                "adminconsole",
50                "servicepoint",
51                "store"
52              ],
53              "confirm": true
54            }
55          ],
56          "attributes": [
57            {
58              "name": "RESERVE_STOCK_DETAILS",
59              "label": "Stock Reservation Details",
60              "type": "STRING",
61              "source": "",
62              "defaultValue": "",
63              "mandatory": false
64            }
65          ]
66        }
67      ]
68    }
69  ]
70}

Language: plain_text

Name: User Action

Description:

[Warning: empty required content area]

The GraphQL Query for the data

The graphql query can help identify whether you are getting the correct data or not. If permissions are wrong, or if you passed in something unexpected to the data query, having a look here can be helpful too.

To find your graphql query, head to the network tab in the dev tools, then refresh the page and filter for “GraphQL”. There will be several calls, but the one that shows the data is the one that uses the same query from your manifest. 

You can also examine the __transition fields this way. 

Errors in the Console Log

This involves checking the console log for any obvious error messages. One such issue that can be found this way is insufficient permissions.

For permission issues, you can sometimes see an error that states something similar to “Failed to get data. Need permission XXX”. If you see this it is possible that at least one of the queries on your page is fully or partially failing due to insufficient permissions.

False Positives

When navigating between pages, you can sometimes also see 

`error building user action lookups failed to get retailer ID for query`

This almost always happens when navigating between pages. In 99% of the cases, this is not a real error. It just means that user actions are being loaded too early before the code has retrieved the retailer ID. But once it has successfully retrieved the retailer Id, there will be a second call to get the retailer ID again.

This error always pops up even on pages where a user action successfully loads. It isn’t useful to figuring out why user actions are not working. You have to basically disregard this message when trying to determine why user actions aren't loading.

Step arrow right iconChecking User Permissions

When making any graphql query, the user calling the API must have the requisite permissions to view the object being retrieved. Otherwise, the object will not be returned in the response. 

As an example, the permission to view the Virtual Catalogue object,

`VIRTUALCATALOGUE_VIEW`
, is by default, not a part of the
`STORE_ASSISTANT`
role. Therefore, any pages that wanted to load user actions from a virtual catalogue workflow would not work for any store assistants. 

The permission to view a specific object type can be found in the schema. It is specified in the

`@permission`
directive under the object type. For example, here's the virtual catalogue, which has a
`@permission`
that specifies that the
`VIRTUALCATALOGUE_VIEW`
is required to view this object type. 

1type VirtualCatalogue implements Node & Referenceable & Orchestrateable & Extendable @map(name: "VirtualCatalogueEntity") @permission(name: "VIRTUALCATALOGUE_VIEW") {
2  id: ID!
3  createdOn: DateTime
4  updatedOn: DateTime
5  ref: String!
6  type: String!
7  workflowRef: String! @map(name: "flexType")
8  workflowVersion: Int! @map(name: "flexVersion")
9  status: String
10  attributes: [Attribute]
11  name: String!
12  description: String
13  virtualPositions(
14  createdOn: DateRange
15  updatedOn: DateRange
16  ref: [String!]
17  type: [String!]
18  workflowRef: [String!]
19  workflowVersion: [Int!]
20  status: [String]
21  productRef: [String]
22  quantity: [Int]
23  groupRef: [String]
24  catalogue: VirtualCatalogueKey
25  first: Int
26  last: Int
27  before: String
28  after: String
29  ): VirtualPositionConnection
30  inventoryCatalogueRef: String
31  productCatalogueRef: String
32  networkIds: [String]
33  controlGroupRef: String
34  retailerRefs: [String]
35}

Language: toml

Name: Virtual Catalogue Schema

Description:

[Warning: empty required content area]

In order to add a permission to a role, a graphql mutation needs to be used from an admin user (or any user with the

`ROLE_UPDATE`
permission). 

The

`Mutation.updateRole`
can be used to insert new permissions into their role.

Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.

Fluent Logo