Skip to content

Commit

Permalink
onboarding integration
Browse files Browse the repository at this point in the history
  • Loading branch information
armintalaie committed Sep 27, 2024
1 parent d0cce69 commit 584536a
Show file tree
Hide file tree
Showing 13 changed files with 873 additions and 437 deletions.
29 changes: 20 additions & 9 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions src/app/portal/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -30,7 +47,7 @@ export default async function Layout({
return (
<Suspense>
<UserContextProvider user={data.user} userMetadata={userMetadata}>
<Toaster position={"bottom-center"} richColors />
<Toaster position={"bottom-center"} />
{children}
</UserContextProvider>
</Suspense>
Expand Down
121 changes: 121 additions & 0 deletions src/app/portal/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <OnboardingGreeter />;
case 1:
return <GithubOnboarding />;
case 2:
return <DiscordOnboarding />;
case 3:
return <NotionOnboarding />;
case 4:
return <FigmaOnboarding />;
case 5:
return <CalendarOnboarding />;
default:
return <OnboardingEnd />;
}
};

return (
<BackgroundWrapper>
<div className="bg-background-700 text-white p-8 pb-2 shadow-2xl w-full max-w-xl h-full max-h-[80svh] flex flex-col justify-center items-center rounded-lg border border-background-600 ">
{stepContent()}
<div className="flex gap-2 justify-center w-full pt-8">
<Button
disabled={firstStep === step}
onClick={() => handlePrevStepClick()}
className="p-4 font-semibold text-lg rounded-xl bg-background-500"
>
<ArrowLeftIcon />
</Button>
<Button
onClick={() => handleNextStepClick()}
disabled={lastStep === step}
className={`p-4 font-semibold text-lg rounded-xl bg-background-500`}
>
<ArrowRight />
</Button>
</div>
</div>
</BackgroundWrapper>
);
}

function BackgroundWrapper({ children }: { children: React.ReactNode }) {
return (
<div className="w-screen min-h-screen bg-background-950 justify-center items-center relative flex flex-col ">
<Link
href={"/"}
className={
"rounded-full absolute top-0 left-4 transform border-4 border-primary z-40"
}
>
<Image
src="/images/logo_circle.png"
width={80}
height={80}
alt="UBC Launch Pad logo"
/>
</Link>
<div className="w-screen h-screen bg-background-800 flex justify-center items-center">
<div className={"fixed space-flow top-0 left-0 w-screen h-screen"}>
<div className={"w-[300px] h-[300px] absolute right-10 top-0"}>
<Image
src={"../images/assets/planet1.svg"}
alt={"planet"}
fill={true}
style={{ objectFit: "contain" }}
/>
</div>
<div className={"w-[267px] h-[200px] absolute left-40 bottom-10"}>
<Image
src={"../images/assets/planet2.svg"}
alt={"planet"}
fill={true}
style={{ objectFit: "contain" }}
/>
</div>
<Image
src={"../images/assets/starsBg.svg"}
alt={"planet"}
fill={true}
style={{ objectFit: "contain" }}
/>
</div>
<div className="relative w-full h-full">
<div className="flex justify-center items-center w-full h-full absolute top-0 left-0">
{children}
</div>
</div>
</div>
</div>
);
}
84 changes: 84 additions & 0 deletions src/components/portal/onboarding/calendarOnboarding.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex w-full flex-col items-center gap-10 h-full">
<section className="flex flex-col items-start gap-4 w-full">
<h1 className="text-4xl font-semibold text-left w-full font-heading ">
{TEXT.title}
</h1>
<p className="text-lg text-left w-full ">{TEXT.description}</p>
</section>

{state === "initial" && (
<section className="flex justify-center items-start gap-4 w-full">
<Link
href={TEXT.calInvite}
className="text-lg text-left w-fit"
target="_blank"
onClick={() => setState("progress")}
>
<Button className="p-6 gap-4 w-fit md:min-w-[350px] f text-lg rounded-full">
{TEXT.button}
</Button>
</Link>
</section>
)}

{state === "progress" && (
<section className="flex flex-col items-start gap-4 w-full">
<p>{TEXT.checkJoin}</p>
<div className="flex flex-1 pt-4 flex-col items-center w-full gap-4">
<Button
onClick={() => handleUpdateNotion("no")}
className="p-6 gap-4 w-fit md:min-w-[350px] f text-lg rounded-full"
>
{TEXT.noJoin}
</Button>
<Button
onClick={() => handleUpdateNotion("yes")}
className="p-6 gap-4 w-fit md:min-w-[350px] f text-lg rounded-full"
>
{TEXT.yesJoin}
</Button>
</div>
</section>
)}
</div>
);
}
Loading

0 comments on commit 584536a

Please sign in to comment.