From 3506d86d50a8994aa8b67a6808d9ce89f594c196 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Wed, 19 Jan 2022 14:25:15 +0100 Subject: [PATCH] =?UTF-8?q?feat(integration):=20=E2=9C=A8=20Add=20Google?= =?UTF-8?q?=20Analytics=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/builder/assets/logos.tsx | 100 +++++++++++++++ .../board/StepTypesList/StepIcon.tsx | 4 +- .../board/StepTypesList/StepTypeLabel.tsx | 15 ++- .../SettingsPopoverContent.tsx | 34 +++-- .../bodies/GoogleAnalyticsSettings.tsx | 121 ++++++++++++++++++ .../UpdateCellList.tsx | 6 +- .../BlockNode/StepNode/SourceEndpoint.tsx | 2 +- .../BlockNode/StepNode/StepNodeContent.tsx | 5 + ...riable.tsx => InputWithVariableButton.tsx} | 21 ++- .../components/shared/VariableSearchInput.tsx | 109 ++++++++-------- apps/builder/cypress/plugins/data.ts | 50 ++++++++ apps/builder/cypress/tests/inputs.ts | 56 +------- .../tests/integrations/googleAnalytics.ts | 36 ++++++ .../googleSheets.ts} | 0 packages/bot-engine/lib/gtag.ts | 37 ++++++ .../ChatStep/bubbles/HostMessageBubble.tsx | 3 +- .../bot-engine/src/services/integration.ts | 31 ++++- packages/bot-engine/src/services/logic.ts | 2 +- packages/bot-engine/src/services/variable.ts | 26 +++- .../models/src/typebot/steps/integration.ts | 20 ++- packages/utils/src/utils.ts | 2 +- 21 files changed, 528 insertions(+), 152 deletions(-) create mode 100644 apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleAnalyticsSettings.tsx rename apps/builder/components/shared/{InputWithVariable.tsx => InputWithVariableButton.tsx} (87%) create mode 100644 apps/builder/cypress/tests/integrations/googleAnalytics.ts rename apps/builder/cypress/tests/{integrations.ts => integrations/googleSheets.ts} (100%) create mode 100644 packages/bot-engine/lib/gtag.ts diff --git a/apps/builder/assets/logos.tsx b/apps/builder/assets/logos.tsx index f0a81f9212..287529ca58 100644 --- a/apps/builder/assets/logos.tsx +++ b/apps/builder/assets/logos.tsx @@ -244,3 +244,103 @@ export const GoogleSheetsLogo = (props: IconProps) => ( ) + +export const GoogleAnalyticsLogo = (props: IconProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/apps/builder/components/board/StepTypesList/StepIcon.tsx b/apps/builder/components/board/StepTypesList/StepIcon.tsx index 752778e459..dfc9a08169 100644 --- a/apps/builder/components/board/StepTypesList/StepIcon.tsx +++ b/apps/builder/components/board/StepTypesList/StepIcon.tsx @@ -12,7 +12,7 @@ import { PhoneIcon, TextIcon, } from 'assets/icons' -import { GoogleSheetsLogo } from 'assets/logos' +import { GoogleAnalyticsLogo, GoogleSheetsLogo } from 'assets/logos' import { BubbleStepType, InputStepType, @@ -48,6 +48,8 @@ export const StepIcon = ({ type, ...props }: StepIconProps) => { return case IntegrationStepType.GOOGLE_SHEETS: return + case IntegrationStepType.GOOGLE_ANALYTICS: + return case 'start': return default: diff --git a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx index 34a94553c2..b0b292ce30 100644 --- a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx +++ b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx @@ -1,4 +1,4 @@ -import { Text } from '@chakra-ui/react' +import { Text, Tooltip } from '@chakra-ui/react' import { BubbleStepType, InputStepType, @@ -41,7 +41,18 @@ export const StepTypeLabel = ({ type }: Props) => { return Condition } case IntegrationStepType.GOOGLE_SHEETS: { - return Sheets + return ( + + Sheets + + ) + } + case IntegrationStepType.GOOGLE_ANALYTICS: { + return ( + + Analytics + + ) } default: { return <> diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx index c2cbc885ef..3c33058270 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx @@ -4,9 +4,7 @@ import { PopoverBody, useEventListener, Portal, - Stack, IconButton, - Flex, } from '@chakra-ui/react' import { ExpandIcon } from 'assets/icons' import { useTypebot } from 'contexts/TypebotContext/TypebotContext' @@ -28,6 +26,7 @@ import { } from './bodies' import { ChoiceInputSettingsBody } from './bodies/ChoiceInputSettingsBody' import { ConditionSettingsBody } from './bodies/ConditionSettingsBody' +import { GoogleAnalyticsSettings } from './bodies/GoogleAnalyticsSettings' import { GoogleSheetsSettingsBody } from './bodies/GoogleSheetsSettingsBody' import { PhoneNumberSettingsBody } from './bodies/PhoneNumberSettingsBody' import { SetVariableSettingsBody } from './bodies/SetVariableSettingsBody' @@ -47,7 +46,7 @@ export const SettingsPopoverContent = ({ step, onExpandClick }: Props) => { useEventListener('wheel', handleMouseWheel, ref.current) return ( - + { ref={ref} shadow="lg" > - - - } - size="xs" - onClick={onExpandClick} - /> - - - + + } + size="xs" + onClick={onExpandClick} + /> ) @@ -162,6 +160,14 @@ export const StepSettings = ({ step }: { step: Step }) => { /> ) } + case IntegrationStepType.GOOGLE_ANALYTICS: { + return ( + + ) + } default: { return <> } diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleAnalyticsSettings.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleAnalyticsSettings.tsx new file mode 100644 index 0000000000..9d6a4673ce --- /dev/null +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleAnalyticsSettings.tsx @@ -0,0 +1,121 @@ +import { + Accordion, + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Box, + FormLabel, + Stack, + Tag, +} from '@chakra-ui/react' +import { DebouncedInput } from 'components/shared/DebouncedInput' +import { InputWithVariableButton } from 'components/shared/InputWithVariableButton' +import { GoogleAnalyticsOptions } from 'models' +import React from 'react' + +type Props = { + options?: GoogleAnalyticsOptions + onOptionsChange: (options: GoogleAnalyticsOptions) => void +} + +export const GoogleAnalyticsSettings = ({ + options, + onOptionsChange, +}: Props) => { + const handleTrackingIdChange = (trackingId: string) => + onOptionsChange({ ...options, trackingId }) + + const handleCategoryChange = (category: string) => + onOptionsChange({ ...options, category }) + + const handleActionChange = (action: string) => + onOptionsChange({ ...options, action }) + + const handleLabelChange = (label: string) => + onOptionsChange({ ...options, label }) + + const handleValueChange = (value?: string) => + onOptionsChange({ + ...options, + value: value ? parseFloat(value) : undefined, + }) + + return ( + + + + Tracking ID: + + + + + + Event category: + + + + + + Event action: + + + + + +

+ + + Advanced + + + +

+ + + + Event label Optional: + + + + + + Event value Optional: + + + + +
+
+
+ ) +} diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleSheetsSettingsBody/UpdateCellList.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleSheetsSettingsBody/UpdateCellList.tsx index 6f329694ea..28ee2e01ef 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleSheetsSettingsBody/UpdateCellList.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/GoogleSheetsSettingsBody/UpdateCellList.tsx @@ -1,7 +1,7 @@ import { Button, Fade, Flex, IconButton, Stack } from '@chakra-ui/react' import { PlusIcon, TrashIcon } from 'assets/icons' import { DropdownList } from 'components/shared/DropdownList' -import { InputWithVariable } from 'components/shared/InputWithVariable' +import { InputWithVariableButton } from 'components/shared/InputWithVariableButton' import { Cell, Table } from 'models' import React, { useEffect, useState } from 'react' import { Sheet } from 'services/integrations' @@ -131,9 +131,9 @@ export const CellWithValueStack = ({ items={columns} placeholder="Select a column" /> - diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SourceEndpoint.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SourceEndpoint.tsx index 4812b55e73..cd5f6c761d 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/SourceEndpoint.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SourceEndpoint.tsx @@ -41,7 +41,7 @@ export const SourceEndpoint = ({ align="center" {...props} > - + ) } diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeContent.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeContent.tsx index c0774fe5c5..7654a33c1a 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeContent.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeContent.tsx @@ -89,6 +89,11 @@ export const StepNodeContent = ({ step }: Props) => { if (!step.options) return Configure... return {step.options?.action} } + case IntegrationStepType.GOOGLE_ANALYTICS: { + if (!step.options || !step.options.action) + return Configure... + return Track "{step.options?.action}" + } case 'start': { return {step.label} } diff --git a/apps/builder/components/shared/InputWithVariable.tsx b/apps/builder/components/shared/InputWithVariableButton.tsx similarity index 87% rename from apps/builder/components/shared/InputWithVariable.tsx rename to apps/builder/components/shared/InputWithVariableButton.tsx index a381a2bab6..6a9fdb2bde 100644 --- a/apps/builder/components/shared/InputWithVariable.tsx +++ b/apps/builder/components/shared/InputWithVariableButton.tsx @@ -14,23 +14,23 @@ import React, { useEffect, useRef, useState } from 'react' import { useDebounce } from 'use-debounce' import { VariableSearchInput } from './VariableSearchInput' -export const InputWithVariable = ({ +export const InputWithVariableButton = ({ initialValue, - noAbsolute, - onValueChange, + onChange, + delay, ...props }: { initialValue: string - onValueChange: (value: string) => void - noAbsolute?: boolean -} & InputProps) => { + onChange: (value: string) => void + delay?: number +} & Omit) => { const inputRef = useRef(null) const [value, setValue] = useState(initialValue) - const [debouncedValue] = useDebounce(value, 100) + const [debouncedValue] = useDebounce(value, delay ?? 100) const [carretPosition, setCarretPosition] = useState(0) useEffect(() => { - onValueChange(debouncedValue) + onChange(debouncedValue) // eslint-disable-next-line react-hooks/exhaustive-deps }, [debouncedValue]) @@ -77,10 +77,7 @@ export const InputWithVariable = ({ {...props} bgColor={'white'} /> - + - - {(inputValue?.length ?? 0) > 0 && - !isDefined(variables.find((v) => v.name === inputValue)) && ( - + + + {(inputValue?.length ?? 0) > 0 && + !isDefined(variables.find((v) => v.name === inputValue)) && ( + + )} + {filteredItems.length > 0 && ( + <> + {filteredItems.map((item, idx) => { + return ( + + ) + })} + )} - {filteredItems.length > 0 && ( - <> - {filteredItems.map((item, idx) => { - return ( - - ) - })} - - )} - + + ) diff --git a/apps/builder/cypress/plugins/data.ts b/apps/builder/cypress/plugins/data.ts index 294874a49d..a2a8eb9838 100644 --- a/apps/builder/cypress/plugins/data.ts +++ b/apps/builder/cypress/plugins/data.ts @@ -1 +1,51 @@ +import { Step, InputStepType } from 'models' +import { parseTestTypebot } from './utils' + export const userIds = ['user1', 'user2'] + +export const createTypebotWithStep = (step: Omit) => { + cy.task( + 'createTypebot', + parseTestTypebot({ + id: 'typebot3', + name: 'Typebot #3', + ownerId: userIds[1], + steps: { + byId: { + step1: { + ...step, + id: 'step1', + blockId: 'block1', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + options: + step.type === InputStepType.CHOICE + ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + { itemIds: ['item1'] } + : undefined, + }, + }, + allIds: ['step1'], + }, + blocks: { + byId: { + block1: { + id: 'block1', + graphCoordinates: { x: 400, y: 200 }, + title: 'Block #1', + stepIds: ['step1'], + }, + }, + allIds: ['block1'], + }, + choiceItems: + step.type === InputStepType.CHOICE + ? { + byId: { item1: { stepId: 'step1', id: 'item1' } }, + allIds: ['item1'], + } + : undefined, + }) + ) +} diff --git a/apps/builder/cypress/tests/inputs.ts b/apps/builder/cypress/tests/inputs.ts index f75ae1e444..339197d2da 100644 --- a/apps/builder/cypress/tests/inputs.ts +++ b/apps/builder/cypress/tests/inputs.ts @@ -1,10 +1,7 @@ -import { userIds } from 'cypress/plugins/data' -import { - parseTestTypebot, - preventUserFromRefreshing, -} from 'cypress/plugins/utils' +import { createTypebotWithStep } from 'cypress/plugins/data' +import { preventUserFromRefreshing } from 'cypress/plugins/utils' import { getIframeBody } from 'cypress/support' -import { InputStep, InputStepType } from 'models' +import { InputStepType } from 'models' describe('Text input', () => { beforeEach(() => { @@ -262,50 +259,3 @@ describe('Button input', () => { getIframeBody().findByText('Cool!').should('exist') }) }) - -const createTypebotWithStep = (step: Omit) => { - cy.task( - 'createTypebot', - parseTestTypebot({ - id: 'typebot3', - name: 'Typebot #3', - ownerId: userIds[1], - steps: { - byId: { - step1: { - ...step, - id: 'step1', - blockId: 'block1', - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - options: - step.type === InputStepType.CHOICE - ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - { itemIds: ['item1'] } - : undefined, - }, - }, - allIds: ['step1'], - }, - blocks: { - byId: { - block1: { - id: 'block1', - graphCoordinates: { x: 400, y: 200 }, - title: 'Block #1', - stepIds: ['step1'], - }, - }, - allIds: ['block1'], - }, - choiceItems: - step.type === InputStepType.CHOICE - ? { - byId: { item1: { stepId: 'step1', id: 'item1' } }, - allIds: ['item1'], - } - : undefined, - }) - ) -} diff --git a/apps/builder/cypress/tests/integrations/googleAnalytics.ts b/apps/builder/cypress/tests/integrations/googleAnalytics.ts new file mode 100644 index 0000000000..a1cdb73574 --- /dev/null +++ b/apps/builder/cypress/tests/integrations/googleAnalytics.ts @@ -0,0 +1,36 @@ +import { createTypebotWithStep } from 'cypress/plugins/data' +import { preventUserFromRefreshing } from 'cypress/plugins/utils' +import { IntegrationStepType } from 'models' + +describe('Google Analytics', () => { + beforeEach(() => { + cy.task('seed') + createTypebotWithStep({ type: IntegrationStepType.GOOGLE_ANALYTICS }) + cy.signOut() + }) + + afterEach(() => { + cy.window().then((win) => { + win.removeEventListener('beforeunload', preventUserFromRefreshing) + }) + }) + + it.only('can be filled correctly', () => { + cy.signIn('test2@gmail.com') + cy.visit('/typebots/typebot3/edit') + cy.intercept({ + url: '/g/collect', + method: 'POST', + }).as('gaRequest') + cy.findByTestId('step-step1').click() + cy.findByRole('textbox', { name: 'Tracking ID:' }).type('G-VWX9WG1TNS') + cy.findByRole('textbox', { name: 'Event category:' }).type('Typebot') + cy.findByRole('textbox', { name: 'Event action:' }).type('Submit email') + cy.findByRole('button', { name: 'Advanced' }).click() + cy.findByRole('textbox', { name: 'Event label Optional :' }).type( + 'Campaign Z' + ) + cy.findByRole('textbox', { name: 'Event value Optional :' }).type('20') + // Not sure how to test if GA integration works correctly in the preview tab + }) +}) diff --git a/apps/builder/cypress/tests/integrations.ts b/apps/builder/cypress/tests/integrations/googleSheets.ts similarity index 100% rename from apps/builder/cypress/tests/integrations.ts rename to apps/builder/cypress/tests/integrations/googleSheets.ts diff --git a/packages/bot-engine/lib/gtag.ts b/packages/bot-engine/lib/gtag.ts new file mode 100644 index 0000000000..b2f11d8f78 --- /dev/null +++ b/packages/bot-engine/lib/gtag.ts @@ -0,0 +1,37 @@ +import { GoogleAnalyticsOptions } from 'models' + +declare const gtag: any + +const initGoogleAnalytics = (id: string): Promise => + new Promise((resolve) => { + const existingScript = document.getElementById('gtag') + if (!existingScript) { + const script = document.createElement('script') + script.src = `https://www.googletagmanager.com/gtag/js?id=${id}` + script.id = 'gtag' + const initScript = document.createElement('script') + initScript.innerHTML = `window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', '${id}'); + ` + document.body.appendChild(script) + document.body.appendChild(initScript) + script.onload = () => { + resolve() + } + } + if (existingScript) resolve() + }) + +export const sendGaEvent = (options: GoogleAnalyticsOptions) => { + if (!options) return + gtag('event', options.action, { + event_category: options.category, + event_label: options.label, + value: options.value, + }) +} + +export default initGoogleAnalytics diff --git a/packages/bot-engine/src/components/ChatBlock/ChatStep/bubbles/HostMessageBubble.tsx b/packages/bot-engine/src/components/ChatBlock/ChatStep/bubbles/HostMessageBubble.tsx index 768a3b8a4a..d39cfc5585 100644 --- a/packages/bot-engine/src/components/ChatBlock/ChatStep/bubbles/HostMessageBubble.tsx +++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/bubbles/HostMessageBubble.tsx @@ -26,7 +26,8 @@ export const HostMessageBubble = ({ const [isTyping, setIsTyping] = useState(true) const content = useMemo( - () => parseVariables(step.content.html, typebot.variables), + () => + parseVariables({ text: step.content.html, variables: typebot.variables }), [typebot.variables] ) diff --git a/packages/bot-engine/src/services/integration.ts b/packages/bot-engine/src/services/integration.ts index 94dde04406..fc913bf8ba 100644 --- a/packages/bot-engine/src/services/integration.ts +++ b/packages/bot-engine/src/services/integration.ts @@ -9,10 +9,12 @@ import { GoogleSheetsUpdateRowOptions, Cell, GoogleSheetsGetOptions, + GoogleAnalyticsStep, } from 'models' import { stringify } from 'qs' import { sendRequest } from 'utils' -import { parseVariables } from './variable' +import { sendGaEvent } from '../../lib/gtag' +import { parseVariables, parseVariablesInObject } from './variable' export const executeIntegration = ( step: IntegrationStep, @@ -22,9 +24,21 @@ export const executeIntegration = ( switch (step.type) { case IntegrationStepType.GOOGLE_SHEETS: return executeGoogleSheetIntegration(step, variables, updateVariableValue) + case IntegrationStepType.GOOGLE_ANALYTICS: + return executeGoogleAnalyticsIntegration(step, variables) } } +export const executeGoogleAnalyticsIntegration = async ( + step: GoogleAnalyticsStep, + variables: Table +) => { + if (!step.options?.trackingId) return + const { default: initGoogleAnalytics } = await import('../../lib/gtag') + await initGoogleAnalytics(step.options.trackingId) + sendGaEvent(parseVariablesInObject(step.options, variables)) +} + const executeGoogleSheetIntegration = async ( step: GoogleSheetsStep, variables: Table, @@ -73,7 +87,10 @@ const updateRowInGoogleSheets = async ( values: parseCellValues(options.cellsToUpsert, variables), referenceCell: { column: options.referenceCell.column, - value: parseVariables(options.referenceCell.value ?? '', variables), + value: parseVariables({ + text: options.referenceCell.value ?? '', + variables, + }), }, }, }) @@ -90,7 +107,10 @@ const getRowFromGoogleSheets = async ( credentialsId: options.credentialsId, referenceCell: { column: options.referenceCell.column, - value: parseVariables(options.referenceCell.value ?? '', variables), + value: parseVariables({ + text: options.referenceCell.value ?? '', + variables, + }), }, columns: options.cellsToExtract.allIds.map( (id) => options.cellsToExtract?.byId[id].column @@ -117,5 +137,8 @@ const parseCellValues = ( const cell = cells.byId[id] return !cell.column || !cell.value ? row - : { ...row, [cell.column]: parseVariables(cell.value, variables) } + : { + ...row, + [cell.column]: parseVariables({ text: cell.value, variables }), + } }, {}) diff --git a/packages/bot-engine/src/services/logic.ts b/packages/bot-engine/src/services/logic.ts index 49a67a6226..59cde15fee 100644 --- a/packages/bot-engine/src/services/logic.ts +++ b/packages/bot-engine/src/services/logic.ts @@ -22,7 +22,7 @@ export const executeLogic = ( return const expression = step.options.expressionToEvaluate const evaluatedExpression = isMathFormula(expression) - ? evaluateExpression(parseVariables(expression, variables)) + ? evaluateExpression(parseVariables({ text: expression, variables })) : expression updateVariableValue(step.options.variableId, evaluatedExpression) return diff --git a/packages/bot-engine/src/services/variable.ts b/packages/bot-engine/src/services/variable.ts index 418f89b537..501ea136c1 100644 --- a/packages/bot-engine/src/services/variable.ts +++ b/packages/bot-engine/src/services/variable.ts @@ -6,11 +6,14 @@ const safeEval = eval export const stringContainsVariable = (str: string): boolean => /\{\{(.*?)\}\}/g.test(str) -export const parseVariables = ( - text: string, +export const parseVariables = ({ + text, + variables, +}: { + text?: string variables: Table -): string => { - if (text === '') return text +}): string => { + if (!text || text === '') return '' return text.replace(/\{\{(.*?)\}\}/g, (_, fullVariableString) => { const matchedVarName = fullVariableString.replace(/{{|}}/g, '') const matchedVariableId = variables.allIds.find((variableId) => { @@ -44,3 +47,18 @@ const countDecimals = (value: number) => { if (value % 1 != 0) return value.toString().split('.')[1].length return 0 } + +export const parseVariablesInObject = ( + object: { [key: string]: string | number }, + variables: Table +) => + Object.keys(object).reduce((newObj, key) => { + const currentValue = object[key] + return { + ...newObj, + [key]: + typeof currentValue === 'string' + ? parseVariables({ text: currentValue, variables }) + : currentValue, + } + }, {}) diff --git a/packages/models/src/typebot/steps/integration.ts b/packages/models/src/typebot/steps/integration.ts index 0f4c089471..663b04d6f1 100644 --- a/packages/models/src/typebot/steps/integration.ts +++ b/packages/models/src/typebot/steps/integration.ts @@ -1,12 +1,15 @@ import { StepBase } from '.' import { Table } from '../..' -export type IntegrationStep = GoogleSheetsStep +export type IntegrationStep = GoogleSheetsStep | GoogleAnalyticsStep -export type IntegrationStepOptions = GoogleSheetsOptions +export type IntegrationStepOptions = + | GoogleSheetsOptions + | GoogleAnalyticsOptions export enum IntegrationStepType { GOOGLE_SHEETS = 'Google Sheets', + GOOGLE_ANALYTICS = 'Google Analytics', } export type GoogleSheetsStep = StepBase & { @@ -14,6 +17,19 @@ export type GoogleSheetsStep = StepBase & { options?: GoogleSheetsOptions } +export type GoogleAnalyticsStep = StepBase & { + type: IntegrationStepType.GOOGLE_ANALYTICS + options?: GoogleAnalyticsOptions +} + +export type GoogleAnalyticsOptions = { + trackingId?: string + category?: string + action?: string + label?: string + value?: number +} + export enum GoogleSheetsAction { GET = 'Get data from sheet', INSERT_ROW = 'Insert a row', diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index ef119b96b1..7b63b11131 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -73,4 +73,4 @@ export const isConditionStep = (step: Step): step is ConditionStep => step.type === LogicStepType.CONDITION export const isIntegrationStep = (step: Step): step is IntegrationStep => - step.type === IntegrationStepType.GOOGLE_SHEETS + (Object.values(IntegrationStepType) as string[]).includes(step.type)