build: add pnpm
This commit is contained in:
@ -4,4 +4,11 @@ node_modules
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
README.md
|
README.md
|
||||||
.next
|
.next
|
||||||
.git
|
.git
|
||||||
|
.github
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
landing-page
|
||||||
|
docs
|
||||||
|
scripts
|
||||||
|
wordpress
|
||||||
|
7
.github/workflows/playwright.yml
vendored
7
.github/workflows/playwright.yml
vendored
@ -12,15 +12,16 @@ jobs:
|
|||||||
- name: Log Info
|
- name: Log Info
|
||||||
run: echo ${{ github.event.deployment_status.target_url }} && echo ${{ github.event.deployment }} && echo ${{ github.event.deployment_status.environment }}
|
run: echo ${{ github.event.deployment_status.target_url }} && echo ${{ github.event.deployment }} && echo ${{ github.event.deployment_status.environment }}
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- uses: pnpm/action-setup@v2.2.2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
run: pnpm i --frozen-lockfile
|
||||||
- name: Build dependencies
|
- name: Build dependencies
|
||||||
run: yarn turbo run build --scope=builder --include-dependencies
|
run: pnpm turbo run build --filter="builder^..."
|
||||||
- name: Install Playwright
|
- name: Install Playwright
|
||||||
run: npx playwright install --with-deps
|
run: npx playwright install --with-deps
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./apps/builder
|
working-directory: ./apps/builder
|
||||||
run: yarn test
|
run: pnpm test
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_BUILDER_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}
|
PLAYWRIGHT_BUILDER_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}
|
||||||
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
|
||||||
|
10
.github/workflows/publish-lib-to-npm.yml
vendored
10
.github/workflows/publish-lib-to-npm.yml
vendored
@ -12,12 +12,10 @@ jobs:
|
|||||||
working-directory: ./packages/typebot-js
|
working-directory: ./packages/typebot-js
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1
|
- uses: pnpm/action-setup@v2.2.2
|
||||||
with:
|
- run: pnpm i --frozen-lockfile
|
||||||
node-version: 16
|
- run: pnpm test
|
||||||
- run: yarn
|
- run: pnpm build
|
||||||
- run: yarn test
|
|
||||||
- run: yarn build
|
|
||||||
- uses: JS-DevTools/npm-publish@v1
|
- uses: JS-DevTools/npm-publish@v1
|
||||||
with:
|
with:
|
||||||
package: './packages/typebot-js/package.json'
|
package: './packages/typebot-js/package.json'
|
||||||
|
39
.github/workflows/publish_docker_images.yml
vendored
39
.github/workflows/publish_docker_images.yml
vendored
@ -9,8 +9,8 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_images_to_docker_hub:
|
push_builder:
|
||||||
name: Push images to Docker Hub
|
name: Builder
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
@ -27,17 +27,6 @@ jobs:
|
|||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
- name: Extract Viewer meta
|
|
||||||
id: viewer-meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: baptistearno/typebot-viewer
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
@ -54,6 +43,30 @@ jobs:
|
|||||||
labels: ${{ steps.builder-meta.outputs.labels }}
|
labels: ${{ steps.builder-meta.outputs.labels }}
|
||||||
build-args: |
|
build-args: |
|
||||||
SCOPE=builder
|
SCOPE=builder
|
||||||
|
push_viewer:
|
||||||
|
name: Viewer
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Extract Viewer meta
|
||||||
|
id: viewer-meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: baptistearno/typebot-viewer
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
- name: Build and push viewer image
|
- name: Build and push viewer image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,7 +5,6 @@ node_modules
|
|||||||
workspace.code-workspace
|
workspace.code-workspace
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.turbo
|
.turbo
|
||||||
yarn-error.log
|
|
||||||
authenticatedState.json
|
authenticatedState.json
|
||||||
playwright-report
|
playwright-report
|
||||||
dist
|
dist
|
||||||
|
46
Dockerfile
46
Dockerfile
@ -1,51 +1,37 @@
|
|||||||
# https://github.com/vercel/turborepo/issues/215#issuecomment-1027058056
|
FROM node:18-slim AS base
|
||||||
FROM node:16-slim AS base
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ARG SCOPE
|
ARG SCOPE
|
||||||
ENV SCOPE=${SCOPE}
|
ENV SCOPE=${SCOPE}
|
||||||
|
RUN npm --global install pnpm
|
||||||
FROM base AS pruner
|
|
||||||
RUN yarn global add turbo@1.2.9
|
|
||||||
COPY . .
|
|
||||||
RUN turbo prune --scope="${SCOPE}" --docker
|
|
||||||
|
|
||||||
FROM base AS installer
|
|
||||||
COPY --from=pruner /app/out/json/ .
|
|
||||||
COPY --from=pruner /app/out/yarn.lock ./yarn.lock
|
|
||||||
RUN yarn install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
RUN apt-get -qy update && apt-get -qy --no-install-recommends install openssl git
|
||||||
COPY --from=installer /app/ .
|
COPY pnpm-lock.yaml .npmrc pnpm-workspace.yaml ./
|
||||||
COPY --from=pruner /app/out/full/ .
|
RUN pnpm fetch
|
||||||
COPY ./apps/${SCOPE}/.env.docker ./apps/${SCOPE}/.env.production
|
ADD . ./
|
||||||
COPY ./apps/${SCOPE}/.env.docker ./apps/${SCOPE}/.env.local
|
RUN pnpm install -r --offline
|
||||||
RUN apt-get -qy update && apt-get -qy --no-install-recommends install openssl
|
RUN pnpm turbo run build --filter=${SCOPE}...
|
||||||
RUN yarn turbo run build --scope="${SCOPE}" --include-dependencies --no-deps
|
|
||||||
RUN find . -name node_modules | xargs rm -rf
|
|
||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
COPY ./packages/db/prisma ./prisma
|
|
||||||
COPY --from=installer /app/node_modules ./node_modules
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/next.config.js ./
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/public ./public
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/package.json ./package.json
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/.next/standalone ./
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/.next/static ./.next/static
|
|
||||||
COPY --from=builder /app/apps/${SCOPE}/.env.docker ./.env.production
|
|
||||||
RUN apt-get -qy update \
|
RUN apt-get -qy update \
|
||||||
&& apt-get -qy --no-install-recommends install \
|
&& apt-get -qy --no-install-recommends install \
|
||||||
openssl \
|
openssl \
|
||||||
&& apt-get autoremove -yq \
|
&& apt-get autoremove -yq \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY ./packages/db/prisma ./prisma
|
||||||
|
COPY ./apps/${SCOPE}/.env.docker ./apps/${SCOPE}/.env.production
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/apps/${SCOPE}/public ./apps/${SCOPE}/public
|
||||||
|
COPY --from=builder --chown=node:node /app/apps/${SCOPE}/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=node:node /app/apps/${SCOPE}/.next/static ./apps/${SCOPE}/.next/static
|
||||||
|
|
||||||
COPY env.sh ${SCOPE}-entrypoint.sh ./
|
COPY env.sh ${SCOPE}-entrypoint.sh ./
|
||||||
RUN chmod +x ./"${SCOPE}"-entrypoint.sh \
|
RUN chmod +x ./${SCOPE}-entrypoint.sh \
|
||||||
&& chmod +x ./env.sh
|
&& chmod +x ./env.sh
|
||||||
ENTRYPOINT ./"${SCOPE}"-entrypoint.sh
|
ENTRYPOINT ./${SCOPE}-entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT 3000
|
ENV PORT 3000
|
||||||
|
@ -54,7 +54,7 @@ Interested in self-hosting Typebot on your server? Take a look at the [self-host
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd typebot.io
|
cd typebot.io
|
||||||
yarn
|
pnpm i
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Set up environment variables
|
3. Set up environment variables
|
||||||
@ -69,7 +69,7 @@ Interested in self-hosting Typebot on your server? Take a look at the [self-host
|
|||||||
5. Start the builder and viewer
|
5. Start the builder and viewer
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Builder is available at `http://localhost:3000`
|
Builder is available at `http://localhost:3000`
|
||||||
@ -86,14 +86,14 @@ Interested in self-hosting Typebot on your server? Take a look at the [self-host
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd apps/landing-page
|
cd apps/landing-page
|
||||||
yarn dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
7. (Optionnal) Start the docs
|
7. (Optionnal) Start the docs
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd apps/docs
|
cd apps/docs
|
||||||
yarn start
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
@ -6,3 +6,4 @@ NEXT_PUBLIC_GIPHY_API_KEY=
|
|||||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
||||||
NEXT_PUBLIC_SENTRY_DSN=
|
NEXT_PUBLIC_SENTRY_DSN=
|
||||||
NEXT_PUBLIC_VIEWER_INTERNAL_URL=
|
NEXT_PUBLIC_VIEWER_INTERNAL_URL=
|
||||||
|
NEXT_PUBLIC_E2E_TEST=
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { FlexProps, Flex, Box, Divider, Text } from '@chakra-ui/react'
|
import {
|
||||||
import { useColorModeValue } from '@chakra-ui/system'
|
FlexProps,
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
Divider,
|
||||||
|
Text,
|
||||||
|
useColorModeValue,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export const DividerWithText = (props: FlexProps) => {
|
export const DividerWithText = (props: FlexProps) => {
|
||||||
|
@ -11,7 +11,7 @@ import { useUser } from 'contexts/UserContext'
|
|||||||
import { Answer, Typebot } from 'models'
|
import { Answer, Typebot } from 'models'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||||
import { sendRequest } from 'utils'
|
import { getViewerUrl, sendRequest } from 'utils'
|
||||||
import confetti from 'canvas-confetti'
|
import confetti from 'canvas-confetti'
|
||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
|
|
||||||
@ -119,6 +119,7 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
{typebot && (
|
{typebot && (
|
||||||
<TypebotViewer
|
<TypebotViewer
|
||||||
|
apiHost={getViewerUrl({ isBuilder: true })}
|
||||||
typebot={parseTypebotToPublicTypebot(typebot)}
|
typebot={parseTypebotToPublicTypebot(typebot)}
|
||||||
predefinedVariables={{
|
predefinedVariables={{
|
||||||
Name: user?.name?.split(' ')[0] ?? undefined,
|
Name: user?.name?.split(' ')[0] ?? undefined,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Flex, HStack, StackProps } from '@chakra-ui/layout'
|
import { CloseButton, Flex, HStack, StackProps } from '@chakra-ui/react'
|
||||||
import { CloseButton } from '@chakra-ui/react'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
type VerifyEmailBannerProps = { id: string } & StackProps
|
type VerifyEmailBannerProps = { id: string } & StackProps
|
||||||
|
@ -18,6 +18,7 @@ import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
|||||||
import { Log } from 'db'
|
import { Log } from 'db'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||||
|
import { getViewerUrl } from 'utils'
|
||||||
|
|
||||||
export const PreviewDrawer = () => {
|
export const PreviewDrawer = () => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
@ -100,6 +101,7 @@ export const PreviewDrawer = () => {
|
|||||||
pointerEvents={isResizing ? 'none' : 'auto'}
|
pointerEvents={isResizing ? 'none' : 'auto'}
|
||||||
>
|
>
|
||||||
<TypebotViewer
|
<TypebotViewer
|
||||||
|
apiHost={getViewerUrl({ isBuilder: true })}
|
||||||
typebot={publicTypebot}
|
typebot={publicTypebot}
|
||||||
onNewGroupVisible={setPreviewingEdge}
|
onNewGroupVisible={setPreviewingEdge}
|
||||||
onNewLog={handleNewLog}
|
onNewLog={handleNewLog}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { chakra, Fade, Button } from '@chakra-ui/react'
|
import { chakra, Fade, Button } from '@chakra-ui/react'
|
||||||
import { Cell as CellProps } from '@tanstack/react-table'
|
import { Cell as CellProps, flexRender } from '@tanstack/react-table'
|
||||||
import { ExpandIcon } from 'assets/icons'
|
import { ExpandIcon } from 'assets/icons'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { TableData } from 'services/typebots/results'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cell: CellProps<any>
|
cell: CellProps<TableData, unknown>
|
||||||
size: number
|
size: number
|
||||||
isExpandButtonVisible: boolean
|
isExpandButtonVisible: boolean
|
||||||
cellIndex: number
|
cellIndex: number
|
||||||
@ -34,7 +35,7 @@ const Cell = ({
|
|||||||
maxWidth: size,
|
maxWidth: size,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{cell.renderCell()}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
<chakra.span
|
<chakra.span
|
||||||
pos="absolute"
|
pos="absolute"
|
||||||
top="0"
|
top="0"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, BoxProps, chakra } from '@chakra-ui/react'
|
import { Box, BoxProps, chakra } from '@chakra-ui/react'
|
||||||
import { HeaderGroup } from '@tanstack/react-table'
|
import { flexRender, HeaderGroup } from '@tanstack/react-table'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { TableData } from 'services/typebots/results'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
headerGroup: HeaderGroup<TableData>
|
||||||
headerGroup: HeaderGroup<any>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderRow = ({ headerGroup }: Props) => {
|
export const HeaderRow = ({ headerGroup }: Props) => {
|
||||||
@ -28,7 +28,9 @@ export const HeaderRow = ({ headerGroup }: Props) => {
|
|||||||
maxWidth: header.getSize(),
|
maxWidth: header.getSize(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{header.isPlaceholder ? null : header.renderHeader()}
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
{header.column.getCanResize() && (
|
{header.column.getCanResize() && (
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
onMouseDown={header.getResizeHandler()}
|
onMouseDown={header.getResizeHandler()}
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
convertResultsToTableData,
|
convertResultsToTableData,
|
||||||
getAllResults,
|
getAllResults,
|
||||||
deleteResults as deleteFetchResults,
|
deleteResults as deleteFetchResults,
|
||||||
} from 'services/typebots'
|
} from 'services/typebots/results'
|
||||||
|
|
||||||
type ResultsActionButtonsProps = {
|
type ResultsActionButtonsProps = {
|
||||||
selectedResultsId: string[]
|
selectedResultsId: string[]
|
||||||
@ -93,7 +93,9 @@ export const ResultsActionButtons = ({
|
|||||||
|
|
||||||
const dataToUnparse = isSelectAll
|
const dataToUnparse = isSelectAll
|
||||||
? await getAllTableData()
|
? await getAllTableData()
|
||||||
: tableData.filter((data) => selectedResultsId.includes(data.id))
|
: tableData.filter((data) =>
|
||||||
|
selectedResultsId.includes(data.id.plainText)
|
||||||
|
)
|
||||||
const csvData = new Blob(
|
const csvData = new Blob(
|
||||||
[
|
[
|
||||||
unparse({
|
unparse({
|
||||||
|
@ -13,10 +13,10 @@ import { ResultHeaderCell, ResultsTablePreferences } from 'models'
|
|||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { LoadingRows } from './LoadingRows'
|
import { LoadingRows } from './LoadingRows'
|
||||||
import {
|
import {
|
||||||
createTable,
|
useReactTable,
|
||||||
useTableInstance,
|
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
ColumnOrderState,
|
ColumnOrderState,
|
||||||
|
ColumnDef,
|
||||||
} from '@tanstack/react-table'
|
} from '@tanstack/react-table'
|
||||||
import { BlockIcon } from 'components/editor/BlocksSideBar/BlockIcon'
|
import { BlockIcon } from 'components/editor/BlocksSideBar/BlockIcon'
|
||||||
import { ColumnSettingsButton } from './ColumnsSettingsButton'
|
import { ColumnSettingsButton } from './ColumnsSettingsButton'
|
||||||
@ -25,21 +25,11 @@ import { useDebounce } from 'use-debounce'
|
|||||||
import { ResultsActionButtons } from './ResultsActionButtons'
|
import { ResultsActionButtons } from './ResultsActionButtons'
|
||||||
import { Row } from './Row'
|
import { Row } from './Row'
|
||||||
import { HeaderRow } from './HeaderRow'
|
import { HeaderRow } from './HeaderRow'
|
||||||
|
import { CellValueType, TableData } from 'services/typebots/results'
|
||||||
type RowType = {
|
|
||||||
id: string
|
|
||||||
[key: string]:
|
|
||||||
| {
|
|
||||||
plainText: string
|
|
||||||
element?: JSX.Element | undefined
|
|
||||||
}
|
|
||||||
| string
|
|
||||||
}
|
|
||||||
const table = createTable().setRowType<RowType>()
|
|
||||||
|
|
||||||
type ResultsTableProps = {
|
type ResultsTableProps = {
|
||||||
resultHeader: ResultHeaderCell[]
|
resultHeader: ResultHeaderCell[]
|
||||||
data: RowType[]
|
data: TableData[]
|
||||||
hasMore?: boolean
|
hasMore?: boolean
|
||||||
preferences?: ResultsTablePreferences
|
preferences?: ResultsTablePreferences
|
||||||
onScrollToBottom: () => void
|
onScrollToBottom: () => void
|
||||||
@ -92,18 +82,18 @@ export const ResultsTable = ({
|
|||||||
const bottomElement = useRef<HTMLDivElement | null>(null)
|
const bottomElement = useRef<HTMLDivElement | null>(null)
|
||||||
const tableWrapper = useRef<HTMLDivElement | null>(null)
|
const tableWrapper = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const columns = React.useMemo(
|
const columns = React.useMemo<ColumnDef<TableData>[]>(
|
||||||
() => [
|
() => [
|
||||||
table.createDisplayColumn({
|
{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
maxSize: 40,
|
maxSize: 40,
|
||||||
header: ({ instance }) => (
|
header: ({ table }) => (
|
||||||
<IndeterminateCheckbox
|
<IndeterminateCheckbox
|
||||||
{...{
|
{...{
|
||||||
checked: instance.getIsAllRowsSelected(),
|
checked: table.getIsAllRowsSelected(),
|
||||||
indeterminate: instance.getIsSomeRowsSelected(),
|
indeterminate: table.getIsSomeRowsSelected(),
|
||||||
onChange: instance.getToggleAllRowsSelectedHandler(),
|
onChange: table.getToggleAllRowsSelectedHandler(),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -118,26 +108,24 @@ export const ResultsTable = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
},
|
||||||
...resultHeader.map((header) =>
|
...resultHeader.map<ColumnDef<TableData>>((header) => ({
|
||||||
table.createDataColumn(header.label, {
|
id: header.id,
|
||||||
id: header.id,
|
accessorKey: header.label,
|
||||||
size: header.isLong ? 400 : 200,
|
size: header.isLong ? 400 : 200,
|
||||||
cell: (info) => {
|
header: () => (
|
||||||
const value = info.getValue()
|
<HStack overflow="hidden" data-testid={`${header.label} header`}>
|
||||||
if (!value) return
|
<HeaderIcon header={header} />
|
||||||
if (typeof value === 'string') return ''
|
<Text>{header.label}</Text>
|
||||||
return value.element || value.plainText || ''
|
</HStack>
|
||||||
},
|
),
|
||||||
header: () => (
|
cell: (info) => {
|
||||||
<HStack overflow="hidden" data-testid={`${header.label} header`}>
|
const value = info.getValue() as CellValueType | undefined
|
||||||
<HeaderIcon header={header} />
|
if (!value) return
|
||||||
<Text>{header.label}</Text>
|
return value.element || value.plainText || ''
|
||||||
</HStack>
|
},
|
||||||
),
|
})),
|
||||||
})
|
{
|
||||||
),
|
|
||||||
table.createDisplayColumn({
|
|
||||||
id: 'logs',
|
id: 'logs',
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
maxSize: 110,
|
maxSize: 110,
|
||||||
@ -152,13 +140,13 @@ export const ResultsTable = ({
|
|||||||
See logs
|
See logs
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
}),
|
},
|
||||||
],
|
],
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[resultHeader]
|
[resultHeader]
|
||||||
)
|
)
|
||||||
|
|
||||||
const instance = useTableInstance(table, {
|
const instance = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
state: {
|
state: {
|
||||||
@ -167,7 +155,7 @@ export const ResultsTable = ({
|
|||||||
columnOrder: columnsOrder,
|
columnOrder: columnsOrder,
|
||||||
columnSizing: columnsWidth,
|
columnSizing: columnsWidth,
|
||||||
},
|
},
|
||||||
getRowId: (row) => row.id,
|
getRowId: (row) => row.id.plainText,
|
||||||
columnResizeMode: 'onChange',
|
columnResizeMode: 'onChange',
|
||||||
onRowSelectionChange: setRowSelection,
|
onRowSelectionChange: setRowSelection,
|
||||||
onColumnVisibilityChange: setColumnsVisibility,
|
onColumnVisibilityChange: setColumnsVisibility,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Row as RowProps } from '@tanstack/react-table'
|
import { Row as RowProps } from '@tanstack/react-table'
|
||||||
import Cell from './Cell'
|
import Cell from './Cell'
|
||||||
|
import { TableData } from 'services/typebots/results'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
row: RowProps<TableData>
|
||||||
row: RowProps<any>
|
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
bottomElement?: React.MutableRefObject<HTMLDivElement | null>
|
bottomElement?: React.MutableRefObject<HTMLDivElement | null>
|
||||||
onExpandButtonClick: () => void
|
onExpandButtonClick: () => void
|
||||||
|
@ -16,11 +16,10 @@ import { useWorkspace } from 'contexts/WorkspaceContext'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { parseDefaultPublicId } from 'services/typebots'
|
import { parseDefaultPublicId } from 'services/typebots'
|
||||||
import { isFreePlan } from 'services/workspace'
|
import { isFreePlan } from 'services/workspace'
|
||||||
import { isDefined, isNotDefined } from 'utils'
|
import { getViewerUrl, isDefined, isNotDefined } from 'utils'
|
||||||
import { CustomDomainsDropdown } from './customDomain/CustomDomainsDropdown'
|
import { CustomDomainsDropdown } from './customDomain/CustomDomainsDropdown'
|
||||||
import { EditableUrl } from './EditableUrl'
|
import { EditableUrl } from './EditableUrl'
|
||||||
import { integrationsList } from './integrations/EmbedButton'
|
import { integrationsList } from './integrations/EmbedButton'
|
||||||
import { env } from 'utils'
|
|
||||||
|
|
||||||
export const ShareContent = () => {
|
export const ShareContent = () => {
|
||||||
const { workspace } = useWorkspace()
|
const { workspace } = useWorkspace()
|
||||||
@ -59,7 +58,9 @@ export const ShareContent = () => {
|
|||||||
</Heading>
|
</Heading>
|
||||||
{typebot && (
|
{typebot && (
|
||||||
<EditableUrl
|
<EditableUrl
|
||||||
hostname={env('VIEWER_URL') ?? 'https://typebot.io'}
|
hostname={
|
||||||
|
getViewerUrl({ isBuilder: true }) ?? 'https://typebot.io'
|
||||||
|
}
|
||||||
pathname={publicId}
|
pathname={publicId}
|
||||||
onPathnameChange={handlePublicIdChange}
|
onPathnameChange={handlePublicIdChange}
|
||||||
/>
|
/>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { FlexProps } from '@chakra-ui/layout'
|
|
||||||
import prettier from 'prettier/standalone'
|
import prettier from 'prettier/standalone'
|
||||||
import parserHtml from 'prettier/parser-html'
|
import parserHtml from 'prettier/parser-html'
|
||||||
import { BubbleParams } from 'typebot-js'
|
import { BubbleParams } from 'typebot-js'
|
||||||
import { parseInitBubbleCode, typebotJsHtml } from '../params'
|
import { parseInitBubbleCode, typebotJsHtml } from '../params'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
import { FlexProps } from '@chakra-ui/react'
|
||||||
|
|
||||||
type ChatEmbedCodeProps = {
|
type ChatEmbedCodeProps = {
|
||||||
withStarterVariables?: boolean
|
withStarterVariables?: boolean
|
||||||
@ -21,9 +21,7 @@ export const ChatEmbedCode = ({
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
createSnippet({
|
createSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
button,
|
button,
|
||||||
proactiveMessage,
|
proactiveMessage,
|
||||||
|
@ -5,7 +5,7 @@ import { parseInitContainerCode, typebotJsHtml } from '../params'
|
|||||||
import { IframeParams } from 'typebot-js'
|
import { IframeParams } from 'typebot-js'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
|
||||||
type ContainerEmbedCodeProps = {
|
type ContainerEmbedCodeProps = {
|
||||||
widthLabel: string
|
widthLabel: string
|
||||||
@ -23,9 +23,7 @@ export const ContainerEmbedCode = ({
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
parseSnippet({
|
parseSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
heightLabel,
|
heightLabel,
|
||||||
widthLabel,
|
widthLabel,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FlexProps } from '@chakra-ui/react'
|
import { FlexProps } from '@chakra-ui/react'
|
||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
widthLabel: string
|
widthLabel: string
|
||||||
@ -14,9 +14,7 @@ export const IframeEmbedCode = ({
|
|||||||
}: Props & FlexProps) => {
|
}: Props & FlexProps) => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
const src = `${
|
const src = `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`
|
}/${typebot?.publicId}`
|
||||||
const code = `<iframe src="${src}" width="${widthLabel}" height="${heightLabel}" />`
|
const code = `<iframe src="${src}" width="${widthLabel}" height="${heightLabel}" />`
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { FlexProps } from '@chakra-ui/layout'
|
import { FlexProps } from '@chakra-ui/react'
|
||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import parserHtml from 'prettier/parser-html'
|
import parserHtml from 'prettier/parser-html'
|
||||||
import prettier from 'prettier/standalone'
|
import prettier from 'prettier/standalone'
|
||||||
import { PopupParams } from 'typebot-js'
|
import { PopupParams } from 'typebot-js'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
import { parseInitPopupCode, typebotJsHtml } from '../params'
|
import { parseInitPopupCode, typebotJsHtml } from '../params'
|
||||||
|
|
||||||
type PopupEmbedCodeProps = {
|
type PopupEmbedCodeProps = {
|
||||||
@ -18,9 +18,7 @@ export const PopupEmbedCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => {
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
createSnippet({
|
createSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
delay,
|
delay,
|
||||||
}),
|
}),
|
||||||
|
@ -10,7 +10,7 @@ import parserBabel from 'prettier/parser-babel'
|
|||||||
import prettier from 'prettier/standalone'
|
import prettier from 'prettier/standalone'
|
||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
|
||||||
type StandardReactDivProps = { widthLabel: string; heightLabel: string }
|
type StandardReactDivProps = { widthLabel: string; heightLabel: string }
|
||||||
export const StandardReactDiv = ({
|
export const StandardReactDiv = ({
|
||||||
@ -21,9 +21,7 @@ export const StandardReactDiv = ({
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
parseContainerSnippet({
|
parseContainerSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
heightLabel,
|
heightLabel,
|
||||||
widthLabel,
|
widthLabel,
|
||||||
@ -73,9 +71,7 @@ export const PopupReactCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => {
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
parsePopupSnippet({
|
parsePopupSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
delay,
|
delay,
|
||||||
}),
|
}),
|
||||||
@ -124,9 +120,7 @@ export const ChatReactCode = ({
|
|||||||
const snippet = prettier.format(
|
const snippet = prettier.format(
|
||||||
parseBubbleSnippet({
|
parseBubbleSnippet({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${typebot?.publicId}`,
|
}/${typebot?.publicId}`,
|
||||||
button,
|
button,
|
||||||
proactiveMessage,
|
proactiveMessage,
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { createCustomDomain } from 'services/user'
|
import { createCustomDomain } from 'services/user'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
|
||||||
const hostnameRegex =
|
const hostnameRegex =
|
||||||
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/
|
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/
|
||||||
@ -118,9 +118,8 @@ export const CustomDomainModal = ({
|
|||||||
<Stack>
|
<Stack>
|
||||||
<Text fontWeight="bold">Value</Text>
|
<Text fontWeight="bold">Value</Text>
|
||||||
<Text>
|
<Text>
|
||||||
{isEmpty(env('VIEWER_INTERNAL_URL'))
|
{env('VIEWER_INTERNAL_URL') ??
|
||||||
? env('VIEWER_URL')
|
getViewerUrl({ isBuilder: true })}
|
||||||
: env('VIEWER_INTERNAL_URL')}
|
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
@ -11,7 +11,7 @@ import { PopupEmbedSettings } from 'components/share/codeSnippets/Popup/EmbedSet
|
|||||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { BubbleParams } from 'typebot-js'
|
import { BubbleParams } from 'typebot-js'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
import { ModalProps } from '../../EmbedButton'
|
import { ModalProps } from '../../EmbedButton'
|
||||||
|
|
||||||
type GtmInstructionsProps = {
|
type GtmInstructionsProps = {
|
||||||
@ -41,9 +41,7 @@ const StandardInstructions = ({ publicId }: Pick<ModalProps, 'publicId'>) => {
|
|||||||
|
|
||||||
const jsCode = parseInitContainerCode({
|
const jsCode = parseInitContainerCode({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`,
|
}/${publicId}`,
|
||||||
})
|
})
|
||||||
const headCode = `${typebotJsHtml}
|
const headCode = `${typebotJsHtml}
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
||||||
import { PublishFirstInfo } from 'components/shared/Info'
|
import { PublishFirstInfo } from 'components/shared/Info'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
import { ModalProps } from '../EmbedButton'
|
import { ModalProps } from '../EmbedButton'
|
||||||
|
|
||||||
export const NotionModal = ({
|
export const NotionModal = ({
|
||||||
@ -46,17 +46,15 @@ export const NotionModal = ({
|
|||||||
pr="4.5rem"
|
pr="4.5rem"
|
||||||
type={'text'}
|
type={'text'}
|
||||||
defaultValue={`${
|
defaultValue={`${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ??
|
||||||
? env('VIEWER_URL')
|
getViewerUrl({ isBuilder: true })
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`}
|
}/${publicId}`}
|
||||||
/>
|
/>
|
||||||
<InputRightElement width="4.5rem">
|
<InputRightElement width="4.5rem">
|
||||||
<CopyButton
|
<CopyButton
|
||||||
textToCopy={`${
|
textToCopy={`${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ??
|
||||||
? env('VIEWER_URL')
|
getViewerUrl({ isBuilder: true })
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`}
|
}/${publicId}`}
|
||||||
/>
|
/>
|
||||||
</InputRightElement>
|
</InputRightElement>
|
||||||
|
@ -14,7 +14,7 @@ import { BubbleParams } from 'typebot-js'
|
|||||||
import { ModalProps } from '../../EmbedButton'
|
import { ModalProps } from '../../EmbedButton'
|
||||||
import parserHtml from 'prettier/parser-html'
|
import parserHtml from 'prettier/parser-html'
|
||||||
import prettier from 'prettier/standalone'
|
import prettier from 'prettier/standalone'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
|
|
||||||
type ShopifyInstructionsProps = {
|
type ShopifyInstructionsProps = {
|
||||||
type: 'standard' | 'popup' | 'bubble'
|
type: 'standard' | 'popup' | 'bubble'
|
||||||
@ -46,9 +46,7 @@ const StandardInstructions = ({ publicId }: Pick<ModalProps, 'publicId'>) => {
|
|||||||
|
|
||||||
const jsCode = parseInitContainerCode({
|
const jsCode = parseInitContainerCode({
|
||||||
url: `${
|
url: `${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ?? getViewerUrl({ isBuilder: true })
|
||||||
? env('VIEWER_URL')
|
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`,
|
}/${publicId}`,
|
||||||
})
|
})
|
||||||
const headCode = prettier.format(
|
const headCode = prettier.format(
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
import { ExternalLinkIcon } from 'assets/icons'
|
import { ExternalLinkIcon } from 'assets/icons'
|
||||||
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
||||||
import { PublishFirstInfo } from 'components/shared/Info'
|
import { PublishFirstInfo } from 'components/shared/Info'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, getViewerUrl } from 'utils'
|
||||||
import { ModalProps } from '../EmbedButton'
|
import { ModalProps } from '../EmbedButton'
|
||||||
|
|
||||||
export const WordpressModal = ({
|
export const WordpressModal = ({
|
||||||
@ -55,17 +55,15 @@ export const WordpressModal = ({
|
|||||||
pr="4.5rem"
|
pr="4.5rem"
|
||||||
type={'text'}
|
type={'text'}
|
||||||
defaultValue={`${
|
defaultValue={`${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ??
|
||||||
? env('VIEWER_URL')
|
getViewerUrl({ isBuilder: true })
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`}
|
}/${publicId}`}
|
||||||
/>
|
/>
|
||||||
<InputRightElement width="4.5rem">
|
<InputRightElement width="4.5rem">
|
||||||
<CopyButton
|
<CopyButton
|
||||||
textToCopy={`${
|
textToCopy={`${
|
||||||
isEmpty(env('VIEWER_INTERNAL_URL'))
|
env('VIEWER_INTERNAL_URL') ??
|
||||||
? env('VIEWER_URL')
|
getViewerUrl({ isBuilder: true })
|
||||||
: env('VIEWER_INTERNAL_URL')
|
|
||||||
}/${publicId}`}
|
}/${publicId}`}
|
||||||
/>
|
/>
|
||||||
</InputRightElement>
|
</InputRightElement>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Box, BoxProps, HStack } from '@chakra-ui/react'
|
import { Box, BoxProps, HStack } from '@chakra-ui/react'
|
||||||
import { EditorState, EditorView, basicSetup } from '@codemirror/basic-setup'
|
import { EditorView, basicSetup } from 'codemirror'
|
||||||
|
import { EditorState } from '@codemirror/state'
|
||||||
import { json, jsonParseLinter } from '@codemirror/lang-json'
|
import { json, jsonParseLinter } from '@codemirror/lang-json'
|
||||||
import { css } from '@codemirror/lang-css'
|
import { css } from '@codemirror/lang-css'
|
||||||
import { javascript } from '@codemirror/lang-javascript'
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
@ -43,7 +44,7 @@ export const CodeEditor = ({
|
|||||||
setPlainTextValue(value)
|
setPlainTextValue(value)
|
||||||
onChange && onChange(value)
|
onChange && onChange(value)
|
||||||
},
|
},
|
||||||
env('E2E_TEST') === 'enabled' ? 0 : debounceTimeout
|
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEventListener } from '@chakra-ui/hooks'
|
import { useEventListener } from '@chakra-ui/react'
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import {
|
import {
|
||||||
useGraph,
|
useGraph,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { chakra } from '@chakra-ui/system'
|
import { chakra } from '@chakra-ui/react'
|
||||||
import { colors } from 'libs/theme'
|
import { colors } from 'libs/theme'
|
||||||
import { Edge as EdgeProps } from 'models'
|
import { Edge as EdgeProps } from 'models'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
@ -3,7 +3,6 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'
|
|||||||
import {
|
import {
|
||||||
Plate,
|
Plate,
|
||||||
selectEditor,
|
selectEditor,
|
||||||
serializeHtml,
|
|
||||||
TEditor,
|
TEditor,
|
||||||
TElement,
|
TElement,
|
||||||
Value,
|
Value,
|
||||||
@ -16,6 +15,7 @@ import { parseHtmlStringToPlainText } from 'services/utils'
|
|||||||
import { defaultTextBubbleContent, TextBubbleContent, Variable } from 'models'
|
import { defaultTextBubbleContent, TextBubbleContent, Variable } from 'models'
|
||||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||||
import { ReactEditor } from 'slate-react'
|
import { ReactEditor } from 'slate-react'
|
||||||
|
import { serializeHtml } from '@udecode/plate-serializer-html'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
initialValue: TElement[]
|
initialValue: TElement[]
|
||||||
@ -98,7 +98,7 @@ export const TextBubbleEditor = ({ initialValue, onClose }: Props) => {
|
|||||||
setValue(val)
|
setValue(val)
|
||||||
setIsVariableDropdownOpen(false)
|
setIsVariableDropdownOpen(false)
|
||||||
}
|
}
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
if (e.shiftKey) return
|
if (e.shiftKey) return
|
||||||
if (e.key === 'Enter') closeEditor()
|
if (e.key === 'Enter') closeEditor()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { StackProps, HStack, Button } from '@chakra-ui/react'
|
import { StackProps, HStack, Button, IconButton } from '@chakra-ui/react'
|
||||||
import {
|
import {
|
||||||
MARK_BOLD,
|
MARK_BOLD,
|
||||||
MARK_ITALIC,
|
MARK_ITALIC,
|
||||||
@ -7,7 +7,13 @@ import {
|
|||||||
import { getPluginType, PlateEditor, Value } from '@udecode/plate-core'
|
import { getPluginType, PlateEditor, Value } from '@udecode/plate-core'
|
||||||
import { LinkToolbarButton } from '@udecode/plate-ui-link'
|
import { LinkToolbarButton } from '@udecode/plate-ui-link'
|
||||||
import { MarkToolbarButton } from '@udecode/plate-ui-toolbar'
|
import { MarkToolbarButton } from '@udecode/plate-ui-toolbar'
|
||||||
import { BoldIcon, ItalicIcon, UnderlineIcon, LinkIcon } from 'assets/icons'
|
import {
|
||||||
|
BoldIcon,
|
||||||
|
ItalicIcon,
|
||||||
|
UnderlineIcon,
|
||||||
|
LinkIcon,
|
||||||
|
UserIcon,
|
||||||
|
} from 'assets/icons'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
editor: PlateEditor<Value>
|
editor: PlateEditor<Value>
|
||||||
@ -33,9 +39,12 @@ export const ToolBar = ({
|
|||||||
borderBottomWidth={1}
|
borderBottomWidth={1}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Button size="sm" onMouseDown={handleVariablesButtonMouseDown}>
|
<IconButton
|
||||||
Variables
|
aria-label="Insert variable"
|
||||||
</Button>
|
size="sm"
|
||||||
|
onMouseDown={handleVariablesButtonMouseDown}
|
||||||
|
icon={<UserIcon />}
|
||||||
|
/>
|
||||||
<span data-testid="bold-button">
|
<span data-testid="bold-button">
|
||||||
<MarkToolbarButton
|
<MarkToolbarButton
|
||||||
type={getPluginType(editor, MARK_BOLD)}
|
type={getPluginType(editor, MARK_BOLD)}
|
||||||
|
@ -1,22 +1,12 @@
|
|||||||
import { ChangeEvent, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import {
|
import { Button, Flex, HStack, Stack, Text } from '@chakra-ui/react'
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
HStack,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
Input as ClassicInput,
|
|
||||||
SimpleGrid,
|
|
||||||
GridItem,
|
|
||||||
} from '@chakra-ui/react'
|
|
||||||
import { SearchContextManager } from '@giphy/react-components'
|
import { SearchContextManager } from '@giphy/react-components'
|
||||||
import { UploadButton } from '../buttons/UploadButton'
|
import { UploadButton } from '../buttons/UploadButton'
|
||||||
import { GiphySearch } from './GiphySearch'
|
import { GiphySearch } from './GiphySearch'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { BaseEmoji, emojiIndex } from 'emoji-mart'
|
|
||||||
import { emojis } from './emojis'
|
|
||||||
import { Input } from '../Textbox/Input'
|
import { Input } from '../Textbox/Input'
|
||||||
import { env, isEmpty } from 'utils'
|
import { env, isEmpty } from 'utils'
|
||||||
|
import { EmojiSearchableList } from './emoji/EmojiSearchableList'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url?: string
|
url?: string
|
||||||
@ -101,7 +91,7 @@ const BodyContent = ({
|
|||||||
case 'giphy':
|
case 'giphy':
|
||||||
return <GiphyContent onNewUrl={onSubmit} />
|
return <GiphyContent onNewUrl={onSubmit} />
|
||||||
case 'emoji':
|
case 'emoji':
|
||||||
return <EmojiContent onEmojiSelected={onSubmit} />
|
return <EmojiSearchableList onEmojiSelected={onSubmit} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,55 +123,6 @@ const EmbedLinkContent = ({ initialUrl, onNewUrl }: ContentProps) => (
|
|||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
|
||||||
const EmojiContent = ({
|
|
||||||
onEmojiSelected,
|
|
||||||
}: {
|
|
||||||
onEmojiSelected: (emoji: string) => void
|
|
||||||
}) => {
|
|
||||||
const [searchValue, setSearchValue] = useState('')
|
|
||||||
const [filteredEmojis, setFilteredEmojis] = useState<string[]>(emojis)
|
|
||||||
|
|
||||||
const handleEmojiClick = (emoji: string) => () => onEmojiSelected(emoji)
|
|
||||||
|
|
||||||
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setSearchValue(e.target.value)
|
|
||||||
setFilteredEmojis(
|
|
||||||
emojiIndex.search(e.target.value)?.map((o) => (o as BaseEmoji).native) ??
|
|
||||||
emojis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack>
|
|
||||||
<ClassicInput
|
|
||||||
placeholder="Search..."
|
|
||||||
value={searchValue}
|
|
||||||
onChange={handleSearchChange}
|
|
||||||
/>
|
|
||||||
<SimpleGrid
|
|
||||||
maxH="350px"
|
|
||||||
overflowY="scroll"
|
|
||||||
overflowX="hidden"
|
|
||||||
spacing={0}
|
|
||||||
columns={7}
|
|
||||||
>
|
|
||||||
{filteredEmojis.map((emoji) => (
|
|
||||||
<GridItem key={emoji}>
|
|
||||||
<Button
|
|
||||||
onClick={handleEmojiClick(emoji)}
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
fontSize="xl"
|
|
||||||
>
|
|
||||||
{emoji}
|
|
||||||
</Button>
|
|
||||||
</GridItem>
|
|
||||||
))}
|
|
||||||
</SimpleGrid>
|
|
||||||
</Stack>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const GiphyContent = ({ onNewUrl }: ContentProps) => {
|
const GiphyContent = ({ onNewUrl }: ContentProps) => {
|
||||||
if (isEmpty(env('GIPHY_API_KEY')))
|
if (isEmpty(env('GIPHY_API_KEY')))
|
||||||
return <Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
return <Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
import emojis from './emojiList.json'
|
||||||
|
import emojiTagsData from 'emojilib'
|
||||||
|
import {
|
||||||
|
Stack,
|
||||||
|
SimpleGrid,
|
||||||
|
GridItem,
|
||||||
|
Button,
|
||||||
|
Input as ClassicInput,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import { useState, ChangeEvent, useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
const emojiTags = emojiTagsData as Record<string, string[]>
|
||||||
|
|
||||||
|
const people = emojis['Smileys & Emotion'].concat(emojis['People & Body'])
|
||||||
|
const nature = emojis['Animals & Nature']
|
||||||
|
const food = emojis['Food & Drink']
|
||||||
|
const activities = emojis['Activities']
|
||||||
|
const travel = emojis['Travel & Places']
|
||||||
|
const objects = emojis['Objects']
|
||||||
|
const symbols = emojis['Symbols']
|
||||||
|
const flags = emojis['Flags']
|
||||||
|
|
||||||
|
export const EmojiSearchableList = ({
|
||||||
|
onEmojiSelected,
|
||||||
|
}: {
|
||||||
|
onEmojiSelected: (emoji: string) => void
|
||||||
|
}) => {
|
||||||
|
const scrollContainer = useRef<HTMLDivElement>(null)
|
||||||
|
const bottomElement = useRef<HTMLDivElement>(null)
|
||||||
|
const [isSearching, setIsSearching] = useState(false)
|
||||||
|
const [filteredPeople, setFilteredPeople] = useState(people)
|
||||||
|
const [filteredAnimals, setFilteredAnimals] = useState(nature)
|
||||||
|
const [filteredFood, setFilteredFood] = useState(food)
|
||||||
|
const [filteredTravel, setFilteredTravel] = useState(travel)
|
||||||
|
const [filteredActivities, setFilteredActivities] = useState(activities)
|
||||||
|
const [filteredObjects, setFilteredObjects] = useState(objects)
|
||||||
|
const [filteredSymbols, setFilteredSymbols] = useState(symbols)
|
||||||
|
const [filteredFlags, setFilteredFlags] = useState(flags)
|
||||||
|
const [totalDisplayedCategories, setTotalDisplayedCategories] = useState(1)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!bottomElement.current) return
|
||||||
|
const observer = new IntersectionObserver(handleObserver, {
|
||||||
|
root: scrollContainer.current,
|
||||||
|
})
|
||||||
|
if (bottomElement.current) observer.observe(bottomElement.current)
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [bottomElement.current])
|
||||||
|
|
||||||
|
const handleObserver = (entities: IntersectionObserverEntry[]) => {
|
||||||
|
const target = entities[0]
|
||||||
|
if (target.isIntersecting) setTotalDisplayedCategories((c) => c + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearchChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const searchValue = e.target.value
|
||||||
|
if (searchValue.length <= 2 && isSearching) return resetEmojiList()
|
||||||
|
setIsSearching(true)
|
||||||
|
setTotalDisplayedCategories(8)
|
||||||
|
const byTag = (emoji: string) =>
|
||||||
|
emojiTags[emoji].find((tag) => tag.includes(searchValue))
|
||||||
|
setFilteredPeople(people.filter(byTag))
|
||||||
|
setFilteredAnimals(nature.filter(byTag))
|
||||||
|
setFilteredFood(food.filter(byTag))
|
||||||
|
setFilteredTravel(travel.filter(byTag))
|
||||||
|
setFilteredActivities(activities.filter(byTag))
|
||||||
|
setFilteredObjects(objects.filter(byTag))
|
||||||
|
setFilteredSymbols(symbols.filter(byTag))
|
||||||
|
setFilteredFlags(flags.filter(byTag))
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetEmojiList = () => {
|
||||||
|
setTotalDisplayedCategories(1)
|
||||||
|
setIsSearching(false)
|
||||||
|
setFilteredPeople(people)
|
||||||
|
setFilteredAnimals(nature)
|
||||||
|
setFilteredFood(food)
|
||||||
|
setFilteredTravel(travel)
|
||||||
|
setFilteredActivities(activities)
|
||||||
|
setFilteredObjects(objects)
|
||||||
|
setFilteredSymbols(symbols)
|
||||||
|
setFilteredFlags(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<ClassicInput placeholder="Search..." onChange={handleSearchChange} />
|
||||||
|
<Stack ref={scrollContainer} overflow="scroll" maxH="350px" spacing={4}>
|
||||||
|
{filteredPeople.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
People
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid emojis={filteredPeople} onEmojiClick={onEmojiSelected} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredAnimals.length > 0 && totalDisplayedCategories >= 2 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Animals & Nature
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid
|
||||||
|
emojis={filteredAnimals}
|
||||||
|
onEmojiClick={onEmojiSelected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredFood.length > 0 && totalDisplayedCategories >= 3 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Food & Drink
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid emojis={filteredFood} onEmojiClick={onEmojiSelected} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredTravel.length > 0 && totalDisplayedCategories >= 4 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Travel & Places
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid emojis={filteredTravel} onEmojiClick={onEmojiSelected} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredActivities.length > 0 && totalDisplayedCategories >= 5 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Activities
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid
|
||||||
|
emojis={filteredActivities}
|
||||||
|
onEmojiClick={onEmojiSelected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredObjects.length > 0 && totalDisplayedCategories >= 6 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Objects
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid
|
||||||
|
emojis={filteredObjects}
|
||||||
|
onEmojiClick={onEmojiSelected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredSymbols.length > 0 && totalDisplayedCategories >= 7 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Symbols
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid
|
||||||
|
emojis={filteredSymbols}
|
||||||
|
onEmojiClick={onEmojiSelected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredFlags.length > 0 && totalDisplayedCategories >= 8 && (
|
||||||
|
<Stack>
|
||||||
|
<Text fontSize="sm" pl="2">
|
||||||
|
Flags
|
||||||
|
</Text>
|
||||||
|
<EmojiGrid emojis={filteredFlags} onEmojiClick={onEmojiSelected} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
<div ref={bottomElement} />
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmojiGrid = ({
|
||||||
|
emojis,
|
||||||
|
onEmojiClick,
|
||||||
|
}: {
|
||||||
|
emojis: string[]
|
||||||
|
onEmojiClick: (emoji: string) => void
|
||||||
|
}) => {
|
||||||
|
const handleClick = (emoji: string) => () => onEmojiClick(emoji)
|
||||||
|
return (
|
||||||
|
<SimpleGrid spacing={0} columns={7}>
|
||||||
|
{emojis.map((emoji) => (
|
||||||
|
<GridItem
|
||||||
|
as={Button}
|
||||||
|
onClick={handleClick(emoji)}
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
fontSize="xl"
|
||||||
|
key={emoji}
|
||||||
|
>
|
||||||
|
{emoji}
|
||||||
|
</GridItem>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
)
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,7 @@ export const SearchableDropdown = ({
|
|||||||
const debounced = useDebouncedCallback(
|
const debounced = useDebouncedCallback(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
onValueChange ? onValueChange : () => {},
|
onValueChange ? onValueChange : () => {},
|
||||||
env('E2E_TEST') === 'enabled' ? 0 : debounceTimeout
|
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||||
)
|
)
|
||||||
const [filteredItems, setFilteredItems] = useState([
|
const [filteredItems, setFilteredItems] = useState([
|
||||||
...items
|
...items
|
||||||
|
@ -23,7 +23,7 @@ export const SmartNumberInput = ({
|
|||||||
const [currentValue, setCurrentValue] = useState(value?.toString() ?? '')
|
const [currentValue, setCurrentValue] = useState(value?.toString() ?? '')
|
||||||
const debounced = useDebouncedCallback(
|
const debounced = useDebouncedCallback(
|
||||||
onValueChange,
|
onValueChange,
|
||||||
env('E2E_TEST') === 'enabled' ? 0 : debounceTimeout
|
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
|
@ -36,7 +36,7 @@ export const TextBox = ({
|
|||||||
(value) => {
|
(value) => {
|
||||||
onChange(value)
|
onChange(value)
|
||||||
},
|
},
|
||||||
env('E2E_TEST') === 'enabled' ? 0 : debounceTimeout
|
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { Editable, EditablePreview, EditableInput } from '@chakra-ui/editable'
|
import {
|
||||||
import { Tooltip } from '@chakra-ui/tooltip'
|
Editable,
|
||||||
|
EditablePreview,
|
||||||
|
EditableInput,
|
||||||
|
Tooltip,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
type EditableProps = {
|
type EditableProps = {
|
||||||
|
@ -47,7 +47,7 @@ export const VariableSearchInput = ({
|
|||||||
const variable = variables.find((v) => v.name === value)
|
const variable = variables.find((v) => v.name === value)
|
||||||
if (variable) onSelectVariable(variable)
|
if (variable) onSelectVariable(variable)
|
||||||
},
|
},
|
||||||
env('E2E_TEST') === 'enabled' ? 0 : debounceTimeout
|
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||||
)
|
)
|
||||||
const [filteredItems, setFilteredItems] = useState<Variable[]>(
|
const [filteredItems, setFilteredItems] = useState<Variable[]>(
|
||||||
variables ?? []
|
variables ?? []
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useToast as useChakraToast, UseToastOptions } from '@chakra-ui/toast'
|
import { useToast as useChakraToast, UseToastOptions } from '@chakra-ui/react'
|
||||||
|
|
||||||
export const useToast = () => {
|
export const useToast = () => {
|
||||||
const toast = useChakraToast()
|
const toast = useChakraToast()
|
||||||
|
@ -17,7 +17,7 @@ import { useToast } from 'components/shared/hooks/useToast'
|
|||||||
import { Typebot } from 'models'
|
import { Typebot } from 'models'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||||
import { sendRequest } from 'utils'
|
import { getViewerUrl, sendRequest } from 'utils'
|
||||||
import { TemplateProps, templates } from './data'
|
import { TemplateProps, templates } from './data'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -71,6 +71,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
|
|||||||
</Heading>
|
</Heading>
|
||||||
{typebot && (
|
{typebot && (
|
||||||
<TypebotViewer
|
<TypebotViewer
|
||||||
|
apiHost={getViewerUrl({ isBuilder: true })}
|
||||||
typebot={parseTypebotToPublicTypebot(typebot)}
|
typebot={parseTypebotToPublicTypebot(typebot)}
|
||||||
key={typebot.id}
|
key={typebot.id}
|
||||||
/>
|
/>
|
||||||
|
@ -2,8 +2,9 @@ import { ResultHeaderCell, ResultWithAnswers } from 'models'
|
|||||||
import { createContext, ReactNode, useContext, useMemo } from 'react'
|
import { createContext, ReactNode, useContext, useMemo } from 'react'
|
||||||
import {
|
import {
|
||||||
convertResultsToTableData,
|
convertResultsToTableData,
|
||||||
|
TableData,
|
||||||
useResults as useFetchResults,
|
useResults as useFetchResults,
|
||||||
} from 'services/typebots'
|
} from 'services/typebots/results'
|
||||||
import { KeyedMutator } from 'swr'
|
import { KeyedMutator } from 'swr'
|
||||||
import { isDefined, parseResultHeader } from 'utils'
|
import { isDefined, parseResultHeader } from 'utils'
|
||||||
import { useTypebot } from './TypebotContext'
|
import { useTypebot } from './TypebotContext'
|
||||||
@ -15,10 +16,7 @@ const resultsContext = createContext<{
|
|||||||
resultHeader: ResultHeaderCell[]
|
resultHeader: ResultHeaderCell[]
|
||||||
totalResults: number
|
totalResults: number
|
||||||
totalHiddenResults?: number
|
totalHiddenResults?: number
|
||||||
tableData: {
|
tableData: TableData[]
|
||||||
id: string
|
|
||||||
[key: string]: { plainText: string; element?: JSX.Element } | string
|
|
||||||
}[]
|
|
||||||
onDeleteResults: (totalResultsDeleted: number) => void
|
onDeleteResults: (totalResultsDeleted: number) => void
|
||||||
fetchMore: () => void
|
fetchMore: () => void
|
||||||
mutate: KeyedMutator<
|
mutate: KeyedMutator<
|
||||||
|
@ -150,12 +150,6 @@ export const TypebotContext = ({
|
|||||||
new Date(typebot.updatedAt) >
|
new Date(typebot.updatedAt) >
|
||||||
new Date(currentTypebotRef.current.updatedAt)
|
new Date(currentTypebotRef.current.updatedAt)
|
||||||
) {
|
) {
|
||||||
console.log(
|
|
||||||
'Incoming typebot',
|
|
||||||
typebot,
|
|
||||||
'Current typebot',
|
|
||||||
currentTypebotRef.current
|
|
||||||
)
|
|
||||||
setLocalTypebot({ ...typebot })
|
setLocalTypebot({ ...typebot })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +386,7 @@ export const useFetchedTypebot = ({
|
|||||||
},
|
},
|
||||||
Error
|
Error
|
||||||
>(`/api/typebots/${typebotId}`, fetcher, {
|
>(`/api/typebots/${typebotId}`, fetcher, {
|
||||||
dedupingInterval: env('E2E_TEST') === 'enabled' ? 0 : undefined,
|
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||||
})
|
})
|
||||||
if (error && onError) onError(error)
|
if (error && onError) onError(error)
|
||||||
return {
|
return {
|
||||||
|
@ -14,14 +14,9 @@ const typebotDndContext = createContext<{
|
|||||||
setDraggedTypebot: Dispatch<SetStateAction<TypebotInDashboard | undefined>>
|
setDraggedTypebot: Dispatch<SetStateAction<TypebotInDashboard | undefined>>
|
||||||
mouseOverFolderId?: string | null
|
mouseOverFolderId?: string | null
|
||||||
setMouseOverFolderId: Dispatch<SetStateAction<string | undefined | null>>
|
setMouseOverFolderId: Dispatch<SetStateAction<string | undefined | null>>
|
||||||
}>({
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
setDraggedTypebot: () => {
|
// @ts-ignore
|
||||||
console.log('Not implemented')
|
}>({})
|
||||||
},
|
|
||||||
setMouseOverFolderId: () => {
|
|
||||||
console.log('Not implemented')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const TypebotDndContext = ({ children }: { children: ReactNode }) => {
|
export const TypebotDndContext = ({ children }: { children: ReactNode }) => {
|
||||||
const [draggedTypebot, setDraggedTypebot] = useState<TypebotInDashboard>()
|
const [draggedTypebot, setDraggedTypebot] = useState<TypebotInDashboard>()
|
||||||
|
@ -4,6 +4,7 @@ import React, { useMemo } from 'react'
|
|||||||
import { TypebotViewer } from 'bot-engine'
|
import { TypebotViewer } from 'bot-engine'
|
||||||
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||||
import { SettingsSideMenu } from 'components/settings/SettingsSideMenu'
|
import { SettingsSideMenu } from 'components/settings/SettingsSideMenu'
|
||||||
|
import { getViewerUrl } from 'utils'
|
||||||
|
|
||||||
export const SettingsContent = () => {
|
export const SettingsContent = () => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
@ -17,7 +18,12 @@ export const SettingsContent = () => {
|
|||||||
<Flex h="full" w="full">
|
<Flex h="full" w="full">
|
||||||
<SettingsSideMenu />
|
<SettingsSideMenu />
|
||||||
<Flex flex="1">
|
<Flex flex="1">
|
||||||
{publicTypebot && <TypebotViewer typebot={publicTypebot} />}
|
{publicTypebot && (
|
||||||
|
<TypebotViewer
|
||||||
|
apiHost={getViewerUrl({ isBuilder: true })}
|
||||||
|
typebot={publicTypebot}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import { TypebotViewer } from 'bot-engine'
|
|||||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||||
|
import { getViewerUrl } from 'utils'
|
||||||
import { ThemeSideMenu } from '../../components/theme/ThemeSideMenu'
|
import { ThemeSideMenu } from '../../components/theme/ThemeSideMenu'
|
||||||
|
|
||||||
export const ThemeContent = () => {
|
export const ThemeContent = () => {
|
||||||
@ -12,7 +13,12 @@ export const ThemeContent = () => {
|
|||||||
<Flex h="full" w="full">
|
<Flex h="full" w="full">
|
||||||
<ThemeSideMenu />
|
<ThemeSideMenu />
|
||||||
<Flex flex="1">
|
<Flex flex="1">
|
||||||
{publicTypebot && <TypebotViewer typebot={publicTypebot} />}
|
{publicTypebot && (
|
||||||
|
<TypebotViewer
|
||||||
|
apiHost={getViewerUrl({ isBuilder: true })}
|
||||||
|
typebot={publicTypebot}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
} from '@udecode/plate-basic-marks'
|
} from '@udecode/plate-basic-marks'
|
||||||
import { createPlugins } from '@udecode/plate-core'
|
import { createPlugins } from '@udecode/plate-core'
|
||||||
import { createLinkPlugin, ELEMENT_LINK } from '@udecode/plate-link'
|
import { createLinkPlugin, ELEMENT_LINK } from '@udecode/plate-link'
|
||||||
|
import { PlateFloatingLink } from '@udecode/plate-ui-link'
|
||||||
|
|
||||||
export const editorStyle: React.CSSProperties = {
|
export const editorStyle: React.CSSProperties = {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -18,7 +19,9 @@ export const platePlugins = createPlugins(
|
|||||||
createBoldPlugin(),
|
createBoldPlugin(),
|
||||||
createItalicPlugin(),
|
createItalicPlugin(),
|
||||||
createUnderlinePlugin(),
|
createUnderlinePlugin(),
|
||||||
createLinkPlugin(),
|
createLinkPlugin({
|
||||||
|
renderAfterEditable: PlateFloatingLink,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
|
@ -100,4 +100,5 @@ const components = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const customTheme = extendTheme({ colors, fonts, components })
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const customTheme: any = extendTheme({ colors, fonts, components })
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import { rest, setupWorker } from 'msw'
|
|
||||||
import { setupServer } from 'msw/node'
|
|
||||||
|
|
||||||
const handlers = () => [
|
|
||||||
rest.get('http://localhost:3000/api/auth/session', (req, res, ctx) => {
|
|
||||||
const authenticatedUser = JSON.parse(
|
|
||||||
typeof localStorage !== 'undefined'
|
|
||||||
? (localStorage.getItem('authenticatedUser') as string)
|
|
||||||
: '{"id":"proUser","name":"Pro user","email":"pro-user@email.com","emailVerified":null,"image":"https://avatars.githubusercontent.com/u/16015833?v=4","plan":"PRO","stripeId":null}'
|
|
||||||
)
|
|
||||||
return res(
|
|
||||||
ctx.json({
|
|
||||||
user: authenticatedUser,
|
|
||||||
expires: '2022-03-13T17:02:42.317Z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
export const enableMocks = () => {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
const server = setupServer(...handlers())
|
|
||||||
server.listen()
|
|
||||||
} else {
|
|
||||||
const worker = setupWorker(...handlers())
|
|
||||||
worker.start({
|
|
||||||
onUnhandledRequest: 'bypass',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,20 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
const { withSentryConfig } = require('@sentry/nextjs')
|
const { withSentryConfig } = require('@sentry/nextjs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
const moduleExports = {
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
output: 'standalone',
|
||||||
experimental: {
|
experimental: {
|
||||||
outputStandalone: true,
|
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||||
},
|
},
|
||||||
optimizeFonts: false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sentryWebpackPluginOptions = {
|
const sentryWebpackPluginOptions = {
|
||||||
silent: true,
|
silent: true,
|
||||||
// For all available options, see:
|
|
||||||
// https://github.com/getsentry/sentry-webpack-plugin#options.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = process.env.SENTRY_AUTH_TOKEN
|
module.exports = process.env.SENTRY_AUTH_TOKEN
|
||||||
? withSentryConfig(moduleExports, sentryWebpackPluginOptions)
|
? withSentryConfig(nextConfig, sentryWebpackPluginOptions)
|
||||||
: moduleExports
|
: nextConfig
|
||||||
|
@ -3,112 +3,112 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dx": "yarn dev",
|
"dx": "pnpm dev",
|
||||||
"dev": "ENVSH_ENV=.env.local bash ../../env.sh next dev -p 3000",
|
"dev": "ENVSH_ENV=.env.local bash ../../env.sh next dev -p 3000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "yarn playwright test",
|
"test": "pnpm playwright test",
|
||||||
"test:open": "PWDEBUG=1 yarn playwright test"
|
"test:open": "PWDEBUG=1 pnpm playwright test"
|
||||||
},
|
|
||||||
"msw": {
|
|
||||||
"workerDirectory": "public"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/css-reset": "^2.0.0",
|
"@chakra-ui/css-reset": "2.0.3",
|
||||||
"@chakra-ui/react": "^2.0.0",
|
"@chakra-ui/react": "^2.2.6",
|
||||||
"@codemirror/basic-setup": "^0.20.0",
|
"@codemirror/lang-css": "^6.0.0",
|
||||||
"@codemirror/lang-css": "^0.20.0",
|
"@codemirror/lang-html": "^6.1.0",
|
||||||
"@codemirror/lang-html": "^0.20.0",
|
"@codemirror/lang-javascript": "^6.0.2",
|
||||||
"@codemirror/lang-javascript": "^0.20.0",
|
"@codemirror/lang-json": "^6.0.0",
|
||||||
"@codemirror/lang-json": "^0.20.0",
|
"@codemirror/lint": "^6.0.0",
|
||||||
"@codemirror/text": "^0.19.6",
|
"@codemirror/state": "6.1.1",
|
||||||
"@dnd-kit/core": "^6.0.5",
|
"@dnd-kit/core": "^6.0.5",
|
||||||
"@dnd-kit/sortable": "^7.0.1",
|
"@dnd-kit/sortable": "^7.0.1",
|
||||||
"@emotion/react": "^11.9.0",
|
"@dnd-kit/utilities": "^3.2.0",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/react": "^11.10.0",
|
||||||
"@giphy/js-fetch-api": "^4.1.2",
|
"@emotion/styled": "^11.10.0",
|
||||||
"@giphy/js-types": "^4.1.0",
|
"@giphy/js-fetch-api": "4.4.0",
|
||||||
"@giphy/react-components": "^5.7.0",
|
"@giphy/js-types": "^4.2.1",
|
||||||
"@googleapis/drive": "^2.3.0",
|
"@giphy/react-components": "6.0.0",
|
||||||
"@sentry/nextjs": "^6.19.7",
|
"@googleapis/drive": "^3.0.1",
|
||||||
"@stripe/stripe-js": "^1.29.0",
|
"@sentry/nextjs": "7.9.0",
|
||||||
"@tanstack/react-table": "^8.0.13",
|
"@stripe/stripe-js": "1.35.0",
|
||||||
"@udecode/plate-basic-marks": "^13.1.0",
|
"@tanstack/react-table": "8.5.11",
|
||||||
|
"@udecode/plate-basic-marks": "16.0.0",
|
||||||
"@udecode/plate-common": "^7.0.2",
|
"@udecode/plate-common": "^7.0.2",
|
||||||
"@udecode/plate-core": "^13.1.0",
|
"@udecode/plate-core": "16.0.0",
|
||||||
"@udecode/plate-link": "^13.1.0",
|
"@udecode/plate-link": "16.0.0",
|
||||||
"@udecode/plate-ui-link": "^13.1.0",
|
"@udecode/plate-serializer-html": "16.0.0",
|
||||||
"@udecode/plate-ui-toolbar": "^13.1.0",
|
"@udecode/plate-ui-link": "16.0.0",
|
||||||
"aws-sdk": "^2.1152.0",
|
"@udecode/plate-ui-toolbar": "16.0.0",
|
||||||
"bot-engine": "*",
|
"aws-sdk": "2.1189.0",
|
||||||
|
"bot-engine": "workspace:*",
|
||||||
"browser-image-compression": "^2.0.0",
|
"browser-image-compression": "^2.0.0",
|
||||||
"canvas-confetti": "^1.5.1",
|
"canvas-confetti": "^1.5.1",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
"db": "*",
|
|
||||||
"deep-object-diff": "^1.1.7",
|
"deep-object-diff": "^1.1.7",
|
||||||
"dequal": "^2.0.2",
|
"dequal": "^2.0.3",
|
||||||
"emoji-mart": "^3.0.1",
|
"emojilib": "3.0.7",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "6.3.3",
|
"framer-motion": "7.0.0",
|
||||||
"google-auth-library": "^8.0.2",
|
"google-auth-library": "^8.1.1",
|
||||||
"google-spreadsheet": "^3.2.0",
|
"google-spreadsheet": "^3.3.0",
|
||||||
"got": "^12.0.4",
|
"got": "12.3.1",
|
||||||
"htmlparser2": "^8.0.1",
|
"htmlparser2": "^8.0.1",
|
||||||
"immer": "^9.0.14",
|
"immer": "^9.0.15",
|
||||||
"js-video-url-parser": "^0.5.1",
|
"js-video-url-parser": "^0.5.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"kbar": "^0.1.0-beta.34",
|
"kbar": "^0.1.0-beta.36",
|
||||||
"micro": "^9.3.4",
|
"micro": "9.4.1",
|
||||||
"micro-cors": "^0.1.1",
|
"micro-cors": "^0.1.1",
|
||||||
"minio": "^7.0.28",
|
"minio": "7.0.30",
|
||||||
"models": "*",
|
"next": "12.2.4",
|
||||||
"next": "^12.1.6",
|
"next-auth": "4.10.3",
|
||||||
"next-auth": "4.9.0",
|
"nodemailer": "^6.7.7",
|
||||||
"nodemailer": "^6.7.5",
|
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"papaparse": "^5.3.2",
|
"papaparse": "^5.3.2",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "2.7.1",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.11.0",
|
||||||
"react": "^18.1.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-draggable": "^4.4.5",
|
"react-draggable": "^4.4.5",
|
||||||
"slate": "^0.81.1",
|
"slate": "0.82.0",
|
||||||
"slate-history": "^0.66.0",
|
"slate-history": "^0.66.0",
|
||||||
"slate-hyperscript": "^0.77.0",
|
"slate-hyperscript": "^0.77.0",
|
||||||
"slate-react": "^0.81.0",
|
"slate-react": "0.82.0",
|
||||||
"stripe": "^9.4.0",
|
"stripe": "10.0.0",
|
||||||
"styled-components": "^5.3.5",
|
"styled-components": "^5.3.5",
|
||||||
"svg-round-corners": "^0.3.0",
|
"svg-round-corners": "^0.3.0",
|
||||||
"swr": "^1.3.0",
|
"swr": "^1.3.0",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
"typebot-js": "*",
|
"typebot-js": "workspace:*",
|
||||||
"use-debounce": "^8.0.1",
|
"use-debounce": "8.0.3"
|
||||||
"utils": "*"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.22.0",
|
"@babel/core": "7.18.10",
|
||||||
"@types/aws-sdk": "^2.7.0",
|
"@playwright/test": "1.24.2",
|
||||||
"@types/canvas-confetti": "^1.4.2",
|
"@types/canvas-confetti": "^1.4.3",
|
||||||
"@types/emoji-mart": "^3.0.9",
|
"@types/google-spreadsheet": "^3.3.0",
|
||||||
"@types/google-spreadsheet": "^3.2.1",
|
|
||||||
"@types/jsonwebtoken": "8.5.8",
|
"@types/jsonwebtoken": "8.5.8",
|
||||||
"@types/micro-cors": "^0.1.2",
|
"@types/micro-cors": "^0.1.2",
|
||||||
"@types/minio": "^7.0.13",
|
"@types/minio": "^7.0.13",
|
||||||
"@types/node": "^17.0.33",
|
"@types/node": "18.6.5",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "6.4.5",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/papaparse": "^5.3.2",
|
"@types/papaparse": "5.3.3",
|
||||||
"@types/prettier": "^2.6.1",
|
"@types/prettier": "2.7.0",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react": "^18.0.9",
|
"@types/react": "^18.0.17",
|
||||||
"@types/react-table": "^7.7.12",
|
"@types/react-table": "^7.7.12",
|
||||||
"@types/tinycolor2": "^1.4.3",
|
"@types/tinycolor2": "^1.4.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
"@typescript-eslint/eslint-plugin": "5.33.0",
|
||||||
|
"@typescript-eslint/parser": "5.33.0",
|
||||||
|
"db": "workspace:*",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "8.21.0",
|
||||||
"eslint-config-next": "12.1.6",
|
"eslint-config-next": "12.2.4",
|
||||||
"msw": "^0.43.1",
|
"eslint-plugin-react": "^7.30.1",
|
||||||
"typescript": "^4.6.4"
|
"models": "workspace:*",
|
||||||
|
"typescript": "^4.7.4",
|
||||||
|
"utils": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { customTheme } from 'libs/theme'
|
|||||||
import { useRouterProgressBar } from 'libs/routerProgressBar'
|
import { useRouterProgressBar } from 'libs/routerProgressBar'
|
||||||
import 'assets/styles/routerProgressBar.css'
|
import 'assets/styles/routerProgressBar.css'
|
||||||
import 'assets/styles/plate.css'
|
import 'assets/styles/plate.css'
|
||||||
import 'focus-visible/dist/focus-visible'
|
|
||||||
import 'assets/styles/submissionsTable.css'
|
import 'assets/styles/submissionsTable.css'
|
||||||
import 'assets/styles/codeMirror.css'
|
import 'assets/styles/codeMirror.css'
|
||||||
import 'assets/styles/custom.css'
|
import 'assets/styles/custom.css'
|
||||||
@ -15,15 +14,12 @@ import { TypebotContext } from 'contexts/TypebotContext'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { KBarProvider } from 'kbar'
|
import { KBarProvider } from 'kbar'
|
||||||
import { actions } from 'libs/kbar'
|
import { actions } from 'libs/kbar'
|
||||||
import { enableMocks } from 'mocks'
|
|
||||||
import { SupportBubble } from 'components/shared/SupportBubble'
|
import { SupportBubble } from 'components/shared/SupportBubble'
|
||||||
import { WorkspaceContext } from 'contexts/WorkspaceContext'
|
import { WorkspaceContext } from 'contexts/WorkspaceContext'
|
||||||
import { toTitleCase } from 'utils'
|
import { toTitleCase } from 'utils'
|
||||||
|
|
||||||
const { ToastContainer, toast } = createStandaloneToast(customTheme)
|
const { ToastContainer, toast } = createStandaloneToast(customTheme)
|
||||||
|
|
||||||
if (process.env.NEXT_PUBLIC_E2E_TEST === 'enabled') enableMocks()
|
|
||||||
|
|
||||||
const App = ({ Component, pageProps: { session, ...pageProps } }: AppProps) => {
|
const App = ({ Component, pageProps: { session, ...pageProps } }: AppProps) => {
|
||||||
useRouterProgressBar()
|
useRouterProgressBar()
|
||||||
const { query, pathname, isReady } = useRouter()
|
const { query, pathname, isReady } = useRouter()
|
||||||
@ -70,6 +66,7 @@ const App = ({ Component, pageProps: { session, ...pageProps } }: AppProps) => {
|
|||||||
|
|
||||||
const displayStripeCallbackMessage = (
|
const displayStripeCallbackMessage = (
|
||||||
status: string | undefined,
|
status: string | undefined,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
toast: any
|
toast: any
|
||||||
) => {
|
) => {
|
||||||
if (status && ['pro', 'team'].includes(status)) {
|
if (status && ['pro', 'team'].includes(status)) {
|
||||||
|
@ -15,7 +15,10 @@ import { env, isNotEmpty } from 'utils'
|
|||||||
|
|
||||||
const providers: Provider[] = []
|
const providers: Provider[] = []
|
||||||
|
|
||||||
if (isNotEmpty(process.env.GITHUB_CLIENT_ID))
|
if (
|
||||||
|
isNotEmpty(process.env.GITHUB_CLIENT_ID) &&
|
||||||
|
isNotEmpty(process.env.GITHUB_CLIENT_SECRET)
|
||||||
|
)
|
||||||
providers.push(
|
providers.push(
|
||||||
GitHubProvider({
|
GitHubProvider({
|
||||||
clientId: process.env.GITHUB_CLIENT_ID,
|
clientId: process.env.GITHUB_CLIENT_ID,
|
||||||
|
@ -10,7 +10,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const workspaceId = req.query.workspaceId as string | undefined
|
const workspaceId = req.query.workspaceId as string | undefined
|
||||||
if (!workspaceId) return badRequest(res)
|
if (!workspaceId) return badRequest(res)
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const credentialsId = req.query.credentialsId.toString()
|
const credentialsId = req.query.credentialsId as string | undefined
|
||||||
const credentials = await prisma.credentials.deleteMany({
|
const credentials = await prisma.credentials.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
id: credentialsId,
|
id: credentialsId,
|
||||||
|
@ -12,8 +12,10 @@ import { getAuthenticatedUser } from 'services/api/utils'
|
|||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
|
const state = req.query.state as string | undefined
|
||||||
|
if (!state) return badRequest(res)
|
||||||
const { redirectUrl, blockId, workspaceId } = JSON.parse(
|
const { redirectUrl, blockId, workspaceId } = JSON.parse(
|
||||||
Buffer.from(req.query.state.toString(), 'base64').toString()
|
Buffer.from(state, 'base64').toString()
|
||||||
)
|
)
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const code = req.query.code as string | undefined
|
const code = req.query.code as string | undefined
|
||||||
|
@ -11,7 +11,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const workspaceId = req.query.workspaceId as string | undefined
|
const workspaceId = req.query.workspaceId as string | undefined
|
||||||
if (!workspaceId) return badRequest(res)
|
if (!workspaceId) return badRequest(res)
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const domain = req.query.domain.toString()
|
const domain = req.query.domain as string
|
||||||
try {
|
try {
|
||||||
await deleteDomainOnVercel(domain)
|
await deleteDomainOnVercel(domain)
|
||||||
} catch {}
|
} catch {}
|
||||||
|
@ -9,7 +9,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
|
|
||||||
const id = req.query.id.toString()
|
const id = req.query.id as string
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const folder = await prisma.dashboardFolder.findFirst({
|
const folder = await prisma.dashboardFolder.findFirst({
|
||||||
where: {
|
where: {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { drive } from '@googleapis/drive'
|
import { drive } from '@googleapis/drive'
|
||||||
import { OAuth2Client } from 'googleapis-common'
|
|
||||||
import { getAuthenticatedGoogleClient } from 'libs/google-sheets'
|
import { getAuthenticatedGoogleClient } from 'libs/google-sheets'
|
||||||
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'
|
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'
|
||||||
import { setUser, withSentry } from '@sentry/nextjs'
|
import { setUser, withSentry } from '@sentry/nextjs'
|
||||||
@ -19,7 +18,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
return res.status(404).send("Couldn't find credentials in database")
|
return res.status(404).send("Couldn't find credentials in database")
|
||||||
const response = await drive({
|
const response = await drive({
|
||||||
version: 'v3',
|
version: 'v3',
|
||||||
auth: auth.client as unknown as OAuth2Client,
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
auth: auth.client,
|
||||||
}).files.list({
|
}).files.list({
|
||||||
q: "mimeType='application/vnd.google-apps.spreadsheet'",
|
q: "mimeType='application/vnd.google-apps.spreadsheet'",
|
||||||
fields: 'nextPageToken, files(id, name)',
|
fields: 'nextPageToken, files(id, name)',
|
||||||
|
@ -4,8 +4,8 @@ import { methodNotAllowed } from 'utils'
|
|||||||
|
|
||||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const firstParam = req.query.firstParam.toString()
|
const firstParam = req.query.firstParam?.toString()
|
||||||
const secondParam = req.query.secondParam.toString()
|
const secondParam = req.query.secondParam?.toString()
|
||||||
const customHeader = req.headers['custom-typebot']
|
const customHeader = req.headers['custom-typebot']
|
||||||
const { body } = req
|
const { body } = req
|
||||||
if (
|
if (
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { withSentry } from '@sentry/nextjs'
|
import { withSentry } from '@sentry/nextjs'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { getSession } from 'next-auth/react'
|
import { getAuthenticatedUser } from 'services/api/utils'
|
||||||
import { badRequest, generatePresignedUrl, methodNotAllowed } from 'utils'
|
import {
|
||||||
|
badRequest,
|
||||||
|
generatePresignedUrl,
|
||||||
|
methodNotAllowed,
|
||||||
|
notAuthenticated,
|
||||||
|
} from 'utils'
|
||||||
|
|
||||||
const handler = async (
|
const handler = async (
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
@ -9,11 +14,8 @@ const handler = async (
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*')
|
res.setHeader('Access-Control-Allow-Origin', '*')
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const session = await getSession({ req })
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!session) {
|
if (!user) return notAuthenticated(res)
|
||||||
res.status(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!process.env.S3_ENDPOINT ||
|
!process.env.S3_ENDPOINT ||
|
||||||
|
@ -8,7 +8,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (!process.env.STRIPE_SECRET_KEY)
|
if (!process.env.STRIPE_SECRET_KEY)
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
throw Error('STRIPE_SECRET_KEY var is missing')
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||||
apiVersion: '2020-08-27',
|
apiVersion: '2022-08-01',
|
||||||
})
|
})
|
||||||
const { email, currency, plan, workspaceId, href } =
|
const { email, currency, plan, workspaceId, href } =
|
||||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||||
|
@ -27,7 +27,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
})
|
})
|
||||||
if (!workspace?.stripeId) return forbidden(res)
|
if (!workspace?.stripeId) return forbidden(res)
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||||
apiVersion: '2020-08-27',
|
apiVersion: '2022-08-01',
|
||||||
})
|
})
|
||||||
const session = await stripe.billingPortal.sessions.create({
|
const session = await stripe.billingPortal.sessions.create({
|
||||||
customer: workspace.stripeId,
|
customer: workspace.stripeId,
|
||||||
|
@ -13,7 +13,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (!process.env.STRIPE_SECRET_KEY)
|
if (!process.env.STRIPE_SECRET_KEY)
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
throw Error('STRIPE_SECRET_KEY var is missing')
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||||
apiVersion: '2020-08-27',
|
apiVersion: '2022-08-01',
|
||||||
})
|
})
|
||||||
const subscriptions = await stripe.subscriptions.list({
|
const subscriptions = await stripe.subscriptions.list({
|
||||||
customer: customerId,
|
customer: customerId,
|
||||||
|
@ -10,7 +10,7 @@ import { withSentry } from '@sentry/nextjs'
|
|||||||
if (!process.env.STRIPE_SECRET_KEY || !process.env.STRIPE_WEBHOOK_SECRET)
|
if (!process.env.STRIPE_SECRET_KEY || !process.env.STRIPE_WEBHOOK_SECRET)
|
||||||
throw new Error('STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET missing')
|
throw new Error('STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET missing')
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||||
apiVersion: '2020-08-27',
|
apiVersion: '2022-08-01',
|
||||||
})
|
})
|
||||||
|
|
||||||
const cors = Cors({
|
const cors = Cors({
|
||||||
|
@ -10,7 +10,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
|
|
||||||
const typebotId = req.query.typebotId.toString()
|
const typebotId = req.query.typebotId as string
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = await prisma.typebot.findFirst({
|
||||||
where: canReadTypebot(typebotId, user),
|
where: canReadTypebot(typebotId, user),
|
||||||
|
@ -66,7 +66,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
await prisma.invitation.create({
|
await prisma.invitation.create({
|
||||||
data: { email: email.toLowerCase().trim(), type, typebotId },
|
data: { email: email.toLowerCase().trim(), type, typebotId },
|
||||||
})
|
})
|
||||||
if (env('E2E_TEST') !== 'enabled')
|
if (env('E2E_TEST') !== 'true')
|
||||||
await sendEmailNotification({
|
await sendEmailNotification({
|
||||||
to: email,
|
to: email,
|
||||||
subject: "You've been invited to collaborate 🤝",
|
subject: "You've been invited to collaborate 🤝",
|
||||||
|
@ -23,9 +23,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
})
|
})
|
||||||
if (!workspace) return forbidden(res)
|
if (!workspace) return forbidden(res)
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const typebotId = req.query.typebotId.toString()
|
const typebotId = req.query.typebotId as string
|
||||||
const lastResultId = req.query.lastResultId?.toString()
|
const lastResultId = req.query.lastResultId?.toString()
|
||||||
const take = parseInt(req.query.limit?.toString())
|
const take = Number(req.query.limit?.toString())
|
||||||
const results = await prisma.result.findMany({
|
const results = await prisma.result.findMany({
|
||||||
take: isNaN(take) ? undefined : take,
|
take: isNaN(take) ? undefined : take,
|
||||||
skip: lastResultId ? 1 : 0,
|
skip: lastResultId ? 1 : 0,
|
||||||
|
@ -10,7 +10,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const typebotId = req.query.typebotId.toString()
|
const typebotId = req.query.typebotId as string
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = await prisma.typebot.findFirst({
|
||||||
where: canReadTypebot(typebotId, user),
|
where: canReadTypebot(typebotId, user),
|
||||||
include: { publishedTypebot: true },
|
include: { publishedTypebot: true },
|
||||||
|
@ -10,7 +10,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const typebotId = req.query.typebotId.toString()
|
const typebotId = req.query.typebotId as string
|
||||||
|
|
||||||
const totalViews = await prisma.result.count({
|
const totalViews = await prisma.result.count({
|
||||||
where: {
|
where: {
|
||||||
|
@ -8,7 +8,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
|
|
||||||
const id = req.query.userId.toString()
|
const id = req.query.userId as string
|
||||||
if (req.method === 'PUT') {
|
if (req.method === 'PUT') {
|
||||||
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||||
const typebots = await prisma.user.update({
|
const typebots = await prisma.user.update({
|
||||||
|
@ -9,7 +9,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
|
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const id = req.query.tokenId.toString()
|
const id = req.query.tokenId as string
|
||||||
const apiToken = await prisma.apiToken.delete({
|
const apiToken = await prisma.apiToken.delete({
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Stack, Text, VStack } from '@chakra-ui/layout'
|
|
||||||
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
|
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { FolderContent } from 'components/dashboard/FolderContent'
|
import { FolderContent } from 'components/dashboard/FolderContent'
|
||||||
import { TypebotDndContext } from 'contexts/TypebotDndContext'
|
import { TypebotDndContext } from 'contexts/TypebotDndContext'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Spinner } from '@chakra-ui/react'
|
import { Spinner, Stack, Text, VStack } from '@chakra-ui/react'
|
||||||
import { pay } from 'services/stripe'
|
import { pay } from 'services/stripe'
|
||||||
import { useUser } from 'contexts/UserContext'
|
import { useUser } from 'contexts/UserContext'
|
||||||
import { NextPageContext } from 'next/types'
|
import { NextPageContext } from 'next/types'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Flex } from '@chakra-ui/layout'
|
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
import {
|
import {
|
||||||
@ -16,7 +15,7 @@ import { GraphProvider, GroupsCoordinatesProvider } from 'contexts/GraphContext'
|
|||||||
import { GraphDndContext } from 'contexts/GraphDndContext'
|
import { GraphDndContext } from 'contexts/GraphDndContext'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { GettingStartedModal } from 'components/editor/GettingStartedModal'
|
import { GettingStartedModal } from 'components/editor/GettingStartedModal'
|
||||||
import { Spinner } from '@chakra-ui/react'
|
import { Spinner, Flex } from '@chakra-ui/react'
|
||||||
|
|
||||||
const TypebotEditPage = () => {
|
const TypebotEditPage = () => {
|
||||||
const { typebot, isReadOnly } = useTypebot()
|
const { typebot, isReadOnly } = useTypebot()
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Flex, Text } from '@chakra-ui/layout'
|
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { HStack, Button, Tag } from '@chakra-ui/react'
|
import { HStack, Button, Tag, Flex, Text } from '@chakra-ui/react'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||||
import { ResultsContent } from 'components/results/ResultsContent'
|
import { ResultsContent } from 'components/results/ResultsContent'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
@ -34,7 +33,7 @@ const ResultsPage = () => {
|
|||||||
stats: { ...stats, totalStarts: stats.totalStarts - total },
|
stats: { ...stats, totalStarts: stats.totalStarts - total },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex overflow="hidden" h="100vh" flexDir="column">
|
<Flex overflow="hidden" h="100vh" flexDir="column">
|
||||||
<Seo
|
<Seo
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/layout'
|
import { Flex } from '@chakra-ui/react'
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { SettingsContent } from 'layouts/settings/SettingsContent'
|
import { SettingsContent } from 'layouts/settings/SettingsContent'
|
||||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/layout'
|
import { Flex } from '@chakra-ui/react'
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { ShareContent } from 'components/share/ShareContent'
|
import { ShareContent } from 'components/share/ShareContent'
|
||||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/layout'
|
import { Flex } from '@chakra-ui/react'
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
import { ThemeContent } from 'layouts/theme/ThemeContent'
|
import { ThemeContent } from 'layouts/theme/ThemeContent'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Flex, Stack } from '@chakra-ui/layout'
|
import { Flex, Stack } from '@chakra-ui/react'
|
||||||
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
|
import { DashboardHeader } from 'components/dashboard/DashboardHeader'
|
||||||
import { Seo } from 'components/Seo'
|
import { Seo } from 'components/Seo'
|
||||||
import { FolderContent } from 'components/dashboard/FolderContent'
|
import { FolderContent } from 'components/dashboard/FolderContent'
|
||||||
|
@ -5,8 +5,6 @@ import path from 'path'
|
|||||||
require('dotenv').config({
|
require('dotenv').config({
|
||||||
path: path.join(__dirname, 'playwright/.env'),
|
path: path.join(__dirname, 'playwright/.env'),
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
require('dotenv').config({ path: path.join(__dirname, '.env.local') })
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
PLAYWRIGHT_BUILDER_TEST_BASE_URL=http://localhost:3000
|
PLAYWRIGHT_BUILDER_TEST_BASE_URL=http://localhost:3000
|
||||||
|
DATABASE_URL=postgresql://postgres:typebot@localhost:5432/typebot
|
||||||
|
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||||
|
|
||||||
# SMTP Credentials (Generated on https://ethereal.email/)
|
# SMTP Credentials (Generated on https://ethereal.email/)
|
||||||
SMTP_HOST=smtp.ethereal.email
|
SMTP_HOST=smtp.ethereal.email
|
||||||
|
@ -1,5 +1,18 @@
|
|||||||
|
import { Page } from '@playwright/test'
|
||||||
|
|
||||||
export const refreshUser = async () => {
|
export const refreshUser = async () => {
|
||||||
await fetch('/api/auth/session?update')
|
await fetch('/api/auth/session?update')
|
||||||
const event = new Event('visibilitychange')
|
const event = new Event('visibilitychange')
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mockSessionApiCalls = (page: Page) =>
|
||||||
|
page.route('/api/auth/session', (route) => {
|
||||||
|
if (route.request().method() === 'GET') {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
body: '{"user":{"id":"proUser","name":"Pro user","email":"pro-user@email.com","emailVerified":null,"image":"https://avatars.githubusercontent.com/u/16015833?v=4","stripeId":null,"graphNavigation": "TRACKPAD"}}',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return route.continue()
|
||||||
|
})
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import test, { expect } from '@playwright/test'
|
import test, { expect } from '@playwright/test'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
// Can't test the update features because of the auth mocking.
|
// Can't test the update features because of the auth mocking.
|
||||||
test('should display user info properly', async ({ page }) => {
|
test('should display user info properly', async ({ page }) => {
|
||||||
@ -7,7 +10,7 @@ test('should display user info properly', async ({ page }) => {
|
|||||||
await page.click('text=Settings & Members')
|
await page.click('text=Settings & Members')
|
||||||
const saveButton = page.locator('button:has-text("Save")')
|
const saveButton = page.locator('button:has-text("Save")')
|
||||||
await expect(saveButton).toBeHidden()
|
await expect(saveButton).toBeHidden()
|
||||||
await expect(
|
expect(
|
||||||
page.locator('input[type="email"]').getAttribute('disabled')
|
page.locator('input[type="email"]').getAttribute('disabled')
|
||||||
).toBeDefined()
|
).toBeDefined()
|
||||||
await page.fill('#name', 'John Doe')
|
await page.fill('#name', 'John Doe')
|
||||||
|
@ -6,11 +6,13 @@ import {
|
|||||||
import { BubbleBlockType, defaultEmbedBubbleContent } from 'models'
|
import { BubbleBlockType, defaultEmbedBubbleContent } from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
const pdfSrc = 'https://www.orimi.com/pdf-test.pdf'
|
const pdfSrc = 'https://www.orimi.com/pdf-test.pdf'
|
||||||
const iframeCode = '<iframe src="https://typebot.io"></iframe>'
|
|
||||||
const siteSrc = 'https://app.cal.com/baptistearno/15min'
|
const siteSrc = 'https://app.cal.com/baptistearno/15min'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.parallel('Embed bubble block', () => {
|
test.describe.parallel('Embed bubble block', () => {
|
||||||
test.describe('Content settings', () => {
|
test.describe('Content settings', () => {
|
||||||
test('should import and parse embed correctly', async ({ page }) => {
|
test('should import and parse embed correctly', async ({ page }) => {
|
||||||
|
@ -7,10 +7,13 @@ import { BubbleBlockType, defaultImageBubbleContent } from 'models'
|
|||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
const unsplashImageSrc =
|
const unsplashImageSrc =
|
||||||
'https://images.unsplash.com/photo-1504297050568-910d24c426d3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80'
|
'https://images.unsplash.com/photo-1504297050568-910d24c426d3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.parallel('Image bubble block', () => {
|
test.describe.parallel('Image bubble block', () => {
|
||||||
test.describe('Content settings', () => {
|
test.describe('Content settings', () => {
|
||||||
test('should upload image file correctly', async ({ page }) => {
|
test('should upload image file correctly', async ({ page }) => {
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
import { BubbleBlockType, defaultTextBubbleContent } from 'models'
|
import { BubbleBlockType, defaultTextBubbleContent } from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Text bubble block', () => {
|
test.describe('Text bubble block', () => {
|
||||||
test('rich text features should work', async ({ page }) => {
|
test('rich text features should work', async ({ page }) => {
|
||||||
@ -42,15 +45,13 @@ test.describe('Text bubble block', () => {
|
|||||||
await page.press('div[role="textbox"]', 'Shift+Enter')
|
await page.press('div[role="textbox"]', 'Shift+Enter')
|
||||||
|
|
||||||
await page.type('div[role="textbox"]', 'My super link')
|
await page.type('div[role="textbox"]', 'My super link')
|
||||||
|
await page.waitForTimeout(300)
|
||||||
await page.press('div[role="textbox"]', 'Shift+Meta+ArrowLeft')
|
await page.press('div[role="textbox"]', 'Shift+Meta+ArrowLeft')
|
||||||
await page.waitForTimeout(200)
|
|
||||||
page.on('dialog', async (dialog) => {
|
|
||||||
await dialog.accept('https://github.com')
|
|
||||||
})
|
|
||||||
await page.click('[data-testid="link-button"]')
|
await page.click('[data-testid="link-button"]')
|
||||||
|
await page.fill('input[placeholder="Paste link"]', 'https://github.com')
|
||||||
|
await page.press('input[placeholder="Paste link"]', 'Enter')
|
||||||
await page.press('div[role="textbox"]', 'Shift+Enter')
|
await page.press('div[role="textbox"]', 'Shift+Enter')
|
||||||
await page.click('button >> text=Variables')
|
await page.click('button[aria-label="Insert variable"]')
|
||||||
await page.fill('[data-testid="variables-input"]', 'test')
|
await page.fill('[data-testid="variables-input"]', 'test')
|
||||||
await page.click('text=Create "test"')
|
await page.click('text=Create "test"')
|
||||||
|
|
||||||
|
@ -10,12 +10,15 @@ import {
|
|||||||
} from 'models'
|
} from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
const videoSrc =
|
const videoSrc =
|
||||||
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4'
|
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4'
|
||||||
const youtubeVideoSrc = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
|
const youtubeVideoSrc = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
|
||||||
const vimeoVideoSrc = 'https://vimeo.com/649301125'
|
const vimeoVideoSrc = 'https://vimeo.com/649301125'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.parallel('Video bubble block', () => {
|
test.describe.parallel('Video bubble block', () => {
|
||||||
test.describe('Content settings', () => {
|
test.describe('Content settings', () => {
|
||||||
test('should import video url correctly', async ({ page }) => {
|
test('should import video url correctly', async ({ page }) => {
|
||||||
|
@ -3,6 +3,7 @@ import cuid from 'cuid'
|
|||||||
import { CollaborationType, Plan, WorkspaceRole } from 'db'
|
import { CollaborationType, Plan, WorkspaceRole } from 'db'
|
||||||
import prisma from 'libs/prisma'
|
import prisma from 'libs/prisma'
|
||||||
import { InputBlockType, defaultTextInputOptions } from 'models'
|
import { InputBlockType, defaultTextInputOptions } from 'models'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
import {
|
import {
|
||||||
createFolder,
|
createFolder,
|
||||||
createResults,
|
createResults,
|
||||||
@ -10,6 +11,8 @@ import {
|
|||||||
parseDefaultGroupWithBlock,
|
parseDefaultGroupWithBlock,
|
||||||
} from '../services/database'
|
} from '../services/database'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Typebot owner', () => {
|
test.describe('Typebot owner', () => {
|
||||||
test('Can invite collaborators', async ({ page }) => {
|
test('Can invite collaborators', async ({ page }) => {
|
||||||
const typebotId = cuid()
|
const typebotId = cuid()
|
||||||
|
@ -7,6 +7,9 @@ import {
|
|||||||
} from '../services/database'
|
} from '../services/database'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test('should be able to connect custom domain', async ({ page }) => {
|
test('should be able to connect custom domain', async ({ page }) => {
|
||||||
const typebotId = cuid()
|
const typebotId = cuid()
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import test, { expect, Page } from '@playwright/test'
|
import test, { expect, Page } from '@playwright/test'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
import { createFolders, createTypebots } from '../services/database'
|
import { createFolders, createTypebots } from '../services/database'
|
||||||
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
|
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Dashboard page', () => {
|
test.describe('Dashboard page', () => {
|
||||||
test('folders navigation should work', async ({ page }) => {
|
test('folders navigation should work', async ({ page }) => {
|
||||||
await page.goto('/typebots')
|
await page.goto('/typebots')
|
||||||
|
@ -8,6 +8,9 @@ import { defaultTextInputOptions, InputBlockType } from 'models'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import { typebotViewer } from '../services/selectorUtils'
|
import { typebotViewer } from '../services/selectorUtils'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.parallel('Editor', () => {
|
test.describe.parallel('Editor', () => {
|
||||||
test('Edges connection should work', async ({ page }) => {
|
test('Edges connection should work', async ({ page }) => {
|
||||||
|
@ -8,6 +8,9 @@ import { defaultChoiceInputOptions, InputBlockType, ItemType } from 'models'
|
|||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.parallel('Buttons input block', () => {
|
test.describe.parallel('Buttons input block', () => {
|
||||||
test('can edit button items', async ({ page }) => {
|
test('can edit button items', async ({ page }) => {
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
import { defaultDateInputOptions, InputBlockType } from 'models'
|
import { defaultDateInputOptions, InputBlockType } from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Date input block', () => {
|
test.describe('Date input block', () => {
|
||||||
test('options should work', async ({ page }) => {
|
test('options should work', async ({ page }) => {
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
import { defaultEmailInputOptions, InputBlockType } from 'models'
|
import { defaultEmailInputOptions, InputBlockType } from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Email input block', () => {
|
test.describe('Email input block', () => {
|
||||||
test('options should work', async ({ page }) => {
|
test('options should work', async ({ page }) => {
|
||||||
@ -31,7 +34,10 @@ test.describe('Email input block', () => {
|
|||||||
await expect(typebotViewer(page).locator(`button`)).toBeDisabled()
|
await expect(typebotViewer(page).locator(`button`)).toBeDisabled()
|
||||||
|
|
||||||
await page.click(`text=${defaultEmailInputOptions.labels.placeholder}`)
|
await page.click(`text=${defaultEmailInputOptions.labels.placeholder}`)
|
||||||
await page.fill('#placeholder', 'Your email...')
|
await page.fill(
|
||||||
|
`input[value="${defaultEmailInputOptions.labels.placeholder}"]`,
|
||||||
|
'Your email...'
|
||||||
|
)
|
||||||
await expect(page.locator('text=Your email...')).toBeVisible()
|
await expect(page.locator('text=Your email...')).toBeVisible()
|
||||||
await page.fill('#button', 'Go')
|
await page.fill('#button', 'Go')
|
||||||
await page.fill(
|
await page.fill(
|
||||||
|
@ -8,6 +8,9 @@ import { defaultFileInputOptions, InputBlockType } from 'models'
|
|||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe.configure({ mode: 'parallel' })
|
test.describe.configure({ mode: 'parallel' })
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
import { defaultNumberInputOptions, InputBlockType } from 'models'
|
import { defaultNumberInputOptions, InputBlockType } from 'models'
|
||||||
import { typebotViewer } from '../../services/selectorUtils'
|
import { typebotViewer } from '../../services/selectorUtils'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Number input block', () => {
|
test.describe('Number input block', () => {
|
||||||
test('options should work', async ({ page }) => {
|
test('options should work', async ({ page }) => {
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
import { defaultPaymentInputOptions, InputBlockType } from 'models'
|
import { defaultPaymentInputOptions, InputBlockType } from 'models'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import { stripePaymentForm, typebotViewer } from '../../services/selectorUtils'
|
import { stripePaymentForm, typebotViewer } from '../../services/selectorUtils'
|
||||||
|
import { mockSessionApiCalls } from 'playwright/services/browser'
|
||||||
|
|
||||||
|
test.beforeEach(({ page }) => mockSessionApiCalls(page))
|
||||||
|
|
||||||
test.describe('Payment input block', () => {
|
test.describe('Payment input block', () => {
|
||||||
test('Can configure Stripe account', async ({ page }) => {
|
test('Can configure Stripe account', async ({ page }) => {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user