Skip to content

Commit 5284d40

Browse files
committed
Merge branch 'develop' into fix/history/ui-for-profile-and-history
2 parents ee8e6ed + 9307ad2 commit 5284d40

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+7899
-5773
lines changed

.github/workflows/codecov.yml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,17 @@ jobs:
1515

1616
- name: Set up Node
1717
uses: actions/setup-node@v4
18+
with:
19+
node-version: '20'
1820

1921
- name: Install root dependencies
20-
run: npm install
21-
working-directory: ./cs3219-ay2526s1-project-g03
22-
23-
- name: Install workspace dependencies
24-
run: npm install --workspaces
25-
working-directory: ./cs3219-ay2526s1-project-g03
22+
run: npm ci
2623

2724
- name: Run tests
2825
run: npx jest --coverage
29-
working-directory: ./cs3219-ay2526s1-project-g03
3026

3127
- name: Upload results to Codecov
3228
if: always()
3329
uses: codecov/codecov-action@v5
3430
with:
35-
token: ${{ secrets.CODECOV_TOKEN }}
36-
directory: ./cs3219-ay2526s1-project-g03/coverage/
31+
token: ${{ secrets.CODECOV_TOKEN }}

.prettierignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
# Node
22
node_modules/
33
**/node_modules/
4+
5+
# Database (local volumes)
6+
**/db
7+
**/db/**
8+
**/.mongodb
9+
**/.mongodb/**

ai/usage-log.md

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
1-
# Date/Time: 2025-10-24
2-
# Tool: Claude 4.5 Sonnet (Anthropic)
3-
# Prompt/Command: Requested for Claude to look through the code for all Controller and Route files and generate test cases.
4-
# Output Summary: Corresponding _.test.ts
5-
# Action Taken:
6-
- [ ] Accepted as-is
7-
- [ X ] Modified
8-
- [ ] Rejected
9-
# Author Notes: Correctness was verified by running Jest. Some `expect` logic was incorrect and modified where appropriate.
10-
11-
# Date/Time: 2025-10-25
12-
# Tool: Claude 4.5 Sonnet (Anthropic)
13-
# Prompt/Command: Requested for Claude to look through the code for all user-service frontend code and generate test cases.
14-
# Output Summary: Corresponding _.test.ts
15-
# Action Taken:
16-
- [ ] Accepted as-is
17-
- [ X ] Modified
18-
- [ ] Rejected
19-
# Author Notes: Correctness was verified by running Jest. Some `expect` logic was incorrect and modified where appropriate.
1+
# Zhen Jie
2+
## Date/Time: 2025-10-24
3+
4+
### Tool: Claude 4.5 Sonnet (Anthropic)
5+
6+
#### Prompt/Command: Requested for Claude to look through the code for all Controller and Route files and generate test cases.
7+
8+
##### Output Summary: Corresponding \_.test.ts
9+
10+
##### Action Taken:
11+
12+
- [ ] Accepted as-is
13+
- [ X ] Modified
14+
- [ ] Rejected
15+
16+
##### Author Notes: Correctness was verified by running Jest. Some `expect` logic was incorrect and modified where appropriate.
17+
18+
## Date/Time: 2025-10-25
19+
20+
### Tool: Claude 4.5 Sonnet (Anthropic)
21+
22+
#### Prompt/Command: Requested for Claude to look through the code for all user-service frontend code and generate test cases.
23+
24+
##### Output Summary: Corresponding \_.test.ts
25+
26+
##### Action Taken:
27+
28+
- [ ] Accepted as-is
29+
- [ X ] Modified
30+
- [ ] Rejected
31+
32+
##### Author Notes: Correctness was verified by running Jest. Some `expect` logic was incorrect and modified where appropriate.

babel.config.js

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
// Adapted from https://medium.com/@tajircuet/support-of-import-meta-on-vite-jest-typescript-91b49eb0446f
22
module.exports = {
3-
presets: [
4-
[
5-
'@babel/preset-env',
6-
{ useBuiltIns: 'entry', corejs: '2',
7-
targets: { node: 'current' } },
8-
],
9-
'@babel/preset-typescript',
10-
],
11-
plugins: [
12-
function () {
13-
return {
14-
visitor: {
15-
MetaProperty(path) {
16-
path.replaceWithSourceString('process')
17-
},
18-
},
19-
}
3+
presets: [
4+
['@babel/preset-env', {useBuiltIns: 'entry', corejs: '2', targets: {node: 'current'}}],
5+
'@babel/preset-typescript',
6+
],
7+
plugins: [
8+
function () {
9+
return {
10+
visitor: {
11+
MetaProperty(path) {
12+
path.replaceWithSourceString('process');
13+
},
2014
},
21-
],
22-
}
15+
};
16+
},
17+
],
18+
};

docker-compose.yml

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
services:
22
# --- DATABASES ---
3-
user-db-mongodb:
4-
image: mongo
5-
container_name: user-db-mongodb
6-
restart: unless-stopped
7-
env_file:
8-
- .env
9-
volumes:
10-
- ./user-service/db:/data/db
11-
networks:
12-
- app-network
3+
# For local volume dockerized testing
4+
# Commented out - using MongoDB Atlas instead
5+
# user-db-mongodb:
6+
# image: mongo
7+
# container_name: user-db-mongodb
8+
# restart: unless-stopped
9+
# env_file:
10+
# - .env
11+
# volumes:
12+
# - ./user-service/db:/data/db
13+
# networks:
14+
# - app-network
1315

14-
# For development purposes
15-
user-db-mongo-express:
16-
image: mongo-express
17-
container_name: user-db-mongo-express
18-
restart: unless-stopped
19-
ports:
20-
- 8084:8081
21-
env_file:
22-
- .env
23-
networks:
24-
- app-network
25-
depends_on:
26-
- user-db-mongodb
16+
# For local volume dokcerized development purposes
17+
# user-db-mongo-express:
18+
# image: mongo-express
19+
# container_name: user-db-mongo-express
20+
# restart: unless-stopped
21+
# ports:
22+
# - 8084:8081
23+
# env_file:
24+
# - .env
25+
# networks:
26+
# - app-network
27+
# depends_on:
28+
# - user-db-mongodb
2729

2830
question-db-pg:
2931
image: postgres:14-alpine
@@ -98,8 +100,8 @@ services:
98100
- /app/node_modules
99101
networks:
100102
- app-network
101-
depends_on:
102-
- user-db-mongodb
103+
# depends_on: # Commented out - using MongoDB Atlas instead of local container
104+
# - user-db-mongodb
103105
command: sh -c "npm run seed:admin || true && npm run dev" # for production change to npm start
104106

105107
# Question service
@@ -202,6 +204,7 @@ services:
202204
env_file:
203205
- ./.env
204206
depends_on:
207+
# - user-db-mongodb # Uncomment if local volume
205208
question-db-pg:
206209
condition: service_healthy # Wait for DB to be ready
207210
volumes:

frontend/src/components/userMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ const UserMenu: React.FC = () => {
5353
</>
5454
) : (
5555
<>
56-
<button className="icon-button">
56+
{/* <button className="icon-button">
5757
<img src={NotificationIcon} alt="Notifications" className="notification-icon" />
58-
</button>
58+
</button> */}
5959

