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
32 changes: 32 additions & 0 deletions src/components/AchievementNotification.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useState, useEffect } from 'react';
import styles from './AchievementNotification.module.css';

const AchievementNotification = () => {
const [notification, setNotification] = useState(null);

useEffect(() => {
const handleAchievement = (event) => {
setNotification(event.detail);
setTimeout(() => {
setNotification(null);
}, 5000); // Hide after 5 seconds
};

window.addEventListener('achievementUnlocked', handleAchievement);
return () => window.removeEventListener('achievementUnlocked', handleAchievement);
}, []);

if (!notification) return null;

return (
<div className={styles.container}>
<div className={styles.icon}>{notification.icon}</div>
<div className={styles.textContainer}>
<div className={styles.title}>Achievement Unlocked!</div>
<div className={styles.description}>{notification.title}</div>
</div>
</div>
);
};

export default AchievementNotification;
56 changes: 56 additions & 0 deletions src/components/AchievementNotification.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.container {
position: fixed;
top: 80px;
right: 20px;
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
color: #333;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(255, 215, 0, 0.3);
display: flex;
align-items: center;
gap: 12px;
z-index: 1100;
animation: slideIn 0.5s ease-out;
max-width: 350px;
border: 2px solid #e6c200;
}

.icon {
font-size: 2rem;
}

.textContainer {
flex: 1;
}

.title {
font-weight: 700;
font-size: 1rem;
margin-bottom: 4px;
}

.description {
font-size: 0.9rem;
opacity: 0.9;
}

@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}

/* Mobile responsiveness */
@media (max-width: 768px) {
.container {
right: 10px;
left: 10px;
max-width: none;
}
}
85 changes: 85 additions & 0 deletions src/components/Achievements.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useState, useEffect } from 'react';
import { getAchievements, getUserStats } from '../utils/progressTracker';
import { Trophy, Star, Zap, Flame } from 'lucide-react';
import styles from './Achievements.module.css';

const Achievements = () => {
const [achievements, setAchievements] = useState([]);
const [userStats, setUserStats] = useState({});

useEffect(() => {
setAchievements(getAchievements());
setUserStats(getUserStats());
}, []);

useEffect(() => {
const handleAchievementUnlocked = (event) => {
setAchievements(getAchievements());
setUserStats(getUserStats());
};

window.addEventListener('achievementUnlocked', handleAchievementUnlocked);
return () => window.removeEventListener('achievementUnlocked', handleAchievementUnlocked);
}, []);

return (
<div className={styles.container}>
<div className={styles.statsSection}>
<h3 className={styles.title}>
<Trophy size={24} />
Your Progress
</h3>
<div className={styles.statsGrid}>
<div className={styles.statCard}>
<div className={styles.statIcon}>⚡</div>
<div className={styles.statValue}>{userStats.totalXP || 0}</div>
<div className={styles.statLabel}>Total XP</div>
</div>
<div className={styles.statCard}>
<div className={styles.statIcon}>🔥</div>
<div className={styles.statValue}>{userStats.streak || 0}</div>
<div className={styles.statLabel}>Day Streak</div>
</div>
<div className={styles.statCard}>
<div className={styles.statIcon}>📚</div>
<div className={styles.statValue}>{userStats.totalModulesCompleted || 0}</div>
<div className={styles.statLabel}>Modules Done</div>
</div>
<div className={styles.statCard}>
<div className={styles.statIcon}>🏆</div>
<div className={styles.statValue}>{userStats.achievements || 0}</div>
<div className={styles.statLabel}>Achievements</div>
</div>
</div>
</div>

<div className={styles.achievementsSection}>
<h3 className={styles.title}>
<Star size={24} />
Achievements
</h3>
<div className={styles.achievementsGrid}>
{achievements.map(achievement => (
<div
key={achievement.id}
className={`${styles.achievementCard} ${
achievement.unlocked ? styles.unlocked : styles.locked
}`}
>
<div className={styles.achievementIcon}>
{achievement.icon}
</div>
<h4 className={styles.achievementTitle}>{achievement.title}</h4>
<p className={styles.achievementDescription}>{achievement.description}</p>
{achievement.unlocked && (
<div className={styles.unlockedBadge}>Unlocked!</div>
)}
</div>
))}
</div>
</div>
</div>
);
};

