Skip to content

Commit ab085ed

Browse files
samsharafrozenhelium
authored andcommitted
feat: add organization type and learning type filter in operational learning
1 parent 1f7204a commit ab085ed

File tree

4 files changed

+114
-26
lines changed

4 files changed

+114
-26
lines changed

.changeset/slimy-eggs-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"go-web-app": patch
3+
---
4+
5+
Add Organization type and Learning type filter in Operational learning

app/src/views/OperationalLearning/Filters/i18n.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"filterOpsLearningSearchLabel": "Search",
1515
"filterOpsLearningSearchPlaceholder": "Search",
1616
"appealStartDate": "Appeal Start Date",
17-
"appealEndDate": "Appeal End Date"
17+
"appealEndDate": "Appeal End Date",
18+
"organizationTypesLabel": "Organization Type",
19+
"organizationTypesLabelPlaceholder": "All Organization Types",
20+
"perLearningTypesLabel": "Learning Type",
21+
"perLearningTypesLabelPlaceholder": "All Learning Type"
1822
}
1923
}

app/src/views/OperationalLearning/Filters/index.tsx

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,41 @@ import {
66
TextInput,
77
} from '@ifrc-go/ui';
88
import { useTranslation } from '@ifrc-go/ui/hooks';
9+
import {
10+
numericIdSelector,
11+
numericKeySelector,
12+
stringLabelSelector,
13+
stringTitleSelector,
14+
stringValueSelector,
15+
} from '@ifrc-go/ui/utils';
916
import { EntriesAsList } from '@togglecorp/toggle-form';
1017

1118
import CountryMultiSelectInput, { type CountryOption } from '#components/domain/CountryMultiSelectInput';
1219
import RegionSelectInput, { type RegionOption } from '#components/domain/RegionSelectInput';
20+
import { type components } from '#generated/types';
1321
import { DisasterType } from '#hooks/domain/useDisasterType';
1422
import { type PerComponent } from '#hooks/domain/usePerComponent';
1523
import { type SecondarySector } from '#hooks/domain/useSecondarySector';
1624
import { getFormattedComponentName } from '#utils/domain/per';
25+
import { type GoApiResponse } from '#utils/restRequest';
1726

1827
import i18n from './i18n.json';
1928

20-
const sectorKeySelector = (d: SecondarySector) => d.key;
21-
const sectorLabelSelector = (d: SecondarySector) => d.label;
22-
const perComponentKeySelector = (option: PerComponent) => option.id;
23-
const disasterTypeKeySelector = (type: DisasterType) => type.id;
24-
const disasterTypeLabelSelector = (type: DisasterType) => type.name ?? '?';
29+
type OpsLearningOrganizationType = NonNullable<GoApiResponse<'/api/v2/ops-learning/organization-type/'>['results']>[number];
30+
export type PerLearningType = components<'read'>['schemas']['PerLearningTypeEnum'];
31+
32+
const disasterTypeLabelSelector = (disasterType: DisasterType) => disasterType.name ?? '?';
33+
34+
const perLearningTypeKeySelector = (perLearningType: PerLearningType) => perLearningType.key;
2535

