feat(integration): ✨ Add Google Sheets integration
This commit is contained in:
@ -3,17 +3,16 @@ import {
|
||||
PopoverArrow,
|
||||
PopoverBody,
|
||||
useEventListener,
|
||||
Portal,
|
||||
} from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import {
|
||||
ChoiceInputOptions,
|
||||
ConditionOptions,
|
||||
InputStep,
|
||||
InputStepType,
|
||||
IntegrationStepType,
|
||||
LogicStepType,
|
||||
SetVariableOptions,
|
||||
Step,
|
||||
TextInputOptions,
|
||||
StepOptions,
|
||||
} from 'models'
|
||||
import { useRef } from 'react'
|
||||
import {
|
||||
@ -25,6 +24,7 @@ import {
|
||||
} from './bodies'
|
||||
import { ChoiceInputSettingsBody } from './bodies/ChoiceInputSettingsBody'
|
||||
import { ConditionSettingsBody } from './bodies/ConditionSettingsBody'
|
||||
import { GoogleSheetsSettingsBody } from './bodies/GoogleSheetsSettingsBody'
|
||||
import { PhoneNumberSettingsBody } from './bodies/PhoneNumberSettingsBody'
|
||||
import { SetVariableSettingsBody } from './bodies/SetVariableSettingsBody'
|
||||
|
||||
@ -41,24 +41,21 @@ export const SettingsPopoverContent = ({ step }: Props) => {
|
||||
}
|
||||
useEventListener('wheel', handleMouseWheel, ref.current)
|
||||
return (
|
||||
<PopoverContent onMouseDown={handleMouseDown}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody p="6" overflowY="scroll" maxH="400px" ref={ref}>
|
||||
<SettingsPopoverBodyContent step={step} />
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
<Portal>
|
||||
<PopoverContent onMouseDown={handleMouseDown}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody p="6" overflowY="scroll" maxH="400px" ref={ref}>
|
||||
<SettingsPopoverBodyContent step={step} />
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Portal>
|
||||
)
|
||||
}
|
||||
|
||||
const SettingsPopoverBodyContent = ({ step }: Props) => {
|
||||
const { updateStep } = useTypebot()
|
||||
const handleOptionsChange = (
|
||||
options:
|
||||
| TextInputOptions
|
||||
| ChoiceInputOptions
|
||||
| SetVariableOptions
|
||||
| ConditionOptions
|
||||
) => updateStep(step.id, { options } as Partial<InputStep>)
|
||||
const handleOptionsChange = (options: StepOptions) =>
|
||||
updateStep(step.id, { options } as Partial<InputStep>)
|
||||
|
||||
switch (step.type) {
|
||||
case InputStepType.TEXT: {
|
||||
@ -133,6 +130,15 @@ const SettingsPopoverBodyContent = ({ step }: Props) => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationStepType.GOOGLE_SHEETS: {
|
||||
return (
|
||||
<GoogleSheetsSettingsBody
|
||||
options={step.options}
|
||||
onOptionsChange={handleOptionsChange}
|
||||
stepId={step.id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default: {
|
||||
return <></>
|
||||
}
|
||||
|
@ -0,0 +1,136 @@
|
||||
import { Button, Fade, Flex, IconButton, Stack } from '@chakra-ui/react'
|
||||
import { PlusIcon, TrashIcon } from 'assets/icons'
|
||||
import { DropdownList } from 'components/shared/DropdownList'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { ExtractingCell, Table, Variable } from 'models'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Sheet } from 'services/integrations'
|
||||
import { generate } from 'short-uuid'
|
||||
import { useImmer } from 'use-immer'
|
||||
|
||||
type Props = {
|
||||
sheet: Sheet
|
||||
initialCells?: Table<ExtractingCell>
|
||||
onCellsChange: (cells: Table<ExtractingCell>) => void
|
||||
}
|
||||
|
||||
const id = generate()
|
||||
const defaultCells: Table<ExtractingCell> = {
|
||||
byId: { [id]: {} },
|
||||
allIds: [id],
|
||||
}
|
||||
|
||||
export const ExtractCellList = ({
|
||||
sheet,
|
||||
initialCells,
|
||||
onCellsChange,
|
||||
}: Props) => {
|
||||
const [cells, setCells] = useImmer(initialCells ?? defaultCells)
|
||||
const [showDeleteId, setShowDeleteId] = useState<string | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
onCellsChange(cells)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [cells])
|
||||
|
||||
const createCell = () => {
|
||||
setCells((cells) => {
|
||||
const id = generate()
|
||||
cells.byId[id] = {}
|
||||
cells.allIds.push(id)
|
||||
})
|
||||
}
|
||||
|
||||
const updateCell = (cellId: string, updates: Partial<ExtractingCell>) =>
|
||||
setCells((cells) => {
|
||||
cells.byId[cellId] = {
|
||||
...cells.byId[cellId],
|
||||
...updates,
|
||||
}
|
||||
})
|
||||
|
||||
const deleteCell = (cellId: string) => () => {
|
||||
setCells((cells) => {
|
||||
delete cells.byId[cellId]
|
||||
const index = cells.allIds.indexOf(cellId)
|
||||
if (index !== -1) cells.allIds.splice(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseEnter = (cellId: string) => () => {
|
||||
setShowDeleteId(cellId)
|
||||
}
|
||||
|
||||
const handleCellChange = (cellId: string) => (cell: ExtractingCell) =>
|
||||
updateCell(cellId, cell)
|
||||
|
||||
const handleMouseLeave = () => setShowDeleteId(undefined)
|
||||
|
||||
return (
|
||||
<Stack spacing="4">
|
||||
{cells.allIds.map((cellId) => (
|
||||
<>
|
||||
<Flex
|
||||
pos="relative"
|
||||
onMouseEnter={handleMouseEnter(cellId)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<CellWithVariableIdStack
|
||||
key={cellId}
|
||||
cell={cells.byId[cellId]}
|
||||
columns={sheet.columns}
|
||||
onCellChange={handleCellChange(cellId)}
|
||||
/>
|
||||
<Fade in={showDeleteId === cellId}>
|
||||
<IconButton
|
||||
icon={<TrashIcon />}
|
||||
aria-label="Remove cell"
|
||||
onClick={deleteCell(cellId)}
|
||||
pos="absolute"
|
||||
left="-10px"
|
||||
top="-10px"
|
||||
size="sm"
|
||||
/>
|
||||
</Fade>
|
||||
</Flex>
|
||||
</>
|
||||
))}
|
||||
<Button leftIcon={<PlusIcon />} onClick={createCell} flexShrink={0}>
|
||||
Add
|
||||
</Button>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export const CellWithVariableIdStack = ({
|
||||
cell,
|
||||
columns,
|
||||
onCellChange,
|
||||
}: {
|
||||
cell: ExtractingCell
|
||||
columns: string[]
|
||||
onCellChange: (cell: ExtractingCell) => void
|
||||
}) => {
|
||||
const handleColumnSelect = (column: string) => {
|
||||
onCellChange({ ...cell, column })
|
||||
}
|
||||
const handleVariableIdChange = (variable: Variable) => {
|
||||
onCellChange({ ...cell, variableId: variable.id })
|
||||
}
|
||||
return (
|
||||
<Stack bgColor="blue.50" p="4" rounded="md" flex="1">
|
||||
<DropdownList<string>
|
||||
currentItem={cell.column}
|
||||
onItemSelect={handleColumnSelect}
|
||||
items={columns}
|
||||
bgColor="white"
|
||||
placeholder="Select a column"
|
||||
/>
|
||||
<VariableSearchInput
|
||||
initialVariableId={cell.variableId}
|
||||
onSelectVariable={handleVariableIdChange}
|
||||
placeholder="Select a variable"
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import { Divider, Stack, Text } from '@chakra-ui/react'
|
||||
import { CredentialsDropdown } from 'components/shared/CredentialsDropdown'
|
||||
import { DropdownList } from 'components/shared/DropdownList'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { CredentialsType } from 'db'
|
||||
import {
|
||||
Cell,
|
||||
ExtractingCell,
|
||||
GoogleSheetsAction,
|
||||
GoogleSheetsOptions,
|
||||
Table,
|
||||
} from 'models'
|
||||
import React, { useMemo } from 'react'
|
||||
import {
|
||||
getGoogleSheetsConsentScreenUrl,
|
||||
Sheet,
|
||||
useSheets,
|
||||
} from 'services/integrations'
|
||||
import { isDefined } from 'utils'
|
||||
import { ExtractCellList } from './ExtractCellList'
|
||||
import { SheetsDropdown } from './SheetsDropdown'
|
||||
import { SpreadsheetsDropdown } from './SpreadsheetDropdown'
|
||||
import { CellWithValueStack, UpdateCellList } from './UpdateCellList'
|
||||
|
||||
type Props = {
|
||||
options?: GoogleSheetsOptions
|
||||
onOptionsChange: (options: GoogleSheetsOptions) => void
|
||||
stepId: string
|
||||
}
|
||||
|
||||
export const GoogleSheetsSettingsBody = ({
|
||||
options,
|
||||
onOptionsChange,
|
||||
stepId,
|
||||
}: Props) => {
|
||||
const { save, hasUnsavedChanges } = useTypebot()
|
||||
const { sheets, isLoading } = useSheets({
|
||||
credentialsId: options?.credentialsId,
|
||||
spreadsheetId: options?.spreadsheetId,
|
||||
})
|
||||
const sheet = useMemo(
|
||||
() => sheets?.find((s) => s.id === options?.sheetId),
|
||||
[sheets, options?.sheetId]
|
||||
)
|
||||
const handleCredentialsIdChange = (credentialsId: string) =>
|
||||
onOptionsChange({ ...options, credentialsId })
|
||||
const handleSpreadsheetIdChange = (spreadsheetId: string) =>
|
||||
onOptionsChange({ ...options, spreadsheetId })
|
||||
const handleSheetIdChange = (sheetId: string) =>
|
||||
onOptionsChange({ ...options, sheetId })
|
||||
const handleActionChange = (action: GoogleSheetsAction) =>
|
||||
onOptionsChange({ ...options, action })
|
||||
|
||||
const handleCreateNewClick = async () => {
|
||||
if (hasUnsavedChanges) {
|
||||
const errorToastId = await save()
|
||||
if (errorToastId) return
|
||||
}
|
||||
const linkElement = document.createElement('a')
|
||||
linkElement.href = getGoogleSheetsConsentScreenUrl(
|
||||
window.location.href,
|
||||
stepId
|
||||
)
|
||||
linkElement.click()
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CredentialsDropdown
|
||||
type={CredentialsType.GOOGLE_SHEETS}
|
||||
currentCredentialsId={options?.credentialsId}
|
||||
onCredentialsSelect={handleCredentialsIdChange}
|
||||
onCreateNewClick={handleCreateNewClick}
|
||||
/>
|
||||
{options?.credentialsId && (
|
||||
<SpreadsheetsDropdown
|
||||
credentialsId={options.credentialsId}
|
||||
spreadsheetId={options.spreadsheetId}
|
||||
onSelectSpreadsheetId={handleSpreadsheetIdChange}
|
||||
/>
|
||||
)}
|
||||
{options?.spreadsheetId && options.credentialsId && (
|
||||
<SheetsDropdown
|
||||
sheets={sheets ?? []}
|
||||
isLoading={isLoading}
|
||||
sheetId={options.sheetId}
|
||||
onSelectSheetId={handleSheetIdChange}
|
||||
/>
|
||||
)}
|
||||
{options?.spreadsheetId &&
|
||||
options.credentialsId &&
|
||||
isDefined(options.sheetId) && (
|
||||
<>
|
||||
<Divider />
|
||||
<DropdownList<GoogleSheetsAction>
|
||||
currentItem={options.action}
|
||||
onItemSelect={handleActionChange}
|
||||
items={Object.values(GoogleSheetsAction)}
|
||||
placeholder="Select an operation"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{sheet && options?.action && (
|
||||
<ActionOptions
|
||||
options={options}
|
||||
sheet={sheet}
|
||||
onOptionsChange={onOptionsChange}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
const ActionOptions = ({
|
||||
options,
|
||||
sheet,
|
||||
onOptionsChange,
|
||||
}: {
|
||||
options: GoogleSheetsOptions
|
||||
sheet: Sheet
|
||||
onOptionsChange: (options: GoogleSheetsOptions) => void
|
||||
}) => {
|
||||
const handleInsertColumnsChange = (cellsToInsert: Table<Cell>) =>
|
||||
onOptionsChange({ ...options, cellsToInsert } as GoogleSheetsOptions)
|
||||
|
||||
const handleUpsertColumnsChange = (cellsToUpsert: Table<Cell>) =>
|
||||
onOptionsChange({ ...options, cellsToUpsert } as GoogleSheetsOptions)
|
||||
|
||||
const handleReferenceCellChange = (referenceCell: Cell) =>
|
||||
onOptionsChange({ ...options, referenceCell } as GoogleSheetsOptions)
|
||||
|
||||
const handleExtractingCellsChange = (cellsToExtract: Table<ExtractingCell>) =>
|
||||
onOptionsChange({ ...options, cellsToExtract } as GoogleSheetsOptions)
|
||||
|
||||
switch (options.action) {
|
||||
case GoogleSheetsAction.INSERT_ROW:
|
||||
return (
|
||||
<UpdateCellList
|
||||
initialCells={options.cellsToInsert}
|
||||
sheet={sheet}
|
||||
onCellsChange={handleInsertColumnsChange}
|
||||
/>
|
||||
)
|
||||
case GoogleSheetsAction.UPDATE_ROW:
|
||||
return (
|
||||
<Stack>
|
||||
<Text>Row to select</Text>
|
||||
<CellWithValueStack
|
||||
cell={options.referenceCell ?? {}}
|
||||
columns={sheet.columns}
|
||||
onCellChange={handleReferenceCellChange}
|
||||
/>
|
||||
<Text>Cells to update</Text>
|
||||
<UpdateCellList
|
||||
initialCells={options.cellsToUpsert}
|
||||
sheet={sheet}
|
||||
onCellsChange={handleUpsertColumnsChange}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
case GoogleSheetsAction.GET:
|
||||
return (
|
||||
<Stack>
|
||||
<Text>Row to select</Text>
|
||||
<CellWithValueStack
|
||||
cell={options.referenceCell ?? {}}
|
||||
columns={sheet.columns}
|
||||
onCellChange={handleReferenceCellChange}
|
||||
/>
|
||||
<Text>Cells to extract</Text>
|
||||
<ExtractCellList
|
||||
initialCells={options.cellsToExtract}
|
||||
sheet={sheet}
|
||||
onCellsChange={handleExtractingCellsChange}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
default:
|
||||
return <></>
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { SearchableDropdown } from 'components/shared/SearchableDropdown'
|
||||
import { useMemo } from 'react'
|
||||
import { Sheet } from 'services/integrations'
|
||||
import { isDefined } from 'utils'
|
||||
|
||||
type Props = {
|
||||
sheets: Sheet[]
|
||||
isLoading: boolean
|
||||
sheetId?: string
|
||||
onSelectSheetId: (id: string) => void
|
||||
}
|
||||
|
||||
export const SheetsDropdown = ({
|
||||
sheets,
|
||||
isLoading,
|
||||
sheetId,
|
||||
onSelectSheetId,
|
||||
}: Props) => {
|
||||
const currentSheet = useMemo(
|
||||
() => sheets?.find((s) => s.id === sheetId),
|
||||
[sheetId, sheets]
|
||||
)
|
||||
|
||||
const handleSpreadsheetSelect = (name: string) => {
|
||||
const id = sheets?.find((s) => s.name === name)?.id
|
||||
if (isDefined(id)) onSelectSheetId(id)
|
||||
}
|
||||
return (
|
||||
<SearchableDropdown
|
||||
selectedItem={currentSheet?.name}
|
||||
items={(sheets ?? []).map((s) => s.name)}
|
||||
onSelectItem={handleSpreadsheetSelect}
|
||||
placeholder={isLoading ? 'Loading...' : 'Select the sheet'}
|
||||
isDisabled={isLoading}
|
||||
/>
|
||||
)
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { SearchableDropdown } from 'components/shared/SearchableDropdown'
|
||||
import { useMemo } from 'react'
|
||||
import { useSpreadsheets } from 'services/integrations'
|
||||
|
||||
type Props = {
|
||||
credentialsId: string
|
||||
spreadsheetId?: string
|
||||
onSelectSpreadsheetId: (id: string) => void
|
||||
}
|
||||
|
||||
export const SpreadsheetsDropdown = ({
|
||||
credentialsId,
|
||||
spreadsheetId,
|
||||
onSelectSpreadsheetId,
|
||||
}: Props) => {
|
||||
const { spreadsheets, isLoading } = useSpreadsheets({ credentialsId })
|
||||
const currentSpreadsheet = useMemo(
|
||||
() => spreadsheets?.find((s) => s.id === spreadsheetId),
|
||||
[spreadsheetId, spreadsheets]
|
||||
)
|
||||
|
||||
const handleSpreadsheetSelect = (name: string) => {
|
||||
const id = spreadsheets?.find((s) => s.name === name)?.id
|
||||
if (id) onSelectSpreadsheetId(id)
|
||||
}
|
||||
return (
|
||||
<SearchableDropdown
|
||||
selectedItem={currentSpreadsheet?.name}
|
||||
items={(spreadsheets ?? []).map((s) => s.name)}
|
||||
onSelectItem={handleSpreadsheetSelect}
|
||||
placeholder={isLoading ? 'Loading...' : 'Search for spreadsheet'}
|
||||
isDisabled={isLoading}
|
||||
/>
|
||||
)
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
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 { Cell, Table } from 'models'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Sheet } from 'services/integrations'
|
||||
import { generate } from 'short-uuid'
|
||||
import { useImmer } from 'use-immer'
|
||||
|
||||
type Props = {
|
||||
sheet: Sheet
|
||||
initialCells?: Table<Cell>
|
||||
onCellsChange: (cells: Table<Cell>) => void
|
||||
}
|
||||
|
||||
const id = generate()
|
||||
const defaultCells: Table<Cell> = {
|
||||
byId: { [id]: {} },
|
||||
allIds: [id],
|
||||
}
|
||||
|
||||
export const UpdateCellList = ({
|
||||
sheet,
|
||||
initialCells,
|
||||
onCellsChange,
|
||||
}: Props) => {
|
||||
const [cells, setCells] = useImmer(initialCells ?? defaultCells)
|
||||
const [showDeleteId, setShowDeleteId] = useState<string | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
onCellsChange(cells)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [cells])
|
||||
|
||||
const createCell = () => {
|
||||
setCells((cells) => {
|
||||
const id = generate()
|
||||
cells.byId[id] = {}
|
||||
cells.allIds.push(id)
|
||||
})
|
||||
}
|
||||
|
||||
const updateCell = (cellId: string, updates: Partial<Cell>) =>
|
||||
setCells((cells) => {
|
||||
cells.byId[cellId] = {
|
||||
...cells.byId[cellId],
|
||||
...updates,
|
||||
}
|
||||
})
|
||||
|
||||
const deleteCell = (cellId: string) => () => {
|
||||
setCells((cells) => {
|
||||
delete cells.byId[cellId]
|
||||
const index = cells.allIds.indexOf(cellId)
|
||||
if (index !== -1) cells.allIds.splice(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseEnter = (cellId: string) => () => {
|
||||
setShowDeleteId(cellId)
|
||||
}
|
||||
|
||||
const handleCellChange = (cellId: string) => (cell: Cell) =>
|
||||
updateCell(cellId, cell)
|
||||
|
||||
const handleMouseLeave = () => setShowDeleteId(undefined)
|
||||
|
||||
return (
|
||||
<Stack spacing="4">
|
||||
{cells.allIds.map((cellId) => (
|
||||
<>
|
||||
<Flex
|
||||
pos="relative"
|
||||
onMouseEnter={handleMouseEnter(cellId)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<CellWithValueStack
|
||||
key={cellId}
|
||||
cell={cells.byId[cellId]}
|
||||
columns={sheet.columns}
|
||||
onCellChange={handleCellChange(cellId)}
|
||||
/>
|
||||
<Fade in={showDeleteId === cellId}>
|
||||
<IconButton
|
||||
icon={<TrashIcon />}
|
||||
aria-label="Remove cell"
|
||||
onClick={deleteCell(cellId)}
|
||||
pos="absolute"
|
||||
left="-10px"
|
||||
top="-10px"
|
||||
size="sm"
|
||||
/>
|
||||
</Fade>
|
||||
</Flex>
|
||||
</>
|
||||
))}
|
||||
<Button leftIcon={<PlusIcon />} onClick={createCell} flexShrink={0}>
|
||||
Add
|
||||
</Button>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export const CellWithValueStack = ({
|
||||
cell,
|
||||
columns,
|
||||
onCellChange,
|
||||
}: {
|
||||
cell: Cell
|
||||
columns: string[]
|
||||
onCellChange: (column: Cell) => void
|
||||
}) => {
|
||||
const handleColumnSelect = (column: string) => {
|
||||
onCellChange({ ...cell, column })
|
||||
}
|
||||
const handleValueChange = (value: string) => {
|
||||
onCellChange({ ...cell, value })
|
||||
}
|
||||
return (
|
||||
<Stack bgColor="blue.50" p="4" rounded="md" flex="1">
|
||||
<DropdownList<string>
|
||||
currentItem={cell.column}
|
||||
onItemSelect={handleColumnSelect}
|
||||
items={columns}
|
||||
bgColor="white"
|
||||
placeholder="Select a column"
|
||||
/>
|
||||
<InputWithVariable
|
||||
initialValue={cell.value ?? ''}
|
||||
onValueChange={handleValueChange}
|
||||
placeholder="Type a value..."
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { GoogleSheetsSettingsBody } from './GoogleSheetsSettingsBody'
|
@ -7,21 +7,27 @@ import {
|
||||
useEventListener,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Block, Step } from 'models'
|
||||
import { Block, DraggableStep, Step } from 'models'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import { isDefined, isInputStep, isLogicStep, isTextBubbleStep } from 'utils'
|
||||
import {
|
||||
isDefined,
|
||||
isInputStep,
|
||||
isLogicStep,
|
||||
isTextBubbleStep,
|
||||
isIntegrationStep,
|
||||
} from 'utils'
|
||||
import { Coordinates } from '@dnd-kit/core/dist/types'
|
||||
import { TextEditor } from './TextEditor/TextEditor'
|
||||
import { StepNodeContent } from './StepNodeContent'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { ContextMenu } from 'components/shared/ContextMenu'
|
||||
import { SettingsPopoverContent } from './SettingsPopoverContent'
|
||||
import { DraggableStep } from 'contexts/DndContext'
|
||||
import { StepNodeContextMenu } from './StepNodeContextMenu'
|
||||
import { SourceEndpoint } from './SourceEndpoint'
|
||||
import { hasDefaultConnector } from 'services/typebots'
|
||||
import { TargetEndpoint } from './TargetEndpoint'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const StepNode = ({
|
||||
step,
|
||||
@ -39,6 +45,7 @@ export const StepNode = ({
|
||||
step: DraggableStep
|
||||
) => void
|
||||
}) => {
|
||||
const { query } = useRouter()
|
||||
const { setConnectingIds, connectingIds } = useGraph()
|
||||
const { moveStep, typebot } = useTypebot()
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
@ -152,7 +159,11 @@ export const StepNode = ({
|
||||
renderMenu={() => <StepNodeContextMenu stepId={step.id} />}
|
||||
>
|
||||
{(ref, isOpened) => (
|
||||
<Popover placement="left" isLazy>
|
||||
<Popover
|
||||
placement="left"
|
||||
isLazy
|
||||
defaultIsOpen={query.stepId?.toString() === step.id}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Flex
|
||||
pos="relative"
|
||||
@ -226,11 +237,12 @@ export const StepNode = ({
|
||||
)}
|
||||
</Flex>
|
||||
</PopoverTrigger>
|
||||
{(isInputStep(step) || isLogicStep(step)) && (
|
||||
<SettingsPopoverContent step={step} />
|
||||
)}
|
||||
{hasPopover(step) && <SettingsPopoverContent step={step} />}
|
||||
</Popover>
|
||||
)}
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
|
||||
const hasPopover = (step: Step) =>
|
||||
isInputStep(step) || isLogicStep(step) || isIntegrationStep(step)
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
LogicStepType,
|
||||
SetVariableStep,
|
||||
ConditionStep,
|
||||
IntegrationStepType,
|
||||
} from 'models'
|
||||
import { ChoiceItemsList } from './ChoiceInputStepNode/ChoiceItemsList'
|
||||
import { SourceEndpoint } from './SourceEndpoint'
|
||||
@ -84,6 +85,10 @@ export const StepNodeContent = ({ step }: Props) => {
|
||||
case LogicStepType.CONDITION: {
|
||||
return <ConditionNodeContent step={step} />
|
||||
}
|
||||
case IntegrationStepType.GOOGLE_SHEETS: {
|
||||
if (!step.options) return <Text color={'gray.500'}>Configure...</Text>
|
||||
return <Text>{step.options?.action}</Text>
|
||||
}
|
||||
case 'start': {
|
||||
return <Text>{step.label}</Text>
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useEventListener, Stack, Flex, Portal } from '@chakra-ui/react'
|
||||
import { Step, Table } from 'models'
|
||||
import { DraggableStep, useDnd } from 'contexts/DndContext'
|
||||
import { DraggableStep, Step, Table } from 'models'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import { Coordinates } from 'contexts/GraphContext'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { StepNode, StepNodeOverlay } from './StepNode'
|
||||
|
@ -2,10 +2,11 @@ import { Flex, FlexProps, useEventListener } from '@chakra-ui/react'
|
||||
import React, { useRef, useMemo } from 'react'
|
||||
import { blockWidth, useGraph } from 'contexts/GraphContext'
|
||||
import { BlockNode } from './BlockNode/BlockNode'
|
||||
import { DraggableStepType, useDnd } from 'contexts/DndContext'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import { Edges } from './Edges'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
|
||||
import { DraggableStepType } from 'models'
|
||||
|
||||
const Graph = ({ ...props }: FlexProps) => {
|
||||
const { draggedStepType, setDraggedStepType, draggedStep, setDraggedStep } =
|
||||
|
Reference in New Issue
Block a user