diff --git a/api/index.js b/api/index.js index a756983..6f304c1 100644 --- a/api/index.js +++ b/api/index.js @@ -337,6 +337,59 @@ app.get('/api/class', async (req, res) => { } }) +// Create or Edit Class +app.post('/api/classes', async (req, res) => { + try { + const { title, level, ageGroup, instructor, schedule } = req.body; + + // Check if class already exists by title (assuming title is unique) + const existingClass = await Class.findOne({ title }); + + if (existingClass) { + // If class exists, update it while preserving the roster + const updatedClass = await Class.findByIdAndUpdate( + existingClass._id, + { + $set: { + title, + level, + ageGroup, + instructor, + schedule + } + }, + { + new: true, // Return the updated document + runValidators: true // Run schema validators + } + ); + + return res.status(200).json({ + message: 'Class updated successfully', + class: updatedClass + }); + } else { + // If class doesn't exist, create a new one with empty roster + const newClass = new Class({ + title, + level, + ageGroup, + instructor, + schedule, + }); + + await newClass.save(); + return res.status(201).json({ + message: 'Class created successfully', + class: newClass + }); + } + } catch (error) { + console.error('Error creating/updating class:', error); + return res.status(500).json({ message: 'Error creating/updating class' }); + } +}); + // Enroll in a class app.put('/api/users/:id/enroll', async (req, res) => { const { classId } = req.body diff --git a/src/api/class-wrapper.js b/src/api/class-wrapper.js index a9b1f25..54671c6 100644 --- a/src/api/class-wrapper.js +++ b/src/api/class-wrapper.js @@ -20,6 +20,17 @@ const getLevels = async (query = "") => { } } +// classData should be an object containing title, level, ageGroup, instructor, and schedule +const createOrUpdateClass = async (classData) => { + try { + const response = await axios.post(apiUrl('/api/classes'), classData); + return response.data; + } catch (error) { + console.error('Error creating/updating class:', error); + throw error; + } +} + const getConversations = async () => { try { const response = await axios.get("/api/conversations/") @@ -82,6 +93,7 @@ const getClassById = async (classId) => { export { getClasses, getLevels, + createOrUpdateClass, getConversations, enrollInClass, unenrollInClass, diff --git a/src/components/CreateClassForm.jsx b/src/components/CreateClassForm.jsx new file mode 100644 index 0000000..a1ecbd1 --- /dev/null +++ b/src/components/CreateClassForm.jsx @@ -0,0 +1,137 @@ +import { useState } from 'react'; +import { createOrUpdateClass } from '../api/class-wrapper'; + +const CreateClassForm = () => { + const [formData, setFormData] = useState({ + title: '', + level: '', + ageGroup: '', + instructor: '', + schedule: [{ day: '', time: '' }] + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + const response = await createOrUpdateClass(formData); + alert(response.message); // Show success message + // Reset form + setFormData({ + title: '', + level: '', + ageGroup: '', + instructor: '', + schedule: [{ day: '', time: '' }] + }); + } catch (error) { + alert('Error creating/updating class: ' + error.message); + } + }; + + const handleChange = (e) => { + setFormData({ + ...formData, + [e.target.name]: e.target.value + }); + }; + + const handleScheduleChange = (index, field, value) => { + const newSchedule = [...formData.schedule]; + newSchedule[index] = { + ...newSchedule[index], + [field]: value + }; + setFormData({ + ...formData, + schedule: newSchedule + }); + }; + + return ( +
+

Create/Edit Class

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + {formData.schedule.map((scheduleItem, index) => ( +
+

Schedule {index + 1}

+
+ handleScheduleChange(index, 'day', e.target.value)} + className="rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" + required + /> + handleScheduleChange(index, 'time', e.target.value)} + className="rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" + required + /> +
+
+ ))} + + +
+
+ ); +}; + +export default CreateClassForm; diff --git a/src/pages/Classes.jsx b/src/pages/Classes.jsx new file mode 100644 index 0000000..6928ec4 --- /dev/null +++ b/src/pages/Classes.jsx @@ -0,0 +1,118 @@ +import { useState, useEffect } from 'react'; +import { useSearch, useLocation } from 'wouter'; +import Class from '../components/Class'; +import Level from '../components/Level'; +import CreateClassForm from '../components/CreateClassForm'; +import { getClasses, getLevels } from '../api/class-wrapper'; + +const Classes = () => { + const [classes, setClasses] = useState([]); + const [level, setLevel] = useState(); + const [allLevels, setAllLevels] = useState([]); + const [loading, setLoading] = useState(true); + const queryString = useSearch(); + const [location, setLocation] = useLocation(); + const classFilter = new URLSearchParams(queryString); + + useEffect(() => { + if (queryString === "") { + setLocation("/levels"); + } + + const fetchData = async () => { + setLoading(true); + const classData = await getClasses(classFilter.toString()); + setClasses(classData); + const levelData = await getLevels(); + setAllLevels(levelData); + setLevel(levelData.find(l => l.level === parseInt(classFilter.get("level")))); + setLoading(false); + }; + fetchData(); + }, []); + + if (loading || !level) return <>; + + return ( +
+ {/* Banner Section */} +
+
+

Level {level.level}

+

{level.name}

+ +
+
+
+
+
+ {level.enrolledCount || 46} students enrolled +
+ +

+ This class is for those with little to no experience in English. It will be going over + the alphabet, basic vocabulary, and simple grammar rules. +

+ +
+ + Basic Conversations + + + The Alphabet + +
+
+
+ + {/* Content Section */} +
+ {/* Create/Edit Class Form */} +
+ +
+ + {/* Open Classes */} +
+

Open Classes

+

+ Here are the open classes in this level. More information will be given by the instructor after you sign up! +

+
+ {classes.map((classObj, classIndex) => ( + + ))} +
+
+ + {/* Other Levels */} +
+

Other Levels

+

+ If this level isn't a good fit for you, take a look at the other available levels! +

+
+ {allLevels + .filter(l => l.level !== level.level) + .map((level, index) => ( +
+
+

Level {level.level}

+

{level.description || "New Concept Book 1 -- Ch. 1-72"}

+ +
+
+ ))} +
+
+
+
+ ); +}; + +export default Classes; \ No newline at end of file