Skip to content

Commit 9082f58

Browse files
authored
Merge branch 'MinavKaria:main' into main
2 parents a09b69e + ffc3ef1 commit 9082f58

12 files changed

Lines changed: 317 additions & 13 deletions

File tree

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Minav Karia
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,9 @@ Want to improve **Notes-Aid**? Follow these steps:
7676
Your contributions are **always welcome!** 🎉
7777

7878
---
79+
80+
## 📄 License
81+
82+
This project is licensed under the [MIT License](LICENSE).
83+
84+
---

notes-aid/src/app/[year]/[branch]/[semester]/page.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,42 @@ const EngineeringCurriculum: React.FC = () => {
110110
const initialSubject = subjects ? Object.keys(subjects)[0] : "";
111111
const [selectedSubject, setSelectedSubject] = useState(initialSubject);
112112
const [selectedModule, setSelectedModule] = useState<number>(1);
113+
114+
115+
useEffect(() => {
116+
const params = new URLSearchParams(window.location.search);
117+
const fromBookmark = params.get('fromBookmark');
118+
119+
if (fromBookmark) {
120+
try {
121+
const bookmarkState = JSON.parse(fromBookmark);
122+
if (bookmarkState.selectedSubject && bookmarkState.selectedModule) {
123+
setSelectedSubject(bookmarkState.selectedSubject);
124+
setSelectedModule(bookmarkState.selectedModule);
125+
}
126+
} catch (e) {
127+
console.error('Error parsing bookmark state', e);
128+
}
129+
}
130+
}, []);
113131

114132
useEffect(() => {
115-
if (subjects && selectedSubject) {
133+
if (subjects && selectedSubject) {
134+
const params = new URLSearchParams(window.location.search);
135+
const moduleParam = params.get("module");
136+
137+
if (moduleParam) {
138+
setSelectedModule(parseInt(moduleParam));
139+
} else {
116140
const firstModuleKey = Object.keys(
117141
subjects[selectedSubject]?.modules || {}
118142
)[0];
119143
setSelectedModule(firstModuleKey ? parseInt(firstModuleKey) : 1);
120144
}
145+
}
121146
}, [selectedSubject, subjects]);
122147

148+
123149
const { progressData, updateVideoProgress, resetProgress } = useProgress(selectedSubject);
124150

125151
if (loading) {
@@ -270,6 +296,7 @@ const EngineeringCurriculum: React.FC = () => {
270296
<ModuleCard
271297
key={moduley}
272298
module={moduley}
299+
subjectName={subjects[selectedSubject].name}
273300
topics={
274301
subjects[selectedSubject].modules[moduley].topics.length
275302
}
@@ -279,7 +306,9 @@ const EngineeringCurriculum: React.FC = () => {
279306
progressData.moduleProgress[moduley] || 0
280307
}
281308
numberOfVideos={numberVideoInModule(moduley)}
282-
309+
currentSubject={selectedSubject}
310+
311+
283312
/>
284313
);
285314
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
'use client';
2+
import { useEffect, useState } from 'react';
3+
import { ChevronDown } from 'lucide-react';
4+
import Link from 'next/link';
5+
6+
interface BookmarkItem {
7+
id: string;
8+
title: string;
9+
subject: string;
10+
url?: string;
11+
module?: number;
12+
topic?: string;
13+
}
14+
15+
type BookmarkType = 'modules' | 'topics' | 'videos';
16+
17+
export default function BookmarksPage() {
18+
const [bookmarks, setBookmarks] = useState<BookmarkItem[]>([]);
19+
const [activeTab, setActiveTab] = useState<BookmarkType>('modules');
20+
const [activeBookmarkId, setActiveBookmarkId] = useState<string | null>(null);
21+
22+
const toggleActive = (id: string) => {
23+
setActiveBookmarkId(prev => (prev === id ? null : id));
24+
};
25+
26+
useEffect(() => {
27+
const stored = JSON.parse(localStorage.getItem('bookmarks') || '[]');
28+
setBookmarks(stored);
29+
}, []);
30+
31+
const removeBookmark = (id: string) => {
32+
const updated = bookmarks.filter(b => b.id !== id);
33+
localStorage.setItem('bookmarks', JSON.stringify(updated));
34+
setBookmarks(updated);
35+
};
36+
37+
const filteredBookmarks = bookmarks.filter(bookmark => {
38+
if (activeTab === 'modules') return !bookmark.id.includes('topic') && !bookmark.id.includes('video');
39+
if (activeTab === 'topics') return bookmark.id.includes('topic');
40+
if (activeTab === 'videos') return bookmark.id.includes('video');
41+
return true;
42+
});
43+
44+
return (
45+
<div className="h-screen max-w-4xl mx-auto px-4 py-8">
46+
<h1 className="text-2xl font-bold mb-6 dark:text-white">Your Bookmarks</h1>
47+
48+
<div className="flex border-b border-gray-200 dark:border-gray-700 mb-6">
49+
<button
50+
onClick={() => setActiveTab('modules')}
51+
className={`px-4 py-2 font-medium text-sm ${activeTab === 'modules' ? 'text-primary border-b-2 border-primary' : 'text-gray-500 dark:text-gray-400'}`}
52+
>
53+
Modules
54+
</button>
55+
<button
56+
onClick={() => setActiveTab('topics')}
57+
className={`px-4 py-2 font-medium text-sm ${activeTab === 'topics' ? 'text-primary border-b-2 border-primary' : 'text-gray-500 dark:text-gray-400'}`}
58+
>
59+
Topics
60+
</button>
61+
<button
62+
onClick={() => setActiveTab('videos')}
63+
className={`px-4 py-2 font-medium text-sm ${activeTab === 'videos' ? 'text-primary border-b-2 border-primary' : 'text-gray-500 dark:text-gray-400'}`}
64+
>
65+
Videos
66+
</button>
67+
</div>
68+
69+
{filteredBookmarks.length === 0 ? (
70+
<p className="text-gray-600 dark:text-gray-400">
71+
No {activeTab} bookmarked yet
72+
</p>
73+
) : (
74+
<div className="space-y-4">
75+
{filteredBookmarks.map((item) => (
76+
<div
77+
key={item.id}
78+
className="p-4 border rounded-lg hover:bg-[#585859] hover:text-white dark:border-gray-700 transition-colors"
79+
>
80+
<div className="flex justify-between items-center">
81+
<div>
82+
<Link href="/" className="block">
83+
<h3 className="font-medium dark:text-white hover:underline">
84+
{item.title}
85+
</h3>
86+
</Link>
87+
<p className="text-sm text-gray-400 dark:text-gray-400">
88+
{item.subject} {item.module && `• Module ${item.module}`}
89+
</p>
90+
{activeTab === 'videos' && (
91+
<p className="text-xs text-gray-400 mt-1">Video</p>
92+
)}
93+
</div>
94+
<div className="flex items-center gap-2">
95+
{activeTab === 'videos' && (
96+
<ChevronDown
97+
onClick={() => toggleActive(item.id)}
98+
className={`w-4 h-4 text-gray-500 dark:text-gray-400 transition-transform duration-200 cursor-pointer ${
99+
activeBookmarkId === item.id ? 'rotate-180' : ''
100+
}`}
101+
/>
102+
)}
103+
<button
104+
onClick={() => removeBookmark(item.id)}
105+
className="text-red-500 hover:text-red-700 dark:hover:text-red-400 p-1"
106+
>
107+
Remove
108+
</button>
109+
</div>
110+
</div>
111+
112+
{activeTab === 'videos' && activeBookmarkId === item.id && item.url && (
113+
<div className="mt-3">
114+
<div className="aspect-video">
115+
<iframe
116+
width="100%"
117+
height="100%"
118+
src={`${item.url}?enablejsapi=1`}
119+
title={item.title}
120+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
121+
allowFullScreen
122+
className="rounded-lg"
123+
></iframe>
124+
</div>
125+
</div>
126+
)}
127+
</div>
128+
))}
129+
</div>
130+
)}
131+
</div>
132+
);
133+
}

notes-aid/src/app/globals.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ layer(base);
77

88
@custom-variant dark (&:is(.dark *));
99

10-
11-
1210
.text-gray {
1311
color: #C2C0B6;
1412
}
@@ -28,3 +26,4 @@ code {
2826
}
2927

3028
/* this is a test */
29+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"use client";
2+
import { useState, useEffect } from 'react';
3+
4+
interface BookmarkItem {
5+
id: string;
6+
title: string;
7+
type: 'module' | 'topic' | 'video';
8+
subject: string;
9+
module?: number;
10+
topics?: string;
11+
url?: string;
12+
state?: {
13+
selectedsubject: string;
14+
selectedmodule: number;
15+
}
16+
}
17+
18+
export const BookmarkButton = ({ item }: { item: BookmarkItem }) => {
19+
const [isBookmarked, setIsBookmarked] = useState(false);
20+
21+
useEffect(() => {
22+
const bookmarks = getBookmarks();
23+
setIsBookmarked(bookmarks.some(b => b.id === item.id));
24+
}, [item.id]);
25+
26+
const toggleBookmark = () => {
27+
const bookmarks = getBookmarks();
28+
const updatedBookmarks = isBookmarked
29+
? bookmarks.filter(b => b.id !== item.id)
30+
: [...bookmarks, item]; // item includes url for videos
31+
32+
localStorage.setItem('bookmarks', JSON.stringify(updatedBookmarks));
33+
setIsBookmarked(!isBookmarked);
34+
};
35+
36+
return (
37+
<button
38+
onClick={toggleBookmark}
39+
className="p-1 h-8 w-8 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
40+
aria-label={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}
41+
>
42+
{isBookmarked ? (
43+
<span className="text-yellow-500"></span>
44+
) : (
45+
<span className="text-gray-400 dark:text-gray-500"></span>
46+
)}
47+
</button>
48+
);
49+
};
50+
51+
const getBookmarks = (): BookmarkItem[] => {
52+
try {
53+
return JSON.parse(localStorage.getItem('bookmarks') || '[]');
54+
} catch {
55+
return [];
56+
}
57+
};

notes-aid/src/components/Footer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ const Footer = () => {
4545
</Link>
4646
&amp;
4747
<Link
48-
href="https://github.com/aarushsaboo"
48+
href="https://github.com/yashankkothari"
4949
target="_blank"
5050
className="underline hover:text-primary"
5151
>
52-
Aarush
52+
Yashank
5353
</Link>
5454
</div>
5555
</div>

notes-aid/src/components/ModuleCard.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,28 @@ import React from "react";
22
// import { useState } from "react";
33
import { ChevronDown } from "lucide-react";
44
import ProgressBar from "./ProgressBar";
5+
import { BookmarkButton } from "./BookmarkButton";
56

67
interface ModuleCardProps {
78
module: number;
9+
subjectName: string;
810
topics: number;
911
isActive: boolean;
1012
onClick: () => void;
1113
numberOfVideos: number;
1214
numberOfVideosCompleted: number;
15+
currentSubject: string;
1316
}
1417

1518
const ModuleCard: React.FC<ModuleCardProps> = ({
1619
module,
20+
subjectName,
1721
topics,
1822
isActive,
1923
onClick,
2024
numberOfVideos,
21-
numberOfVideosCompleted
25+
numberOfVideosCompleted,
26+
currentSubject
2227
}) => {
2328
// const total = 100;
2429
// const [done, setdone] = useState(20);
@@ -66,7 +71,7 @@ const ModuleCard: React.FC<ModuleCardProps> = ({
6671
}
6772
`}
6873
>
69-
<div className="flex flex-col gap-2 ">
74+
<div className="flex flex-col gap-2">
7075
<div className="flex items-center justify-between">
7176
<div className="flex items-center gap-3">
7277
<h3 className={` font-semibold text-base-content`}>
@@ -76,11 +81,28 @@ const ModuleCard: React.FC<ModuleCardProps> = ({
7681
{topics} topics
7782
</span>
7883
</div>
84+
85+
86+
<div className="flex justify-center items-center gap-3">
87+
<BookmarkButton
88+
item={{
89+
id: `${subjectName}-module-${module}`,
90+
title: `Module ${module}`,
91+
subject: subjectName,
92+
type: 'module',
93+
module: module,
94+
state: {
95+
selectedsubject: currentSubject,
96+
selectedmodule: module
97+
}
98+
}}
99+
/>
79100
<ChevronDown
80101
className={`w-4 h-4 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${
81102
isActive ? "rotate-180" : ""
82103
}`}
83104
/>
105+
</div>
84106
</div>
85107

86108
<div className="flex items-center gap-2">

0 commit comments

Comments
 (0)