/* * 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, Chip, CircularProgress, IconButton, InputAdornment, Skeleton, Typography, useTheme, } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; import { forwardRef, SyntheticEvent, useRef } 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: (patterns: NlpPattern[]) => void; }; const NlpPatternSelect = ( { patterns, onChange }: NlpPatternSelectProps, ref, ) => { const inputRef = useRef(null); const theme = useTheme(); const { t } = useTranslate(); const { 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); const handleNlpEntityChange = ( _event: SyntheticEvent, entities: INlpEntity[], ): void => { const intersection = patterns.filter(({ entity: entityName }) => entities.find(({ name }) => name === entityName), ); const additions = entities.filter( ({ name }) => !patterns.find(({ entity: entityName }) => name === entityName), ); const newSelection = [ ...intersection, ...additions.map( ({ name }) => ({ entity: name, match: "entity", value: name, } as NlpPattern), ), ]; onChange(newSelection); }; const handleNlpValueChange = ( { id, name }: Pick, valueId: string, ): void => { const newSelection = patterns.slice(0); const update = newSelection.find(({ entity: e }) => e === name); if (!update) { throw new Error("Unable to find nlp entity"); } if (valueId === id) { update.match = "entity"; update.value = 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(newSelection); }; if (!options.length) { return ( ); } const defaultValue = options.filter(({ name }) => patterns.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(({ id, name, values }, index) => { const { key, onDelete } = getTagProps({ index }); const nlpValues = values.map((vId) => getNlpValueFromCache(vId), ) as INlpValue[]; const selectedValue = patterns.find( (e) => e.entity === name, )?.value; const { id: selectedId = id } = nlpValues.find(({ value }) => value === selectedValue) || {}; return ( { const nlpValueCache = getNlpValueFromCache(option); if (nlpValueCache) { return nlpValueCache?.value; } if (option === id) { return t("label.any"); } return option; }} freeSolo={false} disableClearable popupIcon={false} onChange={(e, valueId) => handleNlpValueChange({ id, name }, valueId) } sx={{ minWidth: 50, ".MuiAutocomplete-input": { minWidth: "100px !important", }, "& .MuiOutlinedInput-root": { paddingRight: "2rem !important", "&.MuiInputBase-sizeSmall": { padding: "0 6px 0 0 !important", }, }, }} renderInput={(props) => ( ), endAdornment: ( {isLoading ? ( ) : ( { onDelete(e); onChange( patterns.filter((p) => p.entity !== name), ); }} edge="end" size="small" > )} ), }} /> )} /> ); })} ); }} renderInput={(props) => ( { 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;