182 lines
5.5 KiB
TypeScript
182 lines
5.5 KiB
TypeScript
import { DashboardFolder } from '.prisma/client'
|
|
import { Typebot } from 'bot-engine'
|
|
import {
|
|
Button,
|
|
Flex,
|
|
Heading,
|
|
HStack,
|
|
Skeleton,
|
|
Stack,
|
|
useToast,
|
|
Wrap,
|
|
} from '@chakra-ui/react'
|
|
import {
|
|
DndContext,
|
|
DragEndEvent,
|
|
DragOverlay,
|
|
DragStartEvent,
|
|
MouseSensor,
|
|
useSensor,
|
|
useSensors,
|
|
} from '@dnd-kit/core'
|
|
import { FolderPlusIcon } from 'assets/icons'
|
|
import React, { useState } from 'react'
|
|
import { createFolder, useFolders } from 'services/folders'
|
|
import { updateTypebot, useTypebots } from 'services/typebots'
|
|
import { BackButton } from './FolderContent/BackButton'
|
|
import { CreateBotButton } from './FolderContent/CreateBotButton'
|
|
import { ButtonSkeleton, FolderButton } from './FolderContent/FolderButton'
|
|
import { TypebotButton } from './FolderContent/TypebotButton'
|
|
import { TypebotCardOverlay } from './FolderContent/TypebotButtonOverlay'
|
|
|
|
type Props = { folder: DashboardFolder | null }
|
|
|
|
export const FolderContent = ({ folder }: Props) => {
|
|
const [isCreatingFolder, setIsCreatingFolder] = useState(false)
|
|
const [draggedTypebot, setDraggedTypebot] = useState<Typebot | undefined>()
|
|
const sensors = useSensors(
|
|
useSensor(MouseSensor, {
|
|
activationConstraint: {
|
|
delay: 100,
|
|
tolerance: 300,
|
|
},
|
|
})
|
|
)
|
|
const toast = useToast({
|
|
position: 'top-right',
|
|
status: 'error',
|
|
})
|
|
const {
|
|
folders,
|
|
isLoading: isFolderLoading,
|
|
mutate: mutateFolders,
|
|
} = useFolders({
|
|
parentId: folder?.id,
|
|
onError: (error) => {
|
|
toast({ title: "Couldn't fetch folders", description: error.message })
|
|
},
|
|
})
|
|
|
|
const {
|
|
typebots,
|
|
isLoading: isTypebotLoading,
|
|
mutate: mutateTypebots,
|
|
} = useTypebots({
|
|
folderId: folder?.id,
|
|
onError: (error) => {
|
|
toast({ title: "Couldn't fetch typebots", description: error.message })
|
|
},
|
|
})
|
|
|
|
const handleDragStart = (event: DragStartEvent) => {
|
|
if (!typebots) return
|
|
setDraggedTypebot(typebots.find((c) => c.id === event.active.id))
|
|
}
|
|
|
|
const handleDragEnd = async (event: DragEndEvent) => {
|
|
if (!typebots) return
|
|
const { over } = event
|
|
if (over?.id && draggedTypebot?.id)
|
|
await moveTypebotToFolder(draggedTypebot.id, over.id)
|
|
setDraggedTypebot(undefined)
|
|
}
|
|
|
|
const moveTypebotToFolder = async (typebotId: string, folderId: string) => {
|
|
if (!typebots) return
|
|
const { error } = await updateTypebot(typebotId, {
|
|
folderId: folderId === 'root' ? null : folderId,
|
|
})
|
|
if (error) toast({ description: error.message })
|
|
mutateTypebots({ typebots: typebots.filter((t) => t.id !== typebotId) })
|
|
}
|
|
|
|
const handleCreateFolder = async () => {
|
|
if (!folders) return
|
|
setIsCreatingFolder(true)
|
|
const { error, data: newFolder } = await createFolder({
|
|
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)),
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Flex w="full" justify="center" align="center" pt={4}>
|
|
<DndContext
|
|
onDragStart={handleDragStart}
|
|
onDragEnd={handleDragEnd}
|
|
sensors={sensors}
|
|
>
|
|
<Stack w="1000px" spacing={6}>
|
|
<Skeleton isLoaded={folder?.name !== undefined}>
|
|
<Heading as="h1">{folder?.name}</Heading>
|
|
</Skeleton>
|
|
<Stack>
|
|
<HStack>
|
|
{folder && <BackButton id={folder.parentFolderId} />}
|
|
<Button
|
|
leftIcon={<FolderPlusIcon />}
|
|
onClick={handleCreateFolder}
|
|
isLoading={isCreatingFolder || isFolderLoading}
|
|
>
|
|
Create a folder
|
|
</Button>
|
|
</HStack>
|
|
<Wrap spacing={4}>
|
|
<CreateBotButton
|
|
folderId={folder?.id}
|
|
isLoading={isTypebotLoading}
|
|
/>
|
|
{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)}
|
|
/>
|
|
))}
|
|
<DragOverlay dropAnimation={null}>
|
|
{draggedTypebot && (
|
|
<TypebotCardOverlay typebot={draggedTypebot} />
|
|
)}
|
|
</DragOverlay>
|
|
</Wrap>
|
|
</Stack>
|
|
</Stack>
|
|
</DndContext>
|
|
</Flex>
|
|
)
|
|
}
|