Seth0330 commited on
Commit
0dc70ba
·
verified ·
1 Parent(s): c616a87

Create ClassEnrollments.jsx

Browse files
frontend/src/components/admin/ClassEnrollments.jsx ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // frontend/src/components/admin/ClassEnrollments.jsx
2
+ import React, { useState } from "react";
3
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
+ import api from "../../api/client";
5
+
6
+ export default function ClassEnrollments({ classData, onClose }) {
7
+ const queryClient = useQueryClient();
8
+ const [email, setEmail] = useState("");
9
+
10
+ const classId = classData?.id;
11
+
12
+ const {
13
+ data: enrollments = [],
14
+ isLoading,
15
+ } = useQuery({
16
+ queryKey: ["class-enrollments", classId],
17
+ enabled: !!classId,
18
+ queryFn: async () => {
19
+ const res = await api.get(`/admin/classes/${classId}/enrollments`);
20
+ return Array.isArray(res.data) ? res.data : [];
21
+ },
22
+ initialData: [],
23
+ });
24
+
25
+ const inviteMutation = useMutation({
26
+ mutationFn: async (payload) => {
27
+ const res = await api.post(
28
+ `/admin/classes/${classId}/invite`,
29
+ payload
30
+ );
31
+ return res.data;
32
+ },
33
+ onSuccess: () => {
34
+ setEmail("");
35
+ queryClient.invalidateQueries({ queryKey: ["class-enrollments", classId] });
36
+ },
37
+ });
38
+
39
+ const removeMutation = useMutation({
40
+ mutationFn: async (enrollmentId) => {
41
+ await api.delete(
42
+ `/admin/classes/${classId}/enrollments/${enrollmentId}`
43
+ );
44
+ },
45
+ onSuccess: () => {
46
+ queryClient.invalidateQueries({ queryKey: ["class-enrollments", classId] });
47
+ },
48
+ });
49
+
50
+ function handleInvite(e) {
51
+ e.preventDefault();
52
+ if (!email.trim()) return;
53
+ inviteMutation.mutate({ student_email: email.trim() });
54
+ }
55
+
56
+ return (
57
+ <div className="fixed inset-0 z-40 flex items-center justify-center bg-black/30">
58
+ <div className="w-full max-w-xl bg-white rounded-2xl shadow-xl border border-stone-200 p-5">
59
+ <div className="flex justify-between items-center mb-4">
60
+ <div>
61
+ <h2 className="text-sm font-semibold text-stone-900">
62
+ Enrollments – {classData?.name}
63
+ </h2>
64
+ <p className="text-xs text-stone-500">
65
+ Invite students by email and manage their enrollment.
66
+ </p>
67
+ </div>
68
+ <button
69
+ type="button"
70
+ onClick={onClose}
71
+ className="text-xs text-stone-500 hover:text-stone-800"
72
+ >
73
+ Close
74
+ </button>
75
+ </div>
76
+
77
+ {/* Invite form */}
78
+ <form onSubmit={handleInvite} className="mb-4 flex gap-3">
79
+ <input
80
+ type="email"
81
+ placeholder="student@example.com"
82
+ className="flex-1 rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500"
83
+ value={email}
84
+ onChange={(e) => setEmail(e.target.value)}
85
+ />
86
+ <button
87
+ type="submit"
88
+ disabled={inviteMutation.isPending}
89
+ className="rounded-lg bg-red-600 hover:bg-red-700 text-white text-sm font-medium px-4 py-2 disabled:opacity-60"
90
+ >
91
+ {inviteMutation.isPending ? "Sending..." : "Invite"}
92
+ </button>
93
+ </form>
94
+
95
+ {/* Enrollments list */}
96
+ <div className="border-t border-stone-100 pt-3 max-h-64 overflow-y-auto">
97
+ {isLoading ? (
98
+ <div className="py-3 text-sm text-stone-500">
99
+ Loading enrollments...
100
+ </div>
101
+ ) : enrollments.length === 0 ? (
102
+ <div className="py-3 text-sm text-stone-500">
103
+ No enrollments yet.
104
+ </div>
105
+ ) : (
106
+ <ul className="divide-y divide-stone-100 text-sm">
107
+ {enrollments.map((en) => (
108
+ <li key={en.id} className="py-2 flex items-center justify-between">
109
+ <div>
110
+ <div className="font-medium text-stone-900">
111
+ {en.student_email}
112
+ </div>
113
+ <div className="text-xs text-stone-500">
114
+ Status: {en.status || "invited"}
115
+ </div>
116
+ </div>
117
+ <button
118
+ type="button"
119
+ onClick={() => removeMutation.mutate(en.id)}
120
+ className="text-xs font-medium text-red-600 hover:text-red-700"
121
+ >
122
+ Remove
123
+ </button>
124
+ </li>
125
+ ))}
126
+ </ul>
127
+ )}
128
+ </div>
129
+ </div>
130
+ </div>
131
+ );
132
+ }