From a1eeaf39e9c41958a60c78a7b01d054fe14b2aff Mon Sep 17 00:00:00 2001 From: "Walisson Pires R. Miranda" Date: Wed, 1 May 2024 11:29:47 -0300 Subject: [PATCH] Add chatbot pattern types --- src/common/primitives/enum/enum-utils.ts | 6 ++ src/common/services/messaging/models.ts | 13 ++- .../ChatbotFlowView/hooks/index.tsx | 98 ++++++++++++++++++- src/components/ChatbotFlowView/index.tsx | 28 +++++- 4 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 src/common/primitives/enum/enum-utils.ts diff --git a/src/common/primitives/enum/enum-utils.ts b/src/common/primitives/enum/enum-utils.ts new file mode 100644 index 0000000..12a6307 --- /dev/null +++ b/src/common/primitives/enum/enum-utils.ts @@ -0,0 +1,6 @@ +import { sortAsc } from "../array/sort-utils"; + +export const getEnumPairValue = (enum2: Object) => { + + return Object.keys(enum2).map(key => ({ value: key, text: (enum2 as any)[key] })).sort((a, b) => sortAsc(a.text, b.text)); +} \ No newline at end of file diff --git a/src/common/services/messaging/models.ts b/src/common/services/messaging/models.ts index 28ea9ed..105e30b 100644 --- a/src/common/services/messaging/models.ts +++ b/src/common/services/messaging/models.ts @@ -43,6 +43,7 @@ export interface ProviderWithStatus { export interface ChatNode { id: string; label: string; + patternType: ChatNodePatternType; pattern: string; output: ChatNodeOutput[]; invalidOutput?: ChatNodeOutput[]; @@ -61,4 +62,14 @@ export interface ChatNodeOutput { export enum ChatNodeAction { GoToPrevious = 1 -} \ No newline at end of file +} + +export enum ChatNodePatternType { + StartsWith = 1, + EndsWith = 2, + Contains = 3, + Exact = 4, + Regex = 5 +}; + +export const AnyText = '.*'; \ No newline at end of file diff --git a/src/components/ChatbotFlowView/hooks/index.tsx b/src/components/ChatbotFlowView/hooks/index.tsx index 67b60ed..fa17334 100644 --- a/src/components/ChatbotFlowView/hooks/index.tsx +++ b/src/components/ChatbotFlowView/hooks/index.tsx @@ -1,6 +1,6 @@ import { useState, useMemo, ChangeEvent, MouseEvent, useEffect } from "react"; import { AppError } from "@/common/error"; -import { ChatNode, ChatNodeOutput, ProviderType } from "@/common/services/messaging/models"; +import { AnyText, ChatNode, ChatNodeOutput, ChatNodePatternType, ProviderType } from "@/common/services/messaging/models"; import { AppToast } from "@/common/ui/toast"; import { MessageProvidersApi } from "@/domains/message-providers/client-api"; import { useLoading } from "../../AppLayout/Loading/hooks"; @@ -74,6 +74,7 @@ export function useChatbotFlow() { const newNode: ChatNode = { id: new Date().getTime().toString(), label: childLabel, + patternType: ChatNodePatternType.Contains, pattern: '', output: [{ type: 'text', content: '' }], childs: [] @@ -111,7 +112,31 @@ export function useChatbotFlow() { node.pattern = event.target.value ?? ''; })); - } + }; + + const handlePatternTypeChange = (event: ChangeEvent) => { + + if (!currentNode || !rootNode) return; + + setRootNode(updateNode(rootNode, node => { + + if (node.id !== currentNode.id) return; + + const patternType = parseInt(event.target.value); + if (patternType === ChatNodePatternTypeAdditional.AnyText) { + + node.patternType = ChatNodePatternType.Regex; + node.pattern = AnyText; + } + else { + + if (node.patternType === ChatNodePatternType.Regex && node.pattern === AnyText) + node.pattern = ''; + + node.patternType = patternType; + } + })); + }; const handleOutputContentChange = ({ type, content }: ChatNodeOutput) => (event: ChangeEvent) => { @@ -178,7 +203,7 @@ export function useChatbotFlow() { return; } - const rootNode = await api.getChatbotFlow({ id: whatsappProvider.id }) ?? { id: 'inicial', label: 'Mensagem inicial', childs: [], output: [{ type: 'text', content: 'Olá! Pronto pra começar' }], pattern: '.*' }; + const rootNode = await api.getChatbotFlow({ id: whatsappProvider.id }) ?? { id: 'inicial', label: 'Mensagem inicial', childs: [], output: [{ type: 'text', content: 'Olá! Pronto pra começar' }], patternType: ChatNodePatternType.Regex, pattern: AnyText }; setRootNode(rootNode); setNodesPath([rootNode.id]); @@ -194,9 +219,12 @@ export function useChatbotFlow() { }, []); + // const currentNodePattern = currentNode ? mapPatternToUserBuildinPatterns(currentNode.pattern) : null; + return { ready, currentNode, + // currentNodePattern, nodesIndex, nodesPath, visible, @@ -207,14 +235,74 @@ export function useChatbotFlow() { handleShowAddChild, handleRemoveChild, handlePatternChange, + handlePatternTypeChange, handleOutputContentChange, handleRemoveOutput, handleSave, }; } +export enum ChatNodePatternTypeAdditional { + AnyText = 99 +} -const updateArrayItem = (array: T[], selector: (item: T) => boolean, newItem: (item: T) => T) => { +export type UserBuildinPatterns = ChatNodePatternType | ChatNodePatternTypeAdditional; + +export const UserBuildinPatternsDisplay: Record = { + [ChatNodePatternTypeAdditional.AnyText]: 'Qualquer texto', + [ChatNodePatternType.StartsWith]: 'Começa com', + [ChatNodePatternType.EndsWith]: 'Termina com', + [ChatNodePatternType.Contains]: 'Contém', + [ChatNodePatternType.Exact]: 'Exatamente', + [ChatNodePatternType.Regex]: 'Regex', +}; + +// export function mapPatternToUserBuildinPatterns(pattern: string) { + +// if (pattern === AnyText) { +// return { +// patternType: ChatNodePatternTypeAdditional.AnyText, +// patternText: '' +// }; +// } + +// if (pattern.startsWith(ChatNodePatterns.exact)) { +// return { +// patternType: UserBuildinPatterns.Exact, +// patternText: pattern.replace(ChatNodePatterns.exact, '') +// }; +// } + +// if (pattern.startsWith(ChatNodePatterns.startsWith)) { +// return { +// patternType: UserBuildinPatterns.StartsWith, +// patternText: pattern.replace(ChatNodePatterns.startsWith, '') +// }; +// } + +// if (pattern.startsWith(ChatNodePatterns.endsWith)) { +// return { +// patternType: UserBuildinPatterns.EndsWith, +// patternText: pattern.replace(ChatNodePatterns.endsWith, '') +// }; +// } + +// if (pattern.startsWith(ChatNodePatterns.contains)) { +// return { +// patternType: UserBuildinPatterns.Contains, +// patternText: pattern.replace(ChatNodePatterns.contains, '') +// }; +// } + +// return { +// patternType: UserBuildinPatterns.Regex, +// patternText: pattern +// }; +// } + + + +function updateArrayItem(array: T[], selector: (item: T) => boolean, newItem: (item: T) => T) { const newArray = []; for(let i = 0; i < array.length; i++) { @@ -226,7 +314,7 @@ const updateArrayItem = (array: T[], selector: (item: T) => boolean, newItem } return newArray; -}; +} function makeChatNodesIndex(root: ChatNode) { diff --git a/src/components/ChatbotFlowView/index.tsx b/src/components/ChatbotFlowView/index.tsx index 1e39da0..099bad5 100644 --- a/src/components/ChatbotFlowView/index.tsx +++ b/src/components/ChatbotFlowView/index.tsx @@ -2,8 +2,10 @@ import Skeleton from "react-loading-skeleton"; import { ArrowRightIcon, ChatBubbleBottomCenterTextIcon, ChevronDownIcon, ChevronRightIcon, EnvelopeIcon, FilmIcon, PlusIcon, TrashIcon, XMarkIcon } from "@heroicons/react/24/outline"; -import { Button, DropdownMenu, DropdownMenuItem, Input } from "../Form"; -import { useChatbotFlow } from "./hooks"; +import { Button, DropdownMenu, DropdownMenuItem, Input, Select } from "../Form"; +import { ChatNodePatternTypeAdditional, UserBuildinPatternsDisplay, useChatbotFlow } from "./hooks"; +import { AnyText, ChatNodePatternType } from "../../common/services/messaging/models"; +import { getEnumPairValue } from "../../common/primitives/enum/enum-utils"; export function ChatbotFlowView() { @@ -21,6 +23,7 @@ export function ChatbotFlowView() { handleShowAddChild, handleRemoveChild, handlePatternChange, + handlePatternTypeChange, handleOutputContentChange, handleRemoveOutput, handleSave, @@ -29,6 +32,10 @@ export function ChatbotFlowView() { if (!ready) return ; + const isAnyTextPattern = currentNode?.patternType === ChatNodePatternType.Regex && currentNode?.pattern == AnyText; + const patternType = isAnyTextPattern ? ChatNodePatternTypeAdditional.AnyText : currentNode?.patternType; + const pattern = isAnyTextPattern ? '' : (currentNode?.pattern ?? ''); + return (
@@ -55,7 +62,18 @@ export function ChatbotFlowView() {
- +
+ {/* */} +
+ +
+ +
@@ -96,7 +114,7 @@ export function ChatbotFlowView() {
    {currentNode?.childs.length === 0 &&
  • Fim da conversa. Clique no botão + para continuar o diálogo.
  • } - {currentNode?.childs.map(node => { console.log(node); return ( + {currentNode?.childs.map(node => (
- )})} + ))}