diff --git a/apps/web/src/api/buster_rest/users/queryRequests.ts b/apps/web/src/api/buster_rest/users/queryRequests.ts index f6dec63ed..0025ae6dc 100644 --- a/apps/web/src/api/buster_rest/users/queryRequests.ts +++ b/apps/web/src/api/buster_rest/users/queryRequests.ts @@ -9,6 +9,7 @@ import { import { organizationQueryKeys } from '@/api/query_keys/organization'; import { userQueryKeys } from '@/api/query_keys/users'; import { useMemoizedFn } from '@/hooks/useMemoizedFn'; +import { timeout } from '@/lib/timeout'; import type { RustApiError } from '../../errors'; import { useCreateOrganization } from '../organizations/queryRequests'; import { @@ -100,12 +101,12 @@ export const useInviteUser = () => { }; export const useCreateUserOrganization = () => { - const { data: userResponse, refetch: refetchUserResponse } = useGetMyUserInfo({}); + const { data: userResponse, refetch: refetchUserResponse } = useGetMyUserInfo(); const { mutateAsync: createOrganization } = useCreateOrganization(); const { mutateAsync: updateUserInfo } = useUpdateUser(); - const onCreateUserOrganization = useMemoizedFn( - async ({ name, company }: { name: string; company: string }) => { + return useMutation({ + mutationFn: async ({ name, company }: { name: string; company: string }) => { const alreadyHasOrganization = !!userResponse?.organizations?.[0]; if (!alreadyHasOrganization) await createOrganization({ name: company }); if (userResponse) { @@ -113,13 +114,10 @@ export const useCreateUserOrganization = () => { userId: userResponse.user.id, name, }); - await refetchUserResponse(); } - await refetchUserResponse(); - } - ); - - return onCreateUserOrganization; + await Promise.all([timeout(450), refetchUserResponse()]); + }, + }); }; export const useGetSuggestedPrompts = (params: Parameters[0]) => { diff --git a/apps/web/src/assets/png/BusterLogoBW.png b/apps/web/src/assets/png/BusterLogoBW.png deleted file mode 100644 index a59fc9e25..000000000 Binary files a/apps/web/src/assets/png/BusterLogoBW.png and /dev/null differ diff --git a/apps/web/src/assets/png/adobe.png b/apps/web/src/assets/png/adobe.png deleted file mode 100644 index 515b7f50d..000000000 Binary files a/apps/web/src/assets/png/adobe.png and /dev/null differ diff --git a/apps/web/src/assets/png/artifact.png b/apps/web/src/assets/png/artifact.png deleted file mode 100644 index fc7fcdbe9..000000000 Binary files a/apps/web/src/assets/png/artifact.png and /dev/null differ diff --git a/apps/web/src/assets/png/bamboo.png b/apps/web/src/assets/png/bamboo.png deleted file mode 100644 index ba81470c9..000000000 Binary files a/apps/web/src/assets/png/bamboo.png and /dev/null differ diff --git a/apps/web/src/assets/png/carta.png b/apps/web/src/assets/png/carta.png deleted file mode 100644 index 3393eadb6..000000000 Binary files a/apps/web/src/assets/png/carta.png and /dev/null differ diff --git a/apps/web/src/assets/png/connectData.png b/apps/web/src/assets/png/connectData.png deleted file mode 100644 index 0ba49d958..000000000 Binary files a/apps/web/src/assets/png/connectData.png and /dev/null differ diff --git a/apps/web/src/assets/png/databaseDiagram.png b/apps/web/src/assets/png/databaseDiagram.png deleted file mode 100644 index 0a1144454..000000000 Binary files a/apps/web/src/assets/png/databaseDiagram.png and /dev/null differ diff --git a/apps/web/src/assets/png/guidecx.png b/apps/web/src/assets/png/guidecx.png deleted file mode 100644 index 6e509afa4..000000000 Binary files a/apps/web/src/assets/png/guidecx.png and /dev/null differ diff --git a/apps/web/src/assets/png/lucid.png b/apps/web/src/assets/png/lucid.png deleted file mode 100644 index 4b1d95d05..000000000 Binary files a/apps/web/src/assets/png/lucid.png and /dev/null differ diff --git a/apps/web/src/assets/png/noCodeBusterText.png b/apps/web/src/assets/png/noCodeBusterText.png deleted file mode 100644 index cebfcc9c3..000000000 Binary files a/apps/web/src/assets/png/noCodeBusterText.png and /dev/null differ diff --git a/apps/web/src/assets/png/ogimage.png b/apps/web/src/assets/png/ogimage.png deleted file mode 100644 index 8d35234cc..000000000 Binary files a/apps/web/src/assets/png/ogimage.png and /dev/null differ diff --git a/apps/web/src/assets/png/podium.png b/apps/web/src/assets/png/podium.png deleted file mode 100644 index 98dfed37c..000000000 Binary files a/apps/web/src/assets/png/podium.png and /dev/null differ diff --git a/apps/web/src/assets/png/sampleApp.png b/apps/web/src/assets/png/sampleApp.png deleted file mode 100644 index c84fc10d5..000000000 Binary files a/apps/web/src/assets/png/sampleApp.png and /dev/null differ diff --git a/apps/web/src/assets/png/tryBuster.png b/apps/web/src/assets/png/tryBuster.png deleted file mode 100644 index 2da42b685..000000000 Binary files a/apps/web/src/assets/png/tryBuster.png and /dev/null differ diff --git a/apps/web/src/assets/png/watchDemoVideo.png b/apps/web/src/assets/png/watchDemoVideo.png deleted file mode 100644 index 00511cc1b..000000000 Binary files a/apps/web/src/assets/png/watchDemoVideo.png and /dev/null differ diff --git a/apps/web/src/components/features/sidebars/SidebarPrimary/SidebarPrimary.tsx b/apps/web/src/components/features/sidebars/SidebarPrimary/SidebarPrimary.tsx index 141455bb3..f1c2d6e2e 100644 --- a/apps/web/src/components/features/sidebars/SidebarPrimary/SidebarPrimary.tsx +++ b/apps/web/src/components/features/sidebars/SidebarPrimary/SidebarPrimary.tsx @@ -41,8 +41,6 @@ import { toggleInviteModal, useInviteModalStore, } from '@/context/GlobalStore/useInviteModalStore'; -import { useGetSelectedAssetTypeLoose } from '@/context/Routes/useAppRoutes'; -import { useWhyDidYouUpdate } from '@/hooks/useWhyDidYouUpdate'; import { cn } from '@/lib/classMerge'; import { InvitePeopleModal } from '../../modals/InvitePeopleModal'; import { SupportModal } from '../../modals/SupportModal'; diff --git a/apps/web/src/components/ui/layouts/AppLayout.tsx b/apps/web/src/components/ui/layouts/AppLayout.tsx index dcf50e27b..5ecbe68b4 100644 --- a/apps/web/src/components/ui/layouts/AppLayout.tsx +++ b/apps/web/src/components/ui/layouts/AppLayout.tsx @@ -75,7 +75,9 @@ const PageLayout: React.FC< className )} > -
+
{children}
diff --git a/apps/web/src/controllers/NewUserController/NewUserController.tsx b/apps/web/src/controllers/NewUserController/NewUserController.tsx new file mode 100644 index 000000000..bd83f7017 --- /dev/null +++ b/apps/web/src/controllers/NewUserController/NewUserController.tsx @@ -0,0 +1,123 @@ +import { useNavigate } from '@tanstack/react-router'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useMemo, useState } from 'react'; +import { useCreateUserOrganization } from '@/api/buster_rest/users'; +import { + useGetUserBasicInfo, + useGetUserOrganization, +} from '@/api/buster_rest/users/useGetUserInfo'; +import { Button } from '@/components/ui/buttons'; +import { Input } from '@/components/ui/inputs'; +import { Paragraph, Title } from '@/components/ui/typography'; +import { useBusterNotifications } from '@/context/BusterNotifications'; +import { useConfetti } from '@/hooks/useConfetti'; +import { useMemoizedFn } from '@/hooks/useMemoizedFn'; +import { inputHasText } from '@/lib/text'; + +export const NewUserController = () => { + const navigate = useNavigate(); + const user = useGetUserBasicInfo(); + const userOrganizations = useGetUserOrganization(); + const { mutateAsync: onCreateUserOrganization, isPending: submitting } = + useCreateUserOrganization(); + + const [started, setStarted] = useState(false); + const [name, setName] = useState(user?.name); + const [company, setCompany] = useState(userOrganizations?.name); + const { fireConfetti } = useConfetti(); + + const { openInfoMessage } = useBusterNotifications(); + + const canSubmit = useMemo(() => inputHasText(name) && inputHasText(company), [name, company]); + + const handleSubmit = useMemoizedFn(async () => { + if (!canSubmit || !name || !company) { + openInfoMessage('Please fill in all fields'); + return; + } + + try { + await onCreateUserOrganization({ + name, + company, + }); + fireConfetti(); + await navigate({ + to: '/app/home', + replace: true, + }); + } catch (error) { + // + } + }); + + return ( + + {!started && ( + + Welcome to Buster + + With Buster, you can ask data questions in plain english & instantly get back data. + + + + )} + + {started && ( + + Tell us about yourself + setName(e.target.value)} + onPressEnter={handleSubmit} + /> + setCompany(e.target.value)} + onPressEnter={handleSubmit} + /> + + + )} + + ); +}; diff --git a/apps/web/src/controllers/NewUserController/index.ts b/apps/web/src/controllers/NewUserController/index.ts new file mode 100644 index 000000000..89c2c6c75 --- /dev/null +++ b/apps/web/src/controllers/NewUserController/index.ts @@ -0,0 +1 @@ +export * from './NewUserController'; diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 637283dba..024d7d58b 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -29,10 +29,12 @@ import { Route as EmbedMetricMetricIdRouteImport } from './routes/embed/metric.$ import { Route as EmbedDashboardDashboardIdRouteImport } from './routes/embed/dashboard.$dashboardId' import { Route as AppSettingsRestricted_layoutRouteImport } from './routes/app/_settings/_restricted_layout' import { Route as AppSettingsPermissionsRouteImport } from './routes/app/_settings/_permissions' +import { Route as AppAppNewUserRouteImport } from './routes/app/_app/new-user' import { Route as AppAppHomeRouteImport } from './routes/app/_app/home' import { Route as AppAppAssetRouteImport } from './routes/app/_app/_asset' import { Route as AppSettingsSettingsIndexRouteImport } from './routes/app/_settings/settings.index' import { Route as AppAppReportsIndexRouteImport } from './routes/app/_app/reports.index' +import { Route as AppAppNewUserIndexRouteImport } from './routes/app/_app/new-user/index' import { Route as AppAppMetricsIndexRouteImport } from './routes/app/_app/metrics.index' import { Route as AppAppLogsIndexRouteImport } from './routes/app/_app/logs.index' import { Route as AppAppDatasetsIndexRouteImport } from './routes/app/_app/datasets.index' @@ -245,6 +247,11 @@ const AppSettingsPermissionsRoute = AppSettingsPermissionsRouteImport.update({ id: '/_permissions', getParentRoute: () => AppSettingsRoute, } as any) +const AppAppNewUserRoute = AppAppNewUserRouteImport.update({ + id: '/new-user', + path: '/new-user', + getParentRoute: () => AppAppRoute, +} as any) const AppAppHomeRoute = AppAppHomeRouteImport.update({ id: '/home', path: '/home', @@ -265,6 +272,11 @@ const AppAppReportsIndexRoute = AppAppReportsIndexRouteImport.update({ path: '/reports/', getParentRoute: () => AppAppRoute, } as any) +const AppAppNewUserIndexRoute = AppAppNewUserIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => AppAppNewUserRoute, +} as any) const AppAppMetricsIndexRoute = AppAppMetricsIndexRouteImport.update({ id: '/metrics/', path: '/metrics/', @@ -945,6 +957,7 @@ export interface FileRoutesByFullPath { '/info/getting-started': typeof InfoGettingStartedRoute '/app/': typeof AppIndexRoute '/app/home': typeof AppAppHomeRoute + '/app/new-user': typeof AppAppNewUserRouteWithChildren '/embed/dashboard/$dashboardId': typeof EmbedDashboardDashboardIdRoute '/embed/metric/$metricId': typeof EmbedMetricMetricIdRoute '/embed/report/$reportId': typeof EmbedReportReportIdRoute @@ -955,6 +968,7 @@ export interface FileRoutesByFullPath { '/app/datasets': typeof AppAppDatasetsIndexRoute '/app/logs': typeof AppAppLogsIndexRoute '/app/metrics': typeof AppAppMetricsIndexRoute + '/app/new-user/': typeof AppAppNewUserIndexRoute '/app/reports': typeof AppAppReportsIndexRoute '/app/settings': typeof AppSettingsSettingsIndexRoute '/app/chats/$chatId': typeof AppAppAssetChatsChatIdRouteWithChildren @@ -1062,6 +1076,7 @@ export interface FileRoutesByTo { '/app/datasets': typeof AppAppDatasetsIndexRoute '/app/logs': typeof AppAppLogsIndexRoute '/app/metrics': typeof AppAppMetricsIndexRoute + '/app/new-user': typeof AppAppNewUserIndexRoute '/app/reports': typeof AppAppReportsIndexRoute '/app/settings': typeof AppSettingsSettingsIndexRoute '/app/datasets/$datasetId/editor': typeof AppAppDatasetsDatasetIdEditorRoute @@ -1147,6 +1162,7 @@ export interface FileRoutesById { '/app/': typeof AppIndexRoute '/app/_app/_asset': typeof AppAppAssetRouteWithChildren '/app/_app/home': typeof AppAppHomeRoute + '/app/_app/new-user': typeof AppAppNewUserRouteWithChildren '/app/_settings/_permissions': typeof AppSettingsPermissionsRouteWithChildren '/app/_settings/_restricted_layout': typeof AppSettingsRestricted_layoutRouteWithChildren '/embed/dashboard/$dashboardId': typeof EmbedDashboardDashboardIdRoute @@ -1160,6 +1176,7 @@ export interface FileRoutesById { '/app/_app/datasets/': typeof AppAppDatasetsIndexRoute '/app/_app/logs/': typeof AppAppLogsIndexRoute '/app/_app/metrics/': typeof AppAppMetricsIndexRoute + '/app/_app/new-user/': typeof AppAppNewUserIndexRoute '/app/_app/reports/': typeof AppAppReportsIndexRoute '/app/_settings/settings/': typeof AppSettingsSettingsIndexRoute '/app/_app/_asset/chats/$chatId': typeof AppAppAssetChatsChatIdRouteWithChildren @@ -1270,6 +1287,7 @@ export interface FileRouteTypes { | '/info/getting-started' | '/app/' | '/app/home' + | '/app/new-user' | '/embed/dashboard/$dashboardId' | '/embed/metric/$metricId' | '/embed/report/$reportId' @@ -1280,6 +1298,7 @@ export interface FileRouteTypes { | '/app/datasets' | '/app/logs' | '/app/metrics' + | '/app/new-user/' | '/app/reports' | '/app/settings' | '/app/chats/$chatId' @@ -1387,6 +1406,7 @@ export interface FileRouteTypes { | '/app/datasets' | '/app/logs' | '/app/metrics' + | '/app/new-user' | '/app/reports' | '/app/settings' | '/app/datasets/$datasetId/editor' @@ -1471,6 +1491,7 @@ export interface FileRouteTypes { | '/app/' | '/app/_app/_asset' | '/app/_app/home' + | '/app/_app/new-user' | '/app/_settings/_permissions' | '/app/_settings/_restricted_layout' | '/embed/dashboard/$dashboardId' @@ -1484,6 +1505,7 @@ export interface FileRouteTypes { | '/app/_app/datasets/' | '/app/_app/logs/' | '/app/_app/metrics/' + | '/app/_app/new-user/' | '/app/_app/reports/' | '/app/_settings/settings/' | '/app/_app/_asset/chats/$chatId' @@ -1736,6 +1758,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppSettingsPermissionsRouteImport parentRoute: typeof AppSettingsRoute } + '/app/_app/new-user': { + id: '/app/_app/new-user' + path: '/new-user' + fullPath: '/app/new-user' + preLoaderRoute: typeof AppAppNewUserRouteImport + parentRoute: typeof AppAppRoute + } '/app/_app/home': { id: '/app/_app/home' path: '/home' @@ -1764,6 +1793,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppAppReportsIndexRouteImport parentRoute: typeof AppAppRoute } + '/app/_app/new-user/': { + id: '/app/_app/new-user/' + path: '/' + fullPath: '/app/new-user/' + preLoaderRoute: typeof AppAppNewUserIndexRouteImport + parentRoute: typeof AppAppNewUserRoute + } '/app/_app/metrics/': { id: '/app/_app/metrics/' path: '/metrics' @@ -2912,6 +2948,18 @@ const AppAppAssetRouteWithChildren = AppAppAssetRoute._addFileChildren( AppAppAssetRouteChildren, ) +interface AppAppNewUserRouteChildren { + AppAppNewUserIndexRoute: typeof AppAppNewUserIndexRoute +} + +const AppAppNewUserRouteChildren: AppAppNewUserRouteChildren = { + AppAppNewUserIndexRoute: AppAppNewUserIndexRoute, +} + +const AppAppNewUserRouteWithChildren = AppAppNewUserRoute._addFileChildren( + AppAppNewUserRouteChildren, +) + interface AppAppDatasetsDatasetIdPermissionsRouteChildren { AppAppDatasetsDatasetIdPermissionsDatasetGroupsRoute: typeof AppAppDatasetsDatasetIdPermissionsDatasetGroupsRoute AppAppDatasetsDatasetIdPermissionsOverviewRoute: typeof AppAppDatasetsDatasetIdPermissionsOverviewRoute @@ -2961,6 +3009,7 @@ const AppAppDatasetsDatasetIdRouteWithChildren = interface AppAppRouteChildren { AppAppAssetRoute: typeof AppAppAssetRouteWithChildren AppAppHomeRoute: typeof AppAppHomeRoute + AppAppNewUserRoute: typeof AppAppNewUserRouteWithChildren AppAppDatasetsDatasetIdRoute: typeof AppAppDatasetsDatasetIdRouteWithChildren AppAppChatsIndexRoute: typeof AppAppChatsIndexRoute AppAppCollectionsIndexRoute: typeof AppAppCollectionsIndexRoute @@ -2974,6 +3023,7 @@ interface AppAppRouteChildren { const AppAppRouteChildren: AppAppRouteChildren = { AppAppAssetRoute: AppAppAssetRouteWithChildren, AppAppHomeRoute: AppAppHomeRoute, + AppAppNewUserRoute: AppAppNewUserRouteWithChildren, AppAppDatasetsDatasetIdRoute: AppAppDatasetsDatasetIdRouteWithChildren, AppAppChatsIndexRoute: AppAppChatsIndexRoute, AppAppCollectionsIndexRoute: AppAppCollectionsIndexRoute, diff --git a/apps/web/src/routes/app.tsx b/apps/web/src/routes/app.tsx index 8c26c7225..165e94bed 100644 --- a/apps/web/src/routes/app.tsx +++ b/apps/web/src/routes/app.tsx @@ -30,15 +30,20 @@ export const Route = createFileRoute('/app')({ if (user && user?.organizations?.length === 0) { throw redirect({ href: BUSTER_SIGN_UP_URL, replace: true, statusCode: 307 }); } + + if (user && !user.user.name) { + throw redirect({ to: '/app/new-user', replace: true, statusCode: 307 }); + } + return { supabaseSession, }; } catch (error) { - // Re-throw redirect Responses so the router can handle them (e.g., getting-started) - if (error instanceof Response) { - throw error; + if (error instanceof Response && error.status === 307) { + return { + supabaseSession, + }; } - console.error('Error in app route loader:', error); throw redirect({ to: '/auth/login', replace: true, statusCode: 307 }); } }, diff --git a/apps/web/src/routes/app/_app/new-user.tsx b/apps/web/src/routes/app/_app/new-user.tsx new file mode 100644 index 000000000..0ccef9710 --- /dev/null +++ b/apps/web/src/routes/app/_app/new-user.tsx @@ -0,0 +1,30 @@ +import { createFileRoute, Outlet } from '@tanstack/react-router'; +import NewUserWelcome from '@/assets/png/new-user-welcome.png'; + +export const Route = createFileRoute('/app/_app/new-user')({ + component: RouteComponent, +}); + +function RouteComponent() { + return ( +
+
+
+
+ +
+
+
+
+
+
+
+ ); +} diff --git a/apps/web/src/routes/app/_app/new-user/index.tsx b/apps/web/src/routes/app/_app/new-user/index.tsx new file mode 100644 index 000000000..0ed7c499d --- /dev/null +++ b/apps/web/src/routes/app/_app/new-user/index.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router'; +import { NewUserController } from '@/controllers/NewUserController'; + +export const Route = createFileRoute('/app/_app/new-user/')({ + component: RouteComponent, +}); + +function RouteComponent() { + return ; +}