From 18867b073a7fcb83aa374d445c8fe07ade33e9ff Mon Sep 17 00:00:00 2001 From: lavanyagarg112 Date: Wed, 2 Jul 2025 01:02:34 +0200 Subject: [PATCH 01/11] Add course tags --- .../organisation/courses/CourseForm.tsx | 205 ++++--- package-lock.json | 530 +++++++++++++++++- package.json | 3 +- 3 files changed, 606 insertions(+), 132 deletions(-) diff --git a/components/organisation/courses/CourseForm.tsx b/components/organisation/courses/CourseForm.tsx index b3353c2..c6b247e 100644 --- a/components/organisation/courses/CourseForm.tsx +++ b/components/organisation/courses/CourseForm.tsx @@ -1,14 +1,23 @@ // components/organisation/courses/CourseForm.tsx "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; -import { useEffect } from "react"; +import CreatableSelect from "react-select/creatable"; export interface Course { id: number; name: string; description?: string; + tags?: { id: number; name: string }[]; +} +interface Tag { + id: number; + name: string; +} +interface Option { + value: number | string; + label: string; } interface Props { @@ -19,121 +28,94 @@ interface Props { export default function CourseForm({ mode, courseId }: Props) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); + const [allTags, setAllTags] = useState([]); + const [options, setOptions] = useState([]); + const [selected, setSelected] = useState([]); const router = useRouter(); useEffect(() => { - async function fetchCourse() { - const response = await fetch( - `http://localhost:4000/api/courses/get-course`, - { - credentials: "include", - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ courseId }), - } - ).then((res) => { - if (!res.ok) { - throw new Error("Failed to fetch course"); - } - return res.json(); - }); - setName(response.name); - setDescription(response.description || ""); - } - if (mode === "edit" && courseId) { - // Fetch course details if in edit mode - fetchCourse().catch((error) => { - console.error("Error fetching course:", error); - alert("Failed to load course data. Please try again."); - router.push("/courses"); - }); - } - }, []); + fetch("/api/courses/tags", { credentials: "include" }) + .then((r) => r.json()) + .then((tags: Tag[]) => { + setAllTags(tags); + setOptions(tags.map((t) => ({ value: t.id, label: t.name }))); + }) + .catch(console.error); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - const payload = { courseName: name, description }; - - if (mode === "create") { - try { - const res = await fetch("/api/courses", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify(payload), - }); - if (!res.ok) { - throw new Error("Failed to create course"); - } - const data = await res.json(); - console.log("Course created:", data); - router.push("/courses"); - } catch (error) { - console.error("Error creating course:", error); - alert("Failed to create course. Please try again."); - return; - } - } else { - try { - const res = await fetch("/api/courses", { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify({ courseId: courseId, ...payload }), + if (mode === "edit" && courseId) { + fetch("/api/courses/get-course", { + method: "POST", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ courseId }), + }) + .then((r) => { + if (!r.ok) throw new Error(); + return r.json(); + }) + .then((course: Course) => { + setName(course.name); + setDescription(course.description || ""); + const pre = (course.tags || []).map((t) => ({ + value: t.id, + label: t.name, + })); + setSelected(pre); + }) + .catch((_) => { + alert("Failed to load course"); + router.push("/courses"); }); - if (!res.ok) { - throw new Error("Failed to update course"); - } - const data = await res.json(); - console.log("Course updated:", data); - router.push("/courses"); - } catch (error) { - console.error("Error creating course:", error); - alert("Failed to update course. Please try again."); - return; - } } + }, [mode, courseId, router]); + + const handleCreate = (inputValue: string) => { + const newOpt = { value: inputValue, label: inputValue }; + setOptions((o) => [...o, newOpt]); + setSelected((s) => [...s, newOpt]); }; - const handleDelete = async (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + const tags = selected.map((o) => o.value); + const payload = { + courseName: name, + description, + tags, + updateTags: mode === "edit", + }; - const confirmed = window.confirm( - "Are you sure you want to delete this course? This action cannot be undone." - ); - if (!confirmed) { - return; - } - if (!courseId) { - alert("Please provide a course name to delete."); + const url = "/api/courses"; + const method = mode === "create" ? "POST" : "PUT"; + const body = mode === "create" ? payload : { courseId, ...payload }; + + const res = await fetch(url, { + method, + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + if (!res.ok) { + alert(`${mode === "create" ? "Create" : "Update"} failed`); return; } - try { - const res = await fetch(`/api/courses`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify({ courseId: courseId }), - }); - if (!res.ok) { - throw new Error("Failed to delete course"); - } - const data = await res.json(); - console.log("Course deleted:", data); - router.push("/courses"); - } catch (error) { - console.error("Error deleting course:", error); - alert("Failed to delete course. Please try again."); + router.push("/courses"); + }; + + const handleDelete = async () => { + if (!confirm("Are you sure?")) return; + if (!courseId) return; + const res = await fetch("/api/courses", { + method: "DELETE", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ courseId }), + }); + if (!res.ok) { + alert("Delete failed"); return; } + router.push("/courses"); }; return ( @@ -154,10 +136,22 @@ export default function CourseForm({ mode, courseId }: Props) {