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

@ -3,8 +3,9 @@ import { AppProps } from 'next/app'
import { SessionProvider } from 'next-auth/react'
import { ChakraProvider } from '@chakra-ui/react'
import { customTheme } from 'libs/chakra'
import 'assets/styles/routerProgressBar.css'
import { useRouterProgressBar } from 'libs/routerProgressBar'
import 'assets/styles/routerProgressBar.css'
import 'focus-visible/dist/focus-visible'
const App = ({ Component, pageProps }: AppProps) => {
useRouterProgressBar()

View File

@ -1,39 +1,87 @@
import NextAuth from 'next-auth'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import { PrismaClient } from '@typebot/prisma'
import EmailProvider from 'next-auth/providers/email'
import GitHubProvider from 'next-auth/providers/github'
import GoogleProvider from 'next-auth/providers/google'
import FacebookProvider from 'next-auth/providers/facebook'
import CredentialsProvider from 'next-auth/providers/credentials'
import prisma from 'libs/prisma'
import { Provider } from 'next-auth/providers'
import { User } from '@typebot/prisma'
const prisma = new PrismaClient()
const providers: Provider[] = [
EmailProvider({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD,
},
},
from: process.env.EMAIL_FROM,
}),
]
if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET)
providers.push(
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
})
)
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET)
providers.push(
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
})
)
if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET)
providers.push(
FacebookProvider({
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
})
)
if (process.env.NODE_ENV !== 'production')
providers.push(
CredentialsProvider({
name: 'Credentials',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'email@email.com',
},
},
async authorize(credentials) {
const user = await prisma.user.findUnique({
where: { email: credentials?.email },
})
return user
},
})
)
export default NextAuth({
adapter: PrismaAdapter(prisma),
secret: process.env.SECRET,
providers: [
EmailProvider({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD,
},
},
from: process.env.EMAIL_FROM,
}),
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID ?? '',
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? '',
}),
FacebookProvider({
clientId: process.env.FACEBOOK_CLIENT_ID ?? '',
clientSecret: process.env.FACEBOOK_CLIENT_SECRET ?? '',
}),
],
providers,
session: {
strategy: process.env.NODE_ENV === 'production' ? 'database' : 'jwt',
},
callbacks: {
jwt: async ({ token, user }) => {
user && (token.user = user)
return token
},
session: async ({ session, token }) => {
if (token.user) session.user = token.user as User
return session
},
},
})

View File