6060
{isVerified ? (
6161
<Link to="/profile/" className="icon-link">

frontend/src/features/progress/RecentSessionsList.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ interface SessionSummary {
2323
question_difficulty: "Easy" | "Medium" | "Hard";
2424
partner_id: string;
2525
partner_username?: string;
26-
is_solved_successfully: boolean;
26+
is_solved_successfully: boolean | null;
2727
has_penalty: boolean; // We need this to determine "Incomplete"
2828
started_at: string;
29-
time_taken_ms: number;
29+
time_taken_ms: number | null;
3030
}
3131

3232
interface RecentSessionsListProps {
@@ -138,11 +138,11 @@ const RecentSessionsList: React.FC<RecentSessionsListProps> = ({ userId, limit,
138138

139139
{/* API provides booleans, we derive the status */}
140140
<Badge className={
141-
session.is_solved_successfully ? 'bg-green-500 text-white' : // "Passed" (blue)
142-
session.has_penalty ? 'bg-orange-500 text-white' : // "Incomplete" (gray)
141+
session.is_solved_successfully === true ? 'bg-green-500 text-white' : // "Passed" (green)
142+
session.has_penalty ? 'bg-orange-500 text-white' : // "Incomplete" (orange)
143143
'bg-red-500 text-white' // "Failed" (red)
144144
}>
145-
{session.is_solved_successfully ? "Passed" : (session.has_penalty ? "Incomplete" : "Failed")}
145+
{session.is_solved_successfully === true ? "Passed" : (session.has_penalty ? "Incomplete" : "Failed")}
146146
</Badge>
147147
</div>
148148
</Card>

frontend/src/features/progress/progressCard.tsx

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,63 @@ import TopicProgress from './topicProgress';
55
import styles from './progressCard.module.css';
66
import ArrowDownIcon from '../../assets/arrow-down-icon.svg'; // Example icon
77
import ArrowUpIcon from '../../assets/arrow-up-icon.svg'; // Example icon
8+
import useAuth from '@/hooks/useAuth';
9+
import { useQuery } from '@tanstack/react-query';
10+
import { getAllAttemptSummaries, getHistoryProgress } from '@/lib/api';
811

9-
// Placeholder data TODO: replace with data fetched from API
10-
const placeholderProgress = {
11-
total_sessions_completed: 18,
12-
total_successes: 15,
13-
total_time_ms: 3600000, // 1 hour in milliseconds
14-
current_streak: 24,
15-
};
12+
const ProgressCard = () => {
13+
const [isOpen, setIsOpen] = useState(false); // Default to false
14+
const { user} = useAuth();
1615

17-
const placeholderRecentSessions = [
18-
{ id: 1, user: 'Jane', difficulty: 'Easy', duration: 45, status: 'Completed' },
19-
{ id: 2, user: 'Thomas', difficulty: 'Medium', duration: 60, status: 'Passed' }, // Assuming 'Passed' is a status
20-
{ id: 3, user: 'Alex', difficulty: 'Hard', duration: 500, status: 'Incomplete'},
21-
];
16+
const { data: userProgress, isLoading } = useQuery({
17+
queryKey: ['userProgress', user?._id],
18+
queryFn: () => getHistoryProgress(user?._id),
19+
enabled: !!user?._id,
20+
});
2221

23-
const placeholderTopicProgress = {
24-
'Arrays': 4,
25-
'Strings': 6,
26-
'Binary Search': 18,
27-
'Trees & Graphs': 2,
28-
'Linked Lists': 3,
29-
'Stack & Queue': 1,
30-
'Hash Tables': 2,
31-
};
22+
const stats = userProgress ? {
23+
totalSessions: userProgress.total_sessions,
24+
completed: userProgress.total_sessions_completed,
25+
successRate: Math.round(userProgress.success_rate * 100), // Decimal to percentage
26+
dayStreak: userProgress.current_streak,
27+
} : {
28+
totalSessions: 0,
29+
completed: 0,
30+
successRate: 0,
31+
dayStreak: 0,
32+
};
3233

34+
const { data: allSessions, isLoading: isLoadingSessions } = useQuery({
35+
queryKey: ['recentSessions', user?._id],
36+
queryFn: () => getAllAttemptSummaries(user?._id),
37+
enabled: !!user?._id,
38+
retry: 1,
39+
});
40+
41+
const recentSessions = allSessions?.slice(0, 3).map((session, index) => ({
42+
id: index + 1,
43+
user: session.partner_id,
44+
difficulty: session.question_difficulty,
45+
duration: session.time_taken_ms ? Math.round(session.time_taken_ms / 60000) : 0, // convert ms to minutes
46+
status: session.is_solved_successfully ? 'Passed' : 'Failed',
47+
})) || [];
3348

34-
const ProgressCard = () => {
35-
const [isOpen, setIsOpen] = useState(false); // Default to false
49+
50+
const topicProgress = allSessions?.reduce((acc, session) => {
51+
session.question_topics.forEach(topic => {
52+
acc[topic] = (acc[topic] || 0) + 1;
53+
});
54+
return acc;
55+
}, {} as Record<string, number>) || {};
56+
57+
console.log('🔍 RAW allSessions from API:', allSessions);
58+
if (allSessions && allSessions.length > 0) {
59+
console.log('🔍 First session question_topics:', allSessions[0].question_topics);
60+
console.log('🔍 Type of question_topics:', typeof allSessions[0].question_topics);
61+
console.log('🔍 Is Array?:', Array.isArray(allSessions[0].question_topics));
62+
}
63+
64+
3665

3766
return (
3867
<div className={styles.progressCard}>
@@ -51,10 +80,10 @@ const ProgressCard = () => {
5180
{isOpen && (
5281
<div className={styles.content}>
5382
<div className={styles.topRow}>
54-
<StatsGrid progress={placeholderProgress} />
55-
<RecentSessions sessions={placeholderRecentSessions} />
83+
<StatsGrid stats={stats} />
84+
<RecentSessions sessions={recentSessions} />
5685
</div>
57-
<TopicProgress progressData={placeholderTopicProgress} />
86+
<TopicProgress progressData={topicProgress} />
5887
</div>
5988
)}
6089
</div>

0 commit comments

Comments
 (0)