Changed on:
12 June 2024
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:
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.
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]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]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()`
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.
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.
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:
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]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]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.
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.
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.
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 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.
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.
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`
`STORE_ASSISTANT`
The permission to view a specific object type can be found in the schema. It is specified in the
`@permission`
`@permission`
`VIRTUALCATALOGUE_VIEW`
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`
The
`Mutation.updateRole`
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.