diff --git a/app/components/ReadingTime.tsx b/app/components/ReadingTime.tsx new file mode 100644 index 0000000..1e9f27f --- /dev/null +++ b/app/components/ReadingTime.tsx @@ -0,0 +1,99 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; + +interface ReadingTimeProps { + /** + * Optional key to force recalculation when routes or chapters change. + */ + chapterKey?: string; +} + +export default function ReadingTime({ chapterKey }: ReadingTimeProps) { + const [readingTime, setReadingTime] = useState(null); + + useEffect(() => { + const calculateReadingTime = () => { + // Look for the specific content area first, then fallbacks + const contentElement = + document.getElementById('reading-content') || + document.querySelector('.course-content') || + document.querySelector('main'); + + if (contentElement) { + // Retrieve pure text content + const text = contentElement.textContent || ''; + // Extract words using whitespace regex matcher + const words = text.trim().split(/\s+/).filter((w) => w.length > 0).length; + // Average reading speed: 200 words per minute, rounded up + const time = Math.ceil(words / 200); + setReadingTime(time || 1); // Minimum 1 min read + } else { + setReadingTime(null); + } + }; + + // Delay slightly to ensure React has fully rendered and painted the child components to the DOM + const timer = setTimeout(calculateReadingTime, 150); + + return () => clearTimeout(timer); + }, [chapterKey]); + + if (readingTime === null) { + return ( +
+ + + + + Calculating reading time... +
+ ); + } + + return ( + <> + +
+ {/* Sleek SVG Clock with animated rotating hands on hover */} + + + + + + {readingTime} min read + +
+ + ); +} diff --git a/app/sem1/c/[chapter]/page.tsx b/app/sem1/c/[chapter]/page.tsx index 38e865e..be997eb 100644 --- a/app/sem1/c/[chapter]/page.tsx +++ b/app/sem1/c/[chapter]/page.tsx @@ -10,6 +10,7 @@ import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { Righteous } from "next/font/google"; +import ReadingTime from "@/app/components/ReadingTime"; import BookmarkButton from "../../../components/BookmarkButton"; const righteous = Righteous({ subsets: ['latin'], @@ -34,7 +35,7 @@ type ChapterProps = { export default async function ChapterPage({ params }: ChapterProps) { const { chapter: chapterId } = await params; - const currentIndex = chapters.findIndex((c) => c.id === chapterId); + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -54,7 +55,7 @@ export default async function ChapterPage({ params }: ChapterProps) { ch6: "c-file-memory-preprocessors", }; - const chapterQuiz = moduleQuizzes.find(async (quiz) => quiz.slug === chapterQuizSlugMap[(await params).chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -63,12 +64,13 @@ export default async function ChapterPage({ params }: ChapterProps) {

Programming in C

-
-

- {chapter.title} -

- -
+
+

+ {chapter.title} +

+ +
+ {/* Navigation Buttons */}
@@ -98,7 +100,9 @@ export default async function ChapterPage({ params }: ChapterProps) {

- +
+ +
{chapterQuiz ? (
diff --git a/app/sem1/em1/[chapter]/page.tsx b/app/sem1/em1/[chapter]/page.tsx index d37247c..23efeee 100644 --- a/app/sem1/em1/[chapter]/page.tsx +++ b/app/sem1/em1/[chapter]/page.tsx @@ -10,6 +10,7 @@ import { Righteous } from "next/font/google"; import BookmarkButton from "../../../components/BookmarkButton"; import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], weight: "400", @@ -26,11 +27,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -47,7 +49,7 @@ export default function ChapterPage({ params }: ChapterProps) { ch3: "em1-ordinary-differential-equations", ch4: "em1-laplace-transforms", }; - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -63,6 +65,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+ {/* Top Navigation */}
@@ -92,7 +95,9 @@ export default function ChapterPage({ params }: ChapterProps) {
{/* Chapter Body */} - +
+ +
{chapterQuiz ? (
diff --git a/app/sem1/ep/[chapter]/page.tsx b/app/sem1/ep/[chapter]/page.tsx index 19edea6..744fb35 100644 --- a/app/sem1/ep/[chapter]/page.tsx +++ b/app/sem1/ep/[chapter]/page.tsx @@ -8,6 +8,8 @@ import { Ch5Content } from "../content/chapter5"; import ChapterQuizInline from "../components/ChapterQuizInline"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { Righteous } from "next/font/google"; +import { moduleQuizzes } from "@/lib/quizData"; +import ReadingTime from "@/app/components/ReadingTime"; import BookmarkButton from "../../../components/BookmarkButton"; @@ -29,11 +31,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -44,15 +47,15 @@ export default function ChapterPage({ params }: ChapterProps) { const prevChapter = currentIndex > 0 ? chapters[currentIndex - 1] : null; const nextChapter = currentIndex < chapters.length - 1 ? chapters[currentIndex + 1] : null; - const chapterQuizSlugMap: Record = { - ch1: "ep-vector-fields", - ch2: "ep-electrostatics-magnetostatics", - ch3: "ep-electrodynamics-maxwell", - ch4: "ep-superconductivity", - ch5: "ep-laser-fibre-optics", - }; - - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuizSlugMap: Record = { + ch1: "ep-vector-fields", + ch2: "ep-electrostatics-magnetostatics", + ch3: "ep-electrodynamics-maxwell", + ch4: "ep-superconductivity", + ch5: "ep-laser-fibre-optics", + }; + + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -67,6 +70,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+ {/* Navigation Buttons */}
{prevChapter ? ( @@ -95,7 +99,9 @@ export default function ChapterPage({ params }: ChapterProps) {

- +
+ +
{chapterQuiz ? (
diff --git a/app/sem2/dsc/[chapter]/page.tsx b/app/sem2/dsc/[chapter]/page.tsx index 494c84d..4becea4 100644 --- a/app/sem2/dsc/[chapter]/page.tsx +++ b/app/sem2/dsc/[chapter]/page.tsx @@ -11,6 +11,7 @@ import { Ch4Content } from "../content/chapter4"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -27,11 +28,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -46,7 +48,7 @@ export default function ChapterPage({ params }: ChapterProps) { const chapterQuizSlugMap: Record = { ch1: "dsc-arrays", }; - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -62,6 +64,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+ {/* Navigation */}
@@ -91,7 +94,9 @@ export default function ChapterPage({ params }: ChapterProps) {

- +
+ +
{chapterQuiz ? (
diff --git a/app/sem2/em2/[chapter]/page.tsx b/app/sem2/em2/[chapter]/page.tsx index a980a88..0897ead 100644 --- a/app/sem2/em2/[chapter]/page.tsx +++ b/app/sem2/em2/[chapter]/page.tsx @@ -10,6 +10,7 @@ import BookmarkButton from "../../../components/BookmarkButton"; import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -26,11 +27,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -48,7 +50,7 @@ export default function ChapterPage({ params }: ChapterProps) { ch3: "em2-complex-variables", ch4: "em2-integral-calculus", }; - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -57,12 +59,13 @@ export default function ChapterPage({ params }: ChapterProps) { Engineering Mathematics II -
-

- {chapter.title} -

- -
+
+

+ {chapter.title} +

+ +
+ {/* Top Navigation */}
@@ -93,7 +96,9 @@ export default function ChapterPage({ params }: ChapterProps) {
- +
+ +
{chapterQuiz ? (
diff --git a/app/sem2/oops/[chapter]/page.tsx b/app/sem2/oops/[chapter]/page.tsx index 693e000..229338c 100644 --- a/app/sem2/oops/[chapter]/page.tsx +++ b/app/sem2/oops/[chapter]/page.tsx @@ -14,6 +14,7 @@ import BookmarkButton from "../../../components/BookmarkButton"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -34,11 +35,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -60,7 +62,7 @@ export default function ChapterPage({ params }: ChapterProps) { ch7: "oops-generics", ch8: "oops-java-lib-swing", }; - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -74,8 +76,10 @@ export default function ChapterPage({ params }: ChapterProps) {

{chapter.title}

- +
+ + {/* Navigation */}
{prevChapter ? ( @@ -104,7 +108,9 @@ export default function ChapterPage({ params }: ChapterProps) {

- +
+ +
{chapterQuiz ? (
diff --git a/app/sem3/coa/[chapter]/page.tsx b/app/sem3/coa/[chapter]/page.tsx index 1ac3bbd..b03800e 100644 --- a/app/sem3/coa/[chapter]/page.tsx +++ b/app/sem3/coa/[chapter]/page.tsx @@ -10,6 +10,7 @@ import { Ch7Content } from "../content/chapter7"; import { Ch8Content } from "../content/chapter8"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { Righteous } from "next/font/google"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ['latin'], @@ -31,11 +32,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -54,6 +56,7 @@ export default function ChapterPage({ params }: ChapterProps) { Computer Organization and Architecture

{chapter.title}

+ {/* Navigation Buttons */}
@@ -83,7 +86,9 @@ export default function ChapterPage({ params }: ChapterProps) {

- +
+ +
{/* Navigation Buttons */} diff --git a/app/sem4/dbms/[chapter]/page.tsx b/app/sem4/dbms/[chapter]/page.tsx index 550df44..08d49b9 100644 --- a/app/sem4/dbms/[chapter]/page.tsx +++ b/app/sem4/dbms/[chapter]/page.tsx @@ -12,6 +12,7 @@ import BookmarkButton from "../../../components/BookmarkButton"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { Righteous } from "next/font/google"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -32,11 +33,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -60,6 +62,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+
{prevChapter ? ( @@ -87,7 +90,9 @@ export default function ChapterPage({ params }: ChapterProps) {
- +
+ +
diff --git a/app/sem4/dops/[chapter]/page.tsx b/app/sem4/dops/[chapter]/page.tsx index 17def06..2c0787c 100644 --- a/app/sem4/dops/[chapter]/page.tsx +++ b/app/sem4/dops/[chapter]/page.tsx @@ -12,6 +12,7 @@ import { Ch7Content } from "../content/chapter7"; import { Ch8Content } from "../content/chapter8"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -32,11 +33,12 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { - const currentIndex = chapters.findIndex((c) => c.id === params.chapter); +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; + const currentIndex = chapters.findIndex((c) => c.id === chapterId); const chapter = chapters[currentIndex]; if (!chapter) { @@ -62,6 +64,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+ {/* Navigation */}
@@ -91,7 +94,9 @@ export default function ChapterPage({ params }: ChapterProps) {

- +
+ +
{/* Bottom Navigation */} diff --git a/app/sem4/os/[chapter]/page.tsx b/app/sem4/os/[chapter]/page.tsx index b1e1944..7dbecb0 100644 --- a/app/sem4/os/[chapter]/page.tsx +++ b/app/sem4/os/[chapter]/page.tsx @@ -15,6 +15,7 @@ import ChapterQuizInline from "../components/ChapterQuizInline"; import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { Righteous } from "next/font/google"; import { moduleQuizzes } from "@/lib/quizData"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -76,12 +77,13 @@ const chapters = [ ]; type ChapterProps = { - params: { chapter: string }; + params: Promise<{ chapter: string }>; }; -export default function ChapterPage({ params }: ChapterProps) { +export default async function ChapterPage({ params }: ChapterProps) { + const { chapter: chapterId } = await params; const currentIndex = chapters.findIndex( - (c) => c.id === params.chapter + (c) => c.id === chapterId ); const chapter = chapters[currentIndex]; @@ -117,7 +119,7 @@ export default function ChapterPage({ params }: ChapterProps) { ch8: "os-file-io", }; - const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[params.chapter]); + const chapterQuiz = moduleQuizzes.find((quiz) => quiz.slug === chapterQuizSlugMap[chapterId]); return (
@@ -135,6 +137,7 @@ export default function ChapterPage({ params }: ChapterProps) {

+ {/* Top Navigation */}
@@ -167,7 +170,9 @@ export default function ChapterPage({ params }: ChapterProps) {
- +
+ +
{chapterQuiz ? (
diff --git a/app/sem5/cd/[chapter]/page.tsx b/app/sem5/cd/[chapter]/page.tsx index 97eb70f..c5dc59f 100644 --- a/app/sem5/cd/[chapter]/page.tsx +++ b/app/sem5/cd/[chapter]/page.tsx @@ -37,6 +37,7 @@ import { ParameterPassingContentExport } from "../content/ch20-parameter-passing import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { chapters, SubTopic } from "../constants"; +import ReadingTime from "@/app/components/ReadingTime"; const righteous = Righteous({ subsets: ["latin"], @@ -199,6 +200,7 @@ export default async function ChapterPage({ params }: ChapterProps) { ? `${parentChapter.title} / ${chapterData.title}` : chapterData.title}

+ {/* Navigation */}
@@ -228,7 +230,9 @@ export default async function ChapterPage({ params }: ChapterProps) {

- {ChapterComponent ? :

Content loading...

} +
+ {ChapterComponent ? :

Content loading...

} +
{/* Bottom Navigation */} diff --git a/app/sem5/cle/[chapter]/page.tsx b/app/sem5/cle/[chapter]/page.tsx index efac1a9..1ba131c 100644 --- a/app/sem5/cle/[chapter]/page.tsx +++ b/app/sem5/cle/[chapter]/page.tsx @@ -4,6 +4,7 @@ import { Righteous } from "next/font/google"; import { Ch0Content } from "../content/chapter0"; // ← only ch0 for now import { ArrowBigLeft, ArrowBigRight } from "lucide-react"; import { chapters, Chapter, SubTopic } from "../constants"; // ← cle constants +import ReadingTime from "@/app/components/ReadingTime"; function findChapterOrSubtopic(chapterId: string) { const chapter = chapters.find((c) => c.id === chapterId); @@ -117,6 +118,7 @@ export default async function ChapterPage({ params }: ChapterProps) { ? `${parentChapter.title} / ${chapterData.title}` : chapterData.title}

+
{prevChapter ? ( @@ -146,16 +148,18 @@ export default async function ChapterPage({ params }: ChapterProps) {
{/* Show content if available, else show coming soon message */} - {ChapterComponent ? ( - - ) : ( -
-

Coming Soon

-

- This chapter is under development. Check back soon! -

-
- )} +
+ {ChapterComponent ? ( + + ) : ( +
+

Coming Soon

+

+ This chapter is under development. Check back soon! +

+
+ )} +
diff --git a/app/sem6/ml/[chapter]/page.tsx b/app/sem6/ml/[chapter]/page.tsx index fcbb1cb..a5bc594 100644 --- a/app/sem6/ml/[chapter]/page.tsx +++ b/app/sem6/ml/[chapter]/page.tsx @@ -48,6 +48,7 @@ import BookmarkButton from "../../../components/BookmarkButton"; import { moduleQuizzes } from "@/lib/quizData"; import ChapterQuizInline from "../components/ChapterQuizInline"; +import ReadingTime from "@/app/components/ReadingTime"; function findChapterOrSubtopic(chapterId: string) { const chapter = chapters.find((c) => c.id === chapterId); @@ -204,8 +205,7 @@ export default async function ChapterPage({ params }: ChapterProps) {

- - + {/* Navigation */}
@@ -235,7 +235,9 @@ export default async function ChapterPage({ params }: ChapterProps) {

- +
+ +
{chapterQuiz ? (