From d9c5423e057fa017f6e9ebd4ef032f862f319876 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Fri, 13 Dec 2024 14:52:12 -0800 Subject: [PATCH 01/13] add updateStatusOnServer() to actions and pass id and newStatus to server --- client/actions/form.js | 31 ++++++++++--------- client/components/admin_dashboard/Grid.jsx | 5 ++- .../admin_dashboard/StatusChange.jsx | 13 +++++--- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index 5541cf7..1cfa148 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -101,18 +101,19 @@ export async function updateVisitedOnServer(id) { } } -// export async function updateStatusOnServer(address, status) { -// const response = await fetch(serverUrl + "appointments/status-change", { -// method: "PATCH", -// headers: { -// "Content-Type": "application/json", -// }, -// body: JSON.stringify({ address, status }), -// }); - -// const data = await response.json(); - -// if (!response.ok) { -// return { message: data.message }; -// } -// } +export async function updateStatusOnServer(id, newStatus) { + console.log("inside updateStatusOnServer"); + const response = await fetch(serverUrl + `appointments/${id}/status-change`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ id, newStatus }), + }); + + const data = await response.json(); + + if (!response.ok) { + return { message: data.message }; + } +} diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 44d09be..837e383 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -85,7 +85,10 @@ export default function Grid({ rows }) { field: "status", headerName: "Status", width: 190, - renderCell: (status) => , + renderCell: (params) => { + const { id } = params.row; + return ; + }, }, { valueFormatter: formatName, diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index 3a05e66..03ae703 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -1,11 +1,16 @@ +"use client"; import React from "react"; import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material"; +import { updateStatusOnServer } from "@/actions/form"; -export default function StatusChange() { +export default function StatusChange({ id }) { const [status, setStatus] = React.useState("Requested"); - const handleChange = (e) => { - setStatus(e.target.value); + const handleChange = (e, id) => { + const newStatus = e.target.value; + console.log("newStatus", newStatus); + setStatus(newStatus); + updateStatusOnServer(id, newStatus); }; return ( @@ -16,7 +21,7 @@ export default function StatusChange() { id="status" value={status} label="Status" - onChange={handleChange} + onChange={(e) => handleChange(e, id)} > Requested Confirmed From 7a45ef61937081d0e1fad83717912cdc39d44517 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Fri, 13 Dec 2024 16:46:00 -0800 Subject: [PATCH 02/13] update newStatus on the server, add patch route --- .../controllers/appointments.controller.js | 21 +++++++++++++++++++ server/src/routes/appointments.routes.js | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/server/src/controllers/appointments.controller.js b/server/src/controllers/appointments.controller.js index 7abe511..9334e7b 100644 --- a/server/src/controllers/appointments.controller.js +++ b/server/src/controllers/appointments.controller.js @@ -207,3 +207,24 @@ export async function updateVisited(req, res) { return res.status(500).json({ message: "Server error: updating status" }); } } + +export async function updateStatus(req, res) { + const { id, newStatus } = req.params; + + try { + const data = await Appointment.findByIdAndUpdate( + id, + { status: newStatus }, + { new: true } + ); + + if (!data) { + return res.status(404).json({ message: "Could not get data" }); + } + + console.log(data.status); + return res.status(200).json(data.status); + } catch (error) { + return res.status(500).json({ message: "Server error: saving new status" }); + } +} diff --git a/server/src/routes/appointments.routes.js b/server/src/routes/appointments.routes.js index 9743c89..eb9ae2a 100644 --- a/server/src/routes/appointments.routes.js +++ b/server/src/routes/appointments.routes.js @@ -6,6 +6,7 @@ import { newAppointment, updateVisited, cancelAppointment, + updateStatus, } from "../controllers/appointments.controller.js"; const router = Router(); @@ -28,4 +29,7 @@ router.patch("/cancel", cancelAppointment); // PATCH "/appointments/:id" router.patch("/:id", updateVisited); +// PATCH "/appointments/:id/status-change" +router.patch("/:id/status-change", updateStatus); + export default router; From a5a1b97df856ad1ac10da70b7979a6d840d62918 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Fri, 13 Dec 2024 17:35:56 -0800 Subject: [PATCH 03/13] await promise --- client/actions/form.js | 2 +- client/components/admin_dashboard/Map.jsx | 1 - client/components/admin_dashboard/StatusChange.jsx | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index 1cfa148..967a0db 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -102,7 +102,7 @@ export async function updateVisitedOnServer(id) { } export async function updateStatusOnServer(id, newStatus) { - console.log("inside updateStatusOnServer"); + console.log("updateStatus: newStatus", newStatus); const response = await fetch(serverUrl + `appointments/${id}/status-change`, { method: "PATCH", headers: { diff --git a/client/components/admin_dashboard/Map.jsx b/client/components/admin_dashboard/Map.jsx index c120e03..cbf7640 100644 --- a/client/components/admin_dashboard/Map.jsx +++ b/client/components/admin_dashboard/Map.jsx @@ -40,7 +40,6 @@ export default function Map({ appointments }) { }); }); }; - console.log(appointments[0]); return ( { + const handleChange = async (e, id) => { const newStatus = e.target.value; console.log("newStatus", newStatus); setStatus(newStatus); - updateStatusOnServer(id, newStatus); + await updateStatusOnServer(id, newStatus); }; return ( From 84f88f2ea0c463c69a8eaf505950d192fda6a11e Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Mon, 16 Dec 2024 17:14:27 -0800 Subject: [PATCH 04/13] fix appt id sent to server to reflect correct datatype and mongodb id for status change --- client/actions/form.js | 3 +- .../controllers/appointments.controller.js | 26 ++------ server/src/models/appointments.model.js | 2 +- server/src/routes/appointments.routes.js | 2 +- server/src/scheduling/scheduler.js | 60 ++++++++++++++----- 5 files changed, 52 insertions(+), 41 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index 967a0db..8600caa 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -103,12 +103,13 @@ export async function updateVisitedOnServer(id) { export async function updateStatusOnServer(id, newStatus) { console.log("updateStatus: newStatus", newStatus); + console.log("id", id); const response = await fetch(serverUrl + `appointments/${id}/status-change`, { method: "PATCH", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ id, newStatus }), + body: JSON.stringify({ newStatus }), }); const data = await response.json(); diff --git a/server/src/controllers/appointments.controller.js b/server/src/controllers/appointments.controller.js index 9334e7b..4452006 100644 --- a/server/src/controllers/appointments.controller.js +++ b/server/src/controllers/appointments.controller.js @@ -120,7 +120,10 @@ export async function getAllAppointments(req, res, next) { try { const appointments = await Appointment.find(); const withScheduling = appendSchedule( - appointments.map((a) => a.toObject()) + appointments.map((a) => ({ + ...a.toObject(), + id: a.id, + })) ); res.status(200).json(withScheduling); } catch (error) { @@ -207,24 +210,3 @@ export async function updateVisited(req, res) { return res.status(500).json({ message: "Server error: updating status" }); } } - -export async function updateStatus(req, res) { - const { id, newStatus } = req.params; - - try { - const data = await Appointment.findByIdAndUpdate( - id, - { status: newStatus }, - { new: true } - ); - - if (!data) { - return res.status(404).json({ message: "Could not get data" }); - } - - console.log(data.status); - return res.status(200).json(data.status); - } catch (error) { - return res.status(500).json({ message: "Server error: saving new status" }); - } -} diff --git a/server/src/models/appointments.model.js b/server/src/models/appointments.model.js index 0691c2a..ae17c97 100644 --- a/server/src/models/appointments.model.js +++ b/server/src/models/appointments.model.js @@ -16,7 +16,7 @@ const appointmentSchema = new mongoose.Schema({ }, status: { type: String, - enum: ["Pending", "Confirmed", "Cancelled", "Visited"], + enum: ["Pending", "Confirmed", "Scheduled", "Cancelled", "Visited"], default: "Pending", }, notifications: { diff --git a/server/src/routes/appointments.routes.js b/server/src/routes/appointments.routes.js index eb9ae2a..36a7848 100644 --- a/server/src/routes/appointments.routes.js +++ b/server/src/routes/appointments.routes.js @@ -6,8 +6,8 @@ import { newAppointment, updateVisited, cancelAppointment, - updateStatus, } from "../controllers/appointments.controller.js"; +import { updateStatus } from "../scheduling/scheduler.js"; const router = Router(); diff --git a/server/src/scheduling/scheduler.js b/server/src/scheduling/scheduler.js index 8ba9506..ba53071 100644 --- a/server/src/scheduling/scheduler.js +++ b/server/src/scheduling/scheduler.js @@ -1,3 +1,5 @@ +import Appointment from "../models/appointments.model.js"; + /** * Comparator function for 2 appointments. See the definition of array.sort() */ @@ -8,21 +10,47 @@ const appointmentComparator = (a, b) => a.dateCreated - b.dateCreated; * Doesn't change the order of the array. */ const appendSchedule = (appointments) => { - const comparator = (a, b) => appointmentComparator(a.appointment, b.appointment) - const sortedArray = appointments.map((appointment, index) => ({ appointment, index })).sort(comparator); - - const orderMap = new Map(); - sortedArray.forEach((item, index) => { - const visitOrder = index + 1; - orderMap.set(item.index, visitOrder); - }); - - return appointments.map((appointment, index) => ({ - ...appointment, - schedule: { - order: orderMap.get(index), - }, - })) + const comparator = (a, b) => + appointmentComparator(a.appointment, b.appointment); + const sortedArray = appointments + .map((appointment, index) => ({ appointment, index })) + .sort(comparator); + + const orderMap = new Map(); + sortedArray.forEach((item, index) => { + const visitOrder = index + 1; + orderMap.set(item.index, visitOrder); + }); + + return appointments.map((appointment, index) => ({ + ...appointment, + schedule: { + order: orderMap.get(index), + }, + })); +}; + +export async function updateStatus(req, res) { + const { id } = req.params; + const { newStatus } = req.body; + + try { + const data = await Appointment.findByIdAndUpdate( + { _id: id }, + { status: newStatus }, + { new: true } + ); + + if (!data) { + return res.status(404).json({ message: "Could not get data" }); + } + + console.log("data.status", data.status); + return res.status(200).json(data.status); + } catch (error) { + console.log(error); + return res.status(500).json({ message: "Server error: saving new status" }); + } } -export { appendSchedule } \ No newline at end of file +export { appendSchedule }; From 0b7dbf54c78ab8e5e4e8b8028e988bb2e5453322 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Tue, 17 Dec 2024 11:36:32 -0800 Subject: [PATCH 05/13] add refreshData() to update stale appts and pass down to StatusChange to be called when admin changes status --- client/app/admin-dashboard/page.jsx | 7 ++++--- client/components/admin_dashboard/Grid.jsx | 4 ++-- client/components/admin_dashboard/Map.jsx | 6 +++--- .../admin_dashboard/ReservationTable.jsx | 15 +++++++++++++-- .../components/admin_dashboard/StatusChange.jsx | 3 ++- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/client/app/admin-dashboard/page.jsx b/client/app/admin-dashboard/page.jsx index a3e81ad..5167af4 100644 --- a/client/app/admin-dashboard/page.jsx +++ b/client/app/admin-dashboard/page.jsx @@ -6,14 +6,15 @@ import Map from "../../components/admin_dashboard/Map"; import { fetchAppointments } from "@/actions/form"; async function Dashboard() { - const appointments = await fetchAppointments(); + const initAppointments = await fetchAppointments(); + return ( - + - + ); diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 837e383..0e1ef45 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -52,7 +52,7 @@ const Toolbar = () => ( ); -export default function Grid({ rows }) { +export default function Grid({ rows, refreshData }) { const [searchText, setSearchText] = useState(""); const [customRows, setCustomRows] = useState(rows); @@ -87,7 +87,7 @@ export default function Grid({ rows }) { width: 190, renderCell: (params) => { const { id } = params.row; - return ; + return ; }, }, { diff --git a/client/components/admin_dashboard/Map.jsx b/client/components/admin_dashboard/Map.jsx index cbf7640..9672866 100644 --- a/client/components/admin_dashboard/Map.jsx +++ b/client/components/admin_dashboard/Map.jsx @@ -3,8 +3,8 @@ import { GoogleMap, LoadScript } from "@react-google-maps/api"; import { googleApiKey, appointmentsMapId } from "@/constants"; import React, { useRef } from "react"; -export default function Map({ appointments }) { - if (!appointments.length) { +export default function Map({ initAppointments }) { + if (!initAppointments.length) { return

