import React, { useState, useCallback, useEffect } from 'react'; import { Search, InlineLoading, Layer, Tile, FormLabel, InlineNotification } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { ArrowUpRight } from '@carbon/react/icons'; import { useConceptId } from '@hooks/useConceptId'; import { useConceptLookup } from '@hooks/useConceptLookup'; import { useDebounce } from '@openmrs/esm-framework'; import type { Concept } from '@types'; import styles from './concept-search.scss'; import { useFormField } from '../../../form-field-context'; interface ConceptSearchProps { label?: string; defaultConcept?: string; onClearSelectedConcept?: () => void; onSelectConcept: (concept: Concept) => void; retainConceptInContextAfterSearch?: boolean; clearSearchAfterSelection?: boolean; } const ConceptSearch: React.FC = ({ label, defaultConcept, onClearSelectedConcept, onSelectConcept, retainConceptInContextAfterSearch = false, clearSearchAfterSelection = false, }) => { const { t } = useTranslation(); const [conceptToLookup, setConceptToLookup] = useState(''); const debouncedConceptToLookup = useDebounce(conceptToLookup); const { concepts, conceptLookupError, isLoadingConcepts } = useConceptLookup(debouncedConceptToLookup); const { concept: initialConcept, conceptName, conceptNameLookupError, isLoadingConcept, } = useConceptId(defaultConcept); const [selectedConcept, setSelectedConcept] = useState(initialConcept); const { concept, setConcept, setIsConceptValid } = useFormField(); useEffect(() => { if (retainConceptInContextAfterSearch && initialConcept && !concept) { setConcept(initialConcept); } }, [initialConcept, retainConceptInContextAfterSearch, concept, setConcept]); const handleConceptChange = useCallback( (event: React.ChangeEvent) => setConceptToLookup(event.target.value), [], ); useEffect(() => { if (conceptLookupError || conceptNameLookupError) { setIsConceptValid(false); } }, [conceptLookupError, conceptNameLookupError, setIsConceptValid]); const clearSelectedConcept = useCallback(() => { setSelectedConcept(null); setConceptToLookup(''); setIsConceptValid(true); if (onClearSelectedConcept) onClearSelectedConcept(); }, [onClearSelectedConcept, setIsConceptValid]); const handleConceptSelect = useCallback( (concept: Concept) => { setConceptToLookup(''); setSelectedConcept(concept); onSelectConcept(concept); setIsConceptValid(true); if (clearSearchAfterSelection) { clearSelectedConcept(); } }, [onSelectConcept, setIsConceptValid, clearSelectedConcept, clearSearchAfterSelection], ); return ( <> {label ?? t('searchForBackingConcept', 'Search for a backing concept')} {(conceptLookupError || conceptNameLookupError) && ( )}
{isLoadingConcept ? ( ) : ( { if (conceptToLookup) { return conceptToLookup; } if (selectedConcept) { return selectedConcept.display; } if (conceptName) { return conceptName; } return ''; })()} /> )} {(() => { if (!conceptToLookup) return null; if (isLoadingConcepts) return ; if (concepts?.length && !isLoadingConcepts) { return (
    {concepts?.map((concept, index) => (
  • handleConceptSelect(concept)} > {concept.display}
  • ))}
); } return (
{t('noMatchingConcepts', 'No concepts were found that match')}{' '} "{debouncedConceptToLookup}".

{t('conceptSearchHelpText', "Can't find a concept?")}

{t('searchInOCL', 'Search in OCL')}
); })()}
); }; export default ConceptSearch;