refactor: nlp select pattern v2

This commit is contained in:
yassinedorbozgithub 2024-11-26 12:17:54 +01:00
parent 93eb937f7f
commit 3c5a45fca5
2 changed files with 102 additions and 162 deletions

View File

@ -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,

View File

@ -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}