Skip to content

Commit 011513e

Browse files
committed
Use async select for all select components
1 parent 245def0 commit 011513e

File tree

5 files changed

+136
-62
lines changed

5 files changed

+136
-62
lines changed

packages/fhir-group-management/src/components/LocationInventory/form.tsx

+62-27
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React, { useMemo } from 'react';
2-
import { Form, Button, Input, DatePicker, Space, Select } from 'antd';
1+
import React from 'react';
2+
import { Form, Button, Input, DatePicker, Space } from 'antd';
33
import { SelectProps } from 'antd/lib/select';
44
import { AsyncSelectProps, formItemLayout, tailLayout } from '@opensrp/react-utils';
55
import { useTranslation } from '../../mls';
66
import { useQueryClient, useMutation } from 'react-query';
77
import { Dictionary } from '@onaio/utils';
8+
import { supplyMgSnomedCode, snomedCodeSystem } from '../../helpers/utils';
89
import {
910
sendSuccessNotification,
1011
sendErrorNotification,
@@ -21,20 +22,20 @@ import {
2122
donor,
2223
PONumber,
2324
groupResourceType,
25+
valuesetResourceType,
26+
UNICEF_SECTION_ENDPOINT,
2427
} from '../../constants';
2528
import { groupSelectfilterFunction, SelectOption } from '../ProductForm/utils';
26-
import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup';
2729
import { FHIRServiceClass, AsyncSelect } from '@opensrp/react-utils';
2830
import { IValueSet } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IValueSet';
29-
import { useQuery } from 'react-query';
30-
import { getValuesetSelectOptions } from './utils';
31+
import { getValuesetSelectOptions, projectOptions } from './utils';
32+
import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle';
3133

3234
const { Item: FormItem } = Form;
3335

3436
export interface LocationInventoryFormProps {
3537
fhirBaseURL: string;
3638
initialValues: Dictionary;
37-
products?: IGroup[];
3839
disabled: string[];
3940
cancelUrl?: string;
4041
successUrl?: string;
@@ -45,29 +46,21 @@ const defaultProps = {
4546
disabled: [],
4647
};
4748

49+
const productQueryFilters = {
50+
code: `${snomedCodeSystem}|${supplyMgSnomedCode}`,
51+
};
52+
4853
/**
4954
* Add location inventory form
5055
*
5156
* @param props - LocationInventoryFormProps component props
5257
* @returns returns form to add location inventories
5358
*/
5459
const AddLocationInventoryForm = (props: LocationInventoryFormProps) => {
55-
const { fhirBaseURL, initialValues, products } = props;
60+
const { fhirBaseURL, initialValues } = props;
5661
const { t } = useTranslation();
5762
const queryClient = useQueryClient();
5863

59-
const projectOptions = useMemo(
60-
() => products?.map((prod: IGroup) => ({ value: prod.id, label: prod.name })),
61-
[products]
62-
);
63-
64-
const valuesetResourceType = 'ValueSet';
65-
const resourceId = '2826/$expand';
66-
const groupQuery = () =>
67-
useQuery([valuesetResourceType, resourceId], async () =>
68-
new FHIRServiceClass<IValueSet>(fhirBaseURL, valuesetResourceType).read(resourceId as string)
69-
);
70-
7164
const { mutate, isLoading } = useMutation(
7265
(values: Dictionary) => {
7366
return postLocationInventory(fhirBaseURL, values);
@@ -85,17 +78,59 @@ const AddLocationInventoryForm = (props: LocationInventoryFormProps) => {
8578
}
8679
);
8780

88-
const unicefSectionProps: AsyncSelectProps = {
81+
const unicefSectionProps: AsyncSelectProps<IValueSet> = {
8982
id: 'unicefSection',
9083
name: unicefSection,
9184
label: t('UNICEF section'),
92-
dataLoader: groupQuery as any,
93-
optionsGetter: getValuesetSelectOptions as any,
85+
optionsGetter: getValuesetSelectOptions,
86+
selectProps: {
87+
placeholder: t('Select section'),
88+
showSearch: true,
89+
filterOption: groupSelectfilterFunction as SelectProps<SelectOption[]>['filterOption'],
90+
},
91+
useQueryParams: {
92+
key: [valuesetResourceType, UNICEF_SECTION_ENDPOINT],
93+
queryFn: async () =>
94+
new FHIRServiceClass<IValueSet>(fhirBaseURL, valuesetResourceType).read(
95+
UNICEF_SECTION_ENDPOINT as string
96+
),
97+
},
98+
};
99+
100+
const donorSelectProps: AsyncSelectProps<IValueSet> = {
101+
id: 'donor',
102+
name: donor,
103+
label: t('Donor'),
104+
optionsGetter: getValuesetSelectOptions,
105+
selectProps: {
106+
placeholder: t('Select donor'),
107+
showSearch: true,
108+
filterOption: groupSelectfilterFunction as SelectProps<SelectOption[]>['filterOption'],
109+
},
110+
useQueryParams: {
111+
key: [valuesetResourceType, UNICEF_SECTION_ENDPOINT],
112+
queryFn: async () =>
113+
new FHIRServiceClass<IValueSet>(fhirBaseURL, valuesetResourceType).read(
114+
UNICEF_SECTION_ENDPOINT as string
115+
),
116+
},
117+
};
118+
119+
const projectsSelectProps: AsyncSelectProps<IBundle> = {
120+
id: 'project',
121+
name: productName,
122+
label: t('Product name'),
123+
optionsGetter: projectOptions,
94124
selectProps: {
95125
placeholder: t('Select product'),
96126
showSearch: true,
97127
filterOption: groupSelectfilterFunction as SelectProps<SelectOption[]>['filterOption'],
98128
},
129+
useQueryParams: {
130+
key: [groupResourceType],
131+
queryFn: async () =>
132+
new FHIRServiceClass<IBundle>(fhirBaseURL, groupResourceType).list(productQueryFilters),
133+
},
99134
};
100135

101136
return (
@@ -107,14 +142,16 @@ const AddLocationInventoryForm = (props: LocationInventoryFormProps) => {
107142
}}
108143
initialValues={initialValues}
109144
>
110-
<FormItem id="productName" name={productName} label={t('Product name')}>
145+
{/* <FormItem id="productName" name={productName} label={t('Product name')}>
111146
<Select
112147
placeholder={t('Select product')}
113148
options={projectOptions}
114149
showSearch={true}
115150
filterOption={groupSelectfilterFunction as SelectProps<SelectOption[]>['filterOption']}
116151
/>
117-
</FormItem>
152+
</FormItem> */}
153+
154+
<AsyncSelect {...projectsSelectProps} />
118155

119156
<FormItem id="quantity" name={quantity} label={t('Quantity (Optional)')}>
120157
<Input required={false} type="number" />
@@ -142,9 +179,7 @@ const AddLocationInventoryForm = (props: LocationInventoryFormProps) => {
142179
<Input type="number" />
143180
</FormItem>
144181

145-
<FormItem id="donor" name={donor} label={t('Donor')}>
146-
<Input />
147-
</FormItem>
182+
<AsyncSelect {...donorSelectProps} />
148183

149184
<FormItem id="poNumber" name={PONumber} label={t('PO number')}>
150185
<Input type="number" />

packages/fhir-group-management/src/components/LocationInventory/index.tsx

+1-22
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
import React from 'react';
22
import { useTranslation } from '../../mls';
33
import { Helmet } from 'react-helmet';
4-
import { PageHeader, BrokenPage, getResourcesFromBundle } from '@opensrp/react-utils';
4+
import { PageHeader } from '@opensrp/react-utils';
55
import { AddLocationInventoryForm } from './form';
6-
import { groupResourceType } from '../../constants';
7-
import { supplyMgSnomedCode, snomedCodeSystem } from '../../helpers/utils';
8-
import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup';
9-
import { FHIRServiceClass } from '@opensrp/react-utils';
10-
import { useQuery } from 'react-query';
11-
import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle';
126

137
interface AddLocationInventoryProps {
148
fhirBaseURL: string;
159
}
1610

17-
const extraQueryFilters = {
18-
code: `${snomedCodeSystem}|${supplyMgSnomedCode}`,
19-
};
20-
2111
/**
2212
* component to add location inventory
2313
*
@@ -29,19 +19,8 @@ export const AddLocationInventory = (props: AddLocationInventoryProps) => {
2919
const { t } = useTranslation();
3020
const pageTitle = t('Add locations Inventory');
3121

32-
const productQuery = useQuery([groupResourceType], async () =>
33-
new FHIRServiceClass<IBundle>(fhirBaseURL, groupResourceType).list(extraQueryFilters)
34-
);
35-
36-
if (productQuery.error && !productQuery.data && !productQuery.isLoading) {
37-
return <BrokenPage errorMessage={(productQuery.error as Error).message} />;
38-
}
39-
40-
const products = productQuery.data ? getResourcesFromBundle<IGroup>(productQuery.data) : [];
41-
4222
const formProps = {
4323
fhirBaseURL,
44-
products,
4524
};
4625

4726
return (
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,48 @@
11
import { ValueSetContains } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/valueSetContains';
22
import { IValueSet } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IValueSet';
33
import { DefaultOptionType } from 'antd/lib/select';
4+
import { Dictionary } from '@onaio/utils';
5+
import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup';
6+
import { getResourcesFromBundle } from '@opensrp/react-utils';
7+
import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle';
48

59
/**
6-
* @param data
10+
* get options from valueset data
11+
*
12+
* @param data - valueset data
13+
* @returns returns select options
714
*/
815
export function getValuesetSelectOptions<TData extends IValueSet>(data: TData) {
9-
const valuesets: ValueSetContains[] = data.expansion?.contains?.map((items) => items) || [];
16+
const valuesetsByCode: Dictionary<ValueSetContains> = {};
17+
data.compose?.include.forEach((item) => {
18+
item.concept?.forEach((record) => {
19+
const code = record.code as string;
20+
valuesetsByCode[code] = { ...record, system: item.system };
21+
});
22+
});
23+
data.expansion?.contains?.forEach((item) => {
24+
const code = item.code as string;
25+
valuesetsByCode[code] = { ...item };
26+
});
27+
const valuesets = Object.values(valuesetsByCode);
1028
const options: DefaultOptionType[] = valuesets.map((record) => ({
11-
value: record.code,
29+
value: JSON.stringify({ code: record.code, display: record.display, system: record.system }),
1230
label: record.display,
1331
}));
1432
return options;
1533
}
34+
35+
/**
36+
* get options from products bundle data
37+
*
38+
* @param data - products bundle data
39+
* @returns returns select options
40+
*/
41+
export const projectOptions = (data: IBundle) => {
42+
const productsList = getResourcesFromBundle<IGroup>(data);
43+
const options: DefaultOptionType[] = productsList.map((prod: IGroup) => ({
44+
value: prod.id,
45+
label: prod.name,
46+
}));
47+
return options;
48+
};

packages/fhir-group-management/src/constants.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ export const LIST_GROUP_URL = '/groups/list';
55
export const LIST_COMMODITY_URL = '/commodity/list';
66
export const ADD_LOCATION_INVENTORY = '/location/inventory';
77

8+
// unicef and donor endpoints
9+
export const UNICEF_SECTION_ENDPOINT = '2826/$expand';
10+
export const DONOR_SECTION_ENDPOINT = '2826/$expand';
11+
812
// magic strings
913
export const groupResourceType = 'Group';
1014
export const listResourceType = 'List';
1115
export const binaryResourceType = 'Binary';
16+
export const valuesetResourceType = 'ValueSet';
1217

1318
// product form constants
1419
export const id = 'id' as const;
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,55 @@
11
import React, { useMemo } from 'react';
2-
import { Form, Select, Input } from 'antd';
3-
import { UseQueryResult } from 'react-query';
2+
import { Form, Select } from 'antd';
3+
import { QueryFunction, QueryKey, UseQueryOptions, useQuery } from 'react-query';
44
import { SelectProps, DefaultOptionType } from 'antd/lib/select';
5+
import { sendErrorNotification } from '@opensrp/notifications';
6+
import { useTranslation } from '../../mls';
57

68
const { Item: FormItem } = Form;
79

8-
export interface AsyncSelectProps {
9-
dataLoader: <TData>() => UseQueryResult<TData>;
10-
optionsGetter: <TData>(data: TData) => DefaultOptionType[];
10+
export interface AsyncSelectProps<TData> {
11+
optionsGetter: (data: TData) => DefaultOptionType[];
1112
name: string | string[];
1213
label: string;
1314
selectProps: SelectProps;
1415
id: string;
16+
useQueryParams: {
17+
key: QueryKey;
18+
queryFn: QueryFunction<TData>;
19+
options?: UseQueryOptions<TData>;
20+
};
1521
}
1622

17-
export const AsyncSelect = (props: AsyncSelectProps) => {
18-
const { dataLoader, optionsGetter, selectProps, name, label } = props;
19-
const { data, isLoading, error } = dataLoader();
20-
// Todo: Handle error
23+
/**
24+
* Renders data in async for select coponent
25+
*
26+
* @param props - AsyncSelect component props
27+
*/
28+
export function AsyncSelect<TData>(props: AsyncSelectProps<TData>) {
29+
const { optionsGetter, selectProps, name, label, useQueryParams } = props;
30+
31+
const { t } = useTranslation();
32+
33+
const { data, isLoading, error } = useQuery(
34+
useQueryParams.key,
35+
useQueryParams.queryFn,
36+
useQueryParams.options
37+
);
38+
39+
if (error) {
40+
sendErrorNotification(t(`Failed to get data for ${label}`));
41+
}
2142
const options = useMemo(() => (data ? optionsGetter(data) : undefined), [data]);
2243
const singleSelectProps = {
2344
...selectProps,
2445
options,
2546
loading: isLoading,
47+
disabled: Boolean(error),
2648
};
2749

2850
return (
2951
<FormItem name={name} label={label}>
3052
<Select {...singleSelectProps} />
3153
</FormItem>
3254
);
33-
};
55+
}

0 commit comments

Comments
 (0)