diff --git a/cypress/e2e/shifting_spec/filter.cy.ts b/cypress/e2e/shifting_spec/filter.cy.ts index 2429648946..3e755db5c6 100644 --- a/cypress/e2e/shifting_spec/filter.cy.ts +++ b/cypress/e2e/shifting_spec/filter.cy.ts @@ -32,8 +32,11 @@ describe("Shifting section filter", () => { it("filter by assigned to user", () => { cy.intercept(/\/api\/v1\/users/).as("users_filter"); - cy.get("[name='assigned_to']").type("cypress").wait("@users_filter"); - cy.get("[name='assigned_to']").type("{downarrow}{enter}"); + cy.get("[id='assigned_to']") + .wait(100) + .type("cypress") + .wait("@users_filter"); + cy.get("[id='assigned_to']").wait(100).type("{downarrow}{enter}"); cy.contains("Apply").click(); }); diff --git a/src/Common/hooks/useAsyncOptions.ts b/src/Common/hooks/useAsyncOptions.ts index 7ed5121e2a..f0a1c89543 100644 --- a/src/Common/hooks/useAsyncOptions.ts +++ b/src/Common/hooks/useAsyncOptions.ts @@ -4,6 +4,7 @@ import { useDispatch } from "react-redux"; interface IUseAsyncOptionsArgs { debounceInterval?: number; + queryResponseExtractor?: (data: any) => any; } /** @@ -40,7 +41,10 @@ export function useAsyncOptions>( debounce(async (action: any) => { setIsLoading(true); const res = await dispatch(action); - if (res?.data) setQueryOptions(res.data as T[]); + if (res?.data) + setQueryOptions( + args?.queryResponseExtractor?.(res.data) ?? (res.data as T[]) + ); setIsLoading(false); }, args?.debounceInterval ?? 300), [dispatch, args?.debounceInterval] diff --git a/src/Components/Common/UserAutocompleteFormField.tsx b/src/Components/Common/UserAutocompleteFormField.tsx new file mode 100644 index 0000000000..ba2e2cd19e --- /dev/null +++ b/src/Components/Common/UserAutocompleteFormField.tsx @@ -0,0 +1,46 @@ +import { useAsyncOptions } from "../../Common/hooks/useAsyncOptions"; +import { getUserList } from "../../Redux/actions"; +import { Autocomplete } from "../Form/FormFields/Autocomplete"; +import FormField from "../Form/FormFields/FormField"; +import { + FormFieldBaseProps, + useFormFieldPropsResolver, +} from "../Form/FormFields/Utils"; +import { UserModel } from "../Users/models"; + +type Props = FormFieldBaseProps & { + placeholder?: string; +}; + +export default function UserAutocompleteFormField(props: Props) { + const field = useFormFieldPropsResolver(props as any); + const { fetchOptions, isLoading, options } = useAsyncOptions( + "id", + { queryResponseExtractor: (data) => data.results } + ); + + return ( + + `${option.user_type}`} + optionValue={(option) => option} + onQuery={(query) => + fetchOptions(getUserList({ limit: 5, offset: 0, search_text: query })) + } + isLoading={isLoading} + /> + + ); +} + +const getUserFullName = (user: UserModel) => { + const personName = user.first_name + " " + user.last_name; + return personName.trim().length > 0 ? personName : user.username || ""; +}; diff --git a/src/Components/Common/UserSelect2.tsx b/src/Components/Common/UserSelect2.tsx deleted file mode 100644 index 612c02871c..0000000000 --- a/src/Components/Common/UserSelect2.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; -import { getUserList } from "../../Redux/actions"; -import { LegacyAutoCompleteAsyncField } from "../Common/HelperInputFields"; -import { UserModel } from "../Users/models"; -import { debounce } from "lodash"; -interface UserSelectProps { - name?: string | ""; - margin?: string; - errors: string; - className?: string; - multiple?: boolean; - selected: UserModel | UserModel[] | null; - setSelected: (selected: UserModel | UserModel[] | null) => void; -} - -export const UserSelect = (props: UserSelectProps) => { - const { - name, - multiple, - selected, - setSelected, - margin, - errors, - className = "", - } = props; - const dispatchAction: any = useDispatch(); - const [userLoading, isUserLoading] = useState(false); - const [hasSearchText, setHasSearchText] = useState(false); - const [UserList, setUserList] = useState>([]); - - const getPersonName = (user: any) => { - const personName = user.first_name + " " + user.last_name; - - return personName.trim().length > 0 ? personName : user.username; - }; - - const handleValueChange = (current: UserModel | UserModel[] | null) => { - if (!current) { - setUserList([]); - isUserLoading(false); - setHasSearchText(false); - } - setSelected(current); - }; - - const handelSearch = (e: any) => { - isUserLoading(true); - setHasSearchText(!!e.target.value); - onUserSearch(e.target.value); - }; - - const onUserSearch = useCallback( - debounce(async (text: string) => { - if (text) { - const params = { - limit: 50, - offset: 0, - search_text: text, - }; - - const res = await dispatchAction(getUserList(params)); - - if (res && res.data) { - setUserList(res.data.results); - } - isUserLoading(false); - } else { - setUserList([]); - isUserLoading(false); - } - }, 300), - [] - ); - - return ( - handleValueChange(selected)} - loading={userLoading} - placeholder="Search by name or username" - noOptionsText={ - hasSearchText - ? "No user found, please try again" - : "Start typing to begin search" - } - renderOption={(option: any) => ( -
- - {getPersonName(option)} - ({option.user_type}) - -
- )} - getOptionSelected={(option: any, value: any) => option.id === value.id} - getOptionLabel={(option: any) => - `${getPersonName(option)} - (${option.user_type})` - } - filterOptions={(options: UserModel[]) => options} - errors={errors} - className={className} - /> - ); -}; diff --git a/src/Components/Shifting/ListFilter.tsx b/src/Components/Shifting/ListFilter.tsx index 099926f78e..ed6baa7d32 100644 --- a/src/Components/Shifting/ListFilter.tsx +++ b/src/Components/Shifting/ListFilter.tsx @@ -18,7 +18,6 @@ import { FieldLabel } from "../Form/FormFields/FormField"; import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; import { LegacySelectField } from "../Common/HelperInputFields"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; -import { UserSelect } from "../Common/UserSelect2"; import moment from "moment"; import { navigate } from "raviger"; import parsePhoneNumberFromString from "libphonenumber-js"; @@ -26,6 +25,7 @@ import useConfig from "../../Common/hooks/useConfig"; import { useDispatch } from "react-redux"; import useMergeState from "../../Common/hooks/useMergeState"; import { useTranslation } from "react-i18next"; +import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; const clearFilterState = { orgin_facility: "", @@ -57,7 +57,6 @@ export default function ListFilter(props: any) { const [isOriginLoading, setOriginLoading] = useState(false); const [isShiftingLoading, setShiftingLoading] = useState(false); const [isAssignedLoading, setAssignedLoading] = useState(false); - const [isAssignedUserLoading, setAssignedUserLoading] = useState(false); const { t } = useTranslation(); const shiftStatusOptions = ( @@ -143,16 +142,13 @@ export default function ListFilter(props: any) { useEffect(() => { async function fetchData() { if (filter.assigned_to) { - setAssignedUserLoading(true); const res = await dispatch(getUserList({ id: filter.assigned_to })); - if (res && res.data && res.data.count) { setFilterState({ ...filterState, assigned_user_ref: res.data.results[0], }); } - setAssignedUserLoading(false); } } fetchData(); @@ -345,23 +341,16 @@ export default function ListFilter(props: any) { -
- {t("assigned_to")} - {isAssignedUserLoading ? ( - - ) : ( - setAssignedUser(obj)} - className="shifting-page-filter-dropdown" - errors={""} - /> - )} -
+ setAssignedUser(value)} + errorClassName="hidden" + /> -
+
{t("ordering")}