Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a5f5554
Made frontend for courses page
colton456p Oct 24, 2024
debd3c5
got rid of a print statement that was added to one of the pages for t…
colton456p Oct 24, 2024
a7829fa
Fixed issue with course name not displaying in card
colton456p Nov 5, 2024
81dfcf9
Applied frontend design fixes
colton456p Nov 5, 2024
1b13d12
formatting
colton456p Nov 5, 2024
ae4568c
Change design of courses pages
colton456p Nov 7, 2024
46160c4
removed unneeded comment
colton456p Nov 7, 2024
8342650
fixed course_memberships datatypes
colton456p Nov 7, 2024
4af49c2
added loading state
colton456p Nov 7, 2024
303c616
base frontend for desktop implemented
colton456p Nov 21, 2024
94501eb
added hooks for getting onboarding progress
colton456p Nov 22, 2024
e7fc8bc
linter
colton456p Nov 22, 2024
33d6aa3
added adjustments for mobile
colton456p Nov 22, 2024
6d94576
Merge branch 'main' of https://github.com/Teamable-Analytics/teamable…
colton456p Nov 22, 2024
6cc7afc
changed text
colton456p Nov 22, 2024
eb9a50a
added use case for when onboarding is complete
colton456p Nov 22, 2024
4a18b9d
added num of students
colton456p Nov 22, 2024
96b11f8
fixed the total Students hook
colton456p Nov 27, 2024
edc2994
added hooks for attributes and date
colton456p Nov 28, 2024
56af785
reformatted code to enhance its quality, readability, reusability, an…
colton456p Dec 4, 2024
528e719
fixed frontend display
colton456p Dec 4, 2024
9dd60ed
Added backend for the total number of students on canvas
colton456p Dec 4, 2024
20ef7d8
added loading state
colton456p Dec 4, 2024
0da73fa
small quality fixes
colton456p Dec 9, 2024
4d3d4d8
Added a conditional to limit the number of attributes that will be sh…
colton456p Dec 11, 2024
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"lucide-react": "^0.306.0",
"next": "14.0.4",
"react": "^18",
"react-circular-progressbar": "^2.1.0",
"react-dom": "^18",
"tailwind-merge": "^2.2.0",
"tailwindcss-animate": "^1.0.7",
Expand Down
8 changes: 8 additions & 0 deletions src/_temp_types/homeAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface Attribute {
name: string;
}

export interface InfoItem {
label: string;
value: string | number;
}
2 changes: 1 addition & 1 deletion src/_temp_types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface AuthUser {
last_name: string;
email: string;
is_staff: boolean;
course_memberships: Array<{ course: number }>;
course_memberships: Array<{ id: number, name: string } >;
}

export interface AnonUser {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Attribute } from "@/_temp_types/homeAttributes"

const AttributesUsed = ({ attributes }: { attributes: Attribute[] }) => {
const displayedAttributes = attributes.slice(0, 15)
const hasMoreAttributes = attributes.length > 15

return (
<div className="mb-8">
<h3 className="text-lg font-semibold mb-2">Attributes Used</h3>
<div className="border border-gray-300 p-4 rounded-md">
<div className={`grid gap-2 ${attributes.length > 0 ? "grid-cols-1 sm:grid-cols-4" : ""}`}>
{attributes.length > 0 ? (
<>
{displayedAttributes.map((attribute, index) => (
<ul key={index} className="list-disc pl-5">
<li>{attribute.name}</li>
</ul>
))}
{hasMoreAttributes && (
<ul className="list-disc pl-5">
<li>...</li>
</ul>
)}
</>
) : (
<ul className="list-disc pl-5">
<li>No attributes used</li>
</ul>
)}
</div>
</div>
</div>
)
}

AttributesUsed.defaultProps = {
attributes: [],
}

export default AttributesUsed
36 changes: 36 additions & 0 deletions src/app/(app)/course/[courseId]/home/(components)/InfoSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { InfoItem } from "@/_temp_types/homeAttributes"

const InfoSection = ({
title,
items = [],
}: {
title: string;
items?: InfoItem[];
}) => (
<div className="mb-8">
<h3 className="text-lg font-semibold mb-2">{title}</h3>
<div className="border border-gray-300 p-4 rounded-md">
{items.length > 0 ? (
<ul className="space-y-2">
{items.map((item, index) => (
<li
key={index}
className="flex justify-between items-center"
>
<span>{item.label}:</span>
<b>{item.value}</b>
</li>
))}
</ul>
) : (
<p>No data available</p>
)}
</div>
</div>
)

