2
0
Files
bot/apps/builder/components/dashboard/FolderContent.tsx

223 lines
7.1 KiB
TypeScript
Raw Normal View History

2022-03-14 16:14:42 +01:00
import { DashboardFolder } from 'db'
2021-12-06 15:48:50 +01:00
import {
Flex,
Heading,
HStack,
Portal,
2021-12-06 15:48:50 +01:00
Skeleton,
Stack,
useEventListener,
2021-12-06 15:48:50 +01:00
useToast,
Wrap,
} from '@chakra-ui/react'
import { useTypebotDnd } from 'contexts/TypebotDndContext'
2022-02-24 11:13:19 +01:00
import { useUser } from 'contexts/UserContext'
import React, { useState } from 'react'
2021-12-06 15:48:50 +01:00
import { createFolder, useFolders } from 'services/folders'
import {
patchTypebot,
TypebotInDashboard,
useTypebots,
} from 'services/typebots'
2021-12-06 15:48:50 +01:00
import { BackButton } from './FolderContent/BackButton'
import { CreateBotButton } from './FolderContent/CreateBotButton'
import { CreateFolderButton } from './FolderContent/CreateFolderButton'
2021-12-06 15:48:50 +01:00
import { ButtonSkeleton, FolderButton } from './FolderContent/FolderButton'
import { TypebotButton } from './FolderContent/TypebotButton'
import { TypebotCardOverlay } from './FolderContent/TypebotButtonOverlay'
2022-03-23 09:56:39 +01:00
import { OnboardingModal } from './OnboardingModal'
2022-05-13 15:22:44 -07:00
import { useWorkspace } from 'contexts/WorkspaceContext'
2021-12-06 15:48:50 +01:00
type Props = { folder: DashboardFolder | null }
const dragDistanceTolerance = 20
2021-12-06 15:48:50 +01:00
export const FolderContent = ({ folder }: Props) => {
2022-02-24 11:13:19 +01:00
const { user } = useUser()
2022-05-13 15:22:44 -07:00
const { workspace } = useWorkspace()
2021-12-06 15:48:50 +01:00
const [isCreatingFolder, setIsCreatingFolder] = useState(false)
const {
setDraggedTypebot,
draggedTypebot,
mouseOverFolderId,
setMouseOverFolderId,
} = useTypebotDnd()
const [mouseDownPosition, setMouseDownPosition] = useState({ x: 0, y: 0 })
const [draggablePosition, setDraggablePosition] = useState({ x: 0, y: 0 })
const [relativeDraggablePosition, setRelativeDraggablePosition] = useState({
x: 0,
y: 0,
})
const [typebotDragCandidate, setTypebotDragCandidate] =
useState<TypebotInDashboard>()
2021-12-06 15:48:50 +01:00
const toast = useToast({
position: 'top-right',
status: 'error',
})
2022-05-14 11:53:03 -07:00
2021-12-06 15:48:50 +01:00
const {
folders,
isLoading: isFolderLoading,
mutate: mutateFolders,
} = useFolders({
2022-05-13 15:22:44 -07:00
workspaceId: workspace?.id,
2021-12-06 15:48:50 +01:00
parentId: folder?.id,
onError: (error) => {
toast({ title: "Couldn't fetch folders", description: error.message })
},
})
const {
typebots,
isLoading: isTypebotLoading,
mutate: mutateTypebots,
} = useTypebots({
2022-05-13 15:22:44 -07:00
workspaceId: workspace?.id,
2021-12-06 15:48:50 +01:00
folderId: folder?.id,
onError: (error) => {
toast({ title: "Couldn't fetch typebots", description: error.message })
},
})
const moveTypebotToFolder = async (typebotId: string, folderId: string) => {
if (!typebots) return
2021-12-23 15:20:21 +01:00
const { error } = await patchTypebot(typebotId, {
2021-12-06 15:48:50 +01:00
folderId: folderId === 'root' ? null : folderId,
})
if (error) toast({ description: error.message })
mutateTypebots({ typebots: typebots.filter((t) => t.id !== typebotId) })
}
const handleCreateFolder = async () => {
2022-05-13 15:22:44 -07:00
if (!folders || !workspace) return
2021-12-06 15:48:50 +01:00
setIsCreatingFolder(true)
2022-05-13 15:22:44 -07:00
const { error, data: newFolder } = await createFolder(workspace.id, {
2021-12-06 15:48:50 +01:00
parentFolderId: folder?.id ?? null,
})
setIsCreatingFolder(false)
if (error)
return toast({ title: 'An error occured', description: error.message })
if (newFolder) mutateFolders({ folders: [...folders, newFolder] })
}
const handleTypebotDeleted = (deletedId: string) => {
if (!typebots) return
mutateTypebots({ typebots: typebots.filter((t) => t.id !== deletedId) })
}
const handleFolderDeleted = (deletedId: string) => {
if (!folders) return
mutateFolders({ folders: folders.filter((f) => f.id !== deletedId) })
}
const handleFolderRenamed = (folderId: string, name: string) => {
if (!folders) return
mutateFolders({
folders: folders.map((f) => (f.id === folderId ? { ...f, name } : f)),
})
}
const handleMouseUp = async () => {
if (mouseOverFolderId !== undefined && draggedTypebot)
await moveTypebotToFolder(draggedTypebot.id, mouseOverFolderId ?? 'root')
setTypebotDragCandidate(undefined)
setMouseOverFolderId(undefined)
setDraggedTypebot(undefined)
}
useEventListener('mouseup', handleMouseUp)
const handleMouseDown =
(typebot: TypebotInDashboard) => (e: React.MouseEvent) => {
const element = e.currentTarget as HTMLDivElement
const rect = element.getBoundingClientRect()
setDraggablePosition({ x: rect.left, y: rect.top })
const x = e.clientX - rect.left
const y = e.clientY - rect.top
setRelativeDraggablePosition({ x, y })
setMouseDownPosition({ x: e.screenX, y: e.screenY })
setTypebotDragCandidate(typebot)
}
const handleMouseMove = (e: MouseEvent) => {
if (!typebotDragCandidate) return
const { clientX, clientY, screenX, screenY } = e
if (
Math.abs(mouseDownPosition.x - screenX) > dragDistanceTolerance ||
Math.abs(mouseDownPosition.y - screenY) > dragDistanceTolerance
)
setDraggedTypebot(typebotDragCandidate)
setDraggablePosition({
...draggablePosition,
x: clientX - relativeDraggablePosition.x,
y: clientY - relativeDraggablePosition.y,
})
}
useEventListener('mousemove', handleMouseMove)
2021-12-06 15:48:50 +01:00
return (
<Flex w="full" flex="1" justify="center">
2022-05-13 15:22:44 -07:00
{typebots && !isTypebotLoading && user && folder === null && (
2022-03-23 09:56:39 +01:00
<OnboardingModal totalTypebots={typebots.length} />
)}
<Stack w="1000px" spacing={6}>
<Skeleton isLoaded={folder?.name !== undefined}>
<Heading as="h1">{folder?.name}</Heading>
</Skeleton>
<Stack>
<HStack>
{folder && <BackButton id={folder.parentFolderId} />}
<CreateFolderButton
onClick={handleCreateFolder}
isLoading={isCreatingFolder || isFolderLoading}
/>
</HStack>
<Wrap spacing={4}>
<CreateBotButton
folderId={folder?.id}
isLoading={isTypebotLoading}
2022-03-23 09:56:39 +01:00
isFirstBot={typebots?.length === 0 && folder === null}
/>
{isFolderLoading && <ButtonSkeleton />}
{folders &&
folders.map((folder) => (
<FolderButton
key={folder.id.toString()}
folder={folder}
onFolderDeleted={() => handleFolderDeleted(folder.id)}
onFolderRenamed={(newName: string) =>
handleFolderRenamed(folder.id, newName)
}
/>
))}
{isTypebotLoading && <ButtonSkeleton />}
{typebots &&
typebots.map((typebot) => (
<TypebotButton
key={typebot.id.toString()}
typebot={typebot}
onTypebotDeleted={() => handleTypebotDeleted(typebot.id)}
onMouseDown={handleMouseDown(typebot)}
/>
))}
</Wrap>
2021-12-06 15:48:50 +01:00
</Stack>
</Stack>
{draggedTypebot && (
<Portal>
<TypebotCardOverlay
typebot={draggedTypebot}
onMouseUp={handleMouseUp}
pos="fixed"
top="0"
left="0"
style={{
transform: `translate(${draggablePosition.x}px, ${draggablePosition.y}px) rotate(-2deg)`,
}}
/>
</Portal>
)}
2021-12-06 15:48:50 +01:00
</Flex>
)
}