mirror of
https://github.com/hexastack/hexabot
synced 2024-12-02 00:54:56 +00:00
refactor: nlp select pattern v2
This commit is contained in:
parent
93eb937f7f
commit
3c5a45fca5
@ -9,6 +9,7 @@
|
||||
import { Cancel } from "@mui/icons-material";
|
||||
import {
|
||||
Box,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
@ -17,7 +18,7 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import { forwardRef, SyntheticEvent, useRef, useState } from "react";
|
||||
import { forwardRef, SyntheticEvent, useRef } from "react";
|
||||
|
||||
import { Input } from "@/app-components/inputs/Input";
|
||||
import { useFind } from "@/hooks/crud/useFind";
|
||||
@ -29,17 +30,22 @@ 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 };
|
||||
type NlpPatternSelectProps = {
|
||||
patterns: NlpPattern[];
|
||||
onChange: (
|
||||
_event: SyntheticEvent<Element, Event> | undefined,
|
||||
patterns: NlpPattern[],
|
||||
) => void;
|
||||
};
|
||||
|
||||
const NlpPatternSelect = (
|
||||
{ patterns, onChange }: NlpPatternSelectProps,
|
||||
ref,
|
||||
) => {
|
||||
const inputRef = useRef(null);
|
||||
const [selected, setSelected] = useState<NlpPattern[]>(patterns);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslate();
|
||||
const { onSearch, searchPayload } = useSearch<INlpEntity>({
|
||||
const { searchPayload } = useSearch<INlpEntity>({
|
||||
$iLike: ["name"],
|
||||
});
|
||||
const { data: options, isLoading } = useFind(
|
||||
@ -47,19 +53,18 @@ const NlpPatternSelect = (
|
||||
{ hasCount: false, params: searchPayload },
|
||||
);
|
||||
const getNlpValueFromCache = useGetFromCache(EntityType.NLP_VALUE);
|
||||
|
||||
function handleNlpEntityChange(
|
||||
const handleNlpEntityChange = (
|
||||
_event: SyntheticEvent<Element, Event>,
|
||||
entities: INlpEntity[],
|
||||
): void {
|
||||
const intersection = selected.filter(({ entity: entityName }) =>
|
||||
): void => {
|
||||
const intersection = patterns.filter(({ entity: entityName }) =>
|
||||
entities.find(({ name }) => name === entityName),
|
||||
);
|
||||
const additions = entities.filter(
|
||||
({ name }) =>
|
||||
!selected.find(({ entity: entityName }) => name === entityName),
|
||||
!patterns.find(({ entity: entityName }) => name === entityName),
|
||||
);
|
||||
const newSelection: NlpPattern[] = [
|
||||
const newSelection = [
|
||||
...intersection,
|
||||
...additions.map(
|
||||
({ name }) =>
|
||||
@ -71,20 +76,22 @@ const NlpPatternSelect = (
|
||||
),
|
||||
];
|
||||
|
||||
setSelected(newSelection);
|
||||
}
|
||||
|
||||
const handleNlpValueChange = (entity: INlpEntity, valueId: string) => {
|
||||
const newSelection = [...selected];
|
||||
const update = newSelection.find(({ entity: e }) => e === entity.name);
|
||||
onChange(undefined, newSelection);
|
||||
};
|
||||
const handleNlpValueChange = (
|
||||
{ id, name }: Pick<INlpEntity, "id" | "name">,
|
||||
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 === entity.id) {
|
||||
if (valueId === id) {
|
||||
update.match = "entity";
|
||||
update.value = entity.name;
|
||||
update.value = name;
|
||||
} else {
|
||||
const value = getNlpValueFromCache(valueId);
|
||||
|
||||
@ -106,7 +113,7 @@ const NlpPatternSelect = (
|
||||
|
||||
const defaultValue =
|
||||
options.filter(({ name }) =>
|
||||
selected.find(({ entity: entityName }) => entityName === name),
|
||||
patterns.find(({ entity: entityName }) => entityName === name),
|
||||
) || {};
|
||||
|
||||
return (
|
||||
@ -159,29 +166,25 @@ const NlpPatternSelect = (
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: 0.5,
|
||||
mx: "0.5rem",
|
||||
}}
|
||||
>
|
||||
{entities.map((entity, index) => {
|
||||
{entities.map(({ id, name, values }, index) => {
|
||||
const { key, onDelete } = getTagProps({ index });
|
||||
const handleChange = (
|
||||
_event: SyntheticEvent<Element, Event>,
|
||||
valueId: string,
|
||||
) => {
|
||||
handleNlpValueChange(entity, valueId);
|
||||
};
|
||||
const values = entity.values.map((vId) =>
|
||||
const nlpValues = values.map((vId) =>
|
||||
getNlpValueFromCache(vId),
|
||||
) as INlpValue[];
|
||||
const selectedValue = patterns?.find(
|
||||
(e) => e.entity === entity.name,
|
||||
const selectedValue = patterns.find(
|
||||
(e) => e.entity === name,
|
||||
)?.value;
|
||||
const value = values.find(({ value }) => value === selectedValue);
|
||||
const { id: selectedId = id } =
|
||||
nlpValues.find(({ value }) => value === selectedValue) || {};
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
size="small"
|
||||
defaultValue={value?.id || entity.id}
|
||||
options={[entity.id].concat(entity.values)}
|
||||
defaultValue={selectedId}
|
||||
options={[id].concat(values)}
|
||||
multiple={false}
|
||||
key={key}
|
||||
getOptionLabel={(option) => {
|
||||
@ -191,7 +194,7 @@ const NlpPatternSelect = (
|
||||
return nlpValueCache?.value;
|
||||
}
|
||||
|
||||
if (option === entity.id) {
|
||||
if (option === id) {
|
||||
return t("label.any");
|
||||
}
|
||||
|
||||
@ -200,10 +203,11 @@ const NlpPatternSelect = (
|
||||
freeSolo={false}
|
||||
disableClearable
|
||||
popupIcon={false}
|
||||
onChange={handleChange}
|
||||
onChange={(e, valueId) =>
|
||||
handleNlpValueChange({ id, name }, valueId)
|
||||
}
|
||||
sx={{
|
||||
minWidth: 50,
|
||||
padding: 0,
|
||||
".MuiAutocomplete-input": {
|
||||
minWidth: "100px !important",
|
||||
},
|
||||
@ -214,59 +218,67 @@ const NlpPatternSelect = (
|
||||
},
|
||||
},
|
||||
}}
|
||||
renderInput={(props) => {
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
InputProps={{
|
||||
...props.InputProps,
|
||||
readOnly: true,
|
||||
sx: {
|
||||
padding: 0,
|
||||
overflow: "hidden",
|
||||
cursor: "pointer",
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment
|
||||
position="start"
|
||||
renderInput={(props) => (
|
||||
<Input
|
||||
{...props}
|
||||
InputProps={{
|
||||
...props.InputProps,
|
||||
readOnly: true,
|
||||
sx: {
|
||||
padding: 0,
|
||||
overflow: "hidden",
|
||||
cursor: "pointer",
|
||||
fontSize: "14px",
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Chip
|
||||
sx={{
|
||||
padding: "0 1rem",
|
||||
height: "100%",
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
p: "0 0.3rem",
|
||||
border: "none",
|
||||
borderRadius: 0,
|
||||
}}
|
||||
>
|
||||
{entity.name}
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
{isLoading ? (
|
||||
<CircularProgress color="inherit" size={20} />
|
||||
) : (
|
||||
<IconButton
|
||||
sx={{ padding: 0 }}
|
||||
onClick={(e) => {
|
||||
onDelete(e);
|
||||
color="primary"
|
||||
label={name}
|
||||
variant="role"
|
||||
/>
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
{isLoading ? (
|
||||
<CircularProgress color="inherit" size={20} />
|
||||
) : (
|
||||
<IconButton
|
||||
sx={{ p: 0, pr: "2px" }}
|
||||
onClick={(e) => {
|
||||
onDelete(e);
|
||||
|
||||
onChange(
|
||||
undefined,
|
||||
patterns.filter(
|
||||
(p) => p.entity !== entity.name,
|
||||
),
|
||||
);
|
||||
onChange(
|
||||
undefined,
|
||||
patterns.filter((p) => p.entity !== name),
|
||||
);
|
||||
}}
|
||||
edge="end"
|
||||
size="small"
|
||||
>
|
||||
<Cancel
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
transition: ".05s",
|
||||
"&:hover": {
|
||||
color: theme.palette.grey[700],
|
||||
},
|
||||
}}
|
||||
edge="end"
|
||||
size="small"
|
||||
>
|
||||
<Cancel htmlColor={theme.palette.grey[500]} />
|
||||
</IconButton>
|
||||
)}
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
htmlColor={theme.palette.grey[500]}
|
||||
/>
|
||||
</IconButton>
|
||||
)}
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -276,8 +288,13 @@ const NlpPatternSelect = (
|
||||
renderInput={(props) => (
|
||||
<Input
|
||||
{...props}
|
||||
sx={{
|
||||
"& .MuiOutlinedInput-root": {
|
||||
paddingRight: "6px !important",
|
||||
},
|
||||
}}
|
||||
size="small"
|
||||
label={t("label.nlp")}
|
||||
onChange={(e) => onSearch(e.target.value)}
|
||||
InputProps={{
|
||||
...props.InputProps,
|
||||
inputRef,
|
||||
|
@ -153,83 +153,6 @@ const PatternInput: FC<PatternInputProps> = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* {patternType === "nlp" ? (
|
||||
<AutoCompleteEntitySelect<INlpValue, "value">
|
||||
value={(pattern as NlpPattern[]).map((v) =>
|
||||
"value" in v && v.value ? v.value : v.entity,
|
||||
)}
|
||||
searchFields={["value", "label"]}
|
||||
entity={EntityType.NLP_VALUE}
|
||||
format={Format.FULL}
|
||||
idKey="value"
|
||||
labelKey="value"
|
||||
label={t("label.nlp")}
|
||||
multiple={true}
|
||||
onChange={(_e, data) => {
|
||||
setPattern(
|
||||
data.map((d) => {
|
||||
const entity = getNlpEntityFromCache(d.entity) as INlpEntity;
|
||||
|
||||
return d.value === "any"
|
||||
? {
|
||||
match: "entity",
|
||||
entity: entity.name,
|
||||
}
|
||||
: {
|
||||
match: "value",
|
||||
entity: entity.name,
|
||||
value: d.value,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}}
|
||||
getOptionLabel={(option) => {
|
||||
const entity = getNlpEntityFromCache(option.entity) as INlpEntity;
|
||||
|
||||
return `${entity.name}=${option.value}`;
|
||||
}}
|
||||
groupBy={(option) => {
|
||||
const entity = getNlpEntityFromCache(option.entity) as INlpEntity;
|
||||
|
||||
return entity.name;
|
||||
}}
|
||||
renderGroup={(params) => (
|
||||
<li key={params.key}>
|
||||
<Typography
|
||||
component="h4"
|
||||
p={2}
|
||||
fontWeight={700}
|
||||
color="primary"
|
||||
>
|
||||
{params.group}
|
||||
</Typography>
|
||||
<Box>{params.children}</Box>
|
||||
</li>
|
||||
)}
|
||||
preprocess={(options) => {
|
||||
return options.reduce((acc, curr) => {
|
||||
const entity = getNlpEntityFromCache(curr.entity) as INlpEntity;
|
||||
|
||||
if (entity.lookups.includes("keywords")) {
|
||||
const exists = acc.find(
|
||||
({ value, id }) => value === "any" && id === entity.id,
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
acc.push({
|
||||
entity: entity.id,
|
||||
id: entity.id,
|
||||
value: "any",
|
||||
} as INlpValue);
|
||||
}
|
||||
}
|
||||
acc.push(curr);
|
||||
|
||||
return acc;
|
||||
}, [] as INlpValue[]);
|
||||
}}
|
||||
/>
|
||||
) : null} */}
|
||||
{patternType === "menu" ? (
|
||||
<AutoCompleteEntitySelect<IMenuItem, "title", false>
|
||||
value={pattern ? (pattern as PayloadPattern).value : null}
|
||||
|
Loading…
Reference in New Issue
Block a user