Skip to content

Commit

Permalink
feat(results): ✨ Add logs in results
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Mar 1, 2022
1 parent 4630512 commit ebf92b5
Show file tree
Hide file tree
Showing 27 changed files with 408 additions and 120 deletions.
9 changes: 9 additions & 0 deletions apps/builder/assets/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,12 @@ export const UsersIcon = (props: IconProps) => (
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</Icon>
)

export const AlignLeftTextIcon = (props: IconProps) => (
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
<line x1="17" y1="10" x2="3" y2="10"></line>
<line x1="21" y1="6" x2="3" y2="6"></line>
<line x1="21" y1="14" x2="3" y2="14"></line>
<line x1="17" y1="18" x2="3" y2="18"></line>
</Icon>
)
23 changes: 8 additions & 15 deletions apps/builder/components/editor/preview/PreviewDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import {
FlexProps,
useEventListener,
useToast,
UseToastOptions,
VStack,
} from '@chakra-ui/react'
import { TypebotViewer } from 'bot-engine'
import { headerHeight } from 'components/shared/TypebotHeader'
import { useEditor } from 'contexts/EditorContext'
import { useGraph } from 'contexts/GraphContext'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import React, { useEffect, useMemo, useState } from 'react'
import { Log } from 'db'
import React, { useMemo, useState } from 'react'
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'

export const PreviewDrawer = () => {
Expand Down Expand Up @@ -57,20 +59,10 @@ export const PreviewDrawer = () => {
setRightPanel(undefined)
}

useEffect(() => {
const onMessageFromBot = (event: MessageEvent) => {
if (event.data.typebotInfo) {
toast({ description: event.data.typebotInfo })
}
if (event.data.typebotError) {
toast({ description: event.data.typebotError, status: 'error' })
}
}
window.addEventListener('message', onMessageFromBot)
return () => {
window.removeEventListener('message', onMessageFromBot)
}
})
const handleNewLog = (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => {
toast(log as UseToastOptions)
console.log(log.details)
}

return (
<Flex
Expand Down Expand Up @@ -113,6 +105,7 @@ export const PreviewDrawer = () => {
<TypebotViewer
typebot={publicTypebot}
onNewBlockVisible={setPreviewingEdge}
onNewLog={handleNewLog}
isPreview
/>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react/jsx-key */
import { chakra, Checkbox, Flex } from '@chakra-ui/react'
import { Button, chakra, Checkbox, Flex, HStack, Text } from '@chakra-ui/react'
import { AlignLeftTextIcon } from 'assets/icons'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import React, { useEffect, useMemo, useRef } from 'react'
import { Hooks, useRowSelect, useTable } from 'react-table'
Expand All @@ -12,20 +13,21 @@ type SubmissionsTableProps = {
hasMore?: boolean
onNewSelection: (indices: number[]) => void
onScrollToBottom: () => void
onLogOpenIndex: (index: number) => () => void
}

export const SubmissionsTable = ({
data,
hasMore,
onNewSelection,
onScrollToBottom,
onLogOpenIndex,
}: SubmissionsTableProps) => {
const { publishedTypebot } = useTypebot()
const columns: any = useMemo(
() => (publishedTypebot ? parseSubmissionsColumns(publishedTypebot) : []),
[publishedTypebot]
)

const bottomElement = useRef<HTMLDivElement | null>(null)
const tableWrapper = useRef<HTMLDivElement | null>(null)

Expand Down Expand Up @@ -87,6 +89,19 @@ export const SubmissionsTable = ({
</chakra.th>
)
})}
<chakra.th
px="4"
py="2"
border="1px"
borderColor="gray.200"
fontWeight="normal"
whiteSpace="nowrap"
>
<HStack>
<AlignLeftTextIcon />
<Text>Logs</Text>
</HStack>
</chakra.th>
</tr>
)
})}
Expand Down Expand Up @@ -118,10 +133,17 @@ export const SubmissionsTable = ({
</chakra.td>
)
})}
<chakra.td px="4" py="2" border="1px" borderColor="gray.200">
<Button size="sm" onClick={onLogOpenIndex(idx)}>
See logs
</Button>
</chakra.td>
</tr>
)
})}
{hasMore === true && <LoadingRows totalColumns={columns.length} />}
{hasMore === true && (
<LoadingRows totalColumns={columns.length + 1} />
)}
</tbody>
</chakra.table>
</Flex>
Expand Down
99 changes: 99 additions & 0 deletions apps/builder/layouts/results/LogsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
Stack,
Spinner,
ModalFooter,
Accordion,
AccordionItem,
AccordionButton,
HStack,
AccordionIcon,
AccordionPanel,
Text,
Tag,
} from '@chakra-ui/react'
import { Log } from 'db'
import { useLogs } from 'services/typebots/logs'
import { isDefined } from 'utils'

export const LogsModal = ({
resultId,
onClose,
}: {
resultId?: string
onClose: () => void
}) => {
const { isLoading, logs } = useLogs(resultId)
return (
<Modal isOpen={isDefined(resultId)} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Logs</ModalHeader>
<ModalCloseButton />
<ModalBody as={Stack}>
{logs?.map((log, idx) => (
<LogCard key={idx} log={log} />
))}
{isLoading && <Spinner />}
</ModalBody>

<ModalFooter />
</ModalContent>
</Modal>
)
}

