Seth0330 commited on
Commit
4c817bd
·
verified ·
1 Parent(s): 269dbd8

Update frontend/src/components/admin/ClassForm.jsx

Browse files
frontend/src/components/admin/ClassForm.jsx CHANGED
@@ -1,24 +1,19 @@
1
  // frontend/src/components/admin/ClassForm.jsx
2
  import React, { useState, useEffect } from "react";
3
-
4
- import { Button } from "../ui/button";
5
- import { Input } from "../ui/input";
6
- import { Textarea } from "../ui/textarea";
7
- import { Label } from "../ui/label";
8
- import { Card } from "../ui/card";
9
- import {
10
- Select,
11
- SelectContent,
12
- SelectItem,
13
- SelectTrigger,
14
- SelectValue,
15
- } from "../ui/select";
16
- import { Loader2, Save, X, Plus, Trash2 } from "lucide-react";
17
-
18
- const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
19
 
20
  export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
21
- // schedule is an array of { day, start_time, end_time }
22
  const [formData, setFormData] = useState({
23
  name: "",
24
  description: "",
@@ -26,17 +21,13 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
26
  is_active: true,
27
  });
28
 
29
- // Map existing classData into the new schedule shape if present
30
  useEffect(() => {
31
  if (!classData) return;
32
-
33
- // If backend later returns something like classData.schedule, you can
34
- // normalize here. For now, just carry through what we have.
35
  setFormData({
36
  name: classData.name || "",
37
  description: classData.description || "",
38
  schedule:
39
- classData.schedule && Array.isArray(classData.schedule) && classData.schedule.length > 0
40
  ? classData.schedule.map((slot) => ({
41
  day: slot.day || "Monday",
42
  start_time: slot.start_time || "",
@@ -67,23 +58,24 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
67
 
68
  const updateScheduleSlot = (index, field, value) => {
69
  setFormData((prev) => {
70
- const newSchedule = [...prev.schedule];
71
- newSchedule[index] = { ...newSchedule[index], [field]: value };
72
- return { ...prev, schedule: newSchedule };
73
  });
74
  };
75
 
76
  const handleSubmit = (e) => {
77
  e.preventDefault();
78
 
79
- // Transform schedule into something the backend can understand.
80
- // For now we flatten into a human-readable string array so you can
81
- // later wire to class_time / days_of_week or a new table.
82
  const transformedSchedule = formData.schedule.map((slot) => ({
83
  day: slot.day,
84
- time: `${slot.start_time || "00:00"}-${slot.end_time || "00:00"}`,
85
  start_time: slot.start_time,
86
  end_time: slot.end_time,
 
 
 
 
87
  }));
88
 
89
  const payload = {
@@ -99,9 +91,13 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
99
  return (
100
  <form onSubmit={handleSubmit} className="space-y-6">
101
  {/* Class Name */}
102
- <div>
103
- <Label>Class Name</Label>
104
- <Input
 
 
 
 
105
  value={formData.name}
106
  onChange={(e) =>
107
  setFormData((prev) => ({ ...prev, name: e.target.value }))
@@ -112,63 +108,76 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
112
  </div>
113
 
114
  {/* Description */}
115
- <div>
116
- <Label>Description</Label>
117
- <Textarea
 
 
 
 
118
  value={formData.description}
119
  onChange={(e) =>
120
- setFormData((prev) => ({ ...prev, description: e.target.value }))
 
 
 
121
  }
122
  placeholder="Class description..."
123
- rows={2}
124
  />
125
  </div>
126
 
127
  {/* Schedule */}
128
  <div>
129
- <div className="flex justify-between items-center mb-3">
130
- <Label className="text-base">Class Schedule</Label>
131
- <Button
 
 
132
  type="button"
133
- variant="outline"
134
- size="sm"
135
  onClick={addScheduleSlot}
136
- className="gap-2"
137
  >
138
  <Plus className="w-4 h-4" />
139
  Add Time Slot
140
- </Button>
141
  </div>
142
 
143
  <div className="space-y-3">
144
  {formData.schedule.map((slot, index) => (
145
- <Card key={index} className="p-3 bg-stone-50">
146
- <div className="flex flex-col md:flex-row md:items-center gap-3">
147
- {/* Day select */}
148
- <Select
149
- value={slot.day}
150
- onValueChange={(value) =>
151
- updateScheduleSlot(index, "day", value)
152
- }
153
- >
154
- <SelectTrigger className="w-full md:w-40">
155
- <SelectValue placeholder="Day" />
156
- </SelectTrigger>
157
- <SelectContent>
 
 
 
 
158
  {DAYS.map((day) => (
159
- <SelectItem key={day} value={day}>
160
  {day}
161
- </SelectItem>
162
  ))}
163
- </SelectContent>
164
- </Select>
165
 
166
- {/* From / To time pickers */}
167
- <div className="flex flex-1 flex-col md:flex-row gap-3">
168
  <div className="flex-1">
169
- <Label className="text-xs mb-1 block">From</Label>
170
- <Input
 
 
171
  type="time"
 
172
  value={slot.start_time}
173
  onChange={(e) =>
174
  updateScheduleSlot(index, "start_time", e.target.value)
@@ -176,9 +185,12 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
176
  />
177
  </div>
178
  <div className="flex-1">
179
- <Label className="text-xs mb-1 block">To</Label>
180
- <Input
 
 
181
  type="time"
 
182
  value={slot.end_time}
183
  onChange={(e) =>
184
  updateScheduleSlot(index, "end_time", e.target.value)
@@ -187,29 +199,28 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
187
  </div>
188
  </div>
189
 
 
190
  {formData.schedule.length > 1 && (
191
- <Button
192
  type="button"
193
- variant="ghost"
194
- size="icon"
195
  onClick={() => removeScheduleSlot(index)}
196
- className="text-red-600 self-start md:self-center"
197
  >
198
  <Trash2 className="w-4 h-4" />
199
- </Button>
200
  )}
201
  </div>
202
- </Card>
203
  ))}
204
  </div>
205
  </div>
206
 
207
  {/* Actions */}
208
  <div className="flex gap-3 pt-2">
209
- <Button
210
  type="submit"
211
  disabled={isLoading}
212
- className="gap-2 bg-red-600 hover:bg-red-700"
213
  >
214
  {isLoading ? (
215
  <Loader2 className="w-4 h-4 animate-spin" />
@@ -217,16 +228,17 @@ export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
217
  <Save className="w-4 h-4" />
218
  )}
219
  {classData ? "Update Class" : "Create Class"}
220
- </Button>
221
- <Button
 
222
  type="button"
223
- variant="outline"
224
  onClick={onCancel}
225
  disabled={isLoading}
 
226
  >
227
- <X className="w-4 h-4 mr-2" />
228
  Cancel
229
- </Button>
230
  </div>
231
  </form>
232
  );
 
1
  // frontend/src/components/admin/ClassForm.jsx
2
  import React, { useState, useEffect } from "react";
3
+ import { Plus, Trash2, Loader2, Save, X } from "lucide-react";
4
+
5
+ const DAYS = [
6
+ "Monday",
7
+ "Tuesday",
8
+ "Wednesday",
9
+ "Thursday",
10
+ "Friday",
11
+ "Saturday",
12
+ "Sunday",
13
+ ];
 
 
 
 
 
14
 
15
  export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
16
+ // schedule: [{ day, start_time, end_time }]
17
  const [formData, setFormData] = useState({
18
  name: "",
19
  description: "",
 
21
  is_active: true,
22
  });
23
 
 
24
  useEffect(() => {
25
  if (!classData) return;
 
 
 
26
  setFormData({
27
  name: classData.name || "",
28
  description: classData.description || "",
29
  schedule:
30
+ Array.isArray(classData.schedule) && classData.schedule.length > 0
31
  ? classData.schedule.map((slot) => ({
32
  day: slot.day || "Monday",
33
  start_time: slot.start_time || "",
 
58
 
59
  const updateScheduleSlot = (index, field, value) => {
60
  setFormData((prev) => {
61
+ const schedule = [...prev.schedule];
62
+ schedule[index] = { ...schedule[index], [field]: value };
63
+ return { ...prev, schedule };
64
  });
65
  };
66
 
67
  const handleSubmit = (e) => {
68
  e.preventDefault();
69
 
70
+ // Transform schedule to a simple structure that backend can later map
 
 
71
  const transformedSchedule = formData.schedule.map((slot) => ({
72
  day: slot.day,
 
73
  start_time: slot.start_time,
74
  end_time: slot.end_time,
75
+ time:
76
+ slot.start_time && slot.end_time
77
+ ? `${slot.start_time}-${slot.end_time}`
78
+ : "",
79
  }));
80
 
81
  const payload = {
 
91
  return (
92
  <form onSubmit={handleSubmit} className="space-y-6">
93
  {/* Class Name */}
94
+ <div className="space-y-1">
95
+ <label className="block text-sm font-medium text-stone-800">
96
+ Class Name
97
+ </label>
98
+ <input
99
+ type="text"
100
+ className="w-full rounded-md border border-stone-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-red-600"
101
  value={formData.name}
102
  onChange={(e) =>
103
  setFormData((prev) => ({ ...prev, name: e.target.value }))
 
108
  </div>
109
 
110
  {/* Description */}
111
+ <div className="space-y-1">
112
+ <label className="block text-sm font-medium text-stone-800">
113
+ Description
114
+ </label>
115
+ <textarea
116
+ className="w-full rounded-md border border-stone-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-red-600"
117
+ rows={2}
118
  value={formData.description}
119
  onChange={(e) =>
120
+ setFormData((prev) => ({
121
+ ...prev,
122
+ description: e.target.value,
123
+ }))
124
  }
125
  placeholder="Class description..."
 
126
  />
127
  </div>
128
 
129
  {/* Schedule */}
130
  <div>
131
+ <div className="flex items-center justify-between mb-3">
132
+ <span className="text-sm font-medium text-stone-800">
133
+ Class Schedule
134
+ </span>
135
+ <button
136
  type="button"
 
 
137
  onClick={addScheduleSlot}
138
+ className="inline-flex items-center gap-2 rounded-md border border-stone-300 px-2.5 py-1 text-xs font-medium text-stone-800 hover:bg-stone-50"
139
  >
140
  <Plus className="w-4 h-4" />
141
  Add Time Slot
142
+ </button>
143
  </div>
144
 
145
  <div className="space-y-3">
146
  {formData.schedule.map((slot, index) => (
147
+ <div
148
+ key={index}
149
+ className="rounded-lg border border-stone-200 bg-stone-50 px-3 py-3"
150
+ >
151
+ <div className="flex flex-col gap-3 md:flex-row md:items-center">
152
+ {/* Day */}
153
+ <div className="w-full md:w-40">
154
+ <label className="block text-xs font-medium text-stone-700 mb-1">
155
+ Day
156
+ </label>
157
+ <select
158
+ className="w-full rounded-md border border-stone-300 px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-600"
159
+ value={slot.day}
160
+ onChange={(e) =>
161
+ updateScheduleSlot(index, "day", e.target.value)
162
+ }
163
+ >
164
  {DAYS.map((day) => (
165
+ <option key={day} value={day}>
166
  {day}
167
+ </option>
168
  ))}
169
+ </select>
170
+ </div>
171
 
172
+ {/* From / To */}
173
+ <div className="flex flex-1 flex-col gap-3 md:flex-row">
174
  <div className="flex-1">
175
+ <label className="block text-xs font-medium text-stone-700 mb-1">
176
+ From
177
+ </label>
178
+ <input
179
  type="time"
180
+ className="w-full rounded-md border border-stone-300 px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-600"
181
  value={slot.start_time}
182
  onChange={(e) =>
183
  updateScheduleSlot(index, "start_time", e.target.value)
 
185
  />
186
  </div>
187
  <div className="flex-1">
188
+ <label className="block text-xs font-medium text-stone-700 mb-1">
189
+ To
190
+ </label>
191
+ <input
192
  type="time"
193
+ className="w-full rounded-md border border-stone-300 px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-600"
194
  value={slot.end_time}
195
  onChange={(e) =>
196
  updateScheduleSlot(index, "end_time", e.target.value)
 
199
  </div>
200
  </div>
201
 
202
+ {/* Remove */}
203
  {formData.schedule.length > 1 && (
204
+ <button
205
  type="button"
 
 
206
  onClick={() => removeScheduleSlot(index)}
207
+ className="self-start rounded-md p-2 text-red-600 hover:bg-red-50"
208
  >
209
  <Trash2 className="w-4 h-4" />
210
+ </button>
211
  )}
212
  </div>
213
+ </div>
214
  ))}
215
  </div>
216
  </div>
217
 
218
  {/* Actions */}
219
  <div className="flex gap-3 pt-2">
220
+ <button
221
  type="submit"
222
  disabled={isLoading}
223
+ className="inline-flex items-center gap-2 rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-60"
224
  >
225
  {isLoading ? (
226
  <Loader2 className="w-4 h-4 animate-spin" />
 
228
  <Save className="w-4 h-4" />
229
  )}
230
  {classData ? "Update Class" : "Create Class"}
231
+ </button>
232
+
233
+ <button
234
  type="button"
 
235
  onClick={onCancel}
236
  disabled={isLoading}
237
+ className="inline-flex items-center gap-2 rounded-md border border-stone-300 px-4 py-2 text-sm font-medium text-stone-800 hover:bg-stone-50 disabled:opacity-60"
238
  >
239
+ <X className="w-4 h-4" />
240
  Cancel
241
+ </button>
242
  </div>
243
  </form>
244
  );