♻️ (results) Introduce tRPC and use it for the results
This commit is contained in:
@ -31,7 +31,12 @@
|
||||
"@googleapis/drive": "4.0.0",
|
||||
"@sentry/nextjs": "7.19.0",
|
||||
"@stripe/stripe-js": "1.44.1",
|
||||
"@tanstack/react-query": "^4.16.1",
|
||||
"@tanstack/react-table": "8.5.28",
|
||||
"@trpc/client": "10.0.0-rc.8",
|
||||
"@trpc/next": "10.0.0-rc.8",
|
||||
"@trpc/react-query": "10.0.0-rc.8",
|
||||
"@trpc/server": "10.0.0-rc.8",
|
||||
"@udecode/plate-basic-marks": "18.9.0",
|
||||
"@udecode/plate-common": "^7.0.2",
|
||||
"@udecode/plate-core": "18.9.0",
|
||||
@ -80,6 +85,7 @@
|
||||
"svg-round-corners": "0.4.1",
|
||||
"swr": "1.3.0",
|
||||
"tinycolor2": "1.4.2",
|
||||
"trpc-openapi": "1.0.0-alpha.4",
|
||||
"typebot-js": "workspace:*",
|
||||
"use-debounce": "8.0.4"
|
||||
},
|
||||
@ -110,7 +116,10 @@
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"models": "workspace:*",
|
||||
"next-transpile-modules": "10.0.0",
|
||||
"superjson": "^1.11.0",
|
||||
"tsconfig": "workspace:*",
|
||||
"typescript": "4.8.4",
|
||||
"utils": "workspace:*"
|
||||
"utils": "workspace:*",
|
||||
"zod": "3.19.1"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export const useAnswersCount = ({
|
||||
{ answersCounts: AnswersCount[] },
|
||||
Error
|
||||
>(
|
||||
typebotId ? `/api/typebots/${typebotId}/results/answers/count` : null,
|
||||
typebotId ? `/api/typebots/${typebotId}/analytics/answersCount` : null,
|
||||
fetcher
|
||||
)
|
||||
if (error) onError(error)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BoxProps, Flex } from '@chakra-ui/react'
|
||||
import { BoxProps, Flex, useEventListener } from '@chakra-ui/react'
|
||||
import { useGraph, useGroupsCoordinates } from '../../providers'
|
||||
import { Source } from 'models'
|
||||
import React, { MouseEvent, useEffect, useRef, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export const SourceEndpoint = ({
|
||||
source,
|
||||
@ -15,11 +15,6 @@ export const SourceEndpoint = ({
|
||||
const { groupsCoordinates } = useGroupsCoordinates()
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const handleMouseDown = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
setConnectingIds({ source })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (ranOnce || !ref.current || Object.keys(groupsCoordinates).length === 0)
|
||||
return
|
||||
@ -32,6 +27,23 @@ export const SourceEndpoint = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ref.current, groupsCoordinates])
|
||||
|
||||
useEventListener(
|
||||
'pointerdown',
|
||||
(e) => {
|
||||
e.stopPropagation()
|
||||
setConnectingIds({ source })
|
||||
},
|
||||
ref.current
|
||||
)
|
||||
|
||||
useEventListener(
|
||||
'mousedown',
|
||||
(e) => {
|
||||
e.stopPropagation()
|
||||
},
|
||||
ref.current
|
||||
)
|
||||
|
||||
if (!groupsCoordinates) return <></>
|
||||
return (
|
||||
<Flex
|
||||
@ -39,13 +51,10 @@ export const SourceEndpoint = ({
|
||||
data-testid="endpoint"
|
||||
boxSize="32px"
|
||||
rounded="full"
|
||||
onPointerDownCapture={handleMouseDown}
|
||||
onMouseDownCapture={(e) => e.stopPropagation()}
|
||||
cursor="copy"
|
||||
justify="center"
|
||||
align="center"
|
||||
pointerEvents="all"
|
||||
className="prevent-group-drag"
|
||||
{...props}
|
||||
>
|
||||
<Flex
|
||||
@ -54,7 +63,6 @@ export const SourceEndpoint = ({
|
||||
align="center"
|
||||
bgColor="gray.100"
|
||||
rounded="full"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<Flex
|
||||
boxSize="13px"
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
useDisclosure,
|
||||
useEventListener,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
@ -152,6 +153,8 @@ export const BlockNode = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [openedBlockId])
|
||||
|
||||
useEventListener('pointerdown', (e) => e.stopPropagation(), blockRef.current)
|
||||
|
||||
return isEditing && isTextBubbleBlock(block) ? (
|
||||
<TextBubbleEditor
|
||||
id={block.id}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { ResultHeaderCell, ResultWithAnswers } from 'models'
|
||||
import { createContext, ReactNode, useContext, useMemo } from 'react'
|
||||
import { KeyedMutator } from 'swr'
|
||||
import { parseResultHeader } from 'utils'
|
||||
import { useTypebot } from '../editor/providers/TypebotProvider'
|
||||
import { useResultsQuery } from './hooks/useResultsQuery'
|
||||
@ -10,42 +10,37 @@ import { convertResultsToTableData } from './utils'
|
||||
const resultsContext = createContext<{
|
||||
resultsList: { results: ResultWithAnswers[] }[] | undefined
|
||||
flatResults: ResultWithAnswers[]
|
||||
hasMore: boolean
|
||||
hasNextPage: boolean
|
||||
resultHeader: ResultHeaderCell[]
|
||||
totalResults: number
|
||||
tableData: TableData[]
|
||||
onDeleteResults: (totalResultsDeleted: number) => void
|
||||
fetchMore: () => void
|
||||
mutate: KeyedMutator<
|
||||
{
|
||||
results: ResultWithAnswers[]
|
||||
}[]
|
||||
>
|
||||
fetchNextPage: () => void
|
||||
refetchResults: () => void
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
export const ResultsProvider = ({
|
||||
children,
|
||||
workspaceId,
|
||||
typebotId,
|
||||
totalResults,
|
||||
onDeleteResults,
|
||||
}: {
|
||||
children: ReactNode
|
||||
workspaceId: string
|
||||
typebotId: string
|
||||
totalResults: number
|
||||
onDeleteResults: (totalResultsDeleted: number) => void
|
||||
}) => {
|
||||
const { publishedTypebot, linkedTypebots } = useTypebot()
|
||||
const { data, mutate, setSize, hasMore } = useResultsQuery({
|
||||
workspaceId,
|
||||
const { showToast } = useToast()
|
||||
const { data, fetchNextPage, hasNextPage, refetch } = useResultsQuery({
|
||||
typebotId,
|
||||
onError: (error) => {
|
||||
showToast({ description: error })
|
||||
},
|
||||
})
|
||||
|
||||
const fetchMore = () => setSize((state) => state + 1)
|
||||
|
||||
const resultHeader = useMemo(
|
||||
() =>
|
||||
publishedTypebot
|
||||
@ -70,13 +65,13 @@ export const ResultsProvider = ({
|
||||
value={{
|
||||
resultsList: data,
|
||||
flatResults: data?.flatMap((d) => d.results) ?? [],
|
||||
hasMore: hasMore ?? true,
|
||||
hasNextPage: hasNextPage ?? true,
|
||||
tableData,
|
||||
resultHeader,
|
||||
totalResults,
|
||||
onDeleteResults,
|
||||
fetchMore,
|
||||
mutate,
|
||||
fetchNextPage,
|
||||
refetchResults: refetch,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -3,12 +3,8 @@ import { canWriteTypebot } from '@/utils/api/dbRules'
|
||||
import { deleteFiles } from '@/utils/api/storage'
|
||||
import { User, Prisma } from 'db'
|
||||
import { InputBlockType, Typebot } from 'models'
|
||||
import { NextApiResponse } from 'next'
|
||||
import { forbidden } from 'utils/api'
|
||||
|
||||
export const archiveResults =
|
||||
(res: NextApiResponse) =>
|
||||
async ({
|
||||
export const archiveResults = async ({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter,
|
||||
@ -21,7 +17,7 @@ export const archiveResults =
|
||||
where: canWriteTypebot(typebotId, user),
|
||||
select: { groups: true },
|
||||
})
|
||||
if (!typebot) return forbidden(res)
|
||||
if (!typebot) return { success: false }
|
||||
const fileUploadBlockIds = (typebot as Typebot).groups
|
||||
.flatMap((g) => g.blocks)
|
||||
.filter((b) => b.type === InputBlockType.FILE)
|
||||
@ -52,4 +48,6 @@ export const archiveResults =
|
||||
variables: [],
|
||||
},
|
||||
})
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './archiveResults'
|
||||
export * from './router'
|
||||
|
@ -0,0 +1,39 @@
|
||||
import { canWriteTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { z } from 'zod'
|
||||
import { archiveResults } from '../archiveResults'
|
||||
|
||||
export const deleteResultsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/typebots/{typebotId}/results',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
ids: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input, ctx: { user } }) => {
|
||||
const idsArray = input.ids?.split(',')
|
||||
const { typebotId } = input
|
||||
const { success } = await archiveResults({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter: {
|
||||
id: (idsArray?.length ?? 0) > 0 ? { in: idsArray } : undefined,
|
||||
typebot: canWriteTypebot(typebotId, user),
|
||||
},
|
||||
})
|
||||
|
||||
if (!success)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Typebot not found',
|
||||
})
|
||||
})
|
@ -0,0 +1,30 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { canReadTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { logSchema } from 'models'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const getResultLogsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/typebots/{typebotId}/results/{resultId}/logs',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
resultId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(z.object({ logs: z.array(logSchema) }))
|
||||
.query(async ({ input: { typebotId, resultId }, ctx: { user } }) => {
|
||||
const logs = await prisma.log.findMany({
|
||||
where: {
|
||||
result: { id: resultId, typebot: canReadTypebot(typebotId, user) },
|
||||
},
|
||||
})
|
||||
|
||||
return { logs }
|
||||
})
|
@ -0,0 +1,59 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { canReadTypebot } from '@/utils/api/dbRules'
|
||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { ResultWithAnswers, resultWithAnswersSchema } from 'models'
|
||||
import { z } from 'zod'
|
||||
|
||||
const maxLimit = 200
|
||||
|
||||
export const getResultsProcedure = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/typebots/{typebotId}/results',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
typebotId: z.string(),
|
||||
limit: z.string().regex(/^[0-9]{1,3}$/),
|
||||
cursor: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
results: z.array(resultWithAnswersSchema),
|
||||
nextCursor: z.string().nullish(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input, ctx: { user } }) => {
|
||||
const limit = Number(input.limit)
|
||||
if (limit < 1 || limit > maxLimit)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'limit must be between 1 and 200',
|
||||
})
|
||||
const { cursor } = input
|
||||
const results = (await prisma.result.findMany({
|
||||
take: limit + 1,
|
||||
cursor: cursor ? { id: cursor } : undefined,
|
||||
where: {
|
||||
typebot: canReadTypebot(input.typebotId, user),
|
||||
answers: { some: {} },
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
include: { answers: true },
|
||||
})) as ResultWithAnswers[]
|
||||
|
||||
let nextCursor: typeof cursor | undefined
|
||||
if (results.length > limit) {
|
||||
const nextResult = results.pop()
|
||||
nextCursor = nextResult?.id
|
||||
}
|
||||
|
||||
return { results, nextCursor }
|
||||
})
|
@ -0,0 +1,3 @@
|
||||
export * from './deleteResultsProcedure'
|
||||
export * from './getResultLogsProcedure'
|
||||
export * from './getResultsProcedure'
|
12
apps/builder/src/features/results/api/router.ts
Normal file
12
apps/builder/src/features/results/api/router.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { router } from '@/utils/server/trpc'
|
||||
import {
|
||||
deleteResultsProcedure,
|
||||
getResultLogsProcedure,
|
||||
getResultsProcedure,
|
||||
} from './procedures'
|
||||
|
||||
export const resultsRouter = router({
|
||||
getResults: getResultsProcedure,
|
||||
deleteResults: deleteResultsProcedure,
|
||||
getResultLogs: getResultLogsProcedure,
|
||||
})
|
@ -27,7 +27,7 @@ type Props = {
|
||||
onClose: () => void
|
||||
}
|
||||
export const LogsModal = ({ typebotId, resultId, onClose }: Props) => {
|
||||
const { isLoading, logs } = useLogs(typebotId, resultId ?? undefined)
|
||||
const { isLoading, logs } = useLogs(typebotId, resultId)
|
||||
|
||||
return (
|
||||
<Modal isOpen={isDefined(resultId)} onClose={onClose} size="xl">
|
||||
|
@ -160,7 +160,6 @@ export const ResultsPage = () => {
|
||||
<AnalyticsGraphContainer stats={stats} />
|
||||
) : (
|
||||
<ResultsProvider
|
||||
workspaceId={workspace.id}
|
||||
typebotId={publishedTypebot.typebotId}
|
||||
totalResults={stats?.totalStarts ?? 0}
|
||||
onDeleteResults={handleDeletedResults}
|
||||
|
@ -9,6 +9,7 @@ type Props = {
|
||||
size: number
|
||||
isExpandButtonVisible: boolean
|
||||
cellIndex: number
|
||||
isSelected: boolean
|
||||
onExpandButtonClick: () => void
|
||||
}
|
||||
|
||||
@ -63,5 +64,6 @@ export default memo(
|
||||
Cell,
|
||||
(prev, next) =>
|
||||
prev.size === next.size &&
|
||||
prev.isExpandButtonVisible === next.isExpandButtonVisible
|
||||
prev.isExpandButtonVisible === next.isExpandButtonVisible &&
|
||||
prev.isSelected === next.isSelected
|
||||
)
|
||||
|
@ -13,10 +13,10 @@ import { useTypebot } from '@/features/editor'
|
||||
import { unparse } from 'papaparse'
|
||||
import React, { useState } from 'react'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { getAllResultsQuery } from '../../queries/getAllResultsQuery'
|
||||
import { convertResultsToTableData } from '../../utils'
|
||||
import { deleteResultsQuery } from '../../queries/deleteResultsQuery'
|
||||
import { useResults } from '../../ResultsProvider'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
|
||||
type ResultsActionButtonsProps = {
|
||||
selectedResultsId: string[]
|
||||
@ -31,10 +31,8 @@ export const ResultsActionButtons = ({
|
||||
const { typebot } = useTypebot()
|
||||
const { showToast } = useToast()
|
||||
const {
|
||||
resultsList: data,
|
||||
flatResults: results,
|
||||
resultHeader,
|
||||
mutate,
|
||||
totalResults,
|
||||
tableData,
|
||||
onDeleteResults,
|
||||
@ -42,14 +40,44 @@ export const ResultsActionButtons = ({
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false)
|
||||
const [isExportLoading, setIsExportLoading] = useState(false)
|
||||
const trpcContext = trpc.useContext()
|
||||
const deleteResultsMutation = trpc.results.deleteResults.useMutation({
|
||||
onMutate: () => {
|
||||
setIsDeleteLoading(true)
|
||||
},
|
||||
onError: (error) => showToast({ description: error.message }),
|
||||
onSuccess: async () => {
|
||||
await trpcContext.results.getResults.invalidate()
|
||||
},
|
||||
onSettled: () => {
|
||||
onDeleteResults(selectedResultsId.length)
|
||||
onClearSelection()
|
||||
setIsDeleteLoading(false)
|
||||
},
|
||||
})
|
||||
|
||||
const workspaceId = typebot?.workspaceId
|
||||
const typebotId = typebot?.id
|
||||
|
||||
const getAllTableData = async () => {
|
||||
if (!workspaceId || !typebotId) return []
|
||||
const results = await getAllResultsQuery(workspaceId, typebotId)
|
||||
return convertResultsToTableData(results, resultHeader)
|
||||
const allResults = []
|
||||
let cursor: string | undefined | null
|
||||
do {
|
||||
try {
|
||||
const { results, nextCursor } =
|
||||
await trpcContext.results.getResults.fetch({
|
||||
typebotId,
|
||||
limit: '200',
|
||||
})
|
||||
allResults.push(...results)
|
||||
cursor = nextCursor
|
||||
} catch (error) {
|
||||
showToast({ description: (error as TRPCError).message })
|
||||
}
|
||||
} while (cursor)
|
||||
|
||||
return convertResultsToTableData(allResults, resultHeader)
|
||||
}
|
||||
|
||||
const totalSelected =
|
||||
@ -59,27 +87,13 @@ export const ResultsActionButtons = ({
|
||||
|
||||
const deleteResults = async () => {
|
||||
if (!workspaceId || !typebotId) return
|
||||
setIsDeleteLoading(true)
|
||||
const { error } = await deleteResultsQuery(
|
||||
workspaceId,
|
||||
deleteResultsMutation.mutate({
|
||||
typebotId,
|
||||
totalSelected === totalResults ? [] : selectedResultsId
|
||||
)
|
||||
if (error) showToast({ description: error.message, title: error.name })
|
||||
else {
|
||||
mutate(
|
||||
ids:
|
||||
totalSelected === totalResults
|
||||
? []
|
||||
: data?.map((d) => ({
|
||||
results: d.results.filter(
|
||||
(r) => !selectedResultsId.includes(r.id)
|
||||
),
|
||||
}))
|
||||
)
|
||||
}
|
||||
onDeleteResults(selectedResultsId.length)
|
||||
onClearSelection()
|
||||
setIsDeleteLoading(false)
|
||||
? undefined
|
||||
: selectedResultsId.join(','),
|
||||
})
|
||||
}
|
||||
|
||||
const exportResultsToCSV = async () => {
|
||||
|
@ -247,9 +247,10 @@ const IndeterminateCheckbox = React.forwardRef(
|
||||
const resolvedRef: any = ref || defaultRef
|
||||
|
||||
return (
|
||||
<Flex justify="center" data-testid="checkbox" {...rest}>
|
||||
<Flex justify="center" data-testid="checkbox">
|
||||
<Checkbox
|
||||
ref={resolvedRef}
|
||||
{...rest}
|
||||
isIndeterminate={indeterminate}
|
||||
isChecked={checked}
|
||||
/>
|
||||
|
@ -10,7 +10,12 @@ type Props = {
|
||||
onExpandButtonClick: () => void
|
||||
}
|
||||
|
||||
export const Row = ({ row, bottomElement, onExpandButtonClick }: Props) => {
|
||||
export const Row = ({
|
||||
row,
|
||||
bottomElement,
|
||||
onExpandButtonClick,
|
||||
isSelected,
|
||||
}: Props) => {
|
||||
const [isExpandButtonVisible, setIsExpandButtonVisible] = useState(false)
|
||||
|
||||
const showExpandButton = () => setIsExpandButtonVisible(true)
|
||||
@ -35,6 +40,7 @@ export const Row = ({ row, bottomElement, onExpandButtonClick }: Props) => {
|
||||
isExpandButtonVisible={isExpandButtonVisible}
|
||||
cellIndex={cellIndex}
|
||||
onExpandButtonClick={onExpandButtonClick}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
))}
|
||||
</tr>
|
||||
|
@ -9,8 +9,8 @@ import { SubmissionsTable } from './ResultsTable'
|
||||
export const ResultsTableContainer = () => {
|
||||
const {
|
||||
flatResults: results,
|
||||
fetchMore,
|
||||
hasMore,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
resultHeader,
|
||||
tableData,
|
||||
} = useResults()
|
||||
@ -60,8 +60,8 @@ export const ResultsTableContainer = () => {
|
||||
preferences={typebot.resultsTablePreferences}
|
||||
resultHeader={resultHeader}
|
||||
data={tableData}
|
||||
onScrollToBottom={fetchMore}
|
||||
hasMore={hasMore}
|
||||
onScrollToBottom={fetchNextPage}
|
||||
hasMore={hasNextPage}
|
||||
onLogOpenIndex={handleLogOpenIndex}
|
||||
onResultExpandIndex={handleResultExpandIndex}
|
||||
/>
|
||||
|
1
apps/builder/src/features/results/components/index.ts
Normal file
1
apps/builder/src/features/results/components/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './ResultsPage'
|
@ -1,17 +1,19 @@
|
||||
import { fetcher } from '@/utils/helpers'
|
||||
import { Log } from 'db'
|
||||
import useSWR from 'swr'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { isDefined } from '@udecode/plate-common'
|
||||
|
||||
export const useLogs = (
|
||||
typebotId: string,
|
||||
resultId?: string,
|
||||
onError?: (e: Error) => void
|
||||
resultId: string | null,
|
||||
onError?: (error: string) => void
|
||||
) => {
|
||||
const { data, error } = useSWR<{ logs: Log[] }>(
|
||||
resultId ? `/api/typebots/${typebotId}/results/${resultId}/logs` : null,
|
||||
fetcher
|
||||
const { data, error } = trpc.results.getResultLogs.useQuery(
|
||||
{
|
||||
resultId: resultId ?? '',
|
||||
typebotId,
|
||||
},
|
||||
{ enabled: isDefined(resultId) }
|
||||
)
|
||||
if (error && onError) onError(error)
|
||||
if (error && onError) onError(error.message)
|
||||
return {
|
||||
logs: data?.logs,
|
||||
isLoading: !error && !data,
|
||||
|
@ -1,64 +1,29 @@
|
||||
import { fetcher } from '@/utils/helpers'
|
||||
import { ResultWithAnswers } from 'models'
|
||||
import { env } from 'utils'
|
||||
import useSWRInfinite from 'swr/infinite'
|
||||
|
||||
const paginationLimit = 50
|
||||
import { trpc } from '@/lib/trpc'
|
||||
|
||||
export const useResultsQuery = ({
|
||||
workspaceId,
|
||||
typebotId,
|
||||
onError,
|
||||
}: {
|
||||
workspaceId: string
|
||||
typebotId: string
|
||||
onError?: (error: Error) => void
|
||||
onError?: (error: string) => void
|
||||
}) => {
|
||||
const { data, error, mutate, setSize, size, isValidating } = useSWRInfinite<
|
||||
{ results: ResultWithAnswers[] },
|
||||
Error
|
||||
>(
|
||||
(
|
||||
pageIndex: number,
|
||||
previousPageData: {
|
||||
results: ResultWithAnswers[]
|
||||
}
|
||||
) => getKey(workspaceId, typebotId, pageIndex, previousPageData),
|
||||
fetcher,
|
||||
const { data, error, fetchNextPage, hasNextPage, refetch } =
|
||||
trpc.results.getResults.useInfiniteQuery(
|
||||
{
|
||||
revalidateAll: true,
|
||||
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||
typebotId,
|
||||
limit: '50',
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
}
|
||||
)
|
||||
|
||||
if (error && onError) onError(error)
|
||||
if (error && onError) onError(error.message)
|
||||
return {
|
||||
data,
|
||||
data: data?.pages,
|
||||
isLoading: !error && !data,
|
||||
mutate,
|
||||
setSize,
|
||||
size,
|
||||
hasMore:
|
||||
isValidating ||
|
||||
(data &&
|
||||
data.length > 0 &&
|
||||
data[data.length - 1].results.length > 0 &&
|
||||
data.length === paginationLimit),
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
refetch,
|
||||
}
|
||||
}
|
||||
|
||||
const getKey = (
|
||||
workspaceId: string,
|
||||
typebotId: string,
|
||||
pageIndex: number,
|
||||
previousPageData: {
|
||||
results: ResultWithAnswers[]
|
||||
}
|
||||
) => {
|
||||
if (previousPageData && previousPageData.results.length === 0) return null
|
||||
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}&workspaceId=${workspaceId}`
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export const useStats = ({
|
||||
onError: (error: Error) => void
|
||||
}) => {
|
||||
const { data, error, mutate } = useSWR<{ stats: Stats }, Error>(
|
||||
typebotId ? `/api/typebots/${typebotId}/results/stats` : null,
|
||||
typebotId ? `/api/typebots/${typebotId}/analytics/stats` : null,
|
||||
fetcher
|
||||
)
|
||||
if (error) onError(error)
|
||||
|
@ -1 +1 @@
|
||||
export { ResultsPage } from './components/ResultsPage'
|
||||
export * from './components'
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
export const deleteResultsQuery = async (
|
||||
workspaceId: string,
|
||||
typebotId: string,
|
||||
ids: string[]
|
||||
) => {
|
||||
const params = stringify({
|
||||
workspaceId,
|
||||
})
|
||||
return sendRequest({
|
||||
url: `/api/typebots/${typebotId}/results?${params}`,
|
||||
method: 'DELETE',
|
||||
body: {
|
||||
ids,
|
||||
},
|
||||
})
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { ResultWithAnswers } from 'models'
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
export const getAllResultsQuery = async (
|
||||
workspaceId: string,
|
||||
typebotId: string
|
||||
) => {
|
||||
const results = []
|
||||
let hasMore = true
|
||||
let lastResultId: string | undefined = undefined
|
||||
do {
|
||||
const query = stringify({ limit: 200, lastResultId, workspaceId })
|
||||
const { data, error } = await sendRequest<{ results: ResultWithAnswers[] }>(
|
||||
{
|
||||
url: `/api/typebots/${typebotId}/results?${query}`,
|
||||
method: 'GET',
|
||||
}
|
||||
)
|
||||
if (error) {
|
||||
console.error(error)
|
||||
break
|
||||
}
|
||||
results.push(...(data?.results ?? []))
|
||||
lastResultId = results[results.length - 1]?.id as string | undefined
|
||||
if (data?.results.length === 0) hasMore = false
|
||||
} while (hasMore)
|
||||
return results
|
||||
}
|
@ -12,17 +12,13 @@ import { HeaderCell, TableData } from './types'
|
||||
import { CodeIcon, CalendarIcon, FileIcon } from '@/components/icons'
|
||||
import { TextLink } from '@/components/TextLink'
|
||||
|
||||
export const parseDateToReadable = (dateStr: string): string => {
|
||||
const date = new Date(dateStr)
|
||||
return (
|
||||
export const parseDateToReadable = (date: Date): string =>
|
||||
date.toDateString().split(' ').slice(1, 3).join(' ') +
|
||||
', ' +
|
||||
date.toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export const parseSubmissionsColumns = (
|
||||
resultHeader: ResultHeaderCell[]
|
||||
|
21
apps/builder/src/lib/trpc.ts
Normal file
21
apps/builder/src/lib/trpc.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { httpBatchLink } from '@trpc/client'
|
||||
import { createTRPCNext } from '@trpc/next'
|
||||
import type { AppRouter } from '../utils/server/routers/_app'
|
||||
import superjson from 'superjson'
|
||||
|
||||
const getBaseUrl = () =>
|
||||
typeof window !== 'undefined' ? '' : process.env.NEXTAUTH_URL
|
||||
|
||||
export const trpc = createTRPCNext<AppRouter>({
|
||||
config() {
|
||||
return {
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
],
|
||||
transformer: superjson,
|
||||
}
|
||||
},
|
||||
ssr: true,
|
||||
})
|
@ -17,6 +17,7 @@ import { WorkspaceProvider } from '@/features/workspace'
|
||||
import { toTitleCase } from 'utils'
|
||||
import { Session } from 'next-auth'
|
||||
import { Plan } from 'db'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
|
||||
const { ToastContainer, toast } = createStandaloneToast(customTheme)
|
||||
|
||||
@ -72,4 +73,4 @@ const App = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
export default trpc.withTRPC(App)
|
||||
|
8
apps/builder/src/pages/api/[...trpc].ts
Normal file
8
apps/builder/src/pages/api/[...trpc].ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { createContext } from '@/utils/server/context'
|
||||
import { appRouter } from '@/utils/server/routers/_app'
|
||||
import { createOpenApiNextHandler } from 'trpc-openapi'
|
||||
|
||||
export default createOpenApiNextHandler({
|
||||
router: appRouter,
|
||||
createContext,
|
||||
})
|
8
apps/builder/src/pages/api/trpc/[trpc].ts
Normal file
8
apps/builder/src/pages/api/trpc/[trpc].ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { createContext } from '@/utils/server/context'
|
||||
import { appRouter } from '@/utils/server/routers/_app'
|
||||
import { createNextApiHandler } from '@trpc/server/adapters/next'
|
||||
|
||||
export default createNextApiHandler({
|
||||
router: appRouter,
|
||||
createContext,
|
||||
})
|
@ -39,11 +39,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
await archiveResults(res)({
|
||||
const { success } = await archiveResults({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter: { typebotId },
|
||||
})
|
||||
if (!success) return res.status(500).send({ success: false })
|
||||
await prisma.publicTypebot.deleteMany({
|
||||
where: { typebot: canWriteTypebot(typebotId, user) },
|
||||
})
|
||||
|
@ -1,67 +0,0 @@
|
||||
import { withSentry } from '@sentry/nextjs'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { canReadTypebot, canWriteTypebot } from '@/utils/api/dbRules'
|
||||
import {
|
||||
badRequest,
|
||||
forbidden,
|
||||
methodNotAllowed,
|
||||
notAuthenticated,
|
||||
} from 'utils/api'
|
||||
import { getAuthenticatedUser } from '@/features/auth'
|
||||
import { archiveResults } from '@/features/results/api'
|
||||
|
||||
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 (!workspaceId) return badRequest(res, 'workspaceId is required')
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where:
|
||||
user.email === process.env.ADMIN_EMAIL
|
||||
? undefined
|
||||
: { id: workspaceId, members: { some: { userId: user.id } } },
|
||||
select: { plan: true },
|
||||
})
|
||||
if (!workspace) return forbidden(res)
|
||||
if (req.method === 'GET') {
|
||||
const typebotId = req.query.typebotId as string
|
||||
const lastResultId = req.query.lastResultId?.toString()
|
||||
const take = Number(req.query.limit?.toString())
|
||||
const results = await prisma.result.findMany({
|
||||
take: isNaN(take) ? undefined : take,
|
||||
skip: lastResultId ? 1 : 0,
|
||||
cursor: lastResultId
|
||||
? {
|
||||
id: lastResultId,
|
||||
}
|
||||
: undefined,
|
||||
where: {
|
||||
typebot: canReadTypebot(typebotId, user),
|
||||
answers: { some: {} },
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
include: { answers: true },
|
||||
})
|
||||
return res.status(200).send({ results })
|
||||
}
|
||||
if (req.method === 'DELETE') {
|
||||
const typebotId = req.query.typebotId as string
|
||||
const data = req.body as { ids: string[] }
|
||||
const ids = data.ids
|
||||
await archiveResults(res)({
|
||||
typebotId,
|
||||
user,
|
||||
resultsFilter: {
|
||||
id: ids.length > 0 ? { in: ids } : undefined,
|
||||
typebot: canWriteTypebot(typebotId, user),
|
||||
},
|
||||
})
|
||||
return res.status(200).send({ message: 'done' })
|
||||
}
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
export default withSentry(handler)
|
@ -1,24 +0,0 @@
|
||||
import { withSentry } from '@sentry/nextjs'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { canReadTypebot } from '@/utils/api/dbRules'
|
||||
import { getAuthenticatedUser } from '@/features/auth'
|
||||
import { methodNotAllowed, notAuthenticated } from 'utils/api'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req)
|
||||
if (!user) return notAuthenticated(res)
|
||||
if (req.method === 'GET') {
|
||||
const typebotId = req.query.typebotId as string
|
||||
const resultId = req.query.resultId as string
|
||||
const logs = await prisma.log.findMany({
|
||||
where: {
|
||||
result: { id: resultId, typebot: canReadTypebot(typebotId, user) },
|
||||
},
|
||||
})
|
||||
return res.send({ logs })
|
||||
}
|
||||
methodNotAllowed(res)
|
||||
}
|
||||
|
||||
export default withSentry(handler)
|
13
apps/builder/src/utils/server/context.ts
Normal file
13
apps/builder/src/utils/server/context.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { getAuthenticatedUser } from '@/features/auth'
|
||||
import { inferAsyncReturnType } from '@trpc/server'
|
||||
import * as trpcNext from '@trpc/server/adapters/next'
|
||||
|
||||
export async function createContext(opts: trpcNext.CreateNextContextOptions) {
|
||||
const user = await getAuthenticatedUser(opts.req)
|
||||
|
||||
return {
|
||||
user,
|
||||
}
|
||||
}
|
||||
|
||||
export type Context = inferAsyncReturnType<typeof createContext>
|
8
apps/builder/src/utils/server/routers/_app.ts
Normal file
8
apps/builder/src/utils/server/routers/_app.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { resultsRouter } from '@/features/results/api'
|
||||
import { router } from '../trpc'
|
||||
|
||||
export const appRouter = router({
|
||||
results: resultsRouter,
|
||||
})
|
||||
|
||||
export type AppRouter = typeof appRouter
|
29
apps/builder/src/utils/server/trpc.ts
Normal file
29
apps/builder/src/utils/server/trpc.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { TRPCError, initTRPC } from '@trpc/server'
|
||||
import { Context } from './context'
|
||||
import { OpenApiMeta } from 'trpc-openapi'
|
||||
import superjson from 'superjson'
|
||||
|
||||
const t = initTRPC.context<Context>().meta<OpenApiMeta>().create({
|
||||
transformer: superjson,
|
||||
})
|
||||
|
||||
const isAuthed = t.middleware(({ next, ctx }) => {
|
||||
if (!ctx.user) {
|
||||
throw new TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
})
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
user: ctx.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
export const middleware = t.middleware
|
||||
|
||||
export const router = t.router
|
||||
|
||||
export const publicProcedure = t.procedure
|
||||
|
||||
export const authenticatedProcedure = t.procedure.use(isAuthed)
|
@ -1,26 +1,11 @@
|
||||
{
|
||||
"extends": "tsconfig/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"composite": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.json"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { resultValues, variables } = (
|
||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||
) as {
|
||||
resultValues: ResultValues | undefined
|
||||
resultValues:
|
||||
| (Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
})
|
||||
| undefined
|
||||
variables: Variable[]
|
||||
}
|
||||
const typebot = (await prisma.typebot.findUnique({
|
||||
@ -83,7 +87,9 @@ export const executeWebhook =
|
||||
webhook: Webhook,
|
||||
variables: Variable[],
|
||||
groupId: string,
|
||||
resultValues?: ResultValues,
|
||||
resultValues?: Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
},
|
||||
resultId?: string
|
||||
): Promise<WebhookResponse> => {
|
||||
if (!webhook.url || !webhook.method)
|
||||
@ -193,7 +199,9 @@ const getBodyContent =
|
||||
groupId,
|
||||
}: {
|
||||
body?: string | null
|
||||
resultValues?: ResultValues
|
||||
resultValues?: Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
}
|
||||
groupId: string
|
||||
}): Promise<string | undefined> => {
|
||||
if (!body) return
|
||||
|
@ -26,7 +26,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { resultValues, variables } = (
|
||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||
) as {
|
||||
resultValues: ResultValues | undefined
|
||||
resultValues:
|
||||
| (Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
})
|
||||
| undefined
|
||||
variables: Variable[]
|
||||
}
|
||||
const typebot = (await prisma.typebot.findUnique({
|
||||
|
@ -54,7 +54,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
} = (
|
||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||
) as SendEmailOptions & {
|
||||
resultValues: ResultValues
|
||||
resultValues: Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
}
|
||||
fileUrls?: string
|
||||
}
|
||||
const { name: replyToName } = parseEmailRecipient(replyTo)
|
||||
@ -162,10 +164,14 @@ const getEmailBody = async ({
|
||||
isBodyCode,
|
||||
typebotId,
|
||||
resultValues,
|
||||
}: { typebotId: string; resultValues: ResultValues } & Pick<
|
||||
SendEmailOptions,
|
||||
'isCustomBody' | 'isBodyCode' | 'body'
|
||||
>): Promise<{ html?: string; text?: string } | undefined> => {
|
||||
}: {
|
||||
typebotId: string
|
||||
resultValues: Omit<ResultValues, 'createdAt'> & {
|
||||
createdAt: string
|
||||
}
|
||||
} & Pick<SendEmailOptions, 'isCustomBody' | 'isBodyCode' | 'body'>): Promise<
|
||||
{ html?: string; text?: string } | undefined
|
||||
> => {
|
||||
if (isCustomBody || (isNotDefined(isCustomBody) && !isEmpty(body)))
|
||||
return {
|
||||
html: isBodyCode ? body : undefined,
|
||||
|
@ -1,8 +1,6 @@
|
||||
{
|
||||
"name": "configs",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.ts",
|
||||
"types": "./index.ts",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
|
@ -21,6 +21,7 @@
|
||||
"devDependencies": {
|
||||
"prisma": "4.6.1",
|
||||
"typescript": "4.8.4",
|
||||
"dotenv-cli": "6.0.0"
|
||||
"dotenv-cli": "6.0.0",
|
||||
"tsconfig": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"composite": true,
|
||||
"outDir": "build",
|
||||
"isolatedModules": false
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules", "build"]
|
||||
"extends": "tsconfig/base.json",
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -11,7 +11,8 @@
|
||||
"devDependencies": {
|
||||
"typescript": "4.8.4",
|
||||
"next": "13.0.3",
|
||||
"db": "workspace:*"
|
||||
"db": "workspace:*",
|
||||
"tsconfig": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "12.0.0",
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { Answer as AnswerFromPrisma } from 'db'
|
||||
import { z } from 'zod'
|
||||
|
||||
export type Answer = Omit<
|
||||
AnswerFromPrisma,
|
||||
'resultId' | 'createdAt' | 'storageUsed'
|
||||
> & { storageUsed?: number }
|
||||
export const answerSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
resultId: z.string(),
|
||||
blockId: z.string(),
|
||||
groupId: z.string(),
|
||||
variableId: z.string().nullable(),
|
||||
content: z.string(),
|
||||
storageUsed: z.number().nullable(),
|
||||
})
|
||||
|
||||
export type Stats = {
|
||||
totalViews: number
|
||||
totalStarts: number
|
||||
totalCompleted: number
|
||||
}
|
||||
|
||||
export type Answer = z.infer<typeof answerSchema>
|
||||
|
@ -1,14 +1,39 @@
|
||||
import { Result as ResultFromPrisma } from 'db'
|
||||
import { Answer } from './answer'
|
||||
import { z } from 'zod'
|
||||
import { answerSchema } from './answer'
|
||||
import { InputBlockType } from './blocks'
|
||||
import { VariableWithValue } from './typebot/variable'
|
||||
import { variableWithValueSchema } from './typebot/variable'
|
||||
|
||||
export type Result = Omit<ResultFromPrisma, 'createdAt' | 'variables'> & {
|
||||
createdAt: string
|
||||
variables: VariableWithValue[]
|
||||
}
|
||||
export const resultSchema = z.object({
|
||||
id: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
typebotId: z.string(),
|
||||
variables: z.array(variableWithValueSchema),
|
||||
isCompleted: z.boolean(),
|
||||
hasStarted: z.boolean().nullable(),
|
||||
isArchived: z.boolean().nullable(),
|
||||
})
|
||||
|
||||
export type ResultWithAnswers = Result & { answers: Answer[] }
|
||||
export const resultWithAnswersSchema = resultSchema.and(
|
||||
z.object({
|
||||
answers: z.array(answerSchema),
|
||||
})
|
||||
)
|
||||
|
||||
export const logSchema = z.object({
|
||||
id: z.string(),
|
||||
createdAt: z.date(),
|
||||
resultId: z.string(),
|
||||
status: z.string(),
|
||||
description: z.string(),
|
||||
details: z.string().nullable(),
|
||||
})
|
||||
|
||||
export type Result = z.infer<typeof resultSchema>
|
||||
|
||||
export type ResultWithAnswers = z.infer<typeof resultWithAnswersSchema>
|
||||
|
||||
export type Log = z.infer<typeof logSchema>
|
||||
|
||||
export type ResultValues = Pick<
|
||||
ResultWithAnswers,
|
||||
|
@ -3,21 +3,29 @@ import { z } from 'zod'
|
||||
export const variableSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
value: z.string().optional().nullable(),
|
||||
value: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Variable when retrieved from the database
|
||||
*/
|
||||
export type VariableWithValue = Omit<Variable, 'value'> & {
|
||||
value: string
|
||||
}
|
||||
export const variableWithValueSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
value: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Variable when computed or retrieved from a block
|
||||
*/
|
||||
export type VariableWithUnknowValue = Omit<VariableWithValue, 'value'> & {
|
||||
value: unknown
|
||||
}
|
||||
const VariableWithUnknowValueSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
value: z.unknown(),
|
||||
})
|
||||
|
||||
export type Variable = z.infer<typeof variableSchema>
|
||||
export type VariableWithValue = z.infer<typeof variableWithValueSchema>
|
||||
export type VariableWithUnknowValue = z.infer<
|
||||
typeof VariableWithUnknowValueSchema
|
||||
>
|
||||
|
@ -1,17 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"declaration": true,
|
||||
"declarationDir": "types",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
"extends": "tsconfig/base.json",
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
21
packages/tsconfig/base.json
Normal file
21
packages/tsconfig/base.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSources": false,
|
||||
"isolatedModules": true,
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"preserveWatchOutput": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
19
packages/tsconfig/nextjs.json
Normal file
19
packages/tsconfig/nextjs.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Next.js",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"incremental": true,
|
||||
"jsx": "preserve",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"include": ["src", "next-env.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
3
packages/tsconfig/package.json
Normal file
3
packages/tsconfig/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "tsconfig"
|
||||
}
|
@ -14,7 +14,8 @@
|
||||
"models": "workspace:*",
|
||||
"next": "13.0.3",
|
||||
"nodemailer": "6.8.0",
|
||||
"typescript": "4.8.4"
|
||||
"typescript": "4.8.4",
|
||||
"tsconfig": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"aws-sdk": "2.1152.0",
|
||||
|
@ -177,7 +177,9 @@ export const parseAnswers =
|
||||
createdAt,
|
||||
answers,
|
||||
variables: resultVariables,
|
||||
}: Pick<ResultWithAnswers, 'createdAt' | 'answers' | 'variables'>): {
|
||||
}: Pick<ResultWithAnswers, 'answers' | 'variables'> & {
|
||||
createdAt: string
|
||||
}): {
|
||||
[key: string]: string
|
||||
} => {
|
||||
const header = parseResultHeader(typebot, linkedTypebots)
|
||||
|
@ -1,17 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"declaration": true,
|
||||
"declarationDir": "types",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
"extends": "tsconfig/base.json",
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
183
pnpm-lock.yaml
generated
183
pnpm-lock.yaml
generated
@ -35,7 +35,12 @@ importers:
|
||||
'@playwright/test': 1.27.1
|
||||
'@sentry/nextjs': 7.19.0
|
||||
'@stripe/stripe-js': 1.44.1
|
||||
'@tanstack/react-query': ^4.16.1
|
||||
'@tanstack/react-table': 8.5.28
|
||||
'@trpc/client': 10.0.0-rc.8
|
||||
'@trpc/next': 10.0.0-rc.8
|
||||
'@trpc/react-query': 10.0.0-rc.8
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
'@types/canvas-confetti': 1.6.0
|
||||
'@types/google-spreadsheet': 3.3.0
|
||||
'@types/jsonwebtoken': 8.5.9
|
||||
@ -104,13 +109,17 @@ importers:
|
||||
slate-react: 0.83.2
|
||||
stripe: 10.17.0
|
||||
styled-components: 5.3.6
|
||||
superjson: ^1.11.0
|
||||
svg-round-corners: 0.4.1
|
||||
swr: 1.3.0
|
||||
tinycolor2: 1.4.2
|
||||
trpc-openapi: 1.0.0-alpha.4
|
||||
tsconfig: workspace:*
|
||||
typebot-js: workspace:*
|
||||
typescript: 4.8.4
|
||||
use-debounce: 8.0.4
|
||||
utils: workspace:*
|
||||
zod: 3.19.1
|
||||
dependencies:
|
||||
'@chakra-ui/css-reset': 2.0.10_hp5f5nkljdiwilp4rgxyefcplu
|
||||
'@chakra-ui/react': 2.4.1_ydu7fid2w3vesyc65jyv4dbawa
|
||||
@ -132,7 +141,12 @@ importers:
|
||||
'@googleapis/drive': 4.0.0
|
||||
'@sentry/nextjs': 7.19.0_next@13.0.3+react@18.2.0
|
||||
'@stripe/stripe-js': 1.44.1
|
||||
'@tanstack/react-query': 4.16.1_biqbaboplfbrettd7655fr4n2y
|
||||
'@tanstack/react-table': 8.5.28_biqbaboplfbrettd7655fr4n2y
|
||||
'@trpc/client': 10.0.0-rc.8_@trpc+server@10.0.0-rc.8
|
||||
'@trpc/next': 10.0.0-rc.8_js37qhiwaz7seiawrwo5qa4qoq
|
||||
'@trpc/react-query': 10.0.0-rc.8_bd5nqhlee2rdtlo5aa5wyumt5u
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
'@udecode/plate-basic-marks': 18.9.0_gmohhyxeb2dq4peb7jfnhckv34
|
||||
'@udecode/plate-common': 7.0.2_l45ryt4vvbsc5mzu26yyqixq5e
|
||||
'@udecode/plate-core': 18.9.0_gmohhyxeb2dq4peb7jfnhckv34
|
||||
@ -181,6 +195,7 @@ importers:
|
||||
svg-round-corners: 0.4.1
|
||||
swr: 1.3.0_react@18.2.0
|
||||
tinycolor2: 1.4.2
|
||||
trpc-openapi: 1.0.0-alpha.4_m7lve6cjvd26xbjqz4zsiln46q
|
||||
typebot-js: link:../../packages/typebot-js
|
||||
use-debounce: 8.0.4_react@18.2.0
|
||||
devDependencies:
|
||||
@ -210,8 +225,11 @@ importers:
|
||||
eslint-plugin-react: 7.31.10_eslint@8.27.0
|
||||
models: link:../../packages/models
|
||||
next-transpile-modules: 10.0.0
|
||||
superjson: 1.11.0
|
||||
tsconfig: link:../../packages/tsconfig
|
||||
typescript: 4.8.4
|
||||
utils: link:../../packages/utils
|
||||
zod: 3.19.1
|
||||
|
||||
apps/docs:
|
||||
specifiers:
|
||||
@ -496,12 +514,14 @@ importers:
|
||||
'@prisma/client': 4.6.1
|
||||
dotenv-cli: 6.0.0
|
||||
prisma: 4.6.1
|
||||
tsconfig: workspace:*
|
||||
typescript: 4.8.4
|
||||
dependencies:
|
||||
'@prisma/client': 4.6.1_prisma@4.6.1
|
||||
devDependencies:
|
||||
dotenv-cli: 6.0.0
|
||||
prisma: 4.6.1
|
||||
tsconfig: link:../tsconfig
|
||||
typescript: 4.8.4
|
||||
|
||||
packages/emails:
|
||||
@ -532,6 +552,7 @@ importers:
|
||||
specifiers:
|
||||
db: workspace:*
|
||||
next: 13.0.3
|
||||
tsconfig: workspace:*
|
||||
typescript: 4.8.4
|
||||
zod: 3.19.1
|
||||
dependencies:
|
||||
@ -539,6 +560,7 @@ importers:
|
||||
devDependencies:
|
||||
db: link:../db
|
||||
next: 13.0.3_biqbaboplfbrettd7655fr4n2y
|
||||
tsconfig: link:../tsconfig
|
||||
typescript: 4.8.4
|
||||
|
||||
packages/scripts:
|
||||
@ -565,6 +587,9 @@ importers:
|
||||
typescript: 4.8.4
|
||||
utils: link:../utils
|
||||
|
||||
packages/tsconfig:
|
||||
specifiers: {}
|
||||
|
||||
packages/typebot-js:
|
||||
specifiers:
|
||||
'@types/jest': 29.2.3
|
||||
@ -605,6 +630,7 @@ importers:
|
||||
models: workspace:*
|
||||
next: 13.0.3
|
||||
nodemailer: 6.8.0
|
||||
tsconfig: workspace:*
|
||||
typescript: 4.8.4
|
||||
devDependencies:
|
||||
'@playwright/test': 1.27.1
|
||||
@ -615,6 +641,7 @@ importers:
|
||||
models: link:../models
|
||||
next: 13.0.3_biqbaboplfbrettd7655fr4n2y
|
||||
nodemailer: 6.8.0
|
||||
tsconfig: link:../tsconfig
|
||||
typescript: 4.8.4
|
||||
|
||||
packages/wordpress:
|
||||
@ -5597,6 +5624,28 @@ packages:
|
||||
dependencies:
|
||||
defer-to-connect: 2.0.1
|
||||
|
||||
/@tanstack/query-core/4.15.1:
|
||||
resolution: {integrity: sha512-+UfqJsNbPIVo0a9ANW0ZxtjiMfGLaaoIaL9vZeVycvmBuWywJGtSi7fgPVMCPdZQFOzMsaXaOsDtSKQD5xLRVQ==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-query/4.16.1_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-PDE9u49wSDykPazlCoLFevUpceLjQ0Mm8i6038HgtTEKb/aoVnUZdlUP7C392ds3Cd75+EGlHU7qpEX06R7d9Q==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@tanstack/query-core': 4.15.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
use-sync-external-store: 1.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-table/8.5.28_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-Rj6oSi2Ef3pYnsv1aCQrN+fhkLJO/gIoVAvfBui/harFc+1+6OPfN0C6O4jOKt0bq0UyQI+yghYkfpAkhqJ31A==}
|
||||
engines: {node: '>=12'}
|
||||
@ -5630,6 +5679,55 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
dev: true
|
||||
|
||||
/@trpc/client/10.0.0-rc.8_@trpc+server@10.0.0-rc.8:
|
||||
resolution: {integrity: sha512-NuQl7g1tkfFYn6P6dZJJkVsxnxutYpaxmpG/w5ZKW3QVu6cY1joUOrWakunKrghh3HS+svgLaPz0Xl0PlmdSlw==}
|
||||
peerDependencies:
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
dependencies:
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
dev: false
|
||||
|
||||
/@trpc/next/10.0.0-rc.8_js37qhiwaz7seiawrwo5qa4qoq:
|
||||
resolution: {integrity: sha512-l2TZF22virmC3RROD7qu0dnc3sdYfVvkOhAu1qP5+8fRgs4cgHKBFdalQ+SKBnU0RNuVLYVw0CkyvU+gHo5u2w==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^4.3.8
|
||||
'@trpc/client': 10.0.0-rc.8
|
||||
'@trpc/react-query': ^10.0.0-proxy-beta.21
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
next: '*'
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@tanstack/react-query': 4.16.1_biqbaboplfbrettd7655fr4n2y
|
||||
'@trpc/client': 10.0.0-rc.8_@trpc+server@10.0.0-rc.8
|
||||
'@trpc/react-query': 10.0.0-rc.8_bd5nqhlee2rdtlo5aa5wyumt5u
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
next: 13.0.3_mqvh5p7ejg4taogoj6tpk3gd5a
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-ssr-prepass: 1.5.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@trpc/react-query/10.0.0-rc.8_bd5nqhlee2rdtlo5aa5wyumt5u:
|
||||
resolution: {integrity: sha512-LKL29llvGtwYmfKGuCYSgNNChOL8/6PInY90i+ZgI8TLO6ZCJaVrPBSvqtxKj3MSezbK3kOxWIviKVFwV35L0A==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^4.3.8
|
||||
'@trpc/client': 10.0.0-rc.8
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@tanstack/react-query': 4.16.1_biqbaboplfbrettd7655fr4n2y
|
||||
'@trpc/client': 10.0.0-rc.8_@trpc+server@10.0.0-rc.8
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@trpc/server/10.0.0-rc.8:
|
||||
resolution: {integrity: sha512-+NsQqaJGHTiiBcOesH1bIictnnSO+SXaCOy5pM/324QBnJRt9VOO8oYDLcHSwAbJpAbOA+vl6YHs8OoykW6D2g==}
|
||||
dev: false
|
||||
|
||||
/@trysound/sax/0.2.0:
|
||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@ -8105,6 +8203,15 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/co-body/6.1.0:
|
||||
resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==}
|
||||
dependencies:
|
||||
inflation: 2.0.0
|
||||
qs: 6.11.0
|
||||
raw-body: 2.5.1
|
||||
type-is: 1.6.18
|
||||
dev: false
|
||||
|
||||
/co/4.6.0:
|
||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||
@ -8340,6 +8447,13 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/copy-anything/3.0.2:
|
||||
resolution: {integrity: sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA==}
|
||||
engines: {node: '>=12.13'}
|
||||
dependencies:
|
||||
is-what: 4.1.7
|
||||
dev: true
|
||||
|
||||
/copy-text-to-clipboard/3.0.1:
|
||||
resolution: {integrity: sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==}
|
||||
engines: {node: '>=12'}
|
||||
@ -11457,6 +11571,11 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/inflation/2.0.0:
|
||||
resolution: {integrity: sha512-m3xv4hJYR2oXw4o4Y5l6P5P16WYmazYof+el6Al3f+YlggGj6qT9kImBAnzDelRALnP5d3h4jGBPKzYCizjZZw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dev: false
|
||||
|
||||
/inflight/1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
dependencies:
|
||||
@ -11844,6 +11963,11 @@ packages:
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
|
||||
/is-what/4.1.7:
|
||||
resolution: {integrity: sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==}
|
||||
engines: {node: '>=12.13'}
|
||||
dev: true
|
||||
|
||||
/is-whitespace-character/1.0.4:
|
||||
resolution: {integrity: sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==}
|
||||
dev: false
|
||||
@ -13836,6 +13960,22 @@ packages:
|
||||
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||
dev: true
|
||||
|
||||
/node-mocks-http/1.12.1:
|
||||
resolution: {integrity: sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==}
|
||||
engines: {node: '>=0.6'}
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
content-disposition: 0.5.4
|
||||
depd: 1.1.2
|
||||
fresh: 0.5.2
|
||||
merge-descriptors: 1.0.1
|
||||
methods: 1.1.2
|
||||
mime: 1.6.0
|
||||
parseurl: 1.3.3
|
||||
range-parser: 1.2.1
|
||||
type-is: 1.6.18
|
||||
dev: false
|
||||
|
||||
/node-releases/2.0.6:
|
||||
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
||||
|
||||
@ -14030,6 +14170,10 @@ packages:
|
||||
is-wsl: 2.2.0
|
||||
dev: false
|
||||
|
||||
/openapi-types/12.0.2:
|
||||
resolution: {integrity: sha512-GuTo7FyZjOIWVhIhQSWJVaws6A82sWIGyQogxxYBYKZ0NBdyP2CYSIgOwFfSB+UVoPExk/YzFpyYitHS8KVZtA==}
|
||||
dev: false
|
||||
|
||||
/opener/1.5.2:
|
||||
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
|
||||
hasBin: true
|
||||
@ -15723,6 +15867,14 @@ packages:
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/react-ssr-prepass/1.5.0_react@18.2.0:
|
||||
resolution: {integrity: sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-style-singleton/2.2.1_fan5qbzahqtxlm5dzefqlqx5ia:
|
||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||
engines: {node: '>=10'}
|
||||
@ -17055,6 +17207,13 @@ packages:
|
||||
pirates: 4.0.5
|
||||
ts-interface-checker: 0.1.13
|
||||
|
||||
/superjson/1.11.0:
|
||||
resolution: {integrity: sha512-6PfAg1FKhqkwWvPb2uXhH4MkMttdc17eJ91+Aoz4s1XUEDZFmLfFx/xVA3wgkPxAGy5dpozgGdK6V/n20Wj9yg==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
copy-anything: 3.0.2
|
||||
dev: true
|
||||
|
||||
/supports-color/5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
@ -17346,6 +17505,21 @@ packages:
|
||||
resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==}
|
||||
dev: false
|
||||
|
||||
/trpc-openapi/1.0.0-alpha.4_m7lve6cjvd26xbjqz4zsiln46q:
|
||||
resolution: {integrity: sha512-Zt+IUlYY/pw3Kk78ByTCOZb47r8zJFBUqr1imiOzyVjpaUMVMSPA2RpATCiOJQGSvR70YqoZAMPAu+KPoDmh2w==}
|
||||
peerDependencies:
|
||||
'@trpc/server': ^10.0.0-rc.7
|
||||
zod: ^3.14.4
|
||||
dependencies:
|
||||
'@trpc/server': 10.0.0-rc.8
|
||||
co-body: 6.1.0
|
||||
lodash.clonedeep: 4.5.0
|
||||
node-mocks-http: 1.12.1
|
||||
openapi-types: 12.0.2
|
||||
zod: 3.19.1
|
||||
zod-to-json-schema: 3.19.0_zod@3.19.1
|
||||
dev: false
|
||||
|
||||
/ts-easing/0.2.0:
|
||||
resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
|
||||
dev: false
|
||||
@ -18523,9 +18697,16 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
/zod-to-json-schema/3.19.0_zod@3.19.1:
|
||||
resolution: {integrity: sha512-ChPYAl3MWnZwKgNMMYP+/MgYWekL4Ef6n0ot/PBURTUfBpzjnSFB8unZaL3MDwH0YcAwQW63n2pnPJi/dqHfwg==}
|
||||
peerDependencies:
|
||||
zod: ^3.19.0
|
||||
dependencies:
|
||||
zod: 3.19.1
|
||||
dev: false
|
||||
|
||||
/zod/3.19.1:
|
||||
resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==}
|
||||
dev: false
|
||||
|
||||
/zustand/3.7.2_react@18.2.0:
|
||||
resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==}
|
||||
|
Reference in New Issue
Block a user