Seth0330's picture
Update frontend/src/components/admin/ClassForm.jsx
25874a8 verified
// frontend/src/components/admin/ClassForm.jsx
import React, { useState, useEffect } from "react";
import { Plus, Trash2, Loader2, Save, X } from "lucide-react";
const DAYS = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
export default function ClassForm({ classData, onSave, onCancel, isLoading }) {
// schedule: [{ day, start_time, end_time }]
const [formData, setFormData] = useState({
name: "",
description: "",
coach_email: "",
schedule: [{ day: "Monday", start_time: "", end_time: "" }],
is_active: true,
});
useEffect(() => {
if (!classData) return;
setFormData({
name: classData.name || "",
description: classData.description || "",
coach_email: classData.coach_email || "",
schedule:
Array.isArray(classData.schedule) && classData.schedule.length > 0
? classData.schedule.map((slot) => ({
day: slot.day || "Monday",
start_time: slot.start_time || "",
end_time: slot.end_time || "",
}))
: [{ day: "Monday", start_time: "", end_time: "" }],
is_active:
classData.is_active !== undefined ? classData.is_active : true,
});
}, [classData]);
const addScheduleSlot = () => {
setFormData((prev) => ({
...prev,
schedule: [
...prev.schedule,
{ day: "Monday", start_time: "", end_time: "" },
],
}));
};
const removeScheduleSlot = (index) => {
setFormData((prev) => ({
...prev,
schedule: prev.schedule.filter((_, i) => i !== index),
}));
};
const updateScheduleSlot = (index, field, value) => {
setFormData((prev) => {
const schedule = [...prev.schedule];
schedule[index] = { ...schedule[index], [field]: value };
return { ...prev, schedule };
});
};
const handleSubmit = (e) => {
e.preventDefault();
// Transform schedule to a simple structure that backend can later map
const transformedSchedule = formData.schedule.map((slot) => ({
day: slot.day,
start_time: slot.start_time,
end_time: slot.end_time,
time:
slot.start_time && slot.end_time
? `${slot.start_time}-${slot.end_time}`
: "",
}));
const payload = {
name: formData.name,
description: formData.description,
coach_email: formData.coach_email || null,
schedule: transformedSchedule,
is_active: formData.is_active,
};
onSave(payload);
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* Class Name */}
<div className="space-y-1">
<label className="block text-sm font-medium text-stone-800">
Class Name
</label>
<input
type="text"
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"
value={formData.name}
onChange={(e) =>
setFormData((prev) => ({ ...prev, name: e.target.value }))
}
placeholder="e.g., Beginner Karate"
required
/>
</div>
{/* Description */}
<div className="space-y-1">
<label className="block text-sm font-medium text-stone-800">
Description
</label>
<textarea
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"
rows={2}
value={formData.description}
onChange={(e) =>
setFormData((prev) => ({
...prev,
description: e.target.value,
}))
}
placeholder="Class description..."
/>
</div>
{/* Coach Email */}
<div className="space-y-1">
<label className="block text-sm font-medium text-stone-800">
Coach Email (Optional)
</label>
<input
type="email"
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"
value={formData.coach_email}
onChange={(e) =>
setFormData((prev) => ({ ...prev, coach_email: e.target.value }))
}
placeholder="coach@dojo.com"
/>
<p className="text-xs text-stone-500 mt-1">
Assign a coach to this class. Coach can login with this email to manage attendance.
</p>
</div>
{/* Schedule */}
<div>
<div className="flex items-center justify-between mb-3">
<span className="text-sm font-medium text-stone-800">
Class Schedule
</span>
<button
type="button"
onClick={addScheduleSlot}
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"
>
<Plus className="w-4 h-4" />
Add Time Slot
</button>
</div>
<div className="space-y-3">
{formData.schedule.map((slot, index) => (
<div
key={index}
className="rounded-lg border border-stone-200 bg-stone-50 px-3 py-3"
>
<div className="flex flex-col gap-3 md:flex-row md:items-center">
{/* Day */}
<div className="w-full md:w-40">
<label className="block text-xs font-medium text-stone-700 mb-1">
Day
</label>
<select
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"
value={slot.day}
onChange={(e) =>
updateScheduleSlot(index, "day", e.target.value)
}
>
{DAYS.map((day) => (
<option key={day} value={day}>
{day}
</option>
))}
</select>
</div>
{/* From / To */}
<div className="flex flex-1 flex-col gap-3 md:flex-row">
<div className="flex-1">
<label className="block text-xs font-medium text-stone-700 mb-1">
From
</label>
<input
type="time"
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"
value={slot.start_time}
onChange={(e) =>
updateScheduleSlot(index, "start_time", e.target.value)
}
/>
</div>
<div className="flex-1">
<label className="block text-xs font-medium text-stone-700 mb-1">
To
</label>
<input
type="time"
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"
value={slot.end_time}
onChange={(e) =>
updateScheduleSlot(index, "end_time", e.target.value)
}
/>
</div>
</div>
{/* Remove */}
{formData.schedule.length > 1 && (
<button
type="button"
onClick={() => removeScheduleSlot(index)}
className="self-start rounded-md p-2 text-red-600 hover:bg-red-50"
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
</div>
))}
</div>
</div>
{/* Actions */}
<div className="flex gap-3 pt-2">
<button
type="submit"
disabled={isLoading}
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"
>
{isLoading ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
{classData ? "Update Class" : "Create Class"}
</button>
<button
type="button"
onClick={onCancel}
disabled={isLoading}
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"
>
<X className="w-4 h-4" />
Cancel
</button>
</div>
</form>
);
}