Seth0330 commited on
Commit
987557e
·
verified ·
1 Parent(s): 3fd27d8

Create ClassForm.jsx

Browse files
frontend/src/components/admin/ClassForm.jsx ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // frontend/src/components/admin/ClassForm.jsx
2
+ import React, { useState } from "react";
3
+
4
+ const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
5
+
6
+ export default function ClassForm({
7
+ classData,
8
+ plans = [],
9
+ onSave,
10
+ onCancel,
11
+ isLoading,
12
+ }) {
13
+ const [name, setName] = useState(classData?.name || "");
14
+ const [classTime, setClassTime] = useState(classData?.class_time || "");
15
+ const [description, setDescription] = useState(classData?.description || "");
16
+ const [daysOfWeek, setDaysOfWeek] = useState(classData?.days_of_week || []);
17
+ const [classesPerWeek, setClassesPerWeek] = useState(
18
+ classData?.classes_per_week || 2
19
+ );
20
+ const [maxStudents, setMaxStudents] = useState(
21
+ classData?.max_students || 20
22
+ );
23
+ const [membershipPlanId, setMembershipPlanId] = useState(
24
+ classData?.membership_plan_id || ""
25
+ );
26
+
27
+ function toggleDay(day) {
28
+ setDaysOfWeek((prev) =>
29
+ prev.includes(day) ? prev.filter((d) => d !== day) : [...prev, day]
30
+ );
31
+ }
32
+
33
+ function handleSubmit(e) {
34
+ e.preventDefault();
35
+ if (!name.trim()) return;
36
+
37
+ const payload = {
38
+ name: name.trim(),
39
+ description: description.trim() || null,
40
+ class_time: classTime || null,
41
+ days_of_week: daysOfWeek,
42
+ classes_per_week: Number(classesPerWeek) || 0,
43
+ max_students: Number(maxStudents) || 0,
44
+ membership_plan_id: membershipPlanId ? Number(membershipPlanId) : null,
45
+ };
46
+
47
+ onSave && onSave(payload);
48
+ }
49
+
50
+ return (
51
+ <form onSubmit={handleSubmit} className="space-y-5">
52
+ {/* Class name & time */}
53
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
54
+ <div>
55
+ <label className="block text-sm font-medium text-stone-800 mb-1">
56
+ Class Name
57
+ </label>
58
+ <input
59
+ type="text"
60
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500"
61
+ placeholder="e.g., Beginner Karate"
62
+ value={name}
63
+ onChange={(e) => setName(e.target.value)}
64
+ />
65
+ </div>
66
+ <div>
67
+ <label className="block text-sm font-medium text-stone-800 mb-1">
68
+ Class Time
69
+ </label>
70
+ <input
71
+ type="time"
72
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500"
73
+ value={classTime}
74
+ onChange={(e) => setClassTime(e.target.value)}
75
+ />
76
+ </div>
77
+ </div>
78
+
79
+ {/* Description */}
80
+ <div>
81
+ <label className="block text-sm font-medium text-stone-800 mb-1">
82
+ Description
83
+ </label>
84
+ <textarea
85
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500 min-h-[80px]"
86
+ placeholder="Class description..."
87
+ value={description}
88
+ onChange={(e) => setDescription(e.target.value)}
89
+ />
90
+ </div>
91
+
92
+ {/* Days of week */}
93
+ <div>
94
+ <label className="block text-sm font-medium text-stone-800 mb-2">
95
+ Days of Week
96
+ </label>
97
+ <div className="flex flex-wrap gap-2">
98
+ {DAYS.map((day) => {
99
+ const active = daysOfWeek.includes(day);
100
+ return (
101
+ <button
102
+ key={day}
103
+ type="button"
104
+ onClick={() => toggleDay(day)}
105
+ className={`px-3 py-1.5 rounded-full text-xs font-medium border ${
106
+ active
107
+ ? "bg-red-600 text-white border-red-600"
108
+ : "bg-white text-stone-700 border-stone-200 hover:bg-stone-50"
109
+ }`}
110
+ >
111
+ {day}
112
+ </button>
113
+ );
114
+ })}
115
+ </div>
116
+ </div>
117
+
118
+ {/* Numbers & plan */}
119
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
120
+ <div>
121
+ <label className="block text-sm font-medium text-stone-800 mb-1">
122
+ Classes Per Week
123
+ </label>
124
+ <input
125
+ type="number"
126
+ min="0"
127
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500"
128
+ value={classesPerWeek}
129
+ onChange={(e) => setClassesPerWeek(e.target.value)}
130
+ />
131
+ </div>
132
+ <div>
133
+ <label className="block text-sm font-medium text-stone-800 mb-1">
134
+ Max Students
135
+ </label>
136
+ <input
137
+ type="number"
138
+ min="0"
139
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-red-500"
140
+ value={maxStudents}
141
+ onChange={(e) => setMaxStudents(e.target.value)}
142
+ />
143
+ </div>
144
+ <div>
145
+ <label className="block text-sm font-medium text-stone-800 mb-1">
146
+ Membership Plan
147
+ </label>
148
+ <select
149
+ className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm bg-white focus:outline-none focus:ring-1 focus:ring-red-500"
150
+ value={membershipPlanId || ""}
151
+ onChange={(e) => setMembershipPlanId(e.target.value)}
152
+ >
153
+ <option value="">Select plan</option>
154
+ {plans.map((p) => (
155
+ <option key={p.id} value={p.id}>
156
+ {p.name}
157
+ </option>
158
+ ))}
159
+ </select>
160
+ </div>
161
+ </div>
162
+
163
+ {/* Actions */}
164
+ <div className="flex justify-start gap-3 pt-2">
165
+ <button
166
+ type="submit"
167
+ disabled={isLoading}
168
+ className="inline-flex items-center justify-center rounded-lg bg-red-600 hover:bg-red-700 text-white text-sm font-medium px-4 py-2 disabled:opacity-60"
169
+ >
170
+ {isLoading ? "Saving..." : classData ? "Update Class" : "Create Class"}
171
+ </button>
172
+ <button
173
+ type="button"
174
+ onClick={onCancel}
175
+ className="inline-flex items-center justify-center rounded-lg border border-stone-200 text-sm font-medium px-4 py-2 text-stone-700 hover:bg-stone-50"
176
+ >
177
+ Cancel
178
+ </button>
179
+ </div>
180
+ </form>
181
+ );
182
+ }