From 2c2cbb94b1c9c7dcfe21a8312ed0fe1474d3b3f2 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 12:52:28 +0100 Subject: [PATCH 01/14] update: unify postback types --- .../form/inputs/triggers/PatternInput.tsx | 49 +--- .../form/inputs/triggers/PostbackInputV2.tsx | 234 ++++++++++++++++++ 2 files changed, 242 insertions(+), 41 deletions(-) create mode 100644 frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx 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 0bab40f0..3e7f3ed4 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -10,12 +10,10 @@ import { Grid, MenuItem, TextFieldProps } from "@mui/material"; import { FC, useEffect, useState } from "react"; 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 { useTranslate } from "@/hooks/useTranslate"; -import { EntityType, Format } from "@/services/types"; import { IBlockAttributes, IBlockFull, @@ -24,10 +22,8 @@ import { PatternType, PayloadPattern, } from "@/types/block.types"; -import { IMenuItem } from "@/types/menu.types"; -import { ContentPostbackInput } from "./ContentPostbackInput"; -import { PostbackInput } from "./PostbackInput"; +import { PostbackInputV2 } from "./PostbackInputV2"; const isRegex = (str: Pattern) => { return typeof str === "string" && str.startsWith("/") && str.endsWith("/"); @@ -77,8 +73,6 @@ const PatternInput: FC = ({ { value: "regex", label: t("label.regex") }, { value: "payload", label: t("label.postback") }, { value: "nlp", label: t("label.nlp") }, - { value: "menu", label: t("label.menu") }, - { value: "content", label: t("label.content") }, ]; const registerInput = ( errorMessage: string, @@ -110,7 +104,11 @@ const PatternInput: FC = ({ { const selected = e.target.value as PatternType; @@ -151,39 +149,8 @@ const PatternInput: FC = ({ onChange={setPattern} /> )} - {patternType === "menu" ? ( - - value={pattern ? (pattern as PayloadPattern).value : null} - searchFields={["title"]} - entity={EntityType.MENU} - format={Format.BASIC} - idKey="payload" - labelKey="title" - label={t("label.menu")} - multiple={false} - onChange={(_e, menuItem) => { - menuItem && - setPattern({ - label: menuItem?.title, - value: menuItem?.payload, - type: "menu", - } as PayloadPattern); - }} - preprocess={(items) => { - return items.filter((item) => "payload" in item); - }} - /> - ) : null} - {patternType === "content" ? ( - { - payload && setPattern(payload); - }} - value={pattern ? (pattern as PayloadPattern).value : null} - /> - ) : null} - {patternType === "payload" ? ( - { payload && setPattern(payload); }} diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx new file mode 100644 index 00000000..ce692a5d --- /dev/null +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx @@ -0,0 +1,234 @@ +/* + * 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 { Box, Skeleton, Typography } from "@mui/material"; +import { useMemo } from "react"; + +import AutoCompleteSelect from "@/app-components/inputs/AutoCompleteSelect"; +import { useFind } from "@/hooks/crud/useFind"; +import { useGetFromCache } from "@/hooks/crud/useGet"; +import { useTranslate } from "@/hooks/useTranslate"; +import { EntityType, Format } from "@/services/types"; +import { IBlock, PayloadPattern } from "@/types/block.types"; +import { + PostBackButton, + StdOutgoingButtonsMessage, + StdOutgoingQuickRepliesMessage, + StdQuickReply, +} from "@/types/message.types"; + +import { useBlock } from "../../BlockFormProvider"; + +type PayloadOption = { + id: string; + label: string; + group?: string; +}; + +type ContentPayloadOption = { + id: string; + label: string; + group?: string; +}; + +type PostbackInputProps = { + value?: string | null; + onChange: (pattern: PayloadPattern) => void; +}; + +export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { + const block = useBlock(); + const getBlockFromCache = useGetFromCache(EntityType.BLOCK); + const { data: menu } = useFind( + { entity: EntityType.MENU, format: Format.FULL }, + { hasCount: false }, + ); + const { data: contents } = useFind( + { entity: EntityType.CONTENT, format: Format.FULL }, + { + hasCount: false, + }, + ); + const { t } = useTranslate(); + // General options + const generalOptions = [ + { + id: "GET_STARTED", + label: t("label.get_started"), + group: t("label.general"), + }, + + { + id: "VIEW_MORE", + label: t("label.view_more"), + group: t("label.general"), + }, + { + id: "LOCATION", + label: t("label.location"), + group: t("label.general"), + }, + ]; + // Gather previous blocks buttons + const btnOptions = useMemo( + () => + (block?.previousBlocks || []) + .map((b) => getBlockFromCache(b)) + .filter((b) => { + return b && typeof b.message === "object" && "buttons" in b.message; + }) + .map((b) => b as IBlock) + .reduce((acc, b) => { + const postbackButtons = ( + (b.message as StdOutgoingButtonsMessage)?.buttons || [] + ) + .filter((btn) => btn.type === "postback") + .map((btn) => { + return { ...btn, group: b.name }; + }); + + return acc.concat(postbackButtons); + }, [] as (PostBackButton & { group: string })[]) + .map((btn) => { + return { + id: btn.payload, + label: btn.title, + group: btn.group, + }; + }), + [block?.previousBlocks, getBlockFromCache], + ); + // Gather previous blocks quick replies + const qrOptions = useMemo( + () => + (block?.previousBlocks || []) + .map((b) => getBlockFromCache(b)) + .filter((b) => { + return ( + b && typeof b.message === "object" && "quickReplies" in b.message + ); + }) + .map((b) => b as IBlock) + .reduce((acc, b) => { + const postbackQuickReplies = ( + (b.message as StdOutgoingQuickRepliesMessage)?.quickReplies || [] + ) + .filter((btn) => btn.content_type === "text") + .map((btn) => { + return { ...btn, group: b.name }; + }); + + return acc.concat(postbackQuickReplies); + }, [] as (StdQuickReply & { group: string })[]) + .map((btn) => { + return { + id: btn.payload as string, + label: btn.title as string, + group: btn.group, + }; + }), + [block?.previousBlocks], + ); + const menuOptions = menu + .filter((menu) => menu.payload) + .map(({ payload, title }) => ({ + id: payload as string, + label: title as string, + group: "menu", + })); + const contentOptions = useMemo( + () => + (block?.previousBlocks || []) + .map((bId) => getBlockFromCache(bId)) + .filter((b) => { + return ( + b && + b.options?.content?.entity && + b.options.content.buttons.length > 0 + ); + }) + .map((b) => b as IBlock) + .map((b) => { + const availableContents = (contents || []).filter( + ({ entity, status }) => + status && entity === b.options?.content?.entity, + ); + + return (b.options?.content?.buttons || []).reduce((payloads, btn) => { + // Return a payload for each node/button combination + payloads.push({ + id: btn.title, + label: btn.title, + group: "content", + }); + + return availableContents.reduce((acc, n) => { + acc.push({ + id: n.title, + label: n.title, + group: "content", + }); + + return acc; + }, payloads); + }, [] as ContentPayloadOption[]); + }) + .flat(), + [block?.previousBlocks, contents, getBlockFromCache], + ); + // Concat all previous blocks + const options = [ + ...generalOptions, + ...btnOptions, + ...qrOptions, + ...menuOptions, + ...contentOptions, + ]; + const existOption = options.find((e) => e.id === value); + + if (!existOption) { + return ( + + ); + } + + return ( + <> + + value={value} + options={options} + labelKey="label" + label={t("label.postback")} + multiple={false} + onChange={(_e, content) => { + if (content) { + onChange({ + label: content.label, + value: content.id, + type: ["content", "menu"].includes(content.group || "") + ? content.group + : undefined, + } as PayloadPattern); + } + }} + groupBy={(option) => { + return option.group ?? t("label.other"); + }} + getOptionLabel={({ group, label }) => `${group}:${label}`} + renderGroup={(params) => ( +
  • + + {params.group} + + {params.children} +
  • + )} + /> + + ); +}; From e9633b4614103e6420e8249f519531562aa238f5 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 14:39:35 +0100 Subject: [PATCH 02/14] update: Skeleton height --- .../visual-editor/form/inputs/triggers/PostbackInputV2.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx index ce692a5d..68dccd11 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx @@ -193,7 +193,7 @@ export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { if (!existOption) { return ( - + ); } From 2a3290be4b92538a77df43930768bb8c89752804 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 14:43:40 +0100 Subject: [PATCH 03/14] fix: remove unused components --- .../inputs/triggers/ContentPostbackInput.tsx | 113 ------------- .../form/inputs/triggers/PostbackInput.tsx | 152 ------------------ 2 files changed, 265 deletions(-) delete mode 100644 frontend/src/components/visual-editor/form/inputs/triggers/ContentPostbackInput.tsx delete mode 100644 frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/ContentPostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/ContentPostbackInput.tsx deleted file mode 100644 index a297d12e..00000000 --- a/frontend/src/components/visual-editor/form/inputs/triggers/ContentPostbackInput.tsx +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 { Box, Typography } from "@mui/material"; -import { useMemo } from "react"; - -import AutoCompleteSelect from "@/app-components/inputs/AutoCompleteSelect"; -import { useFind } from "@/hooks/crud/useFind"; -import { useGetFromCache } from "@/hooks/crud/useGet"; -import { useTranslate } from "@/hooks/useTranslate"; -import { EntityType, Format } from "@/services/types"; -import { IBlock, PayloadPattern } from "@/types/block.types"; - -import { useBlock } from "../../BlockFormProvider"; - -type ContentPayloadOption = { - id: string; - label: string; -}; - -type ContentPostbackInputProps = { - value?: string | null; - onChange: (pattern: PayloadPattern) => void; -}; - -export const ContentPostbackInput = ({ - value, - onChange, -}: ContentPostbackInputProps) => { - const { t } = useTranslate(); - const block = useBlock(); - const { data: contents } = useFind( - { entity: EntityType.CONTENT, format: Format.FULL }, - { - hasCount: false, - }, - ); - const getBlockFromCache = useGetFromCache(EntityType.BLOCK); - const options = useMemo( - () => - (block?.previousBlocks || []) - .map((bId) => getBlockFromCache(bId)) - .filter((b) => { - return ( - b && - b.options?.content?.entity && - b.options.content.buttons.length > 0 - ); - }) - .map((b) => b as IBlock) - .map((b) => { - const availableContents = (contents || []).filter( - ({ entity, status }) => - status && entity === b.options?.content?.entity, - ); - - return (b.options?.content?.buttons || []).reduce((payloads, btn) => { - // Return a payload for each node/button combination - payloads.push({ - id: btn.title, - label: btn.title, - }); - - return availableContents.reduce((acc, n) => { - acc.push({ - id: btn.title + ":" + n.title, - label: btn.title + ":" + n.title, - }); - - return acc; - }, payloads); - }, [] as ContentPayloadOption[]); - }) - .flat(), - [block?.previousBlocks, contents, getBlockFromCache], - ); - - return ( - - value={value} - options={options} - labelKey="label" - label={t("label.content")} - multiple={false} - onChange={(_e, content) => { - content && - onChange({ - label: content.label, - value: content.id, - type: "content", - } as PayloadPattern); - }} - groupBy={(option) => { - const [btn] = option.label.split(":"); - - return btn; - }} - renderGroup={(params) => ( -
  • - - {params.group} - - {params.children} -
  • - )} - /> - ); -}; diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx deleted file mode 100644 index 5fbd96b1..00000000 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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 { Box, Typography } from "@mui/material"; -import { useMemo } from "react"; - -import AutoCompleteSelect from "@/app-components/inputs/AutoCompleteSelect"; -import { useGetFromCache } from "@/hooks/crud/useGet"; -import { useTranslate } from "@/hooks/useTranslate"; -import { EntityType } from "@/services/types"; -import { IBlock, PayloadPattern } from "@/types/block.types"; -import { - PostBackButton, - StdOutgoingButtonsMessage, - StdOutgoingQuickRepliesMessage, - StdQuickReply, -} from "@/types/message.types"; - -import { useBlock } from "../../BlockFormProvider"; - -type PayloadOption = { - id: string; - label: string; - group?: string; -}; - -type PostbackInputProps = { - value?: string | null; - onChange: (pattern: PayloadPattern) => void; -}; - -export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { - const block = useBlock(); - const getBlockFromCache = useGetFromCache(EntityType.BLOCK); - const { t } = useTranslate(); - // General options - const generalOptions = [ - { - id: "GET_STARTED", - label: t("label.get_started"), - group: t("label.general"), - }, - - { - id: "VIEW_MORE", - label: t("label.view_more"), - group: t("label.general"), - }, - { - id: "LOCATION", - label: t("label.location"), - group: t("label.general"), - }, - ]; - // Gather previous blocks buttons - const btnOptions = useMemo( - () => - (block?.previousBlocks || []) - .map((b) => getBlockFromCache(b)) - .filter((b) => { - return b && typeof b.message === "object" && "buttons" in b.message; - }) - .map((b) => b as IBlock) - .reduce((acc, b) => { - const postbackButtons = ( - (b.message as StdOutgoingButtonsMessage)?.buttons || [] - ) - .filter((btn) => btn.type === "postback") - .map((btn) => { - return { ...btn, group: b.name }; - }); - - return acc.concat(postbackButtons); - }, [] as (PostBackButton & { group: string })[]) - .map((btn) => { - return { - id: btn.payload, - label: btn.title, - group: btn.group, - }; - }), - [block?.previousBlocks, getBlockFromCache], - ); - // Gather previous blocks quick replies - const qrOptions = useMemo( - () => - (block?.previousBlocks || []) - .map((b) => getBlockFromCache(b)) - .filter((b) => { - return ( - b && typeof b.message === "object" && "quickReplies" in b.message - ); - }) - .map((b) => b as IBlock) - .reduce((acc, b) => { - const postbackQuickReplies = ( - (b.message as StdOutgoingQuickRepliesMessage)?.quickReplies || [] - ) - .filter((btn) => btn.content_type === "text") - .map((btn) => { - return { ...btn, group: b.name }; - }); - - return acc.concat(postbackQuickReplies); - }, [] as (StdQuickReply & { group: string })[]) - .map((btn) => { - return { - id: btn.payload as string, - label: btn.title as string, - group: btn.group, - }; - }), - [block?.previousBlocks], - ); - // Concat all previous blocks - const options = [...generalOptions, ...btnOptions, ...qrOptions]; - - return ( - <> - - value={value} - options={options} - labelKey="label" - label={t("label.postback")} - multiple={false} - onChange={(_e, content) => { - content && - onChange({ - label: content.label, - value: content.id, - } as PayloadPattern); - }} - groupBy={(option) => { - return option.group ?? t("label.other"); - }} - renderGroup={(params) => ( -
  • - - {params.group} - - {params.children} -
  • - )} - /> - - ); -}; From 8ac62d796f71b624f050731efb3f94b40bcb9cc0 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 14:48:28 +0100 Subject: [PATCH 04/14] fix: code syntax --- .../form/inputs/triggers/PatternInput.tsx | 4 ++-- .../{PostbackInputV2.tsx => PostbackInput.tsx} | 17 +++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) rename frontend/src/components/visual-editor/form/inputs/triggers/{PostbackInputV2.tsx => PostbackInput.tsx} (95%) 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 3e7f3ed4..1f63454c 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -23,7 +23,7 @@ import { PayloadPattern, } from "@/types/block.types"; -import { PostbackInputV2 } from "./PostbackInputV2"; +import { PostbackInput } from "./PostbackInput"; const isRegex = (str: Pattern) => { return typeof str === "string" && str.startsWith("/") && str.endsWith("/"); @@ -150,7 +150,7 @@ const PatternInput: FC = ({ /> )} {["payload", "content", "menu"].includes(patternType) ? ( - { payload && setPattern(payload); }} diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx similarity index 95% rename from frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx rename to frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index 68dccd11..a326dd1f 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInputV2.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -30,18 +30,12 @@ type PayloadOption = { group?: string; }; -type ContentPayloadOption = { - id: string; - label: string; - group?: string; -}; - type PostbackInputProps = { value?: string | null; onChange: (pattern: PayloadPattern) => void; }; -export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { +export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { const block = useBlock(); const getBlockFromCache = useGetFromCache(EntityType.BLOCK); const { data: menu } = useFind( @@ -176,7 +170,7 @@ export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { return acc; }, payloads); - }, [] as ContentPayloadOption[]); + }, [] as PayloadOption[]); }) .flat(), [block?.previousBlocks, contents, getBlockFromCache], @@ -189,9 +183,9 @@ export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { ...menuOptions, ...contentOptions, ]; - const existOption = options.find((e) => e.id === value); + const isOptionsReady = options.find((e) => e.id === value); - if (!existOption) { + if (!isOptionsReady) { return ( ); @@ -206,7 +200,7 @@ export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { label={t("label.postback")} multiple={false} onChange={(_e, content) => { - if (content) { + content && onChange({ label: content.label, value: content.id, @@ -214,7 +208,6 @@ export const PostbackInputV2 = ({ value, onChange }: PostbackInputProps) => { ? content.group : undefined, } as PayloadPattern); - } }} groupBy={(option) => { return option.group ?? t("label.other"); From 3b72ad8ded5c94352bbc4098cba3a267cadec58a Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 14:53:25 +0100 Subject: [PATCH 05/14] fix: add new postback infinite Skeleton loading --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index a326dd1f..82022f23 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -183,7 +183,7 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { ...menuOptions, ...contentOptions, ]; - const isOptionsReady = options.find((e) => e.id === value); + const isOptionsReady = !value || options.find((e) => e.id === value); if (!isOptionsReady) { return ( From 28dc3e0e4fd4e3910eceb5fc62b7046be22ad5e8 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 15:13:04 +0100 Subject: [PATCH 06/14] fix: feedback updates v0.0.1 --- .../visual-editor/form/inputs/triggers/PatternInput.tsx | 9 +++------ .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 9 deletions(-) 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 1f63454c..2e9f4a6c 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -68,6 +68,7 @@ const PatternInput: FC = ({ // const getNlpEntityFromCache = useGetFromCache(EntityType.NLP_ENTITY); const [pattern, setPattern] = useState(value); const [patternType, setPatternType] = useState(getType(value)); + const isPostbackType = ["payload", "content", "menu"].includes(patternType); const types = [ { value: "text", label: t("label.match_sound") }, { value: "regex", label: t("label.regex") }, @@ -104,11 +105,7 @@ const PatternInput: FC = ({ { const selected = e.target.value as PatternType; @@ -149,7 +146,7 @@ const PatternInput: FC = ({ onChange={setPattern} /> )} - {["payload", "content", "menu"].includes(patternType) ? ( + {isPostbackType ? ( { payload && setPattern(payload); diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index 82022f23..e609afb8 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -16,6 +16,8 @@ import { useTranslate } from "@/hooks/useTranslate"; import { EntityType, Format } from "@/services/types"; import { IBlock, PayloadPattern } from "@/types/block.types"; import { + ButtonType, + PayloadType, PostBackButton, StdOutgoingButtonsMessage, StdOutgoingQuickRepliesMessage, @@ -81,7 +83,7 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { const postbackButtons = ( (b.message as StdOutgoingButtonsMessage)?.buttons || [] ) - .filter((btn) => btn.type === "postback") + .filter((btn) => btn.type === ButtonType.postback) .map((btn) => { return { ...btn, group: b.name }; }); @@ -205,9 +207,9 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { label: content.label, value: content.id, type: ["content", "menu"].includes(content.group || "") - ? content.group + ? PayloadType[content?.group || ""] : undefined, - } as PayloadPattern); + }); }} groupBy={(option) => { return option.group ?? t("label.other"); From cc6e50e5d0f70fc69aeb775c8b50cab6538f6356 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 15:19:19 +0100 Subject: [PATCH 07/14] fix: use title as id for menu --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index e609afb8..dd25d241 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -131,10 +131,10 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { [block?.previousBlocks], ); const menuOptions = menu - .filter((menu) => menu.payload) - .map(({ payload, title }) => ({ - id: payload as string, - label: title as string, + .filter(({ payload }) => payload) + .map(({ title }) => ({ + id: title, + label: title, group: "menu", })); const contentOptions = useMemo( From eb7d6ebe3cc611f533abdc160598ce1e3d0a205d Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Wed, 27 Nov 2024 16:34:52 +0100 Subject: [PATCH 08/14] feat: minor adjustements --- api/src/chat/schemas/types/message.ts | 2 + api/src/chat/schemas/types/quick-reply.ts | 6 +- api/src/utils/test/mocks/block.ts | 7 +- .../form/inputs/triggers/PatternInput.tsx | 2 +- .../form/inputs/triggers/PostbackInput.tsx | 154 +++++++++++------- frontend/src/types/block.types.ts | 2 + frontend/src/types/message.types.ts | 2 + 7 files changed, 112 insertions(+), 63 deletions(-) diff --git a/api/src/chat/schemas/types/message.ts b/api/src/chat/schemas/types/message.ts index 5c8d9bec..2c6f2fab 100644 --- a/api/src/chat/schemas/types/message.ts +++ b/api/src/chat/schemas/types/message.ts @@ -70,6 +70,8 @@ export enum FileType { export enum PayloadType { location = 'location', attachments = 'attachments', + quick_reply = 'quick_reply', + button = 'button', } export type StdOutgoingTextMessage = { text: string }; diff --git a/api/src/chat/schemas/types/quick-reply.ts b/api/src/chat/schemas/types/quick-reply.ts index 700cbb7c..df47e1a3 100644 --- a/api/src/chat/schemas/types/quick-reply.ts +++ b/api/src/chat/schemas/types/quick-reply.ts @@ -7,11 +7,7 @@ */ import { IncomingAttachmentPayload } from './attachment'; - -export enum PayloadType { - location = 'location', - attachments = 'attachments', -} +import { PayloadType } from './message'; export type Payload = | { diff --git a/api/src/utils/test/mocks/block.ts b/api/src/utils/test/mocks/block.ts index f7322e2f..1c8d3c58 100644 --- a/api/src/utils/test/mocks/block.ts +++ b/api/src/utils/test/mocks/block.ts @@ -13,10 +13,13 @@ import { import { BlockFull } from '@/chat/schemas/block.schema'; import { FileType } from '@/chat/schemas/types/attachment'; import { ButtonType } from '@/chat/schemas/types/button'; -import { OutgoingMessageFormat } from '@/chat/schemas/types/message'; +import { + OutgoingMessageFormat, + PayloadType, +} from '@/chat/schemas/types/message'; import { BlockOptions, ContentOptions } from '@/chat/schemas/types/options'; import { Pattern } from '@/chat/schemas/types/pattern'; -import { PayloadType, QuickReplyType } from '@/chat/schemas/types/quick-reply'; +import { QuickReplyType } from '@/chat/schemas/types/quick-reply'; import { CaptureVar } from '@/chat/validation-rules/is-valid-capture'; import { modelInstance } from './misc'; 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 2e9f4a6c..729fea4c 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PatternInput.tsx @@ -151,7 +151,7 @@ const PatternInput: FC = ({ onChange={(payload) => { payload && setPattern(payload); }} - value={pattern ? (pattern as PayloadPattern).value : null} + defaultValue={pattern as PayloadPattern} /> ) : null} {typeof value === "string" && patternType === "regex" ? ( diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index dd25d241..cbaf8b5f 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -6,10 +6,17 @@ * 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 { Box, Skeleton, Typography } from "@mui/material"; -import { useMemo } from "react"; +import { + Autocomplete, + Box, + CircularProgress, + InputAdornment, + Skeleton, + Typography, +} from "@mui/material"; +import { useMemo, useState } from "react"; -import AutoCompleteSelect from "@/app-components/inputs/AutoCompleteSelect"; +import { Input } from "@/app-components/inputs/Input"; import { useFind } from "@/hooks/crud/useFind"; import { useGetFromCache } from "@/hooks/crud/useGet"; import { useTranslate } from "@/hooks/useTranslate"; @@ -26,25 +33,31 @@ import { import { useBlock } from "../../BlockFormProvider"; -type PayloadOption = { - id: string; - label: string; - group?: string; +type PayloadOption = PayloadPattern & { + group: string; +}; + +const isSamePostback = (a: T, b: T) => { + return a.label === b.label && a.value === b.value; }; type PostbackInputProps = { - value?: string | null; - onChange: (pattern: PayloadPattern) => void; + defaultValue?: PayloadPattern; + onChange: (pattern: PayloadPattern | null) => void; }; -export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { +export const PostbackInput = ({ + defaultValue, + onChange, +}: PostbackInputProps) => { const block = useBlock(); + const [selectedValue, setSelectedValue] = useState(defaultValue || null); const getBlockFromCache = useGetFromCache(EntityType.BLOCK); - const { data: menu } = useFind( + const { data: menu, isLoading: isLoadingMenu } = useFind( { entity: EntityType.MENU, format: Format.FULL }, { hasCount: false }, ); - const { data: contents } = useFind( + const { data: contents, isLoading: isLoadingContent } = useFind( { entity: EntityType.CONTENT, format: Format.FULL }, { hasCount: false, @@ -54,20 +67,21 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { // General options const generalOptions = [ { - id: "GET_STARTED", label: t("label.get_started"), - group: t("label.general"), + value: "GET_STARTED", + group: "general", }, { - id: "VIEW_MORE", label: t("label.view_more"), - group: t("label.general"), + value: "VIEW_MORE", + group: "general", }, { - id: "LOCATION", label: t("label.location"), - group: t("label.general"), + value: "LOCATION", + type: PayloadType.location, + group: "general", }, ]; // Gather previous blocks buttons @@ -92,8 +106,8 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { }, [] as (PostBackButton & { group: string })[]) .map((btn) => { return { - id: btn.payload, label: btn.title, + value: btn.payload, group: btn.group, }; }), @@ -125,6 +139,8 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { return { id: btn.payload as string, label: btn.title as string, + value: btn.payload as string, + type: PayloadType.menu, group: btn.group, }; }), @@ -132,9 +148,11 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { ); const menuOptions = menu .filter(({ payload }) => payload) - .map(({ title }) => ({ + .map(({ title, payload }) => ({ id: title, label: title, + value: payload as string, + type: PayloadType.menu, group: "menu", })); const contentOptions = useMemo( @@ -158,15 +176,17 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { return (b.options?.content?.buttons || []).reduce((payloads, btn) => { // Return a payload for each node/button combination payloads.push({ - id: btn.title, label: btn.title, + value: btn.title, + type: PayloadType.content, group: "content", }); return availableContents.reduce((acc, n) => { acc.push({ - id: n.title, label: n.title, + value: n.title, + type: PayloadType.content, group: "content", }); @@ -178,52 +198,76 @@ export const PostbackInput = ({ value, onChange }: PostbackInputProps) => { [block?.previousBlocks, contents, getBlockFromCache], ); // Concat all previous blocks - const options = [ + const options: PayloadOption[] = [ ...generalOptions, ...btnOptions, ...qrOptions, ...menuOptions, ...contentOptions, ]; - const isOptionsReady = !value || options.find((e) => e.id === value); + const isOptionsReady = + !defaultValue || options.find((o) => isSamePostback(o, defaultValue)); if (!isOptionsReady) { return ( ); } + const selected = defaultValue + ? options.find((o) => { + return isSamePostback(o, defaultValue); + }) + : undefined; return ( - <> - - value={value} - options={options} - labelKey="label" - label={t("label.postback")} - multiple={false} - onChange={(_e, content) => { - content && - onChange({ - label: content.label, - value: content.id, - type: ["content", "menu"].includes(content.group || "") - ? PayloadType[content?.group || ""] - : undefined, - }); - }} - groupBy={(option) => { - return option.group ?? t("label.other"); - }} - getOptionLabel={({ group, label }) => `${group}:${label}`} - renderGroup={(params) => ( -
  • - - {params.group} - - {params.children} -
  • - )} - /> - + + size="small" + defaultValue={selected} + options={options} + // label={t("label.postback")} + multiple={false} + onChange={(_e, value) => { + setSelectedValue(value); + if (value) { + const { group: _g, ...payloadPattern } = value; + + onChange(payloadPattern); + } else { + onChange(null); + } + }} + groupBy={(option) => { + return option.group ?? t("label.other"); + }} + getOptionLabel={({ label }) => label} + renderGroup={(params) => ( +
  • + + {t(`label.${params.group}`)} + + {params.children} +
  • + )} + renderInput={(props) => { + return ( + + {selectedValue?.type || t("label.postback")} + + ), + endAdornment: + isLoadingMenu || isLoadingContent ? ( + + ) : null, + }} + /> + ); + }} + /> ); }; diff --git a/frontend/src/types/block.types.ts b/frontend/src/types/block.types.ts index 5eb686d6..c8825a3f 100644 --- a/frontend/src/types/block.types.ts +++ b/frontend/src/types/block.types.ts @@ -66,6 +66,8 @@ export interface PayloadPattern { label: string; value: string; // @todo : rename 'attachment' to 'attachments' + // @todo: If undefined, that means the payload could be either quick_reply or button + // We will move soon so that it will be a required attribute type?: PayloadType; } diff --git a/frontend/src/types/message.types.ts b/frontend/src/types/message.types.ts index a1b9dea6..50b5fb7d 100644 --- a/frontend/src/types/message.types.ts +++ b/frontend/src/types/message.types.ts @@ -28,6 +28,8 @@ export enum PayloadType { attachments = "attachments", menu = "menu", content = "content", + quick_reply = "quick_reply", + button = "button", } export enum FileType { From 3be60063802c3ec787b06a604c998d281342f96d Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 17:56:32 +0100 Subject: [PATCH 09/14] fix: PostbackInput --- .../form/inputs/triggers/PostbackInput.tsx | 144 +++++++++--------- 1 file changed, 70 insertions(+), 74 deletions(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index cbaf8b5f..6035043f 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -9,6 +9,7 @@ import { Autocomplete, Box, + Chip, CircularProgress, InputAdornment, Skeleton, @@ -26,6 +27,7 @@ import { ButtonType, PayloadType, PostBackButton, + QuickReplyType, StdOutgoingButtonsMessage, StdOutgoingQuickRepliesMessage, StdQuickReply, @@ -37,9 +39,8 @@ type PayloadOption = PayloadPattern & { group: string; }; -const isSamePostback = (a: T, b: T) => { - return a.label === b.label && a.value === b.value; -}; +const isSamePostback = (a: T, b: T) => + a.label === b.label && a.value === b.value; type PostbackInputProps = { defaultValue?: PayloadPattern; @@ -89,28 +90,24 @@ export const PostbackInput = ({ () => (block?.previousBlocks || []) .map((b) => getBlockFromCache(b)) - .filter((b) => { - return b && typeof b.message === "object" && "buttons" in b.message; - }) + .filter( + (b) => b && typeof b.message === "object" && "buttons" in b.message, + ) .map((b) => b as IBlock) .reduce((acc, b) => { const postbackButtons = ( (b.message as StdOutgoingButtonsMessage)?.buttons || [] ) .filter((btn) => btn.type === ButtonType.postback) - .map((btn) => { - return { ...btn, group: b.name }; - }); + .map((btn) => ({ ...btn, group: b.name })); return acc.concat(postbackButtons); }, [] as (PostBackButton & { group: string })[]) - .map((btn) => { - return { - label: btn.title, - value: btn.payload, - group: btn.group, - }; - }), + .map((btn) => ({ + label: btn.title, + value: btn.payload, + group: "buttons", + })), [block?.previousBlocks, getBlockFromCache], ); // Gather previous blocks quick replies @@ -118,32 +115,27 @@ export const PostbackInput = ({ () => (block?.previousBlocks || []) .map((b) => getBlockFromCache(b)) - .filter((b) => { - return ( - b && typeof b.message === "object" && "quickReplies" in b.message - ); - }) + .filter( + (b) => + b && typeof b.message === "object" && "quickReplies" in b.message, + ) .map((b) => b as IBlock) .reduce((acc, b) => { const postbackQuickReplies = ( (b.message as StdOutgoingQuickRepliesMessage)?.quickReplies || [] ) - .filter((btn) => btn.content_type === "text") - .map((btn) => { - return { ...btn, group: b.name }; - }); + .filter(({ content_type }) => content_type === QuickReplyType.text) + .map((btn) => ({ ...btn, group: b.name })); return acc.concat(postbackQuickReplies); }, [] as (StdQuickReply & { group: string })[]) - .map((btn) => { - return { - id: btn.payload as string, - label: btn.title as string, - value: btn.payload as string, - type: PayloadType.menu, - group: btn.group, - }; - }), + .map((btn) => ({ + id: btn.payload as string, + label: btn.title as string, + value: btn.payload as string, + type: PayloadType.menu, + group: "quick_replies", + })), [block?.previousBlocks], ); const menuOptions = menu @@ -158,15 +150,13 @@ export const PostbackInput = ({ const contentOptions = useMemo( () => (block?.previousBlocks || []) - .map((bId) => getBlockFromCache(bId)) - .filter((b) => { - return ( + .map((bId) => getBlockFromCache(bId) as IBlock) + .filter( + (b) => b && b.options?.content?.entity && - b.options.content.buttons.length > 0 - ); - }) - .map((b) => b as IBlock) + b.options.content.buttons.length > 0, + ) .map((b) => { const availableContents = (contents || []).filter( ({ entity, status }) => @@ -214,60 +204,66 @@ export const PostbackInput = ({ ); } const selected = defaultValue - ? options.find((o) => { - return isSamePostback(o, defaultValue); - }) + ? options.find((o) => isSamePostback(o, defaultValue)) : undefined; return ( - + { setSelectedValue(value); if (value) { - const { group: _g, ...payloadPattern } = value; + const { group: _g, ...payloadPattern } = value; onChange(payloadPattern); } else { onChange(null); } }} - groupBy={(option) => { - return option.group ?? t("label.other"); - }} + groupBy={({ group }) => group ?? t("label.other")} getOptionLabel={({ label }) => label} - renderGroup={(params) => ( -
  • + renderGroup={({ key, group, children }) => ( +
  • - {t(`label.${params.group}`)} + {t(`label.${group}`)} - {params.children} + {children}
  • )} - renderInput={(props) => { - return ( - - {selectedValue?.type || t("label.postback")} - - ), - endAdornment: - isLoadingMenu || isLoadingContent ? ( - - ) : null, - }} - /> - ); - }} + renderInput={(props) => ( + + + + ), + endAdornment: + isLoadingMenu || isLoadingContent ? ( + + ) : null, + }} + /> + )} /> ); }; From 76e2031829462b7acef312379a34ac2cbe36d924 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 17:57:48 +0100 Subject: [PATCH 10/14] fix: remove unused attributes --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index 6035043f..71b567dd 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -243,8 +243,6 @@ export const PostbackInput = ({ Date: Wed, 27 Nov 2024 18:43:35 +0100 Subject: [PATCH 11/14] fix: update PostbackInput chip padding --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index 71b567dd..fd65fe19 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -21,6 +21,7 @@ import { Input } from "@/app-components/inputs/Input"; import { useFind } from "@/hooks/crud/useFind"; import { useGetFromCache } from "@/hooks/crud/useGet"; import { useTranslate } from "@/hooks/useTranslate"; +import { theme } from "@/layout/themes/theme"; import { EntityType, Format } from "@/services/types"; import { IBlock, PayloadPattern } from "@/types/block.types"; import { @@ -243,12 +244,16 @@ export const PostbackInput = ({ Date: Wed, 27 Nov 2024 19:15:43 +0100 Subject: [PATCH 12/14] fix: update translations --- frontend/public/locales/en/translation.json | 1 + frontend/public/locales/fr/translation.json | 1 + .../form/inputs/triggers/PostbackInput.tsx | 21 ++++++++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 26366b6c..96af5d5e 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -300,6 +300,7 @@ "from_channels": "Target channels", "simple_text": "Simple Text", "quick_replies": "Quick Replies", + "button": "Button", "buttons": "Buttons", "web_url": "Web URL", "payload": "Payload", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index cffef81a..ab90512f 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -300,6 +300,7 @@ "from_channels": "Cibler les canaux", "simple_text": "Texte simple", "quick_replies": "Réponses rapides", + "button": "Boutton", "buttons": "Boutons", "web_url": "URL Web", "payload": "Payload", diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index fd65fe19..9b8154db 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -71,12 +71,14 @@ export const PostbackInput = ({ { label: t("label.get_started"), value: "GET_STARTED", + type: PayloadType.button, group: "general", }, { label: t("label.view_more"), value: "VIEW_MORE", + type: PayloadType.button, group: "general", }, { @@ -107,7 +109,8 @@ export const PostbackInput = ({ .map((btn) => ({ label: btn.title, value: btn.payload, - group: "buttons", + type: PayloadType.button, + group: "button", })), [block?.previousBlocks, getBlockFromCache], ); @@ -134,8 +137,8 @@ export const PostbackInput = ({ id: btn.payload as string, label: btn.title as string, value: btn.payload as string, - type: PayloadType.menu, - group: "quick_replies", + type: PayloadType.quick_reply, + group: "quick_reply", })), [block?.previousBlocks], ); @@ -199,7 +202,7 @@ export const PostbackInput = ({ const isOptionsReady = !defaultValue || options.find((o) => isSamePostback(o, defaultValue)); - if (!isOptionsReady) { + if (!isOptionsReady || isLoadingContent || isLoadingMenu) { return ( ); @@ -212,6 +215,7 @@ export const PostbackInput = ({ { @@ -244,18 +248,19 @@ export const PostbackInput = ({ From a7f8a205afa2095ef0f598a35fa5f56317edd52a Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 19:23:17 +0100 Subject: [PATCH 13/14] fix: remove unused id field --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index 9b8154db..f75b9e2d 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -145,7 +145,6 @@ export const PostbackInput = ({ const menuOptions = menu .filter(({ payload }) => payload) .map(({ title, payload }) => ({ - id: title, label: title, value: payload as string, type: PayloadType.menu, From 66c3533b4bc3ec2ef1dab09d82ab961144a8feda Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 27 Nov 2024 19:24:31 +0100 Subject: [PATCH 14/14] fix: remove quick replies unused id field --- .../visual-editor/form/inputs/triggers/PostbackInput.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx index f75b9e2d..9ac684d6 100644 --- a/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx +++ b/frontend/src/components/visual-editor/form/inputs/triggers/PostbackInput.tsx @@ -134,7 +134,6 @@ export const PostbackInput = ({ return acc.concat(postbackQuickReplies); }, [] as (StdQuickReply & { group: string })[]) .map((btn) => ({ - id: btn.payload as string, label: btn.title as string, value: btn.payload as string, type: PayloadType.quick_reply,