Skip to content

Commit efb286b

Browse files
Merge pull request #52 from sustech-cs304/frontend
Frontend
2 parents e9e6ccf + 5f18802 commit efb286b

File tree

12 files changed

+823
-326
lines changed

12 files changed

+823
-326
lines changed

peachide/src/app/UserEnvProvider.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ interface GroupsMap {
1919
interface UserContextType {
2020
token: string | null;
2121
userId: string | null;
22+
fetchFailed: boolean;
2223
isAuthenticated: boolean;
2324
isTeacher: boolean;
2425
myGroups: GroupsMap;
2526
userData: UserData | null;
2627
setUserData: (userData: UserData) => void;
28+
setFetchFailed: (fetchFailed: boolean) => void;
2729
setIsTeacher: (isTeacher: boolean) => void;
2830
setMyGroups: (groups: GroupsMap) => void;
2931
login: (token: string, userId: string, isTeacher: boolean) => void;
@@ -65,6 +67,7 @@ const defaultSidebarItems: SidebarItem[] = [...baseSidebarItems];
6567
const UserContext = createContext<UserContextType>({
6668
token: null,
6769
userId: null,
70+
fetchFailed: false,
6871
isAuthenticated: false,
6972
isTeacher: false,
7073
myGroups: {} as GroupsMap,
@@ -73,6 +76,7 @@ const UserContext = createContext<UserContextType>({
7376
login: () => { },
7477
logout: () => { },
7578
setUserData: () => { },
79+
setFetchFailed: () => { },
7680
sidebarItems: defaultSidebarItems,
7781
setSidebarItems: () => { },
7882
setMyGroups: () => { },
@@ -85,6 +89,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
8589
const [userId, setUserId] = useState<string | null>(null);
8690
const [isTeacher, setIsTeacher] = useState<boolean>(false);
8791
const [userData, setUserData] = useState<UserData | null>(null);
92+
const [fetchFailed, setFetchFailed] = useState<boolean>(false);
8893
const [sidebarItems, setSidebarItems] = useState<SidebarItem[]>(defaultSidebarItems);
8994
const [myGroups, setMyGroups] = useState<GroupsMap>({} as GroupsMap);
9095

@@ -107,7 +112,9 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
107112
});
108113

109114
if (!response.ok) {
110-
throw new Error('Failed to fetch user data');
115+
setFetchFailed(true);
116+
console.log("Failed to fetch user data", response);
117+
return;
111118
}
112119

113120
const data = await response.json();
@@ -132,6 +139,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
132139
}
133140
setMyGroups(groups as GroupsMap);
134141
} catch (error) {
142+
setFetchFailed(true);
135143
console.error('Error fetching user data:', error);
136144
toast.error('Failed to load user information');
137145
}
@@ -185,6 +193,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
185193
localStorage.setItem('sidebarItems', JSON.stringify([...defaultSidebarItems, ...studentSidebarItems]));
186194
console.log("studentSidebarItems", [...defaultSidebarItems, ...studentSidebarItems]);
187195
}
196+
setFetchFailed(false);
188197
};
189198

190199
const logout = () => {
@@ -197,6 +206,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
197206
// Reset sidebar items to default when logging out
198207
setSidebarItems(defaultSidebarItems);
199208
localStorage.setItem('sidebarItems', JSON.stringify(defaultSidebarItems));
209+
setFetchFailed(false);
200210
};
201211

202212
const isAuthenticated = IS_MOCK_AUTH || !!token;
@@ -212,6 +222,8 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
212222
setIsTeacher,
213223
login,
214224
logout,
225+
fetchFailed,
226+
setFetchFailed,
215227
userData,
216228
setMyGroups,
217229
setUserData,

peachide/src/app/classes/Assignment.tsx

Lines changed: 80 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
ChevronRight,
1010
AlertCircle,
1111
BookOpen,
12-
CodeXml
12+
CodeXml,
13+
Users,
14+
User
1315
} from 'lucide-react';
1416
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@/components/ui/card';
1517
import { Button } from '@/components/ui/button';
@@ -20,9 +22,13 @@ import { useUserContext } from '../UserEnvProvider';
2022

2123
interface Assignment {
2224
assignment_id: string;
25+
is_group_assign: boolean;
2326
name: string;
27+
course_id: string;
28+
teacher_id: string;
2429
deadline: string;
2530
isOver: boolean;
31+
description?: string;
2632
}
2733

2834
interface AssignmentData {
@@ -67,44 +73,7 @@ export default function Assignment({ courseId }: AssignmentProps) {
6773
console.error('Error fetching assignments:', err);
6874
// Mock data for development
6975
setData({
70-
assignments: [
71-
{
72-
assignment_id: "asn_001",
73-
name: "Getting Started with Algorithms",
74-
deadline: "2023-06-15T23:59:59Z",
75-
isOver: false
76-
},
77-
{
78-
assignment_id: "asn_002",
79-
name: "Data Structures Implementation",
80-
deadline: "2023-06-20T23:59:59Z",
81-
isOver: false
82-
},
83-
{
84-
assignment_id: "asn_003",
85-
name: "Algorithm Complexity Analysis",
86-
deadline: "2023-06-10T23:59:59Z",
87-
isOver: true
88-
},
89-
{
90-
assignment_id: "asn_004",
91-
name: "Advanced Problem Solving",
92-
deadline: "2023-07-05T23:59:59Z",
93-
isOver: false
94-
},
95-
{
96-
assignment_id: "asn_005",
97-
name: "Team Project Planning",
98-
deadline: "2023-06-25T23:59:59Z",
99-
isOver: false
100-
},
101-
{
102-
assignment_id: "asn_006",
103-
name: "Basic Programming Concepts",
104-
deadline: "2023-05-30T23:59:59Z",
105-
isOver: true
106-
}
107-
]
76+
assignments: []
10877
});
10978
} finally {
11079
setLoading(false);
@@ -116,7 +85,11 @@ export default function Assignment({ courseId }: AssignmentProps) {
11685
}
11786
}, [courseId]);
11887