No map data available

; } @@ -15,7 +15,7 @@ export default function Map({ appointments }) { height: "400px", }; - const startCoords = appointments.map((item) => ({ + const startCoords = initAppointments.map((item) => ({ location: item.location, visitOrder: item.schedule.order, customerName: item.name, diff --git a/client/components/admin_dashboard/ReservationTable.jsx b/client/components/admin_dashboard/ReservationTable.jsx index 6733489..cbf10c1 100644 --- a/client/components/admin_dashboard/ReservationTable.jsx +++ b/client/components/admin_dashboard/ReservationTable.jsx @@ -1,10 +1,21 @@ +"use client"; + +import { useState } from "react"; import Box from "@mui/material/Box"; import Grid from "./Grid"; +import { fetchAppointments } from "@/actions/form"; + +export default function ReservationTable({ initAppointments }) { + const [appointments, setAppointments] = useState(initAppointments); + + const refreshData = async () => { + const updatedAppts = await fetchAppointments(); + setAppointments(updatedAppts); + }; -export default function ReservationTable({ appointments }) { return ( - + ); } diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index 34ccc06..f3ae468 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -3,7 +3,7 @@ import React from "react"; import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material"; import { updateStatusOnServer } from "@/actions/form"; -export default function StatusChange({ id }) { +export default function StatusChange({ id, refreshData }) { const [status, setStatus] = React.useState("Requested"); const handleChange = async (e, id) => { @@ -11,6 +11,7 @@ export default function StatusChange({ id }) { console.log("newStatus", newStatus); setStatus(newStatus); await updateStatusOnServer(id, newStatus); + refreshData(); }; return ( From 81d764b360da3040a6027a4a19a64bbbc1f2f4f0 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Tue, 17 Dec 2024 12:24:34 -0800 Subject: [PATCH 06/13] add revalidatePath for status update --- client/actions/form.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/actions/form.js b/client/actions/form.js index 8600caa..53c3c78 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -19,6 +19,7 @@ export async function requestAppt(formValues) { // GET ALL APPTS export async function fetchAppointments() { + console.log("Fetching updated appointments..."); try { const response = await fetch(serverUrl + "appointments", { cache: "no-store", @@ -114,6 +115,9 @@ export async function updateStatusOnServer(id, newStatus) { const data = await response.json(); + revalidatePath("/admin-dashboard"); + console.log("Revalidation triggered for /admin-dashboard"); + if (!response.ok) { return { message: data.message }; } From 5f9e379c2dede35642190d03ef986260aaa1bd97 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Tue, 17 Dec 2024 12:49:17 -0800 Subject: [PATCH 07/13] initialize status state on StatusChange with currentStatus --- client/actions/form.js | 6 +++--- client/components/admin_dashboard/Grid.jsx | 10 ++++++++-- client/components/admin_dashboard/ReservationTable.jsx | 1 + client/components/admin_dashboard/StatusChange.jsx | 6 ++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index 53c3c78..d1f36e6 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -115,10 +115,10 @@ export async function updateStatusOnServer(id, newStatus) { const data = await response.json(); - revalidatePath("/admin-dashboard"); - console.log("Revalidation triggered for /admin-dashboard"); - if (!response.ok) { return { message: data.message }; } + + revalidatePath("/admin-dashboard"); + console.log("Revalidation triggered for /admin-dashboard"); } diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 0e1ef45..7404f98 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -86,8 +86,14 @@ export default function Grid({ rows, refreshData }) { headerName: "Status", width: 190, renderCell: (params) => { - const { id } = params.row; - return ; + const { id, status } = params.row; + return ( + + ); }, }, { diff --git a/client/components/admin_dashboard/ReservationTable.jsx b/client/components/admin_dashboard/ReservationTable.jsx index cbf10c1..07617b8 100644 --- a/client/components/admin_dashboard/ReservationTable.jsx +++ b/client/components/admin_dashboard/ReservationTable.jsx @@ -10,6 +10,7 @@ export default function ReservationTable({ initAppointments }) { const refreshData = async () => { const updatedAppts = await fetchAppointments(); + console.log("Updated Appointments:", updatedAppts); setAppointments(updatedAppts); }; diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index f3ae468..a52454f 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -3,8 +3,10 @@ import React from "react"; import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material"; import { updateStatusOnServer } from "@/actions/form"; -export default function StatusChange({ id, refreshData }) { - const [status, setStatus] = React.useState("Requested"); +export default function StatusChange({ id, refreshData, currentStatus }) { + const [status, setStatus] = React.useState(currentStatus); + + console.log("StatusChange component rendered for id:", id); const handleChange = async (e, id) => { const newStatus = e.target.value; From ab268b1e92f883b7f6ab3a0274afae40a3731b35 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Tue, 17 Dec 2024 13:57:13 -0800 Subject: [PATCH 08/13] toggle button as 'visited' when status changes to 'completed' --- client/actions/form.js | 7 ++----- client/components/admin_dashboard/Grid.jsx | 6 +++--- client/components/admin_dashboard/ReservationTable.jsx | 1 - client/components/admin_dashboard/StatusChange.jsx | 3 --- server/src/controllers/appointments.controller.js | 7 +++++-- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index d1f36e6..cae1a9f 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -19,7 +19,6 @@ export async function requestAppt(formValues) { // GET ALL APPTS export async function fetchAppointments() { - console.log("Fetching updated appointments..."); try { const response = await fetch(serverUrl + "appointments", { cache: "no-store", @@ -87,12 +86,13 @@ export async function cancelAppointment(email) { revalidatePath("/my-appointments"); } -export async function updateVisitedOnServer(id) { +export async function updateVisitedOnServer(id, { status }) { const response = await fetch(serverUrl + `appointments/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, + body: JSON.stringify({ status }), }); const data = await response.json(); @@ -103,8 +103,6 @@ export async function updateVisitedOnServer(id) { } export async function updateStatusOnServer(id, newStatus) { - console.log("updateStatus: newStatus", newStatus); - console.log("id", id); const response = await fetch(serverUrl + `appointments/${id}/status-change`, { method: "PATCH", headers: { @@ -120,5 +118,4 @@ export async function updateStatusOnServer(id, newStatus) { } revalidatePath("/admin-dashboard"); - console.log("Revalidation triggered for /admin-dashboard"); } diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 7404f98..45fe37c 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -72,11 +72,11 @@ export default function Grid({ rows, refreshData }) { return ( ); }, @@ -129,7 +129,7 @@ export default function Grid({ rows, refreshData }) { ]; const toggleVisited = async (id) => { - await updateVisitedOnServer(id); + await updateVisitedOnServer(id, { status: "Completed" }); const updatedRows = await fetchAppointments(); setCustomRows(updatedRows); }; diff --git a/client/components/admin_dashboard/ReservationTable.jsx b/client/components/admin_dashboard/ReservationTable.jsx index 07617b8..cbf10c1 100644 --- a/client/components/admin_dashboard/ReservationTable.jsx +++ b/client/components/admin_dashboard/ReservationTable.jsx @@ -10,7 +10,6 @@ export default function ReservationTable({ initAppointments }) { const refreshData = async () => { const updatedAppts = await fetchAppointments(); - console.log("Updated Appointments:", updatedAppts); setAppointments(updatedAppts); }; diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index a52454f..29198b8 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -6,11 +6,8 @@ import { updateStatusOnServer } from "@/actions/form"; export default function StatusChange({ id, refreshData, currentStatus }) { const [status, setStatus] = React.useState(currentStatus); - console.log("StatusChange component rendered for id:", id); - const handleChange = async (e, id) => { const newStatus = e.target.value; - console.log("newStatus", newStatus); setStatus(newStatus); await updateStatusOnServer(id, newStatus); refreshData(); diff --git a/server/src/controllers/appointments.controller.js b/server/src/controllers/appointments.controller.js index 4452006..70f24cb 100644 --- a/server/src/controllers/appointments.controller.js +++ b/server/src/controllers/appointments.controller.js @@ -194,16 +194,19 @@ export async function cancelAppointment(req, res, next) { export async function updateVisited(req, res) { const { id } = req.params; + const { status } = req.body; try { const data = await Appointment.findById(id); - console.log("data", data); + // console.log("data", data); if (!data) { return res.status(404).json({ message: "Could not grab data" }); } - const newStatus = data.status === "Confirmed" ? "Visited" : "Confirmed"; + const newStatus = data.status === "Visited" ? "Completed" : "Visited"; + data.status = newStatus; const visited = await data.save(); + console.log("server: visited", visited); return res.status(200).json(visited); } catch (error) { From 242deafd299053ee52d6c47d992c517f807e3874 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Tue, 17 Dec 2024 16:07:41 -0800 Subject: [PATCH 09/13] add refreshData() to toggleVisited --- client/app/admin-dashboard/page.jsx | 2 ++ client/components/admin_dashboard/Grid.jsx | 21 +++++++++++++++++-- .../admin_dashboard/ReservationTable.jsx | 3 +++ .../admin_dashboard/StatusChange.jsx | 2 +- .../controllers/appointments.controller.js | 9 ++++++-- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/client/app/admin-dashboard/page.jsx b/client/app/admin-dashboard/page.jsx index 5167af4..748235d 100644 --- a/client/app/admin-dashboard/page.jsx +++ b/client/app/admin-dashboard/page.jsx @@ -8,6 +8,8 @@ import { fetchAppointments } from "@/actions/form"; async function Dashboard() { const initAppointments = await fetchAppointments(); + console.log("dashboard", initAppointments); + return ( diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 45fe37c..19bd7cb 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -129,9 +129,26 @@ export default function Grid({ rows, refreshData }) { ]; const toggleVisited = async (id) => { + setCustomRows((customRows) => + customRows.map((row) => + row.id === id + ? { + ...row, + status: + row.status === "Scheduled" + ? "Completed" + : row.status === "Visited" || row.status === "Completed" + ? "Scheduled" + : "Visited", + } + : row + ) + ); + await updateVisitedOnServer(id, { status: "Completed" }); - const updatedRows = await fetchAppointments(); - setCustomRows(updatedRows); + // const updatedRows = await fetchAppointments(); + // setCustomRows(updatedRows); + await refreshData(); }; const filteredRows = useMemo(() => { diff --git a/client/components/admin_dashboard/ReservationTable.jsx b/client/components/admin_dashboard/ReservationTable.jsx index cbf10c1..28b18b5 100644 --- a/client/components/admin_dashboard/ReservationTable.jsx +++ b/client/components/admin_dashboard/ReservationTable.jsx @@ -10,9 +10,12 @@ export default function ReservationTable({ initAppointments }) { const refreshData = async () => { const updatedAppts = await fetchAppointments(); + setAppointments(updatedAppts); }; + console.log("reso table - grid", appointments); + return ( diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index 29198b8..f5ac09d 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -10,7 +10,7 @@ export default function StatusChange({ id, refreshData, currentStatus }) { const newStatus = e.target.value; setStatus(newStatus); await updateStatusOnServer(id, newStatus); - refreshData(); + await refreshData(); }; return ( diff --git a/server/src/controllers/appointments.controller.js b/server/src/controllers/appointments.controller.js index 70f24cb..6c60228 100644 --- a/server/src/controllers/appointments.controller.js +++ b/server/src/controllers/appointments.controller.js @@ -194,7 +194,7 @@ export async function cancelAppointment(req, res, next) { export async function updateVisited(req, res) { const { id } = req.params; - const { status } = req.body; + // const { status } = req.body; try { const data = await Appointment.findById(id); @@ -202,7 +202,12 @@ export async function updateVisited(req, res) { if (!data) { return res.status(404).json({ message: "Could not grab data" }); } - const newStatus = data.status === "Visited" ? "Completed" : "Visited"; + const newStatus = + data.status === "Scheduled" + ? "Completed" + : data.status === "Visited" || data.status === "Completed" + ? "Scheduled" + : "Visited"; data.status = newStatus; const visited = await data.save(); From 29d6d49fb05f831f31f6d77c9945d13827c5ccc1 Mon Sep 17 00:00:00 2001 From: Chris Daly Date: Tue, 17 Dec 2024 17:10:55 -0800 Subject: [PATCH 10/13] Remove unneeded states Resolves bugs with grid not updating --- client/actions/form.js | 16 --------- client/components/admin_dashboard/Grid.jsx | 35 +++++-------------- .../admin_dashboard/StatusChange.jsx | 8 ++--- 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/client/actions/form.js b/client/actions/form.js index cae1a9f..00c82b5 100644 --- a/client/actions/form.js +++ b/client/actions/form.js @@ -86,22 +86,6 @@ export async function cancelAppointment(email) { revalidatePath("/my-appointments"); } -export async function updateVisitedOnServer(id, { status }) { - const response = await fetch(serverUrl + `appointments/${id}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ status }), - }); - - const data = await response.json(); - - if (!response.ok) { - return { message: data.message }; - } -} - export async function updateStatusOnServer(id, newStatus) { const response = await fetch(serverUrl + `appointments/${id}/status-change`, { method: "PATCH", diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 19bd7cb..5b5b160 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -10,7 +10,7 @@ import { Paper, Box, Tooltip } from "@mui/material"; import Button from "@mui/material/Button"; import SearchBar from "./SearchBar"; import StatusChange from "./StatusChange"; -import { fetchAppointments, updateVisitedOnServer } from "@/actions/form"; +import { updateStatusOnServer } from "@/actions/form"; const formatName = (name) => { const [firstName, ...rest] = name.split(" "); @@ -54,7 +54,6 @@ const Toolbar = () => ( export default function Grid({ rows, refreshData }) { const [searchText, setSearchText] = useState(""); - const [customRows, setCustomRows] = useState(rows); const columns = [ { @@ -72,11 +71,11 @@ export default function Grid({ rows, refreshData }) { return ( ); }, @@ -128,31 +127,15 @@ export default function Grid({ rows, refreshData }) { }, ]; - const toggleVisited = async (id) => { - setCustomRows((customRows) => - customRows.map((row) => - row.id === id - ? { - ...row, - status: - row.status === "Scheduled" - ? "Completed" - : row.status === "Visited" || row.status === "Completed" - ? "Scheduled" - : "Visited", - } - : row - ) - ); - - await updateVisitedOnServer(id, { status: "Completed" }); + const toggleVisited = async (id, status) => { // const updatedRows = await fetchAppointments(); // setCustomRows(updatedRows); + await updateStatusOnServer(id, status); await refreshData(); }; const filteredRows = useMemo(() => { - if (!searchText) return customRows; + if (!searchText) return rows; const needToFormat = { dateCreated: (value) => formatDateCreated(new Date(value)), @@ -161,7 +144,7 @@ export default function Grid({ rows, refreshData }) { row.markVisited ? "Visited" : "Need to Visit", }; - return customRows.filter((row) => + return rows.filter((row) => columns.some((col) => { const value = row[col.field]; const formattedValue = needToFormat[col.field] @@ -173,7 +156,7 @@ export default function Grid({ rows, refreshData }) { .includes(searchText.toLowerCase()); }) ); - }, [searchText, customRows]); + }, [searchText, rows]); const onSearchChange = (value) => { setSearchText(value); diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index f5ac09d..a29cd5b 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -4,8 +4,6 @@ import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material"; import { updateStatusOnServer } from "@/actions/form"; export default function StatusChange({ id, refreshData, currentStatus }) { - const [status, setStatus] = React.useState(currentStatus); - const handleChange = async (e, id) => { const newStatus = e.target.value; setStatus(newStatus); @@ -19,15 +17,15 @@ export default function StatusChange({ id, refreshData, currentStatus }) { From 4a581ebdbd2f229262ba56396cb58dde297e86fb Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Wed, 18 Dec 2024 23:59:29 -0800 Subject: [PATCH 11/13] change Visited to Completed on StatusChange --- client/components/admin_dashboard/Grid.jsx | 13 ++++++++++--- client/components/admin_dashboard/StatusChange.jsx | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index 5b5b160..d0be540 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -71,11 +71,18 @@ export default function Grid({ rows, refreshData }) { return ( ); }, diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index a29cd5b..c2e8613 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -24,7 +24,7 @@ export default function StatusChange({ id, refreshData, currentStatus }) { Requested Confirmed Scheduled - Visited + Completed Cancelled From 8761f074c7a29dcee763619a372abcb56a9e6521 Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Thu, 19 Dec 2024 20:01:58 -0800 Subject: [PATCH 12/13] fix and clean up functions and model so status updates correctly on Visited and Status columns --- client/components/admin_dashboard/Grid.jsx | 14 ++++++---- .../admin_dashboard/StatusChange.jsx | 1 - .../controllers/appointments.controller.js | 27 ------------------- server/src/models/appointments.model.js | 2 +- server/src/routes/appointments.routes.js | 4 --- 5 files changed, 10 insertions(+), 38 deletions(-) diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index d0be540..aac2ce3 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -72,17 +72,23 @@ export default function Grid({ rows, refreshData }) { return ( ); }, @@ -135,8 +141,6 @@ export default function Grid({ rows, refreshData }) { ]; const toggleVisited = async (id, status) => { - // const updatedRows = await fetchAppointments(); - // setCustomRows(updatedRows); await updateStatusOnServer(id, status); await refreshData(); }; diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/StatusChange.jsx index c2e8613..2830e93 100644 --- a/client/components/admin_dashboard/StatusChange.jsx +++ b/client/components/admin_dashboard/StatusChange.jsx @@ -6,7 +6,6 @@ import { updateStatusOnServer } from "@/actions/form"; export default function StatusChange({ id, refreshData, currentStatus }) { const handleChange = async (e, id) => { const newStatus = e.target.value; - setStatus(newStatus); await updateStatusOnServer(id, newStatus); await refreshData(); }; diff --git a/server/src/controllers/appointments.controller.js b/server/src/controllers/appointments.controller.js index 6c60228..b50f81d 100644 --- a/server/src/controllers/appointments.controller.js +++ b/server/src/controllers/appointments.controller.js @@ -191,30 +191,3 @@ export async function cancelAppointment(req, res, next) { return next({ message: "An internal server error occurred" }); } } - -export async function updateVisited(req, res) { - const { id } = req.params; - // const { status } = req.body; - - try { - const data = await Appointment.findById(id); - // console.log("data", data); - if (!data) { - return res.status(404).json({ message: "Could not grab data" }); - } - const newStatus = - data.status === "Scheduled" - ? "Completed" - : data.status === "Visited" || data.status === "Completed" - ? "Scheduled" - : "Visited"; - - data.status = newStatus; - const visited = await data.save(); - - console.log("server: visited", visited); - return res.status(200).json(visited); - } catch (error) { - return res.status(500).json({ message: "Server error: updating status" }); - } -} diff --git a/server/src/models/appointments.model.js b/server/src/models/appointments.model.js index ae17c97..d3916b4 100644 --- a/server/src/models/appointments.model.js +++ b/server/src/models/appointments.model.js @@ -16,7 +16,7 @@ const appointmentSchema = new mongoose.Schema({ }, status: { type: String, - enum: ["Pending", "Confirmed", "Scheduled", "Cancelled", "Visited"], + enum: ["Requested", "Confirmed", "Scheduled", "Completed", "Cancelled"], default: "Pending", }, notifications: { diff --git a/server/src/routes/appointments.routes.js b/server/src/routes/appointments.routes.js index 36a7848..57c5d99 100644 --- a/server/src/routes/appointments.routes.js +++ b/server/src/routes/appointments.routes.js @@ -4,7 +4,6 @@ import { getSingleAppointment, getUsersAppointments, newAppointment, - updateVisited, cancelAppointment, } from "../controllers/appointments.controller.js"; import { updateStatus } from "../scheduling/scheduler.js"; @@ -26,9 +25,6 @@ router.get("/:email/all", getUsersAppointments); // PATCH "/appointments/cancel" router.patch("/cancel", cancelAppointment); -// PATCH "/appointments/:id" -router.patch("/:id", updateVisited); - // PATCH "/appointments/:id/status-change" router.patch("/:id/status-change", updateStatus); From 86504c92bd42411102c5c00d6b504adeb60e24cf Mon Sep 17 00:00:00 2001 From: Kristi Hwang Date: Thu, 19 Dec 2024 21:30:19 -0800 Subject: [PATCH 13/13] break down Grid into smaller components --- client/components/admin_dashboard/Grid.jsx | 125 +----------------- .../{ => grid-components}/StatusChange.jsx | 0 .../grid-components/VisitedChip.jsx | 17 +++ .../grid-components/columns.jsx | 74 +++++++++++ .../grid-components/formatters.jsx | 31 +++++ 5 files changed, 128 insertions(+), 119 deletions(-) rename client/components/admin_dashboard/{ => grid-components}/StatusChange.jsx (100%) create mode 100644 client/components/admin_dashboard/grid-components/VisitedChip.jsx create mode 100644 client/components/admin_dashboard/grid-components/columns.jsx create mode 100644 client/components/admin_dashboard/grid-components/formatters.jsx diff --git a/client/components/admin_dashboard/Grid.jsx b/client/components/admin_dashboard/Grid.jsx index aac2ce3..0e5b054 100644 --- a/client/components/admin_dashboard/Grid.jsx +++ b/client/components/admin_dashboard/Grid.jsx @@ -7,43 +7,10 @@ import { GridToolbarContainer, } from "@mui/x-data-grid"; import { Paper, Box, Tooltip } from "@mui/material"; -import Button from "@mui/material/Button"; import SearchBar from "./SearchBar"; -import StatusChange from "./StatusChange"; +import { getColumns } from "./grid-components/columns"; import { updateStatusOnServer } from "@/actions/form"; -const formatName = (name) => { - const [firstName, ...rest] = name.split(" "); - const lastName = rest.join(" "); - if (!lastName) { - return firstName; - } - return `${lastName}, ${firstName}`; -}; - -const formatTime = (hour) => { - return hour <= 12 ? `${hour}a` : `${hour - 12}p`; -}; - -const formatTimeRange = (timeRange) => { - return `${formatTime(timeRange?.preferredEarlyTime)} - ${formatTime( - timeRange?.preferredLateTime - )}`; -}; - -const formatDateCreated = (date) => { - return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })}`; -}; - -const formatPhone = (phone) => { - const cleaned = ("" + phone).replace(/\D/g, ""); - const match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/); - return match ? `(${match[1]}) ${match[2]}-${match[3]}` : phone; -}; - const paginationModel = { page: 0, pageSize: 15 }; const Toolbar = () => ( @@ -55,96 +22,16 @@ const Toolbar = () => ( export default function Grid({ rows, refreshData }) { const [searchText, setSearchText] = useState(""); - const columns = [ - { - field: "visitOrder", - headerName: "Visit Order", - width: 190, - valueGetter: (_, row) => row.schedule.order, - }, - { - field: "markVisited", - headerName: "Visited?", - width: 190, - renderCell: (params) => { - const { id, status } = params.row; - - return ( - - ); - }, - }, - { - field: "status", - headerName: "Status", - width: 190, - renderCell: (params) => { - const { id, status } = params.row; - return ( - - ); - }, - }, - { - valueFormatter: formatName, - field: "name", - headerName: "Name", - width: 190, - }, - { - valueFormatter: formatDateCreated, - field: "dateCreated", - headerName: "Requested on", - width: 190, - }, - { - valueFormatter: formatTimeRange, - field: "timeRange", - headerName: "Timeslot", - width: 190, - }, - { - valueFormatter: formatPhone, - field: "phone", - headerName: "Phone", - width: 190, - }, - { field: "email", headerName: "Email", width: 190 }, - { - field: "address", - headerName: "Address", - width: 190, - }, - ]; - const toggleVisited = async (id, status) => { await updateStatusOnServer(id, status); await refreshData(); }; + const columns = useMemo( + () => getColumns(refreshData, toggleVisited), + [refreshData] + ); + const filteredRows = useMemo(() => { if (!searchText) return rows; diff --git a/client/components/admin_dashboard/StatusChange.jsx b/client/components/admin_dashboard/grid-components/StatusChange.jsx similarity index 100% rename from client/components/admin_dashboard/StatusChange.jsx rename to client/components/admin_dashboard/grid-components/StatusChange.jsx diff --git a/client/components/admin_dashboard/grid-components/VisitedChip.jsx b/client/components/admin_dashboard/grid-components/VisitedChip.jsx new file mode 100644 index 0000000..cc71689 --- /dev/null +++ b/client/components/admin_dashboard/grid-components/VisitedChip.jsx @@ -0,0 +1,17 @@ +import { Button } from "@mui/material"; + +const VisitedChip = ({ id, status, toggleVisited }) => { + const visited = status === "Visited" || status === "Completed"; + + return ( + + ); +}; + +export default VisitedChip; diff --git a/client/components/admin_dashboard/grid-components/columns.jsx b/client/components/admin_dashboard/grid-components/columns.jsx new file mode 100644 index 0000000..c98079d --- /dev/null +++ b/client/components/admin_dashboard/grid-components/columns.jsx @@ -0,0 +1,74 @@ +import VisitedChip from "./VisitedChip"; +import StatusChange from "./StatusChange"; +import { + formatName, + formatDateCreated, + formatTimeRange, + formatPhone, +} from "./formatters"; + +export const getColumns = (refreshData, toggleVisited) => [ + { + field: "visitOrder", + headerName: "Visit Order", + width: 190, + valueGetter: (_, row) => row.schedule.order, + }, + { + field: "markVisited", + headerName: "Visited?", + width: 190, + renderCell: (params) => ( + + ), + }, + { + field: "status", + headerName: "Status", + width: 190, + renderCell: (params) => { + const { id, status } = params.row; + return ( + + ); + }, + }, + { + valueFormatter: formatName, + field: "name", + headerName: "Name", + width: 190, + }, + { + valueFormatter: formatDateCreated, + field: "dateCreated", + headerName: "Requested on", + width: 190, + }, + { + valueFormatter: formatTimeRange, + field: "timeRange", + headerName: "Timeslot", + width: 190, + }, + { + valueFormatter: formatPhone, + field: "phone", + headerName: "Phone", + width: 190, + }, + { field: "email", headerName: "Email", width: 190 }, + { + field: "address", + headerName: "Address", + width: 190, + }, +]; diff --git a/client/components/admin_dashboard/grid-components/formatters.jsx b/client/components/admin_dashboard/grid-components/formatters.jsx new file mode 100644 index 0000000..67293e6 --- /dev/null +++ b/client/components/admin_dashboard/grid-components/formatters.jsx @@ -0,0 +1,31 @@ +export const formatName = (name) => { + const [firstName, ...rest] = name.split(" "); + const lastName = rest.join(" "); + if (!lastName) { + return firstName; + } + return `${lastName}, ${firstName}`; +}; + +export const formatTime = (hour) => { + return hour <= 12 ? `${hour}a` : `${hour - 12}p`; +}; + +export const formatTimeRange = (timeRange) => { + return `${formatTime(timeRange?.preferredEarlyTime)} - ${formatTime( + timeRange?.preferredLateTime + )}`; +}; + +export const formatDateCreated = (date) => { + return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })}`; +}; + +export const formatPhone = (phone) => { + const cleaned = ("" + phone).replace(/\D/g, ""); + const match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/); + return match ? `(${match[1]}) ${match[2]}-${match[3]}` : phone; +};