Skip to content

Commit

Permalink
fix(demo): hide after complete (NangoHQ#1849)
Browse files Browse the repository at this point in the history
## Describe your changes

Fixes NAN-516

- Hide demo after completion
This will only happen after refresh. I was going to animate it but
definitely not worth the time at this stage.

- Change the way we handle 404 and redirection
Because I needed to wait for meta to be ready before making a
redirection (I don't want to redirect to the demo if we already
completed it) I needed to have a hook instead of a function. I split
NotFound, Homepage, and handled the different scenario there.

---

NB: I noticed this bug
https://linear.app/nango/issue/NAN-568/fix-env-in-url-is-not-synced-to-cookie
  • Loading branch information
bodinsamuel authored Mar 15, 2024
1 parent 04eed88 commit 5214395
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 41 deletions.
15 changes: 13 additions & 2 deletions packages/server/lib/controllers/environment.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
getGlobalWebhookReceiveUrl,
packageJsonFile,
getEnvironmentId,
Environment
Environment,
getOnboardingProgress
} from '@nangohq/shared';
import { getUserAccountAndEnvironmentFromSession } from '../utils/utils.js';
import { NANGO_ADMIN_UUID } from './account.controller.js';
Expand All @@ -22,6 +23,7 @@ export interface GetMeta {
version: string;
baseUrl: string;
debugMode: boolean;
onboardingComplete: boolean;
}

class EnvironmentController {
Expand All @@ -32,12 +34,21 @@ class EnvironmentController {
errorManager.errResFromNangoErr(res, sessionError);
return;
}

const { account, user } = response;

const environments = await environmentService.getEnvironmentsByAccountId(account.id);
const version = packageJsonFile().version;
const baseUrl = getBaseUrl();
res.status(200).send({ environments, version, email: user.email, baseUrl, debugMode: req.session.debugMode === true });
const onboarding = await getOnboardingProgress(user.id);
res.status(200).send({
environments,
version,
email: user.email,
baseUrl,
debugMode: req.session.debugMode === true,
onboardingComplete: onboarding?.complete || false
});
} catch (err) {
next(err);
}
Expand Down
8 changes: 6 additions & 2 deletions packages/shared/lib/services/onboarding.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ export const updateOnboardingProgress = async (id: number, progress: number): Pr
await q;
};