const LogCard = ({ log }: { log: Log }) => {
if (log.details)
return (
<Accordion allowToggle>
<AccordionItem style={{ borderBottomWidth: 0, borderWidth: 0 }}>
<AccordionButton
as={HStack}
p="4"
cursor="pointer"
justifyContent="space-between"
borderRadius="md"
>
<HStack>
<StatusTag status={log.status} />
<Text>{log.description}</Text>
</HStack>
<AccordionIcon />
</AccordionButton>
<AccordionPanel
as="pre"
overflow="scroll"
borderWidth="1px"
borderRadius="md"
>
{log.details}
</AccordionPanel>
</AccordionItem>
</Accordion>
)
return (
<HStack p="4">
<StatusTag status={log.status} />
<Text>{log.description}</Text>
</HStack>
)
}

const StatusTag = ({ status }: { status: string }) => {
switch (status) {
case 'error':
return <Tag colorScheme={'red'}>Fail</Tag>
case 'warning':
return <Tag colorScheme={'orange'}>Warn</Tag>
case 'info':
return <Tag colorScheme={'blue'}>Info</Tag>
default:
return <Tag colorScheme={'green'}>Ok</Tag>
}
}
14 changes: 14 additions & 0 deletions apps/builder/layouts/results/SubmissionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from 'services/typebots'
import { unparse } from 'papaparse'
import { UnlockProPlanInfo } from 'components/shared/Info'
import { LogsModal } from './LogsModal'

type Props = {
typebotId: string
Expand All @@ -27,6 +28,7 @@ export const SubmissionsContent = ({
const [selectedIndices, setSelectedIndices] = useState<number[]>([])
const [isDeleteLoading, setIsDeleteLoading] = useState(false)
const [isExportLoading, setIsExportLoading] = useState(false)
const [inspectingLogsResultId, setInspectingLogsResultId] = useState<string>()

const toast = useToast({
position: 'top-right',
Expand Down Expand Up @@ -109,6 +111,13 @@ export const SubmissionsContent = ({
[results]
)

const handleLogsModalClose = () => setInspectingLogsResultId(undefined)

const handleLogOpenIndex = (index: number) => () => {
if (!results) return
setInspectingLogsResultId(results[index].id)
}

return (
<Stack maxW="1200px" w="full" pb="28">
{totalHiddenResults && (
Expand All @@ -117,6 +126,10 @@ export const SubmissionsContent = ({
contentLabel="You are seeing complete submissions only."
/>
)}
<LogsModal
resultId={inspectingLogsResultId}
onClose={handleLogsModalClose}
/>
<Flex w="full" justifyContent="flex-end">
<ResultsActionButtons
isDeleteLoading={isDeleteLoading}
Expand All @@ -132,6 +145,7 @@ export const SubmissionsContent = ({
onNewSelection={handleNewSelection}
onScrollToBottom={handleScrolledToBottom}
hasMore={hasMore}
onLogOpenIndex={handleLogOpenIndex}
/>
</Stack>
)
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/pages/api/typebots/[typebotId]/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
typebotId,
typebot: { ownerId: user.email === adminEmail ? undefined : user.id },
answers: { some: {} },
isCompleted: isFreePlan(user) ? false : undefined,
isCompleted: isFreePlan(user) ? true : undefined,
},
orderBy: {
createdAt: 'desc',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getAuthenticatedUser } from 'services/api/utils'
import { methodNotAllowed, notAuthenticated } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req)
if (!user) return notAuthenticated(res)
if (req.method === 'GET') {
const resultId = req.query.resultId as string
const logs = await prisma.log.findMany({ where: { resultId } })
return res.send({ logs })
}
methodNotAllowed(res)
}

export default withSentry(handler)
15 changes: 15 additions & 0 deletions apps/builder/services/typebots/logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Log } from 'db'
import { fetcher } from 'services/utils'
import useSWR from 'swr'

export const useLogs = (resultId?: string, onError?: (e: Error) => void) => {
const { data, error } = useSWR<{ logs: Log[] }>(
resultId ? `/api/typebots/t/results/${resultId}/logs` : null,
fetcher
)
if (error && onError) onError(error)
return {
logs: data?.logs,
isLoading: !error && !data,
}
}
12 changes: 11 additions & 1 deletion apps/viewer/layouts/TypebotPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { TypebotViewer } from 'bot-engine'
import { Log } from 'db'
import { Answer, PublicTypebot, VariableWithValue } from 'models'
import React, { useEffect, useState } from 'react'
import { upsertAnswer } from 'services/answer'
import { SEO } from '../components/Seo'
import { createResult, updateResult } from '../services/result'
import { createLog, createResult, updateResult } from '../services/result'
import { ErrorPage } from './ErrorPage'

export type TypebotPageProps = {
Expand Down Expand Up @@ -58,6 +59,14 @@ export const TypebotPage = ({
if (error) setError(error)
}

const handleNewLog = async (
log: Omit<Log, 'id' | 'createdAt' | 'resultId'>
) => {
if (!resultId) return setError(new Error('Result was not created'))
const { error } = await createLog(resultId, log)
if (error) setError(error)
}

if (error) {
return <ErrorPage error={error} />
}
Expand All @@ -74,6 +83,7 @@ export const TypebotPage = ({
onNewAnswer={handleNewAnswer}
onCompleted={handleCompleted}
onVariablesPrefilled={initializeResult}
onNewLog={handleNewLog}
/>
)}
</div>
Expand Down
23 changes: 0 additions & 23 deletions apps/viewer/pages/api/results.ts

This file was deleted.

Loading

2 comments on commit ebf92b5

@vercel
Copy link

@vercel vercel bot commented on ebf92b5 Mar 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on ebf92b5 Mar 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app

Please sign in to comment.