diff --git a/app/(routes)/courses/[courseId]/_components/CourseChapters.tsx b/app/(routes)/courses/[courseId]/_components/CourseChapters.tsx index ba7022a..645ab40 100644 --- a/app/(routes)/courses/[courseId]/_components/CourseChapters.tsx +++ b/app/(routes)/courses/[courseId]/_components/CourseChapters.tsx @@ -8,6 +8,12 @@ import { AccordionTrigger, } from "@/components/ui/accordion" import { Button } from '@/components/ui/button' +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" type Props = { @@ -16,6 +22,38 @@ type Props = { } function CourseChapters({loading,courseDetail}:Props) { + +const EnableExercise = ( + chapterIndex: number, + exerciseIndex: number, + chapterExercisesLength: number +) => { + const completed = courseDetail?.completedExcercises; + + // If nothing is completed, enable FIRST exercise ONLY + if (!completed || completed.length === 0) { + return chapterIndex === 0 && exerciseIndex === 0; + } + + // last completed + const last = completed[completed.length - 1]; + + // Convert to global exercise number + const currentExerciseNumber = + chapterIndex * chapterExercisesLength + exerciseIndex + 1; + + const lastCompletedNumber = + (last.chapterId - 1) * chapterExercisesLength + last.exerciseId; + + return currentExerciseNumber === lastCompletedNumber + 2; +}; + +const isExerciseCompleted=(chapterId:number,exerciseId:number)=>{ + const completeChapters = courseDetail?.completedExcercises; + const completeChapter = completeChapters?.find(item=>(item.chapterId==chapterId && item.exerciseId== exerciseId)) + return completeChapter?true:false +} + return (
@@ -31,7 +69,22 @@ function CourseChapters({loading,courseDetail}:Props) {

Exercise {index +1}

{exc.name}

+ { + isExerciseCompleted(chapter?.chapterId,index+1)&& + } + {EnableExercise(index,index, chapter?.exercises.length)? + + : + + + + + +

Please Enroll First!!

+
+
+
}
))} diff --git a/app/(routes)/courses/[courseId]/_components/CourseStatus.tsx b/app/(routes)/courses/[courseId]/_components/CourseStatus.tsx index bb49d8e..901c004 100644 --- a/app/(routes)/courses/[courseId]/_components/CourseStatus.tsx +++ b/app/(routes)/courses/[courseId]/_components/CourseStatus.tsx @@ -1,22 +1,66 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import Image from 'next/image' import { Progress } from '@/components/ui/progress' -function CourseStatus() { +import { Course } from '../../_components/CourseList' +import { Item } from '@radix-ui/react-navigation-menu' + + +type Props={ + courseDetail:Course|undefined +} + +function CourseStatus({courseDetail}:Props) { + + const [counts,setCounts] = useState<{ + totalExce: number, + totalXp: number, + }>() + + useEffect(()=>{ + courseDetail && GetCounts() + },[courseDetail]) + + const GetCounts=()=>{ + let totalExercises = 0; + let totalXp = 0; + courseDetail?.chapters?.forEach((chapters)=>{ + totalExercises = totalExercises+chapters?.exercises?.length + chapters?.exercises?.forEach(exc=>{ + totalXp=totalXp+exc?.xp; + }) + }) + + setCounts({ + totalExce:totalExercises, + totalXp:totalXp + }) + } + + const updateProgress =(currentValue:number,totalvalue: number)=>{ + if(currentValue&&totalvalue){ + + const perc = (currentValue*100)/totalvalue; + return perc + + }return 0 + } return (

Course Progress

image

Exercises- - 1/72

- + 1/{courseDetail?.completedExcercises?.length}/{counts?.totalExce} + {/* @ts-ignore */} +
image

XP Earned - 1/340

- + {courseDetail?.courseEnrolledInfo?.xpEarned}/{counts?.totalXp} + {/* @ts-ignore */} +
diff --git a/app/(routes)/courses/[courseId]/page.tsx b/app/(routes)/courses/[courseId]/page.tsx index a8c35d7..394f304 100644 --- a/app/(routes)/courses/[courseId]/page.tsx +++ b/app/(routes)/courses/[courseId]/page.tsx @@ -6,7 +6,7 @@ import axios from 'axios' import { Course } from '../_components/CourseList' import CourseChapters from './_components/CourseChapters' import CourseStatus from './_components/CourseStatus' -import UpgradeToPro from '../../dashboard/_components/Upgrade-To-Pro' +import UpgradeToPro from '../../dashboard/_components/UpgradeToPro' import Header from '@/app/_components/Header' function CourseDetails() { @@ -53,7 +53,7 @@ function CourseDetails() {
- +
diff --git a/app/(routes)/courses/_components/CourseList.tsx b/app/(routes)/courses/_components/CourseList.tsx index 89f6555..11532d2 100644 --- a/app/(routes)/courses/_components/CourseList.tsx +++ b/app/(routes)/courses/_components/CourseList.tsx @@ -15,9 +15,17 @@ export type Course = { tag: string, chapters?:Chapter[], userEnrolled?: boolean, - courseEnrolledInfo?: courseEnrolledInfo + courseEnrolledInfo?: courseEnrolledInfo, + completedExcercises?: completedExcercises[] } +type completedExcercises={ + chapterId: number, + courseId: number, + exerciseId: number +} + + type courseEnrolledInfo={ xpEarned: number, enrolledDate: any, diff --git a/app/(routes)/dashboard/_components/InviteFrame.tsx b/app/(routes)/dashboard/_components/InviteFriend.tsx similarity index 93% rename from app/(routes)/dashboard/_components/InviteFrame.tsx rename to app/(routes)/dashboard/_components/InviteFriend.tsx index 5e7dd2e..1cc3f55 100644 --- a/app/(routes)/dashboard/_components/InviteFrame.tsx +++ b/app/(routes)/dashboard/_components/InviteFriend.tsx @@ -3,7 +3,7 @@ import Image from "next/image"; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; -function InviteFrame() { +function InviteFriend() { return (
mail @@ -17,4 +17,4 @@ function InviteFrame() { ) } -export default InviteFrame \ No newline at end of file +export default InviteFriend \ No newline at end of file diff --git a/app/(routes)/dashboard/_components/Upgrade-To-Pro.tsx b/app/(routes)/dashboard/_components/UpgradeToPro.tsx similarity index 100% rename from app/(routes)/dashboard/_components/Upgrade-To-Pro.tsx rename to app/(routes)/dashboard/_components/UpgradeToPro.tsx diff --git a/app/(routes)/dashboard/_components/UserStates.tsx b/app/(routes)/dashboard/_components/UserStates.tsx index 26b0228..c912a25 100644 --- a/app/(routes)/dashboard/_components/UserStates.tsx +++ b/app/(routes)/dashboard/_components/UserStates.tsx @@ -6,6 +6,7 @@ import { useUser } from '@clerk/nextjs'; function UserStates() { const { user } = useUser() + return (
diff --git a/app/(routes)/dashboard/page.tsx b/app/(routes)/dashboard/page.tsx index bef416b..ab3992f 100644 --- a/app/(routes)/dashboard/page.tsx +++ b/app/(routes)/dashboard/page.tsx @@ -2,9 +2,9 @@ import React from 'react' import WelcomeBanner from './_components/WelcomeBanner' import EnrolledCourses from './_components/EnrolledCourses' import ExploreMore from './_components/ExploreMore' -import InviteFrame from './_components/InviteFrame' +import InviteFrame from './_components/InviteFriend' import UserStates from './_components/UserStates' -import UpgradeToPro from './_components/Upgrade-To-Pro' +import UpgradeToPro from './_components/UpgradeToPro' import Header from '@/app/_components/Header' function Dashboard() { diff --git a/app/api/course/route.ts b/app/api/course/route.ts index be1d793..4e0ae85 100644 --- a/app/api/course/route.ts +++ b/app/api/course/route.ts @@ -1,7 +1,7 @@ import {db} from '@/config/db'; -import { CourseChapterTable, CourseTable, EnrolledCourseTable } from '@/config/schema'; +import { CompletedExerciseTable, CourseChapterTable, CourseTable, EnrolledCourseTable } from '@/config/schema'; import { currentUser } from '@clerk/nextjs/server'; -import { and, asc, eq } from 'drizzle-orm'; +import { and, asc, desc, eq } from 'drizzle-orm'; import { NextRequest, NextResponse } from 'next/server'; @@ -25,11 +25,17 @@ if (courseId){ .where(and(eq(EnrolledCourseTable?.courseId,courseId),eq(EnrolledCourseTable.userId,user?.primaryEmailAddress?.emailAddress) )) const isEnrolledCourse = enrolledCourse?.length>0?true:false + //@ts-ignore + const completedExercises = await db.select().from(CompletedExerciseTable).where(and(eq(CompletedExerciseTable.courseId,courseId),eq(CompletedExerciseTable.userId,user?.primaryEmailAddress?.emailAddress))) + .orderBy(desc(CompletedExerciseTable?.courseId)) + + return NextResponse.json({ ...result[0], chapters:chapterResult, userEnrolled: isEnrolledCourse, - courseEnrolledInfo: enrolledCourse[0] + courseEnrolledInfo: enrolledCourse[0], + completedExercises: completedExercises }) } else{ diff --git a/components/ui/button.tsx b/components/ui/button.tsx index f732ce1..a93d123 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -21,6 +21,11 @@ const buttonVariants = cva( link: "text-primary underline-offset-4 hover:underline", pixel: "bg-yellow-400 text-black border-2 border-black shadow-[0px_0px_0_0_#c69405,2px_2px_0_0_#c69405] active:shadow-[0_0_0_0_#000] hover:brightness-105", + + pixelDisabled: + "bg-gray-400 text-gray-700 border-2 border-gray-500 shadow-[0px_0px_0_0_#9ca3af,2px_2px_0_0_#9ca3af] cursor-not-allowed hover:brightness-100 active:shadow-[0_0_0_0_#000]" + + }, diff --git a/config/db.js b/config/db.js deleted file mode 100644 index d2ffac9..0000000 --- a/config/db.js +++ /dev/null @@ -1,3 +0,0 @@ -import { drizzle } from 'drizzle-orm/neon-http'; - -export const db = drizzle(process.env.DATABASE_URL); \ No newline at end of file diff --git a/config/schema.ts b/config/schema.ts index fc7378b..4c49bf1 100644 --- a/config/schema.ts +++ b/config/schema.ts @@ -43,4 +43,4 @@ export const CompletedExerciseTable = pgTable('completedExercise',{ chapterId: integer(), exerciseId: integer(), userId: varchar() -}) \ No newline at end of file +}) diff --git a/drizzle.config.ts b/drizzle.config.ts index 2972634..b5801b3 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -2,7 +2,7 @@ import 'dotenv/config'; import { defineConfig } from 'drizzle-kit'; export default defineConfig({ - schema: './config/schema.tsx', + schema: './config/schema.ts', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL!,