mirror of
https://github.com/hexastack/hexabot
synced 2024-12-02 09:05:06 +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 { Cancel } from "@mui/icons-material";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Chip,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
@ -17,7 +18,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import Autocomplete from "@mui/material/Autocomplete";
|
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 { Input } from "@/app-components/inputs/Input";
|
||||||
import { useFind } from "@/hooks/crud/useFind";
|
import { useFind } from "@/hooks/crud/useFind";
|
||||||
@ -29,17 +30,22 @@ import { NlpPattern } from "@/types/block.types";
|
|||||||
import { INlpEntity } from "@/types/nlp-entity.types";
|
import { INlpEntity } from "@/types/nlp-entity.types";
|
||||||
import { INlpValue } from "@/types/nlp-value.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 = (
|
const NlpPatternSelect = (
|
||||||
{ patterns, onChange }: NlpPatternSelectProps,
|
{ patterns, onChange }: NlpPatternSelectProps,
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [selected, setSelected] = useState<NlpPattern[]>(patterns);
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslate();
|
||||||
const { onSearch, searchPayload } = useSearch<INlpEntity>({
|
const { searchPayload } = useSearch<INlpEntity>({
|
||||||
$iLike: ["name"],
|
$iLike: ["name"],
|
||||||
});
|
});
|
||||||
const { data: options, isLoading } = useFind(
|
const { data: options, isLoading } = useFind(
|
||||||
@ -47,19 +53,18 @@ const NlpPatternSelect = (
|
|||||||
{ hasCount: false, params: searchPayload },
|
{ hasCount: false, params: searchPayload },
|
||||||
);
|
);
|
||||||
const getNlpValueFromCache = useGetFromCache(EntityType.NLP_VALUE);
|
const getNlpValueFromCache = useGetFromCache(EntityType.NLP_VALUE);
|
||||||
|
const handleNlpEntityChange = (
|
||||||
function handleNlpEntityChange(
|
|
||||||
_event: SyntheticEvent<Element, Event>,
|
_event: SyntheticEvent<Element, Event>,
|
||||||
entities: INlpEntity[],
|
entities: INlpEntity[],
|
||||||
): void {
|
): void => {
|
||||||
const intersection = selected.filter(({ entity: entityName }) =>
|
const intersection = patterns.filter(({ entity: entityName }) =>
|
||||||
entities.find(({ name }) => name === entityName),
|
entities.find(({ name }) => name === entityName),
|
||||||
);
|
);
|
||||||
const additions = entities.filter(
|
const additions = entities.filter(
|
||||||
({ name }) =>
|
({ name }) =>
|
||||||
!selected.find(({ entity: entityName }) => name === entityName),
|
!patterns.find(({ entity: entityName }) => name === entityName),
|
||||||
);
|
);
|
||||||
const newSelection: NlpPattern[] = [
|
const newSelection = [
|
||||||
...intersection,
|
...intersection,
|
||||||
...additions.map(
|
...additions.map(
|
||||||
({ name }) =>
|
({ name }) =>
|
||||||
@ -71,20 +76,22 @@ const NlpPatternSelect = (
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
setSelected(newSelection);
|
onChange(undefined, newSelection);
|
||||||
}
|
};
|
||||||
|
const handleNlpValueChange = (
|
||||||
const handleNlpValueChange = (entity: INlpEntity, valueId: string) => {
|
{ id, name }: Pick<INlpEntity, "id" | "name">,
|
||||||
const newSelection = [...selected];
|
valueId: string,
|
||||||
const update = newSelection.find(({ entity: e }) => e === entity.name);
|
): void => {
|
||||||
|
const newSelection = patterns.slice(0);
|
||||||
|
const update = newSelection.find(({ entity: e }) => e === name);
|
||||||
|
|
||||||
if (!update) {
|
if (!update) {
|
||||||
throw new Error("Unable to find nlp entity");
|
throw new Error("Unable to find nlp entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueId === entity.id) {
|
if (valueId === id) {
|
||||||
update.match = "entity";
|
update.match = "entity";
|
||||||
update.value = entity.name;
|
update.value = name;
|
||||||
} else {
|
} else {
|
||||||
const value = getNlpValueFromCache(valueId);
|
const value = getNlpValueFromCache(valueId);
|
||||||
|
|
||||||
@ -106,7 +113,7 @@ const NlpPatternSelect = (
|
|||||||
|
|
||||||
const defaultValue =
|
const defaultValue =
|
||||||
options.filter(({ name }) =>
|
options.filter(({ name }) =>
|
||||||
selected.find(({ entity: entityName }) => entityName === name),
|
patterns.find(({ entity: entityName }) => entityName === name),
|
||||||
) || {};
|
) || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -159,29 +166,25 @@ const NlpPatternSelect = (
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: 0.5,
|
gap: 0.5,
|
||||||
|
mx: "0.5rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{entities.map((entity, index) => {
|
{entities.map(({ id, name, values }, index) => {
|
||||||
const { key, onDelete } = getTagProps({ index });
|
const { key, onDelete } = getTagProps({ index });
|
||||||
const handleChange = (
|
const nlpValues = values.map((vId) =>
|
||||||
_event: SyntheticEvent<Element, Event>,
|
|
||||||
valueId: string,
|
|
||||||
) => {
|
|
||||||
handleNlpValueChange(entity, valueId);
|
|
||||||
};
|
|
||||||
const values = entity.values.map((vId) =>
|
|
||||||
getNlpValueFromCache(vId),
|
getNlpValueFromCache(vId),
|
||||||
) as INlpValue[];
|
) as INlpValue[];
|
||||||
const selectedValue = patterns?.find(
|
const selectedValue = patterns.find(
|
||||||
(e) => e.entity === entity.name,
|
(e) => e.entity === name,
|
||||||
)?.value;
|
)?.value;
|
||||||
const value = values.find(({ value }) => value === selectedValue);
|
const { id: selectedId = id } =
|
||||||
|
nlpValues.find(({ value }) => value === selectedValue) || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
size="small"
|
size="small"
|
||||||
defaultValue={value?.id || entity.id}
|
defaultValue={selectedId}
|
||||||
options={[entity.id].concat(entity.values)}
|
options={[id].concat(values)}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
key={key}
|
key={key}
|
||||||
getOptionLabel={(option) => {
|
getOptionLabel={(option) => {
|
||||||
@ -191,7 +194,7 @@ const NlpPatternSelect = (
|
|||||||
return nlpValueCache?.value;
|
return nlpValueCache?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option === entity.id) {
|
if (option === id) {
|
||||||
return t("label.any");
|
return t("label.any");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,10 +203,11 @@ const NlpPatternSelect = (
|
|||||||
freeSolo={false}
|
freeSolo={false}
|
||||||
disableClearable
|
disableClearable
|
||||||
popupIcon={false}
|
popupIcon={false}
|
||||||
onChange={handleChange}
|
onChange={(e, valueId) =>
|
||||||
|
handleNlpValueChange({ id, name }, valueId)
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: 50,
|
minWidth: 50,
|
||||||
padding: 0,
|
|
||||||
".MuiAutocomplete-input": {
|
".MuiAutocomplete-input": {
|
||||||
minWidth: "100px !important",
|
minWidth: "100px !important",
|
||||||
},
|
},
|
||||||
@ -214,59 +218,67 @@ const NlpPatternSelect = (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
renderInput={(props) => {
|
renderInput={(props) => (
|
||||||
return (
|
<Input
|
||||||
<Input
|
{...props}
|
||||||
{...props}
|
InputProps={{
|
||||||
InputProps={{
|
...props.InputProps,
|
||||||
...props.InputProps,
|
readOnly: true,
|
||||||
readOnly: true,
|
sx: {
|
||||||
sx: {
|
padding: 0,
|
||||||
padding: 0,
|
overflow: "hidden",
|
||||||
overflow: "hidden",
|
cursor: "pointer",
|
||||||
cursor: "pointer",
|
fontSize: "14px",
|
||||||
},
|
},
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment
|
<InputAdornment position="start">
|
||||||
position="start"
|
<Chip
|
||||||
sx={{
|
sx={{
|
||||||
padding: "0 1rem",
|
p: "0 0.3rem",
|
||||||
height: "100%",
|
border: "none",
|
||||||
backgroundColor: theme.palette.grey[200],
|
borderRadius: 0,
|
||||||
}}
|
}}
|
||||||
>
|
color="primary"
|
||||||
{entity.name}
|
label={name}
|
||||||
</InputAdornment>
|
variant="role"
|
||||||
),
|
/>
|
||||||
endAdornment: (
|
</InputAdornment>
|
||||||
<InputAdornment position="end">
|
),
|
||||||
{isLoading ? (
|
endAdornment: (
|
||||||
<CircularProgress color="inherit" size={20} />
|
<InputAdornment position="end">
|
||||||
) : (
|
{isLoading ? (
|
||||||
<IconButton
|
<CircularProgress color="inherit" size={20} />
|
||||||
sx={{ padding: 0 }}
|
) : (
|
||||||
onClick={(e) => {
|
<IconButton
|
||||||
onDelete(e);
|
sx={{ p: 0, pr: "2px" }}
|
||||||
|
onClick={(e) => {
|
||||||
|
onDelete(e);
|
||||||
|
|
||||||
onChange(
|
onChange(
|
||||||
undefined,
|
undefined,
|
||||||
patterns.filter(
|
patterns.filter((p) => p.entity !== name),
|
||||||
(p) => p.entity !== entity.name,
|
);
|
||||||
),
|
}}
|
||||||
);
|
edge="end"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<Cancel
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
transition: ".05s",
|
||||||
|
"&:hover": {
|
||||||
|
color: theme.palette.grey[700],
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
edge="end"
|
htmlColor={theme.palette.grey[500]}
|
||||||
size="small"
|
/>
|
||||||
>
|
</IconButton>
|
||||||
<Cancel htmlColor={theme.palette.grey[500]} />
|
)}
|
||||||
</IconButton>
|
</InputAdornment>
|
||||||
)}
|
),
|
||||||
</InputAdornment>
|
}}
|
||||||
),
|
/>
|
||||||
}}
|
)}
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -276,8 +288,13 @@ const NlpPatternSelect = (
|
|||||||
renderInput={(props) => (
|
renderInput={(props) => (
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
|
sx={{
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
paddingRight: "6px !important",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
label={t("label.nlp")}
|
label={t("label.nlp")}
|
||||||
onChange={(e) => onSearch(e.target.value)}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...props.InputProps,
|
...props.InputProps,
|
||||||
inputRef,
|
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" ? (
|
{patternType === "menu" ? (
|
||||||
<AutoCompleteEntitySelect<IMenuItem, "title", false>
|
<AutoCompleteEntitySelect<IMenuItem, "title", false>
|
||||||
value={pattern ? (pattern as PayloadPattern).value : null}
|
value={pattern ? (pattern as PayloadPattern).value : null}
|
||||||
|
Loading…
Reference in New Issue
Block a user