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

Commit ab70116

Browse files
authored
Merge pull request #41 from nursoltan-s/user-onboarding-feature
add feature user redirection to onboarding app
2 parents 9ea51aa + 3cfac38 commit ab70116

File tree

5 files changed

+123
-7
lines changed

5 files changed

+123
-7
lines changed

config/dev.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ module.exports = {
1212
V5: "https://api.topcoder-dev.com/v5",
1313
},
1414
REAUTH_OFFSET: 55, // seconds
15+
PROFILE_CREATION_DATE_THRESHOLD: "2019-01-01", // format: "YYYY-MM-DD"
1516
};

src/App.jsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import React, { useState, useCallback, useMemo, useEffect } from "react";
55
import _ from "lodash";
66
import MainMenu from "./components/MainMenu";
77
import NavBar from "./components/NavBar";
8-
import { Router } from "@reach/router";
8+
import { navigate, Router } from "@reach/router";
99
import { useSelector } from "react-redux";
1010
import useMatchSomeRoute from "./hooks/useMatchSomeRoute";
1111
import NotificationsModal from "./components/NotificationsModal";
1212
import "./styles/main.module.scss";
13+
import { checkOnboarding, checkProfileCreationDate } from "./utils";
14+
import { getOnboardingChecklist } from "./services/auth";
1315

