From 584536a79c861d495fa1d9a4d43a225e8db3f219 Mon Sep 17 00:00:00 2001 From: Armin Talaie Date: Fri, 27 Sep 2024 16:16:06 -0700 Subject: [PATCH] onboarding integration --- prisma/schema.prisma | 29 +- src/app/portal/layout.tsx | 21 +- src/app/portal/onboarding/page.tsx | 121 +++++ .../portal/onboarding/calendarOnboarding.tsx | 84 ++++ .../portal/onboarding/discordOnboarding.tsx | 196 ++++++++ .../portal/onboarding/figmaOnboarding.tsx | 84 ++++ .../portal/onboarding/githubOnboarding.tsx | 173 +++++++ .../portal/onboarding/notionOnboarding.tsx | 84 ++++ .../portal/onboarding/onboarding.ts | 35 ++ .../portal/onboarding/onboardingEnd.tsx | 23 + .../portal/onboarding/onboardingGreeter.tsx | 19 + src/lib/context/usercontext.tsx | 18 +- src/pages/portal/onboarding.tsx | 423 ------------------ 13 files changed, 873 insertions(+), 437 deletions(-) create mode 100644 src/app/portal/onboarding/page.tsx create mode 100644 src/components/portal/onboarding/calendarOnboarding.tsx create mode 100644 src/components/portal/onboarding/discordOnboarding.tsx create mode 100644 src/components/portal/onboarding/figmaOnboarding.tsx create mode 100644 src/components/portal/onboarding/githubOnboarding.tsx create mode 100644 src/components/portal/onboarding/notionOnboarding.tsx create mode 100644 src/components/portal/onboarding/onboarding.ts create mode 100644 src/components/portal/onboarding/onboardingEnd.tsx create mode 100644 src/components/portal/onboarding/onboardingGreeter.tsx delete mode 100644 src/pages/portal/onboarding.tsx diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 473ee931..5f477a63 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -367,15 +367,17 @@ model forms { /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model members { - id String @id @db.Uuid - first_name String - last_name String - grad_year Int @db.SmallInt - faculty String? - specialization String? - year_level Int? @db.SmallInt - users users @relation(fields: [id], references: [id]) - team_members team_members[] + id String @id @db.Uuid + first_name String + last_name String + grad_year Int @db.SmallInt + faculty String? + specialization String? + year_level Int? @db.SmallInt + discord_id String? + github_username String? + users users @relation(fields: [id], references: [id]) + team_members team_members[] @@schema("public") } @@ -438,12 +440,21 @@ model teams { name String @unique start_year Int? @db.SmallInt end_year Int? @db.SmallInt + meta Json? applications applications[] team_members team_members[] @@schema("public") } +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model onboarding { + id BigInt @id @default(autoincrement()) + created_at DateTime @default(now()) @db.Timestamptz(6) + + @@schema("public") +} + enum aal_level { aal1 aal2 diff --git a/src/app/portal/layout.tsx b/src/app/portal/layout.tsx index 28c4a86f..7698486b 100644 --- a/src/app/portal/layout.tsx +++ b/src/app/portal/layout.tsx @@ -6,11 +6,28 @@ import { db } from "@/db"; import { Toaster } from "@/components/primitives/sonner"; async function getUserMetadata(id: string) { - return db.roles.findUnique({ + const rolesP = db.roles.findUnique({ where: { id: id, }, }); + const memberP = db.members.findUnique({ + where: { + id: id, + }, + include: { + team_members: { + include: { + teams: true, + }, + }, + }, + }); + const [roles, member] = await Promise.all([rolesP, memberP]); + return { + roles: roles, + member: member, + }; } export default async function Layout({ @@ -30,7 +47,7 @@ export default async function Layout({ return ( - + {children} diff --git a/src/app/portal/onboarding/page.tsx b/src/app/portal/onboarding/page.tsx new file mode 100644 index 00000000..b0d8e63c --- /dev/null +++ b/src/app/portal/onboarding/page.tsx @@ -0,0 +1,121 @@ +"use client"; + +import React, { useState } from "react"; +import Image from "next/image"; +import Link from "next/link"; +import { Button } from "@/components/primitives/button"; +import GithubOnboarding from "@/components/portal/onboarding/githubOnboarding"; +import { ArrowLeftIcon, ArrowRight } from "lucide-react"; +import DiscordOnboarding from "@/components/portal/onboarding/discordOnboarding"; +import NotionOnboarding from "@/components/portal/onboarding/notionOnboarding"; +import FigmaOnboarding from "@/components/portal/onboarding/figmaOnboarding"; +import CalendarOnboarding from "@/components/portal/onboarding/calendarOnboarding"; +import OnboardingGreeter from "@/components/portal/onboarding/onboardingGreeter"; +import OnboardingEnd from "@/components/portal/onboarding/onboardingEnd"; + +const firstStep = 0; +const lastStep = 6; + +export default function OnboardingPage() { + const [step, setStep] = useState(2); + + const handleNextStepClick = () => + setStep((prev) => (prev === 6 ? prev : prev + 1)); + + const handlePrevStepClick = () => + setStep((prev) => (prev === 0 ? prev : prev - 1)); + + const stepContent = () => { + switch (step) { + case 0: + return ; + case 1: + return ; + case 2: + return ; + case 3: + return ; + case 4: + return ; + case 5: + return ; + default: + return ; + } + }; + + return ( + +
+ {stepContent()} +
+ + +
+
+
+ ); +} + +function BackgroundWrapper({ children }: { children: React.ReactNode }) { + return ( +
+ + UBC Launch Pad logo + +
+
+
+ {"planet"} +
+
+ {"planet"} +
+ {"planet"} +
+
+
+ {children} +
+
+
+
+ ); +} diff --git a/src/components/portal/onboarding/calendarOnboarding.tsx b/src/components/portal/onboarding/calendarOnboarding.tsx new file mode 100644 index 00000000..174badfe --- /dev/null +++ b/src/components/portal/onboarding/calendarOnboarding.tsx @@ -0,0 +1,84 @@ +"use client"; + +import React, { useState } from "react"; +import { Button } from "@/components/primitives/button"; +import { toast } from "sonner"; +import Link from "next/link"; + +const TEXT = { + title: "Google Calendar", + description: + "Add the Google Calendar to your account to have all the events and updates.", + calInvite: + "https://calendar.google.com/calendar/u/0?cid=Y19rMHJqc3JlZzJjbXQzZWtiYWNuNjBvMGprNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t", + loadingButton: "Loading...", + successButton: "You're all set!", + checkJoin: "Have you added the Google Calendar?", + yesJoin: "Yes, I've added!", + noJoin: "No, I haven't added yet.", + button: "Add Google Calendar", + joined: "You're all set!", + inital: "Click on the join button to add the Google Calendar.", +}; + +export default function CalendarOnboarding() { + const [state, setState] = useState<"initial" | "progress" | "success">( + "initial", + ); + + function handleUpdateNotion(toState: "yes" | "no") { + if (toState === "no") { + setState("initial"); + toast.info(TEXT.inital); + } else { + setState("success"); + toast.success(TEXT.joined); + } + } + + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+ + {state === "initial" && ( +
+ setState("progress")} + > + + +
+ )} + + {state === "progress" && ( +
+

{TEXT.checkJoin}

+
+ + +
+
+ )} +
+ ); +} diff --git a/src/components/portal/onboarding/discordOnboarding.tsx b/src/components/portal/onboarding/discordOnboarding.tsx new file mode 100644 index 00000000..910f4e28 --- /dev/null +++ b/src/components/portal/onboarding/discordOnboarding.tsx @@ -0,0 +1,196 @@ +"use client"; + +import React, { useContext, useState } from "react"; +import { Button } from "@/components/primitives/button"; +import { z } from "zod"; +import { userContext } from "@/lib/context/usercontext"; +import { Input } from "@/components/primitives/input"; +import { toast } from "sonner"; +import { updateDiscordUsername, updateGithubUsername } from "./onboarding"; +import Link from "next/link"; + +const TEXT = { + title: "Discord", + description: + "Join the Discord server to access the community and how teams communicate.", + discordInvite: "https://discord.gg/AgQbWykt", + joinDiscord: `First, please join our Discord server. You can join by clicking the link below:`, + inputPlaceholder: "Enter Discord Username", + button: "Join Discord Server", + roles: "The following roles will be assigned to you:", + loadingButton: "Loading...", + successButton: "You're all set!", + successUpdate: "Your Discord username has been updated.", + errorMessage: "Discord validation failed. Please try again.", + discordusername: + "Now, let's confirm your Discord username. Only one Discord account can be linked to you. ", + actionBtn: "Add Discord Roles", +}; + +const DiscordIntegrationSchema = z.object({ + discordUsername: z.string(), + actions: z.object({ + roles: z.array(z.string()), + }), +}); + +export default function DiscordOnboarding() { + const { user, userMetadata } = useContext(userContext); + const [state, setState] = useState< + "initial" | "progress" | "loading" | "success" | "error" + >("initial"); + const [verifiedDiscordUsername, setVerifiedDiscordUsername] = useState(false); + const [discordUsername, setDiscordUsername] = useState( + userMetadata.member?.discord_id || "", + ); + + function handleUpdateDiscordUsername() { + if (userMetadata.member?.discord_id === discordUsername) { + setVerifiedDiscordUsername(true); + } else { + updateDiscordUsername(discordUsername, user.id) + .then(() => { + setVerifiedDiscordUsername(true); + toast.success(TEXT.successUpdate); + }) + .catch(() => { + toast.error(TEXT.errorMessage); + }); + } + } + const handleGithubSubmit = async () => { + try { + const roles: string[] = []; + if (!userMetadata.member?.team_members) { + return; + } + for (const member of userMetadata.member?.team_members) { + roles.push(...member.teams.meta.discord.roles); + } + const parsed = DiscordIntegrationSchema.parse({ + discordUsername: discordUsername, + actions: { + roles: roles, + }, + }); + + const response = await fetch( + `${process.env.NEXT_PUBLIC_COLONY_URL}/colony/integrations/discord`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(parsed), + }, + ); + + const responseBody = await response.text(); + if (!response.ok) { + toast.error(responseBody); + setState("error"); + } else { + toast.success(responseBody); + } + } catch (error) { + console.error(error); + toast.error(TEXT.errorMessage); + } + }; + + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+ + {state === "initial" && ( +
+

{TEXT.joinDiscord}

+
+ setState("progress")} + > + + +
+
+ )} + + {state === "progress" && ( +
+

{TEXT.discordusername}

+
+ setDiscordUsername(e.target.value)} + /> + {!verifiedDiscordUsername && ( + + )} +
+
+ )} + + {verifiedDiscordUsername && ( +
+

{TEXT.roles}

+
+
    + {userMetadata.member?.team_members && + userMetadata.member.team_members.map((member) => { + const roles: string[] = []; + if (member.teams.meta.discord.roles) { + roles.push(...member.teams.meta.discord.roles); + } + + return roles.map((role) => ( +
  • + {role} +
  • + )); + })} +
+
+ +
+
+
+ )} +
+ ); +} diff --git a/src/components/portal/onboarding/figmaOnboarding.tsx b/src/components/portal/onboarding/figmaOnboarding.tsx new file mode 100644 index 00000000..b8e2b46d --- /dev/null +++ b/src/components/portal/onboarding/figmaOnboarding.tsx @@ -0,0 +1,84 @@ +"use client"; + +import React, { useState } from "react"; +import { Button } from "@/components/primitives/button"; +import { toast } from "sonner"; +import Link from "next/link"; + +const TEXT = { + title: "Figma", + description: + "Join the Figma workspace to access the design files and resources.", + figmaInvite: + "https://www.figma.com/team_invite/redeem/LEa6Ds0anjxIFqZx37WF1X", + loadingButton: "Loading...", + successButton: "You're all set!", + checkJoin: "Have you joined the Notion workspace?", + yesJoin: "Yes, I've joined!", + noJoin: "No, I haven't joined yet.", + button: "Join Figma Workspace", + joined: "You're all set!", + inital: "Click on the join button to join the Figma workspace.", +}; + +export default function FigmaOnboarding() { + const [state, setState] = useState<"initial" | "progress" | "success">( + "initial", + ); + + function handleUpdateNotion(toState: "yes" | "no") { + if (toState === "no") { + setState("initial"); + toast.info(TEXT.inital); + } else { + setState("success"); + toast.success(TEXT.joined); + } + } + + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+ + {state === "initial" && ( +
+ setState("progress")} + > + + +
+ )} + + {state === "progress" && ( +
+

{TEXT.checkJoin}

+
+ + +
+
+ )} +
+ ); +} diff --git a/src/components/portal/onboarding/githubOnboarding.tsx b/src/components/portal/onboarding/githubOnboarding.tsx new file mode 100644 index 00000000..30d50725 --- /dev/null +++ b/src/components/portal/onboarding/githubOnboarding.tsx @@ -0,0 +1,173 @@ +"use client"; + +import React, { useContext, useState } from "react"; +import { Button } from "@/components/primitives/button"; +import { z } from "zod"; +import { userContext } from "@/lib/context/usercontext"; +import { Input } from "@/components/primitives/input"; +import { toast } from "sonner"; +import { updateGithubUsername } from "./onboarding"; + +const TEXT = { + title: "Github", + description: "Join the GitHub organization to access the codebase.", + githubUsername: + "First, let's confirm your GitHub username. Only one GitHub account can be linked to you.", + inputPlaceholder: "Enter GitHub Username", + button: "Join GitHub Organization", + errorMessage: "GitHub validation failed. Please try again.", + teams: "You are a member of the following teams:", + loadingButton: "Loading...", + successButton: "You're all set!", + successUpdate: "Your GitHub username has been updated.", + firstTime: + "If you're joining the organization for the first time, Github will invite you to join the organization.", +}; + +const GitHubIntegrationSchema = z.object({ + githubUsername: z.string(), + actions: z.object({ + teams: z.array( + z.object({ + name: z.string(), + role: z.enum(["maintainer", "member"]), + }), + ), + }), +}); + +export default function GithubOnboarding() { + const { user, userMetadata } = useContext(userContext); + const [githubSetupState, setGithubSetupState] = useState< + "initial" | "loading" | "success" | "error" + >("initial"); + const [verifiedGithubUsername, setVerifiedGithubUsername] = useState(false); + const [githubUsername, setGithubUsername] = useState( + userMetadata.member?.github_username || "", + ); + + function handleUpdateGithubUsername() { + if (userMetadata.member?.github_username === githubUsername) { + setVerifiedGithubUsername(true); + } else { + updateGithubUsername(githubUsername, user.id) + .then(() => { + setVerifiedGithubUsername(true); + // update github username + toast.success(TEXT.successUpdate); + }) + .catch(() => { + toast.error(TEXT.errorMessage); + }); + } + } + const handleGithubSubmit = async () => { + try { + const teams = userMetadata.member?.team_members.map((member) => ({ + name: member.teams.meta.github.team.name, + role: "maintainer", + })); + const parsedGithubData = GitHubIntegrationSchema.parse({ + githubUsername: githubUsername, + actions: { + teams: teams, + }, + }); + + const response = await fetch( + `${process.env.NEXT_PUBLIC_COLONY_URL}/colony/integrations/github`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(parsedGithubData), + }, + ); + + const responseBody = await response.text(); + if (!response.ok) { + toast.error(responseBody); + setGithubSetupState("error"); + } else { + toast.success(responseBody); + toast.success(TEXT.firstTime); + } + } catch (error) { + console.error(error); + toast.error(TEXT.errorMessage); + } + }; + + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+ +
+

{TEXT.githubUsername}

+
+ setGithubUsername(e.target.value)} + /> + {!verifiedGithubUsername && ( + + )} +
+
+ + {verifiedGithubUsername && ( +
+

{TEXT.teams}

+
+
    + {userMetadata.member?.team_members && + userMetadata.member.team_members.map((member) => ( +
  • + {member.teams.name} +
  • + ))} +
+
+ +
+
+
+ )} +
+ ); +} diff --git a/src/components/portal/onboarding/notionOnboarding.tsx b/src/components/portal/onboarding/notionOnboarding.tsx new file mode 100644 index 00000000..a1dcff52 --- /dev/null +++ b/src/components/portal/onboarding/notionOnboarding.tsx @@ -0,0 +1,84 @@ +"use client"; + +import React, { useState } from "react"; +import { Button } from "@/components/primitives/button"; +import { toast } from "sonner"; +import Link from "next/link"; + +const TEXT = { + title: "Notion", + description: + "Join the Notion workspace to access the documentation and resources.", + notionInvite: + "https://www.notion.so/team/ad259dbf-777e-4e03-b71e-cff48817296f/join", + loadingButton: "Loading...", + successButton: "You're all set!", + checkJoin: "Have you joined the Notion workspace?", + yesJoin: "Yes, I've joined!", + noJoin: "No, I haven't joined yet.", + button: "Join Notion Workspace", + joined: "You're all set!", + inital: "Click on the join button to join the Notion workspace.", +}; + +export default function NotionOnboarding() { + const [state, setState] = useState<"initial" | "progress" | "success">( + "initial", + ); + + function handleUpdateNotion(toState: "yes" | "no") { + if (toState === "no") { + setState("initial"); + toast.info(TEXT.inital); + } else { + setState("success"); + toast.success(TEXT.joined); + } + } + + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+ + {state === "initial" && ( +
+ setState("progress")} + > + + +
+ )} + + {state === "progress" && ( +
+

{TEXT.checkJoin}

+
+ + +
+
+ )} +
+ ); +} diff --git a/src/components/portal/onboarding/onboarding.ts b/src/components/portal/onboarding/onboarding.ts new file mode 100644 index 00000000..9573109c --- /dev/null +++ b/src/components/portal/onboarding/onboarding.ts @@ -0,0 +1,35 @@ +"use server"; + +import { db } from "@/db"; + +export async function updateGithubUsername(username: string, id: string) { + try { + await db.members.update({ + where: { + id: id, + }, + data: { + github_username: username, + }, + }); + } catch (error) { + console.error("Error updating github username", error); + throw new Error("Error updating github username"); + } +} + +export async function updateDiscordUsername(username: string, id: string) { + try { + await db.members.update({ + where: { + id: id, + }, + data: { + discord_id: username, + }, + }); + } catch (error) { + console.error("Error updating discord username", error); + throw new Error("Error updating discord username"); + } +} diff --git a/src/components/portal/onboarding/onboardingEnd.tsx b/src/components/portal/onboarding/onboardingEnd.tsx new file mode 100644 index 00000000..1da2e958 --- /dev/null +++ b/src/components/portal/onboarding/onboardingEnd.tsx @@ -0,0 +1,23 @@ +const TEXT = { + title: "Member Onboarding", + description: "All done! You can now access all the resources and tools. ", + description2: "You can close this tab now", +}; + +export default function OnboardingEnd() { + return ( +
+
+

+ {TEXT.title} +

+
+
+

+ {TEXT.description} + {TEXT.description2} +

+
+
+ ); +} diff --git a/src/components/portal/onboarding/onboardingGreeter.tsx b/src/components/portal/onboarding/onboardingGreeter.tsx new file mode 100644 index 00000000..02398add --- /dev/null +++ b/src/components/portal/onboarding/onboardingGreeter.tsx @@ -0,0 +1,19 @@ +const TEXT = { + title: "Member Onboarding", + description: + "Here are the steps to get access to all the resources and tools. You can come back to this page to redo the onboarding.", + description2: "You can come back to this page to redo the onboarding.", +}; + +export default function OnboardingGreeter() { + return ( +
+
+

+ {TEXT.title} +

+

{TEXT.description}

+
+
+ ); +} diff --git a/src/lib/context/usercontext.tsx b/src/lib/context/usercontext.tsx index 5c1339e2..6478e28d 100644 --- a/src/lib/context/usercontext.tsx +++ b/src/lib/context/usercontext.tsx @@ -6,11 +6,23 @@ import { User } from "@supabase/auth-js"; type UserContext = { user: User; - userMetadata: { [key: string]: any } | null; + userMetadata: UserMetadata; }; export const userContext = React.createContext({} as UserContext); +type UserMetadata = { + [key: string]: any; + member?: { + team_members: { + member_id: string; + team_id: bigint; + joined_on: Date | null; + role: string | null; + }[]; + }; +}; + export function UserContextProvider({ children, user, @@ -18,13 +30,13 @@ export function UserContextProvider({ }: { children: React.ReactNode; user: User; - userMetadata: { [key: string]: any } | null; + userMetadata: UserMetadata; }) { return ( {children} diff --git a/src/pages/portal/onboarding.tsx b/src/pages/portal/onboarding.tsx deleted file mode 100644 index 6435d313..00000000 --- a/src/pages/portal/onboarding.tsx +++ /dev/null @@ -1,423 +0,0 @@ -import React, { useState } from "react"; -import Image from "next/image"; -import Link from "next/link"; -import "../../app/globals.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faGithub } from "@fortawesome/free-brands-svg-icons"; -import { faDiscord } from "@fortawesome/free-brands-svg-icons"; -import { faFigma } from "@fortawesome/free-brands-svg-icons"; -import { faCalendar } from "@fortawesome/free-solid-svg-icons"; -import { SiNotion } from "react-icons/si"; -import { Button } from "@/components/primitives/button"; -import { useRouter } from "next/router"; -import { z } from "zod"; -// import { db } from "@/db"; - -const URLs = { - Discord: "https://discord.gg/AgQbWykt", - Notion: - "https://www.notion.so/team/ad259dbf-777e-4e03-b71e-cff48817296f/join", - Figma: "https://www.figma.com/team_invite/redeem/LEa6Ds0anjxIFqZx37WF1X", - Calendar: - "https://calendar.google.com/calendar/u/0?cid=Y19rMHJqc3JlZzJjbXQzZWtiYWNuNjBvMGprNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t", -}; - -const DiscordIntegrationSchema = z.object({ - discordUsername: z.string(), - actions: z.object({ - roles: z.array(z.string()), - }), -}); - -const GitHubIntegrationSchema = z.object({ - githubUsername: z.string(), - actions: z.object({ - teams: z.array( - z.object({ - name: z.string(), - role: z.enum(["maintainer", "member"]), - }), - ), - }), -}); - -const Onboarding = () => { - const router = useRouter(); - const [step, setStep] = useState(0); - const [isNextClickable, setIsNextClickable] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); - const [githubUsername, setGithubUsername] = useState(""); - const [discordUsername, setDiscordUsername] = useState(""); - - const handleGithubSubmit = async () => { - try { - // Validate GitHub input with schema - const parsedGithubData = GitHubIntegrationSchema.parse({ - githubUsername: githubUsername, - actions: { - teams: [ - { - name: "execs", - role: "maintainer", - }, - ], - }, - }); - - const response = await fetch( - "https://colony-production.up.railway.app/colony/integrations/github", - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(parsedGithubData), - }, - ); - - const responseBody = await response.text(); - if (!response.ok) { - console.error(`Error: ${response.status} - ${responseBody}`); - throw new Error("Failed to add GitHub user"); - } - - setIsNextClickable(true); - } catch (error) { - setErrorMessage("GitHub validation failed. Please try again."); - console.error("GitHub Submission Error:", error); - } - }; - - const handleDiscordSubmit = async () => { - try { - const response = await fetch( - "https://colony-production.up.railway.app/colony/integrations/discord", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.NEXT_PUBLIC_DISCORD_API_KEY}`, - }, - body: JSON.stringify({ - discordUsername, - actions: { - roles: ["member", "2024-member", "developer", "designer"], - }, - }), - }, - ); - - if (!response.ok) throw new Error("Failed to add Discord user"); - } catch (error) { - console.error(error); - setErrorMessage("Failed to add Discord user. Please try again."); - } - }; - - const handleStartClick = () => { - setStep(1); - setIsNextClickable(false); - setTimeout(() => { - router.push({ - pathname: "/portal/onboarding", - query: { step: "1", state: "not_started" }, - }); - }, 1000); - }; - - const handleNextStepClick = (nextStep: number, state: string) => { - setStep(nextStep); - setIsNextClickable(false); - setTimeout(() => { - router.push({ - pathname: "/portal/onboarding", - query: { step: nextStep.toString(), state }, - }); - }, 1000); - }; - - const handlePrevStepClick = (prevStep: number, state: string) => { - setStep(prevStep); - setIsNextClickable(false); - setTimeout(() => { - router.push({ - pathname: "/portal/onboarding", - query: { step: prevStep.toString(), state }, - }); - }, 1000); - }; - - const handleActionClick = () => { - setIsNextClickable(true); - }; - - const handleEndClick = () => { - setStep(7); - router.push("/"); - }; - - return ( -
- - UBC Launch Pad logo - -
-
-
- {"planet"} -
-
- {"planet"} -
- {"planet"} -
-
-
-
- {step === 0 ? ( - <> -
- Welcome to Member{" "} -
Onboarding
-
- - - ) : step === 1 ? ( - // Step 1: GitHub - <> -
- Step 1: Connect to GitHub -
- setGithubUsername(e.target.value)} - /> - - {errorMessage && ( -
- {errorMessage} -
- )} - - - - ) : step === 2 ? ( - // Step 2: Discord - <> -
- Step 2: Connect to Discord -
- setDiscordUsername(e.target.value)} - className="p-2 border border-gray-400 rounded mb-4" - /> - - {errorMessage && ( -
- {errorMessage} -
- )} - - - - ) : step === 3 ? ( - // Step 3: Notion - <> -
- Step 3: Connect to Notion -
- - - - - ) : step === 4 ? ( - // Step 4: Figma - <> -
- Step 4: Connect to Figma -
- - - - - ) : step === 5 ? ( - // Step 5: Calendar - <> -
- Step 5: Connect to our Calendar -
- - - - - ) : ( - <> -
- Onboarding Complete! -
- You can repeat these steps in future terms when needed -
-
- - - )} -
-
-
-
-
- ); -}; - -export default Onboarding;