InfoSection.defaultProps = {
items: [],
}

export default InfoSection
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import CircularProgressBar from "@/components/ui/circular-progress-bar"

interface OnboardingProgressProps {
completionPercentage: number;
nextStepTitle: string;
courseId: string | number;
}

const OnboardingProgress = ({
completionPercentage,
nextStepTitle,
courseId,
}: OnboardingProgressProps) => {
return (
<>
<h2 className="text-lg font-semibold mb-4">
Welcome back!{" "}
{completionPercentage === 100 ? (
<a
href={`/course/${courseId}/setup`}
className="text-gray-500 hover:underline"
>
Wanting to start a new team formation?
</a>
) : (
<span className="text-gray">
Your onboarding process is incomplete...
</span>
)}
</h2>

{completionPercentage !== 100 && (
<div className="flex flex-col sm:flex-row items-center sm:items-start sm:space-x-4 space-y-4 sm:space-y-0 mb-8">
<div className="w-20 h-20 md:w-12 md:h-12">
<CircularProgressBar
value={completionPercentage}
text={`${completionPercentage}`}
/>
</div>
<div className="text-sm text-center sm:text-left">
<p>
You are <b>{completionPercentage}%</b> done with your current onboarding process.
</p>
<p className="font-semibold">
<span className="text-gray-800">Next step: </span>
<a
href={`/course/${courseId}/setup`}
className="underline hover:font-bold"
>
{nextStepTitle}
</a>
</p>
</div>
</div>
)}
</>
)
}

export default OnboardingProgress
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSetupSteps } from "@/app/(app)/course/[courseId]/setup/(hooks)/useSetupSteps"

export const CalculateOnboardingCompletion = () => {
const { steps, isLoading } = useSetupSteps()
const enabledSteps = steps.filter((step) => step.enabled)

const completedSteps = enabledSteps.filter((step) => step.completed)

const completionPercentage = Math.round((completedSteps.length / enabledSteps.length) * 100)

const firstIncompleteStep = enabledSteps.find((step) => !step.completed)

return {
completionPercentage,
nextStepTitle: firstIncompleteStep ? firstIncompleteStep.title : null,
isLoading,
}
}
46 changes: 46 additions & 0 deletions src/app/(app)/course/[courseId]/home/(hooks)/useAttributes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client"

import { useCourse } from "@/app/(app)/course/[courseId]/(hooks)/useCourse"
import { useQuery } from "@tanstack/react-query"

interface PastAttributesResponse {
team_set_name: string;
formation_date: string;
total_attributes_used: number;
attributes: { name: string }[];
}

const usePastAttributesQuery = ({ courseId }: { courseId: number }) => {
const attributeQuery = useQuery<unknown, unknown, PastAttributesResponse>({
queryKey: [`courses/${courseId}/previous-attributes`],
})

return {
getPastAttributes: attributeQuery.refetch,
...attributeQuery,
}
}

export const usePastAttributes = () => {
const { courseId } = useCourse()
const { data, isLoading, error, getPastAttributes } = usePastAttributesQuery({
courseId: Number(courseId),
})

const formattedData = data
? {
...data,
formation_date: new Date(data.formation_date)
.toISOString()
.split("T")[0]
.replace(/-/g, "/"),
}
: undefined

return {
data: formattedData,
isLoading,
error,
refetch: getPastAttributes,
}
}
26 changes: 26 additions & 0 deletions src/app/(app)/course/[courseId]/home/(hooks)/useHandleErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useToast } from "@/hooks/use-toast"
import { useEffect } from "react"

interface ErrorState {
totalStudentsError?: any;
pastAttributesError?: any;
}

export const useHandleErrors = ({ totalStudentsError, pastAttributesError }: ErrorState) => {
const { toast } = useToast()

useEffect(() => {
if (totalStudentsError) {
toast({
title: "Error fetching students",
description: "There was an error fetching the number of students enrolled on your LMS.",
})
}
if (pastAttributesError) {
toast({
title: "Error fetching attributes",
description: "There was an error fetching the attributes used in previous team formation.",
})
}
}, [totalStudentsError, pastAttributesError, toast])
}
34 changes: 34 additions & 0 deletions src/app/(app)/course/[courseId]/home/(hooks)/useTotalStudents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client"

import { useCourse } from "@/app/(app)/course/[courseId]/(hooks)/useCourse"
import { useQuery } from "@tanstack/react-query"

