refactor(models): 🎨 Build types from validation schemas
This commit is contained in:
@ -5,7 +5,7 @@ import {
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { byId, isNotEmpty } from 'utils'
|
||||
import { byId } from 'utils'
|
||||
import { MemberInWorkspace, Plan, Workspace, WorkspaceRole } from 'db'
|
||||
import {
|
||||
createNewWorkspace,
|
||||
@ -87,8 +87,11 @@ export const WorkspaceContext = ({ children }: { children: ReactNode }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [typebot?.workspaceId])
|
||||
|
||||
const switchWorkspace = (workspaceId: string) =>
|
||||
setCurrentWorkspace(workspaces?.find(byId(workspaceId)))
|
||||
const switchWorkspace = (workspaceId: string) => {
|
||||
const newWorkspace = workspaces?.find(byId(workspaceId))
|
||||
if (!newWorkspace) return
|
||||
setCurrentWorkspace(newWorkspace)
|
||||
}
|
||||
|
||||
const createWorkspace = async (name?: string) => {
|
||||
if (!workspaces) return
|
||||
|
@ -70,11 +70,13 @@ export const ResultsContent = () => {
|
||||
</HStack>
|
||||
</Flex>
|
||||
<Flex pt={['10px', '60px']} w="full" justify="center">
|
||||
{publishedTypebot &&
|
||||
{workspace &&
|
||||
publishedTypebot &&
|
||||
(isAnalytics ? (
|
||||
<AnalyticsContent stats={stats} />
|
||||
) : (
|
||||
<SubmissionsContent
|
||||
workspaceId={workspace.id}
|
||||
typebotId={publishedTypebot.typebotId}
|
||||
onDeleteResults={handleDeletedResults}
|
||||
totalResults={stats?.totalStarts ?? 0}
|
||||
|
@ -18,12 +18,14 @@ import { Plan } from 'db'
|
||||
import { useToast } from 'components/shared/hooks/useToast'
|
||||
|
||||
type Props = {
|
||||
workspaceId: string
|
||||
typebotId: string
|
||||
totalResults: number
|
||||
totalHiddenResults?: number
|
||||
onDeleteResults: (total: number) => void
|
||||
}
|
||||
export const SubmissionsContent = ({
|
||||
workspaceId,
|
||||
typebotId,
|
||||
totalResults,
|
||||
totalHiddenResults,
|
||||
@ -51,6 +53,7 @@ export const SubmissionsContent = ({
|
||||
const resultHeader = parseResultHeader(blocksAndVariables)
|
||||
|
||||
const { data, mutate, setSize, hasMore } = useResults({
|
||||
workspaceId,
|
||||
typebotId,
|
||||
onError: (err) => showToast({ title: err.name, description: err.message }),
|
||||
})
|
||||
@ -125,7 +128,7 @@ export const SubmissionsContent = ({
|
||||
|
||||
const getAllTableData = async () => {
|
||||
if (!publishedTypebot) return []
|
||||
const results = await getAllResults(typebotId)
|
||||
const results = await getAllResults(workspaceId, typebotId)
|
||||
return convertResultsToTableData(results, resultHeader)
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,19 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { canReadTypebot, canWriteTypebot } from 'services/api/dbRules'
|
||||
import { getAuthenticatedUser } from 'services/api/utils'
|
||||
import { isFreePlan } from 'services/workspace'
|
||||
import { forbidden, methodNotAllowed, notAuthenticated } from 'utils'
|
||||
import {
|
||||
badRequest,
|
||||
forbidden,
|
||||
methodNotAllowed,
|
||||
notAuthenticated,
|
||||
} from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req)
|
||||
if (!user) return notAuthenticated(res)
|
||||
const workspaceId = req.query.workspaceId as string | undefined
|
||||
if (req.method === 'GET') {
|
||||
if (!workspaceId) return badRequest(res, 'workspaceId is required')
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId, members: { some: { userId: user.id } } },
|
||||
select: { plan: true },
|
||||
|
@ -143,12 +143,13 @@ test.describe.parallel('Editor', () => {
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
|
||||
await page.click('[data-testid="editable-icon"]')
|
||||
await expect(page.locator('text="My awesome typebot"')).toBeVisible()
|
||||
await page.fill('input[placeholder="Search..."]', 'love')
|
||||
await page.click('text="😍"')
|
||||
await page.click('text="My awesome typebot"')
|
||||
await page.fill('input[value="My awesome typebot"]', 'My superb typebot')
|
||||
await page.press('input[value="My superb typebot"]', 'Enter')
|
||||
await page.goto(`/typebots`)
|
||||
await page.click('[aria-label="Navigate back"]')
|
||||
await expect(page.locator('text="😍"')).toBeVisible()
|
||||
await expect(page.locator('text="My superb typebot"')).toBeVisible()
|
||||
})
|
||||
|
@ -78,6 +78,7 @@ test.describe.parallel('Settings page', () => {
|
||||
)
|
||||
await page.goto(`/typebots/${typebotId}/settings`)
|
||||
await page.click('button:has-text("Metadata")')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Fav icon
|
||||
const favIconImg = page.locator('img >> nth=0')
|
||||
|
@ -2,7 +2,7 @@ import { ResultWithAnswers, VariableWithValue, ResultHeaderCell } from 'models'
|
||||
import useSWRInfinite from 'swr/infinite'
|
||||
import { stringify } from 'qs'
|
||||
import { Answer } from 'db'
|
||||
import { isDefined, sendRequest } from 'utils'
|
||||
import { isDefined, isEmpty, sendRequest } from 'utils'
|
||||
import { fetcher } from 'services/utils'
|
||||
import { HStack, Text } from '@chakra-ui/react'
|
||||
import { CodeIcon, CalendarIcon } from 'assets/icons'
|
||||
@ -11,6 +11,7 @@ import { StepIcon } from 'components/editor/StepsSideBar/StepIcon'
|
||||
const paginationLimit = 50
|
||||
|
||||
const getKey = (
|
||||
workspaceId: string,
|
||||
typebotId: string,
|
||||
pageIndex: number,
|
||||
previousPageData: {
|
||||
@ -18,16 +19,19 @@ const getKey = (
|
||||
}
|
||||
) => {
|
||||
if (previousPageData && previousPageData.results.length === 0) return null
|
||||
if (pageIndex === 0) return `/api/typebots/${typebotId}/results?limit=50`
|
||||
if (pageIndex === 0)
|
||||
return `/api/typebots/${typebotId}/results?limit=50&workspaceId=${workspaceId}`
|
||||
return `/api/typebots/${typebotId}/results?lastResultId=${
|
||||
previousPageData.results[previousPageData.results.length - 1].id
|
||||
}&limit=${paginationLimit}`
|
||||
}&limit=${paginationLimit}&workspaceId=${workspaceId}`
|
||||
}
|
||||
|
||||
export const useResults = ({
|
||||
workspaceId,
|
||||
typebotId,
|
||||
onError,
|
||||
}: {
|
||||
workspaceId: string
|
||||
typebotId: string
|
||||
onError: (error: Error) => void
|
||||
}) => {
|
||||
@ -40,9 +44,14 @@ export const useResults = ({
|
||||
previousPageData: {
|
||||
results: ResultWithAnswers[]
|
||||
}
|
||||
) => getKey(typebotId, pageIndex, previousPageData),
|
||||
) => getKey(workspaceId, typebotId, pageIndex, previousPageData),
|
||||
fetcher,
|
||||
{ revalidateAll: true }
|
||||
{
|
||||
revalidateAll: true,
|
||||
dedupingInterval: isEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? undefined
|
||||
: 0,
|
||||
}
|
||||
)
|
||||
|
||||
if (error) onError(error)
|
||||
@ -80,12 +89,12 @@ export const deleteAllResults = async (typebotId: string) =>
|
||||
method: 'DELETE',
|
||||
})
|
||||
|
||||
export const getAllResults = async (typebotId: string) => {
|
||||
export const getAllResults = async (workspaceId: string, typebotId: string) => {
|
||||
const results = []
|
||||
let hasMore = true
|
||||
let lastResultId: string | undefined = undefined
|
||||
do {
|
||||
const query = stringify({ limit: 200, lastResultId })
|
||||
const query = stringify({ limit: 200, lastResultId, workspaceId })
|
||||
const { data } = await sendRequest<{ results: ResultWithAnswers[] }>({
|
||||
url: `/api/typebots/${typebotId}/results?${query}`,
|
||||
method: 'GET',
|
||||
|
Reference in New Issue
Block a user