@ -0,0 +1,36 @@
import { DashboardFolder, User } from '@typebot/prisma'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { methodNotAllowed } from 'services/api/utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getSession({ req })
if (!session?.user)
return res.status(401).json({ message: 'Not authenticated' })
const user = session.user as User
const parentFolderId = req.query.parentId
? req.query.parentId.toString()
: null
if (req.method === 'GET') {
const folders = await prisma.dashboardFolder.findMany({
where: {
ownerId: user.id,
parentFolderId,
},
})
return res.send({ folders })
}
if (req.method === 'POST') {
const data = JSON.parse(req.body) as Pick<DashboardFolder, 'parentFolderId'>
const folder = await prisma.dashboardFolder.create({
data: { ...data, ownerId: user.id, name: 'New folder' },
})
return res.send(folder)
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,37 @@
import { DashboardFolder } from '@typebot/prisma'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { methodNotAllowed } from 'services/api/utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getSession({ req })
if (!session?.user)
return res.status(401).json({ message: 'Not authenticated' })
const id = req.query.id.toString()
if (req.method === 'GET') {
const folder = await prisma.dashboardFolder.findUnique({
where: { id },
})
return res.send({ folder })
}
if (req.method === 'DELETE') {
const folders = await prisma.dashboardFolder.delete({
where: { id },
})
return res.send({ folders })
}
if (req.method === 'PATCH') {
const data = JSON.parse(req.body) as Partial<DashboardFolder>
const folders = await prisma.dashboardFolder.update({
where: { id },
data,
})
return res.send({ typebots: folders })
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,34 @@
import { Typebot, User } from '@typebot/prisma'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { methodNotAllowed } from 'services/api/utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getSession({ req })
if (!session?.user)
return res.status(401).json({ message: 'Not authenticated' })
const user = session.user as User
if (req.method === 'GET') {
const folderId = req.query.folderId ? req.query.folderId.toString() : null
const typebots = await prisma.typebot.findMany({
where: {
ownerId: user.id,
folderId,
},
})
return res.send({ typebots })
}
if (req.method === 'POST') {
const data = JSON.parse(req.body) as Typebot
const typebot = await prisma.typebot.create({
data: { ...data, ownerId: user.id },
})
return res.send(typebot)
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,31 @@
import { Typebot } from '.prisma/client'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { methodNotAllowed } from 'services/api/utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getSession({ req })
if (!session?.user)
return res.status(401).json({ message: 'Not authenticated' })
const id = req.query.id.toString()
if (req.method === 'DELETE') {
const typebots = await prisma.typebot.delete({
where: { id },
})
return res.send({ typebots })
}
if (req.method === 'PATCH') {
const data = JSON.parse(req.body) as Partial<Typebot>
const typebots = await prisma.typebot.update({
where: { id },
data,
})
return res.send({ typebots })
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,18 @@
import withAuth from 'components/HOC/withUser'
import React from 'react'
import { Stack } from '@chakra-ui/layout'
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
import { Seo } from 'components/Seo'
import { FolderContent } from 'components/dashboard/FolderContent'
const DashboardPage = () => {
return (
<Stack>
<Seo title="My typebots" />
<DashboardHeader />
<FolderContent folder={null} />
</Stack>
)
}
export default withAuth(DashboardPage)

View File

@ -0,0 +1,43 @@
import React, { useState } from 'react'
import { Button, Stack, useToast } from '@chakra-ui/react'
import { useUser } from 'services/user'
import { useRouter } from 'next/router'
import { Seo } from 'components/Seo'
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
import { createTypebot } from 'services/typebots'
const TemplatesPage = () => {
const user = useUser()
const router = useRouter()
const [isLoading, setIsLoading] = useState(false)
const toast = useToast({
position: 'top-right',
status: 'error',
title: 'An error occured',
})
const handleCreateSubmit = async () => {
if (!user) return
setIsLoading(true)
const { error, data } = await createTypebot({
folderId: router.query.folderId?.toString() ?? null,
})
if (error) toast({ description: error.message })
if (data) router.push(`/typebots/${data.id}`)
setIsLoading(false)
}
return (
<Stack>
<Seo title="Templates" />
<DashboardHeader />
<Button ml={4} onClick={() => handleCreateSubmit()} isLoading={isLoading}>
Start from scratch
</Button>
</Stack>
)
}
export default TemplatesPage

View File

@ -0,0 +1,44 @@
import withAuth from 'components/HOC/withUser'
import React from 'react'
import { Flex, Stack } from '@chakra-ui/layout'
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
import { Seo } from 'components/Seo'
import { FolderContent } from 'components/dashboard/FolderContent'
import { useRouter } from 'next/router'
import { useFolderContent } from 'services/folders'
import { Spinner, useToast } from '@chakra-ui/react'
const FolderPage = () => {
const router = useRouter()
const toast = useToast({
position: 'top-right',
status: 'error',
})
const { folder } = useFolderContent({
folderId: router.query.id?.toString(),
onError: (error) => {
toast({
title: "Couldn't fetch folder content",
description: error.message,
})
},
})
return (
<Stack>
<Seo title="My typebots" />
<DashboardHeader />
{!folder ? (
<Flex flex="1">
<Spinner mx="auto" />
</Flex>
) : (
<FolderContent folder={folder} />
)}
</Stack>
)
}
export default withAuth(FolderPage)

View File

@ -1,9 +0,0 @@
import withAuth, { withAuthProps } from 'components/HOC/withUser'
import { Text } from '@chakra-ui/react'
import React from 'react'
const TypebotsPage = ({ user }: withAuthProps) => {
return <Text data-testid="authenticated">Hello {user?.email}</Text>
}
export default withAuth(TypebotsPage)