Fluent Commerce Logo
Docs
Sign In

Building Better Filters: From Reusable Components to Customer Search

How-to Guide

Authors:

Yulia Andreyanova, Alexey Kaminskiy

Changed on:

12 June 2025

Key Points

No alt text provided

Steps

Step arrow right iconStart with the Default Manifest

Begin by using the default manifest

`fc.mystique.manifest.oms.fragment.ordermanagement`
as your base. If you're unfamiliar with overriding a manifest or need access to the baseline manifest, refer to the 'Overriding the Default Manifest' guide.

This guide will focus exclusively on the Orders menu item within the manifest, so the full fragment won’t be included here.

Step arrow right iconRefine the GraphQL Query

Use a GraphQL query tailored for this feature. Start with an existing query, but remove unnecessary fields to ensure simplicity and efficiency. Modify the query to support the customer input field. Make sure the

`$customer`
variable is defined and passed in the query. Here’s the adjusted version:

1query ($orders_first: Int, $customer: [SearchCustomerInput]) {
2  orders(first: $orders_first, customer: $customer) {
3    edges {
4      node {
5        id
6        ref
7        retailer {
8          id
9          tradingName
10        }
11        type
12        status
13        createdOn
14        totalPrice
15        items {
16          edges {
17            node {
18              quantity
19              currency
20            }
21          }
22        }
23        customer {
24          id
25          firstName
26          lastName
27          username
28        }
29      }
30    }
31  }
32}

Language: json

Name: Refined Query

Description:

This query fetches key details about orders, including retailer info, items, and customer data.

Step arrow right iconConfigure the Filter Panel

After refining the query, set up the Filter Panel Component to enable advanced filtering options. The configuration guide provides detailed instructions on how to do this. 

Key Optimizations:
  • Exclude Unwanted Filters: Use the
    `exclude`
    property to remove unnecessary fields, simplifying the filtering process.
1 "exclude": [
2                "eta",
3                "totalprice",
4                "price",
5                "totaltaxprice",
6                "currency",
7                "paidprice",
8                "quantity",
9                "retailerid",
10                "fulfilmentchoiceref",
11                "expirytime",
12                "deliverytype",
13                "updatedon",
14                "taxprice",
15                "taxtype",
16                "status",
17                "createdon"
18            ]

Language: plain_text

Name: Example: Using exclude to Remove Unnecessary Fields

Description:

This example excludes unused fields like

`eta`
and
`taxprice`
to simplify filtering options.

  • Handle Duplicate Field Names: Fields like
    `status`
    may appear in both orders and order items. Excluding
    `status`
    removes it from both. To avoid this, explicitly define filters using
    `additionalFields`
    .
  • Override Filters for Custom Components: Some fields, like
    `type`
    , may need custom configurations. Use the
    `overrides`
    property to define custom dropdown values. You can do this through the manifest by listing options or by creating a JSON setting at the ACCOUNT or RETAILER level. The examples below show both methods.
1{
2  "component": "fc.filterPanel",
3  "props": {
4    "allowReadWriteUrlParams": true,
5    "overrides": {
6      "type": {
7        "component": "select",
8        "label": "Type",
9        "multiple": true,
10        "sortPrefix": 3,
11        "extensions": {
12          "hideDefaultValue": true
13        },
14        "options": [
15          {
16            "label": "HD",
17            "value": "HD"
18          },
19          {
20            "label": "CC",
21            "value": "CC"
22          }
23        ]
24      }
25    },
26    "additionalFields": [
27      {
28        "component": "select",
29        "props": {
30          "label": "Status",
31          "multiple": true,
32          "variableName": "orders_status",
33          "sortPrefix": 3,
34          "extensions": {
35            "hideDefaultValue": true
36          },
37          "options": [
38            {
39              "label": "BOOKED",
40              "value": "BOOKED"
41            },
42            {
43              "label": "PROCESSING",
44              "value": "PROCESSING"
45            }
46          ]
47        }
48      },
49      {
50        "component": "daterange",
51        "props": {
52          "label": "CreatedOn",
53          "sortPrefix": 5,
54          "variableName": "orders_createdOn"
55        }
56      }
57    ],
58    "exclude": [
59      "eta",
60      "totalprice",
61      "price",
62      "totaltaxprice",
63      "currency",
64      "paidprice",
65      "quantity",
66      "retailerid",
67      "fulfilmentchoiceref",
68      "expirytime",
69      "deliverytype",
70      "updatedon",
71      "ref",
72      "taxprice",
73      "taxtype",
74      "status",
75      "createdon"
76    ]
77  }
78}

