Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

add feature user redirection to onboarding app #41

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/dev.js
Original file line number Diff line number Diff line change
@@ -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"
};
27 changes: 25 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -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>
21 changes: 16 additions & 5 deletions src/components/NavBar/index.jsx
Original file line number Diff line number Diff line change
@@ -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;
12 changes: 12 additions & 0 deletions src/services/auth.js
Original file line number Diff line number Diff line change
@@ -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 || [] }));
}
69 changes: 69 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -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;
};