Skip to content

Commit e83a798

Browse files
committed
Restructured onboarding to be a component instead of context
1 parent 0c4e302 commit e83a798

15 files changed

Lines changed: 256 additions & 304 deletions
Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
1-
import React, { ReactNode } from "react";
1+
import classNames from "classnames";
2+
import React, { ReactNode, useState } from "react";
23
import { Card, Text } from "../atomic";
3-
import { useOnboarding } from "./OnboardingContext";
4+
import OnboardingContent from "./OnboardingContent";
45

5-
type OnboardingLayoutProps = {
6+
type OnboardingProps = {
67
currentPageChildren: ReactNode;
78
};
89

9-
const OnboardingLayout = ({ currentPageChildren }: OnboardingLayoutProps) => {
10-
const { switchingRoutes, OnboardingComponent } = useOnboarding();
10+
const OnboardingComponent = ({ currentPageChildren }: OnboardingProps) => {
11+
const [switchingRoutes, setSwitchingRoutes] = useState(false);
1112

12-
if (switchingRoutes) return <></>;
13+
const onboardingStyles = classNames({
14+
"flex flex-col h-full w-full bg-tertiary": true,
15+
hidden: switchingRoutes,
16+
});
1317

1418
return (
15-
<div className="flex flex-col h-full w-full bg-tertiary">
19+
<div className={onboardingStyles}>
1620
<div className="h-3/4 w-full box-border">{currentPageChildren}</div>
1721

1822
<div className="h-1/4 w-full z-10 px-6">
1923
<Card className="animate-border-pulse z-10 w-full p-4 space-y-4 box-border">
2024
<Text h2 b>
2125
Welcome to your new program!
2226
</Text>
23-
{OnboardingComponent}
27+
<OnboardingContent
28+
switchingRoutes={switchingRoutes}
29+
setSwitchingRoutes={setSwitchingRoutes}
30+
/>
2431
</Card>
2532
</div>
2633
</div>
2734
);
2835
};
2936

30-
export default OnboardingLayout;
37+
export default OnboardingComponent;

src/components/Onboarding/OnboardingComponent.tsx

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import Link from "next/link";
2+
import { useRouter } from "next/router";
3+
import React from "react";
4+
import {
5+
UpdateProfileInput,
6+
useUpdateProfileMutation,
7+
} from "../../generated/graphql";
8+
import {
9+
AuthorizationLevel,
10+
useAuthorizationLevel,
11+
useCurrentProfile,
12+
} from "../../hooks";
13+
import LocalStorage from "../../utils/localstorage";
14+
import { Button, Text } from "../atomic";
15+
import StepTracker from "../atomic/StepTracker";
16+
17+
const ONBOARDING_STEP = "Onboarding Step";
18+
19+
type OnboardingText = (baseRoute: string) => {
20+
[key: number]: { step: string; route: string };
21+
};
22+
23+
const authorizationLevelToMaxSteps = (authLevel: AuthorizationLevel) => {
24+
switch (authLevel) {
25+
case AuthorizationLevel.Admin:
26+
return 5;
27+
case AuthorizationLevel.Mentor:
28+
return 2;
29+
case AuthorizationLevel.Mentee:
30+
return 2;
31+
default:
32+
return 0;
33+
}
34+
};
35+
36+
const AdminOnboardingText: OnboardingText = (baseRoute: string) => ({
37+
1: {
38+
step: "Set up your program homepage",
39+
route: baseRoute,
40+
},
41+
2: {
42+
step: "Edit your mentor applications",
43+
route: baseRoute + "/applications/edit-mentor-app",
44+
},
45+
3: {
46+
step: "Edit your mentee applications",
47+
route: baseRoute + "/applications/edit-mentee-app",
48+
},
49+
4: {
50+
step: "Edit your mentor profile structure",
51+
route: baseRoute + "/mentors/edit-profile",
52+
},
53+
5: {
54+
step: "Edit your mentee profile structure",
55+
route: baseRoute + "/mentees/edit-profile",
56+
},
57+
});
58+
59+
const MentorOnboardingText: OnboardingText = (baseRoute: string) => ({
60+
1: {
61+
step: "Fill out your profile",
62+
route: baseRoute + "/edit-profile",
63+
},
64+
2: {
65+
step: "Set your availability",
66+
route: baseRoute + "/availability",
67+
},
68+
});
69+
70+
const MenteeOnboardingText: OnboardingText = (baseRoute: string) => ({
71+
1: {
72+
step: "Fill out your profile",
73+
route: baseRoute + "/edit-profile",
74+
},
75+
2: {
76+
step: "Browse through available mentors",
77+
route: baseRoute + "/mentors",
78+
},
79+
});
80+
81+
const authLevelToText = (authLevel: AuthorizationLevel) => {
82+
switch (authLevel) {
83+
case AuthorizationLevel.Admin:
84+
return AdminOnboardingText;
85+
case AuthorizationLevel.Mentor:
86+
return MentorOnboardingText;
87+
default:
88+
return MenteeOnboardingText;
89+
}
90+
};
91+
92+
type OnboardingProps = {
93+
switchingRoutes: boolean;
94+
setSwitchingRoutes: (bool: boolean) => void;
95+
};
96+
97+
const OnboardingContent = ({
98+
switchingRoutes,
99+
setSwitchingRoutes,
100+
}: OnboardingProps) => {
101+
const currentProfile = useCurrentProfile();
102+
const [updateProfile] = useUpdateProfileMutation({
103+
refetchQueries: ["getMyUser"],
104+
});
105+
106+
const authorizationLevel = useAuthorizationLevel();
107+
const router = useRouter();
108+
109+
const MAX_STEPS = authorizationLevelToMaxSteps(authorizationLevel);
110+
const baseRoute = `/program/${router.query.slug}/${router.query.profileRoute}`;
111+
const onboardingText = authLevelToText(authorizationLevel)(baseRoute);
112+
113+
const onFinish = () => {
114+
const updateProfileInput: UpdateProfileInput = {
115+
onboarded: true,
116+
};
117+
updateProfile({
118+
variables: {
119+
profileId: currentProfile.currentProfile!.profileId,
120+
data: updateProfileInput,
121+
},
122+
})
123+
.then(() => {
124+
currentProfile.refetchCurrentProfile!();
125+
LocalStorage.delete(ONBOARDING_STEP);
126+
})
127+
.catch((err) => console.error(err));
128+
};
129+
130+
const storedStep = LocalStorage.get(ONBOARDING_STEP);
131+
const currentStep =
132+
storedStep && typeof storedStep == "number" ? storedStep : 1;
133+
134+
//If the user tries to navigate to another route or if they land on a page that isn't the first step
135+
//Return them to the actual page of the current step
136+
if (
137+
router.asPath !== onboardingText[currentStep]["route"] &&
138+
!switchingRoutes
139+
) {
140+
//Hide content if switching routes
141+
setSwitchingRoutes(true);
142+
router
143+
.push(onboardingText[currentStep]["route"])
144+
.then(() => setSwitchingRoutes(false));
145+
}
146+
147+
const prevStep = Math.max(currentStep - 1, 1);
148+
const nextStep = Math.min(currentStep + 1, MAX_STEPS);
149+
150+
//Use Links to switch between tabs so that you don't have to wait for router.push
151+
return (
152+
<div className="w-full flex flex-col items-center space-y-2 z-10">
153+
<Text h3 className="w-full">
154+
{currentStep}) {onboardingText[currentStep]["step"]}
155+
</Text>
156+
<div className="h-2" />
157+
<div className="flex w-full justify-end items-center box-border">
158+
<div className="w-full">
159+
<StepTracker steps={MAX_STEPS} currentStep={currentStep} />
160+
</div>
161+
162+
<Button
163+
size="small"
164+
variant="inverted"
165+
disabled={currentStep === 1}
166+
onClick={() => {
167+
LocalStorage.set(ONBOARDING_STEP, prevStep);
168+
}}
169+
>
170+
{currentStep !== 1 ? (
171+
<Link href={onboardingText[prevStep]["route"]}>Back</Link>
172+
) : (
173+
"Back"
174+
)}
175+
</Button>
176+
<div className="w-4" />
177+
<Button
178+
size="small"
179+
onClick={() => {
180+
if (currentStep !== MAX_STEPS) {
181+
LocalStorage.set(ONBOARDING_STEP, nextStep);
182+
} else {
183+
onFinish();
184+
}
185+
}}
186+
>
187+
{currentStep !== MAX_STEPS ? (
188+
<Link href={onboardingText[nextStep]["route"]}>Next</Link>
189+
) : (
190+
"Finish"
191+
)}
192+
</Button>
193+
</div>
194+
</div>
195+
);
196+
};
197+
198+
export default OnboardingContent;

0 commit comments

Comments
 (0)