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: 2 additions & 2 deletions app/components/subjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const subjectCodes: Record<string, string> = {
"Information Retrieval": "ir",
"Management Information Systems": "mis",
"VLSI and Embedded Systems": "vlsi",
"Entrepreneurship & Startup": "es",
"Entrepreneurship & Startup": "eas",
"Financial Management": "fm",
"Robotic Access Automation": "raa",
"Marketing Management": "mm",
Expand All @@ -125,7 +125,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","eas"];

export default function SubjectsSection() {
return (
Expand Down
208 changes: 208 additions & 0 deletions app/sem7/eas/[chapter]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import Link from "next/link";
import { Metadata } from "next";
import { Righteous } from "next/font/google";
import { ArrowBigLeft, ArrowBigRight } from "lucide-react";

import { chapters, Chapter, SubTopic } from "../constants";
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 BookmarkButton from "../../../components/BookmarkButton";

// Map chapter/subtopic IDs to their content components
const chapterComponents: Record<string, React.ComponentType> = {
ch0: Ch0Content,
ch1: Ch1Content,
ch2: Ch2Content,
ch3: Ch3Content,
ch4: Ch4Content,
};

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

// Helper: find a chapter or subtopic by id
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 };
}

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

// Dynamic SEO metadata
export async function generateMetadata({ params }: ChapterProps): Promise<Metadata> {
const { chapter: chapterId } = await params;
const { data: chapterData } = findChapterOrSubtopic(chapterId);

const title = chapterData
? `${chapterData.title} | Entrepreneurship & Startup | openCSE`
: "Entrepreneurship & Startup | openCSE";

return { title };
}

export default async function ChapterPage({ params }: ChapterProps) {
const { chapter: chapterId } = await params;

const { data: chapterData, isSubTopic, parentChapter } =
findChapterOrSubtopic(chapterId);

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

// For subtopics: use their parent chapter's content component
const componentKey = isSubTopic && parentChapter ? parentChapter.id : chapterData.id;
const ChapterComponent = chapterComponents[componentKey] ?? null;

// Prev / Next navigation
let prevChapter: { id: string; title: string } | null = null;
let nextChapter: { id: string; title: string } | null = 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);
prevChapter = currentIndex > 0 ? chapters[currentIndex - 1] : null;
nextChapter =
currentIndex < chapters.length - 1 ? chapters[currentIndex + 1] : null;
}

return (
<div className="flex flex-col bg-[#1B0D00] min-h-full p-2 pt-6 text-[#e2d1c1]">
<div className="flex-1">
{/* Subject title */}
<h1 className={`text-3xl sm:text-4xl font-bold ${righteous.className} mb-2`}>
Entrepreneurship & Startup
</h1>

{/* Chapter title + bookmark */}
<div className="flex items-center justify-between gap-2 flex-wrap">
<p className={`text-xl sm:text-2xl mt-[-4px] ${righteous.className}`}>
{isSubTopic && parentChapter
? `${parentChapter.title} / ${chapterData.title}`
: chapterData.title}
</p>
<BookmarkButton title={`EAS: ${chapterData.title}`} />
</div>

{/* Top navigation */}
<div className="flex justify-between mt-3 gap-2">
{prevChapter ? (
<Link
href={`/sem7/eas/${prevChapter.id}`}
className="px-3 py-1 text-lg sm: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={`/sem7/eas/${nextChapter.id}`}
className="px-3 py-1 text-lg sm: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-3" />

{/* Chapter content */}
{ChapterComponent ? (
<ChapterComponent />
) : (
<p className="text-[#e2d1c1] opacity-60 text-center mt-12">
Content coming soon.
</p>
)}
</div>

{/* Bottom navigation */}
<div className="flex justify-between my-8 gap-2 flex-wrap">
{prevChapter ? (
<Link
href={`/sem7/eas/${prevChapter.id}`}
className="px-3 py-2 bg-[#e2d1c1] text-base sm:text-xl flex items-center justify-center text-[#1b0d00] rounded hover:bg-[#ac9e91] transition max-w-[45%] text-center"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
<ArrowBigLeft className="inline-block mr-1 shrink-0" />
<span className="truncate">{prevChapter.title}</span>
</Link>
) : (
<div />
)}

{nextChapter ? (
<Link
href={`/sem7/eas/${nextChapter.id}`}
className="px-3 py-2 bg-[#e2d1c1] text-base sm:text-xl flex items-center justify-center text-[#1b0d00] rounded hover:bg-[#ac9e91] transition max-w-[45%] text-center"
style={{ fontFamily: "Rockwell, Serif, serif" }}
>
<span className="truncate">{nextChapter.title}</span>
<ArrowBigRight className="inline-block ml-1 shrink-0" />
</Link>
) : (
<div />
)}
</div>
</div>
);
}
144 changes: 144 additions & 0 deletions app/sem7/eas/components/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"use client";
import { Righteous } from "next/font/google";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState, useEffect } from "react";
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 (window.innerWidth >= 768) {
setOpen(true);
}
}, []);

const quizHref = "/quiz/eas";
const quizActive = pathname.startsWith("/quiz");

return (
<>
{/* Backdrop overlay - only on mobile when open */}
<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">
{/* Sidebar */}
<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 === `/sem7/eas/${ch.id}` ||
(ch.subTopics?.some(
(sub) => sub.isPage && pathname === `/sem7/eas/${sub.id}`
) ?? false);
return (
<li key={ch.id} className="flex flex-col">
<Link
href={`/sem7/eas/${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 === `/sem7/eas/${sub.id}`;
return (
<li key={sub.id}>
<Link
href={
sub.isPage
? `/sem7/eas/${sub.id}`
: `/sem7/eas/${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