Skip to content

Commit 68600d8

Browse files
authored
Merge pull request #312 from Code102SoftwareProject/feat/user-dashboard
Feat/user dashboard
2 parents 401769d + 1f05281 commit 68600d8

2 files changed

Lines changed: 98 additions & 11 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { NextResponse } from 'next/server';
2+
import mongoose from 'mongoose';
3+
import '@/lib/models/badgeSchema'; // <-- Import badgeSchema BEFORE BadgeAssignment
4+
import BadgeAssignment from '@/lib/models/badgeAssignmentSchema';
5+
import connect from '@/lib/db';
6+
7+
export async function GET(
8+
req: Request,
9+
{ params }: { params: Promise<{ id: string }> } // ← PROMISE here
10+
): Promise<NextResponse> {
11+
const { id: userId } = await params; // ← AWAIT here
12+
13+
try {
14+
if (!mongoose.Types.ObjectId.isValid(userId)) {
15+
return NextResponse.json({ error: 'Invalid user ID format' }, { status: 400 });
16+
}
17+
18+
await connect();
19+
20+
const badgeAssignments = await BadgeAssignment.find({ userId })
21+
.populate('badgeId')
22+
.sort({ assignedAt: -1 });
23+
24+
const responseData = badgeAssignments.map((assignment) => ({
25+
id: assignment._id,
26+
badge: {
27+
id: assignment.badgeId._id,
28+
badgeName: assignment.badgeId.badgeName,
29+
badgeImage: assignment.badgeId.badgeImage,
30+
criteria: assignment.badgeId.criteria,
31+
description: assignment.badgeId.description
32+
},
33+
assignedAt: assignment.assignedAt,
34+
context: assignment.assignmentContext
35+
}));
36+
37+
return NextResponse.json(responseData);
38+
} catch (error) {
39+
console.error('Error fetching badge assignments:', error);
40+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
41+
}
42+
}

src/components/Dashboard/EarnedBadges.tsx

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import React, { useEffect, useState } from 'react';
44
import { useRouter } from 'next/navigation';
5+
import { processAvatarUrl } from '@/utils/imageUtils';
6+
import Image from 'next/image';
57

68
interface Badge {
7-
id: string;
8-
icon: string; // emoji or image
9-
label: string;
9+
_id: string;
10+
badgeImage: string;
11+
badgeName: string;
12+
// Add other badge properties you need
1013
}
1114

1215
interface Props {
@@ -15,25 +18,57 @@ interface Props {
1518

1619
export default function EarnedBadges({ userId }: Props) {
1720
const [badges, setBadges] = useState<Badge[]>([]);
21+
const [loading, setLoading] = useState(true);
22+
const [error, setError] = useState<string | null>(null);
1823
const router = useRouter();
1924

2025
useEffect(() => {
2126
const fetchBadges = async () => {
2227
try {
23-
const res = await fetch('/api/badge');
28+
setLoading(true);
29+
const res = await fetch(`/api/badge-assignments/${userId}`);
30+
31+
if (!res.ok) {
32+
throw new Error('Failed to fetch badges');
33+
}
34+
2435
const data = await res.json();
25-
setBadges(data || []);
36+
37+
// Transform API response to match your Badge interface
38+
const transformedBadges = data
39+
.filter((assignment: any) => assignment.badge) // Only keep assignments with a populated badge
40+
.map((assignment: any) => ({
41+
_id: assignment.badge.id,
42+
badgeImage: assignment.badge.badgeImage, // use badgeImage from schema
43+
badgeName: assignment.badge.badgeName // use badgeName from schema
44+
}));
45+
46+
setBadges(transformedBadges);
47+
setError(null);
2648
} catch (err) {
2749
console.error('Error fetching badges:', err);
50+
setError('Failed to load badges');
2851
setBadges([]);
52+
} finally {
53+
setLoading(false);
2954
}
3055
};
3156

32-
fetchBadges();
57+
if (userId) {
58+
fetchBadges();
59+
}
3360
}, [userId]);
3461

3562
const displayedBadges = badges.slice(0, 5);
3663

64+
if (loading) {
65+
return <div className="text-sm text-gray-500">Loading badges...</div>;
66+
}
67+
68+
if (error) {
69+
return <div className="text-sm text-red-500">{error}</div>;
70+
}
71+
3772
return (
3873
<div className="relative">
3974
{/* Learn More Button - top right */}
@@ -50,11 +85,21 @@ export default function EarnedBadges({ userId }: Props) {
5085
<div className="flex flex-wrap gap-6 pt-6">
5186
{displayedBadges.length > 0 ? (
5287
displayedBadges.map((badge, idx) => (
53-
<div key={badge.id || idx} className="flex flex-col items-center">
54-
<div className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-2 text-lg">
55-
{badge.icon}
88+
<div key={badge._id || idx} className="flex flex-col items-center">
89+
<div className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-2 text-lg overflow-hidden">
90+
{badge.badgeImage ? (
91+
<Image
92+
src={processAvatarUrl(badge.badgeImage) || '/placeholder-badge.png'}
93+
alt={badge.badgeName}
94+
width={48}
95+
height={48}
96+
className="object-contain w-full h-full"
97+
/>
98+
) : (
99+
<span className="text-gray-400">?</span>
100+
)}
56101
</div>
57-
<span className="text-xs text-center">{badge.label}</span>
102+
<span className="text-xs text-center">{badge.badgeName}</span>
58103
</div>
59104
))
60105
) : (
@@ -63,4 +108,4 @@ export default function EarnedBadges({ userId }: Props) {
63108
</div>
64109
</div>
65110
);
66-
}
111+
}

0 commit comments

Comments
 (0)