Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const LearnAutomatedTasks = () => {
justifyContent="flex-start"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
gap={0}
>
<Box
bg="blueGray.200"
Expand Down
10 changes: 6 additions & 4 deletions apps/builder/components/editor/StepsSideBar/StepCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React, { useEffect, useState } from 'react'
import { StepIcon } from './StepIcon'
import { StepTypeLabel } from './StepTypeLabel'
import { InfoIcon } from '@chakra-ui/icons'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'

export const StepCard = ({
type,
Expand All @@ -28,17 +29,18 @@ export const StepCard = ({
}) => {
const { draggedStepType } = useStepDnd()
const [isMouseDown, setIsMouseDown] = useState(false)
const { typebot } = useTypebot()

useEffect(() => {
setIsMouseDown(draggedStepType === type)
}, [draggedStepType, type])

const handleMouseDown = (e: React.MouseEvent) => onMouseDown(e, type)

const shouldAddGradientBorder = [
WOZStepType.MESSAGE,
WOZStepType.INTERPRET_DATA_WITH_AI,
].includes(type as WOZStepType)
const shouldAddGradientBorder =
[WOZStepType.MESSAGE, WOZStepType.INTERPRET_DATA_WITH_AI].includes(
type as WOZStepType
) && typebot?.availableFor?.includes('automated-tasks')

return (
<Tooltip
Expand Down
5 changes: 4 additions & 1 deletion apps/builder/components/editor/StepsSideBar/StepSideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ export const StepsSideBar = () => {
)
}

const EVENT_AVAILABLE_STEPS: StepType[] = [IntegrationStepType.WEBHOOK]
const EVENT_AVAILABLE_STEPS: StepType[] = [
IntegrationStepType.WEBHOOK,
WOZStepType.INTERPRET_DATA_WITH_AI,
]
const AUTOMATED_TASKS_AVAILABLE_STEPS: StepType[] = [
WOZStepType.MESSAGE,
WOZStepType.INTERPRET_DATA_WITH_AI,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import React, { DetailedHTMLProps, InputHTMLAttributes } from 'react'
import { SELECT_ACTION, SELECT_ACTIONS } from './OctaSelect';
import { SELECT_ACTION } from './OctaSelect'

export type OctaSelectProps = {
onChange: (value: any, itemFull?: any) => void
export type OctaSelectProps<T> = {
onChange: (value: T, itemFull?: T) => void
defaultSelected?: any
label?: string;
findable?: boolean;
options?: Array<OptionType>;
showEdit?: boolean;
showDelete?: boolean;
label?: string
findable?: boolean
options?: Array<OptionType>
showEdit?: boolean
showDelete?: boolean
onIconClicked?: (value: any, action: SELECT_ACTION) => {}
} & DetailedHTMLProps<InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>

export type OptionProps = {
value: any;
children?: string;
optionKey?: string | number;
isTitle?: boolean;
disabled?: boolean;
selected: any;
showEdit?: boolean;
showDelete?: boolean;
value: any
children?: string
optionKey?: string | number
isTitle?: boolean
disabled?: boolean
selected: any
showEdit?: boolean
showDelete?: boolean
onIconClicked?: (value: any, action: SELECT_ACTION) => {}
} & DetailedHTMLProps<InputHTMLAttributes<HTMLLIElement>, HTMLLIElement>

export type OptionType = {
value: any;
label: string;
isTitle?: boolean;
disabled?: boolean;
key: string | number;
optionKey?: string | number;
subType?: string;
value: any
label: string
isTitle?: boolean
disabled?: boolean
key: string | number
optionKey?: string | number
subType?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ import {
Spinner,
HStack,
} from '@chakra-ui/react'
import { IntegrationStepType, WOZInterpretDataWithAIOptions } from 'models'
import { useMemo, useState, useRef, useEffect } from 'react'
import {
IntegrationStepType,
WOZInterpretDataWithAIOptions,
WOZInterpretDataWithAIResponseFormat,
} from 'models'
import { useMemo, useState, useRef } from 'react'
import { useInterpretDataWithAI } from 'hooks/InterpretDataWithAI/useInterpretDataWithAI'
import { VariablesMenu } from './VariablesMenu'
import { MdInfoOutline } from 'react-icons/md'
import { WOZInterpretDataWithAI } from 'models'
import { getDeepKeys } from 'services/integrations'
import { useTypebot } from 'contexts/TypebotContext'
import OctaSelect from 'components/octaComponents/OctaSelect/OctaSelect'

type Props = {
step: WOZInterpretDataWithAI
Expand All @@ -34,6 +40,9 @@ export const InterpretDataWithAI = ({ step, onContentChange }: Props) => {
testReturn,
refetch,
} = useInterpretDataWithAI({ step })

const { typebot } = useTypebot()
const isAutomatedTasksBot = typebot?.availableFor.includes('automated-tasks')
const [isTesting, setIsTesting] = useState(false)

const [resultOfInterpretWithAi, setResultOfInterpretWithAi] =
Expand Down Expand Up @@ -78,13 +87,30 @@ export const InterpretDataWithAI = ({ step, onContentChange }: Props) => {
}, 0)
}

const stepDescription = useMemo(() => {
if (isAutomatedTasksBot) {
return 'Defina como a IA deve apresentar os dados coletados na conversa.'
}
return 'Defina como a IA deve apresentar os dados coletados para o próximo passo do fluxo.'
}, [])

const placeholderInstructions = useMemo(() => {
return `Ex: Retorne ao cliente a lista dos tickets encontrados.
\b\b
Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
{{ criado-em }}`
}, [])

const placeholderInstructionsEvents = useMemo(() => {
if (
step?.content?.responseFormat ===
WOZInterpretDataWithAIResponseFormat.JSON
) {
return `Ex: Crie um JSON com os dados do ticket. Use as variáveis: {ticked_id}, {assunto}, {responsavel}, {data_de_criacao}`
}
return `Ex: Gere um resumo das informações coletadas, listando o motivo do contato, o status atual e a data de abertura.`
}, [step?.content?.responseFormat])

const tooltipInstructions = useMemo(() => {
return `Como instruir a IA?
<ol style="margin: 8px 0; padding-left: 20px; list-style-type: decimal;">
Expand All @@ -94,6 +120,27 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
</ol>`
}, [])

const tooltipInstructionsEvents = useMemo(() => {
return `Como instruir a IA?
<ol style="margin: 8px 0; padding-left: 20px; list-style-type: decimal;">
<li style="margin-bottom: 4px;">Defina a estrutura de dados (ex: um JSON) que o próximo passo do fluxo deve receber.</li>
<li style="margin-bottom: 4px;">Escreva manualmente o nome das variáveis capturadas nos passos anteriores usando chaves, como {nome_da_variavel}.</li>
<li style="margin-bottom: 4px;">Este campo é técnico: a IA usará suas instruções para organizar as informações antes de enviá-las ao sistema, sem que o cliente veja este texto. </li>
</ol>`
}, [])

const tooltipInstructionsResponseFormat = useMemo(() => {
return `<strong>Mensagem natural (texto):</strong>
<br />
Quando a informação for usada como um texto simples ou resumo em etapas posteriores do fluxo.
<br />
<br />
<strong>JSON (estrutura):</strong>
<br />
Quando o próximo passo é uma integração/sistema externo e é preciso transformar informação em código para enviar para outro sistema.
`
}, [])

const responseKeys = useMemo(
() => getDeepKeys(data?.response || {}),
[data?.response]
Expand Down Expand Up @@ -122,6 +169,30 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
}
}

const handleSelectResponseFormat = (
value: WOZInterpretDataWithAIResponseFormat
) => {
onContentChange({
...step.content,
responseFormat: value,
})
}

const responseFormatOptions = useMemo(() => {
return [
{
key: 'json',
label: 'JSON',
value: WOZInterpretDataWithAIResponseFormat.JSON,
},
{
key: 'text',
label: 'Mensagem natural',
value: WOZInterpretDataWithAIResponseFormat.TEXT,
},
]
}, [])

const componentToRender = useMemo(() => {
if (whoIsConnectedOnMyBlock?.length <= 0) {
return (
Expand All @@ -133,7 +204,10 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},

if (whoIsConnectedOnMyBlock?.length === 1) {
const block = whoIsConnectedOnMyBlock[0]
if (block.steps[0].type !== IntegrationStepType.WEBHOOK)
if (
block.steps[0].type !== IntegrationStepType.WEBHOOK &&
isAutomatedTasksBot
)
return (
<Stack>
<Text>
Expand All @@ -148,8 +222,11 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
return (
<Stack>
<Text>
Este bloco deve receber apenas uma conexão, sendo esta conexão um
componente chamado "Conecte a outro sistema"
Este bloco deve receber apenas uma conexão{' '}
{isAutomatedTasksBot
? `, sendo esta conexão um
componente chamado "Conecte a outro sistema"`
: ''}
</Text>
</Stack>
)
Expand All @@ -164,7 +241,7 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
)
}

if (!success) {
if (!success && isAutomatedTasksBot) {
return (
<Stack>
<Text color="red">
Expand All @@ -179,18 +256,52 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
}

return (
<Stack>
<Text>
Defina como a IA deve apresentar os dados coletados na conversa.
</Text>

<Stack direction="column" gap={4}>
<Text>{stepDescription}</Text>
{!isAutomatedTasksBot && (
<Stack direction="row" justifyContent="space-between" w="full">
<Stack direction="row" alignItems="center" gap={2}>
<Text fontWeight="bold">Formato de saída</Text>
<Tooltip
label={
<Box
dangerouslySetInnerHTML={{
__html: tooltipInstructionsResponseFormat,
}}
/>
}
hasArrow
>
<Box as="span" display="inline-flex" cursor="pointer">
<Icon as={MdInfoOutline} boxSize={4} />
</Box>
</Tooltip>
</Stack>
</Stack>
)}
{!isAutomatedTasksBot && (
<OctaSelect
defaultSelected={
step?.content?.responseFormat ||
WOZInterpretDataWithAIResponseFormat.TEXT
}
onChange={handleSelectResponseFormat}
placeholder="selecione uma opção"
options={responseFormatOptions}
findable
/>
)}
<Stack direction="row" justifyContent="space-between" w="full">
<Stack direction="row" alignItems="center" gap={2}>
<Text fontWeight="bold">Instruções de retorno</Text>
<Tooltip
label={
<Box
dangerouslySetInnerHTML={{ __html: tooltipInstructions }}
dangerouslySetInnerHTML={{
__html: isAutomatedTasksBot
? tooltipInstructions
: tooltipInstructionsEvents,
}}
/>
}
hasArrow
Expand All @@ -208,7 +319,11 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
<Box position="relative" w="full">
<Textarea
ref={textareaRef}
placeholder={placeholderInstructions}
placeholder={
isAutomatedTasksBot
? placeholderInstructions
: placeholderInstructionsEvents
}
resize="none"
maxLength={5000}
minLength={1}
Expand All @@ -219,12 +334,14 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
className="scrollbar-custom"
/>

<Box position="absolute" bottom="14px" right="14px" zIndex={1}>
<VariablesMenu
variables={responseKeys || []}
onVariableSelect={handleVariableSelected}
/>
</Box>
{responseKeys.length > 0 && (
<Box position="absolute" bottom="14px" right="14px" zIndex={1}>
<VariablesMenu
variables={responseKeys || []}
onVariableSelect={handleVariableSelected}
/>
</Box>
)}
</Box>
<Text mt={2} fontSize="xs" color="gray.500">
A IA usará este texto como base. Não é necessário dar comandos de
Expand All @@ -250,19 +367,21 @@ Use as variáveis: {{ numero-ticket }}, {{ status-ticket }},
</Box>
)}

<Button
disabled={isTesting || !step?.content?.systemMessage?.length}
w="full"
colorScheme="blue"
onClick={handleTestReturn}
>
<HStack alignItems="center" gap={2}>
{isTesting && <Spinner size="sm" />}
<Text>
{isTesting ? 'Testando retorno...' : 'Testar retorno'}
</Text>
</HStack>
</Button>
{isAutomatedTasksBot && (
<Button
disabled={isTesting || !step?.content?.systemMessage?.length}
w="full"
colorScheme="blue"
onClick={handleTestReturn}
>
<HStack alignItems="center" gap={2}>
{isTesting && <Spinner size="sm" />}
<Text>
{isTesting ? 'Testando retorno...' : 'Testar retorno'}
</Text>
</HStack>
</Button>
)}
</VStack>
</Stack>
)
Expand Down
Loading
Loading