export const getOnboardingProgress = async (user_id: number): Promise<Required<Pick<Onboarding, 'id' | 'progress'>> | undefined> => {
const result = await db.knex.from<Onboarding>(TABLE).select<Required<Pick<Onboarding, 'progress' | 'id'>>>('progress', 'id').where({ user_id }).first();
export const getOnboardingProgress = async (user_id: number): Promise<Required<Pick<Onboarding, 'id' | 'progress' | 'complete'>> | undefined> => {
const result = await db.knex
.from<Onboarding>(TABLE)
.select<Required<Pick<Onboarding, 'progress' | 'id' | 'complete'>>>('progress', 'id', 'complete')
.where({ user_id })
.first();
return result;
};

Expand Down
3 changes: 0 additions & 3 deletions packages/webapp/public/images/rocket-icon.svg

This file was deleted.

36 changes: 5 additions & 31 deletions packages/webapp/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { SWRConfig } from 'swr';
import { Routes, Route, Navigate, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes } from 'react-router-dom';
import { Routes, Route, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes } from 'react-router-dom';
import { MantineProvider } from '@mantine/core';
import * as Sentry from '@sentry/react';
import { useSignout } from './utils/user';
Expand Down Expand Up @@ -28,6 +28,8 @@ import Activity from './pages/Activity';
import AuthLink from './pages/AuthLink';
import AccountSettings from './pages/AccountSettings';
import UserSettings from './pages/UserSettings';
import { Homepage } from './pages/Homepage';
import { NotFound } from './pages/NotFound';

Sentry.init({
dsn: process.env.REACT_APP_PUBLIC_SENTRY_KEY,
Expand All @@ -39,18 +41,6 @@ Sentry.init({
tracesSampleRate: 0.1
});

const VALID_PATHS = [
'interactive-demo',
'integration',
'integrations',
'syncs',
'connections',
'activity',
'project-settings',
'user-settings',
'account-settings'
];

const App = () => {
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
const env = useStore((state) => state.cookieValue);
Expand All @@ -62,22 +52,6 @@ const App = () => {
setShowInteractiveDemo(env === 'dev' && (isCloud() || isLocal()));
}, [env, setShowInteractiveDemo]);

const correctPage = (): string => {
const url = new URL(window.location.href);
const pathSegments = url.pathname.split('/').filter(Boolean);

const rawUrl = window.location.href;

if (VALID_PATHS.some((path) => rawUrl.includes(path))) {
const newPathSegments = [env, ...pathSegments];
url.pathname = '/' + newPathSegments.join('/');

return url.pathname;
}

return showInteractiveDemo ? '/dev/interactive-demo' : `/${env}/integrations`;
};

return (
<MantineProvider
theme={{
Expand Down Expand Up @@ -114,7 +88,7 @@ const App = () => {
}}
>
<SentryRoutes>
<Route path="/" element={<Navigate to={correctPage()} replace />} />
<Route path="/" element={<Homepage />} />
<Route element={<PrivateRoute />}>
{showInteractiveDemo && (
<Route path="/dev/interactive-demo" element={<PrivateRoute />}>
Expand Down Expand Up @@ -147,7 +121,7 @@ const App = () => {
</>
)}
{(isCloud() || isLocal()) && <Route path="/signup" element={<Signup />} />}
<Route path="*" element={<Navigate to="/" replace />} />
<Route path="*" element={<NotFound />} />
</SentryRoutes>
</SWRConfig>
<ToastContainer />
Expand Down
19 changes: 17 additions & 2 deletions packages/webapp/src/components/LeftNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useStore } from '../store';
import { isLocal, isCloud, isEnterprise } from '../utils/utils';
import { useMeta } from '../hooks/useMeta';
import { useSignout } from '../utils/user';
import { RocketIcon } from '@radix-ui/react-icons';

export enum LeftNavBarItems {
Integrations = 0,
Expand Down Expand Up @@ -127,14 +128,14 @@ export default function LeftNavBar(props: LeftNavBarProps) {
</div>
)}
<div className="space-y-1">
{showInteractiveDemo && (
{showInteractiveDemo && !meta.onboardingComplete && (
<Link
to="/dev/interactive-demo"
className={`flex h-9 p-2 gap-x-3 items-center rounded-md text-sm ${navTextColor} ${
props.selectedItem === LeftNavBarItems.InteractiveDemo ? `${navActiveBg} text-white` : `text-gray-400 ${navHoverBg}`
}`}
>
<img className="h-5" src="/images/rocket-icon.svg" alt="" />
<RocketIcon />
<p>Interactive Demo</p>
</Link>
)}
Expand Down Expand Up @@ -210,6 +211,20 @@ export default function LeftNavBar(props: LeftNavBarProps) {
<UserGroupIcon className="h-5 w-5 mr-2" />
<span>Team</span>
</li>

{showInteractiveDemo && meta.onboardingComplete && (
<Link
to="/dev/interactive-demo"
className={`flex h-9 p-2 gap-x-3 items-center rounded-md text-sm ${navTextColor} ${
props.selectedItem === LeftNavBarItems.InteractiveDemo
? `${navActiveBg} text-white`
: `text-gray-400 ${navHoverBg}`
}`}
>
<RocketIcon />
<p>Interactive Demo</p>
</Link>
)}
<li
className="flex items-center w-full px-2 py-2.5 hover:text-white hover:bg-hover-gray rounded p-1"
onClick={async () => await signout()}
Expand Down
28 changes: 28 additions & 0 deletions packages/webapp/src/pages/Homepage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useMeta } from '../hooks/useMeta';
import { useLocation, useNavigate } from 'react-router-dom';
import { useStore } from '../store';
import { useEffect } from 'react';

export const Homepage: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();

const showInteractiveDemo = useStore((state) => state.showInteractiveDemo);
const env = useStore((state) => state.cookieValue);
const { meta } = useMeta();

useEffect(() => {
if (!meta) {
return;
}

if (env === 'dev' && showInteractiveDemo && !meta.onboardingComplete) {
navigate('/dev/interactive-demo');
return;
}

navigate(`/${env}/integrations`);
}, [meta, location, env, navigate, showInteractiveDemo]);

return null;
};
36 changes: 36 additions & 0 deletions packages/webapp/src/pages/NotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useLocation, useNavigate } from 'react-router-dom';
import { useStore } from '../store';
import { useEffect } from 'react';

const VALID_PATHS = [
'interactive-demo',
'integration',
'integrations',
'syncs',
'connections',
'activity',
'project-settings',
'user-settings',
'account-settings'
];

export const NotFound: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();

const showInteractiveDemo = useStore((state) => state.showInteractiveDemo);
const env = useStore((state) => state.cookieValue);

useEffect(() => {
const pathSegments = location.pathname.split('/').filter(Boolean);
// Add env in URL
if (pathSegments[0] !== env && VALID_PATHS.includes(pathSegments[0])) {
navigate(`/${env}/${pathSegments.join('/')}`);
return;
}

navigate(`/${env}/integrations`);
}, [location, env, navigate, showInteractiveDemo]);

return null;
};
2 changes: 1 addition & 1 deletion packages/webapp/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const useStore = create<State>((set, get) => ({
envs: [{ name: 'dev' }, { name: 'prod' }],
baseUrl: 'https://api.nango.dev',
email: '',
showInteractiveDemo: false,
showInteractiveDemo: true,
debugMode: false,

setCookieValue: (value) => {
Expand Down

0 comments on commit 5214395

Please sign in to comment.