Language: plain_text

Name: Defining Custom Dropdown Values in the Manifest

Description:

[Warning: empty required content area]
1 {
2  "additionalFields": [
3    {
4      "component": "select",
5      "props": {
6        "label": "Status",
7        "multiple": true,
8        "variableName": "orders_status",
9        "sortPrefix": 3,
10        "extensions": {
11          "hideDefaultValue": true,
12          "source": "fc.oms.mystique.order.search.statuses"
13        }
14      }
15    },
16    {
17      "component": "daterange",
18      "props": {
19        "label": "CreatedOn",
20        "sortPrefix": 5,
21        "variableName": "orders_createdOn"
22      }
23    }
24  ]
25}

Language: json

Name: Defining Fields for Filtering via a Setting

Description:

[Warning: empty required content area]

Step arrow right iconSearch Orders by Customer Name, Email, or Phone

Customer filtering on the Order List can be configured in two ways, depending on the level of flexibility and UX required.

Option 1: Complex Filter Component (Recommended for advanced use cases)

Use a Complex Filter Component to enable flexible search by customer fields such as

`username`
,
`firstName`
,
`lastName`
,
`primaryEmail`
, and
`primaryPhone`
. This setup provides an enhanced user experience through autocomplete, searchable cards, and chip-based selection.

The component connects to the

`customers`
data source and maps selected values to the
`customer`
variable in the query using the
`SearchCustomerInput`
structure.

It supports dynamic rendering of selected users as chips and configurable card views for search results, and hides irrelevant fields to keep the filter UI clean and focused.

1{
2  "component": "fc.field.filterComplex",
3  "props": {
4    "label": "Customer",
5    "variableName": "customer",
6    "sortPrefix": 5,
7    "outputTemplate": "{\"username\": \"{{value}}\"}",
8    "inputTemplate": "{{username}}",
9    "extensions": {
10      "filtersSource": "customers",
11      "query": "query customers($customers_first: Int) { customers(first: $customers_first) { edges { node { id username ref primaryPhone primaryEmail firstName lastName } } } }",
12      "variables": {
13        "customers_first": 100
14      },
15      "overrides": {
16        "firstName": {
17          "component": "fc.field.multistring",
18          "sortPrefix": 1
19        }
20      },
21      "searchItemConfig": {
22        "component": "fc.card.product",
23        "props": {
24          "title": "{{node.username}}",
25          "attributes": [
26            { "value": "{{node.firstName}} {{node.lastName}}" },
27            { "value": "{{node.primaryPhone}}" },
28            { "value": "{{node.primaryEmail}}" }
29          ]
30        }
31      },
32      "chipItemConfig": {
33        "label": "{{node.firstName}} {{node.lastName}}"
34      },
35      "onChangeValues": {
36        "value": "node.username",
37        "variableName": "customer"
38      },
39      "exclude": [
40        "updatedon", "status", "createdon", "department",
41        "title", "country", "timeZone", "promotionOptIn"
42      ]
43    }
44  }
45}

Language: plain_text

Name: Configure Complex Customer Filter

Description:

Adds customer search to the order filter using the flexible

`SearchCustomerInput`
type and dynamic UI elements.

Use this configuration in the

`additionalFields`
array of the filter panel. It complements other filters and extends customer search across the order list.

Option 2: Basic Filter Fields

Use the Multi-Value Search Component to add individual input fields to target nested variables such as

`customer.firstName`
,
`customer.lastName`
, etc. These fields are configured directly in the
`additionalFields`
array of the
`fc.filterPanel`
.

