diff --git a/config/dev.js b/config/dev.js index 00153e8..06cc7bd 100644 --- a/config/dev.js +++ b/config/dev.js @@ -12,4 +12,5 @@ module.exports = { V5: "https://api.topcoder-dev.com/v5", }, REAUTH_OFFSET: 55, // seconds + PROFILE_CREATION_DATE_THRESHOLD: "2019-01-01", // format: "YYYY-MM-DD" }; diff --git a/src/App.jsx b/src/App.jsx index 0a7c047..0031e6b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,11 +5,13 @@ import React, { useState, useCallback, useMemo, useEffect } from "react"; import _ from "lodash"; import MainMenu from "./components/MainMenu"; import NavBar from "./components/NavBar"; -import { Router } from "@reach/router"; +import { navigate, Router } from "@reach/router"; import { useSelector } from "react-redux"; import useMatchSomeRoute from "./hooks/useMatchSomeRoute"; import NotificationsModal from "./components/NotificationsModal"; import "./styles/main.module.scss"; +import { checkOnboarding, checkProfileCreationDate } from "./utils"; +import { getOnboardingChecklist } from "./services/auth"; const App = () => { // all menu options @@ -18,10 +20,14 @@ const App = () => { const apps = useMemo(() => _.flatMap(menu, "apps"), [menu]); // list of routes where we have to disabled sidebar const disabledRoutes = useSelector((state) => state.menu.disabledRoutes); + // user profile information + const auth = useSelector((state) => state.auth); // `true` is sidebar has to be disabled for the current route const isSideBarDisabled = useMatchSomeRoute(disabledRoutes); // Left sidebar collapse state const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + // hide switch tools and notification when user is onboarding + const [hideSwitchTools, setHideSwitchTools] = useState(false); // Toggle left sidebar callback const toggleSidebar = useCallback(() => { setSidebarCollapsed(!sidebarCollapsed); @@ -43,9 +49,26 @@ const App = () => { } }, [isSideBarDisabled]); + useEffect(() => { + (async () => { + if (auth?.profile && checkProfileCreationDate(auth?.profile)) { + const { profile, tokenV3 } = auth; + + const response = await getOnboardingChecklist(profile?.handle, tokenV3); + const onboardingPath = checkOnboarding(response); + if (onboardingPath) { + setHideSwitchTools(true); + navigate(onboardingPath); + } else { + setHideSwitchTools(false); + } + } + })(); + }, [auth]); + return ( <> - <NavBar /> + <NavBar hideSwitchTools={hideSwitchTools} /> {!isSideBarDisabled && ( <div className="main-menu-wrapper"> <Router> diff --git a/src/components/NavBar/index.jsx b/src/components/NavBar/index.jsx index b755e7b..c093665 100644 --- a/src/components/NavBar/index.jsx +++ b/src/components/NavBar/index.jsx @@ -11,6 +11,7 @@ import React, { useMemo, } from "react"; import _ from "lodash"; +import PropTypes from "prop-types"; import UserMenu from "../UserMenu"; import AllAppsMenu from "../AllAppsMenu"; import { useSelector } from "react-redux"; @@ -21,7 +22,7 @@ import "./styles.css"; import { useMediaQuery } from "react-responsive"; import NotificationsMenu from "../NotificationsMenu"; -const NavBar = () => { +const NavBar = ({ hideSwitchTools }) => { // all menu options const menu = useSelector((state) => state.menu.categories); // flat list of all apps @@ -54,7 +55,9 @@ const NavBar = () => { <div className="navbar"> <div className="navbar-left"> {isMobile ? ( - <AllAppsMenu /> + hideSwitchTools ? null : ( + <AllAppsMenu /> + ) ) : ( <Fragment> <Link to="/"> @@ -88,7 +91,7 @@ const NavBar = () => { (auth.tokenV3 ? ( auth.profile && ( <Fragment> - <NotificationsMenu /> + {hideSwitchTools ? null : <NotificationsMenu />} <UserMenu profile={auth.profile} /> </Fragment> ) @@ -100,13 +103,13 @@ const NavBar = () => { </Fragment> ) : ( <Fragment> - <AllAppsMenu appChange={changeApp} /> + {hideSwitchTools ? null : <AllAppsMenu appChange={changeApp} />} <div className="navbar-divider"></div> {auth.isInitialized && (auth.tokenV3 ? ( auth.profile && ( <Fragment> - <NotificationsMenu /> + {hideSwitchTools ? null : <NotificationsMenu />} <UserMenu profile={auth.profile} /> </Fragment> ) @@ -122,4 +125,12 @@ const NavBar = () => { ); }; +NavBar.defaultProps = { + hideSwitchTools: false, +}; + +NavBar.propTypes = { + hideSwitchTools: PropTypes.boolean, +}; + export default NavBar; diff --git a/src/services/auth.js b/src/services/auth.js index a3a0c30..59c1613 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -125,3 +125,15 @@ export function authenticate(store) { } }); } + +/** + * Get the onboarding checklist data to know completed steps + */ +export function getOnboardingChecklist(username, userTokenV3) { + const fetcher = getFetcher(userTokenV3); + return fetcher( + `${config.API.V5}/members/${username}/traits?traitIds=onboarding_checklist` + ) + .then((res) => res.json()) + .then((res) => ({ data: res || [] })); +} diff --git a/src/utils/index.js b/src/utils/index.js index 30e94c6..09d13cf 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,5 @@ +import _ from 'lodash'; +import moment from "moment"; import config from "../../config"; /** @@ -45,3 +47,70 @@ export const login = () => { export const businessLogin = () => { window.location = getBusinessLoginUrl(); }; + +/** + * TODO: Re-check when onboarding processor is ready + * Check Onboarding API + * + * @param resp {Object} User trait object + * + * @returns {boolean | string} + */ +export function checkOnboarding(resp) { + if (resp?.data.length === 0) { + return false; + } + const data = resp?.data.filter( + (item) => item.traitId === "onboarding_checklist" + )[0].traits.data[0].profile_completed; + if (data.status === "completed") { + return false; + } + + // TODO: Re-check when onboarding processor is ready. + // It checks for at least one onboarding checklist was completed, then we don't enter onboarding flow + // This logic will be changed. + for (const item in data.metadata) { + if (data.metadata[item]) { + return false; + } + } + + const steps = { + "/onboard/": ["profile_picture", "skills"], + "/onboard/contact-details": ["country"], + "/onboard/payments-setup": [], + "/onboard/build-my-profile": ["bio", "work", "education", "language"], + }; + if (data.status === "pending_at_user") { + const flags = Object.keys(data.metadata); + for (const step of Object.keys(steps)) { + for (const flag of steps[step]) { + if (flags.indexOf(flag) >= 0 && !data.metadata[flag]) { + return step; + } + } + } + } + return false; +} + +/** + * Checks If current user's profile creation time + * + * @param profile {Object} user profile + * + * @returns {boolean} + */ +export const checkProfileCreationDate = (profile) => { + const thresholdDate = moment( + config.PROFILE_CREATION_DATE_THRESHOLD, + "YYYY-MM-DD" + ); + + if (profile?.createdAt) { + return thresholdDate.isBefore(moment(profile?.createdAt)); + } + + return false; +};