diff --git a/apps/builder/src/components/Toaster.tsx b/apps/builder/src/components/Toaster.tsx
new file mode 100644
index 000000000..4a8d04cc7
--- /dev/null
+++ b/apps/builder/src/components/Toaster.tsx
@@ -0,0 +1,33 @@
+import { colors } from '@/lib/theme'
+import { useColorMode, useColorModeValue } from '@chakra-ui/react'
+import { Toaster as SonnerToaster } from 'sonner'
+
+export const Toaster = () => {
+ const { colorMode } = useColorMode()
+ const theme = useColorModeValue(
+ {
+ bg: undefined,
+ actionBg: colors.blue[500],
+ actionColor: undefined,
+ },
+ {
+ bg: colors.gray[900],
+ actionBg: colors.blue[400],
+ actionColor: 'white',
+ }
+ )
+ return (
+
+ )
+}
diff --git a/apps/builder/src/components/icons.tsx b/apps/builder/src/components/icons.tsx
index 87b4ce210..aea324d2b 100644
--- a/apps/builder/src/components/icons.tsx
+++ b/apps/builder/src/components/icons.tsx
@@ -643,3 +643,11 @@ export const XCircleIcon = (props: IconProps) => (
)
+
+export const LightBulbIcon = (props: IconProps) => (
+
+
+
+
+
+)
diff --git a/apps/builder/src/features/account/UserProvider.tsx b/apps/builder/src/features/account/UserProvider.tsx
index df4edefe7..00e1e2b94 100644
--- a/apps/builder/src/features/account/UserProvider.tsx
+++ b/apps/builder/src/features/account/UserProvider.tsx
@@ -2,7 +2,7 @@ import { signOut, useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
import { createContext, ReactNode, useEffect, useState } from 'react'
import { isDefined, isNotDefined } from '@typebot.io/lib'
-import { User } from '@typebot.io/prisma'
+import { User } from '@typebot.io/schemas'
import { setUser as setSentryUser } from '@sentry/nextjs'
import { useToast } from '@/hooks/useToast'
import { updateUserQuery } from './queries/updateUserQuery'
diff --git a/apps/builder/src/features/editor/components/BoardMenuButton.tsx b/apps/builder/src/features/editor/components/BoardMenuButton.tsx
index 750283845..62b0c30a4 100644
--- a/apps/builder/src/features/editor/components/BoardMenuButton.tsx
+++ b/apps/builder/src/features/editor/components/BoardMenuButton.tsx
@@ -17,25 +17,17 @@ import {
SettingsIcon,
} from '@/components/icons'
import { useTypebot } from '../providers/TypebotProvider'
-import { useUser } from '@/features/account/hooks/useUser'
-import { useRouter } from 'next/router'
-import React, { useEffect, useState } from 'react'
+import React, { useState } from 'react'
import { EditorSettingsModal } from './EditorSettingsModal'
import { parseDefaultPublicId } from '@/features/publish/helpers/parseDefaultPublicId'
import { useTranslate } from '@tolgee/react'
export const BoardMenuButton = (props: FlexProps) => {
- const { query } = useRouter()
const { typebot } = useTypebot()
- const { user } = useUser()
const [isDownloading, setIsDownloading] = useState(false)
const { isOpen, onOpen, onClose } = useDisclosure()
const { t } = useTranslate()
- useEffect(() => {
- if (user && !user.graphNavigation && !query.isFirstBot) onOpen()
- }, [onOpen, query.isFirstBot, user, user?.graphNavigation])
-
const downloadFlow = () => {
assert(typebot)
setIsDownloading(true)
@@ -54,7 +46,7 @@ export const BoardMenuButton = (props: FlexProps) => {
}
const redirectToDocumentation = () =>
- window.open('https://docs.typebot.io/get-started/overview', '_blank')
+ window.open('https://docs.typebot.io/editor/graph', '_blank')
return (
{
+ window.open('https://docs.typebot.io/editor/graph', '_blank')
+ },
+ },
+ duration: 30000,
+ icon: ,
+ })
+ updateUser({
+ displayedInAppNotifications: {
+ ...user.displayedInAppNotifications,
+ [graphGestureNotficationKey]: true,
+ },
+ })
}
setSelectBoxCoordinates(undefined)
setOpenedBlockId(undefined)
diff --git a/apps/builder/src/pages/_app.tsx b/apps/builder/src/pages/_app.tsx
index fff4fea00..9ad8542b7 100644
--- a/apps/builder/src/pages/_app.tsx
+++ b/apps/builder/src/pages/_app.tsx
@@ -22,7 +22,7 @@ import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
import { initPostHogIfEnabled } from '@/features/telemetry/posthog'
import { TolgeeProvider, useTolgeeSSR } from '@tolgee/react'
import { tolgee } from '@/lib/tolgee'
-import { Toaster } from 'sonner'
+import { Toaster } from '@/components/Toaster'
initPostHogIfEnabled()
@@ -63,8 +63,8 @@ const App = ({ Component, pageProps }: AppProps) => {
return (
-
+
diff --git a/apps/builder/src/pages/api/users/[userId].ts b/apps/builder/src/pages/api/users/[userId].ts
index 2de569a65..4276d4b3b 100644
--- a/apps/builder/src/pages/api/users/[userId].ts
+++ b/apps/builder/src/pages/api/users/[userId].ts
@@ -2,7 +2,7 @@ import prisma from '@typebot.io/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import { methodNotAllowed, notAuthenticated } from '@typebot.io/lib/api'
-import { User } from '@typebot.io/prisma'
+import { Prisma, User } from '@typebot.io/prisma'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req, res)
@@ -18,6 +18,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
data: {
...data,
onboardingCategories: data.onboardingCategories ?? [],
+ displayedInAppNotifications:
+ data.displayedInAppNotifications ?? Prisma.DbNull,
},
})
return res.send({ typebots })
diff --git a/apps/docs/mint.json b/apps/docs/mint.json
index d1d4cb9d9..e035d52c0 100644
--- a/apps/docs/mint.json
+++ b/apps/docs/mint.json
@@ -64,15 +64,6 @@
"group": "Get Started",
"pages": ["get-started/introduction", "get-started/overview"]
},
- {
- "group": "Workspace",
- "pages": [
- "workspace",
- "workspace/add-members-and-guests",
- "workspace/subscription",
- "workspace/preferences"
- ]
- },
{
"group": "Flow",
"pages": [
@@ -191,6 +182,15 @@
"group": "Results",
"pages": ["results/overview", "results/analytics"]
},
+ {
+ "group": "Workspace",
+ "pages": [
+ "workspace",
+ "workspace/add-members-and-guests",
+ "workspace/subscription",
+ "workspace/preferences"
+ ]
+ },
{
"group": "Guides",
"pages": [
diff --git a/packages/lib/mockedUser.ts b/packages/lib/mockedUser.ts
index 944815c5e..b7cfea491 100644
--- a/packages/lib/mockedUser.ts
+++ b/packages/lib/mockedUser.ts
@@ -1,4 +1,5 @@
import { User } from '@typebot.io/prisma'
+import { graphGestureNotficationKey } from '@typebot.io/schemas/features/user/constants'
export const mockedUser: User = {
id: 'userId',
@@ -13,4 +14,7 @@ export const mockedUser: User = {
lastActivityAt: new Date('2022-01-01'),
onboardingCategories: [],
updatedAt: new Date('2022-01-01'),
+ displayedInAppNotifications: {
+ [graphGestureNotficationKey]: true,
+ },
}
diff --git a/packages/lib/playwright/databaseActions.ts b/packages/lib/playwright/databaseActions.ts
index 2f9062d0e..4f433756e 100644
--- a/packages/lib/playwright/databaseActions.ts
+++ b/packages/lib/playwright/databaseActions.ts
@@ -144,6 +144,8 @@ export const updateUser = (data: Partial) =>
data: {
...data,
onboardingCategories: data.onboardingCategories ?? [],
+ displayedInAppNotifications:
+ data.displayedInAppNotifications ?? Prisma.DbNull,
},
where: {
id: userId,
diff --git a/packages/prisma/mysql/schema.prisma b/packages/prisma/mysql/schema.prisma
index 4d8782560..67bbda365 100644
--- a/packages/prisma/mysql/schema.prisma
+++ b/packages/prisma/mysql/schema.prisma
@@ -41,24 +41,25 @@ model Session {
}
model User {
- id String @id @default(cuid())
- createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @updatedAt
- lastActivityAt DateTime @default(now())
- name String? @db.VarChar(255)
- email String? @unique
- emailVerified DateTime?
- image String? @db.VarChar(1000)
- company String?
- onboardingCategories Json
- graphNavigation GraphNavigation?
- preferredAppAppearance String?
- accounts Account[]
- apiTokens ApiToken[]
- CollaboratorsOnTypebots CollaboratorsOnTypebots[]
- workspaces MemberInWorkspace[]
- sessions Session[]
- bannedIps BannedIp[]
+ id String @id @default(cuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @default(now()) @updatedAt
+ lastActivityAt DateTime @default(now())
+ name String? @db.VarChar(255)
+ email String? @unique
+ emailVerified DateTime?
+ image String? @db.VarChar(1000)
+ company String?
+ onboardingCategories Json
+ graphNavigation GraphNavigation?
+ preferredAppAppearance String?
+ accounts Account[]
+ apiTokens ApiToken[]
+ CollaboratorsOnTypebots CollaboratorsOnTypebots[]
+ workspaces MemberInWorkspace[]
+ sessions Session[]
+ bannedIps BannedIp[]
+ displayedInAppNotifications Json?
}
model ApiToken {
diff --git a/packages/prisma/postgresql/migrations/20240124090203_add_displayed_inapp_notifications/migration.sql b/packages/prisma/postgresql/migrations/20240124090203_add_displayed_inapp_notifications/migration.sql
new file mode 100644
index 000000000..2a0f6c750
--- /dev/null
+++ b/packages/prisma/postgresql/migrations/20240124090203_add_displayed_inapp_notifications/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "displayedInAppNotifications" JSONB;
diff --git a/packages/prisma/postgresql/schema.prisma b/packages/prisma/postgresql/schema.prisma
index 6ca462a62..7820c040c 100644
--- a/packages/prisma/postgresql/schema.prisma
+++ b/packages/prisma/postgresql/schema.prisma
@@ -37,24 +37,25 @@ model Session {
}
model User {
- id String @id @default(cuid())
- createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @updatedAt
- lastActivityAt DateTime @default(now())
- name String?
- email String? @unique
- emailVerified DateTime?
- image String?
- company String?
- onboardingCategories Json
- graphNavigation GraphNavigation?
- preferredAppAppearance String?
- accounts Account[]
- apiTokens ApiToken[]
- CollaboratorsOnTypebots CollaboratorsOnTypebots[]
- workspaces MemberInWorkspace[]
- sessions Session[]
- bannedIps BannedIp[]
+ id String @id @default(cuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @default(now()) @updatedAt
+ lastActivityAt DateTime @default(now())
+ name String?
+ email String? @unique
+ emailVerified DateTime?
+ image String?
+ company String?
+ onboardingCategories Json
+ graphNavigation GraphNavigation?
+ preferredAppAppearance String?
+ accounts Account[]
+ apiTokens ApiToken[]
+ CollaboratorsOnTypebots CollaboratorsOnTypebots[]
+ workspaces MemberInWorkspace[]
+ sessions Session[]
+ bannedIps BannedIp[]
+ displayedInAppNotifications Json?
}
model ApiToken {
diff --git a/packages/schemas/features/user/constants.ts b/packages/schemas/features/user/constants.ts
new file mode 100644
index 000000000..92691fd91
--- /dev/null
+++ b/packages/schemas/features/user/constants.ts
@@ -0,0 +1 @@
+export const graphGestureNotficationKey = 'graphGestureNotification'
diff --git a/packages/schemas/features/user/schema.ts b/packages/schemas/features/user/schema.ts
new file mode 100644
index 000000000..e78de94cc
--- /dev/null
+++ b/packages/schemas/features/user/schema.ts
@@ -0,0 +1,22 @@
+import { GraphNavigation, User as PrismaUser } from '@typebot.io/prisma'
+import { z } from '../../zod'
+
+const displayedInAppNotificationsSchema = z.record(z.boolean())
+
+export const userSchema = z.object({
+ id: z.string(),
+ createdAt: z.date(),
+ updatedAt: z.date(),
+ lastActivityAt: z.date(),
+ name: z.string().nullable(),
+ email: z.string().nullable(),
+ emailVerified: z.date().nullable(),
+ image: z.string().nullable(),
+ company: z.string().nullable(),
+ onboardingCategories: z.array(z.string()),
+ graphNavigation: z.nativeEnum(GraphNavigation),
+ preferredAppAppearance: z.string().nullable(),
+ displayedInAppNotifications: displayedInAppNotificationsSchema.nullable(),
+}) satisfies z.ZodType
+
+export type User = z.infer
diff --git a/packages/schemas/index.ts b/packages/schemas/index.ts
index f5a64710b..126564fa2 100644
--- a/packages/schemas/index.ts
+++ b/packages/schemas/index.ts
@@ -13,3 +13,4 @@ export * from './features/workspace'
export * from './features/items'
export * from './features/analytics'
export * from './features/events'
+export * from './features/user/schema'
diff --git a/packages/scripts/inspectUser.ts b/packages/scripts/inspectUser.ts
index 8f103e7eb..f23b8871e 100644
--- a/packages/scripts/inspectUser.ts
+++ b/packages/scripts/inspectUser.ts
@@ -18,6 +18,7 @@ const inspectUser = async () => {
},
select: {
name: true,
+ createdAt: true,
lastActivityAt: true,
company: true,
onboardingCategories: true,