1{
2  "manifestVersion": "2.0",
3  "routes": [
4    {
5      "type": "section",
6      "nav": {
7        "label": "i18n:fc.om.nav",
8        "icon": "shopping_cart"
9      },
10      "pages": [
11        {
12          "type": "page",
13          "path": "events",
14          "component": "fc.page",
15          "data": {
16            "query": "query ($orders_first: Int, $customer: [SearchCustomerInput]) { orders(first: $orders_first, customer: $customer) { edges { node {id ref retailer { id tradingName } type status createdOn totalPrice items {edges {node {quantity currency}}} customer {id firstName lastName username}}}}}",
17            "variables": {
18              "orders_first": 100
19            }
20          },
21          "nav": {
22            "label": "i18n:fc.om.orders.index.nav",
23            "icon": "library_books"
24          },
25          "props": {
26            "title": "i18n:fc.om.orders.index.title"
27          },
28          "descendants": [
29            {
30              "component": "fc.filterPanel",
31              "props": {
32                "allowReadWriteUrlParams": true,
33                "filtersSource": "orders",
34                "overrides": {
35                  "status": {
36                    "component": "select",
37                    "label": "Status",
38                    "multiple": true,
39                    "sortPrefix": 3,
40                    "extensions": {
41                      "hideDefaultValue": true,
42                      "source": "fc.oms.mystique.inventory.search.inventory.position.stock.statuses"
43                    }
44                  }
45                },
46                "additionalFields": [
47                  {
48                    "component": "fc.field.multistring",
49                    "props": {
50                      "label": "First Name",
51                      "variableName": "customer.firstName",
52                      "sortPrefix": 1
53                    }
54                  },
55                  {
56                    "component": "fc.filter.string",
57                    "props": {
58                      "label": "Last Name",
59                      "variableName": "customer.lastName",
60                      "sortPrefix": 2
61                    }
62                  }
63                ],
64                "exclude": [
65                  "createdOn",
66                  "currency",
67                  "paidprice",
68                  "price",
69                  "productRef",
70                  "ref",
71                  "ref2",
72                  "retailerid",
73                  "tag1",
74                  "tag2",
75                  "tag3",
76                  "taxprice",
77                  "taxtype",
78                  "totalprice",
79                  "totaltaxprice",
80                  "type",
81                  "quantity",
82                  "updatedon"
83                ]
84              }
85            },
86            {
87              "component": "fc.list",
88              "props": {
89                "defaultPageSize": 100,
90                "dataSource": "orders",
91                "responsiveness": "card",
92                "attributes": [
93                  {
94                    "label": "i18n:fc.om.orders.index.list.column.orderRef.heading",
95                    "template": "{{node.ref}}",
96                    "link": "#/orders/{{node.id}}/{{node.retailer.id}}/{{node.ref}}",
97                    "condition": "{{and node.id node.retailer.id node.ref}}"
98                  },
99                  {
100                    "label": "i18n:fc.om.orders.index.list.column.customer.heading",
101                    "template": "{{node.customer.firstName}} {{node.customer.lastName}}"
102                  },
103                  {
104                    "label": "i18n:fc.om.orders.index.list.column.orderType.heading",
105                    "template": "{{node.type}}"
106                  },
107                  {
108                    "label": "i18n:fc.om.orders.index.list.column.status.heading",
109                    "type": "component",
110                    "options": {
111                      "component": "fc.attribute.column",
112                      "props": {
113                        "contentColumnSetting": "fc.order.list.status.column",
114                        "value": "{{status}}"
115                      },
116                      "dataSource": "node"
117                    }
118                  },
119                  {
120                    "label": "i18n:fc.om.orders.index.list.column.orderValue.heading",
121                    "template": "{{currency node.totalPrice node.items.edges.0.node.currency}}"
122                  },
123                  {
124                    "label": "Total Order Items",
125                    "value": "{{node.items.edges.length}}"
126                  },
127                  {
128                    "label": "Total Order Item Qty",
129                    "value": "{{arraySum node.items.edges 'node.quantity'}}"
130                  },
131                  {
132                    "label": "i18n:fc.om.orders.index.list.column.createdOn.heading",
133                    "template": "{{dateStringFormatter node.createdOn}} ({{dateRelative node.createdOn}})"
134                  }
135                ]
136              }
137            }
138          ]
139        }
140      ]
141    }
142  ]
143}

Language: plain_text

Name: Basic Filter Fields Configuration

Description:

Configuration example

Step arrow right iconUse Multi-Value Search Component for Filtering by Order References

When users need to search for multiple order references at once, precision matters; the Multi-Value Search Component handles this well. Each input becomes a chip, duplicates are removed, and extra spaces are trimmed.

Below is how to configure the

`ref`
field for exact, case-sensitive matching. The chip display collapses after three entries for a cleaner UI.

