import React, { useCallback, useMemo, useState } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { IconButton, RadioButton, RadioButtonGroup, RadioButtonSkeleton, Search } from '@carbon/react'; import { type ControllerRenderProps } from 'react-hook-form'; import { type RadioButtonGroupProps } from '@carbon/react/lib/components/RadioButtonGroup/RadioButtonGroup'; import { ChevronLeftIcon, ChevronRightIcon, isDesktop, ResponsiveWrapper, useDebounce, useLayoutType, type Location, } from '@openmrs/esm-framework'; import useEmrConfiguration from '../hooks/useEmrConfiguration'; import useLocations from '../hooks/useLocations'; import styles from './location-selector.scss'; interface LocationSelectorProps extends RadioButtonGroupProps { paginationSize?: number; /** * If provided, limits the locations to child locations of the given ancestor location. */ ancestorLocation?: Location; /** * a list of locations that should be filtered from the location selector */ excludeLocations?: Location[]; field: ControllerRenderProps< { note?: string; location?: string; transferType?: string; }, 'location' >; } export default function LocationSelector({ paginationSize = 15, ancestorLocation, excludeLocations: locationsToFilter, field, }: LocationSelectorProps) { const { t } = useTranslation(); const { emrConfiguration, isLoadingEmrConfiguration } = useEmrConfiguration(); const isTablet = !isDesktop(useLayoutType()); const [searchTerm, setSearchTerm] = useState(''); const debouncedSearchTerm = useDebounce(searchTerm); const filterCriteria: Array> = useMemo(() => { const criteria = []; if (debouncedSearchTerm) { criteria.push(['name:contains', debouncedSearchTerm]); } if (emrConfiguration) { // limit to locations tagged as transfer locations criteria.push(['_tag', emrConfiguration.supportsTransferLocationTag.name]); } if (ancestorLocation) { // limit to locations that are "part of" the facility where the patient has an active visit criteria.push(['partof:below', ancestorLocation.uuid]); } return criteria; }, [debouncedSearchTerm, emrConfiguration, ancestorLocation]); const { data: locations, isLoading, totalCount, currentPage, totalPages, goToNext, goToPrevious, } = useLocations(filterCriteria, paginationSize, !emrConfiguration); const filteredLocations = locations?.filter((l) => !locationsToFilter?.some((fl) => fl.uuid === l.id)); const handleSearch = useCallback( (event: React.ChangeEvent) => { setSearchTerm(event.target.value); }, [setSearchTerm], ); return (
{isLoading || isLoadingEmrConfiguration ? (
) : ( {filteredLocations?.length > 0 ? ( filteredLocations?.map((location) => ( )) ) : ( {t('noLocationsFound', 'No locations found')} )} )} {totalCount > paginationSize && (
{t('showingLocations', '{{start}}-{{end}} of {{count}} locations', { start: (currentPage - 1) * paginationSize + 1, end: Math.min(currentPage * paginationSize, totalCount), count: totalCount, })}
goToPrevious()}> = totalPages} kind="ghost" label={t('nextPage', 'Next page')} onClick={() => goToNext()}>
)}
); }