From 73ab0fe54cfe54867d6524d0dc23366b633488f3 Mon Sep 17 00:00:00 2001 From: Patryk Dabrowski Date: Tue, 7 May 2024 12:27:53 +0200 Subject: [PATCH] Improve global program filter --- backend/.gitignore | 6 +- backend/hct_mis_api/apps/program/filters.py | 2 + .../page_object/base_components.py | 2 +- frontend/data/schema.graphql | 4 +- frontend/src/__generated__/graphql.tsx | 10 +- .../queries/program/AllProgramsForChoices.ts | 2 + .../src/apollo/queries/program/Program.ts | 1 + .../CashPlanDetails.test.tsx.snap | 3 +- .../components/core/StatusBox/StatusBox.tsx | 13 +- .../GrievancesDetails.test.tsx.snap | 9 +- .../EditPaymentPlanHeader.test.tsx.snap | 3 +- .../ProgramDetails.test.tsx.snap | 3 +- .../src/components/programs/constants.tsx | 2 - .../RegistrationDetails.test.tsx.snap | 3 +- .../src/containers/GlobalProgramSelect.tsx | 425 +++++++++++++----- .../ProgrammesTable.test.tsx.snap | 3 +- .../ReportingTable.test.tsx.snap | 3 +- .../PaymentRecordTable.test.tsx.snap | 3 +- .../VerificationRecordsTable.test.tsx.snap | 30 +- ...tionDataImportForPeopleTable.test.tsx.snap | 3 +- .../RegistrationDataImportTable.test.tsx.snap | 3 +- .../TargetPopulationTable.test.tsx.snap | 12 +- frontend/yarn.lock | 2 +- 23 files changed, 374 insertions(+), 173 deletions(-) diff --git a/backend/.gitignore b/backend/.gitignore index cac4aa2844..4038fbf07d 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -3,4 +3,8 @@ Makefile *.isorted test_times.txt screenshot -report \ No newline at end of file +pytest_screenshots +report +archive +output.json +pytest_html_report.html \ No newline at end of file diff --git a/backend/hct_mis_api/apps/program/filters.py b/backend/hct_mis_api/apps/program/filters.py index 7543342394..2cc992a3dd 100644 --- a/backend/hct_mis_api/apps/program/filters.py +++ b/backend/hct_mis_api/apps/program/filters.py @@ -24,6 +24,7 @@ class ProgramFilter(FilterSet): start_date = DateFilter(field_name="start_date", lookup_expr="gte") end_date = DateFilter(field_name="end_date", lookup_expr="lte") data_collecting_type = CharFilter(field_name="data_collecting_type__code", lookup_expr="exact") + name = CharFilter(field_name="name", lookup_expr="icontains") class Meta: fields = ( @@ -35,6 +36,7 @@ class Meta: "budget", "start_date", "end_date", + "name", ) model = Program diff --git a/backend/selenium_tests/page_object/base_components.py b/backend/selenium_tests/page_object/base_components.py index a8d46b6c15..6c04175ef8 100644 --- a/backend/selenium_tests/page_object/base_components.py +++ b/backend/selenium_tests/page_object/base_components.py @@ -6,7 +6,7 @@ class BaseComponents(Common): # Labels businessAreaContainer = 'div[data-cy="business-area-container"]' globalProgramFilterContainer = 'div[data-cy="global-program-filter-container"]' - globalProgramFilter = 'div[data-cy="global-program-filter"]' + globalProgramFilter = 'button[data-cy="global-program-filter"]' menuUserProfile = 'button[data-cy="menu-user-profile"]' sideNav = 'div[data-cy="side-nav"]' navCountryDashboard = 'a[data-cy="nav-Country Dashboard"]' diff --git a/frontend/data/schema.graphql b/frontend/data/schema.graphql index 26ecefd439..8444f0d211 100644 --- a/frontend/data/schema.graphql +++ b/frontend/data/schema.graphql @@ -3713,7 +3713,7 @@ type Query { dataCollectingType(id: ID!): DataCollectingTypeNode dataCollectionTypeChoices: [DataCollectingTypeChoiceObject] program(id: ID!): ProgramNode - allPrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, orderBy: String): ProgramNodeConnection + allPrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, orderBy: String): ProgramNodeConnection chartProgrammesBySector(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode chartTotalTransferredByMonth(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode cashPlan(id: ID!): CashPlanNode @@ -3724,7 +3724,7 @@ type Query { programScopeChoices: [ChoiceObject] cashPlanStatusChoices: [ChoiceObject] dataCollectingTypeChoices: [ChoiceObject] - allActivePrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, orderBy: String): ProgramNodeConnection + allActivePrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, orderBy: String): ProgramNodeConnection targetPopulation(id: ID!): TargetPopulationNode allTargetPopulation(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, orderBy: String): TargetPopulationNodeConnection targetPopulationHouseholds(targetPopulation: ID!, offset: Int, before: String, after: String, first: Int, last: Int, orderBy: String, businessArea: String): HouseholdNodeConnection diff --git a/frontend/src/__generated__/graphql.tsx b/frontend/src/__generated__/graphql.tsx index ff8e1fe8e0..ec389de103 100644 --- a/frontend/src/__generated__/graphql.tsx +++ b/frontend/src/__generated__/graphql.tsx @@ -5887,6 +5887,7 @@ export type QueryAllActiveProgramsArgs = { endDate?: InputMaybe; first?: InputMaybe; last?: InputMaybe; + name?: InputMaybe; numberOfHouseholds?: InputMaybe; numberOfHouseholdsWithTpInProgram?: InputMaybe; offset?: InputMaybe; @@ -6368,6 +6369,7 @@ export type QueryAllProgramsArgs = { endDate?: InputMaybe; first?: InputMaybe; last?: InputMaybe; + name?: InputMaybe; numberOfHouseholds?: InputMaybe; numberOfHouseholdsWithTpInProgram?: InputMaybe; offset?: InputMaybe; @@ -10670,6 +10672,7 @@ export type AllProgramsForChoicesQueryVariables = Exact<{ startDate?: InputMaybe; endDate?: InputMaybe; orderBy?: InputMaybe; + name?: InputMaybe; }>; @@ -10680,7 +10683,7 @@ export type ProgramQueryVariables = Exact<{ }>; -export type ProgramQuery = { __typename?: 'Query', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate: any, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null } | null }; +export type ProgramQuery = { __typename?: 'Query', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate: any, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null } | null }; export type ProgrammeChoiceDataQueryVariables = Exact<{ [key: string]: never; }>; @@ -20972,7 +20975,7 @@ export type AllProgramsLazyQueryHookResult = ReturnType; export type AllProgramsQueryResult = Apollo.QueryResult; export const AllProgramsForChoicesDocument = gql` - query AllProgramsForChoices($before: String, $after: String, $first: Int, $last: Int, $status: [String], $sector: [String], $businessArea: String!, $search: String, $numberOfHouseholds: String, $budget: String, $startDate: Date, $endDate: Date, $orderBy: String) { + query AllProgramsForChoices($before: String, $after: String, $first: Int, $last: Int, $status: [String], $sector: [String], $businessArea: String!, $search: String, $numberOfHouseholds: String, $budget: String, $startDate: Date, $endDate: Date, $orderBy: String, $name: String) { allPrograms( before: $before after: $after @@ -20987,6 +20990,7 @@ export const AllProgramsForChoicesDocument = gql` orderBy: $orderBy startDate: $startDate endDate: $endDate + name: $name ) { pageInfo { hasNextPage @@ -21043,6 +21047,7 @@ export const AllProgramsForChoicesDocument = gql` * startDate: // value for 'startDate' * endDate: // value for 'endDate' * orderBy: // value for 'orderBy' + * name: // value for 'name' * }, * }); */ @@ -21094,6 +21099,7 @@ export const ProgramDocument = gql` individualFiltersAvailable householdFiltersAvailable description + type } partnerAccess partners { diff --git a/frontend/src/apollo/queries/program/AllProgramsForChoices.ts b/frontend/src/apollo/queries/program/AllProgramsForChoices.ts index 7fdde2f068..b86b7d3f1d 100644 --- a/frontend/src/apollo/queries/program/AllProgramsForChoices.ts +++ b/frontend/src/apollo/queries/program/AllProgramsForChoices.ts @@ -15,6 +15,7 @@ export const AllProgramsForChoices = gql` $startDate: Date $endDate: Date $orderBy: String + $name: String ) { allPrograms( before: $before @@ -30,6 +31,7 @@ export const AllProgramsForChoices = gql` orderBy: $orderBy startDate: $startDate endDate: $endDate + name: $name ) { pageInfo { hasNextPage diff --git a/frontend/src/apollo/queries/program/Program.ts b/frontend/src/apollo/queries/program/Program.ts index e205b5ff62..0fa53f39d8 100644 --- a/frontend/src/apollo/queries/program/Program.ts +++ b/frontend/src/apollo/queries/program/Program.ts @@ -32,6 +32,7 @@ export const PROGRAM_QUERY = gql` individualFiltersAvailable householdFiltersAvailable description + type } partnerAccess partners { diff --git a/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap b/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap index 5f9f67651e..4b21ddfa2f 100644 --- a/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap +++ b/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap @@ -48,9 +48,8 @@ exports[`components/core/CashPlanDetails should render 1`] = ` class="sc-jTQCzO eAkjXM" >
DISTRIBUTION COMPLETED
diff --git a/frontend/src/components/core/StatusBox/StatusBox.tsx b/frontend/src/components/core/StatusBox/StatusBox.tsx index e5d2364d54..2a52afbf26 100644 --- a/frontend/src/components/core/StatusBox/StatusBox.tsx +++ b/frontend/src/components/core/StatusBox/StatusBox.tsx @@ -15,16 +15,16 @@ const StatusContainer = styled.div` `; interface StatusBoxContainerProps { - status: string; + $status: string; $statusToColor: (theme: any, status: string) => string; theme: any; } const StatusBoxContainer = styled.div` - color: ${({ status, $statusToColor, theme }) => - $statusToColor(theme, status)}; - background-color: ${({ status, $statusToColor, theme }) => - `${$statusToColor(theme, status)}${opacityToHex(0.15)}`}; + color: ${({ $status, $statusToColor, theme }) => + $statusToColor(theme, $status)}; + background-color: ${({ $status, $statusToColor, theme }) => + `${$statusToColor(theme, $status)}${opacityToHex(0.15)}`}; border-radius: 16px; font-family: Roboto; font-size: 10px; @@ -47,7 +47,8 @@ export const StatusBox = ({ return ( diff --git a/frontend/src/components/grievances/GrievancesDetails/__snapshots__/GrievancesDetails.test.tsx.snap b/frontend/src/components/grievances/GrievancesDetails/__snapshots__/GrievancesDetails.test.tsx.snap index 85a86604be..21041b9f8f 100644 --- a/frontend/src/components/grievances/GrievancesDetails/__snapshots__/GrievancesDetails.test.tsx.snap +++ b/frontend/src/components/grievances/GrievancesDetails/__snapshots__/GrievancesDetails.test.tsx.snap @@ -44,9 +44,8 @@ exports[`components/grievances/GrievancesDetails should render 1`] = ` class="sc-jsEeTM xQgAl" >
In Progress
@@ -76,9 +75,8 @@ exports[`components/grievances/GrievancesDetails should render 1`] = ` class="sc-jsEeTM xQgAl" >
High
@@ -108,9 +106,8 @@ exports[`components/grievances/GrievancesDetails should render 1`] = ` class="sc-jsEeTM xQgAl" >
Very urgent
diff --git a/frontend/src/components/paymentmodule/EditPaymentPlan/EditPaymentPlanHeader/__snapshots__/EditPaymentPlanHeader.test.tsx.snap b/frontend/src/components/paymentmodule/EditPaymentPlan/EditPaymentPlanHeader/__snapshots__/EditPaymentPlanHeader.test.tsx.snap index 9cf17619f2..05d08e0289 100644 --- a/frontend/src/components/paymentmodule/EditPaymentPlan/EditPaymentPlanHeader/__snapshots__/EditPaymentPlanHeader.test.tsx.snap +++ b/frontend/src/components/paymentmodule/EditPaymentPlan/EditPaymentPlanHeader/__snapshots__/EditPaymentPlanHeader.test.tsx.snap @@ -66,9 +66,8 @@ exports[`components/paymentmodule/EditPaymentPlanHeader should render 1`] = ` class="sc-eDLKkx deAQag" >
LOCKED
diff --git a/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap b/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap index 301b4b279c..9021d3223a 100644 --- a/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap +++ b/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap @@ -41,9 +41,8 @@ exports[`components/ProgramDetails should render 1`] = ` class="sc-eDLKkx deAQag" >
FINISHED
diff --git a/frontend/src/components/programs/constants.tsx b/frontend/src/components/programs/constants.tsx index 62097469f4..b5150bf819 100644 --- a/frontend/src/components/programs/constants.tsx +++ b/frontend/src/components/programs/constants.tsx @@ -15,5 +15,3 @@ export const partnerAccessChoices = Object.entries(PartnerAccess).map( label, }), ); - -console.log(partnerAccessChoices); diff --git a/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap b/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap index 5b0f0d1824..c72a37541d 100644 --- a/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap +++ b/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap @@ -50,9 +50,8 @@ exports[`components/rdi/details/RegistrationDetails should render 1`] = ` class="sc-eDLKkx deAQag" >
MERGED
diff --git a/frontend/src/containers/GlobalProgramSelect.tsx b/frontend/src/containers/GlobalProgramSelect.tsx index 552f4e3198..b30f9d252c 100644 --- a/frontend/src/containers/GlobalProgramSelect.tsx +++ b/frontend/src/containers/GlobalProgramSelect.tsx @@ -1,18 +1,78 @@ import * as React from 'react'; -import { MenuItem, Select } from '@mui/material'; -import { useCallback, useEffect, useRef } from 'react'; -import { useNavigate } from 'react-router-dom'; -import styled from 'styled-components'; +import { styled } from '@mui/material/styles'; +import Popper from '@mui/material/Popper'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; +import ArrowDropDown from '@mui/icons-material/ArrowDropDown'; +import Autocomplete, { + autocompleteClasses, + AutocompleteCloseReason, +} from '@mui/material/Autocomplete'; +import ButtonBase from '@mui/material/ButtonBase'; +import Box from '@mui/material/Box'; import { - AllProgramsForChoicesQuery, - useAllProgramsForChoicesQuery, -} from '@generated/graphql'; -import { LoadingComponent } from '@components/core/LoadingComponent'; + CircularProgress, + IconButton, + InputAdornment, + TextField, +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import { programStatusToColor } from '@utils/utils'; +import { StatusBox } from '@core/StatusBox'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { useProgramContext } from '../programContext'; -import { isProgramNodeUuidFormat } from '@utils/utils'; +import { useNavigate } from 'react-router-dom'; +import { + ProgramStatus, + useAllProgramsForChoicesLazyQuery, + useProgramLazyQuery, +} from '@generated/graphql'; +import { KeyboardEvent, useEffect, useRef, useState } from 'react'; +import ClearIcon from '@mui/icons-material/Clear'; + +interface PopperComponentProps { + anchorEl?: any; + disablePortal?: boolean; + open: boolean; +} + +const StyledAutocompletePopper = styled('div')` + & .${autocompleteClasses.paper} { + box-shadow: none; + } + & .${autocompleteClasses.listbox} { + & .${autocompleteClasses.option} { + min-height: auto; + align-items: flex-start; + justify-content: space-between; + padding: 10px; + & .status-box-container { + margin-right: 0; + } + } + } +`; + +const PopperComponent = (props: PopperComponentProps) => { + const { disablePortal, anchorEl, open, ...other } = props; + return ; +}; + +const StyledPopper = styled(Popper)` + border-radius: 6px; + width: 300px; + z-index: ${({ theme }) => theme.zIndex.modal}; + background-color: #fff; + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +`; + +const StyledTextField = styled(TextField)` + padding: 10px; +`; -const CountrySelect = styled(Select)` +const Button = styled(ButtonBase)` && { width: ${({ theme }) => theme.spacing(58)}; background-color: rgba(104, 119, 127, 0.5); @@ -20,45 +80,68 @@ const CountrySelect = styled(Select)` border-bottom-width: 0; border-radius: 4px; height: 40px; - } - .MuiFilledInput-input { + display: flex; + justify-content: space-between; + font-family: Roboto, Helvetica, Arial, sans-serif; + font-weight: 400; + font-size: 1rem; padding: 0 10px; - background-color: transparent; - } - .MuiSelect-select:focus { - background-color: transparent; - } - .MuiSelect-icon { - color: #e3e6e7; } &&:hover { - border-bottom-width: 0; - border-radius: 4px; - } - &&:hover::before { - border-bottom-width: 0; - } - &&::before { - border-bottom-width: 0; - } - &&::after { - border-bottom-width: 0; - } - &&::after:hover { - border-bottom-width: 0; + background-color: rgba(0, 0, 0, 0.09); } `; -export function GlobalProgramSelect(): React.ReactElement { +const NameBox = styled(Box)` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 10px; +`; + +const ButtonLabel = styled('span')` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 10px; +`; + +interface ProgramRecord { + id: string; + name: string; + status: string; +} + +export const GlobalProgramSelect = () => { + const [anchorEl, setAnchorEl] = React.useState(null); const { businessArea, programId } = useBaseUrl(); const { selectedProgram, setSelectedProgram } = useProgramContext(); const navigate = useNavigate(); - const { data, loading } = useAllProgramsForChoicesQuery({ - variables: { businessArea, first: 100 }, + const [ + loadProgramsList, + { data: programsList, loading: loadingProgramsList }, + ] = useAllProgramsForChoicesLazyQuery({ + variables: { + businessArea, + first: 5, + orderBy: 'name', + status: [ProgramStatus.Active, ProgramStatus.Draft], + }, fetchPolicy: 'network-only', }); - const isMounted = useRef(false); + const [inputValue, setInputValue] = useState(''); + const [loadProgram, { data: programData, loading: loadingProgram }] = + useProgramLazyQuery({ + variables: { + id: programId, + }, + }); + const [programs, setPrograms] = useState([]); + + useEffect(() => { + void loadProgramsList(); + }, [loadProgramsList]); useEffect(() => { isMounted.current = true; @@ -67,98 +150,228 @@ export function GlobalProgramSelect(): React.ReactElement { }; }, []); - const isOneOfAvailableProgramsId = useCallback( - (id: string): boolean => - data?.allPrograms.edges.some((each) => each.node.id === id), - [data], - ); - - const getCurrentProgram = useCallback((): - | AllProgramsForChoicesQuery['allPrograms']['edges'][number]['node'] - | null => { - const obj = data?.allPrograms.edges.find((el) => el.node.id === programId); - return obj ? obj.node : null; - }, [data, programId]); + useEffect(() => { + if (programId !== 'all') { + void loadProgram(); + } + }, [programId, loadProgram]); useEffect(() => { if (programId !== 'all') { - const program = getCurrentProgram(); - if (!selectedProgram || selectedProgram?.id !== programId) { - if (program && isMounted.current) { - const { id, name, status, dataCollectingType } = - program; - - setSelectedProgram({ - id, - name, - status, - dataCollectingType: { - id: dataCollectingType?.id, - code: dataCollectingType?.code, - type: dataCollectingType?.type, - label: dataCollectingType?.label, - householdFiltersAvailable: - dataCollectingType?.householdFiltersAvailable, - individualFiltersAvailable: - dataCollectingType?.individualFiltersAvailable, - }, - }); - } + const program = programData?.program; + if ( + program && + isMounted.current && + (!selectedProgram || selectedProgram?.id !== programId) + ) { + const { id, name, status, dataCollectingType } = program; + + setSelectedProgram({ + id, + name, + status, + dataCollectingType: { + id: dataCollectingType?.id, + code: dataCollectingType?.code, + type: dataCollectingType?.type, + label: dataCollectingType?.label, + householdFiltersAvailable: + dataCollectingType?.householdFiltersAvailable, + individualFiltersAvailable: + dataCollectingType?.individualFiltersAvailable, + }, + }); } } else { setSelectedProgram(null); } - }, [programId, selectedProgram, setSelectedProgram, getCurrentProgram]); + }, [programId, selectedProgram, setSelectedProgram, programData]); useEffect(() => { // If the programId is not in a valid format or not one of the available programs, redirect to the access denied page if ( programId && - !loading && - (!isProgramNodeUuidFormat(programId) || - !isOneOfAvailableProgramsId(programId)) && - programId !== 'all' + programId !== 'all' && + !loadingProgram && + programData?.program === null ) { + setSelectedProgram(null); navigate(`/access-denied/${businessArea}`); } - }, [programId, navigate, businessArea, isOneOfAvailableProgramsId, loading]); + }, [ + programId, + navigate, + businessArea, + loadingProgram, + programData, + setSelectedProgram, + ]); - const onChange = (e): void => { - if (e.target.value === 'all') { - navigate(`/${businessArea}/programs/all/list`); - } else { - navigate( - `/${businessArea}/programs/${e.target.value}/details/${e.target.value}`, + useEffect(() => { + if (programsList?.allPrograms) { + const newProgramsList: ProgramRecord[] = []; + if (inputValue === '') { + newProgramsList.push({ + id: 'all', + name: 'All Programmes', + status: null, + }); + } + const { edges } = programsList.allPrograms; + newProgramsList.push( + ...edges.map(({ node: { id, name, status } }) => ({ + id, + name, + status, + })), ); + setPrograms(newProgramsList); + } + }, [programsList?.allPrograms]); + + const handleClose = () => { + setAnchorEl(null); + }; + + const onChange = (_event: any, selectedValue: ProgramRecord): void => { + if (selectedValue) { + handleClose(); + if (selectedValue.id === 'all') { + navigate(`/${businessArea}/programs/all/list`); + } else { + navigate( + `/${businessArea}/programs/${selectedValue.id}/details/${selectedValue.id}`, + ); + } } }; - if (loading) { - return ; - } + const searchPrograms = () => { + void loadProgramsList({ + variables: { + businessArea, + first: 5, + orderBy: 'name', + status: [ProgramStatus.Active, ProgramStatus.Draft], + name: inputValue, + }, + fetchPolicy: 'network-only', + }); + }; + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; - if (!data) { + const handleOnChangeInput = (event: any) => { + setInputValue(event.target.value); + }; + + const handleEnter = (event: KeyboardEvent) => { + if (event.key === 'Enter') { + searchPrograms(); + } + }; + + const clearInput = () => { + setInputValue(''); + void loadProgramsList(); + }; + + const open = Boolean(anchorEl); + const id = open ? 'global-program-filter' : undefined; + const buttonTitle = selectedProgram?.name || 'All Programmes'; + + if (loadingProgram) { return null; } return ( - - - All Programmes - - {data.allPrograms.edges - // TODO fix sorting - // .sort((objA, objB) => objA.node.name.localeCompare(objB.node.name)) - .map((each) => ( - - {each.node.name} - - ))} - + <> + + + + + + { + if (reason === 'escape') { + handleClose(); + } + }} + onChange={onChange} + PopperComponent={PopperComponent} + noOptionsText="No results" + renderOption={(props, option) => ( +
  • + {option.name} + {option.status && ( + + )} +
  • + )} + filterOptions={(x) => x} + options={programs} + getOptionLabel={(option) => option.name} + forcePopupIcon={false} + loading={loadingProgramsList} + inputValue={inputValue} + renderInput={(params) => ( + + {params.InputProps.endAdornment} + + {loadingProgramsList && } + {inputValue && ( + + + + )} + + + + + + ), + }} + /> + )} + /> +
    +
    + ); -} +}; diff --git a/frontend/src/containers/tables/ProgrammesTable/__snapshots__/ProgrammesTable.test.tsx.snap b/frontend/src/containers/tables/ProgrammesTable/__snapshots__/ProgrammesTable.test.tsx.snap index 0fb3687571..5d38693d0d 100644 --- a/frontend/src/containers/tables/ProgrammesTable/__snapshots__/ProgrammesTable.test.tsx.snap +++ b/frontend/src/containers/tables/ProgrammesTable/__snapshots__/ProgrammesTable.test.tsx.snap @@ -608,9 +608,8 @@ exports[`containers/tables/ProgrammesTable should render with data 1`] = ` class="sc-dJGMql eSUODg" >
    ACTIVE
    diff --git a/frontend/src/containers/tables/ReportingTable/__snapshots__/ReportingTable.test.tsx.snap b/frontend/src/containers/tables/ReportingTable/__snapshots__/ReportingTable.test.tsx.snap index fae31d4686..5b1199d46e 100644 --- a/frontend/src/containers/tables/ReportingTable/__snapshots__/ReportingTable.test.tsx.snap +++ b/frontend/src/containers/tables/ReportingTable/__snapshots__/ReportingTable.test.tsx.snap @@ -674,9 +674,8 @@ exports[`containers/tables/ReportingTable should render with data 1`] = ` class="sc-ifyrAs fSIDzb" >
    Processing
    diff --git a/frontend/src/containers/tables/payments/PaymentRecordTable/__snapshots__/PaymentRecordTable.test.tsx.snap b/frontend/src/containers/tables/payments/PaymentRecordTable/__snapshots__/PaymentRecordTable.test.tsx.snap index d522bb765f..3ae7fee8e5 100644 --- a/frontend/src/containers/tables/payments/PaymentRecordTable/__snapshots__/PaymentRecordTable.test.tsx.snap +++ b/frontend/src/containers/tables/payments/PaymentRecordTable/__snapshots__/PaymentRecordTable.test.tsx.snap @@ -770,9 +770,8 @@ exports[`containers/tables/payments/PaymentRecordTable should render with data 1 class="sc-ifyrAs fSIDzb" >
    DELIVERED FULLY
    diff --git a/frontend/src/containers/tables/payments/VerificationRecordsTable/__snapshots__/VerificationRecordsTable.test.tsx.snap b/frontend/src/containers/tables/payments/VerificationRecordsTable/__snapshots__/VerificationRecordsTable.test.tsx.snap index 4ed79129f3..7b92892143 100644 --- a/frontend/src/containers/tables/payments/VerificationRecordsTable/__snapshots__/VerificationRecordsTable.test.tsx.snap +++ b/frontend/src/containers/tables/payments/VerificationRecordsTable/__snapshots__/VerificationRecordsTable.test.tsx.snap @@ -876,9 +876,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    PENDING
    @@ -896,9 +895,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    ACTIVE
    @@ -956,9 +954,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    PENDING
    @@ -976,9 +973,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    ACTIVE
    @@ -1036,9 +1032,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    PENDING
    @@ -1056,9 +1051,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    ACTIVE
    @@ -1116,9 +1110,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    PENDING
    @@ -1136,9 +1129,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    ACTIVE
    @@ -1196,9 +1188,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    PENDING
    @@ -1216,9 +1207,8 @@ exports[`containers/tables/payments/VerificationRecordsTable should render with class="sc-ifyrAs fSIDzb" >
    ACTIVE
    diff --git a/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap b/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap index eb549a2735..c89bd3b873 100644 --- a/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap +++ b/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap @@ -785,9 +785,8 @@ exports[`containers/tables/rdi/RegistrationDataImportTable should render with da class="sc-ifyrAs fSIDzb" >
    IN REVIEW
    diff --git a/frontend/src/containers/tables/rdi/RegistrationDataImportTable/__snapshots__/RegistrationDataImportTable.test.tsx.snap b/frontend/src/containers/tables/rdi/RegistrationDataImportTable/__snapshots__/RegistrationDataImportTable.test.tsx.snap index 4f3b076652..3a628e9434 100644 --- a/frontend/src/containers/tables/rdi/RegistrationDataImportTable/__snapshots__/RegistrationDataImportTable.test.tsx.snap +++ b/frontend/src/containers/tables/rdi/RegistrationDataImportTable/__snapshots__/RegistrationDataImportTable.test.tsx.snap @@ -833,9 +833,8 @@ exports[`containers/tables/rdi/RegistrationDataImportTable should render with da class="sc-ifyrAs fSIDzb" >
    IN REVIEW
    diff --git a/frontend/src/containers/tables/targeting/TargetPopulationTable/__snapshots__/TargetPopulationTable.test.tsx.snap b/frontend/src/containers/tables/targeting/TargetPopulationTable/__snapshots__/TargetPopulationTable.test.tsx.snap index ee2d121f08..a58f246254 100644 --- a/frontend/src/containers/tables/targeting/TargetPopulationTable/__snapshots__/TargetPopulationTable.test.tsx.snap +++ b/frontend/src/containers/tables/targeting/TargetPopulationTable/__snapshots__/TargetPopulationTable.test.tsx.snap @@ -785,9 +785,8 @@ exports[`containers/tables/targeting/TargetPopulation/TargetPopulationTable shou class="sc-dJGMql eSUODg" >
    READY FOR CASH ASSIST
    @@ -845,9 +844,8 @@ exports[`containers/tables/targeting/TargetPopulation/TargetPopulationTable shou class="sc-dJGMql eSUODg" >
    READY FOR CASH ASSIST
    @@ -905,9 +903,8 @@ exports[`containers/tables/targeting/TargetPopulation/TargetPopulationTable shou class="sc-dJGMql eSUODg" >
    READY FOR CASH ASSIST
    @@ -965,9 +962,8 @@ exports[`containers/tables/targeting/TargetPopulation/TargetPopulationTable shou class="sc-dJGMql eSUODg" >
    ASSIGNED
    diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f8751902ba..fa54228937 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2012,7 +2012,7 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@humanwhocodes/config-array@^0.11.14": +"@humanwhocodes/config-array@^0.11.13": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==