Skip to content

Commit 71e5f43

Browse files
Merge pull request saumyayadav25#241 from SaiSruthisri/main
Flashcards
2 parents b137909 + 19cd8e3 commit 71e5f43

File tree

9 files changed

+988
-0
lines changed

9 files changed

+988
-0
lines changed

app/flashcards/page.tsx

Lines changed: 462 additions & 0 deletions
Large diffs are not rendered by default.

components/AuthButtons.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ export default function AuthButtons() {
4646
href: "/cp-tracker",
4747
icon: "🎯",
4848
},
49+
{
50+
label: "Flashcards",
51+
href: "/flashcards",
52+
icon: "🧠",
53+
},
4954
{
5055
label: "Star on GitHub",
5156
href: "https://github.com/saumyayadav25/DSA-Supreme-3.0",
@@ -92,6 +97,7 @@ export default function AuthButtons() {
9297
{ href: "/", label: "Home", isActive: pathname === "/" },
9398
{ href: "/notes", label: "Notes", isActive: pathname === "/notes" },
9499
{ href: "/sheet", label: "Sheet", isActive: pathname === "/sheet" },
100+
{ href: "/flashcards", label: "Flashcards", isActive: pathname === "/flashcards" },
95101
{
96102
href: "/progress",
97103
label: "Progress",

components/Filters.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use client";
2+
3+
interface FiltersProps {
4+
onFilterChange: (difficulty: string) => void;
5+
currentFilter: string;
6+
}
7+
8+
export default function Filters({ onFilterChange, currentFilter }: FiltersProps) {
9+
return (
10+
<div className="flex gap-4 mt-4">
11+
{["All", "Basic", "Intermediate"].map((filter) => (
12+
<button
13+
key={filter}
14+
onClick={() => onFilterChange(filter)}
15+
className={`px-4 py-2 rounded ${
16+
currentFilter === filter ? "bg-blue-500 text-white" : "bg-gray-200"
17+
}`}
18+
>
19+
{filter}
20+
</button>
21+
))}
22+
</div>
23+
);
24+
}

components/Flashcard.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use client";
2+
3+
import { motion } from "framer-motion";
4+
import { useState } from "react";
5+
6+
interface FlashcardProps {
7+
term: string;
8+
explanation: string;
9+
}
10+
11+
export default function Flashcard({ term, explanation }: FlashcardProps) {
12+
const [flipped, setFlipped] = useState(false);
13+
14+
return (
15+
<>
16+
{/* Custom CSS via Tailwind utilities */}
17+
<style jsx global>{`
18+
.perspective {
19+
perspective: 1000px;
20+
}
21+
.backface-hidden {
22+
backface-visibility: hidden;
23+
}
24+
`}</style>
25+
26+
<div
27+
className="w-80 h-48 cursor-pointer perspective"
28+
onClick={() => setFlipped(!flipped)}
29+
>
30+
<motion.div
31+
className="relative w-full h-full"
32+
animate={{ rotateY: flipped ? 180 : 0 }}
33+
transition={{ duration: 0.6 }}
34+
style={{ transformStyle: "preserve-3d" }}
35+
>
36+
{/* Front */}
37+
<div className="absolute w-full h-full bg-blue-500 text-white flex items-center justify-center rounded-xl backface-hidden">
38+
<h2 className="text-2xl font-bold">{term}</h2>
39+
</div>
40+
{/* Back */}
41+
<div
42+
className="absolute w-full h-full bg-gray-800 text-white flex items-center justify-center rounded-xl backface-hidden"
43+
style={{ transform: "rotateY(180deg)" }}
44+
>
45+
<p className="text-center px-4">{explanation}</p>
46+
</div>
47+
</motion.div>
48+
</div>
49+
</>
50+
);
51+
}

components/FlashcardComponent.tsx

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
"use client";
2+
3+
import { motion, AnimatePresence } from "framer-motion";
4+
import { useState } from "react";
5+
import { Flashcard } from "@/data/flashcards";
6+
7+
interface FlashcardComponentProps {
8+
flashcard: Flashcard;
9+
isFlipped: boolean;
10+
onFlip: () => void;
11+
}
12+
13+
export default function FlashcardComponent({
14+
flashcard,
15+
isFlipped,
16+
onFlip
17+
}: FlashcardComponentProps) {
18+
const [isHovered, setIsHovered] = useState(false);
19+
20+
const getDifficultyColor = (difficulty: string) => {
21+
switch (difficulty) {
22+
case 'Basic':
23+
return 'from-green-500 to-emerald-500';
24+
case 'Intermediate':
25+
return 'from-yellow-500 to-orange-500';
26+
default:
27+
return 'from-blue-500 to-cyan-500';
28+
}
29+
};
30+
31+
const getCategoryIcon = (category: string) => {
32+
switch (category) {
33+
case 'Time Complexity':
34+
return '⏱️';
35+
case 'Graph Traversals':
36+
return '🕸️';
37+
case 'Sorting':
38+
return '📊';
39+
case 'Data Structures':
40+
return '🏗️';
41+
case 'Recursion':
42+
return '🔄';
43+
default:
44+
return '💡';
45+
}
46+
};
47+
48+
return (
49+
<div className="perspective-1000 w-full max-w-2xl mx-auto">
50+
<motion.div
51+
className="relative w-full h-80 cursor-pointer preserve-3d"
52+
onClick={onFlip}
53+
onHoverStart={() => setIsHovered(true)}
54+
onHoverEnd={() => setIsHovered(false)}
55+
animate={{
56+
rotateY: isFlipped ? 180 : 0,
57+
scale: isHovered ? 1.02 : 1,
58+
}}
59+
transition={{
60+
duration: 0.6,
61+
type: "spring",
62+
stiffness: 300,
63+
damping: 30
64+
}}
65+
style={{ transformStyle: "preserve-3d" }}
66+
>
67+
{/* Front of card */}
68+
<motion.div
69+
className="absolute inset-0 backface-hidden rounded-2xl overflow-hidden"
70+
style={{ backfaceVisibility: "hidden" }}
71+
>
72+
{/* Background with gradient and effects */}
73+
<div className="relative w-full h-full bg-white dark:bg-gray-900 border border-gray-200/50 dark:border-white/10 rounded-2xl overflow-hidden">
74+
{/* Animated background gradient */}
75+
<motion.div
76+
className={`absolute inset-0 bg-gradient-to-br ${getDifficultyColor(flashcard.difficulty)} opacity-0`}
77+
animate={{
78+
opacity: isHovered ? 0.1 : 0,
79+
backgroundPosition: isHovered ? "100% 100%" : "0% 0%",
80+
}}
81+
transition={{ duration: 0.6 }}
82+
style={{ backgroundSize: "200% 200%" }}
83+
/>
84+
85+
{/* Glowing border effect */}
86+
<motion.div
87+
className="absolute inset-0 rounded-2xl"
88+
animate={{
89+
boxShadow: isHovered
90+
? "0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 20px rgba(59, 130, 246, 0.2)"
91+
: "0 0 0 1px rgba(229, 231, 235, 0.3)"
92+
}}
93+
transition={{ duration: 0.3 }}
94+
/>
95+
96+
{/* Shimmer effect */}
97+
<motion.div
98+
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -skew-x-12"
99+
initial={{ x: "-100%" }}
100+
animate={{ x: isHovered ? "200%" : "-100%" }}
101+
transition={{ duration: 0.8, ease: "easeInOut" }}
102+
/>
103+
104+
{/* Content */}
105+
<div className="relative z-10 p-8 h-full flex flex-col justify-between">
106+
{/* Header */}
107+
<div className="flex items-start justify-between mb-6">
108+
<div className="flex items-center gap-3">
109+
<div className={`w-12 h-12 rounded-xl bg-gradient-to-br ${getDifficultyColor(flashcard.difficulty)} flex items-center justify-center text-2xl shadow-lg`}>
110+
{getCategoryIcon(flashcard.category)}
111+
</div>
112+
<div>
113+
<div className={`text-sm font-semibold px-3 py-1 rounded-full bg-gradient-to-r ${getDifficultyColor(flashcard.difficulty)} text-white`}>
114+
{flashcard.difficulty}
115+
</div>
116+
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
117+
{flashcard.category}
118+
</div>
119+
</div>
120+
</div>
121+
122+
{/* Flip indicator */}
123+
<motion.div
124+
className="text-gray-400 dark:text-gray-500"
125+
animate={{ rotate: isHovered ? 180 : 0 }}
126+
transition={{ duration: 0.3 }}
127+
>
128+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
129+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
130+
<path d="M21 3v5h-5"/>
131+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
132+
<path d="M3 21v-5h5"/>
133+
</svg>
134+
</motion.div>
135+
</div>
136+
137+
{/* Term */}
138+
<div className="flex-1 flex items-center justify-center text-center">
139+
<motion.h2
140+
className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white leading-tight"
141+
animate={{
142+
color: isHovered ? "#3B82F6" : undefined
143+
}}
144+
transition={{ duration: 0.3 }}
145+
>
146+
{flashcard.term}
147+
</motion.h2>
148+
</div>
149+
150+
{/* Footer */}
151+
<div className="text-center">
152+
<motion.p
153+
className="text-gray-500 dark:text-gray-400 text-sm"
154+
animate={{ opacity: isHovered ? 1 : 0.7 }}
155+
>
156+
Click to reveal explanation
157+
</motion.p>
158+
</div>
159+
</div>
160+
</div>
161+
</motion.div>
162+
163+
{/* Back of card */}
164+
<motion.div
165+
className="absolute inset-0 backface-hidden rounded-2xl overflow-hidden"
166+
style={{
167+
backfaceVisibility: "hidden",
168+
transform: "rotateY(180deg)"
169+
}}
170+
>
171+
{/* Background */}
172+
<div className="relative w-full h-full bg-white dark:bg-gray-900 border border-gray-200/50 dark:border-white/10 rounded-2xl overflow-hidden">
173+
{/* Animated background gradient */}
174+
<motion.div
175+
className={`absolute inset-0 bg-gradient-to-br ${getDifficultyColor(flashcard.difficulty)} opacity-0`}
176+
animate={{
177+
opacity: isHovered ? 0.1 : 0,
178+
backgroundPosition: isHovered ? "100% 100%" : "0% 0%",
179+
}}
180+
transition={{ duration: 0.6 }}
181+
style={{ backgroundSize: "200% 200%" }}
182+
/>
183+
184+
{/* Glowing border effect */}
185+
<motion.div
186+
className="absolute inset-0 rounded-2xl"
187+
animate={{
188+
boxShadow: isHovered
189+
? "0 0 0 2px rgba(139, 92, 246, 0.3), 0 0 20px rgba(139, 92, 246, 0.2)"
190+
: "0 0 0 1px rgba(229, 231, 235, 0.3)"
191+
}}
192+
transition={{ duration: 0.3 }}
193+
/>
194+
195+
{/* Content */}
196+
<div className="relative z-10 p-8 h-full flex flex-col">
197+
{/* Header */}
198+
<div className="flex items-center justify-between mb-6">
199+
<div className="flex items-center gap-3">
200+
<div className={`w-10 h-10 rounded-lg bg-gradient-to-br ${getDifficultyColor(flashcard.difficulty)} flex items-center justify-center text-lg shadow-lg`}>
201+
💡
202+
</div>
203+
<div className="text-sm font-semibold text-gray-600 dark:text-gray-300">
204+
Explanation
205+
</div>
206+
</div>
207+
208+
{/* Back indicator */}
209+
<motion.div
210+
className="text-gray-400 dark:text-gray-500"
211+
animate={{ rotate: isHovered ? 180 : 0 }}
212+
transition={{ duration: 0.3 }}
213+
>
214+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
215+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
216+
<path d="M21 3v5h-5"/>
217+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
218+
<path d="M3 21v-5h5"/>
219+
</svg>
220+
</motion.div>
221+
</div>
222+
223+
{/* Explanation */}
224+
<div className="flex-1 flex items-center">
225+
<motion.div
226+
initial={{ opacity: 0, y: 20 }}
227+
animate={{ opacity: 1, y: 0 }}
228+
transition={{ duration: 0.5, delay: 0.3 }}
229+
className="w-full"
230+
>
231+
<p className="text-gray-700 dark:text-gray-300 leading-relaxed text-lg">
232+
{flashcard.explanation}
233+
</p>
234+
</motion.div>
235+
</div>
236+
237+
{/* Footer */}
238+
<div className="text-center mt-6">
239+
<motion.p
240+
className="text-gray-500 dark:text-gray-400 text-sm"
241+
animate={{ opacity: isHovered ? 1 : 0.7 }}
242+
>
243+
Click to flip back
244+
</motion.p>
245+
</div>
246+
</div>
247+
</div>
248+
</motion.div>
249+
</motion.div>
250+
</div>
251+
);
252+
}

components/ProgressBar.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use client";
2+
3+
interface ProgressBarProps {
4+
currentIndex: number;
5+
total: number;
6+
}
7+
8+
export default function ProgressBar({ currentIndex, total }: ProgressBarProps) {
9+
const progress = ((currentIndex + 1) / total) * 100;
10+
11+
return (
12+
<div className="w-full max-w-xs bg-gray-200 h-2 rounded mt-6">
13+
<div
14+
className="bg-blue-500 h-2 rounded"
15+
style={{ width: `${progress}%` }}
16+
></div>
17+
</div>
18+
);
19+
}
20+
21+
//This is progress bar for flascard page

0 commit comments

Comments
 (0)