export default Achievements;
144 changes: 144 additions & 0 deletions src/components/Achievements.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
.container {
max-width: 1200px;
margin: 0 auto;
padding: 32px 20px;
}

.statsSection {
margin-bottom: 48px;
}

.title {
display: flex;
align-items: center;
gap: 12px;
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 24px;
color: var(--ifm-color-primary);
}

.statsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}

.statCard {
background: var(--ifm-background-color);
border: 2px solid var(--ifm-color-emphasis-200);
border-radius: 16px;
padding: 24px;
text-align: center;
transition: all 0.3s ease;
}

.statCard:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
border-color: var(--ifm-color-primary);
}

.statIcon {
font-size: 2.5rem;
margin-bottom: 12px;
}

.statValue {
font-size: 2.5rem;
font-weight: 700;
color: var(--ifm-color-primary);
margin-bottom: 8px;
}

.statLabel {
font-size: 0.9rem;
color: var(--ifm-color-emphasis-600);
font-weight: 500;
}

.achievementsSection {
margin-top: 48px;
}

.achievementsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}

.achievementCard {
background: var(--ifm-background-color);
border: 2px solid var(--ifm-color-emphasis-200);
border-radius: 16px;
padding: 24px;
text-align: center;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}

.achievementCard.unlocked {
border-color: var(--ifm-color-success);
background: linear-gradient(135deg,
var(--ifm-background-color) 0%,
var(--ifm-color-success-contrast-background) 100%);
}

.achievementCard.locked {
opacity: 0.6;
filter: grayscale(30%);
}

.achievementCard:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}

.achievementIcon {
font-size: 3rem;
margin-bottom: 16px;
}

.achievementTitle {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 8px;
color: var(--ifm-color-emphasis-800);
}

.achievementDescription {
font-size: 0.9rem;
color: var(--ifm-color-emphasis-600);
line-height: 1.5;
margin-bottom: 16px;
}

.unlockedBadge {
position: absolute;
top: 12px;
right: 12px;
background: var(--ifm-color-success);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}

/* Responsive Design */
@media (max-width: 768px) {
.statsGrid,
.achievementsGrid {
grid-template-columns: 1fr;
}

.container {
padding: 20px 16px;
}

.title {
font-size: 1.5rem;
}
}
49 changes: 49 additions & 0 deletions src/components/ProgressTracker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useState, useEffect } from 'react';
import { getUserStats } from '../utils/progressTracker';
import styles from './ProgressTracker.module.css';

const ProgressTracker = () => {
const [userStats, setUserStats] = useState({
totalXP: 0,
streak: 0,
completedCourses: 0,
totalModulesCompleted: 0,
achievements: 0
});

useEffect(() => {
setUserStats(getUserStats());
}, []);

const progressData = [
{ label: 'Week 1', modules: 2, xp: 50 },
{ label: 'Week 2', modules: 5, xp: 120 },
{ label: 'Week 3', modules: 8, xp: 210 },
{ label: 'Week 4', modules: 12, xp: 350 },
];

return (
<div className={styles.container}>
<h3 className={styles.title}>Your Learning Progress</h3>
<div className={styles.progressCards}>
{progressData.map((week, index) => (
<div key={week.label} className={styles.progressCard}>
<h4 className={styles.weekTitle}>{week.label}</h4>
<div className={styles.statsRow}>
<div className={styles.stat}>
<span className={styles.statValue}>{week.modules}</span>
<span className={styles.statLabel}>Modules</span>
</div>
<div className={styles.stat}>
<span className={styles.statValue}>{week.xp}</span>
<span className={styles.statLabel}>XP</span>
</div>
</div>
</div>
))}
</div>
</div>
);
};

export default ProgressTracker;
Loading