Skip to content

Commit

Permalink
Add chatbot pattern types
Browse files Browse the repository at this point in the history
  • Loading branch information
WalissonPires committed May 1, 2024
1 parent 4b7ec5e commit a1eeaf3
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/common/primitives/enum/enum-utils.ts
Original file line number Diff line number Diff line change
@@ -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));
}
13 changes: 12 additions & 1 deletion src/common/services/messaging/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface ProviderWithStatus {
export interface ChatNode {
id: string;
label: string;
patternType: ChatNodePatternType;
pattern: string;
output: ChatNodeOutput[];
invalidOutput?: ChatNodeOutput[];
Expand All @@ -61,4 +62,14 @@ export interface ChatNodeOutput {

export enum ChatNodeAction {
GoToPrevious = 1
}
}

export enum ChatNodePatternType {
StartsWith = 1,
EndsWith = 2,
Contains = 3,
Exact = 4,
Regex = 5
};

export const AnyText = '.*';
98 changes: 93 additions & 5 deletions src/components/ChatbotFlowView/hooks/index.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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: []
Expand Down Expand Up @@ -111,7 +112,31 @@ export function useChatbotFlow() {

node.pattern = event.target.value ?? '';
}));
}
};

const handlePatternTypeChange = (event: ChangeEvent<HTMLSelectElement>) => {

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<HTMLPreElement>) => {

Expand Down Expand Up @@ -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]);
Expand All @@ -194,9 +219,12 @@ export function useChatbotFlow() {

}, []);

// const currentNodePattern = currentNode ? mapPatternToUserBuildinPatterns(currentNode.pattern) : null;

return {
ready,
currentNode,
// currentNodePattern,
nodesIndex,
nodesPath,
visible,
Expand All @@ -207,14 +235,74 @@ export function useChatbotFlow() {
handleShowAddChild,
handleRemoveChild,
handlePatternChange,
handlePatternTypeChange,
handleOutputContentChange,
handleRemoveOutput,
handleSave,
};
}

export enum ChatNodePatternTypeAdditional {
AnyText = 99
}

const updateArrayItem = <T,>(array: T[], selector: (item: T) => boolean, newItem: (item: T) => T) => {
export type UserBuildinPatterns = ChatNodePatternType | ChatNodePatternTypeAdditional;

export const UserBuildinPatternsDisplay: Record<UserBuildinPatterns, string> = {
[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<T,>(array: T[], selector: (item: T) => boolean, newItem: (item: T) => T) {

const newArray = [];
for(let i = 0; i < array.length; i++) {
Expand All @@ -226,7 +314,7 @@ const updateArrayItem = <T,>(array: T[], selector: (item: T) => boolean, newItem
}

return newArray;
};
}

function makeChatNodesIndex(root: ChatNode) {

Expand Down
28 changes: 23 additions & 5 deletions src/components/ChatbotFlowView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -21,6 +23,7 @@ export function ChatbotFlowView() {
handleShowAddChild,
handleRemoveChild,
handlePatternChange,
handlePatternTypeChange,
handleOutputContentChange,
handleRemoveOutput,
handleSave,
Expand All @@ -29,6 +32,10 @@ export function ChatbotFlowView() {
if (!ready)
return <ChatbotFlowView.Skeleton />;

const isAnyTextPattern = currentNode?.patternType === ChatNodePatternType.Regex && currentNode?.pattern == AnyText;
const patternType = isAnyTextPattern ? ChatNodePatternTypeAdditional.AnyText : currentNode?.patternType;
const pattern = isAnyTextPattern ? '' : (currentNode?.pattern ?? '');

return (
<div className="container mx-auto max-w-3xl">
<div className="bg-white border m-4 p-4">
Expand All @@ -55,7 +62,18 @@ export function ChatbotFlowView() {
</div>
<div>
<label className="block font-bold">Mensagem do usuário</label>
<Input value={currentNode?.pattern ?? ''} onChange={handlePatternChange} placeholder="Regex" className="flex-1 mr-2" />
<div className="flex flex-row">
{/* <label className="flex flex-row items-center p-2 bg-slate-100">
<input type="checkbox" className="mr-2" checked={currentNode?.pattern === AnyText} onChange={handleAnyTextChange} />
<span>Qualquer texto</span>
</label> */}
<div>
<Select defaultValue={ChatNodePatternType.Contains} value={patternType} onChange={handlePatternTypeChange}>
{getEnumPairValue(UserBuildinPatternsDisplay).map(({ value, text }) => <option value={value} key={value}>{text}</option>)}
</Select>
</div>
<Input value={pattern} onChange={handlePatternChange} disabled={isAnyTextPattern} placeholder="Mensagem" className="flex-1 mr-2" />
</div>
</div>

<div className="mt-3">
Expand Down Expand Up @@ -96,7 +114,7 @@ export function ChatbotFlowView() {
</div>
<ul className="list-none">
{currentNode?.childs.length === 0 && <li className="mt-3"><small className="text-slate-400">Fim da conversa. Clique no botão <b>+</b> para continuar o diálogo.</small></li>}
{currentNode?.childs.map(node => { console.log(node); return (
{currentNode?.childs.map(node => (
<li key={node.id} className="border-b py-4 flex flex-row flex-wrap justify-between items-center">
<Button onClick={handleNext(node)} variant="textOnly" className="text-left flex-1">
<span className="block"><ChatBubbleBottomCenterTextIcon className="h-5 w-5 inline-block" /> {node.label}</span>
Expand All @@ -106,7 +124,7 @@ export function ChatbotFlowView() {
<Button variant="transparent" onClick={handleRemoveChild(node)}><TrashIcon className="h-5 w-5 inline-block text-red-400" title="Excluír mensagem" /></Button>
</div>
</li>
)})}
))}
</ul>
</div>
</div>
Expand Down

0 comments on commit a1eeaf3

Please sign in to comment.