Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion app/components/subjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const subjects = {
"IoT (Internet of Things)",
"Compiler Design",
"Cyber Laws and Ethics",
"Cryptography & Network Security",
],
"Semester-6": [
"Machine Learning",
Expand Down Expand Up @@ -107,6 +108,7 @@ const subjectCodes: Record<string, string> = {
"IoT (Internet of Things)": "iot",
"Compiler Design": "cd",
"Cyber Laws and Ethics": "cle",
"Cryptography & Network Security": "cns",
"Machine Learning": "ml",
"Natural Language Processing": "nlp",
"Deep Learning": "dl",
Expand All @@ -125,7 +127,7 @@ const subjectCodes: Record<string, string> = {
};

// Available subjects
const available = ["ep", "c", "em1", "em2", "oops", "dsc", "coa", "os", "ml", "dops", "cd", "cle","ec"];
const available = ["ep", "c", "em1", "em2", "oops", "dsc", "coa", "os", "ml", "dops", "cd", "cle", "ec", "cns"];

export default function SubjectsSection() {
return (
Expand Down
207 changes: 207 additions & 0 deletions app/sem5/cns/[chapter]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import React from "react";
import Link from "next/link";
import { Metadata } from "next";
import { Righteous } from "next/font/google";
import { Ch0Content } from "../content/chapter0";
import { Ch1Content } from "../content/chapter1";
import { Ch2Content } from "../content/chapter2";
import { Ch3Content } from "../content/chapter3";
import { Ch4Content } from "../content/chapter4";
import { Ch5Content } from "../content/chapter5";
import { Ch6Content } from "../content/chapter6";
import { ArrowBigLeft, ArrowBigRight } from "lucide-react";
import { chapters, SubTopic } from "../constants";

const righteous = Righteous({
subsets: ["latin"],
weight: "400",
variable: "--font-righteous",
});

function findChapterOrSubtopic(chapterId: string) {
const chapter = chapters.find((c) => c.id === chapterId);
if (chapter) return { data: chapter, isSubTopic: false, parentChapter: null };

for (const ch of chapters) {
if (ch.subTopics) {
const sub = ch.subTopics.find(
(s) => s.id === chapterId && s.isPage
) as (SubTopic & { isPage: true }) | undefined;
if (sub) return { data: sub, isSubTopic: true, parentChapter: ch };
}
}
return { data: undefined, isSubTopic: false, parentChapter: null };
}

const chapterComponents: Record<string, React.ComponentType> = {
ch0: Ch0Content,
ch1: Ch1Content,
ch2: Ch2Content,
ch3: Ch3Content,
ch4: Ch4Content,
ch5: Ch5Content,
ch6: Ch6Content,
};

type ChapterProps = {
params: Promise<{ chapter: string }>;
};

export async function generateMetadata({
params,
}: ChapterProps): Promise<Metadata> {
const { chapter: chapterId } = await params;
Comment on lines +46 to +53
const { data: chapterData } = findChapterOrSubtopic(chapterId);

const title = chapterData
? `${chapterData.title} | Cryptography & Network Security | openCSE`
: "Cryptography & Network Security | openCSE";

return { title };
}

export default async function ChapterPage({ params }: ChapterProps) {
const { chapter: chapterId } = await params;
const { data: chapterData, isSubTopic, parentChapter } =
findChapterOrSubtopic(chapterId);

if (!chapterData) {
return (
<div className="flex flex-col items-center justify-center min-h-[50vh] text-[#e2d1c1]">
<h1 className="text-2xl font-bold mb-4">Chapter not found</h1>
<Link
href="/sem5/cns/ch0"
className="px-4 py-2 bg-[#e2d1c1] text-[#1b0d00] rounded hover:bg-[#ac9e91] transition font-bold"
>
Return to Course Outline
</Link>
</div>
);
}

const ChapterComponent = chapterComponents[chapterData.id];
let prevChapter = null;
let nextChapter = null;

if (isSubTopic && parentChapter && parentChapter.subTopics) {
const pageSubTopics = parentChapter.subTopics.filter(
(s): s is SubTopic & { isPage: true } => !!s.isPage
);
const subIndex = pageSubTopics.findIndex((s) => s.id === chapterId);

if (subIndex > 0) {
prevChapter = pageSubTopics[subIndex - 1];
} else {
prevChapter = {
id: parentChapter.id,
title: `Back to ${parentChapter.title}`,
};
}

if (subIndex < pageSubTopics.length - 1) {
nextChapter = pageSubTopics[subIndex + 1];
} else {
const parentIndex = chapters.findIndex((c) => c.id === parentChapter.id);
if (parentIndex < chapters.length - 1) {
nextChapter = chapters[parentIndex + 1];
}
}
} else {
const currentIndex = chapters.findIndex((c) => c.id === chapterId);
if (currentIndex > 0) {
const prevParent = chapters[currentIndex - 1];
if (prevParent.subTopics && prevParent.subTopics.length > 0) {
const pageSubTopics = prevParent.subTopics.filter(
(s): s is SubTopic & { isPage: true } => !!s.isPage
);
prevChapter =
pageSubTopics.length > 0
? pageSubTopics[pageSubTopics.length - 1]
: prevParent;
} else {
prevChapter = prevParent;
}
}

const currentParent = chapters[currentIndex];
if (currentParent.subTopics && currentParent.subTopics.length > 0) {
const pageSubTopics = currentParent.subTopics.filter(
(s): s is SubTopic & { isPage: true } => !!s.isPage
);
nextChapter = pageSubTopics.length > 0 ? pageSubTopics[0] : null;
} else if (currentIndex < chapters.length - 1) {
nextChapter = chapters[currentIndex + 1];
}
}

return (
<div className="flex flex-col bg-[#1B0D00] min-h-full p-4 pt-6 text-[#e2d1c1]">
<div className="flex-1 w-full">
<h1 className={`text-4xl font-bold ${righteous.className} mb-2`}>
Cryptography &amp; Network Security
</h1>

<p className={`text-2xl mt-[-8px] ${righteous.className}`}>
{isSubTopic && parentChapter
? `${parentChapter.title} / ${chapterData.title}`
: chapterData.title}
</p>

<div className="flex justify-between mt-4">
{prevChapter ? (
<Link
href={`/sem5/cns/${prevChapter.id}`}
className="px-4 py-1 text-2xl flex items-center justify-center bg-[#e2d1c1] text-[#1b0d00] rounded hover:bg-[#ac9e91] transition"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
<ArrowBigLeft className="inline-block mr-1" /> Previous
</Link>
) : (
<div />
)}

{nextChapter ? (
<Link
href={`/sem5/cns/${nextChapter.id}`}
className="px-4 py-1 text-2xl flex items-center justify-center bg-[#e2d1c1] text-[#1b0d00] rounded hover:bg-[#ac9e91] transition"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
Next <ArrowBigRight className="inline-block ml-1" />
</Link>
) : (
<div />
)}
</div>

<hr className="my-6 border-t-2 border-[#c7a669]/40" />
{ChapterComponent ? <ChapterComponent /> : <p>Content loading...</p>}
</div>

<div className="flex justify-between my-8 w-full">
{prevChapter ? (
<Link
href={`/sem5/cns/${prevChapter.id}`}
className="px-4 py-2 bg-[#e2d1c1] text-xl flex items-center justify-center text-[#1b0d00] rounded hover:bg-[#ac9e91] transition"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
<ArrowBigLeft className="inline-block mr-1" /> {prevChapter.title}
</Link>
) : (
<div />
)}

{nextChapter ? (
<Link
href={`/sem5/cns/${nextChapter.id}`}
className="px-4 py-2 bg-[#e2d1c1] text-xl flex items-center justify-center text-[#1b0d00] rounded hover:bg-[#ac9e91] transition"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
{nextChapter.title} <ArrowBigRight className="inline-block ml-1" />
</Link>
) : (
<div />
)}
</div>
</div>
);
}
146 changes: 146 additions & 0 deletions app/sem5/cns/components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"use client";
import React, { useState, useEffect } from "react";
import { Righteous } from "next/font/google";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { chapters } from "../constants";

const righteous = Righteous({
subsets: ["latin"],
weight: "400",
variable: "--font-righteous",
});

export default function Sidebar() {
const pathname = usePathname();
const [open, setOpen] = useState(false);

useEffect(() => {
if (typeof window !== "undefined" && window.innerWidth >= 768) {
setOpen(true);
}
}, []);
Comment on lines +16 to +22

const quizSlugMap: Record<string, string> = {
cns: "cns",
};

const subjectKey = pathname.split("/")[2] ?? "";
const quizSlug = quizSlugMap[subjectKey];
const quizHref = quizSlug ? `/quiz/${quizSlug}` : "/quiz";
Comment on lines +24 to +30
const quizActive = pathname.startsWith("/quiz");

return (
<>
<div
className={`fixed inset-0 md:hidden bg-black/50 z-30 transition-opacity duration-300 ${open ? "opacity-100" : "opacity-0 pointer-events-none"}`}
onClick={() => setOpen(false)}
/>

<div className="flex sticky top-14 z-40 h-[calc(100vh-3.5rem)] w-[50px] md:w-auto pointer-events-none md:pointer-events-auto">
<aside
className={`h-full shrink-0 bg-[#fae8d7] text-[#1B0D00] p-0 flex flex-col transition-all duration-300 pointer-events-auto border-r-0 ${
open ? "w-64 border-r-2 md:border-r-0" : "w-0 overflow-hidden"
}`}
>
<h2
className="flex items-center text-2xl font-normal pt-3 pl-3 mb-2 bg-[#cebb9c] text-[#1B0D00] pb-2 border-b-4 border-[#1B0D00]"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
Chapters
</h2>
<ul className="flex-1 overflow-y-auto no-scrollbar space-y-0">
{chapters.map((ch) => {
const active =
pathname === `/sem5/cns/${ch.id}` ||
(ch.subTopics?.some(
(sub) => sub.isPage && pathname === `/sem5/cns/${sub.id}`
) ?? false);
return (
<li key={ch.id} className="flex flex-col">
<Link
href={`/sem5/cns/${ch.id}`}
className={`block px-3 py-2 text-xl transition ${
active ? "bg-[#fccc7e]" : "hover:bg-[#ffdda7af]"
} ${righteous.className}`}
>
{ch.title}
</Link>
{active && ch.subTopics && (
<ul className="ml-4 border-l-2 border-[#1B0D00]/20 pl-2 my-2 space-y-2">
{ch.subTopics.map((sub) => {
const subActive =
sub.isPage && pathname === `/sem5/cns/${sub.id}`;
return (
<li key={sub.id}>
<Link
href={
sub.isPage
? `/sem5/cns/${sub.id}`
: `/sem5/cns/${ch.id}#${sub.id}`
}
className={`block text-sm transition hover:font-bold ${
subActive
? "text-black font-bold"
: "text-[#3a2a14] hover:text-black"
}`}
>
{sub.title}
</Link>
</li>
);
})}
</ul>
)}
</li>
);
})}
</ul>

<div className="border-t-4 border-[#1B0D00]">
<h2
className="flex items-center text-2xl font-normal pt-3 pl-3 mb-2 bg-[#cebb9c] text-[#1B0D00] pb-2"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
Quiz
</h2>
<Link
href={quizHref}
className={`flex items-center gap-2 px-3 py-2 text-xl transition ${
quizActive ? "bg-[#fccc7e]" : "hover:bg-[#ffdda7af]"
} ${righteous.className}`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 shrink-0"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 20h9" />
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z" />
</svg>
Take the Quiz
</Link>
</div>
</aside>

<button
onClick={() => setOpen(!open)}
className="toggle-sidebar shrink-0 pointer-events-auto bg-[#ffdda7] h-full w-[50px] text-[#1B0D00] text-center font-semibold text-2xl border-l-4 rounded-r-2xl border-[#1B0D00] flex items-center justify-center transition-all duration-300 md:shadow-none"
style={{
fontFamily: "Rockwell, Serif, serif",
boxShadow: open ? "4px 0 15px rgba(0,0,0,0.1)" : "none",
}}
>
<p className="leading-5">
C<br />H<br />A<br />P<br />T<br />E<br />R<br />S
</p>
</button>
</div>
</>
);
}
Loading