diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 96af5d5..37b15c0 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -232,7 +232,7 @@ "capture_context_vars": "Capture context variables?", "block_event_type": "Type of event", "patterns": "Patterns", - "no_patterns": "- No patterns -", + "no_patterns": "- No triggers -", "text_patterns": " Text Patterns", "triggers": "Triggers", "payloads": "Payloads", @@ -558,7 +558,7 @@ "media_library": "Media Library", "manage_roles": "Manage Roles", "connect_with_sso": "Connect with SSO", - "add_pattern": "Add pattern", + "add_pattern": "New Trigger", "mark_as_default": "Mark as Default", "toggle": "Toggle button" }, diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index ab90512..6018897 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -559,7 +559,7 @@ "media_library": "Bibliothéque Media", "manage_roles": "Gérer les rôles", "connect_with_sso": "Se connecter avec SSO", - "add_pattern": "Ajouter un motif", + "add_pattern": "Ajouter un déclencheur", "mark_as_default": "Par Défaut", "toggle": "Bouton de bascule" }, diff --git a/frontend/src/app-components/buttons/DropdownButton.tsx b/frontend/src/app-components/buttons/DropdownButton.tsx new file mode 100644 index 0000000..79dacb0 --- /dev/null +++ b/frontend/src/app-components/buttons/DropdownButton.tsx @@ -0,0 +1,96 @@ +/* + * 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 { ArrowDropDown } from "@mui/icons-material"; +import { + Box, + Button, + List, + ListItemButton, + ListItemIcon, + ListItemText, + Popover, + SxProps, + Theme, +} from "@mui/material"; +import React, { useState } from "react"; + +export interface DropdownButtonAction { + icon: React.ReactNode; + name: string; + defaultValue: any; +} + +interface AddPatternProps { + actions: DropdownButtonAction[]; + onClick: (action: DropdownButtonAction) => void; + label?: string; + icon?: React.ReactNode; + sx?: SxProps | undefined; +} + +const DropdownButton: React.FC = ({ + actions, + onClick, + label = "Add", + icon, + sx, +}) => { + const [anchorEl, setAnchorEl] = useState(null); + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleAddFieldset = (action: DropdownButtonAction) => { + onClick(action); + handleClose(); + }; + const open = Boolean(anchorEl); + + return ( + + + + + {actions.map((action, index) => ( + handleAddFieldset(action)} + > + {action.icon} + + + ))} + + + + ); +}; + +export default DropdownButton; diff --git a/frontend/src/app-components/inputs/NlpPatternSelect.tsx b/frontend/src/app-components/inputs/NlpPatternSelect.tsx index 63b193b..142a5d0 100644 --- a/frontend/src/app-components/inputs/NlpPatternSelect.tsx +++ b/frontend/src/app-components/inputs/NlpPatternSelect.tsx @@ -117,6 +117,7 @@ const NlpPatternSelect = ( { return typeof str === "string" && str.startsWith("/") && str.endsWith("/"); }; @@ -65,16 +66,9 @@ const PatternInput: FC = ({ register, formState: { errors }, } = useFormContext(); - // const getNlpEntityFromCache = useGetFromCache(EntityType.NLP_ENTITY); const [pattern, setPattern] = useState(value); - const [patternType, setPatternType] = useState(getType(value)); + const patternType = getType(value); const isPostbackType = ["payload", "content", "menu"].includes(patternType); - const types = [ - { value: "text", label: t("label.match_sound") }, - { value: "regex", label: t("label.regex") }, - { value: "payload", label: t("label.postback") }, - { value: "nlp", label: t("label.nlp") }, - ]; const registerInput = ( errorMessage: string, idx: number, @@ -100,53 +94,15 @@ const PatternInput: FC = ({ }, [pattern]); return ( - <> - - { - const selected = e.target.value as PatternType; - - switch (selected) { - case "regex": { - setPattern("//"); - break; - } - case "nlp": { - setPattern([]); - break; - } - case "menu": - case "content": - case "payload": { - setPattern(null); - break; - } - default: { - setPattern(""); - } - } - - setPatternType(selected); - }} - > - {types.map((item) => ( - - {item.label} - - ))} - - - - {patternType === "nlp" && ( - - )} - {isPostbackType ? ( + + {patternType === "nlp" && ( + + )} + + {isPostbackType ? ( { payload && setPattern(payload); @@ -154,40 +110,39 @@ const PatternInput: FC = ({ defaultValue={pattern as PayloadPattern} /> ) : null} - {typeof value === "string" && patternType === "regex" ? ( - { - try { - const parsedPattern = new RegExp(pattern.slice(1, -1)); + {typeof value === "string" && patternType === "regex" ? ( + { + try { + const parsedPattern = new RegExp(pattern.slice(1, -1)); - if (String(parsedPattern) !== pattern) { - throw t("message.regex_is_invalid"); - } - - return true; - } catch (_e) { - return t("message.regex_is_invalid"); + if (String(parsedPattern) !== pattern) { + throw t("message.regex_is_invalid"); } - }, - setValueAs: (v) => (isRegex(v) ? v : `/${v}/`), - })} - label={t("label.regex")} - value={value.slice(1, -1)} - onChange={(v) => onChange(v)} - required - /> - ) : null} - {typeof value === "string" && patternType === "text" ? ( - onChange(e.target.value)} - /> - ) : null} - - + + return true; + } catch (_e) { + return t("message.regex_is_invalid"); + } + }, + setValueAs: (v) => (isRegex(v) ? v : `/${v}/`), + })} + label={t("label.regex")} + value={value.slice(1, -1)} + onChange={(v) => onChange(v)} + required + /> + ) : null} + {typeof value === "string" && patternType === "text" ? ( + onChange(e.target.value)} + /> + ) : null} + ); }; diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PatternsInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PatternsInput.tsx index 87c6fe4..2bd49da 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternsInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternsInput.tsx @@ -6,12 +6,21 @@ * 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 AddIcon from "@mui/icons-material/Add"; -import DeleteIcon from "@mui/icons-material/Delete"; -import { Box, Button, Grid, IconButton, styled } from "@mui/material"; -import { FC, Fragment, useEffect, useState } from "react"; +import { + Abc, + Add, + Mouse, + PsychologyAlt, + RemoveCircleOutline, + Spellcheck, +} from "@mui/icons-material"; +import { Box, IconButton, styled } from "@mui/material"; +import { FC, useEffect, useState } from "react"; import { useFormContext } from "react-hook-form"; +import DropdownButton, { + DropdownButtonAction, +} from "@/app-components/buttons/DropdownButton"; import { useTranslate } from "@/hooks/useTranslate"; import { Pattern } from "@/types/block.types"; import { SXStyleOptions } from "@/utils/SXStyleOptions"; @@ -21,11 +30,6 @@ import { getInputControls } from "../../utils/inputControls"; import PatternInput from "./PatternInput"; -type PatternsInputProps = { - value: Pattern[]; - onChange: (patterns: Pattern[]) => void; - minInput: number; -}; const StyledNoPatternsDiv = styled("div")( SXStyleOptions({ color: "grey.500", @@ -35,6 +39,19 @@ const StyledNoPatternsDiv = styled("div")( width: "100%", }), ); +const actions: DropdownButtonAction[] = [ + { icon: , name: "Exact Match", defaultValue: "" }, + { icon: , name: "Pattern Match", defaultValue: "//" }, + { icon: , name: "Intent Match", defaultValue: [] }, + { icon: , name: "Interaction", defaultValue: {} }, +]; + +type PatternsInputProps = { + value: Pattern[]; + onChange: (patterns: Pattern[]) => void; + minInput: number; +}; + const PatternsInput: FC = ({ value, onChange }) => { const { t } = useTranslate(); const [patterns, setPatterns] = useState[]>( @@ -44,8 +61,8 @@ const PatternsInput: FC = ({ value, onChange }) => { register, formState: { errors }, } = useFormContext(); - const addInput = () => { - setPatterns([...patterns, createValueWithId("")]); + const addInput = (defaultValue: Pattern) => { + setPatterns([...patterns, createValueWithId(defaultValue)]); }; const removeInput = (index: number) => { const updatedPatterns = [...patterns]; @@ -64,18 +81,13 @@ const PatternsInput: FC = ({ value, onChange }) => { }, [patterns]); return ( - - + + {patterns.length == 0 ? ( {t("label.no_patterns")} ) : ( patterns.map(({ value, id }, idx) => ( - - - removeInput(idx)}> - - - + = ({ value, onChange }) => { t("message.text_is_required"), )} /> - + removeInput(idx)}> + + + )) )} - - + + addInput(action.defaultValue as Pattern)} + icon={} + /> ); };