88+
const [envLoading, setEnvLoading] = useState(false);
89+
11990
const handleStartAssignment = async (assignmentId: string) => {
91+
setError(null);
92+
setEnvLoading(true);
12093
var result: any;
12194
try {
12295
const formData = new FormData();
@@ -135,11 +108,13 @@ export default function Assignment({ courseId }: AssignmentProps) {
135108
result = await response.json();
136109
if (result.message == "Require group" && !(courseId in myGroups)) {
137110
setError('You need to join a group to start this assignment.');
111+
setEnvLoading(false);
138112
return;
139113
}
140114
} catch (error) {
141115
console.error('Error starting assignment environment:', error);
142116
setError('Failed to start assignment environment. Please try again later.');
117+
setEnvLoading(false);
143118
return;
144119
}
145120
const environmentId = result.environment_id;
@@ -152,8 +127,38 @@ export default function Assignment({ courseId }: AssignmentProps) {
152127
icon: "CodeXml"
153128
}
154129
]);
130+
setEnvLoading(false);
155131
};
156132

133+
// Loading popout overlay
134+
if (envLoading) {
135+
return (
136+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
137+
<Card className="p-8 flex flex-col items-center gap-4 shadow-2xl">
138+
<div className="animate-spin mb-2">
139+
<svg className="h-10 w-10 text-primary" viewBox="0 0 24 24" fill="none">
140+
<circle
141+
className="opacity-25"
142+
cx="12"
143+
cy="12"
144+
r="10"
145+
stroke="currentColor"
146+
strokeWidth="4"
147+
/>
148+
<path
149+
className="opacity-75"
150+
fill="currentColor"
151+
d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
152+
/>
153+
</svg>
154+
</div>
155+
<h3 className="text-lg font-semibold">Preparing your coding environment...</h3>
156+
<p className="text-muted-foreground text-center text-sm">This may take a few moments. Please wait.</p>
157+
</Card>
158+
</div>
159+
);
160+
}
161+
157162
// Sort assignments: active ones by deadline (earliest first), then completed ones
158163
const sortedAssignments = data?.assignments
159164
? [...data.assignments].sort((a, b) => {
@@ -411,21 +416,42 @@ export default function Assignment({ courseId }: AssignmentProps) {
411416
{!isActive && (
412417
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-muted-foreground/30 to-muted-foreground/10"></div>
413418
)}
414-
<CardHeader className="pb-2">
415-
<CardTitle className={`text-xl font-semibold flex items-center ${!isActive ? 'text-muted-foreground' : ''}`}>
416-
<BookOpen className="h-5 w-5 mr-2 text-primary" />
417-
{assignment.name}
418-
{assignment.isOver && (
419-
<Badge variant="outline" className="ml-2 text-xs">
420-
Completed
421-
</Badge>
422-
)}
423-
{isMissed && (
424-
<Badge variant="destructive" className="ml-2 text-xs">
425-
Missed
426-
</Badge>
427-
)}
428-
</CardTitle>
419+
<CardHeader className="pb-3">
420+
<div className="flex items-start justify-between">
421+
<CardTitle className={`text-xl font-semibold flex items-center ${!isActive ? 'text-muted-foreground' : ''}`}>
422+
<BookOpen className="h-5 w-5 mr-2 text-primary" />
423+
{assignment.name}
424+
</CardTitle>
425+
<div className="flex gap-2 ml-4">
426+
{assignment.is_group_assign && (
427+
<Badge variant="secondary" className="flex items-center gap-1 text-xs">
428+
<Users size={10} />
429+
Group
430+
</Badge>
431+
)}
432+
{!assignment.is_group_assign && (
433+
<Badge variant="outline" className="flex items-center gap-1 text-xs">
434+
<User size={10} />
435+
Individual
436+
</Badge>
437+
)}
438+
{assignment.isOver && (
439+
<Badge variant="outline" className="text-xs">
440+
Completed
441+
</Badge>
442+
)}
443+
{isMissed && (
444+
<Badge variant="destructive" className="text-xs">
445+
Missed
446+
</Badge>
447+
)}
448+
</div>
449+
</div>
450+
{assignment.description && (
451+
<p className={`text-sm mt-2 ${!isActive ? 'text-muted-foreground' : 'text-muted-foreground'}`}>
452+
{assignment.description}
453+
</p>
454+
)}
429455
</CardHeader>
430456
<CardContent className="pb-4">
431457
{renderDeadlineInfo(assignment.deadline, assignment.isOver)}

peachide/src/app/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ export default function RootLayout({
2626
<body>
2727
<ThemeProvider
2828
attribute="class"
29-
defaultTheme="system"
30-
enableSystem
29+
defaultTheme="light"
30+
enableSystem={false}
3131
disableTransitionOnChange
3232
>
3333
<UserProvider>

0 commit comments

Comments
 (0)