♻️ Add shared eslint config
This commit is contained in:
@ -1,43 +1,4 @@
|
||||
module.exports = {
|
||||
ignorePatterns: ['node_modules'],
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react/recommended',
|
||||
'next/core-web-vitals',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true, // Allows for the parsing of JSX
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
},
|
||||
},
|
||||
plugins: ['react', '@typescript-eslint'],
|
||||
rules: {
|
||||
'react/no-unescaped-entities': [0],
|
||||
'react/display-name': [0],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
patterns: [
|
||||
'*/src/*',
|
||||
'src/*',
|
||||
'*/src',
|
||||
'@/features/*/*',
|
||||
'!@/features/blocks/*',
|
||||
'!@/features/*/api',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
root: true,
|
||||
extends: ['custom'],
|
||||
}
|
||||
|
@ -106,20 +106,16 @@
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.43.0",
|
||||
"@typescript-eslint/parser": "5.43.0",
|
||||
"configs": "workspace:*",
|
||||
"db": "workspace:*",
|
||||
"dotenv": "16.0.3",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "13.0.3",
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"models": "workspace:*",
|
||||
"next-transpile-modules": "10.0.0",
|
||||
"superjson": "^1.11.0",
|
||||
"tsconfig": "workspace:*",
|
||||
"typescript": "4.8.4",
|
||||
"utils": "workspace:*",
|
||||
"zod": "3.19.1"
|
||||
"zod": "3.19.1",
|
||||
"eslint": "8.28.0",
|
||||
"eslint-config-custom": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test'
|
||||
import path from 'path'
|
||||
import { playwrightBaseConfig } from 'configs/playwright'
|
||||
import { playwrightBaseConfig } from 'utils/playwright/baseConfig'
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
...playwrightBaseConfig,
|
||||
|
@ -36,7 +36,6 @@ export const SearchableDropdown = ({
|
||||
const { onOpen, onClose, isOpen } = useDisclosure()
|
||||
const [inputValue, setInputValue] = useState(selectedItem ?? '')
|
||||
const debounced = useDebouncedCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onValueChange ? onValueChange : () => {},
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
)
|
||||
|
@ -209,7 +209,7 @@ export const VariableSearchInput = ({
|
||||
leftIcon={<PlusIcon />}
|
||||
bgColor={keyboardFocusIndex === 0 ? 'gray.200' : 'transparent'}
|
||||
>
|
||||
Create "{inputValue}"
|
||||
Create "{inputValue}"
|
||||
</Button>
|
||||
)}
|
||||
{filteredItems.length > 0 && (
|
||||
|
@ -24,7 +24,6 @@ const userContext = createContext<{
|
||||
currentWorkspaceId?: string
|
||||
updateUser: (newUser: Partial<User>) => void
|
||||
saveUser: (newUser?: Partial<User>) => Promise<void>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -24,7 +24,7 @@ export const SignInPage = ({ type }: Props) => {
|
||||
</Heading>
|
||||
{type === 'signin' ? (
|
||||
<Text>
|
||||
Don't have an account?{' '}
|
||||
Don't have an account?{' '}
|
||||
<TextLink href="/register">Sign up for free</TextLink>
|
||||
</Text>
|
||||
) : (
|
||||
|
@ -52,8 +52,8 @@ export const UsageContent = ({ workspace }: Props) => {
|
||||
p="3"
|
||||
label={
|
||||
<Text>
|
||||
Your typebots are popular! You will soon reach your plan's
|
||||
chats limit. 🚀
|
||||
Your typebots are popular! You will soon reach your
|
||||
plan's chats limit. 🚀
|
||||
<br />
|
||||
<br />
|
||||
Make sure to <strong>update your plan</strong> to increase
|
||||
@ -111,8 +111,8 @@ export const UsageContent = ({ workspace }: Props) => {
|
||||
p="3"
|
||||
label={
|
||||
<Text>
|
||||
Your typebots are popular! You will soon reach your plan's
|
||||
storage limit. 🚀
|
||||
Your typebots are popular! You will soon reach your
|
||||
plan's storage limit. 🚀
|
||||
<br />
|
||||
<br />
|
||||
Make sure to <strong>update your plan</strong> in order to
|
||||
|
@ -90,7 +90,7 @@ export const ChangePlanForm = () => {
|
||||
<Text color="gray.500">
|
||||
Need custom limits? Specific features?{' '}
|
||||
<TextLink href={'https://typebot.io/enterprise-lead-form'} isExternal>
|
||||
Let's chat!
|
||||
Let's chat!
|
||||
</TextLink>
|
||||
</Text>
|
||||
</Stack>
|
||||
|
@ -162,14 +162,20 @@ const ActionOptions = ({
|
||||
onOptionsChange({ ...options, cellsToExtract } as GoogleSheetsOptions)
|
||||
|
||||
const UpdatingCellItem = useMemo(
|
||||
() => (props: TableListItemProps<Cell>) =>
|
||||
<CellWithValueStack {...props} columns={sheet?.columns ?? []} />,
|
||||
() =>
|
||||
function Component(props: TableListItemProps<Cell>) {
|
||||
return <CellWithValueStack {...props} columns={sheet?.columns ?? []} />
|
||||
},
|
||||
[sheet?.columns]
|
||||
)
|
||||
|
||||
const ExtractingCellItem = useMemo(
|
||||
() => (props: TableListItemProps<ExtractingCell>) =>
|
||||
<CellWithVariableIdStack {...props} columns={sheet?.columns ?? []} />,
|
||||
() =>
|
||||
function Component(props: TableListItemProps<ExtractingCell>) {
|
||||
return (
|
||||
<CellWithVariableIdStack {...props} columns={sheet?.columns ?? []} />
|
||||
)
|
||||
},
|
||||
[sheet?.columns]
|
||||
)
|
||||
|
||||
|
@ -143,8 +143,10 @@ export const WebhookSettings = ({
|
||||
}
|
||||
|
||||
const ResponseMappingInputs = useMemo(
|
||||
() => (props: TableListItemProps<ResponseVariableMapping>) =>
|
||||
<DataVariableInputs {...props} dataItems={responseKeys} />,
|
||||
() =>
|
||||
function Component(props: TableListItemProps<ResponseVariableMapping>) {
|
||||
return <DataVariableInputs {...props} dataItems={responseKeys} />
|
||||
},
|
||||
[responseKeys]
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const getDeepKeys = (obj: any): string[] => {
|
||||
let keys: string[] = []
|
||||
for (const key in obj) {
|
||||
|
@ -36,7 +36,7 @@ export const AnnoucementModal = ({ isOpen, onClose }: Props) => {
|
||||
<Modal isOpen={isOpen} onClose={handleCloseClick} size="2xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>What's new in Typebot 2.0?</ModalHeader>
|
||||
<ModalHeader>What's new in Typebot 2.0?</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody as={Stack} spacing="6" pb="10">
|
||||
<Text>Typebo 2.0 has been launched February the 15th 🎉.</Text>
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { TypebotViewer } from 'bot-engine'
|
||||
import { useUser } from '@/features/account'
|
||||
import { Answer, Typebot } from 'models'
|
||||
import { AnswerInput, Typebot } from 'models'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { getViewerUrl, sendRequest } from 'utils'
|
||||
import confetti from 'canvas-confetti'
|
||||
@ -79,7 +79,7 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
|
||||
setTypebot(data as Typebot)
|
||||
}
|
||||
|
||||
const handleNewAnswer = async (answer: Answer) => {
|
||||
const handleNewAnswer = async (answer: AnswerInput) => {
|
||||
const isName = answer.variableId === 'cl126f4hf000i2e6d8zvzc3t1'
|
||||
const isCompany = answer.variableId === 'cl126jqww000w2e6dq9yv4ifq'
|
||||
const isCategories = answer.variableId === 'cl126mo3t001b2e6dvyi16bkd'
|
||||
|
@ -11,7 +11,7 @@ enum ActionType {
|
||||
Flush = 'FLUSH',
|
||||
}
|
||||
|
||||
export interface Actions<T> {
|
||||
export interface Actions<T extends { updatedAt: string } | undefined> {
|
||||
set: (
|
||||
newPresent: T | ((current: T) => T),
|
||||
options?: { updateDate: boolean }
|
||||
@ -24,13 +24,13 @@ export interface Actions<T> {
|
||||
presentRef: React.MutableRefObject<T>
|
||||
}
|
||||
|
||||
interface Action<T> {
|
||||
interface Action<T extends { updatedAt: string } | undefined> {
|
||||
type: ActionType
|
||||
newPresent?: T
|
||||
updateDate?: boolean
|
||||
}
|
||||
|
||||
export interface State<T> {
|
||||
export interface State<T extends { updatedAt: string } | undefined> {
|
||||
past: T[]
|
||||
present: T
|
||||
future: T[]
|
||||
@ -42,7 +42,10 @@ const initialState = {
|
||||
future: [],
|
||||
}
|
||||
|
||||
const reducer = <T>(state: State<T>, action: Action<T>) => {
|
||||
const reducer = <T extends { updatedAt: string } | undefined>(
|
||||
state: State<T>,
|
||||
action: Action<T>
|
||||
) => {
|
||||
const { past, present, future } = state
|
||||
|
||||
switch (action.type) {
|
||||
@ -98,8 +101,6 @@ const reducer = <T>(state: State<T>, action: Action<T>) => {
|
||||
past: [...past, present].filter(isDefined),
|
||||
present: {
|
||||
...newPresent,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
updatedAt: updateDate ? new Date() : newPresent.updatedAt,
|
||||
},
|
||||
future: [],
|
||||
@ -111,11 +112,13 @@ const reducer = <T>(state: State<T>, action: Action<T>) => {
|
||||
}
|
||||
}
|
||||
|
||||
const useUndo = <T>(initialPresent: T): [State<T>, Actions<T>] => {
|
||||
const useUndo = <T extends { updatedAt: string } | undefined>(
|
||||
initialPresent: T
|
||||
): [State<T>, Actions<T>] => {
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
...initialState,
|
||||
present: initialPresent,
|
||||
}) as [State<T>, React.Dispatch<Action<T>>]
|
||||
})
|
||||
const presentRef = useRef<T>(initialPresent)
|
||||
|
||||
const canUndo = state.past.length !== 0
|
||||
@ -136,7 +139,7 @@ const useUndo = <T>(initialPresent: T): [State<T>, Actions<T>] => {
|
||||
const set = useCallback(
|
||||
(newPresent: T | ((current: T) => T), options = { updateDate: true }) => {
|
||||
const updatedTypebot =
|
||||
'id' in newPresent
|
||||
newPresent && 'id' in newPresent
|
||||
? newPresent
|
||||
: (newPresent as (current: T) => T)(presentRef.current)
|
||||
presentRef.current = updatedTypebot
|
||||
@ -153,7 +156,10 @@ const useUndo = <T>(initialPresent: T): [State<T>, Actions<T>] => {
|
||||
dispatch({ type: ActionType.Flush })
|
||||
}, [])
|
||||
|
||||
return [state, { set, undo, redo, flush, canUndo, canRedo, presentRef }]
|
||||
return [
|
||||
state as State<T>,
|
||||
{ set, undo, redo, flush, canUndo, canRedo, presentRef },
|
||||
]
|
||||
}
|
||||
|
||||
export default useUndo
|
||||
|
@ -16,7 +16,6 @@ const editorContext = createContext<{
|
||||
setRightPanel: Dispatch<SetStateAction<RightPanel | undefined>>
|
||||
startPreviewAtGroup: string | undefined
|
||||
setStartPreviewAtGroup: Dispatch<SetStateAction<string | undefined>>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -88,7 +88,6 @@ const typebotContext = createContext<
|
||||
ItemsActions &
|
||||
VariablesActions &
|
||||
EdgesActions
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
>({})
|
||||
|
||||
|
@ -14,7 +14,6 @@ const typebotDndContext = createContext<{
|
||||
setDraggedTypebot: Dispatch<SetStateAction<TypebotInDashboard | undefined>>
|
||||
mouseOverFolderId?: string | null
|
||||
setMouseOverFolderId: Dispatch<SetStateAction<string | undefined | null>>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -47,197 +47,196 @@ export const GroupNode = ({ group, groupIndex }: Props) => {
|
||||
)
|
||||
}
|
||||
|
||||
const DraggableGroupNode = memo(
|
||||
({
|
||||
group,
|
||||
groupIndex,
|
||||
onGroupDrag,
|
||||
}: Props & { onGroupDrag: (newCoord: Coordinates) => void }) => {
|
||||
const {
|
||||
connectingIds,
|
||||
setConnectingIds,
|
||||
previewingEdge,
|
||||
isReadOnly,
|
||||
focusedGroupId,
|
||||
setFocusedGroupId,
|
||||
graphPosition,
|
||||
} = useGraph()
|
||||
const { typebot, updateGroup } = useTypebot()
|
||||
const { setMouseOverGroup, mouseOverGroup } = useBlockDnd()
|
||||
const { setRightPanel, setStartPreviewAtGroup } = useEditor()
|
||||
const NonMemoizedDraggableGroupNode = ({
|
||||
group,
|
||||
groupIndex,
|
||||
onGroupDrag,
|
||||
}: Props & { onGroupDrag: (newCoord: Coordinates) => void }) => {
|
||||
const {
|
||||
connectingIds,
|
||||
setConnectingIds,
|
||||
previewingEdge,
|
||||
isReadOnly,
|
||||
focusedGroupId,
|
||||
setFocusedGroupId,
|
||||
graphPosition,
|
||||
} = useGraph()
|
||||
const { typebot, updateGroup } = useTypebot()
|
||||
const { setMouseOverGroup, mouseOverGroup } = useBlockDnd()
|
||||
const { setRightPanel, setStartPreviewAtGroup } = useEditor()
|
||||
|
||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const [currentCoordinates, setCurrentCoordinates] = useState(
|
||||
group.graphCoordinates
|
||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const [currentCoordinates, setCurrentCoordinates] = useState(
|
||||
group.graphCoordinates
|
||||
)
|
||||
const [groupTitle, setGroupTitle] = useState(group.title)
|
||||
|
||||
// When the group is moved from external action (e.g. undo/redo), update the current coordinates
|
||||
useEffect(() => {
|
||||
setCurrentCoordinates({
|
||||
x: group.graphCoordinates.x,
|
||||
y: group.graphCoordinates.y,
|
||||
})
|
||||
}, [group.graphCoordinates.x, group.graphCoordinates.y])
|
||||
|
||||
// Same for group title
|
||||
useEffect(() => {
|
||||
setGroupTitle(group.title)
|
||||
}, [group.title])
|
||||
|
||||
const isPreviewing =
|
||||
previewingEdge?.from.groupId === group.id ||
|
||||
(previewingEdge?.to.groupId === group.id &&
|
||||
isNotDefined(previewingEdge.to.blockId))
|
||||
const isStartGroup =
|
||||
isDefined(group.blocks[0]) && group.blocks[0].type === 'start'
|
||||
|
||||
const groupRef = useRef<HTMLDivElement | null>(null)
|
||||
const [debouncedGroupPosition] = useDebounce(currentCoordinates, 100)
|
||||
useEffect(() => {
|
||||
if (!currentCoordinates || isReadOnly) return
|
||||
if (
|
||||
currentCoordinates?.x === group.graphCoordinates.x &&
|
||||
currentCoordinates.y === group.graphCoordinates.y
|
||||
)
|
||||
const [groupTitle, setGroupTitle] = useState(group.title)
|
||||
return
|
||||
updateGroup(groupIndex, { graphCoordinates: currentCoordinates })
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedGroupPosition])
|
||||
|
||||
// When the group is moved from external action (e.g. undo/redo), update the current coordinates
|
||||
useEffect(() => {
|
||||
setCurrentCoordinates({
|
||||
x: group.graphCoordinates.x,
|
||||
y: group.graphCoordinates.y,
|
||||
})
|
||||
}, [group.graphCoordinates.x, group.graphCoordinates.y])
|
||||
|
||||
// Same for group title
|
||||
useEffect(() => {
|
||||
setGroupTitle(group.title)
|
||||
}, [group.title])
|
||||
|
||||
const isPreviewing =
|
||||
previewingEdge?.from.groupId === group.id ||
|
||||
(previewingEdge?.to.groupId === group.id &&
|
||||
isNotDefined(previewingEdge.to.blockId))
|
||||
const isStartGroup =
|
||||
isDefined(group.blocks[0]) && group.blocks[0].type === 'start'
|
||||
|
||||
const groupRef = useRef<HTMLDivElement | null>(null)
|
||||
const [debouncedGroupPosition] = useDebounce(currentCoordinates, 100)
|
||||
useEffect(() => {
|
||||
if (!currentCoordinates || isReadOnly) return
|
||||
if (
|
||||
currentCoordinates?.x === group.graphCoordinates.x &&
|
||||
currentCoordinates.y === group.graphCoordinates.y
|
||||
)
|
||||
return
|
||||
updateGroup(groupIndex, { graphCoordinates: currentCoordinates })
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedGroupPosition])
|
||||
|
||||
useEffect(() => {
|
||||
setIsConnecting(
|
||||
connectingIds?.target?.groupId === group.id &&
|
||||
isNotDefined(connectingIds.target?.blockId)
|
||||
)
|
||||
}, [connectingIds, group.id])
|
||||
|
||||
const handleTitleSubmit = (title: string) =>
|
||||
title.length > 0 ? updateGroup(groupIndex, { title }) : undefined
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (isReadOnly) return
|
||||
if (mouseOverGroup?.id !== group.id && !isStartGroup)
|
||||
setMouseOverGroup({ id: group.id, ref: groupRef })
|
||||
if (connectingIds)
|
||||
setConnectingIds({ ...connectingIds, target: { groupId: group.id } })
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
if (isReadOnly) return
|
||||
setMouseOverGroup(undefined)
|
||||
if (connectingIds)
|
||||
setConnectingIds({ ...connectingIds, target: undefined })
|
||||
}
|
||||
|
||||
const startPreviewAtThisGroup = () => {
|
||||
setStartPreviewAtGroup(group.id)
|
||||
setRightPanel(RightPanel.PREVIEW)
|
||||
}
|
||||
|
||||
useDrag(
|
||||
({ first, last, offset: [offsetX, offsetY], event, target }) => {
|
||||
event.stopPropagation()
|
||||
if ((target as HTMLElement).classList.contains('prevent-group-drag'))
|
||||
return
|
||||
if (first) {
|
||||
setFocusedGroupId(group.id)
|
||||
setIsMouseDown(true)
|
||||
}
|
||||
if (last) {
|
||||
setIsMouseDown(false)
|
||||
}
|
||||
const newCoord = {
|
||||
x: offsetX / graphPosition.scale,
|
||||
y: offsetY / graphPosition.scale,
|
||||
}
|
||||
setCurrentCoordinates(newCoord)
|
||||
onGroupDrag(newCoord)
|
||||
},
|
||||
{
|
||||
target: groupRef,
|
||||
pointer: { keys: false },
|
||||
from: () => [
|
||||
currentCoordinates.x * graphPosition.scale,
|
||||
currentCoordinates.y * graphPosition.scale,
|
||||
],
|
||||
}
|
||||
useEffect(() => {
|
||||
setIsConnecting(
|
||||
connectingIds?.target?.groupId === group.id &&
|
||||
isNotDefined(connectingIds.target?.blockId)
|
||||
)
|
||||
}, [connectingIds, group.id])
|
||||
|
||||
return (
|
||||
<ContextMenu<HTMLDivElement>
|
||||
renderMenu={() => <GroupNodeContextMenu groupIndex={groupIndex} />}
|
||||
isDisabled={isReadOnly || isStartGroup}
|
||||
>
|
||||
{(ref, isOpened) => (
|
||||
<Stack
|
||||
ref={setMultipleRefs([ref, groupRef])}
|
||||
data-testid="group"
|
||||
p="4"
|
||||
rounded="xl"
|
||||
bgColor="#ffffff"
|
||||
borderWidth="2px"
|
||||
borderColor={
|
||||
isConnecting || isOpened || isPreviewing ? 'blue.400' : '#ffffff'
|
||||
}
|
||||
w="300px"
|
||||
transition="border 300ms, box-shadow 200ms"
|
||||
pos="absolute"
|
||||
style={{
|
||||
transform: `translate(${currentCoordinates?.x ?? 0}px, ${
|
||||
currentCoordinates?.y ?? 0
|
||||
}px)`,
|
||||
}}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
cursor={isMouseDown ? 'grabbing' : 'pointer'}
|
||||
shadow="md"
|
||||
_hover={{ shadow: 'lg' }}
|
||||
zIndex={focusedGroupId === group.id ? 10 : 1}
|
||||
>
|
||||
<Editable
|
||||
value={groupTitle}
|
||||
onChange={setGroupTitle}
|
||||
onSubmit={handleTitleSubmit}
|
||||
fontWeight="semibold"
|
||||
pointerEvents={isReadOnly || isStartGroup ? 'none' : 'auto'}
|
||||
pr="8"
|
||||
>
|
||||
<EditablePreview
|
||||
_hover={{ bgColor: 'gray.200' }}
|
||||
px="1"
|
||||
userSelect={'none'}
|
||||
/>
|
||||
<EditableInput minW="0" px="1" className="prevent-group-drag" />
|
||||
</Editable>
|
||||
{typebot && (
|
||||
<BlockNodesList
|
||||
groupId={group.id}
|
||||
blocks={group.blocks}
|
||||
groupIndex={groupIndex}
|
||||
groupRef={ref}
|
||||
isStartGroup={isStartGroup}
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
icon={<PlayIcon />}
|
||||
aria-label={'Preview bot from this group'}
|
||||
pos="absolute"
|
||||
right={2}
|
||||
top={0}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={startPreviewAtThisGroup}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</ContextMenu>
|
||||
)
|
||||
const handleTitleSubmit = (title: string) =>
|
||||
title.length > 0 ? updateGroup(groupIndex, { title }) : undefined
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
)
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (isReadOnly) return
|
||||
if (mouseOverGroup?.id !== group.id && !isStartGroup)
|
||||
setMouseOverGroup({ id: group.id, ref: groupRef })
|
||||
if (connectingIds)
|
||||
setConnectingIds({ ...connectingIds, target: { groupId: group.id } })
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
if (isReadOnly) return
|
||||
setMouseOverGroup(undefined)
|
||||
if (connectingIds) setConnectingIds({ ...connectingIds, target: undefined })
|
||||
}
|
||||
|
||||
const startPreviewAtThisGroup = () => {
|
||||
setStartPreviewAtGroup(group.id)
|
||||
setRightPanel(RightPanel.PREVIEW)
|
||||
}
|
||||
|
||||
useDrag(
|
||||
({ first, last, offset: [offsetX, offsetY], event, target }) => {
|
||||
event.stopPropagation()
|
||||
if ((target as HTMLElement).classList.contains('prevent-group-drag'))
|
||||
return
|
||||
if (first) {
|
||||
setFocusedGroupId(group.id)
|
||||
setIsMouseDown(true)
|
||||
}
|
||||
if (last) {
|
||||
setIsMouseDown(false)
|
||||
}
|
||||
const newCoord = {
|
||||
x: offsetX / graphPosition.scale,
|
||||
y: offsetY / graphPosition.scale,
|
||||
}
|
||||
setCurrentCoordinates(newCoord)
|
||||
onGroupDrag(newCoord)
|
||||
},
|
||||
{
|
||||
target: groupRef,
|
||||
pointer: { keys: false },
|
||||
from: () => [
|
||||
currentCoordinates.x * graphPosition.scale,
|
||||
currentCoordinates.y * graphPosition.scale,
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<ContextMenu<HTMLDivElement>
|
||||
renderMenu={() => <GroupNodeContextMenu groupIndex={groupIndex} />}
|
||||
isDisabled={isReadOnly || isStartGroup}
|
||||
>
|
||||
{(ref, isOpened) => (
|
||||
<Stack
|
||||
ref={setMultipleRefs([ref, groupRef])}
|
||||
data-testid="group"
|
||||
p="4"
|
||||
rounded="xl"
|
||||
bgColor="#ffffff"
|
||||
borderWidth="2px"
|
||||
borderColor={
|
||||
isConnecting || isOpened || isPreviewing ? 'blue.400' : '#ffffff'
|
||||
}
|
||||
w="300px"
|
||||
transition="border 300ms, box-shadow 200ms"
|
||||
pos="absolute"
|
||||
style={{
|
||||
transform: `translate(${currentCoordinates?.x ?? 0}px, ${
|
||||
currentCoordinates?.y ?? 0
|
||||
}px)`,
|
||||
}}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
cursor={isMouseDown ? 'grabbing' : 'pointer'}
|
||||
shadow="md"
|
||||
_hover={{ shadow: 'lg' }}
|
||||
zIndex={focusedGroupId === group.id ? 10 : 1}
|
||||
>
|
||||
<Editable
|
||||
value={groupTitle}
|
||||
onChange={setGroupTitle}
|
||||
onSubmit={handleTitleSubmit}
|
||||
fontWeight="semibold"
|
||||
pointerEvents={isReadOnly || isStartGroup ? 'none' : 'auto'}
|
||||
pr="8"
|
||||
>
|
||||
<EditablePreview
|
||||
_hover={{ bgColor: 'gray.200' }}
|
||||
px="1"
|
||||
userSelect={'none'}
|
||||
/>
|
||||
<EditableInput minW="0" px="1" className="prevent-group-drag" />
|
||||
</Editable>
|
||||
{typebot && (
|
||||
<BlockNodesList
|
||||
groupId={group.id}
|
||||
blocks={group.blocks}
|
||||
groupIndex={groupIndex}
|
||||
groupRef={ref}
|
||||
isStartGroup={isStartGroup}
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
icon={<PlayIcon />}
|
||||
aria-label={'Preview bot from this group'}
|
||||
pos="absolute"
|
||||
right={2}
|
||||
top={0}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={startPreviewAtThisGroup}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
|
||||
export const DraggableGroupNode = memo(NonMemoizedDraggableGroupNode)
|
||||
|
@ -27,7 +27,6 @@ const graphDndContext = createContext<{
|
||||
setMouseOverGroup: Dispatch<SetStateAction<NodeInfo | undefined>>
|
||||
mouseOverBlock?: NodeInfo
|
||||
setMouseOverBlock: Dispatch<SetStateAction<NodeInfo | undefined>>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -74,7 +74,6 @@ const graphContext = createContext<{
|
||||
isReadOnly: boolean
|
||||
focusedGroupId?: string
|
||||
setFocusedGroupId: Dispatch<SetStateAction<string | undefined>>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({
|
||||
graphPosition: graphPositionDefaultValue,
|
||||
|
@ -12,7 +12,6 @@ import { GroupsCoordinates, Coordinates } from './GraphProvider'
|
||||
const groupsCoordinatesContext = createContext<{
|
||||
groupsCoordinates: GroupsCoordinates
|
||||
updateGroupCoordinates: (groupId: string, newCoord: Coordinates) => void
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -17,7 +17,6 @@ const resultsContext = createContext<{
|
||||
onDeleteResults: (totalResultsDeleted: number) => void
|
||||
fetchNextPage: () => void
|
||||
refetchResults: () => void
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { ToolIcon, EyeIcon, EyeOffIcon, GripIcon } from '@/components/icons'
|
||||
import { ResultHeaderCell } from 'models'
|
||||
import React, { forwardRef, useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { isNotDefined } from 'utils'
|
||||
import {
|
||||
DndContext,
|
||||
@ -128,7 +128,7 @@ export const ColumnSettingsButton = ({
|
||||
</SortableContext>
|
||||
<Portal>
|
||||
<DragOverlay dropAnimation={{ duration: 0 }}>
|
||||
{draggingColumnId ? <SortableColumnOverlay /> : null}
|
||||
{draggingColumnId ? <Flex /> : null}
|
||||
</DragOverlay>
|
||||
</Portal>
|
||||
</DndContext>
|
||||
@ -210,9 +210,3 @@ const SortableColumns = ({
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
const SortableColumnOverlay = forwardRef(
|
||||
(_, ref: React.LegacyRef<HTMLDivElement>) => {
|
||||
return <HStack ref={ref}></HStack>
|
||||
}
|
||||
)
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { Checkbox, Flex } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
const TableCheckBox = (
|
||||
{ indeterminate, checked, ...rest }: any,
|
||||
ref: React.LegacyRef<HTMLInputElement>
|
||||
) => {
|
||||
const defaultRef = React.useRef()
|
||||
const resolvedRef: any = ref || defaultRef
|
||||
|
||||
return (
|
||||
<Flex justify="center" data-testid="checkbox">
|
||||
<Checkbox
|
||||
ref={resolvedRef}
|
||||
{...rest}
|
||||
isIndeterminate={indeterminate}
|
||||
isChecked={checked}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export const IndeterminateCheckbox = React.forwardRef(TableCheckBox)
|
@ -2,7 +2,6 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
chakra,
|
||||
Checkbox,
|
||||
Flex,
|
||||
HStack,
|
||||
Stack,
|
||||
@ -26,6 +25,7 @@ import { Row } from './Row'
|
||||
import { HeaderRow } from './HeaderRow'
|
||||
import { CellValueType, TableData } from '../../types'
|
||||
import { HeaderIcon } from '../../utils'
|
||||
import { IndeterminateCheckbox } from './IndeterminateCheckbox'
|
||||
|
||||
type ResultsTableProps = {
|
||||
resultHeader: ResultHeaderCell[]
|
||||
@ -238,23 +238,3 @@ export const ResultsTable = ({
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
({ indeterminate, checked, ...rest }: any, ref) => {
|
||||
const defaultRef = React.useRef()
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const resolvedRef: any = ref || defaultRef
|
||||
|
||||
return (
|
||||
<Flex justify="center" data-testid="checkbox">
|
||||
<Checkbox
|
||||
ref={resolvedRef}
|
||||
{...rest}
|
||||
isIndeterminate={indeterminate}
|
||||
isChecked={checked}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -30,7 +30,6 @@ const workspaceContext = createContext<{
|
||||
) => Promise<void>
|
||||
deleteCurrentWorkspace: () => Promise<void>
|
||||
refreshWorkspace: (expectedUpdates: Partial<Workspace>) => void
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
|
@ -88,7 +88,7 @@ const DeleteWorkspaceButton = ({
|
||||
message={
|
||||
<Text>
|
||||
Are you sure you want to delete {workspaceName} workspace? All its
|
||||
folders, typebots and results will be deleted forever.'
|
||||
folders, typebots and results will be deleted forever.
|
||||
</Text>
|
||||
}
|
||||
confirmButtonLabel="Delete"
|
||||
|
@ -7,8 +7,7 @@ export const useAutoSave = <T>(
|
||||
item,
|
||||
debounceTimeout,
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
handler: () => Promise<any>
|
||||
handler: () => Promise<void>
|
||||
item?: T
|
||||
debounceTimeout: number
|
||||
},
|
||||
|
@ -106,5 +106,4 @@ const components = {
|
||||
},
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const customTheme: any = extendTheme({ colors, fonts, components })
|
||||
|
@ -1,4 +1,4 @@
|
||||
import NextErrorComponent from 'next/error'
|
||||
import NextErrorComponent, { ErrorProps } from 'next/error'
|
||||
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
import { NextPageContext } from 'next'
|
||||
@ -24,14 +24,14 @@ const MyError = ({
|
||||
}
|
||||
|
||||
MyError.getInitialProps = async (context: NextPageContext) => {
|
||||
const errorInitialProps = await NextErrorComponent.getInitialProps(context)
|
||||
const errorInitialProps = (await NextErrorComponent.getInitialProps(
|
||||
context
|
||||
)) as ErrorProps & { hasGetInitialPropsRun: boolean }
|
||||
|
||||
const { res, err, asPath } = context
|
||||
|
||||
// Workaround for https://github.com/vercel/next.js/issues/8592, mark when
|
||||
// getInitialProps has run
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
errorInitialProps.hasGetInitialPropsRun = true
|
||||
|
||||
// Returning early because we don't want to log 404 errors to Sentry.
|
||||
|
@ -18,8 +18,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return res.status(404).send("Couldn't find credentials in database")
|
||||
const response = await drive({
|
||||
version: 'v3',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
auth: auth.client,
|
||||
}).files.list({
|
||||
q: "mimeType='application/vnd.google-apps.spreadsheet'",
|
||||
|
@ -128,5 +128,4 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default withSentry(cors(webhookHandler as any))
|
||||
|
@ -76,9 +76,9 @@ export const readFile = (file: File): Promise<string> => {
|
||||
}
|
||||
|
||||
export const timeSince = (date: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const seconds = Math.floor((new Date() - new Date(date)) / 1000)
|
||||
const seconds = Math.floor(
|
||||
(new Date().getTime() - new Date(date).getTime()) / 1000
|
||||
)
|
||||
|
||||
let interval = seconds / 31536000
|
||||
|
||||
|
@ -6,6 +6,5 @@
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
Reference in New Issue
Block a user