Skip to content

Commit

Permalink
feat: add organization type and learning type filter in operational l…
Browse files Browse the repository at this point in the history
…earning
  • Loading branch information
samshara committed Nov 26, 2024
1 parent 8cdc946 commit 8485076
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-eggs-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"go-web-app": patch
---

Add Organization type and Learning type filter in Operational learning
6 changes: 5 additions & 1 deletion app/src/views/OperationalLearning/Filters/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
"filterOpsLearningSearchLabel": "Search",
"filterOpsLearningSearchPlaceholder": "Search",
"appealStartDate": "Appeal Start Date",
"appealEndDate": "Appeal End Date"
"appealEndDate": "Appeal End Date",
"organizationTypesLabel": "Organization Type",
"organizationTypesLabelPlaceholder": "All Organization Types",
"perLearningTypesLabel": "Learning Type",
"perLearningTypesLabelPlaceholder": "All Learning Type"
}
}
61 changes: 52 additions & 9 deletions app/src/views/OperationalLearning/Filters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,41 @@ import {
TextInput,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';
import {
numericIdSelector,
numericKeySelector,
stringLabelSelector,
stringTitleSelector,
stringValueSelector,
} from '@ifrc-go/ui/utils';
import { EntriesAsList } from '@togglecorp/toggle-form';

import CountryMultiSelectInput, { type CountryOption } from '#components/domain/CountryMultiSelectInput';
import RegionSelectInput, { type RegionOption } from '#components/domain/RegionSelectInput';
import { type components } from '#generated/types';
import { DisasterType } from '#hooks/domain/useDisasterType';
import { type PerComponent } from '#hooks/domain/usePerComponent';
import { type SecondarySector } from '#hooks/domain/useSecondarySector';
import { getFormattedComponentName } from '#utils/domain/per';
import { type GoApiResponse } from '#utils/restRequest';

import i18n from './i18n.json';

const sectorKeySelector = (d: SecondarySector) => d.key;
const sectorLabelSelector = (d: SecondarySector) => d.label;
const perComponentKeySelector = (option: PerComponent) => option.id;
const disasterTypeKeySelector = (type: DisasterType) => type.id;
const disasterTypeLabelSelector = (type: DisasterType) => type.name ?? '?';
type OpsLearningOrganizationType = NonNullable<GoApiResponse<'/api/v2/ops-learning/organization-type/'>['results']>[number];
export type PerLearningType = components<'read'>['schemas']['PerLearningTypeEnum'];

const disasterTypeLabelSelector = (disasterType: DisasterType) => disasterType.name ?? '?';

const perLearningTypeKeySelector = (perLearningType: PerLearningType) => perLearningType.key;

export type FilterValue = Partial<{
region: RegionOption['key'],
countries: CountryOption['id'][],
disasterTypes: DisasterType['id'][],
secondarySectors: SecondarySector['key'][],
perComponents: PerComponent['id'][],
organizationTypes: OpsLearningOrganizationType['id'][],
perLearningTypes: PerLearningType['key'][],
appealStartDateAfter: string,
appealStartDateBefore: string,
appealSearchText: string;
Expand All @@ -44,15 +56,22 @@ interface Props {
disasterTypeOptions: DisasterType[] | undefined;
secondarySectorOptions: SecondarySector[] | undefined;
perComponentOptions: PerComponent[] | undefined;
organizationTypeOptions: OpsLearningOrganizationType[] | undefined,
organizationTypePending: boolean;
perLearningTypeOptions: PerLearningType[] | undefined;
disabled?: boolean;
}

function Filters(props: Props) {
const {
value,
onChange,
disasterTypeOptions,
secondarySectorOptions,
perComponentOptions,
organizationTypeOptions,
organizationTypePending,
perLearningTypeOptions,
disabled,
} = props;

Expand Down Expand Up @@ -94,7 +113,7 @@ function Filters(props: Props) {
label={strings.filterDisasterTypeLabel}
placeholder={strings.filterDisasterTypePlaceholder}
options={disasterTypeOptions}
keySelector={disasterTypeKeySelector}
keySelector={numericIdSelector}
labelSelector={disasterTypeLabelSelector}
value={value.disasterTypes}
onChange={onChange}
Expand All @@ -105,8 +124,8 @@ function Filters(props: Props) {
label={strings.filterSectorLabel}
placeholder={strings.filterSectorPlaceholder}
options={secondarySectorOptions}
keySelector={sectorKeySelector}
labelSelector={sectorLabelSelector}
keySelector={numericKeySelector}
labelSelector={stringLabelSelector}
disabled={disabled}
value={value.secondarySectors}
onChange={onChange}
Expand All @@ -117,13 +136,37 @@ function Filters(props: Props) {
label={strings.filterComponentLabel}
placeholder={strings.filterComponentPlaceholder}
options={perComponentOptions}
keySelector={perComponentKeySelector}
keySelector={numericIdSelector}
labelSelector={getFormattedComponentName}
disabled={disabled}
value={value.perComponents}
onChange={onChange}
withSelectAll
/>
<MultiSelectInput
name="organizationTypes"
label={strings.organizationTypesLabel}
placeholder={strings.organizationTypesLabelPlaceholder}
options={organizationTypeOptions}
keySelector={numericIdSelector}
labelSelector={stringTitleSelector}
disabled={disabled || organizationTypePending}
value={value.organizationTypes}
onChange={onChange}
withSelectAll
/>
<MultiSelectInput
name="perLearningTypes"
label={strings.perLearningTypesLabel}
placeholder={strings.perLearningTypesLabelPlaceholder}
options={perLearningTypeOptions}
keySelector={perLearningTypeKeySelector}
labelSelector={stringValueSelector}
disabled={disabled}
value={value.perLearningTypes}
onChange={onChange}
withSelectAll
/>
<DateInput
name="appealStartDateAfter"
label={strings.appealStartDate}
Expand Down
68 changes: 52 additions & 16 deletions app/src/views/OperationalLearning/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import { useTranslation } from '@ifrc-go/ui/hooks';
import {
hasSomeDefinedValue,
numericIdSelector,
numericKeySelector,
resolveToComponent,
resolveToString,
stringLabelSelector,
stringNameSelector,
stringTitleSelector,
stringValueSelector,
} from '@ifrc-go/ui/utils';
import {
Expand All @@ -41,11 +44,11 @@ import { type RegionOption } from '#components/domain/RegionSelectInput';
import Link from '#components/Link';
import Page from '#components/Page';
import { type components } from '#generated/types';
import useCountry, { Country } from '#hooks/domain/useCountry';
import useCountry from '#hooks/domain/useCountry';
import useDisasterTypes, { DisasterType } from '#hooks/domain/useDisasterType';
import useGlobalEnums from '#hooks/domain/useGlobalEnums';
import usePerComponent, { type PerComponent } from '#hooks/domain/usePerComponent';
import useSecondarySector, { type SecondarySector } from '#hooks/domain/useSecondarySector';
import usePerComponent from '#hooks/domain/usePerComponent';
import useSecondarySector from '#hooks/domain/useSecondarySector';
import useAlert from '#hooks/useAlert';
import useFilterState from '#hooks/useFilterState';
import useRecursiveCsvExport from '#hooks/useRecursiveCsvRequest';
Expand All @@ -56,7 +59,10 @@ import {
useRequest,
} from '#utils/restRequest';

import Filters, { type FilterValue } from './Filters';
import Filters, {
type FilterValue,
type PerLearningType,
} from './Filters';
import KeyInsights from './KeyInsights';
import Summary, { type Props as SummaryProps } from './Summary';

Expand Down Expand Up @@ -89,12 +95,8 @@ type QueryType = Pick<
>;

const regionKeySelector = (region: RegionOption) => region.key;
const countryKeySelector = (country: Country) => country.id;
const sectorKeySelector = (d: SecondarySector) => d.key;
const sectorLabelSelector = (d: SecondarySector) => d.label;
const perComponentKeySelector = (option: PerComponent) => option.id;
const disasterTypeKeySelector = (type: DisasterType) => type.id;
const disasterTypeLabelSelector = (type: DisasterType) => type.name ?? '?';
const perLearningTypeKeySelector = (perLearningType: PerLearningType) => perLearningType.key;

// eslint-disable-next-line import/prefer-default-export
export function Component() {
Expand Down Expand Up @@ -122,7 +124,10 @@ export function Component() {

const alert = useAlert();

const { api_region_name: regionList } = useGlobalEnums();
const {
api_region_name: regionList,
per_learning_type: perLearningTypeOptions,
} = useGlobalEnums();
const countryList = useCountry({ region: rawFilter.region });
const disasterTypeOptions = useDisasterTypes();
const secondarySectorOptions = useSecondarySector();
Expand Down Expand Up @@ -212,8 +217,16 @@ export function Component() {
preserveResponse: true,
});

const {
pending: opsLearningOrganizationTypePending,
response: opsLearningOrganizationTypes,
} = useRequest({
url: '/api/v2/ops-learning/organization-type/',
preserveResponse: true,
});

const [
pendingExport,,
pendingExport, ,
triggerExportStart,
] = useRecursiveCsvExport({
disableProgress: true,
Expand Down Expand Up @@ -269,6 +282,10 @@ export function Component() {
? filter.perComponents : undefined,
search_extracts: isTruthyString(filter.appealSearchText)
? (filter.appealSearchText) : undefined,
type_validated__in: hasSomeDefinedValue(filter.perLearningTypes)
? filter.perLearningTypes : undefined,
organization_validated__in: hasSomeDefinedValue(filter.organizationTypes)
? filter.organizationTypes : undefined,
};
setFilterPristine(true);
setQuery(newQuery);
Expand Down Expand Up @@ -332,6 +349,9 @@ export function Component() {
disasterTypeOptions={disasterTypeOptions}
secondarySectorOptions={secondarySectorOptions}
perComponentOptions={perComponentOptions}
organizationTypeOptions={opsLearningOrganizationTypes?.results}
perLearningTypeOptions={perLearningTypeOptions}
organizationTypePending={opsLearningOrganizationTypePending}
/>
)}
footerContent={(
Expand Down Expand Up @@ -360,31 +380,47 @@ export function Component() {
value={rawFilter.countries}
options={countryList}
labelSelector={stringNameSelector}
keySelector={countryKeySelector}
keySelector={numericIdSelector}
/>
<DismissableMultiListOutput
name="disasterTypes"
onDismiss={onFilterChange}
value={rawFilter.disasterTypes}
options={disasterTypeOptions}
labelSelector={disasterTypeLabelSelector}
keySelector={disasterTypeKeySelector}
keySelector={numericIdSelector}
/>
<DismissableMultiListOutput
name="secondarySectors"
onDismiss={onFilterChange}
value={rawFilter.secondarySectors}
options={secondarySectorOptions}
labelSelector={sectorLabelSelector}
keySelector={sectorKeySelector}
labelSelector={stringLabelSelector}
keySelector={numericKeySelector}
/>
<DismissableMultiListOutput
name="perComponents"
onDismiss={onFilterChange}
value={rawFilter.perComponents}
options={perComponentOptions}
labelSelector={getFormattedComponentName}
keySelector={perComponentKeySelector}
keySelector={numericIdSelector}
/>
<DismissableMultiListOutput
name="organizationTypes"
onDismiss={onFilterChange}
value={rawFilter.organizationTypes}
options={opsLearningOrganizationTypes?.results}
labelSelector={stringTitleSelector}
keySelector={numericIdSelector}
/>
<DismissableMultiListOutput
name="perLearningTypes"
onDismiss={onFilterChange}
value={rawFilter.perLearningTypes}
options={perLearningTypeOptions}
labelSelector={stringValueSelector}
keySelector={perLearningTypeKeySelector}
/>
<DismissableTextOutput
name="appealStartDateAfter"
Expand Down

0 comments on commit 8485076

Please sign in to comment.