1416
const App = () => {
1517
// all menu options
@@ -18,10 +20,14 @@ const App = () => {
1820
const apps = useMemo(() => _.flatMap(menu, "apps"), [menu]);
1921
// list of routes where we have to disabled sidebar
2022
const disabledRoutes = useSelector((state) => state.menu.disabledRoutes);
23+
// user profile information
24+
const auth = useSelector((state) => state.auth);
2125
// `true` is sidebar has to be disabled for the current route
2226
const isSideBarDisabled = useMatchSomeRoute(disabledRoutes);
2327
// Left sidebar collapse state
2428
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
29+
// hide switch tools and notification when user is onboarding
30+
const [hideSwitchTools, setHideSwitchTools] = useState(false);
2531
// Toggle left sidebar callback
2632
const toggleSidebar = useCallback(() => {
2733
setSidebarCollapsed(!sidebarCollapsed);
@@ -43,9 +49,26 @@ const App = () => {
4349
}
4450
}, [isSideBarDisabled]);
4551

52+
useEffect(() => {
53+
(async () => {
54+
if (auth?.profile && checkProfileCreationDate(auth?.profile)) {
55+
const { profile, tokenV3 } = auth;
56+
57+
const response = await getOnboardingChecklist(profile?.handle, tokenV3);
58+
const onboardingPath = checkOnboarding(response);
59+
if (onboardingPath) {
60+
setHideSwitchTools(true);
61+
navigate(onboardingPath);
62+
} else {
63+
setHideSwitchTools(false);
64+
}
65+
}
66+
})();
67+
}, [auth]);
68+
4669
return (
4770
<>
48-
<NavBar />
71+
<NavBar hideSwitchTools={hideSwitchTools} />
4972
{!isSideBarDisabled && (
5073
<div className="main-menu-wrapper">
5174
<Router>

src/components/NavBar/index.jsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import React, {
1111
useMemo,
1212
} from "react";
1313
import _ from "lodash";
14+
import PropTypes from "prop-types";
1415
import UserMenu from "../UserMenu";
1516
import AllAppsMenu from "../AllAppsMenu";
1617
import { useSelector } from "react-redux";
@@ -21,7 +22,7 @@ import "./styles.css";
2122
import { useMediaQuery } from "react-responsive";
2223
import NotificationsMenu from "../NotificationsMenu";
2324

24-
const NavBar = () => {
25+
const NavBar = ({ hideSwitchTools }) => {
2526
// all menu options
2627
const menu = useSelector((state) => state.menu.categories);
2728
// flat list of all apps
@@ -54,7 +55,9 @@ const NavBar = () => {
5455
<div className="navbar">
5556
<div className="navbar-left">
5657
{isMobile ? (
57-
<AllAppsMenu />
58+
hideSwitchTools ? null : (
59+
<AllAppsMenu />
60+
)
5861
) : (
5962
<Fragment>
6063
<Link to="/">
@@ -88,7 +91,7 @@ const NavBar = () => {
8891
(auth.tokenV3 ? (
8992
auth.profile && (
9093
<Fragment>
91-
<NotificationsMenu />
94+
{hideSwitchTools ? null : <NotificationsMenu />}
9295
<UserMenu profile={auth.profile} />
9396
</Fragment>
9497
)
@@ -100,13 +103,13 @@ const NavBar = () => {
100103
</Fragment>
101104
) : (
102105
<Fragment>
103-
<AllAppsMenu appChange={changeApp} />
106+
{hideSwitchTools ? null : <AllAppsMenu appChange={changeApp} />}
104107
<div className="navbar-divider"></div>
105108
{auth.isInitialized &&
106109
(auth.tokenV3 ? (
107110
auth.profile && (
108111
<Fragment>
109-
<NotificationsMenu />
112+
{hideSwitchTools ? null : <NotificationsMenu />}
110113
<UserMenu profile={auth.profile} />
111114
</Fragment>
112115
)
@@ -122,4 +125,12 @@ const NavBar = () => {
122125
);
123126
};
124127

128+
NavBar.defaultProps = {
129+
hideSwitchTools: false,
130+
};
131+
132+
NavBar.propTypes = {
133+
hideSwitchTools: PropTypes.boolean,
134+
};
135+
125136
export default NavBar;

src/services/auth.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,15 @@ export function authenticate(store) {
125125
}
126126
});
127127
}
128+
129+
/**
130+
* Get the onboarding checklist data to know completed steps
131+
*/
132+
export function getOnboardingChecklist(username, userTokenV3) {
133+
const fetcher = getFetcher(userTokenV3);
134+
return fetcher(
135+
`${config.API.V5}/members/${username}/traits?traitIds=onboarding_checklist`
136+
)
137+
.then((res) => res.json())
138+
.then((res) => ({ data: res || [] }));
139+
}

src/utils/index.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import _ from 'lodash';
2+
import moment from "moment";
13
import config from "../../config";
24

35
/**
@@ -45,3 +47,70 @@ export const login = () => {
4547
export const businessLogin = () => {
4648
window.location = getBusinessLoginUrl();
4749
};
50+
51+
/**
52+
* TODO: Re-check when onboarding processor is ready
53+
* Check Onboarding API
54+
*
55+
* @param resp {Object} User trait object
56+
*
57+
* @returns {boolean | string}
58+
*/
59+
export function checkOnboarding(resp) {
60+
if (resp?.data.length === 0) {
61+
return false;
62+
}
63+
const data = resp?.data.filter(
64+
(item) => item.traitId === "onboarding_checklist"
65+
)[0].traits.data[0].profile_completed;
66+
if (data.status === "completed") {
67+
return false;
68+
}
69+
70+
// TODO: Re-check when onboarding processor is ready.
71+
// It checks for at least one onboarding checklist was completed, then we don't enter onboarding flow
72+
// This logic will be changed.
73+
for (const item in data.metadata) {
74+
if (data.metadata[item]) {
75+
return false;
76+
}
77+
}
78+
79+
const steps = {
80+
"/onboard/": ["profile_picture", "skills"],
81+
"/onboard/contact-details": ["country"],
82+
"/onboard/payments-setup": [],
83+
"/onboard/build-my-profile": ["bio", "work", "education", "language"],
84+
};
85+
if (data.status === "pending_at_user") {
86+
const flags = Object.keys(data.metadata);
87+
for (const step of Object.keys(steps)) {
88+
for (const flag of steps[step]) {
89+
if (flags.indexOf(flag) >= 0 && !data.metadata[flag]) {
90+
return step;
91+
}
92+
}
93+
}
94+
}
95+
return false;
96+
}
97+
98+
/**
99+
* Checks If current user's profile creation time
100+
*
101+
* @param profile {Object} user profile
102+
*
103+
* @returns {boolean}
104+
*/
105+
export const checkProfileCreationDate = (profile) => {
106+
const thresholdDate = moment(
107+
config.PROFILE_CREATION_DATE_THRESHOLD,
108+
"YYYY-MM-DD"
109+
);
110+
111+
if (profile?.createdAt) {
112+
return thresholdDate.isBefore(moment(profile?.createdAt));
113+
}
114+
115+
return false;
116+
};

0 commit comments

Comments
 (0)