/* * Copyright © 2024 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ import { Cancel } from "@mui/icons-material"; import { Box, CircularProgress, IconButton, InputAdornment, Skeleton, Typography, useTheme, } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; import { forwardRef, SyntheticEvent, useRef, useState } from "react"; import { Input } from "@/app-components/inputs/Input"; import { useFind } from "@/hooks/crud/useFind"; import { useGetFromCache } from "@/hooks/crud/useGet"; import { useSearch } from "@/hooks/useSearch"; import { useTranslate } from "@/hooks/useTranslate"; import { EntityType, Format } from "@/services/types"; import { NlpPattern } from "@/types/block.types"; import { INlpEntity } from "@/types/nlp-entity.types"; import { INlpValue } from "@/types/nlp-value.types"; type NlpPatternSelectProps = { patterns: NlpPattern[]; onChange: any }; const NlpPatternSelect = ( { patterns, onChange }: NlpPatternSelectProps, ref, ) => { const inputRef = useRef(null); const [selected, setSelected] = useState(patterns); const theme = useTheme(); const { t } = useTranslate(); const { onSearch, searchPayload } = useSearch({ $iLike: ["name"], }); const { data: options, isLoading } = useFind( { entity: EntityType.NLP_ENTITY, format: Format.FULL }, { hasCount: false, params: searchPayload }, ); const getNlpValueFromCache = useGetFromCache(EntityType.NLP_VALUE); function handleNlpEntityChange( _event: SyntheticEvent, entities: INlpEntity[], ): void { const intersection = selected.filter(({ entity: entityName }) => entities.find(({ name }) => name === entityName), ); const additions = entities.filter( ({ name }) => !selected.find(({ entity: entityName }) => name === entityName), ); const newSelection: NlpPattern[] = [ ...intersection, ...additions.map( ({ name }) => ({ entity: name, match: "entity", value: name, } as NlpPattern), ), ]; setSelected(newSelection); } const handleNlpValueChange = (entity: INlpEntity, valueId: string) => { const newSelection = [...selected]; const update = newSelection.find(({ entity: e }) => e === entity.name); if (!update) { throw new Error("Unable to find nlp entity"); } if (valueId === entity.id) { update.match = "entity"; update.value = entity.name; } else { const value = getNlpValueFromCache(valueId); if (!value) { throw new Error("Unable to find nlp value in cache"); } update.match = "value"; update.value = value.value; } onChange(undefined, newSelection); }; if (!options.length) { return ( ); } const defaultValue = options.filter(({ name }) => selected.find(({ entity: entityName }) => entityName === name), ) || {}; return ( ( {name} {doc && ( {doc} )} )} getOptionLabel={({ name }) => name} isOptionEqualToValue={(option, value) => option.id === value.id} freeSolo={false} loading={isLoading} renderTags={(entities, getTagProps) => { return ( {entities.map((entity, index) => { const { key, onDelete } = getTagProps({ index }); const handleChange = ( _event: SyntheticEvent, valueId: string, ) => { handleNlpValueChange(entity, valueId); }; const values = entity.values.map((vId) => getNlpValueFromCache(vId), ) as INlpValue[]; const selectedValue = patterns?.find( (e) => e.entity === entity.name, )?.value; const value = values.find(({ value }) => value === selectedValue); return ( { const nlpValueCache = getNlpValueFromCache(option); if (nlpValueCache) { return nlpValueCache?.value; } if (option === entity.id) { return t("label.any"); } return option; }} freeSolo={false} disableClearable popupIcon={false} onChange={handleChange} sx={{ minWidth: 50, padding: 0, ".MuiAutocomplete-input": { minWidth: "100px !important", }, "& .MuiOutlinedInput-root": { paddingRight: "2rem !important", "&.MuiInputBase-sizeSmall": { padding: "0 6px 0 0 !important", }, }, }} renderInput={(props) => { return ( {entity.name} ), endAdornment: ( {isLoading ? ( ) : ( { onDelete(e); onChange( undefined, patterns.filter( (p) => p.entity !== entity.name, ), ); }} edge="end" size="small" > )} ), }} /> ); }} /> ); })} ); }} renderInput={(props) => ( onSearch(e.target.value)} InputProps={{ ...props.InputProps, inputRef, onClick: (event) => { if (event.target !== inputRef.current) { event.stopPropagation(); event.preventDefault(); } }, endAdornment: isLoading ? ( ) : null, }} /> )} /> ); }; NlpPatternSelect.displayName = "NlpPatternSelect"; export default forwardRef(NlpPatternSelect) as ( props: NlpPatternSelectProps & { ref?: React.ForwardedRef; }, ) => ReturnType;