2
0

Add Dashboard

This commit is contained in:
Baptiste Arnaud
2021-12-06 15:48:50 +01:00
parent 5e14a94dea
commit 54a641b819
47 changed files with 2002 additions and 168 deletions

View File

@ -0,0 +1,24 @@
import { Button } from '@chakra-ui/react'
import { useDroppable } from '@dnd-kit/core'
import { ChevronLeftIcon } from 'assets/icons'
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
import React from 'react'
export const BackButton = ({ id }: { id: string | null }) => {
const { setNodeRef, isOver } = useDroppable({
id: id?.toString() ?? 'root',
})
return (
<Button
as={NextChakraLink}
href={id ? `/typebots/folders/${id}` : '/typebots'}
leftIcon={<ChevronLeftIcon />}
variant={'outline'}
colorScheme={isOver ? 'blue' : 'gray'}
borderWidth={isOver ? '3px' : '1px'}
ref={setNodeRef}
>
Back
</Button>
)
}

View File

@ -0,0 +1,41 @@
import { Button, ButtonProps, Text, VStack } from '@chakra-ui/react'
import { PlusIcon } from 'assets/icons'
import { useRouter } from 'next/router'
import React from 'react'
export const CreateBotButton = ({
folderId,
...props
}: { folderId?: string } & ButtonProps) => {
const router = useRouter()
const handleClick = () =>
folderId
? router.push(`/typebots/create?folderId=${folderId}`)
: router.push('/typebots/create')
return (
<Button
mr={{ sm: 6 }}
mb={6}
style={{ width: '225px', height: '270px' }}
onClick={handleClick}
paddingX={6}
whiteSpace={'normal'}
{...props}
>
<VStack spacing="6">
<PlusIcon fontSize="40px" />
<Text
fontSize={18}
fontWeight="medium"
maxW={40}
textAlign="center"
mt="6"
>
Create a typebot
</Text>
</VStack>
</Button>
)
}

View File

@ -0,0 +1,158 @@
import { DashboardFolder } from '.prisma/client'
import {
Button,
Editable,
EditableInput,
EditablePreview,
MenuItem,
useDisclosure,
Text,
VStack,
IconButton,
Menu,
MenuButton,
MenuList,
useToast,
SkeletonText,
SkeletonCircle,
WrapItem,
} from '@chakra-ui/react'
import { useDroppable } from '@dnd-kit/core'
import { FolderIcon, MoreVerticalIcon } from 'assets/icons'
import { ConfirmModal } from 'components/modals/ConfirmModal'
import { useRouter } from 'next/router'
import React from 'react'
import { deleteFolder, updateFolder } from 'services/folders'
export const FolderButton = ({
folder,
onFolderDeleted,
onFolderRenamed,
}: {
folder: DashboardFolder
onFolderDeleted: () => void
onFolderRenamed: (newName: string) => void
}) => {
const router = useRouter()
const { setNodeRef, isOver } = useDroppable({
id: folder.id.toString(),
})
const { isOpen, onOpen, onClose } = useDisclosure()
const toast = useToast({
position: 'top-right',
status: 'error',
})
const onDeleteClick = async () => {
const { error } = await deleteFolder(folder.id)
return error
? toast({
title: "Couldn't delete the folder",
description: error.message,
})
: onFolderDeleted()
}
const onRenameSubmit = async (newName: string) => {
if (newName === '' || newName === folder.name) return
const { error } = await updateFolder(folder.id, { name: newName })
return error
? toast({ title: 'An error occured', description: error.message })
: onFolderRenamed(newName)
}
const handleClick = () => {
router.push(`/typebots/folders/${folder.id}`)
}
return (
<Button
as={WrapItem}
ref={setNodeRef}
style={{ width: '225px', height: '270px' }}
paddingX={6}
whiteSpace={'normal'}
pos="relative"
cursor="pointer"
variant="outline"
colorScheme={isOver ? 'blue' : 'gray'}
borderWidth={isOver ? '3px' : '1px'}
justifyContent="center"
onClick={handleClick}
data-testid="folder-button"
>
<Menu>
<MenuButton
as={IconButton}
icon={<MoreVerticalIcon />}
aria-label="Show folder menu"
onClick={(e) => e.stopPropagation()}
colorScheme="blue"
variant="ghost"
size="lg"
pos="absolute"
top="5"
right="5"
/>
<MenuList>
<MenuItem
color="red"
onClick={(e) => {
e.stopPropagation()
onOpen()
}}
>
Delete
</MenuItem>
</MenuList>
</Menu>
<VStack spacing="4">
<FolderIcon fontSize={50} color="blue.500" fill="blue.500" />
<Editable
defaultValue={folder.name}
fontSize="18"
onClick={(e) => e.stopPropagation()}
onSubmit={onRenameSubmit}
>
<EditablePreview _hover={{ bgColor: 'gray.300' }} px="2" />
<EditableInput textAlign="center" />
</Editable>
</VStack>
<ConfirmModal
isOpen={isOpen}
onClose={onClose}
confirmButtonLabel={'Delete'}
message={
<Text>
Are you sure you want to delete <strong>{folder.name}</strong>{' '}
folder? (Everything inside will be move to your dashboard)
</Text>
}
title={`Delete ${folder.name}?`}
onConfirm={onDeleteClick}
confirmButtonColor="red"
/>
</Button>
)
}
export const ButtonSkeleton = () => (
<Button
as={VStack}
mr={{ sm: 6 }}
mb={6}
style={{ width: '225px', height: '270px' }}
paddingX={6}
whiteSpace={'normal'}
pos="relative"
cursor="pointer"
variant="outline"
colorScheme={'gray'}
>
<VStack spacing="6" w="full">
<SkeletonCircle boxSize="45px" />
<SkeletonText noOfLines={2} w="full" />
</VStack>
</Button>
)

