Creating card events

An event is an action and some data. All cards on the page have access to the event and can act on it.

Before you begin

  1. Install a GraphQL client such as Insomnia
  2. Gather your instance clientId and orgId. Your administrator has this information.

About this task

This tutorial describes a customization example to add two cards and understand card events. In this tutorial, you make the table clickable. When a row is clicked, two other cards respond to the event and refresh their data.

Procedure

  1. Update an existing card.

    In this step, you are updating the card in the following ways:

    • Making it unmovable
    • Auto resizing to the inner content
    • Making the rows selectable
    • Automatically selecting the first row on load
    • Firing an event when a row is clicked
    1. To make the card immovable, remove the definition property:
      {
      	"name": "draggable",
      	"values": ["true"], 
      	"metaInformation": "widgetOption"
      }  
    2. To auto resize content, add the definition:
      {
      	"name": "resizeToContent",
      	"values": ["true"],
      	"metaInformation": "widgetOption"
      }
    3. To make a row selectable, add this code to the definition:
      {
      	"name": "enableRowSelect",
      	"values": ["true"],
      	"metaInformation": "tableOption"
      }
    4. Update the query fields so that the query returns a unique ID for every row with the name id as shown in the following example.
      ... on Inventory { 
      	id 
      	product { 
      		partNumber 
      	} 
      	daysOfSupply 
      	futureStockoutCount
      	turns
      	quantity
      	plannerCode
      	shelfLife
      	value
      	location { 
      		id 
      		locationName 
      	} 
      }
    5. To auto select the first row of the table, add this code to the definition:
      {
      	"name": "selectFirstRow",
      	"values": ["true"],
      	"metaInformation": "tableOption"
      }
    6. To fire an event when a row is clicked, create the namespaceinventoryPositionListRow.row and create a state scope for the rowClicked event.
      A stateScope sends an event to the web browser for all other cards to listen to. And the whole rows data object is stored at inventoryPositionListRow.row.
      {
      	"name": "rowClicked",
      	"values": ["inventoryPositionListRow.row"],
      	"metaInformation": "stateScope"
      }
    Note: In the following examples, you need to make suitable replacements for these variables:
    • <INSERT_YOUR_ORG_ID_HERE>
    • <INSERT_YOUR_TENANT_ID_HERE>
    • <redacted>
    PUT Updated Inventory Units widget Definition example
    curl --location --request PUT 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/widget_definitions/CUSTOM_DASHBOARD_INVENTORY_TABLE_VIEW_ALL' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
        "offeringId": "SCO",
        "id": "CUSTOM_DASHBOARD_INVENTORY_TABLE_VIEW_ALL",
        "state": "ACTIVE",
        "identifier": "CUSTOM_DASHBOARD_INVENTORY_TABLE_VIEW_ALL",
        "type": "Dashboard_Widget",
        "tenantId":"<INSERT_YOUR_TENANT_ID_HERE>",
        "path": "/api",
        "vendor": "IBM",
        "defaultLanguage": "en",
        "version": 1,
        "descriptions": {
            "en": {
                "name": "Stock by Inventory Level",
                "description": "Stock by Inventory Level - view all"
            }
        },
        "devConfigurations": [
            {
                "name": "header.path",
                "values": ["/template/CUSTOM_DASHBOARD_INVENTORY_VIEW_ALL_LAYOUT_TEMPLATE"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "header.type",
                "values": ["metric_header"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "header.value",
                "values": ["{{ totalCount }}"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "header.title",
                "values": ["Inventory units"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "header.tooltip",
                "values": ["Stock by Inventory Level Tooltip."], 
                "metaInformation": "widgetOption"
            }, {
                "name": "resizeToContent",
                "values": ["true"],
                "metaInformation": "widgetOption"
            }, {
                "name": "data.service",
                "values": ["infohub"]
            }, {
                "name": "queries[0].fields",
                "values": ["query InventoryPosition( $tenantId: String! $cursorId: String $sortBy: String = \"quantity\" $sortOrder: SortOrder = DESC $locationCityFilter: BooleanExp = { CONSTANT_VALUE: true } $locationFilter: BooleanExp = { CONSTANT_VALUE: true } $categoryFilter: BooleanExp = { CONSTANT_VALUE: true } $inventoryPlannerCodeFilter: BooleanExp = { CONSTANT_VALUE: true }, $pageLength: Int = 10) {businessObjects(simpleFilter: { type: Inventory, tenantId: $tenantId }advancedFilter: {AND: [$locationFilter $locationCityFilter $categoryFilter $inventoryPlannerCodeFilter ] } hint: { viewId: \"graph\" } cursorParams: { first: $pageLength after: $cursorId sort: { fieldPath: $sortBy, order: $sortOrder } } ) { totalCount pageInfo { hasNextPage endCursor} edges { cursor object { ... on Inventory { id product { partNumber } daysOfSupply futureStockoutCount turns quantity plannerCode shelfLife value location { id locationName } }}}}}"], 
                "metaInformation": "infohub.fields"
            }, {
                "name": "queries[0].variables.tenantId",
                "values": ["{{ tenantId }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.sortBy",
                "values": ["{{ inventoryPosition.sort.by }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.sortOrder",
                "values": ["{{ inventoryPosition.sort.order }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.cursorId",
                "values": ["{{ cursorId }}"],
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.pageLength",
                "values": ["{{ inventoryPosition.pageLength }}"],
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.locationFilter",
                "values": ["{ \"EQUALS_ANY\": [ {\"SELECT\": \"location.id\", \"type\": \"STRING\" }, { \"VALUES\": {{custom.appliedFilters.Location.ids}}, \"type\": \"STRING\" } ] }"], 
                "metaInformation": "infohub.variable.dynamicExpr"
            }, {
                "name": "queries[0].variables.inventoryPlannerCodeFilter",
                "values": ["{ \"EQUALS_ANY\": [ {\"SELECT\": \"plannerCode\", \"type\": \"STRING\" }, { \"VALUES\": {{custom.appliedFilters.PlannerCode.ids}}, \"type\": \"STRING\" } ] }"], 
                "metaInformation": "infohub.variable.dynamicExpr"
            }, {
                "name": "queries[0].variables.categoryFilter",
                "values": ["{ \"EQUALS_ANY\": [ {\"SELECT\": \"product.category.id\", \"type\": \"STRING\" }, { \"VALUES\": {{custom.appliedFilters.Category.ids}}, \"type\": \"STRING\" } ] }"], 
                "metaInformation": "infohub.variable.dynamicExpr"
            }, {
                "name": "queries[0].variables.locationCityFilter",
                "values": ["{\"EQUALS_ANY\":[{\"SELECT\":\"location.city\",\"type\":\"STRING\"},{\"VALUES\":{{custom.appliedFilters.LocationCity.ids}},\"type\":\"STRING\"}]}"], 
                "metaInformation": "infohub.variable.dynamicExpr"
            }, {
                "name": "columnSorted",
                "values": ["inventoryPosition"], 
                "metaInformation": "stateScope"
            }, {
                "name": "inventoryPosition.sort",
                "values": ["refreshData","refreshSavedState"], 
                "metaInformation": "stateSubscription"
            },{
                "name": "enableRowSelect",
                "values": ["true"],
                "metaInformation": "tableOption"
            }, {
                "name": "selectFirstRow",
                "values": ["true"],
                "metaInformation": "tableOption"
            },{
                "name": "custom.appliedFilters",
                "values": ["refreshData"], 
                "metaInformation": "stateSubscription"
            }, {
                "name": "horizontalScroll.enable",
                "values": ["true"], 
                "metaInformation": "tableOption"
            }, {
                "name": "pagination.enable",
                "values": ["true"],
                "metaInformation": "tableOption"
            }, {
                "name": "pageInput.enable",
                "values": ["true"],
                "metaInformation": "tableOption"
            }, {
                "name": "pageChanged",
                "values": ["inventoryPosition"],
                "metaInformation": "stateScope"
            }, {
                "name": "inventoryPosition.page",
                "values": ["refreshData"],
                "metaInformation": "stateSubscription"
            }, {
                "name": "inventoryPosition.pageLength",
                "values": ["refreshData"],
                "metaInformation": "stateSubscription"
            }, {
                "name": "rowClicked",
                "values": ["inventoryPositionListRow.row"],
                "metaInformation": "stateScope"
            }, {
                "name": "columns[0].cell.label",
                "values": ["{{ product.partNumber }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].header.label",
                "values": ["Product ID"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].sortable",
                "values": ["false"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].cell.customTemplate",
                "values": ["linkTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].cell.href.type",
                "values": ["LAYOUT_TEMPLATE"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].cell.href.id",
                "values": ["DEFAULT_INVENTORY_DETAIL_LAYOUT_TEMPLATE"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].cell.href.params",
                "values": ["inventoryId={{ id }},hiddenLayoutSections=queue"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[0].sequence",
                "values": ["0"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[1].cell.label",
                "values": ["{{ quantity }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[1].header.label",
                "values": ["Quantity"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[1].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[1].cell.customTemplate",
                "values": ["numberTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[1].sequence",
                "values": ["1"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[2].cell.label",
                "values": ["{{ turns }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[2].header.label",
                "values": ["Turns"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[2].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[2].cell.customTemplate",
                "values": ["numberTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[2].sequence",
                "values": ["2"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[3].cell.label",
                "values": ["{{ daysOfSupply }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[3].header.label",
                "values": ["Days of Supply"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[3].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[3].cell.customTemplate",
                "values": ["numberTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[3].sequence",
                "values": ["3"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[4].cell.label",
                "values": ["{{ plannerCode }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[4].header.label",
                "values": ["Planner Code"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[4].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[4].sequence",
                "values": ["4"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[5].cell.label",
                "values": ["{{ shelfLife }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[5].header.label",
                "values": ["Shelf Life"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[5].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[5].cell.customTemplate",
                "values": ["numberTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[5].sequence",
                "values": ["5"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[6].cell.label",
                "values": ["{{ value }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[6].header.label",
                "values": ["Value"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[6].sortable",
                "values": ["true"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[6].cell.customTemplate",
                "values": ["currencyTpl"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[6].sequence",
                "values": ["6"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[7].cell.label",
                "values": ["{{ location.locationName }}"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[7].header.label",
                "values": ["Location Name"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[7].sortable",
                "values": ["false"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "columns[7].sequence",
                "values": ["7"], 
                "metaInformation": "tableColumn"
            }, {
                "name": "widgetInitialized",
                "values": ["loadState", "refreshData"], 
                "metaInformation": "eventSubscription"
            }, {
                "name": "transformation",
                "values": ["{\"errors\":\"{{#? this.errors}}\",\"pageInfo\":\"{{this.data.businessObjects.pageInfo}}\",\"totalCount\":\"{{this.data.businessObjects.totalCount}}\",\"results\":{\"{{#each this.data.businessObjects.edges}}\":{\"cursor\":\"{{this.cursor}}\",\"daysOfSupply\":\"{{this.object.daysOfSupply}}\",\"futureStockoutCount\":\"{{this.object.futureStockoutCount}}\",\"turns\":\"{{this.object.turns}}\",\"quantity\":\"{{this.object.quantity}}\",\"product\":\"{{this.object.product}}\",\"plannerCode\":\"{{this.object.plannerCode}}\",\"shelfLife\":\"{{this.object.shelfLife}}\",\"value\":\"{{this.object.value}}\",\"location\":\"{{this.object.location}}\",\"id\": \"{{this.object.id}}\"}}}"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.title",
                "values": ["No data"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.body",
                "values": ["There is no business data to display"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.link.label",
                "values": ["Learn more"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.link.href.type",
                "values": ["NEW_WINDOW"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.link.href.path",
                "values": ["https://www.ibm.com/docs/en/sscis?topic=troubleshooting-no-data-display"], 
                "metaInformation": "widgetOption"
            }
        ],
        "userConfigurations": [
            {
                "name": "chartType",
                "values": ["data_table"]
            }
        ],
        "adminConfigurations": []
    }'
  2. Add a location details table card.
    1. The card queries a location object based on the selected row in the previous query. To listen to an event, add the following lines to the card:
      {
      	"name": "inventoryPositionListRow.row",
      	"values": ["refreshData"],
      	"metaInformation": "stateSubscription"
      }
    2. Use the following property to read the new location ID from the event memory and set a new parameter on the query.

      Changing the query causes a new query to fire.

      Now this card waits for the data to change in the namespace inventoryPositionListRow.row. When this data is changed, the card needs to fire its query to load data about a location. This card does not issue the same query. Instead, it fetches the data object, which is the selected row, at location inventoryPositionListRow.row in memory and fetches the location.id.

      {
      	"name": "queries[0].variables.locationId",
      	"values": ["{{ inventoryPositionListRow.row.location.id }}"], 
      	"metaInformation": "infohub.variable.dynamic"
      }
    POST location details widget definition example
    curl --location --request POST 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/widget_definitions' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
        "offeringId": "SCO",
        "id": "CUSTOM_DASHBOARD_LOC_DETAIL_VIEW_ALL",
        "state": "ACTIVE",
        "identifier": "CUSTOM_DASHBOARD_LOC_DETAIL_VIEW_ALL",
        "type": "Dashboard_Widget",
        "tenantId":"<INSERT_YOUR_TENANT_ID_HERE>",
        "path": "/api",
        "vendor": "IBM",
        "defaultLanguage": "en",
        "version": 1,
        "descriptions": {
            "en": {
                "name": "Location Details",
                "description": "Location Details"
            }
        },
        "devConfigurations": [{
                "name": "resizeToContent",
                "values": ["true"],
                "metaInformation": "widgetOption",
                "sensitive": false
            }, {
                "name": "header.type",
                "values": ["text_header"],
                "metaInformation": "widgetOption",
                "required": false
            }, {
                "name": "header.title",
                "values": ["Location Details"],
                "metaInformation": "widgetOption",
                "required": false
            }, {
                "name": "draggable",
                "values": ["true"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "data.service",
                "values": ["infohub"]
            }, {
                "name": "queries[0].fields",
                "values": ["query LocationDetails($tenantId: String! $locationId: String = \"a\") { businessObjects( simpleFilter: { type: Location, tenantId: $tenantId } advancedFilter: { AND: [  {EQUALS: [{SELECT: \"id\", type: STRING} {VALUE: $locationId, type: STRING} ]} ]} hint: { viewId: \"graph\" } ) { totalCount pageInfo { hasNextPage endCursor } edges { cursor object { ... on Location { id locationName address1 address2 city country geo locationSubtype locationType stateProvince}}}}}"], 
                "metaInformation": "infohub.fields"
            }, {
                "name": "queries[0].variables.tenantId",
                "values": ["{{ tenantId }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.locationId",
                "values": ["{{ inventoryPositionListRow.row.location.id }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "direction",
                "values": ["vertical"],
                "metaInformation": "gridOption",
                "required": false
            }, {
                "name": "item[0].title",
                "values": ["Location Name"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[0].name",
                "values": ["locationName"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[1].title",
                "values": ["Address 1"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[1].name",
                "values": ["address1"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[2].title",
                "values": ["Address 2"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[2].name",
                "values": ["address2"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[3].title",
                "values": ["City"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[3].name",
                "values": ["city"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[4].title",
                "values": ["State"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[4].name",
                "values": ["state"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[5].title",
                "values": ["Country"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[5].name",
                "values": ["country"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[6].title",
                "values": ["Location Type"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[6].name",
                "values": ["locationType"],
                "metaInformation": "gridOption"
            }, {
                "name": "widgetInitialized",
                "values": ["refreshData"], 
                "metaInformation": "eventSubscription"
            }, {
                "name": "inventoryPositionListRow.row",
                "values": ["refreshData"],
                "metaInformation": "stateSubscription"
            }, {
                "name": "transformation",
                "values": ["{\"errors\":\"{{#? this.errors}}\",\"pageInfo\":{},\"totalCount\":\"{{this.data.businessObjects.totalCount}}\",\"results\":{\"{{#each this.data.businessObjects.edges}}\":{\"cursor\":\"{{this.cursor}}\",\"id\":\"{{this.object.id}}\",\"locationName\":\"{{this.object.locationName}}\",\"address1\":\"{{this.object.address1}}\",\"address2\":\"{{this.object.address2}}\",\"city\":\"{{this.object.city}}\",\"country\":\"{{this.object.country}}\",\"geo\":\"{{this.object.geo}}\",\"locationSubtype\":\"{{this.object.locationSubtype}}\",\"locationType\":\"{{this.object.locationType}}\",\"stateProvince\":\"{{this.object.stateProvince}}\"}}}"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.title",
                "values": ["Location Details"],
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.body",
                "values": ["Select a row in the table above"],
                "metaInformation": "widgetOption"
            }
        ],
        "userConfigurations": [
            {
                "name": "chartType",
                "values": ["grid_details"]
            }
        ],
        "adminConfigurations": []
    }'  
  3. Add an inventory details table card.
    Adding two card listeners shows that multiple listeners can act on the same event. The card listens to the same event at inventoryPositionListRow.row, but it queries for an inventory object instead of a location object. As an alternative, one card can make two queries. If the state of a card query infohub.variable.dynamic changes, the query fires.

    Additionally, these two cards can be made moveable by setting these values:

    {
    	"name": "draggable",
    	"values": ["true"], 
    	"metaInformation": "widgetOption"
    }
    POST new Inventory Details widget definition example
    curl --location --request POST 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/widget_definitions' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
        "offeringId": "SCO",
        "id": "CUSTOM_DASHBOARD_INV_DETAIL_VIEW_ALL",
        "state": "ACTIVE",
        "identifier": "CUSTOM_DASHBOARD_INV_DETAIL_VIEW_ALL",
        "type": "Dashboard_Widget",
        "tenantId":"<INSERT_YOUR_TENANT_ID_HERE>",
        "path": "/api",
        "vendor": "IBM",
        "defaultLanguage": "en",
        "version": 1,
        "descriptions": {
            "en": {
                "name": "Inventory Details",
                "description": "Inventory Details"
            }
        },
        "devConfigurations": [{
                "name": "header.type",
                "values": ["text_header"],
                "metaInformation": "widgetOption",
                "required": false
            }, {
                "name": "header.title",
                "values": ["Inventory Details"],
                "metaInformation": "widgetOption",
                "required": false
            }, {
                "name": "draggable",
                "values": ["true"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "data.service",
                "values": ["infohub"]
            }, {
                "name": "queries[0].fields",
                "values": ["query InventoryDetails($tenantId: String! $inventoryId: String = \"\") { businessObjects( simpleFilter: { type: Inventory, tenantId: $tenantId } advancedFilter: { AND: [  {EQUALS: [{SELECT: \"id\", type: STRING} {VALUE: $inventoryId, type: STRING} ]} ]} hint: { viewId: \"graph\" } ) { totalCount pageInfo { hasNextPage endCursor } edges { cursor object { ... on Inventory { product { partNumber } id  daysOfSupply quantity value }}}}}"], 
                "metaInformation": "infohub.fields"
            }, {
                "name": "queries[0].variables.tenantId",
                "values": ["{{ tenantId }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "queries[0].variables.inventoryId",
                "values": ["{{ inventoryPositionListRow.row.id }}"], 
                "metaInformation": "infohub.variable.dynamic"
            }, {
                "name": "item[0].title",
                "values": ["Product ID"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[0].name",
                "values": ["product.partNumber"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[1].title",
                "values": ["Days of Supply"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[1].name",
                "values": ["daysOfSupply"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[1].count.href.labelType",
                "values": ["numberTpl"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[2].title",
                "values": ["Value"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[2].name",
                "values": ["value"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[3].title",
                "values": ["Quantity"],
                "metaInformation": "gridOption"
            }, {
                "name": "item[3].name",
                "values": ["quantity"],
                "metaInformation": "gridOption"
            }, {
                "name": "widgetInitialized",
                "values": ["refreshData"], 
                "metaInformation": "eventSubscription"
            }, {
                "name": "inventoryPositionListRow.row",
                "values": ["refreshData"],
                "metaInformation": "stateSubscription"
            }, {
                "name": "transformation",
                "values": ["{\"errors\":\"{{#? this.errors}}\",\"pageInfo\":{},\"totalCount\":\"{{this.data.businessObjects.totalCount}}\",\"results\":{\"{{#each this.data.businessObjects.edges}}\":{\"cursor\":\"{{this.cursor}}\",\"id\":\"{{this.object.id}}\",\"product\":\"{{this.object.product}}\",\"daysOfSupply\":\"{{this.object.daysOfSupply}}\",\"quantity\":\"{{this.object.quantity}}\",\"value\":\"{{this.object.value}}\"}}}"], 
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.title",
                "values": ["Inventory Details"],
                "metaInformation": "widgetOption"
            }, {
                "name": "state.empty.body",
                "values": ["Select a row in the table"],
                "metaInformation": "widgetOption"
            }
        ],
        "userConfigurations": [
            {
                "name": "chartType",
                "values": ["grid_details"]
            }
        ],
        "adminConfigurations": []
    }'
  4. Add widget subscriptions for the two new cards.
    curl --location --request POST 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/subscriptions/subscribe' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
      "widgetDefinitionId": "CUSTOM_DASHBOARD_LOC_DETAIL_VIEW_ALL",
      "state": "ACTIVE",
      "subscriptionConfig": [],
      "tenantId":"<INSERT_YOUR_TENANT_ID_HERE>"
    }'
    curl --location --request POST 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/subscriptions/subscribe' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
      "widgetDefinitionId": "CUSTOM_DASHBOARD_INV_DETAIL_VIEW_ALL",
      "state": "ACTIVE",
      "subscriptionConfig": [],
      "tenantId":"<INSERT_YOUR_TENANT_ID_HERE>"
    }'
  5. Update the view all template.
    Update the template to include the two new cards and resize them to fit side by side. First, delete the view all template before you insert the new one.
    curl --location --request DELETE 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/layout_templates/CUSTOM_DASHBOARD_INVENTORY_VIEW_ALL_LAYOUT_TEMPLATE' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    POST updated layout template example
    curl --location --request POST 'https://api.ibm.com/supplychainui/run/na/api/v1/wms/layout_templates' \
    --header 'Content-Type: application/json' \
    --header 'x-ibm-client-id: supplychainui-<INSERT_YOUR_TENANT_ID_HERE>' \
    --header 'Authorization: Bearer <redacted>' \
    --data-raw '{
        "id": "CUSTOM_DASHBOARD_INVENTORY_VIEW_ALL_LAYOUT_TEMPLATE",
        "offeringId": "SCO",
        "state": "ACTIVE",
        "sequence": 0.0,
        "tenantId": "<INSERT_YOUR_TENANT_ID_HERE>",
        "type": "View_All_Layout",
    	"defaultLanguage": "en",
        "descriptions": {
            "en": {
                "name": "Inventory By Product",
                "description": "Inventory By Product"
            }
        },
        "widgets": [
            {
                "tenantId": "<INSERT_YOUR_TENANT_ID_HERE>",
                "sequence": 0.0,
                "widgetDefinitionId": "CUSTOM_DASHBOARD_INVENTORY_TABLE_VIEW_ALL",
                "name": "Inventory Units",
                "widgetProperties": {},
                "displayProperties": {
                    "xPos": "0",
                    "yPos": "0",
                    "rowSpan": "30",
                    "colSpan": "2",
                    "chartType": "data_table",
                    "fullBodyWidget": "true",
                    "enabled": "true",
                    "section": "layout_body"
                }
            },
    
            {
                "tenantId": "<INSERT_YOUR_TENANT_ID_HERE>",
                "sequence": 2.0,
                "widgetDefinitionId": "CUSTOM_DASHBOARD_LOC_DETAIL_VIEW_ALL",
                "name": "Location Details",
                "widgetProperties": {},
                "displayProperties": {
                    "xPos": "2",
                    "yPos": "0",
                    "rowSpan": "15",
                    "colSpan": "2",
                    "chartType": "grid_details",
                    "enabled": "true",
                    "section": "layout_body"
                }
            },
            {
                "tenantId": "<INSERT_YOUR_TENANT_ID_HERE>",
                "sequence": 3.0,
                "widgetDefinitionId": "CUSTOM_DASHBOARD_INV_DETAIL_VIEW_ALL",
                "name": "Inventory Details",
                "widgetProperties": {},
                "displayProperties": {
                    "xPos": "2",
                    "yPos": "1",
                    "rowSpan": "15",
                    "colSpan": "2",
                    "chartType": "grid_details",
                    "enabled": "true",
                    "section": "layout_body"
                }
            }
        ],
        "properties": [{
                "name": "breadcrumbEnabled",
                "metaInformation": "layoutOption",
                "values": ["true"]
            }, {
                "name": "breadcrumbList",
                "metaInformation": "layoutOption",
                "values": ["{\"label\": \"Inventory By Product\", \"routeName\": \"/template/CUSTOM_DASHBOARD_INVENTORY_VIEW_ALL_LAYOUT_TEMPLATE\"}"]
            }, {
                "name": "timestampEnabled",
                "metaInformation": "layoutOption",
                "values": ["true"]
            }, {
                "name": "ivAssistantEnabled",
                "metaInformation": "layoutOption",
                "values": ["true"]
            }, {
                "name": "state.empty.title",
                "metaInformation": "layoutOption",
                "values": ["No data"]
            }, {
                "name": "state.empty.body",
                "metaInformation": "layoutOption",
                "values": ["There is no data to display"]
            }, {
                "name": "state.empty.image",
                "metaInformation": "layoutOption",
                "values": ["dataEmpty"]
            }, {
                "name": "state.empty.link.label",
                "metaInformation": "layoutOption",
                "values": ["Learn more"]
            }, {
                "name": "state.empty.link.href.type",
                "metaInformation": "layoutOption",
                "values": ["NEW_WINDOW"]
            }, {
                "name": "state.empty.link.href.path",
                "metaInformation": "layoutOption",
                "values": ["https://www.ibm.com/docs/en/sscis?topic=troubleshooting-no-data-display"]
            }
        ],
        "version": 1
    }'

What to do next

Go to Intelligence Suite to test your changes. As you click each row, the two other cards will reload data instantly. If you click the same row twice, the data does not reload because it is in memory already.

Notes about events and state stores:

When you create a namespace, such as inventoryPositionListRow.row in this example, it persists in memory through the user's session. The dashboard might show filtered results. The filters are kept in memory and recalled from your card on the View All page. If you have two different pages, you might want your namespaces to be named differently.

All events that are supported by the framework are listed. Each event can be fired by an action from another card or component.
  • buttonClicked
  • columnSorted
  • comboBoxSearch
  • comboBoxTypeAhead
  • filterChanged
  • filterSearched
  • pageChanged
  • persistData
  • rowClicked
  • searched
  • tabSelected
  • widgetDestroyed
  • widgetInitialized