Skip to content

Commit

Permalink
Merge pull request #87 from chingu-voyages/admin_dashboard
Browse files Browse the repository at this point in the history
Admin dashboard
kristi-h authored Dec 23, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents f0b4b99 + 86504c9 commit 7c8987f
Showing 13 changed files with 228 additions and 182 deletions.
23 changes: 5 additions & 18 deletions client/actions/form.js
Original file line number Diff line number Diff line change
@@ -86,33 +86,20 @@ export async function cancelAppointment(email) {
revalidatePath("/my-appointments");
}

export async function updateVisitedOnServer(id) {
const response = await fetch(serverUrl + `appointments/${id}`, {
export async function updateStatusOnServer(id, newStatus) {
const response = await fetch(serverUrl + `appointments/${id}/status-change`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ newStatus }),
});

const data = await response.json();

if (!response.ok) {
return { message: data.message };
}
}

// 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 };
// }
// }
revalidatePath("/admin-dashboard");
}
9 changes: 6 additions & 3 deletions client/app/admin-dashboard/page.jsx
Original file line number Diff line number Diff line change
@@ -6,14 +6,17 @@ import Spinner from "@/components/Spinner";
import { fetchAppointments } from "@/actions/form";

async function Dashboard() {
const appointments = await fetchAppointments();
const initAppointments = await fetchAppointments();

console.log("dashboard", initAppointments);

return (
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<Box sx={{ flex: 1, height: "50vh" }}>
<ReservationTable appointments={appointments} />
<ReservationTable initAppointments={initAppointments} />
</Box>
<Box sx={{ flex: 1, height: "50vh" }}>
<Map appointments={appointments} />
<Map initAppointments={initAppointments} />
</Box>
</Box>
);
121 changes: 14 additions & 107 deletions client/components/admin_dashboard/Grid.jsx
Original file line number Diff line number Diff line change
@@ -7,42 +7,9 @@ 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 { fetchAppointments, updateVisitedOnServer } 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;
};
import { getColumns } from "./grid-components/columns";
import { updateStatusOnServer } from "@/actions/form";

const paginationModel = { page: 0, pageSize: 15 };

@@ -52,81 +19,21 @@ const Toolbar = () => (
</GridToolbarContainer>
);

export default function Grid({ rows }) {
export default function Grid({ rows, refreshData }) {
const [searchText, setSearchText] = useState("");
const [customRows, setCustomRows] = useState(rows);

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 (
<Button
variant={status === "Visited" ? "contained" : "outlined"}
color="primary"
onClick={() => toggleVisited(id)}
>
{status === "Visited" ? "Visited" : "Not Visited"}
</Button>
);
},
},
{
field: "status",
headerName: "Status",
width: 190,
renderCell: (status) => <StatusChange />,
},
{
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) => {
await updateVisitedOnServer(id);
const updatedRows = await fetchAppointments();
setCustomRows(updatedRows);
const toggleVisited = async (id, status) => {
await updateStatusOnServer(id, status);
await refreshData();
};

const columns = useMemo(
() => getColumns(refreshData, toggleVisited),
[refreshData]
);

const filteredRows = useMemo(() => {
if (!searchText) return customRows;
if (!searchText) return rows;

const needToFormat = {
dateCreated: (value) => formatDateCreated(new Date(value)),
@@ -135,7 +42,7 @@ export default function Grid({ rows }) {
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]
@@ -147,7 +54,7 @@ export default function Grid({ rows }) {
.includes(searchText.toLowerCase());
})
);
}, [searchText, customRows]);
}, [searchText, rows]);

const onSearchChange = (value) => {
setSearchText(value);
7 changes: 3 additions & 4 deletions client/components/admin_dashboard/Map.jsx
Original file line number Diff line number Diff line change
@@ -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 <p>No map data available</p>;
}

@@ -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,
@@ -40,7 +40,6 @@ export default function Map({ appointments }) {
});
});
};
console.log(appointments[0]);

return (
<LoadScript
18 changes: 16 additions & 2 deletions client/components/admin_dashboard/ReservationTable.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
"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);
};

console.log("reso table - grid", appointments);

export default function ReservationTable({ appointments }) {
return (
<Box sx={{ height: "100%", width: "100%" }}>
<Grid rows={appointments} />
<Grid rows={appointments} refreshData={refreshData} />
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"use client";
import React from "react";
import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material";
import { updateStatusOnServer } from "@/actions/form";

export default function StatusChange() {
const [status, setStatus] = React.useState("Requested");

const handleChange = (e) => {
setStatus(e.target.value);
export default function StatusChange({ id, refreshData, currentStatus }) {
const handleChange = async (e, id) => {
const newStatus = e.target.value;
await updateStatusOnServer(id, newStatus);
await refreshData();
};
return (
<Box sx={{ minWidth: 120 }}>
@@ -14,15 +16,15 @@ export default function StatusChange() {
<Select
labelId="status-label"
id="status"
value={status}
value={currentStatus}
label="Status"
onChange={handleChange}
onChange={(e) => handleChange(e, id)}
>
<MenuItem value={"Requested"}>Requested</MenuItem>
<MenuItem value={"Confirmed"}>Confirmed</MenuItem>
<MenuItem value={"Scheduled"}>Scheduled</MenuItem>
<MenuItem value={"Completed"}>Completed</MenuItem>
<MenuItem value={"Canceled"}>Canceled</MenuItem>
<MenuItem value={"Cancelled"}>Cancelled</MenuItem>
</Select>
</FormControl>
</Box>
17 changes: 17 additions & 0 deletions client/components/admin_dashboard/grid-components/VisitedChip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Button } from "@mui/material";

const VisitedChip = ({ id, status, toggleVisited }) => {
const visited = status === "Visited" || status === "Completed";

return (
<Button
variant={visited ? "contained" : "outlined"}
color="primary"
onClick={() => toggleVisited(id, visited ? "Scheduled" : "Completed")}
>
{visited ? "Visited" : "Not Visited"}
</Button>
);
};

export default VisitedChip;
Loading

0 comments on commit 7c8987f

Please sign in to comment.