From 29457a8e6cab159932e53d1bed1aba3abd9eccee Mon Sep 17 00:00:00 2001 From: Guillermo Flores V Date: Sun, 30 Jul 2023 18:36:09 -0700 Subject: [PATCH] (feat): Added hot reloading in our /classes page (#357) * Added functionality for hot reloading in our /classes page * Added fast refresh for deleting a classroom * added fast-refresh for editing classes --- components/ClassInviteTable.js | 38 +++++++++++++++++----------- components/modal.js | 28 +++++++++++++++------ pages/api/create_class_teacher.js | 7 +++--- pages/api/editclass.js | 4 +-- pages/classes/index.js | 42 ++++++++++++++++++++++++++++--- 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/components/ClassInviteTable.js b/components/ClassInviteTable.js index fcacae97..f1965fc8 100644 --- a/components/ClassInviteTable.js +++ b/components/ClassInviteTable.js @@ -6,9 +6,11 @@ import 'react-toastify/dist/ReactToastify.css'; import { MultiSelect } from 'react-multi-select-component'; export default function ClassInviteTable({ - classes, + currentClass, certificationNames, - userId + userId, + handleDelete, + handleEdit }) { const router = useRouter(); const [showOptions, setShowOptions] = useState(false); @@ -16,7 +18,7 @@ export default function ClassInviteTable({ const [formData, setFormData] = useState({}); const getSelectedCerts = () => { - const selectedCerts = classes.selectedCertifications.map(x => x); + const selectedCerts = currentClass.fccCertifications.map(x => x); return certificationNames.filter(x => selectedCerts.includes(x.value)); }; const [selected, setSelected] = useState(() => @@ -32,7 +34,7 @@ export default function ClassInviteTable({ const copy = async () => { //Add the full URL to send to student await navigator.clipboard.writeText( - `${userCurrentDomain}/join/` + classes.classroomId + `${userCurrentDomain}/join/` + currentClass.classroomId ); toast('Class code successfully copied', { @@ -42,7 +44,8 @@ export default function ClassInviteTable({ const deleteClass = async () => { if (confirm('Do you want to delete this class?') == true) { - const JSONdata = JSON.stringify(classes.classroomId); + const JSONdata = JSON.stringify(currentClass.classroomId); + const classToDelete = currentClass.classroomId; try { const res = await fetch(`/api/deleteclass`, { method: 'DELETE', @@ -52,11 +55,10 @@ export default function ClassInviteTable({ body: JSONdata }); if (res.status === 403) { - router.reload('/classes'); alert('Cannot delete class, not valid user'); } else { - router.reload('/classes'); - alert('Successfully Deleted Class'); + handleDelete(classToDelete); + alert('Class successfully deleted.'); } } catch (error) { alert('Sorry, there was an error on our end. Please try again later.'); @@ -73,7 +75,7 @@ export default function ClassInviteTable({ return a - b; }); formData.fccCertifications = fccCertifications; - formData.classroomId = classes.classroomId; + formData.classroomId = currentClass.classroomId; const JSONdata = JSON.stringify(formData); try { const res = await fetch(`/api/editclass`, { @@ -87,7 +89,13 @@ export default function ClassInviteTable({ router.reload('/classes'); alert('No changes modified.'); } else { - router.reload('/classes'); + const jsonRes = await res.json(); + const updatedClassroom = { + classroomName: jsonRes.classroomName, + description: jsonRes.description, + fccCertifications: jsonRes.fccCertifications + }; + handleEdit(currentClass.classroomId, updatedClassroom); alert('Successfully Edited Class'); } } catch (error) { @@ -126,7 +134,7 @@ export default function ClassInviteTable({ >

- Classroom: {classes.classroomName} + Classroom: {currentClass.classroomName}

{/* <-------Menu Item Selection -----> */}
@@ -283,7 +291,7 @@ export default function ClassInviteTable({ id='class-name' name='classname' className='appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm' - placeholder={classes.classroomName} + placeholder={currentClass.classroomName} >
@@ -303,7 +311,7 @@ export default function ClassInviteTable({ id='description-text' name='description' className='appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm' - placeholder={classes.description} + placeholder={currentClass.description} > @@ -348,10 +356,10 @@ export default function ClassInviteTable({

- {classes.description} + {currentClass.description}

- + diff --git a/components/modal.js b/components/modal.js index aae4a221..cba6bc20 100644 --- a/components/modal.js +++ b/components/modal.js @@ -1,8 +1,11 @@ import { useState } from 'react'; -import { useRouter } from 'next/router'; import { MultiSelect } from 'react-multi-select-component'; -export default function Modal({ userId, certificationNames }) { +export default function Modal({ + userId, + certificationNames, + setCurrentClassrooms +}) { const handleCancelClick = () => { setSelected([]); setModalOn(false); @@ -12,7 +15,6 @@ export default function Modal({ userId, certificationNames }) { const [selected, setSelected] = useState([]); const [modalOn, setModalOn] = useState(false); - const router = useRouter(); const clicked = () => { setModalOn(true); @@ -32,9 +34,21 @@ export default function Modal({ userId, certificationNames }) { }, body: JSON.stringify(formData) }); - router.reload(); - alert('Successfully Created Class'); - return await response.json(); + + const jsonRes = await response.json(); + let newClassroom = { + classroomName: jsonRes.classroomName, + description: jsonRes.description, + classroomTeacherId: jsonRes.classroomTeacherId, + fccCertifications: jsonRes.fccCertifications, + classroomId: jsonRes.classroomId, + createdAt: jsonRes.createdAt + }; + setCurrentClassrooms(currentClassrooms => [ + ...currentClassrooms, + newClassroom + ]); + setSelected([]); } return ( @@ -69,7 +83,7 @@ export default function Modal({ userId, certificationNames }) { onChange={e => setFormData({ ...formData, - className: e.target.value, + classroomName: e.target.value, classroomTeacherId: userId }) } diff --git a/pages/api/create_class_teacher.js b/pages/api/create_class_teacher.js index 0497b0ec..08100564 100644 --- a/pages/api/create_class_teacher.js +++ b/pages/api/create_class_teacher.js @@ -1,10 +1,10 @@ import prisma from '../../prisma/prisma'; -import { unstable_getServerSession } from 'next-auth'; +import { getServerSession } from 'next-auth'; import { authOptions } from './auth/[...nextauth]'; export default async function handle(req, res) { //unstable_getServerSession is recommended here: https://next-auth.js.org/configuration/nextjs - const session = await unstable_getServerSession(req, res, authOptions); + const session = await getServerSession(req, res, authOptions); let user; if (!req.method == 'POST') { @@ -43,12 +43,11 @@ export default async function handle(req, res) { const createClassInDB = await prisma.classroom.create({ data: { - classroomName: data['className'], + classroomName: data['classroomName'], description: data['description'], classroomTeacherId: data['classroomTeacherId'], fccCertifications: data['fccCertifications'] } }); - return res.json(createClassInDB); } diff --git a/pages/api/editclass.js b/pages/api/editclass.js index 9605012b..d0808bc9 100644 --- a/pages/api/editclass.js +++ b/pages/api/editclass.js @@ -45,7 +45,7 @@ export default async function handle(req, res) { return res.status(304).end(); } - await prisma.classroom.update({ + const editClassInDB = await prisma.classroom.update({ where: { classroomId: data.classroomId }, @@ -55,5 +55,5 @@ export default async function handle(req, res) { fccCertifications: data.fccCertifications } }); - return res.status(200).end(); + return res.json(editClassInDB); } diff --git a/pages/classes/index.js b/pages/classes/index.js index 7969ba77..76ce051a 100644 --- a/pages/classes/index.js +++ b/pages/classes/index.js @@ -7,6 +7,7 @@ import { getSession } from 'next-auth/react'; import Modal from '../../components/modal'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; +import { useState } from 'react'; export async function getServerSideProps(ctx) { const userSession = await getSession(ctx); @@ -49,7 +50,7 @@ export async function getServerSideProps(ctx) { classroomId: classroom.classroomId, description: classroom.description, createdAt: JSON.stringify(classroom.createdAt), - selectedCertifications: classroom.fccCertifications + fccCertifications: classroom.fccCertifications }) ); @@ -77,6 +78,29 @@ export default function Classes({ user, certificationNames }) { + let [currentClassrooms, setCurrentClassrooms] = useState(classrooms); + const handleDelete = classToDelete => { + setCurrentClassrooms(currentClassrooms => + currentClassrooms.filter( + currClass => currClass.classroomId != classToDelete + ) + ); + }; + const handleEdit = (classToEditId, updatedData) => { + const updatedClassrooms = currentClassrooms.map(currClass => { + if (classToEditId == currClass.classroomId) { + return { + ...currClass, + classroomName: updatedData.classroomName, + description: updatedData.description, + fccCertifications: updatedData.fccCertifications + }; + } + return currClass; + }); + setCurrentClassrooms(updatedClassrooms); + }; + return ( <> Copy invite code by clicking on your preferred class. - {} - {classrooms.map(classroom => ( + { + + } + {currentClassrooms.map(classroom => (