From 5cda7ae2c62074f67cd35184235ffeaca56490ee Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Mon, 25 Nov 2024 09:51:19 +0100 Subject: [PATCH 01/10] feat: new nlp pattern input (partial) --- docker/docker-compose.yml | 2 +- frontend/public/locales/en/translation.json | 3 +- frontend/public/locales/fr/translation.json | 3 +- .../inputs/NlpPatternSelect.tsx | 248 ++++++++++++++++++ .../form/inputs/triggers/PatternInput.tsx | 2 + frontend/src/types/block.types.ts | 15 +- 6 files changed, 260 insertions(+), 13 deletions(-) create mode 100644 frontend/src/app-components/inputs/NlpPatternSelect.tsx diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 36ebce9..f13c4c4 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,7 +11,7 @@ services: - db-network - app-network volumes: - - api-data:/app/uploads + - ../api/uploads:/app/uploads depends_on: mongo: condition: service_healthy diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 259cd6a..26366b6 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -456,7 +456,8 @@ "is_rtl": "RTL", "original_text": "Original Text", "inputs": "Inputs", - "outputs": "Outputs" + "outputs": "Outputs", + "any": "- Any -" }, "placeholder": { "your_username": "Your username", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index 8ad387c..6b05d9b 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -457,7 +457,8 @@ "is_rtl": "RTL", "original_text": "Texte par défaut", "inputs": "Port d'entré", - "outputs": "Port de sorti" + "outputs": "Port de sorti", + "any": "- Toutes -" }, "placeholder": { "your_username": "Votre nom d'utilisateur", diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx new file mode 100644 index 0000000..3d98f4b --- /dev/null +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -0,0 +1,248 @@ +/* + * 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 { RemoveOutlined } from "@mui/icons-material"; +import { + Box, + CircularProgress, + IconButton, + InputAdornment, + Typography, + useTheme, +} from "@mui/material"; +import Autocomplete from "@mui/material/Autocomplete"; +import { forwardRef, SyntheticEvent, 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"; + +type NlpPatternSelectProps = {}; + +const NlpPatternSelect = ({}: NlpPatternSelectProps, ref) => { + const [selected, setSelected] = useState([]); + 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 value = getNlpValueFromCache(valueId); + + if (!value) { + throw new Error("Unable to find nlp value in cache"); + } + + const newSelection = [...selected]; + const update = newSelection.find(({ entity: e }) => e === entity.name); + + if (!update) { + throw new Error("Unable to find nlp entity"); + } + + if (value.id === entity.id) { + update.match = "entity"; + update.value = entity.name; + } else { + update.match = "value"; + update.value = value.value; + } + }; + + return ( + + selected.find(({ entity: entityName }) => entityName === name), + )} + multiple={true} + options={options} + onChange={handleNlpEntityChange} + renderOption={(props, { name, doc }, { selected }) => ( + + + {name} + + {doc && ( + + {doc} + + )} + + )} + getOptionLabel={({ name }) => name} + isOptionEqualToValue={(option, value) => option.id === value.id} + freeSolo={false} + loading={isLoading} + renderTags={(entities, getTagProps) => ( + + {entities.map((entity, index) => { + const { key, onDelete } = getTagProps({ index }); + const handleChange = ( + event: SyntheticEvent, + valueId: string, + ) => { + handleNlpValueChange(entity, valueId); + }; + + return ( + + option + ? getNlpValueFromCache(option)?.value || "-" + : t("label.any") + } + disableClearable + popupIcon={false} + onChange={handleChange} + sx={{ + minWidth: 50, + padding: 0, + ".MuiAutocomplete-input": { + minWidth: "100px !important", + }, + "& .MuiOutlinedInput-root": { + paddingRight: "2rem !important", + "&.MuiInputBase-sizeSmall": { + padding: "0 !important", + }, + }, + }} + renderInput={(props) => ( + onSearch(e.target.value)} + InputProps={{ + ...props.InputProps, + startAdornment: ( + + {entity.name} + + ), + endAdornment: ( + + {isLoading ? ( + + ) : ( + + + + )} + + ), + }} + /> + )} + /> + ); + })} + + )} + renderInput={(props) => ( + onSearch(e.target.value)} + InputProps={{ + ...props.InputProps, + endAdornment: isLoading ? ( + + ) : null, + }} + /> + )} + /> + ); +}; + +NlpPatternSelect.displayName = "NlpPatternSelect"; + +export default forwardRef(NlpPatternSelect) as ( + props: NlpPatternSelectProps & { + ref?: React.ForwardedRef; + }, +) => ReturnType; diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx index c0152bb..fece843 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -12,6 +12,7 @@ import { RegisterOptions, useFormContext } from "react-hook-form"; import AutoCompleteEntitySelect from "@/app-components/inputs/AutoCompleteEntitySelect"; import { Input } from "@/app-components/inputs/Input"; +import NlpPatternSelect from "@/app-components/inputs/NlpPatternSelect"; import { RegexInput } from "@/app-components/inputs/RegexInput"; import { useGetFromCache } from "@/hooks/crud/useGet"; import { useTranslate } from "@/hooks/useTranslate"; @@ -147,6 +148,7 @@ const PatternInput: FC = ({ + {patternType === "nlp" && } {patternType === "nlp" ? ( value={(pattern as NlpPattern[]).map((v) => diff --git a/frontend/src/types/block.types.ts b/frontend/src/types/block.types.ts index 352117b..5eb686d 100644 --- a/frontend/src/types/block.types.ts +++ b/frontend/src/types/block.types.ts @@ -69,16 +69,11 @@ export interface PayloadPattern { type?: PayloadType; } -export type NlpPattern = - | { - entity: string; - match: "entity"; - } - | { - entity: string; - match: "value"; - value: string; - }; +export type NlpPattern = { + entity: string; + match: "value" | "entity"; + value: string; +}; export type Pattern = null | string | PayloadPattern | NlpPattern[]; From eba199c7d7a7f256a2e89ef135229b7c01ff4d32 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Mon, 25 Nov 2024 19:01:35 +0100 Subject: [PATCH 02/10] update: nlp select pattern v0.0.1 --- .../inputs/NlpPatternSelect.tsx | 229 ++++++++++-------- .../form/inputs/triggers/PatternInput.tsx | 20 +- 2 files changed, 144 insertions(+), 105 deletions(-) diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index 3d98f4b..f8e5782 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -6,7 +6,7 @@ * 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 { RemoveOutlined } from "@mui/icons-material"; +import { Cancel } from "@mui/icons-material"; import { Box, CircularProgress, @@ -27,10 +27,13 @@ import { EntityType, Format } from "@/services/types"; import { NlpPattern } from "@/types/block.types"; import { INlpEntity } from "@/types/nlp-entity.types"; -type NlpPatternSelectProps = {}; +type NlpPatternSelectProps = { patterns: NlpPattern[]; onChange: any }; -const NlpPatternSelect = ({}: NlpPatternSelectProps, ref) => { - const [selected, setSelected] = useState([]); +const NlpPatternSelect = ( + { patterns, onChange }: NlpPatternSelectProps, + ref, +) => { + const [selected, setSelected] = useState(patterns); const theme = useTheme(); const { t } = useTranslate(); const { onSearch, searchPayload } = useSearch({ @@ -69,12 +72,6 @@ const NlpPatternSelect = ({}: NlpPatternSelectProps, ref) => { } const handleNlpValueChange = (entity: INlpEntity, valueId: string) => { - const value = getNlpValueFromCache(valueId); - - if (!value) { - throw new Error("Unable to find nlp value in cache"); - } - const newSelection = [...selected]; const update = newSelection.find(({ entity: e }) => e === entity.name); @@ -82,23 +79,32 @@ const NlpPatternSelect = ({}: NlpPatternSelectProps, ref) => { throw new Error("Unable to find nlp entity"); } - if (value.id === entity.id) { + 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); }; + const defaultValue = + options.filter(({ name }) => + selected.find(({ entity: entityName }) => entityName === name), + ) || {}; return ( - selected.find(({ entity: entityName }) => entityName === name), - )} + defaultValue={defaultValue} multiple={true} options={options} onChange={handleNlpEntityChange} @@ -136,92 +142,121 @@ const NlpPatternSelect = ({}: NlpPatternSelectProps, ref) => { isOptionEqualToValue={(option, value) => option.id === value.id} freeSolo={false} loading={isLoading} - renderTags={(entities, getTagProps) => ( - - {entities.map((entity, index) => { - const { key, onDelete } = getTagProps({ index }); - const handleChange = ( - event: SyntheticEvent, - valueId: string, - ) => { - handleNlpValueChange(entity, valueId); - }; + renderTags={(entities, getTagProps) => { + return ( + + {entities.map((entity, index) => { + const { key, onDelete } = getTagProps({ index }); + const handleChange = ( + event: SyntheticEvent, + valueId: string, + ) => { + handleNlpValueChange(entity, valueId); + }; + const selected = ( + Array.isArray(patterns) + ? patterns?.find((e) => e.entity === entity.name) + : {} + ) as NlpPattern; - return ( - - option - ? getNlpValueFromCache(option)?.value || "-" - : t("label.any") - } - disableClearable - popupIcon={false} - onChange={handleChange} - sx={{ - minWidth: 50, - padding: 0, - ".MuiAutocomplete-input": { - minWidth: "100px !important", - }, - "& .MuiOutlinedInput-root": { - paddingRight: "2rem !important", - "&.MuiInputBase-sizeSmall": { - padding: "0 !important", + return ( + { + const nlpValueCache = getNlpValueFromCache(option); + + if (nlpValueCache) { + return nlpValueCache?.value; + } + + if (selected?.entity === option || option === entity.id) { + return t("label.any"); + } + + return option; + }} + disableClearable + popupIcon={false} + onChange={handleChange} + sx={{ + minWidth: 50, + padding: 0, + ".MuiAutocomplete-input": { + minWidth: "100px !important", }, - }, - }} - renderInput={(props) => ( - onSearch(e.target.value)} - InputProps={{ - ...props.InputProps, - startAdornment: ( - - {entity.name} - - ), - endAdornment: ( - - {isLoading ? ( - - ) : ( - ( + + - - - )} - - ), - }} - /> - )} - /> - ); - })} - - )} + {entity.name} + + + ), + endAdornment: ( + + {isLoading ? ( + + ) : ( + { + onDelete(e); + + onChange( + undefined, + patterns.filter( + (p) => p.entity !== entity.name, + ), + ); + }} + edge="end" + size="small" + > + + + )} + + ), + }} + /> + )} + /> + ); + })} + + ); + }} renderInput={(props) => ( = ({ register, formState: { errors }, } = useFormContext(); - const getNlpEntityFromCache = useGetFromCache(EntityType.NLP_ENTITY); + // const getNlpEntityFromCache = useGetFromCache(EntityType.NLP_ENTITY); const [pattern, setPattern] = useState(value); const [patternType, setPatternType] = useState(getType(value)); const types = [ @@ -148,8 +145,15 @@ const PatternInput: FC = ({ - {patternType === "nlp" && } - {patternType === "nlp" ? ( + {patternType === "nlp" && ( + { + setPattern(data); + }} + /> + )} + {/* {patternType === "nlp" ? ( value={(pattern as NlpPattern[]).map((v) => "value" in v && v.value ? v.value : v.entity, @@ -225,7 +229,7 @@ const PatternInput: FC = ({ }, [] as INlpValue[]); }} /> - ) : null} + ) : null} */} {patternType === "menu" ? ( value={pattern ? (pattern as PayloadPattern).value : null} From 93eb937f7f3dfd8ea96ffd8e9aefe98a4b8467fd Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Tue, 26 Nov 2024 07:12:27 +0100 Subject: [PATCH 03/10] fix: minor fixes --- .../inputs/NlpPatternSelect.tsx | 119 +++++++++++------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index f8e5782..5d241ee 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -12,11 +12,12 @@ import { CircularProgress, IconButton, InputAdornment, + Skeleton, Typography, useTheme, } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; -import { forwardRef, SyntheticEvent, useState } from "react"; +import { forwardRef, SyntheticEvent, useRef, useState } from "react"; import { Input } from "@/app-components/inputs/Input"; import { useFind } from "@/hooks/crud/useFind"; @@ -26,6 +27,7 @@ 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 }; @@ -33,6 +35,7 @@ const NlpPatternSelect = ( { patterns, onChange }: NlpPatternSelectProps, ref, ) => { + const inputRef = useRef(null); const [selected, setSelected] = useState(patterns); const theme = useTheme(); const { t } = useTranslate(); @@ -94,6 +97,13 @@ const NlpPatternSelect = ( onChange(undefined, newSelection); }; + + if (!options.length) { + return ( + + ); + } + const defaultValue = options.filter(({ name }) => selected.find(({ entity: entityName }) => entityName === name), @@ -154,21 +164,23 @@ const NlpPatternSelect = ( {entities.map((entity, index) => { const { key, onDelete } = getTagProps({ index }); const handleChange = ( - event: SyntheticEvent, + _event: SyntheticEvent, valueId: string, ) => { handleNlpValueChange(entity, valueId); }; - const selected = ( - Array.isArray(patterns) - ? patterns?.find((e) => e.entity === entity.name) - : {} - ) as NlpPattern; + 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 ( ( - + renderInput={(props) => { + return ( + {entity.name} - - ), - endAdornment: ( - - {isLoading ? ( - - ) : ( - { - onDelete(e); + ), + endAdornment: ( + + {isLoading ? ( + + ) : ( + { + onDelete(e); - onChange( - undefined, - patterns.filter( - (p) => p.entity !== entity.name, - ), - ); - }} - edge="end" - size="small" - > - - - )} - - ), - }} - /> - )} + onChange( + undefined, + patterns.filter( + (p) => p.entity !== entity.name, + ), + ); + }} + edge="end" + size="small" + > + + + )} + + ), + }} + /> + ); + }} /> ); })} @@ -264,6 +280,13 @@ const NlpPatternSelect = ( onChange={(e) => onSearch(e.target.value)} InputProps={{ ...props.InputProps, + inputRef, + onClick: (event) => { + if (event.target !== inputRef.current) { + event.stopPropagation(); + event.preventDefault(); + } + }, endAdornment: isLoading ? ( ) : null, From 3c5a45fca5e76fd08d93babc21fa01e7fe00c2aa Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 26 Nov 2024 12:17:54 +0100 Subject: [PATCH 04/10] refactor: nlp select pattern v2 --- .../inputs/NlpPatternSelect.tsx | 187 ++++++++++-------- .../form/inputs/triggers/PatternInput.tsx | 77 -------- 2 files changed, 102 insertions(+), 162 deletions(-) diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index 5d241ee..8ef8ba1 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -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 | undefined, + patterns: NlpPattern[], + ) => void; +}; 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({ + const { searchPayload } = useSearch({ $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, 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, + 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, - 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 ( { @@ -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 ( - ( + + - {entity.name} - - ), - endAdornment: ( - - {isLoading ? ( - - ) : ( - { - onDelete(e); + color="primary" + label={name} + variant="role" + /> + + ), + endAdornment: ( + + {isLoading ? ( + + ) : ( + { + onDelete(e); - onChange( - undefined, - patterns.filter( - (p) => p.entity !== entity.name, - ), - ); + onChange( + undefined, + patterns.filter((p) => p.entity !== name), + ); + }} + edge="end" + size="small" + > + - - - )} - - ), - }} - /> - ); - }} + htmlColor={theme.palette.grey[500]} + /> + + )} + + ), + }} + /> + )} /> ); })} @@ -276,8 +288,13 @@ const NlpPatternSelect = ( renderInput={(props) => ( onSearch(e.target.value)} InputProps={{ ...props.InputProps, inputRef, diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx index d80f3b1..5791f2d 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -153,83 +153,6 @@ const PatternInput: FC = ({ }} /> )} - {/* {patternType === "nlp" ? ( - - 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) => ( -
  • - - {params.group} - - {params.children} -
  • - )} - 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" ? ( value={pattern ? (pattern as PayloadPattern).value : null} From d4c3e59e620070706d0d3f6cbc55d8eb40b12959 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 26 Nov 2024 12:18:27 +0100 Subject: [PATCH 05/10] fix: blockForm reset values --- .../components/visual-editor/BlockDialog.tsx | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/visual-editor/BlockDialog.tsx b/frontend/src/components/visual-editor/BlockDialog.tsx index 5088621..25991b5 100644 --- a/frontend/src/components/visual-editor/BlockDialog.tsx +++ b/frontend/src/components/visual-editor/BlockDialog.tsx @@ -35,7 +35,7 @@ import { useTranslate } from "@/hooks/useTranslate"; import { EntityType } from "@/services/types"; import { OutgoingMessageFormat } from "@/types/message.types"; -import { IBlockAttributes, IBlock } from "../../types/block.types"; +import { IBlock, IBlockAttributes } from "../../types/block.types"; import BlockFormProvider from "./form/BlockFormProvider"; import { MessageForm } from "./form/MessageForm"; @@ -69,24 +69,25 @@ const BlockDialog: FC = ({ toast.success(t("message.success_save")); }, }); - const methods = useForm({ - defaultValues: { - name: block?.name || "", - patterns: block?.patterns || [], - trigger_labels: block?.trigger_labels || [], - trigger_channels: block?.trigger_channels || [], - options: block?.options || { - typing: 0, - content: { - display: OutgoingMessageFormat.list, - top_element_style: "compact", - limit: 2, - }, - assignTo: block?.options?.assignTo, + const DEFAULT_VALUES = { + name: block?.name || "", + patterns: block?.patterns || [], + trigger_labels: block?.trigger_labels || [], + trigger_channels: block?.trigger_channels || [], + options: block?.options || { + typing: 0, + content: { + display: OutgoingMessageFormat.list, + top_element_style: "compact", + limit: 2, }, - assign_labels: block?.assign_labels || [], - message: block?.message || [""], + assignTo: block?.options?.assignTo, }, + assign_labels: block?.assign_labels || [], + message: block?.message || [""], + } as IBlockAttributes; + const methods = useForm({ + defaultValues: DEFAULT_VALUES, }); const { reset, @@ -114,10 +115,8 @@ const BlockDialog: FC = ({ }, [open, reset]); useEffect(() => { - if (block) { - reset({ - name: block.name, - }); + if (block && open) { + reset(DEFAULT_VALUES); } else { reset(); } From 638b405bb05d70b9884063e92be43d35e4309cec Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 26 Nov 2024 13:25:53 +0100 Subject: [PATCH 06/10] fix: multiple autocomplete rendering --- frontend/src/app-components/inputs/NlpPatternSelect.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index 8ef8ba1..e42e2ee 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -121,7 +121,7 @@ const NlpPatternSelect = ( ref={ref} size="medium" disabled={options.length === 0} - defaultValue={defaultValue} + value={defaultValue} multiple={true} options={options} onChange={handleNlpEntityChange} @@ -183,7 +183,7 @@ const NlpPatternSelect = ( return ( Date: Wed, 27 Nov 2024 06:37:19 +0100 Subject: [PATCH 07/10] fix: remove unused event prop --- .../src/app-components/inputs/NlpPatternSelect.tsx | 10 +++------- .../form/inputs/triggers/PatternInput.tsx | 4 +--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index e42e2ee..63b193b 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -32,10 +32,7 @@ import { INlpValue } from "@/types/nlp-value.types"; type NlpPatternSelectProps = { patterns: NlpPattern[]; - onChange: ( - _event: SyntheticEvent | undefined, - patterns: NlpPattern[], - ) => void; + onChange: (patterns: NlpPattern[]) => void; }; const NlpPatternSelect = ( @@ -76,7 +73,7 @@ const NlpPatternSelect = ( ), ]; - onChange(undefined, newSelection); + onChange(newSelection); }; const handleNlpValueChange = ( { id, name }: Pick, @@ -102,7 +99,7 @@ const NlpPatternSelect = ( update.value = value.value; } - onChange(undefined, newSelection); + onChange(newSelection); }; if (!options.length) { @@ -255,7 +252,6 @@ const NlpPatternSelect = ( onDelete(e); onChange( - undefined, patterns.filter((p) => p.entity !== name), ); }} diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx index 5791f2d..0bab40f 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -148,9 +148,7 @@ const PatternInput: FC = ({ {patternType === "nlp" && ( { - setPattern(data); - }} + onChange={setPattern} /> )} {patternType === "menu" ? ( From 3871464486d85d387f39b0c0e71de976705f9e93 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 06:40:49 +0100 Subject: [PATCH 08/10] fix: add fallback and capture_vars defaultValues --- frontend/src/components/visual-editor/BlockDialog.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/components/visual-editor/BlockDialog.tsx b/frontend/src/components/visual-editor/BlockDialog.tsx index 25991b5..27bb00e 100644 --- a/frontend/src/components/visual-editor/BlockDialog.tsx +++ b/frontend/src/components/visual-editor/BlockDialog.tsx @@ -82,9 +82,15 @@ const BlockDialog: FC = ({ limit: 2, }, assignTo: block?.options?.assignTo, + fallback: block?.options?.fallback || { + active: true, + message: [], + max_attempts: 1, + }, }, assign_labels: block?.assign_labels || [], message: block?.message || [""], + capture_vars: block?.capture_vars || [], } as IBlockAttributes; const methods = useForm({ defaultValues: DEFAULT_VALUES, From 2d97ed3bd0c318192d95ad92901cd3b3e8660d2f Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 06:42:52 +0100 Subject: [PATCH 09/10] fix: adjust i18n translations --- frontend/public/locales/fr/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index 6b05d9b..cffef81 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -456,8 +456,8 @@ "is_default": "Par Défaut", "is_rtl": "RTL", "original_text": "Texte par défaut", - "inputs": "Port d'entré", - "outputs": "Port de sorti", + "inputs": "Ports d'entrée", + "outputs": "Ports de sortie", "any": "- Toutes -" }, "placeholder": { From 3de40a22824f0488cfb377fe3620ec6e2b7aa83d Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 08:35:17 +0100 Subject: [PATCH 10/10] fix: docker compose volume syntax --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f13c4c4..36ebce9 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,7 +11,7 @@ services: - db-network - app-network volumes: - - ../api/uploads:/app/uploads + - api-data:/app/uploads depends_on: mongo: condition: service_healthy