View File

@ -0,0 +1,133 @@
import React from 'react'
import {
Button,
Flex,
MenuItem,
Text,
useDisclosure,
useToast,
VStack,
WrapItem,
} from '@chakra-ui/react'
import { useDraggable } from '@dnd-kit/core'
import { useRouter } from 'next/router'
import { Typebot } from '@typebot/prisma'
import { isMobile } from 'services/utils'
import { MoreButton } from 'components/MoreButton'
import { ConfirmModal } from 'components/modals/ConfirmModal'
import { GlobeIcon, ToolIcon } from 'assets/icons'
import { deleteTypebot, duplicateTypebot } from 'services/typebots'
type ChatbotCardProps = {
typebot: Typebot
onTypebotDeleted: () => void
}
export const TypebotButton = ({
typebot,
onTypebotDeleted,
}: ChatbotCardProps) => {
const router = useRouter()
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
id: typebot.id.toString(),
})
const {
isOpen: isDeleteOpen,
onOpen: onDeleteOpen,
onClose: onDeleteClose,
} = useDisclosure()
const toast = useToast({
position: 'top-right',
status: 'error',
})
const handleTypebotClick = () => {
router.push(
isMobile
? `/typebots/${typebot.id}/results/responses`
: `/typebots/${typebot.id}/edit`
)
}
const handleDeleteTypebotClick = async () => {
const { error } = await deleteTypebot(typebot.id)
if (error)
return toast({
title: "Couldn't delete typebot",
description: error.message,
})
onTypebotDeleted()
}
const handleDuplicateClick = async () => {
const { data: createdTypebot, error } = await duplicateTypebot(typebot)
if (error)
return toast({
title: "Couldn't duplicate typebot",
description: error.message,
})
if (createdTypebot) router.push(`/typebots/${createdTypebot?.id}`)
}
return (
<Button
as={WrapItem}
onClick={handleTypebotClick}
display="flex"
flexDir="column"
variant="outline"
colorScheme="gray"
color="gray.800"
w="225px"
h="270px"
mr={{ sm: 6 }}
mb={6}
rounded="lg"
whiteSpace="normal"
data-testid="typebot-button"
opacity={isDragging ? 0.2 : 1}
ref={setNodeRef}
{...listeners}
{...attributes}
>
<MoreButton pos="absolute" top="10px" right="10px">
<MenuItem onClick={handleDuplicateClick}>Duplicate</MenuItem>
<MenuItem color="red" onClick={onDeleteOpen}>
Delete
</MenuItem>
</MoreButton>
<VStack spacing="4">
<Flex
boxSize="45px"
rounded="full"
justifyContent="center"
alignItems="center"
bgColor={typebot.publishedTypebotId ? 'blue.500' : 'gray.400'}
color="white"
>
{typebot.publishedTypebotId ? (
<GlobeIcon fill="white" fontSize="20px" />
) : (
<ToolIcon fill="white" fontSize="20px" />
)}
</Flex>
<Text>{typebot.name}</Text>
</VStack>
<ConfirmModal
message={
<Text>
Are you sure you want to delete your Typebot &quot;{typebot.name}
&quot;.
<br />
All associated data will be lost.
</Text>
}
confirmButtonLabel="Delete"
onConfirm={handleDeleteTypebotClick}
isOpen={isDeleteOpen}
onClose={onDeleteClose}
/>
</Button>
)
}

View File

@ -0,0 +1,44 @@
import { Button, Flex, Text, VStack } from '@chakra-ui/react'
import { Typebot } from '.prisma/client'
import { GlobeIcon, ToolIcon } from 'assets/icons'
type Props = {
typebot: Typebot
}
export const TypebotCardOverlay = ({ typebot }: Props) => {
return (
<div
className="sm:mr-6 mb-6 focus:outline-none rounded-lg "
style={{ width: '225px', height: '270px' }}
>
<Button
display="flex"
flexDir="column"
variant="outline"
colorScheme="gray"
w="full"
h="full"
whiteSpace="normal"
>
<VStack spacing={4}>
<Flex
boxSize="45px"
rounded="full"
justifyContent="center"
alignItems="center"
bgColor={typebot.publishedTypebotId ? 'blue.500' : 'gray.400'}
color="white"
>
{typebot.publishedTypebotId ? (
<GlobeIcon fill="white" fontSize="20px" />
) : (
<ToolIcon fill="white" fontSize="20px" />
)}
</Flex>
<Text>{typebot.name}</Text>
</VStack>
</Button>
</div>
)
}