Author:
Siarhei Vaitsiakhovich
Changed on:
4 July 2024
To effectively manage customer information within our system, the Edit Customer feature is used. This functionality allows users to update customer details such as contact information, ensuring that the customer database remains accurate and up-to-date.
The feature can be implemented via the declaration of a custom mutation user action
`updateCustomer`
Additionally, the fields have to be reordered to enhance the logical flow of the user interface, making it more intuitive for users to navigate and input their information efficiently. New fields order:
To save data consistency, options for Timezone and Country selectors can be loaded from settings. All fields have to be prefilled with current user information. Additionally, the field username has to be loaded as a customer reference but hidden to avoid reference change.
To support the business solution design, the following technical areas need to be enhanced:
`SettingBaseStringSelector`
`BooleanSelector`
Each of these tasks will be described below in steps.
`SettingBaseStringSelector`
Create a new file:
`SettingBaseStringSelector.tsx`
1import { FC, useEffect, useState } from 'react';
2import { FormFieldProps } from 'mystique/registry/FieldRegistry';
3import { getSettings } from 'mystique/hooks/getSettings';
4import {
5 FormControl,
6 FormHelperText,
7 InputLabel,
8 MenuItem,
9 Select,
10} from '@material-ui/core';
11import { useAuth } from 'mystique/hooks/useAuth';
12
13const SettingBaseStringSelector: FC<FormFieldProps<any>> = (props) => {
14 const auth = useAuth();
15
16 const settingName = props.extensions?.settingName;
17 const [items, setItems] = useState<string[]>([]);
18 const [currentItem, setCurrentItem] = useState<string | undefined>(
19 props.value,
20 );
21
22 useEffect(() => {
23 if (settingName) {
24 getSettings(
25 { setting: settingName },
26 parseInt(auth.context.current.contextId),
27 ).then((value) => {
28 if (value.setting.status == 'ok') {
29 const list: string[] = [...value.setting.result.value];
30 if (props.value && !list.includes(props.value)) {
31 list.push(props.value);
32 }
33 setItems(list);
34 }
35 });
36 } else {
37 setItems([]);
38 }
39 }, [settingName]);
40
41 useEffect(() => {
42 if (currentItem) {
43 props.onChange(currentItem);
44 } else {
45 props.onChange(undefined);
46 }
47 }, [currentItem]);
48
49 const handleChange = (event: any) => {
50 setCurrentItem(event.target.value as string);
51 };
52
53 const handleOnBlur = () => {
54 props && props.onBlur && props.onBlur();
55 };
56
57 return (
58 <FormControl fullWidth error={!!props.error}>
59 <InputLabel id="string-select-label">{props.label}</InputLabel>
60 <Select
61 labelId="string-select-label"
62 id="string-select"
63 label={props.label}
64 onChange={handleChange}
65 onBlur={handleOnBlur}
66 value={currentItem}
67 >
68 {items.map((value, idx) => {
69 return (
70 <MenuItem key={idx} value={value} selected={currentItem === value}>
71 {value}
72 </MenuItem>
73 );
74 })}
75 </Select>
76 {props.error && <FormHelperText>{props.error}</FormHelperText>}
77 </FormControl>
78 );
79};
80
81export default SettingBaseStringSelector;
Language: typescript
Name: SettingBaseStringSelector.tsx
Description:
SettingBaseStringSelector component implementation example
1import SettingBaseStringSelector from './fields/SettingBaseStringSelector';
2
3
4
5
6
7FieldRegistry.register(
8 ['settingBaseStringSelector'],
9 SettingBaseStringSelector,
10);
Language: typescript
Name: index.tsx
Description:
SettingBaseStringSelector field component registration
`BooleanSelector`
Create a new file:
`BooleanSelector.tsx`
1import { FC, useEffect, useState } from 'react';
2import { FormFieldProps } from 'mystique/registry/FieldRegistry';
3import {
4 Checkbox,
5 FormControl,
6 FormControlLabel,
7 FormHelperText,
8} from '@material-ui/core';
9
10
11const BooleanSelector: FC<FormFieldProps<any>> = (props) => {
12 const [checked, setChecked] = useState<boolean>(
13 props.extensions?.checked || props.value || false,
14 );
15
16 useEffect(() => {
17 props.onChange(`${checked}`);
18 }, [checked]);
19
20 const handleChange = (event: any) => {
21 setChecked(event.target.checked as boolean);
22 };
23
24 const handleOnBlur = () => {
25 props && props.onBlur && props.onBlur();
26 };
27
28 return (
29 <FormControl fullWidth error={!!props.error}>
30 <FormControlLabel
31 control={
32 <Checkbox
33 id="checkbox-selector"
34 onChange={handleChange}
35 onBlur={handleOnBlur}
36 checked={checked}
37 />
38 }
39 label={props.label}
40 />
41 {props.error && <FormHelperText>{props.error}</FormHelperText>}
42 </FormControl>
43 );
44};
45
46export default BooleanSelector;
Language: typescript
Name: BooleanSelector.tsx
Description:
BooleanSelector component implementation example
1import BooleanSelector from './fields/BooleanSelector';
2
3
4
5FieldRegistry.register(['booleanSelector'], BooleanSelector);
Language: typescript
Name: index.tsx
Description:
BooleanSelector field component registration
`EDIT_LOCATION_COUNTRIES`
Name | EDIT_LOCATION_COUNTRIES |
Value Type | JSON |
Context | ACCOUNT or RETAILER |
Context ID | 0 or Retailer ID |
JSON Value |
|
1POST: {{fluentApiHost}}/graphql
2
3## create a postman environment variable:
4## Variable: json_value
5## initial val + current value:
6[
7 "Australia",
8 "Canada",
9 "France",
10 "Germany",
11 "United Kingdom",
12 "United States"
13]
14
15
16# GraphQL variables:
17{
18 "retailerId": {{retailer_id}},
19 "lobValue" : {{json_value}}
20}
21
22
23#GraphQL Query:
24mutation CreateSetting($retailerId:Int! , $lobValue:Json) {
25createSetting(input: {
26 name: "EDIT_LOCATION_COUNTRIES",
27 valueType: "JSON",
28 lobValue:$lobValue ,
29 context: "RETAILER",
30 contextId:$retailerId}) {
31 id
32 name
33 }
34}
Language: graphqlschema
Name: create setting EDIT_LOCATION_COUNTRIES
Description:
[Warning: empty required content area]1POST: {{fluentApiHost}}/graphql
2
3## create a postman environment variable:
4## Variable: json_value
5## initial val + current value:
6[
7 "Australia",
8 "Canada",
9 "France",
10 "Germany",
11 "United Kingdom",
12 "United States"
13]
14
15
16# GraphQL variables:
17{
18 "retailerId": {{retailer_id}},
19 "lobValue" : {{json_value}}
20}
21
22
23#GraphQL Query:
24mutation updateSetting($retailerId:Int! , $lobValue:Json) {
25updateSetting(input: {
26 id: 5001471,
27 name: "EDIT_LOCATION_COUNTRIES",
28 valueType: "JSON",
29 lobValue: $lobValue,
30 context: "RETAILER",
31 contextId: $retailerId}) {
32 id
33 name
34 }
35}
36
Language: graphqlschema
Name: update setting EDIT_LOCATION_COUNTRIES
Description:
[Warning: empty required content area]`EDIT_LOCATION_TIME_ZONES`
Name | EDIT_LOCATION_TIME_ZONES |
Value Type | JSON |
Context | ACCOUNT or RETAILER |
Context ID | 0 or Retailer ID |
JSON Value |
|
1POST: {{fluentApiHost}}/graphql
2
3## create a postman environment variable:
4## Variable: json_value
5## initial val + current value:
6[
7 "GMT",
8 "UTC",
9 "ECT",
10 "EET",
11 "ACT",
12 "AET",
13 "HST",
14 "AST",
15 "PST",
16 "PNT",
17 "MST",
18 "CST",
19 "EST",
20 "IET",
21 "PRT",
22 "CNT"
23]
24
25
26# GraphQL variables:
27{
28 "retailerId": {{retailer_id}},
29 "lobValue" : {{json_value}}
30}
31
32
33#GraphQL Query:
34mutation CreateSetting($retailerId:Int! , $lobValue:Json) {
35createSetting(input: {
36 name: "EDIT_LOCATION_TIME_ZONES",
37 valueType: "JSON",
38 lobValue:$lobValue ,
39 context: "RETAILER",
40 contextId:$retailerId}) {
41 id
42 name
43 }
44}
Language: graphqlschema
Name: create setting EDIT_LOCATION_TIME_ZONES
Description:
[Warning: empty required content area]1POST: {{fluentApiHost}}/graphql
2
3## create a postman environment variable:
4## Variable: json_value
5## initial val + current value:
6[
7 "GMT",
8 "UTC",
9 "ECT",
10 "EET",
11 "ACT",
12 "AET",
13 "HST",
14 "AST",
15 "PST",
16 "PNT",
17 "MST",
18 "CST",
19 "EST",
20 "IET",
21 "PRT",
22 "CNT"
23]
24
25
26# GraphQL variables:
27{
28 "retailerId": {{retailer_id}},
29 "lobValue" : {{json_value}}
30}
31
32
33#GraphQL Query:
34mutation updateSetting($retailerId:Int! , $lobValue:Json) {
35updateSetting(input: {
36 id: 5001471,
37 name: "EDIT_LOCATION_TIME_ZONES",
38 valueType: "JSON",
39 lobValue: $lobValue,
40 context: "RETAILER",
41 contextId: $retailerId}) {
42 id
43 name
44 }
45}
46
Language: graphqlschema
Name: update setting EDIT_LOCATION_TIME_ZONES
Description:
[Warning: empty required content area]`Edit Customer`
`updateCustomer`
1{
2 "type": "mutation",
3 "label": "i18n:fc.om.customers.updateCustomer.button.Label",
4 "name": "updateCustomer",
5 "args": {
6 "ref": "{{customerById.username}}"
7 },
8 "overrides": {
9 "title": {
10 "sortPrefix": 11
11 },
12 "firstName": {
13 "sortPrefix": 12
14 },
15 "lastName": {
16 "sortPrefix": 13
17 },
18 "primaryEmail": {
19 "sortPrefix": 14
20 },
21 "primaryPhone": {
22 "sortPrefix": 15
23 },
24 "country": {
25 "sortPrefix": 17,
26 "component": "settingBaseStringSelector",
27 "extensions": {
28 "settingName": "EDIT_LOCATION_COUNTRIES"
29 }
30 },
31 "timezone": {
32 "sortPrefix": 18,
33 "component": "settingBaseStringSelector",
34 "extensions": {
35 "settingName": "EDIT_LOCATION_TIME_ZONES"
36 }
37 },
38 "promotionOptIn": {
39 "component": "booleanSelector",
40 "sortPrefix": 19
41 },
42 "attributes": {
43 "sortPrefix": 21
44 },
45 "username": {
46 "sortPrefix": 22,
47 "component": "input",
48 "options": [
49 {
50 "label": "current Username",
51 "value": "{{customerById.username}}"
52 }
53 ]
54 }
55 }
56}
Language: json
Name: Edit Customer mutation user action
Description:
Edit Customer mutation user action declaration example. Property
`sortPrefix`
Localization of fields is done by creating localization entries to mutation fields according to name convention (see Languages and Localisation, chapter 'Adding new Mutation Actions').
1{
2 "translation": {
3 "fc.om.customers.updateCustomer.button.Label": "Update Customer",
4 "fc.gql.customer.title": "Title",
5 "fc.gql.customer.firstName": "First Name",
6 "fc.gql.customer.lastName": "Last Name",
7 "fc.gql.customer.username": "Username",
8 "fc.gql.customer.primaryEmail": "Primary Email",
9 "fc.gql.customer.primaryPhone": "Primary Phone",
10 "fc.gql.customer.country": "Country",
11 "fc.gql.customer.timezone": "Timezone",
12 "fc.gql.customer.promotionOptIn": "Customer has opted to receive promotions",
13 "fc.gql.customer.retailer.label": "Retailer"
14 }
15}
Language: json
Name: New localization entries
Description:
Example of mutation field labels localisation with using name convention
After all steps,
`Edit Customer`
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.