diff --git a/public/locale/en.json b/public/locale/en.json index 0454ba6a8ad..31cb125d9ab 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -973,6 +973,7 @@ "facility": "Facility", "facility_actions_menu": "Facility action menu", "facility_added_successfully": "Facility created successfully", + "facility_assign_request": "What facility would you like to assign the request to?", "facility_consent_requests_page_title": "Patient Consent List", "facility_count_one": "{{count}} Facility", "facility_count_other": "{{count}} Facilities ", @@ -2031,6 +2032,7 @@ "start_time_before_authored_error": "Start time cannot be before the medication was prescribed", "start_time_future_error": "Start time cannot be in the future", "start_time_must_be_before_end_time": "Start time must be before end time", + "start_typing_to_search": "Start typing to search...", "state": "State", "state_reason_for_archiving": "State reason for archiving {{name}} file?", "status": "Status", diff --git a/src/components/Common/FacilitySelect.tsx b/src/components/Common/FacilitySelect.tsx deleted file mode 100644 index 4209de3fc89..00000000000 --- a/src/components/Common/FacilitySelect.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { t } from "i18next"; -import { useCallback } from "react"; - -import { FacilityModel } from "@/components/Facility/models"; -import AutoCompleteAsync from "@/components/Form/AutoCompleteAsync"; - -import request from "@/Utils/request/request"; -import facilityApi from "@/types/facility/facilityApi"; - -interface BaseFacilitySelectProps { - name: string; - exclude_user?: string; - errors?: string | undefined; - className?: string; - required?: boolean; - searchAll?: boolean; - disabled?: boolean; - multiple?: boolean; - facilityType?: number; - showAll?: boolean; - showNOptions?: number | undefined; - freeText?: boolean; - allowNone?: boolean; - placeholder?: string; - filter?: (facilities: FacilityModel) => boolean; - id?: string; -} - -interface SingleFacilitySelectProps extends BaseFacilitySelectProps { - multiple?: false; - selected: FacilityModel | null; - setSelected: (selected: FacilityModel | null) => void; -} - -interface MultipleFacilitySelectProps extends BaseFacilitySelectProps { - multiple: true; - selected: FacilityModel[]; - setSelected: (selected: FacilityModel[] | null) => void; -} - -type FacilitySelectProps = - | SingleFacilitySelectProps - | MultipleFacilitySelectProps; - -export const FacilitySelect = ({ - name, - exclude_user, - required, - multiple, - selected, - setSelected, - searchAll, - disabled = false, - showAll = true, - showNOptions, - className = "", - facilityType, - allowNone = false, - freeText = false, - errors = "", - placeholder, - filter, - id, -}: FacilitySelectProps) => { - const facilitySearch = useCallback( - async (text: string) => { - const query = { - limit: 50, - offset: 0, - search_text: text, - all: searchAll, - facility_type: facilityType, - exclude_user: exclude_user, - }; - - const { data } = await request(facilityApi.getAllFacilities, { query }); - - if (allowNone) - return [ - { name: t("no_home_facility"), id: "NONE" }, - ...(data?.results || []), - ]; - - return data?.results; - }, - [searchAll, showAll, facilityType, exclude_user, freeText], - ); - - return ( - option.name} - compareBy="id" - className={className} - error={errors} - filter={filter} - /> - ); -}; diff --git a/src/components/Form/AutoCompleteAsync.tsx b/src/components/Form/AutoCompleteAsync.tsx deleted file mode 100644 index b561b1f0648..00000000000 --- a/src/components/Form/AutoCompleteAsync.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { - Combobox, - ComboboxButton, - ComboboxInput, - ComboboxOption, - ComboboxOptions, -} from "@headlessui/react"; -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { DropdownTransition } from "@/components/Common/HelperComponents"; -import { - MultiSelectOptionChip, - dropdownOptionClassNames, -} from "@/components/Form/MultiSelectMenuV2"; - -import useDebounce from "@/hooks/useDebounce"; - -import { classNames } from "@/Utils/utils"; - -interface Props { - id?: string; - name?: string; - selected: any | any[]; - fetchData: (search: string) => Promise | undefined; - onChange: (selected: any) => void; - optionLabel?: (option: any) => string; - optionLabelChip?: (option: any) => string; - showNOptions?: number | undefined; - multiple?: boolean; - compareBy?: string; - debounceTime?: number; - className?: string; - placeholder?: string; - disabled?: boolean; - error?: string; - required?: boolean; - onBlur?: () => void; - onFocus?: () => void; - filter?: (data: any) => boolean; -} - -const AutoCompleteAsync = (props: Props) => { - const { - id, - name, - selected, - fetchData, - onChange, - optionLabel = (option: any) => option.label, - optionLabelChip = (option: any) => option.label, - showNOptions, - multiple = false, - compareBy, - debounceTime = 300, - className = "", - placeholder, - disabled = false, - required = false, - error, - filter, - } = props; - const [data, setData] = useState([]); - const [query, setQuery] = useState(""); - const [loading, setLoading] = useState(false); - const { t } = useTranslation(); - - const hasSelection = - (!multiple && selected) || (multiple && selected?.length > 0); - - const fetchDataDebounced = useDebounce(async (searchQuery: string) => { - setLoading(true); - try { - const fetchedData = (await fetchData(searchQuery)) || []; - const filteredData = filter - ? fetchedData.filter((item: any) => filter(item)) - : fetchedData; - - setData( - showNOptions !== undefined - ? filteredData.slice(0, showNOptions) - : filteredData, - ); - } catch (error) { - console.error("Error fetching data:", error); - } finally { - setLoading(false); - } - }, debounceTime); - - useEffect(() => { - fetchDataDebounced(query); - }, [query]); - - return ( -
- -
-
- - hasSelection && !multiple ? optionLabel?.(selected) : "" - } - onChange={({ target }) => setQuery(target.value)} - onFocus={props.onFocus} - onBlur={() => { - props.onBlur?.(); - }} - autoComplete="off" - /> - {!disabled && ( - -
- {hasSelection && !loading && !required && ( -
- { - e.preventDefault(); - onChange(null); - }} - /> - - {t("clear_selection")} - -
- )} - {loading ? ( - - ) : ( - - )} -
-
- )} -
- - - {data?.length === 0 ? ( -
- {query !== "" - ? "Nothing found." - : "Start typing to search..."} -
- ) : ( - data?.map((item: any) => ( - - {({ selected }) => ( -
-
- {optionLabel(item)} - {optionLabelChip(item) && ( -
- {optionLabelChip(item)} -
- )} -
- {selected && ( - - )} -
- )} -
- )) - )} -
-
- {multiple && selected?.length > 0 && ( -
- {selected?.map((option: any, index: number) => ( - - onChange( - selected.filter((item: any) => item.id !== option.id), - ) - } - /> - ))} -
- )} - {error && ( -
- {error} -
- )} -
-
-
- ); -}; - -export default AutoCompleteAsync; diff --git a/src/components/Resource/ResourceCreate.tsx b/src/components/Resource/ResourceCreate.tsx index 73a5ff652e0..4e45ad78d96 100644 --- a/src/components/Resource/ResourceCreate.tsx +++ b/src/components/Resource/ResourceCreate.tsx @@ -1,6 +1,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery } from "@tanstack/react-query"; import { Link, navigate, useQueryParams } from "raviger"; +import { useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; @@ -10,6 +11,7 @@ import Card from "@/CAREUI/display/Card"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Alert, AlertDescription } from "@/components/ui/alert"; +import Autocomplete from "@/components/ui/autocomplete"; import { Button } from "@/components/ui/button"; import { Form, @@ -33,7 +35,6 @@ import { import { Separator } from "@/components/ui/separator"; import { Textarea } from "@/components/ui/textarea"; -import { FacilitySelect } from "@/components/Common/FacilitySelect"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; @@ -46,6 +47,7 @@ import routes from "@/Utils/request/api"; import mutate from "@/Utils/request/mutate"; import query from "@/Utils/request/query"; import validators from "@/Utils/validators"; +import facilityApi from "@/types/facility/facilityApi"; import { ResourceRequest } from "@/types/resourceRequest/resourceRequest"; interface ResourceProps { @@ -53,6 +55,7 @@ interface ResourceProps { } export default function ResourceCreate(props: ResourceProps) { + const [facilitySearch, setFacilitySearch] = useState(""); const { goBack } = useAppHistory(); const { facilityId } = props; const { t } = useTranslation(); @@ -134,6 +137,21 @@ export default function ResourceCreate(props: ResourceProps) { }); }; + const { data: facilities } = useQuery({ + queryKey: ["facilities", facilitySearch], + queryFn: query.debounced(facilityApi.getAllFacilities, { + queryParams: { + search_text: facilitySearch, + limit: 50, + }, + }), + }); + + const facilityOptions = facilities?.results.map((facility) => ({ + label: facility.name, + value: facility.id, + })); + const fillMyDetails = () => { form.setValue( "referring_facility_contact_name", @@ -204,11 +222,18 @@ export default function ResourceCreate(props: ResourceProps) { {t("facility_for_care_support")} - { + const facility = + facilities?.results.find( + (f) => f.id === value, + ) ?? null; + form.setValue("assigned_facility", facility); + }} /> diff --git a/src/components/Resource/ResourceDetailsUpdate.tsx b/src/components/Resource/ResourceDetailsUpdate.tsx index f3e48df7af8..475fdd3da5c 100644 --- a/src/components/Resource/ResourceDetailsUpdate.tsx +++ b/src/components/Resource/ResourceDetailsUpdate.tsx @@ -7,16 +7,15 @@ import { toast } from "sonner"; import Card from "@/CAREUI/display/Card"; +import Autocomplete from "@/components/ui/autocomplete"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import CircularProgress from "@/components/Common/CircularProgress"; -import { FacilitySelect } from "@/components/Common/FacilitySelect"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; import UserAutocomplete from "@/components/Common/UserAutocompleteFormField"; -import { FieldLabel } from "@/components/Form/FormFields/FormField"; import RadioFormField from "@/components/Form/FormFields/RadioFormField"; import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; import TextFormField from "@/components/Form/FormFields/TextFormField"; @@ -30,6 +29,7 @@ import { RESOURCE_STATUS_CHOICES } from "@/common/constants"; import routes from "@/Utils/request/api"; import mutate from "@/Utils/request/mutate"; import query from "@/Utils/request/query"; +import facilityApi from "@/types/facility/facilityApi"; import { UpdateResourceRequest } from "@/types/resourceRequest/resourceRequest"; interface resourceProps { @@ -64,6 +64,7 @@ const initialState = { export const ResourceDetailsUpdate = (props: resourceProps) => { const { goBack } = useAppHistory(); const [qParams, _] = useQueryParams(); + const [facilitySearch, setFacilitySearch] = useState(""); const [assignedUser, SetAssignedUser] = useState(); const resourceFormReducer = (state = initialState, action: any) => { switch (action.type) { @@ -159,6 +160,21 @@ export const ResourceDetailsUpdate = (props: resourceProps) => { }, }); + const { data: facilities } = useQuery({ + queryKey: ["facilities", facilitySearch], + queryFn: query.debounced(facilityApi.getAllFacilities, { + queryParams: { + search_text: facilitySearch, + limit: 50, + }, + }), + }); + + const facilityOptions = facilities?.results.map((facility) => ({ + label: facility.name, + value: facility.id, + })); + const handleSubmit = async () => { const validForm = validateForm(); @@ -226,15 +242,17 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
- - What facility would you like to assign the request to - - setFacility(obj, "assigned_facility")} - errors={state.errors.assigned_facility} + + + setFacility(selected, "assigned_facility") + } />
diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts deleted file mode 100644 index 66ee398f962..00000000000 --- a/src/hooks/useDebounce.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useEffect, useRef } from "react"; - -export default function useDebounce( - callback: (...args: T) => void, - delay: number, -) { - const callbackRef = useRef(callback); - const timeoutRef = useRef | null>(null); - - useEffect(() => { - callbackRef.current = callback; - }, [callback]); - - useEffect(() => { - return () => { - if (timeoutRef.current) clearTimeout(timeoutRef.current); - }; - }, []); - - const debouncedCallback = (...args: T) => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - timeoutRef.current = setTimeout(() => { - callbackRef.current(...args); - }, delay); - }; - return debouncedCallback; -}