const useTotalStudentsQuery = ({ courseId }: { courseId: number }) => {
const studentQuery = useQuery<
unknown,
unknown,
{ total_students: number; opted_in_students: number }
>({
queryKey: [`courses/${courseId}/student-counts`],
})

return {
getTotalStudentsAsync: studentQuery.refetch,
...studentQuery,
}
}

export const useTotalStudents = () => {
const { courseId } = useCourse()
const { data, isLoading, error, getTotalStudentsAsync } = useTotalStudentsQuery({
courseId: Number(courseId),
})

return {
totalStudents: data?.total_students ?? 0,
optedInStudents: data?.opted_in_students ?? 0,
isLoading,
error,
refetch: getTotalStudentsAsync,
}
}
56 changes: 56 additions & 0 deletions src/app/(app)/course/[courseId]/home/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client"

import { formatDate } from "@/../utils/format-date"
import { useCourse } from "@/app/(app)/course/[courseId]/(hooks)/useCourse"
import PageView from "@/components/views/Page"
import { ReloadIcon } from "@radix-ui/react-icons"
import AttributesUsed from "./(components)/AttributesUsed"
import InfoSection from "./(components)/InfoSection"
import OnboardingProgress from "./(components)/OnboardingProgress"
import { CalculateOnboardingCompletion } from "./(hooks)/calculateOnboardingCompletion"
import { usePastAttributes } from "./(hooks)/useAttributes"
import { useHandleErrors } from "./(hooks)/useHandleErrors"
import { useTotalStudents } from "./(hooks)/useTotalStudents"

const HomePage = () => {
const { courseId } = useCourse()
const { completionPercentage, nextStepTitle, isLoading: isLoadingOnboarding } = CalculateOnboardingCompletion()
const { totalStudents, optedInStudents, isLoading: isLoadingStudents, error: totalStudentsError } = useTotalStudents()
const { data: pastAttributes, isLoading: isLoadingAttributes, error: pastAttributesError } = usePastAttributes()
useHandleErrors({ totalStudentsError, pastAttributesError })

const isLoading = isLoadingOnboarding || isLoadingStudents || isLoadingAttributes

const signUpStats = [
{ label: "Students Enrolled on Your LMS", value: totalStudents },
{ label: "Total Team Formation Acceptions", value: optedInStudents },
]

const previousTeamFormation = [
{ label: "Number of Adopted Attributes", value: pastAttributes?.total_attributes_used || 0 },
{ label: "Team Formation Date", value: pastAttributes ? formatDate(pastAttributes.formation_date) : "N/A" },
]

return (
<PageView title={"Your dashboard,"}>
{isLoading ? (
<div className="flex justify-center items-center h-48">
<ReloadIcon className="mr-2 h-10 w-10 animate-spin text-gray-500" />
</div>
) : (
<>
<OnboardingProgress
completionPercentage={completionPercentage}
nextStepTitle={nextStepTitle ?? "No next step"}
courseId={courseId}
/>
<InfoSection title="Sign up Stats" items={signUpStats} />
<InfoSection title="Previous Team Formation" items={previousTeamFormation} />
<AttributesUsed attributes={pastAttributes?.attributes ?? []} />
</>
)}
</PageView>
)
}

export default HomePage
2 changes: 1 addition & 1 deletion src/app/(app)/course/[courseId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { redirect } from "next/navigation"

const CourseHomepage = async ({ params }: { params: { courseId: string } }) => {
redirect(`/course/${params.courseId}/setup`)
redirect(`/course/${params.courseId}/home`)
}

export default CourseHomepage
8 changes: 4 additions & 4 deletions src/app/(app)/course/[courseId]/setup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client"

import React from "react"
import PageView from "@/components/views/Page"
import { useCourse } from "@/app/(app)/course/[courseId]/(hooks)/useCourse"
import { SetupStepDetailCard } from "@/app/(app)/course/[courseId]/setup/(components)/SetupStepDetailCard"
import { useSetupSteps } from "@/app/(app)/course/[courseId]/setup/(hooks)/useSetupSteps"
import { useCourse } from "@/app/(app)/course/[courseId]/(hooks)/useCourse"
import PageView from "@/components/views/Page"


const SetupPage = () => {
const { steps, addedComponents } = useSetupSteps()
Expand All @@ -13,7 +13,7 @@ const SetupPage = () => {
<PageView
title={"Onboarding"}
breadcrumbs={[
{ title: "Home", href: "/" },
{ title: "Home", href: `/course/${courseId}/home` },
{ title: "Onboarding", href: `course/${courseId}/setup` },
]}
>
Expand Down
Loading