2636
export type FilterValue = Partial<{
2737
region: RegionOption['key'],
2838
countries: CountryOption['id'][],
2939
disasterTypes: DisasterType['id'][],
3040
secondarySectors: SecondarySector['key'][],
3141
perComponents: PerComponent['id'][],
42+
organizationTypes: OpsLearningOrganizationType['id'][],
43+
perLearningTypes: PerLearningType['key'][],
3244
appealStartDateAfter: string,
3345
appealStartDateBefore: string,
3446
appealSearchText: string;
@@ -40,15 +52,22 @@ interface Props {
4052
disasterTypeOptions: DisasterType[] | undefined;
4153
secondarySectorOptions: SecondarySector[] | undefined;
4254
perComponentOptions: PerComponent[] | undefined;
55+
organizationTypeOptions: OpsLearningOrganizationType[] | undefined,
56+
organizationTypePending: boolean;
57+
perLearningTypeOptions: PerLearningType[] | undefined;
4358
disabled?: boolean;
4459
}
60+
4561
function Filters(props: Props) {
4662
const {
4763
value,
4864
onChange,
4965
disasterTypeOptions,
5066
secondarySectorOptions,
5167
perComponentOptions,
68+
organizationTypeOptions,
69+
organizationTypePending,
70+
perLearningTypeOptions,
5271
disabled,
5372
} = props;
5473

@@ -90,7 +109,7 @@ function Filters(props: Props) {
90109
label={strings.filterDisasterTypeLabel}
91110
placeholder={strings.filterDisasterTypePlaceholder}
92111
options={disasterTypeOptions}
93-
keySelector={disasterTypeKeySelector}
112+
keySelector={numericIdSelector}
94113
labelSelector={disasterTypeLabelSelector}
95114
value={value.disasterTypes}
96115
onChange={onChange}
@@ -101,8 +120,8 @@ function Filters(props: Props) {
101120
label={strings.filterSectorLabel}
102121
placeholder={strings.filterSectorPlaceholder}
103122
options={secondarySectorOptions}
104-
keySelector={sectorKeySelector}
105-
labelSelector={sectorLabelSelector}
123+
keySelector={numericKeySelector}
124+
labelSelector={stringLabelSelector}
106125
disabled={disabled}
107126
value={value.secondarySectors}
108127
onChange={onChange}
@@ -113,13 +132,37 @@ function Filters(props: Props) {
113132
label={strings.filterComponentLabel}
114133
placeholder={strings.filterComponentPlaceholder}
115134
options={perComponentOptions}
116-
keySelector={perComponentKeySelector}
135+
keySelector={numericIdSelector}
117136
labelSelector={getFormattedComponentName}
118137
disabled={disabled}
119138
value={value.perComponents}
120139
onChange={onChange}
121140
withSelectAll
122141
/>
142+
<MultiSelectInput
143+
name="organizationTypes"
144+
label={strings.organizationTypesLabel}
145+
placeholder={strings.organizationTypesLabelPlaceholder}
146+
options={organizationTypeOptions}
147+
keySelector={numericIdSelector}
148+
labelSelector={stringTitleSelector}
149+
disabled={disabled || organizationTypePending}
150+
value={value.organizationTypes}
151+
onChange={onChange}
152+
withSelectAll
153+
/>
154+
<MultiSelectInput
155+
name="perLearningTypes"
156+
label={strings.perLearningTypesLabel}
157+
placeholder={strings.perLearningTypesLabelPlaceholder}
158+
options={perLearningTypeOptions}
159+
keySelector={perLearningTypeKeySelector}
160+
labelSelector={stringValueSelector}
161+
disabled={disabled}
162+
value={value.perLearningTypes}
163+
onChange={onChange}
164+
withSelectAll
165+
/>
123166
<DateInput
124167
name="appealStartDateAfter"
125168
label={strings.appealStartDate}

app/src/views/OperationalLearning/index.tsx

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import { useTranslation } from '@ifrc-go/ui/hooks';
2222
import {
2323
hasSomeDefinedValue,
2424
numericIdSelector,
25+
numericKeySelector,
2526
resolveToComponent,
2627
resolveToString,
28+
stringLabelSelector,
2729
stringNameSelector,
30+
stringTitleSelector,
2831
stringValueSelector,
2932
} from '@ifrc-go/ui/utils';
3033
import {
@@ -41,11 +44,11 @@ import { type RegionOption } from '#components/domain/RegionSelectInput';
4144
import Link from '#components/Link';
4245
import Page from '#components/Page';
4346
import { type components } from '#generated/types';
44-
import useCountry, { Country } from '#hooks/domain/useCountry';
47+
import useCountry from '#hooks/domain/useCountry';
4548
import useDisasterTypes, { DisasterType } from '#hooks/domain/useDisasterType';
4649
import useGlobalEnums from '#hooks/domain/useGlobalEnums';
47-
import usePerComponent, { type PerComponent } from '#hooks/domain/usePerComponent';
48-
import useSecondarySector, { type SecondarySector } from '#hooks/domain/useSecondarySector';
50+
import usePerComponent from '#hooks/domain/usePerComponent';
51+
import useSecondarySector from '#hooks/domain/useSecondarySector';
4952
import useAlert from '#hooks/useAlert';
5053
import useFilterState from '#hooks/useFilterState';
5154
import useRecursiveCsvExport from '#hooks/useRecursiveCsvRequest';
@@ -56,7 +59,10 @@ import {
5659
useRequest,
5760
} from '#utils/restRequest';
5861

59-
import Filters, { type FilterValue } from './Filters';
62+
import Filters, {
63+
type FilterValue,
64+
type PerLearningType,
65+
} from './Filters';
6066
import KeyInsights from './KeyInsights';
6167
import Summary, { type Props as SummaryProps } from './Summary';
6268

@@ -89,12 +95,8 @@ type QueryType = Pick<
8995
>;
9096

9197
const regionKeySelector = (region: RegionOption) => region.key;
92-
const countryKeySelector = (country: Country) => country.id;
93-
const sectorKeySelector = (d: SecondarySector) => d.key;
94-
const sectorLabelSelector = (d: SecondarySector) => d.label;
95-
const perComponentKeySelector = (option: PerComponent) => option.id;
96-
const disasterTypeKeySelector = (type: DisasterType) => type.id;
9798
const disasterTypeLabelSelector = (type: DisasterType) => type.name ?? '?';
99+
const perLearningTypeKeySelector = (perLearningType: PerLearningType) => perLearningType.key;
98100

99101
/** @knipignore */
100102
// eslint-disable-next-line import/prefer-default-export
@@ -123,7 +125,10 @@ export function Component() {
123125

124126
const alert = useAlert();
125127

126-
const { api_region_name: regionList } = useGlobalEnums();
128+
const {
129+
api_region_name: regionList,
130+
per_learning_type: perLearningTypeOptions,
131+
} = useGlobalEnums();
127132
const countryList = useCountry({ region: rawFilter.region });
128133
const disasterTypeOptions = useDisasterTypes();
129134
const secondarySectorOptions = useSecondarySector();
@@ -213,8 +218,16 @@ export function Component() {
213218
preserveResponse: true,
214219
});
215220

221+
const {
222+
pending: opsLearningOrganizationTypePending,
223+
response: opsLearningOrganizationTypes,
224+
} = useRequest({
225+
url: '/api/v2/ops-learning/organization-type/',
226+
preserveResponse: true,
227+
});
228+
216229
const [
217-
pendingExport,,
230+
pendingExport, ,
218231
triggerExportStart,
219232
] = useRecursiveCsvExport({
220233
disableProgress: true,
@@ -270,6 +283,10 @@ export function Component() {
270283
? filter.perComponents : undefined,
271284
search_extracts: isTruthyString(filter.appealSearchText)
272285
? (filter.appealSearchText) : undefined,
286+
type_validated__in: hasSomeDefinedValue(filter.perLearningTypes)
287+
? filter.perLearningTypes : undefined,
288+
organization_validated__in: hasSomeDefinedValue(filter.organizationTypes)
289+
? filter.organizationTypes : undefined,
273290
};
274291
setFilterPristine(true);
275292
setQuery(newQuery);
@@ -333,6 +350,9 @@ export function Component() {
333350
disasterTypeOptions={disasterTypeOptions}
334351
secondarySectorOptions={secondarySectorOptions}
335352
perComponentOptions={perComponentOptions}
353+
organizationTypeOptions={opsLearningOrganizationTypes?.results}
354+
perLearningTypeOptions={perLearningTypeOptions}
355+
organizationTypePending={opsLearningOrganizationTypePending}
336356
/>
337357
)}
338358
footerContent={(
@@ -361,31 +381,47 @@ export function Component() {
361381
value={rawFilter.countries}
362382
options={countryList}
363383
labelSelector={stringNameSelector}
364-
keySelector={countryKeySelector}
384+
keySelector={numericIdSelector}
365385
/>
366386
<DismissableMultiListOutput
367387
name="disasterTypes"
368388
onDismiss={onFilterChange}
369389
value={rawFilter.disasterTypes}
370390
options={disasterTypeOptions}
371391
labelSelector={disasterTypeLabelSelector}
372-
keySelector={disasterTypeKeySelector}
392+
keySelector={numericIdSelector}
373393
/>
374394
<DismissableMultiListOutput
375395
name="secondarySectors"
376396
onDismiss={onFilterChange}
377397
value={rawFilter.secondarySectors}
378398
options={secondarySectorOptions}
379-
labelSelector={sectorLabelSelector}
380-
keySelector={sectorKeySelector}
399+
labelSelector={stringLabelSelector}
400+
keySelector={numericKeySelector}
381401
/>
382402
<DismissableMultiListOutput
383403
name="perComponents"
384404
onDismiss={onFilterChange}
385405
value={rawFilter.perComponents}
386406
options={perComponentOptions}
387407
labelSelector={getFormattedComponentName}
388-
keySelector={perComponentKeySelector}
408+
keySelector={numericIdSelector}
409+
/>
410+
<DismissableMultiListOutput
411+
name="organizationTypes"
412+
onDismiss={onFilterChange}
413+
value={rawFilter.organizationTypes}
414+
options={opsLearningOrganizationTypes?.results}
415+
labelSelector={stringTitleSelector}
416+
keySelector={numericIdSelector}
417+
/>
418+
<DismissableMultiListOutput
419+
name="perLearningTypes"
420+
onDismiss={onFilterChange}
421+
value={rawFilter.perLearningTypes}
422+
options={perLearningTypeOptions}
423+
labelSelector={stringValueSelector}
424+
keySelector={perLearningTypeKeySelector}
389425
/>
390426
<DismissableTextOutput
391427
name="appealStartDateAfter"

0 commit comments

Comments
 (0)