1 {
2  "overrides": {
3    "ref": {
4      "component": "fc.field.multistring",
5      "label": "Order Reference",
6      "exactSearch": true,
7      "visibleItemsThreshold": 3,
8      "sortPrefix": 3
9    }
10  }
11}

Language: plain_text

Name: Configure Multi-Value Order Reference Filter

Description:

Adds a filter field for exact match multi-reference lookup with chip display and auto-cleaning.

Use this in the

`fc.filterPanel`
`props.overrides`
section, alongside your other custom filters.

Step arrow right iconAdd the Grid with the Embedded Filter Panel

Use the standard List component for the grid. Configure the row setting of the

`fc.list`
component to embed the filter panel into the grid.

  • A separate query is executed to fetch an order by its ID and associated fulfillments for the selected order.
  • Like the filter panel, exclude unnecessary fields and override others to match specific requirements. For fulfillment status and delivery type filters, either create JSON settings or define dropdown values through the manifest. In the example below, the options are set using ACCOUNT level JSON settings:
    `fc.oms.mystique.fulfilment.search.statuses`
    and
    `fc.oms.mystique.fulfilment.search.delivery.types`
    .
  • The grid itself, also a
    `fc.list `
    component, is configured with a data source and attributes similar to those used for fulfillment on the Order Details Page.
1{
2  "component": "fc.list",
3  "props": {
4    "defaultPageSize": 100,
5    "dataSource": "orders",
6    "responsiveness": "card",
7    "row": {
8      "expansion": {
9        "toggle": true,
10        "descendants": [
11          {
12            "component": "fc.filterPanel",
13            "props": {
14              "noCard": true,
15              "query": "query orderById($id: ID!) { orderById(id: $id) { fulfilments { edges { node { id status deliveryType createdOn fromAddress { ref companyName name } toAddress { ref companyName name } } } } } } }",
16              "variables": {
17                "id": "{{node.id}}"
18              },
19              "overrides": {
20                "status": {
21                  "component": "select",
22                  "label": "Status",
23                  "multiple": true,
24                  "sortPrefix": 3,
25                  "extensions": {
26                    "hideDefaultValue": true,
27                    "source": "fc.oms.mystique.fulfilment.search.statuses"
28                  }
29                },
30                "deliveryType": {
31                  "component": "select",
32                  "label": "Delivery Type",
33                  "multiple": true,
34                  "sortPrefix": 3,
35                  "extensions": {
36                    "hideDefaultValue": true,
37                    "source": "fc.oms.mystique.fulfilment.search.delivery.types"
38                  }
39                }
40              },
41              "exclude": ["ref", "fulfilmentchoiceref", "type", "eta", "updatedon", "expirytime", "retailerid"],
42              "descendants": [
43                {
44                  "component": "fc.list",
45                  "dataSource": "orderById.fulfilments",
46                  "props": {
47                    "attributes": [
48                      {
49                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.id.heading",
50                        "template": "{{node.id}}",
51                        "link_template": "#/fulfilment/{{node.id}}",
52                        "condition": "{{and node.id}}"
53                      },
54                      {
55                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.status.heading",
56                        "template": "{{node.status}}"
57                      },
58                      {
59                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.deliveryType.heading",
60                        "template": "{{node.deliveryType}}"
61                      },
62                      {
63                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.fulfilmentLocation.heading",
64                        "template": "{{node.fromAddress.companyName}} {{#if node.fromAddress.name}}{{node.fromAddress.name}}{{/if}}"
65                      },
66                      {
67                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.destination.heading",
68                        "template": "{{node.toAddress.companyName}} {{node.toAddress.name}}"
69                      },
70                      {
71                        "label": "i18n:fc.om.orders.detail.list.fulfilments.column.createdOn.heading",
72                        "template": "{{dateStringFormatter node.createdOn}}"
73                      }
74                    ]
75                  }
76                }
77              ]
78            }
79          }
80        ]
81      }
82    }
83  }
84}

Language: plain_text

Name: List Configuration Example

Description:

This grid displays orders with expandable rows that include filter panels for more options.

Step arrow right iconFinal Step: Add the Fragment to the Main Manifest

After configuring the new fragment, add it to your main manifest.

Yulia Andreyanova

Yulia Andreyanova

Contributors:
Alexey Kaminskiy