From e503484ee92def8e06289b8d3383e564ab87a79c Mon Sep 17 00:00:00 2001 From: jamesspearsv <58987727+jamesspearsv@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:33:54 -0500 Subject: [PATCH 01/16] Add admin reporting route to admin subapp. Signed-off-by: jamesspearsv <58987727+jamesspearsv@users.noreply.github.com> --- client/src/Admin.jsx | 1 + client/src/pages-admin/Reporting/AdminReporting.jsx | 12 ++++++++++++ client/src/pages-admin/Reporting/ReportingParams.jsx | 5 +++++ client/src/routes.jsx | 2 ++ 4 files changed, 20 insertions(+) create mode 100644 client/src/pages-admin/Reporting/AdminReporting.jsx create mode 100644 client/src/pages-admin/Reporting/ReportingParams.jsx diff --git a/client/src/Admin.jsx b/client/src/Admin.jsx index 3eec2ab..219a6cf 100644 --- a/client/src/Admin.jsx +++ b/client/src/Admin.jsx @@ -60,6 +60,7 @@ function Admin() { { label: "Back to App", route: "/" }, { label: "Dashboard", route: "/admin" }, { label: "Database", route: "/admin/database" }, + { label: "Reporting", route: "/admin/reporting" }, ]} /> {auth && ( diff --git a/client/src/pages-admin/Reporting/AdminReporting.jsx b/client/src/pages-admin/Reporting/AdminReporting.jsx new file mode 100644 index 0000000..e6b3025 --- /dev/null +++ b/client/src/pages-admin/Reporting/AdminReporting.jsx @@ -0,0 +1,12 @@ +import ReportingParams from "./ReportingParams.jsx"; + +function AdminReporting() { + return ( +
+ +

Admin Reporting

+
+ ); +} + +export default AdminReporting; diff --git a/client/src/pages-admin/Reporting/ReportingParams.jsx b/client/src/pages-admin/Reporting/ReportingParams.jsx new file mode 100644 index 0000000..dbe4eeb --- /dev/null +++ b/client/src/pages-admin/Reporting/ReportingParams.jsx @@ -0,0 +1,5 @@ +function ReportingParams() { + return "Component"; +} + +export default ReportingParams; diff --git a/client/src/routes.jsx b/client/src/routes.jsx index 591ac77..8f0a492 100644 --- a/client/src/routes.jsx +++ b/client/src/routes.jsx @@ -7,6 +7,7 @@ import Admin from "./Admin.jsx"; import Login from "./pages-admin/Login.jsx"; import Dashboard from "./pages-admin/Dashboard/Dashboard.jsx"; import Database from "./pages-admin/Database/Database.jsx"; +import AdminReporting from "./pages-admin/Reporting/AdminReporting.jsx"; const routes = [ { @@ -26,6 +27,7 @@ const routes = [ { index: true, element: }, { path: "login", element: }, { path: "database", element: }, + { path: "reporting", element: }, ], }, ]; From 8c9f9e1804646ed757d610cbfa96469ae21e7862 Mon Sep 17 00:00:00 2001 From: jamesspearsv <58987727+jamesspearsv@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:18:43 -0500 Subject: [PATCH 02/16] Build basic reporting params form Signed-off-by: jamesspearsv <58987727+jamesspearsv@users.noreply.github.com> --- client/src/components/SelectInput.jsx | 7 +- .../pages-admin/Reporting/AdminReporting.jsx | 12 ---- .../src/pages-admin/Reporting/Reporting.jsx | 23 +++++++ .../pages-admin/Reporting/ReportingParams.jsx | 66 ++++++++++++++++++- client/src/routes.jsx | 4 +- http-requests/admin.http | 2 +- 6 files changed, 97 insertions(+), 17 deletions(-) delete mode 100644 client/src/pages-admin/Reporting/AdminReporting.jsx create mode 100644 client/src/pages-admin/Reporting/Reporting.jsx diff --git a/client/src/components/SelectInput.jsx b/client/src/components/SelectInput.jsx index 10e5db8..f69b720 100644 --- a/client/src/components/SelectInput.jsx +++ b/client/src/components/SelectInput.jsx @@ -30,7 +30,12 @@ function SelectInput({ label, options, handleChange, value }) { SelectInput.propTypes = { label: PropTypes.string.isRequired, - options: PropTypes.array.isRequired, + options: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + }), + ).isRequired, handleChange: PropTypes.func.isRequired, value: PropTypes.string.isRequired, }; diff --git a/client/src/pages-admin/Reporting/AdminReporting.jsx b/client/src/pages-admin/Reporting/AdminReporting.jsx deleted file mode 100644 index e6b3025..0000000 --- a/client/src/pages-admin/Reporting/AdminReporting.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import ReportingParams from "./ReportingParams.jsx"; - -function AdminReporting() { - return ( -
- -

Admin Reporting

-
- ); -} - -export default AdminReporting; diff --git a/client/src/pages-admin/Reporting/Reporting.jsx b/client/src/pages-admin/Reporting/Reporting.jsx new file mode 100644 index 0000000..2e98e0f --- /dev/null +++ b/client/src/pages-admin/Reporting/Reporting.jsx @@ -0,0 +1,23 @@ +import ReportingParams from "./ReportingParams.jsx"; +import { Navigate, useOutletContext } from "react-router-dom"; + +function Reporting() { + const { auth } = useOutletContext(); + + if (!auth) return ; + + return ( +
+ +
+ ); +} + +export default Reporting; diff --git a/client/src/pages-admin/Reporting/ReportingParams.jsx b/client/src/pages-admin/Reporting/ReportingParams.jsx index dbe4eeb..52a41fd 100644 --- a/client/src/pages-admin/Reporting/ReportingParams.jsx +++ b/client/src/pages-admin/Reporting/ReportingParams.jsx @@ -1,5 +1,69 @@ +import PropTypes from "prop-types"; +import CardWrapper from "../../components/CardWrapper.jsx"; +import Form from "../../components/Form.jsx"; +import SelectInput from "../../components/SelectInput.jsx"; +import { useState } from "react"; + +const categories = [ + { id: "type", value: "Types" }, + { id: "format", value: "Formats" }, + { id: "location", value: "Locations" }, +]; + function ReportingParams() { - return "Component"; + // todo : raise state to parent component + const [params, setParams] = useState({ + startMonth: null, + endMonth: null, + category: "", + }); + + function handleCagetoryChange(e) { + setParams((params) => ({ ...params, category: e.target.value })); + } + + // todo: finish updater function + function handleMonthChange(e) { + return; + } + + return ( +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ ); } export default ReportingParams; diff --git a/client/src/routes.jsx b/client/src/routes.jsx index 8f0a492..539105f 100644 --- a/client/src/routes.jsx +++ b/client/src/routes.jsx @@ -7,7 +7,7 @@ import Admin from "./Admin.jsx"; import Login from "./pages-admin/Login.jsx"; import Dashboard from "./pages-admin/Dashboard/Dashboard.jsx"; import Database from "./pages-admin/Database/Database.jsx"; -import AdminReporting from "./pages-admin/Reporting/AdminReporting.jsx"; +import Reporting from "./pages-admin/Reporting/Reporting.jsx"; const routes = [ { @@ -27,7 +27,7 @@ const routes = [ { index: true, element: }, { path: "login", element: }, { path: "database", element: }, - { path: "reporting", element: }, + { path: "reporting", element: }, ], }, ]; diff --git a/http-requests/admin.http b/http-requests/admin.http index 975a30d..7718cfe 100644 --- a/http-requests/admin.http +++ b/http-requests/admin.http @@ -29,5 +29,5 @@ GET http://localhost:3001/admin/count/interactions Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.Mzc3MWRlYTkwYTc4NjllNzdjMTc.z8ZJjS4aSbhn6NnoW65WIWZWRzxxVUsIUEzwInvHnew ### Admin report -GET http://localhost:3001/admin/report?start=2024-11&end=2024-12&category=locations +GET http://localhost:3001/admin/report?start=2024-11&end=2024-12&category=location Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.Mzc3MWRlYTkwYTc4NjllNzdjMTc.z8ZJjS4aSbhn6NnoW65WIWZWRzxxVUsIUEzwInvHnew From bfbbe809c2ceae732c6485b2c0d2dd635aaf00d3 Mon Sep 17 00:00:00 2001 From: James Spears Date: Wed, 11 Dec 2024 07:48:03 -0500 Subject: [PATCH 03/16] Complete basic admin reporting state management Signed-off-by: James Spears --- api/src/controllers/adminController.js | 15 ++++--- .../pages-admin/Dashboard/DashboardTable.jsx | 1 - .../src/pages-admin/Reporting/Reporting.jsx | 25 +++++++++++- .../pages-admin/Reporting/ReportingParams.jsx | 39 ++++++++++--------- http-requests/admin.http | 2 +- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/api/src/controllers/adminController.js b/api/src/controllers/adminController.js index 9d4cd0a..050a0bd 100644 --- a/api/src/controllers/adminController.js +++ b/api/src/controllers/adminController.js @@ -127,8 +127,8 @@ async function countTable(req, res, next) { async function adminReportGet(req, res, next) { try { - const { start, end, category } = req.query; - if (!start || !end || !category) { + const { startMonth, endMonth, category } = req.query; + if (!startMonth || !endMonth || !category) { throw new BadRequestError("Start, end, and category must be provided"); } @@ -137,15 +137,18 @@ async function adminReportGet(req, res, next) { } // query database for cumulative interactions counts during range - const total_interactions = await queries.countInteractionsAdmin(start, end); + const total_interactions = await queries.countInteractionsAdmin( + startMonth, + endMonth, + ); const total_detailed = await queries.countInteractionByCategoryAdmin( - start, - end, + startMonth, + endMonth, category, ); // parse range between start and end month - const range = parseMonthRange(start, end); + const range = parseMonthRange(startMonth, endMonth); const monthly_details = []; // query database for each month in range for (const month of range) { diff --git a/client/src/pages-admin/Dashboard/DashboardTable.jsx b/client/src/pages-admin/Dashboard/DashboardTable.jsx index 718d1d1..c73639c 100644 --- a/client/src/pages-admin/Dashboard/DashboardTable.jsx +++ b/client/src/pages-admin/Dashboard/DashboardTable.jsx @@ -35,7 +35,6 @@ function DashboardTable() { validateAdminResponse(res, json, setAuth); const total = Math.ceil(json.total_rows / limit); - console.log(total); setTotalPages(total); } catch (error) { console.error(error); diff --git a/client/src/pages-admin/Reporting/Reporting.jsx b/client/src/pages-admin/Reporting/Reporting.jsx index 2e98e0f..49f6a4a 100644 --- a/client/src/pages-admin/Reporting/Reporting.jsx +++ b/client/src/pages-admin/Reporting/Reporting.jsx @@ -1,8 +1,26 @@ import ReportingParams from "./ReportingParams.jsx"; import { Navigate, useOutletContext } from "react-router-dom"; +import { useState } from "react"; + +const defaultParams = { + startMonth: "", + endMonth: "", + category: "", +}; function Reporting() { const { auth } = useOutletContext(); + const [params, setParams] = useState(defaultParams); + + function updateParams(param, value) { + setParams((prevParams) => { + return { ...prevParams, [param]: value }; + }); + } + + function resetParams() { + setParams(defaultParams); + } if (!auth) return ; @@ -15,7 +33,12 @@ function Reporting() { alignItems: "center", }} > - + +
+

startMonth: {params.startMonth}

+

endMonth: {params.endMonth}

+

category: {params.category}

+
); } diff --git a/client/src/pages-admin/Reporting/ReportingParams.jsx b/client/src/pages-admin/Reporting/ReportingParams.jsx index 52a41fd..5bd46a8 100644 --- a/client/src/pages-admin/Reporting/ReportingParams.jsx +++ b/client/src/pages-admin/Reporting/ReportingParams.jsx @@ -1,8 +1,6 @@ import PropTypes from "prop-types"; -import CardWrapper from "../../components/CardWrapper.jsx"; import Form from "../../components/Form.jsx"; import SelectInput from "../../components/SelectInput.jsx"; -import { useState } from "react"; const categories = [ { id: "type", value: "Types" }, @@ -10,21 +8,15 @@ const categories = [ { id: "location", value: "Locations" }, ]; -function ReportingParams() { - // todo : raise state to parent component - const [params, setParams] = useState({ - startMonth: null, - endMonth: null, - category: "", - }); +function ReportingParams({ params, updateParams }) { + function handleFormChange(e) { + console.log("id", e.target.id); + console.log("value", e.target.value); - function handleCagetoryChange(e) { - setParams((params) => ({ ...params, category: e.target.value })); - } + const param = e.target.id; + const value = e.target.value; - // todo: finish updater function - function handleMonthChange(e) { - return; + updateParams(param, value); } return ( @@ -36,10 +28,11 @@ function ReportingParams() { Starting Month
@@ -47,17 +40,18 @@ function ReportingParams() { Ending Month
@@ -66,4 +60,13 @@ function ReportingParams() { ); } +ReportingParams.propTypes = { + params: PropTypes.shape({ + startMonth: PropTypes.string.isRequired, + endMonth: PropTypes.string.isRequired, + category: PropTypes.string.isRequired, + }), + updateParams: PropTypes.func.isRequired, +}; + export default ReportingParams; diff --git a/http-requests/admin.http b/http-requests/admin.http index 7718cfe..3d8f232 100644 --- a/http-requests/admin.http +++ b/http-requests/admin.http @@ -29,5 +29,5 @@ GET http://localhost:3001/admin/count/interactions Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.Mzc3MWRlYTkwYTc4NjllNzdjMTc.z8ZJjS4aSbhn6NnoW65WIWZWRzxxVUsIUEzwInvHnew ### Admin report -GET http://localhost:3001/admin/report?start=2024-11&end=2024-12&category=location +GET http://localhost:3001/admin/report?startMonth=2024-11&endMonth=2024-12&category=location Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.Mzc3MWRlYTkwYTc4NjllNzdjMTc.z8ZJjS4aSbhn6NnoW65WIWZWRzxxVUsIUEzwInvHnew From a891fed111b420cb347934b4dc9842b0a89efa80 Mon Sep 17 00:00:00 2001 From: James Spears Date: Wed, 11 Dec 2024 09:18:47 -0500 Subject: [PATCH 04/16] Set up ReportingDisplay.jsx and add data fetching effect Signed-off-by: James Spears --- .../src/pages-admin/Reporting/Reporting.jsx | 2 + .../Reporting/ReportingDisplay.jsx | 79 +++++++++++++++++++ .../pages-admin/Reporting/ReportingParams.jsx | 6 +- 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 client/src/pages-admin/Reporting/ReportingDisplay.jsx diff --git a/client/src/pages-admin/Reporting/Reporting.jsx b/client/src/pages-admin/Reporting/Reporting.jsx index 49f6a4a..b098f9a 100644 --- a/client/src/pages-admin/Reporting/Reporting.jsx +++ b/client/src/pages-admin/Reporting/Reporting.jsx @@ -1,6 +1,7 @@ import ReportingParams from "./ReportingParams.jsx"; import { Navigate, useOutletContext } from "react-router-dom"; import { useState } from "react"; +import ReportingDisplay from "./ReportingDisplay.jsx"; const defaultParams = { startMonth: "", @@ -39,6 +40,7 @@ function Reporting() {

endMonth: {params.endMonth}

category: {params.category}

+ ); } diff --git a/client/src/pages-admin/Reporting/ReportingDisplay.jsx b/client/src/pages-admin/Reporting/ReportingDisplay.jsx new file mode 100644 index 0000000..d3d3eb0 --- /dev/null +++ b/client/src/pages-admin/Reporting/ReportingDisplay.jsx @@ -0,0 +1,79 @@ +import PropTypes from "prop-types"; +import { useState, useEffect } from "react"; +import { useOutletContext } from "react-router-dom"; +import { validateAdminResponse } from "../../lib/response.js"; +import { toast } from "react-hot-toast"; + +function ReportingDisplay({ params }) { + const { auth, apihost, setAuth } = useOutletContext(); + const [report, setReport] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const regex = /[0-9]{4}-[0-9]{2}/; + + useEffect(() => { + // check that params form is complete + const { startMonth, endMonth, category } = params; + + // todo : write matching function to validate month inputs in unsupported browsers + const match = startMonth.match(regex); + if (!match || match[0] !== startMonth) console.error("no match"); + console.log(match); + + if (!startMonth || !endMonth || !category) return; + + // perform async fetch operation + (async () => { + try { + // define fetch url and options + const url = `${apihost}/admin/report?startMonth=${startMonth}&endMonth=${endMonth}&category=${category}`; + const options = { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `${auth.token_type} ${auth.token}`, + }, + }; + + const res = await fetch(url, options); + const json = await res.json(); + + // validate API response + validateAdminResponse(res, json, setAuth); + + setReport(json); + setLoading(false); + setError(null); + } catch (error) { + console.error(error); + toast.error(error.message); + setLoading(false); + setError({ message: error.message }); + } + + // clean up state + return () => { + setReport(null); + setLoading(true); + setError(null); + }; + })(); + }, [params]); + + // if params are incomplete or error has occurred + if (loading) return
Set your report parameters above
; + if (error) return
Error loading report -- {error.message}
; + + return "Reporting!"; +} + +ReportingDisplay.propTypes = { + params: PropTypes.shape({ + startMonth: PropTypes.string.isRequired, + endMonth: PropTypes.string.isRequired, + category: PropTypes.string.isRequired, + }), +}; + +export default ReportingDisplay; diff --git a/client/src/pages-admin/Reporting/ReportingParams.jsx b/client/src/pages-admin/Reporting/ReportingParams.jsx index 5bd46a8..e13641e 100644 --- a/client/src/pages-admin/Reporting/ReportingParams.jsx +++ b/client/src/pages-admin/Reporting/ReportingParams.jsx @@ -10,12 +10,8 @@ const categories = [ function ReportingParams({ params, updateParams }) { function handleFormChange(e) { - console.log("id", e.target.id); - console.log("value", e.target.value); - const param = e.target.id; const value = e.target.value; - updateParams(param, value); } @@ -33,6 +29,7 @@ function ReportingParams({ params, updateParams }) { name="startMonth" value={params.startMonth} onChange={handleFormChange} + placeholder={"YYYY-MM"} />
@@ -44,6 +41,7 @@ function ReportingParams({ params, updateParams }) { type="month" value={params.endMonth} onChange={handleFormChange} + placeholder={"YYYY-MM"} />
From 415011d9be0a51111096112423867d6f8796e85d Mon Sep 17 00:00:00 2001 From: James Spears Date: Wed, 11 Dec 2024 21:46:19 -0500 Subject: [PATCH 05/16] Write parseMonthInput utility function Signed-off-by: James Spears --- client/src/lib/parseMonthInput.js | 18 +++++++++++++ .../Reporting/ReportingDisplay.jsx | 27 ++++++++++--------- 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 client/src/lib/parseMonthInput.js diff --git a/client/src/lib/parseMonthInput.js b/client/src/lib/parseMonthInput.js new file mode 100644 index 0000000..5e2f783 --- /dev/null +++ b/client/src/lib/parseMonthInput.js @@ -0,0 +1,18 @@ +/** + * Validates dates strings for YYYY-MM format + * @param {string[]} inputs + * @returns {boolean[]} + */ + +export default function parseMonthInput(inputs) { + const regex = /[0-9]{4}-[0-9]{2}/; + const results = []; + for (const input of inputs) { + const match = input.match(regex); + + if (!match || match[0] !== input) results.push(false); + else results.push(true); + } + + return results; +} diff --git a/client/src/pages-admin/Reporting/ReportingDisplay.jsx b/client/src/pages-admin/Reporting/ReportingDisplay.jsx index d3d3eb0..2faf3b1 100644 --- a/client/src/pages-admin/Reporting/ReportingDisplay.jsx +++ b/client/src/pages-admin/Reporting/ReportingDisplay.jsx @@ -1,8 +1,14 @@ +/** + * + * + */ + import PropTypes from "prop-types"; import { useState, useEffect } from "react"; import { useOutletContext } from "react-router-dom"; import { validateAdminResponse } from "../../lib/response.js"; import { toast } from "react-hot-toast"; +import parseMonthInput from "../../lib/parseMonthInput.js"; function ReportingDisplay({ params }) { const { auth, apihost, setAuth } = useOutletContext(); @@ -10,22 +16,19 @@ function ReportingDisplay({ params }) { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const regex = /[0-9]{4}-[0-9]{2}/; - useEffect(() => { - // check that params form is complete - const { startMonth, endMonth, category } = params; - - // todo : write matching function to validate month inputs in unsupported browsers - const match = startMonth.match(regex); - if (!match || match[0] !== startMonth) console.error("no match"); - console.log(match); - - if (!startMonth || !endMonth || !category) return; - // perform async fetch operation (async () => { try { + // check that params form is complete + const { startMonth, endMonth, category } = params; + if (!startMonth || !endMonth || !category) return; + + // todo : write matching function to validate month inputs in unsupported browsers + const results = parseMonthInput([startMonth, endMonth]); + if (results.includes(false)) + throw new Error("Months must be in YYYY-MM format"); + // define fetch url and options const url = `${apihost}/admin/report?startMonth=${startMonth}&endMonth=${endMonth}&category=${category}`; const options = { From c99df0e2b40c2376ea1aa1c0f120fb80c7691874 Mon Sep 17 00:00:00 2001 From: James Spears Date: Wed, 11 Dec 2024 22:54:40 -0500 Subject: [PATCH 06/16] Improve client documentation using jsdoc Signed-off-by: James Spears --- client/src/components/Button.jsx | 10 ++++++++-- client/src/components/CardWrapper.jsx | 7 ++++++- client/src/components/CountReport.jsx | 7 +++++++ client/src/components/DateInput.jsx | 2 ++ client/src/lib/response.js | 8 ++++++-- client/src/pages-admin/Dashboard/Dashboard.jsx | 5 +++++ .../src/pages-admin/Dashboard/DashboardStats.jsx | 5 +++++ .../src/pages-admin/Dashboard/DashboardTable.jsx | 5 +++++ client/src/pages-admin/Database/Database.jsx | 9 +++++---- client/src/pages-admin/Database/DatabaseModal.jsx | 10 ++++++++++ client/src/pages-admin/Database/DatabaseTable.jsx | 14 +++++++++----- client/src/pages-admin/Reporting/Reporting.jsx | 14 +++++++++----- .../src/pages-admin/Reporting/ReportingDisplay.jsx | 11 ++++++----- .../src/pages-admin/Reporting/ReportingParams.jsx | 7 +++++++ client/src/pages/Home.jsx | 5 +++++ client/src/pages/Record.jsx | 5 +++++ client/src/pages/Report.jsx | 5 +++++ 17 files changed, 105 insertions(+), 24 deletions(-) diff --git a/client/src/components/Button.jsx b/client/src/components/Button.jsx index 49d9adb..66c1b48 100644 --- a/client/src/components/Button.jsx +++ b/client/src/components/Button.jsx @@ -1,9 +1,15 @@ import PropTypes from "prop-types"; import styles from "./Button.module.css"; -/* +/** * Custom button component - * Variant corresponds to styles in Button.module.css + * @param {string} id - prop to set `data-id` property of button + * @param {string} text - button text property + * @param {() => void} action - button click callback function + * @param {'primary' | 'danger'} variant - styled button variant + * @param {'button' | 'submit'} type + * @param {Object} [style] - Optional React style object + * @returns {JSX.Element} */ function Button({ id, text, action, variant, type, style }) { diff --git a/client/src/components/CardWrapper.jsx b/client/src/components/CardWrapper.jsx index 75e19ac..41fc56e 100644 --- a/client/src/components/CardWrapper.jsx +++ b/client/src/components/CardWrapper.jsx @@ -1,7 +1,12 @@ import styles from "./CardWrapper.module.css"; import PropTypes from "prop-types"; -/* Wrapper to provide UI styling to elements */ +/** + * Wrapper to provide UI styling to children + * @param {JSX.ElementS} children + * @param {Objest} style - optional React inline styles + * @returns {JSX.Element} + */ function CardWrapper({ children, style }) { return ( diff --git a/client/src/components/CountReport.jsx b/client/src/components/CountReport.jsx index 714669a..597d067 100644 --- a/client/src/components/CountReport.jsx +++ b/client/src/components/CountReport.jsx @@ -8,6 +8,13 @@ import styles from "./CountReport.module.css"; * id, value, number_of_interactions */ +/** + * Component used to display count summary on app report page + * @param {string} title - report title + * @param {{id: number, value: string, number_of_interaction: number}} count - report from database + * @returns {JSX.Element} + */ + function CountReport({ title, count }) { return (
diff --git a/client/src/components/DateInput.jsx b/client/src/components/DateInput.jsx index d7943aa..a54175d 100644 --- a/client/src/components/DateInput.jsx +++ b/client/src/components/DateInput.jsx @@ -3,6 +3,8 @@ import styles from "./DateInput.module.css"; /* Custom date input with label and callback */ +// todo: begin here with jsdoc task + function DateInput({ label, value, handleChange }) { const id = label.toLowerCase(); diff --git a/client/src/lib/response.js b/client/src/lib/response.js index f60137c..504c8fd 100644 --- a/client/src/lib/response.js +++ b/client/src/lib/response.js @@ -1,5 +1,9 @@ -/* -Helper functions to validate API responses +/** + * Helper function to validate API responses + * @param {res} res + * @param {json} json + * @param {function} setAuth + * @returns {void} */ export function validateAdminResponse(res, json, setAuth) { diff --git a/client/src/pages-admin/Dashboard/Dashboard.jsx b/client/src/pages-admin/Dashboard/Dashboard.jsx index 09ba95d..b7bbd67 100644 --- a/client/src/pages-admin/Dashboard/Dashboard.jsx +++ b/client/src/pages-admin/Dashboard/Dashboard.jsx @@ -2,6 +2,11 @@ import { Navigate, useOutletContext } from "react-router-dom"; import DashboardStats from "./DashboardStats.jsx"; import DashboardTable from "./DashboardTable.jsx"; +/** + * Dashboard page in admin app + * @returns {JSX.Element} + */ + function Dashboard() { const { auth } = useOutletContext(); diff --git a/client/src/pages-admin/Dashboard/DashboardStats.jsx b/client/src/pages-admin/Dashboard/DashboardStats.jsx index d769411..49eef8d 100644 --- a/client/src/pages-admin/Dashboard/DashboardStats.jsx +++ b/client/src/pages-admin/Dashboard/DashboardStats.jsx @@ -14,6 +14,11 @@ import { import { validateAdminResponse } from "../../lib/response.js"; import ErrorComponent from "../../components/ErrorComponent.jsx"; +/** + * Component used to fetch and render database stats + * @returns {JSX.Element} + */ + function DashboardStats() { const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042", "#185c36"]; const { apihost, auth, setAuth } = useOutletContext(); diff --git a/client/src/pages-admin/Dashboard/DashboardTable.jsx b/client/src/pages-admin/Dashboard/DashboardTable.jsx index c73639c..5a3f32d 100644 --- a/client/src/pages-admin/Dashboard/DashboardTable.jsx +++ b/client/src/pages-admin/Dashboard/DashboardTable.jsx @@ -7,6 +7,11 @@ import CardWrapper from "../../components/CardWrapper.jsx"; import ErrorComponent from "../../components/ErrorComponent.jsx"; import { validateAdminResponse } from "../../lib/response.js"; +/** + * Component used to fetch and render all rows in interactions table + * @returns {JSX.Element} + */ + function DashboardTable() { const { apihost, auth, setAuth } = useOutletContext(); const [rows, setRows] = useState([]); diff --git a/client/src/pages-admin/Database/Database.jsx b/client/src/pages-admin/Database/Database.jsx index c97f03a..2da3abb 100644 --- a/client/src/pages-admin/Database/Database.jsx +++ b/client/src/pages-admin/Database/Database.jsx @@ -1,7 +1,3 @@ -/* -Database page in app admin area. Contains DatabaseTable and DatabaseModal components to help organize code. - */ - import { Navigate, useOutletContext } from "react-router-dom"; import { useState } from "react"; import TabSelector from "../../components/TabSelector.jsx"; @@ -9,6 +5,11 @@ import DatabaseTable from "./DatabaseTable.jsx"; import DatabaseModal from "./DatabaseModal.jsx"; import styles from "./Database.module.css"; +/** + * Database page in admin app + * @returns {JSX.Element} + */ + function Database() { const { auth } = useOutletContext(); const [activeTab, setActiveTab] = useState(""); diff --git a/client/src/pages-admin/Database/DatabaseModal.jsx b/client/src/pages-admin/Database/DatabaseModal.jsx index eb10450..fd89bea 100644 --- a/client/src/pages-admin/Database/DatabaseModal.jsx +++ b/client/src/pages-admin/Database/DatabaseModal.jsx @@ -13,6 +13,16 @@ import Form from "../../components/Form.jsx"; import Button from "../../components/Button.jsx"; import { validateAdminResponse } from "../../lib/response.js"; +/** + * Modal component used to add and edit rows in database tables + * @param {string} table + * @param {string} rowId + * @param {boolean} modalOpen + * @param {() => void} setModalOpen + * @param {() => void} setRefresh + * @returns {JSX.Element} + */ + function DatabaseModal({ table, rowId, modalOpen, setModalOpen, setRefresh }) { /* Component State */ const { apihost, auth, setAuth } = useOutletContext(); diff --git a/client/src/pages-admin/Database/DatabaseTable.jsx b/client/src/pages-admin/Database/DatabaseTable.jsx index 1ce2dff..f4a7a45 100644 --- a/client/src/pages-admin/Database/DatabaseTable.jsx +++ b/client/src/pages-admin/Database/DatabaseTable.jsx @@ -1,8 +1,3 @@ -/* -Table wrapper component for Database.jsx. -Responsible for fetching and rendering table rows using the app's table component. - */ - import { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { useOutletContext } from "react-router-dom"; @@ -12,6 +7,15 @@ import Table from "../../components/Table.jsx"; import Button from "../../components/Button.jsx"; import { validateAdminResponse } from "../../lib/response.js"; +/** + * Component used to fetch and render database table rows + * @param {string} table + * @param {(string) => void} setRowId + * @param {(boolean) => void} setModalOpen + * @param {number} refresh + * @returns {JSX.Element} + */ + function DatabaseTable({ table, setRowId, setModalOpen, refresh }) { const { apihost, auth, setAuth } = useOutletContext(); const [rows, setRows] = useState([]); diff --git a/client/src/pages-admin/Reporting/Reporting.jsx b/client/src/pages-admin/Reporting/Reporting.jsx index b098f9a..235e06d 100644 --- a/client/src/pages-admin/Reporting/Reporting.jsx +++ b/client/src/pages-admin/Reporting/Reporting.jsx @@ -3,13 +3,17 @@ import { Navigate, useOutletContext } from "react-router-dom"; import { useState } from "react"; import ReportingDisplay from "./ReportingDisplay.jsx"; -const defaultParams = { - startMonth: "", - endMonth: "", - category: "", -}; +/** + * Admin reporting page in admin app + * @returns {JSX.Element} + */ function Reporting() { + const defaultParams = { + startMonth: "", + endMonth: "", + category: "", + }; const { auth } = useOutletContext(); const [params, setParams] = useState(defaultParams); diff --git a/client/src/pages-admin/Reporting/ReportingDisplay.jsx b/client/src/pages-admin/Reporting/ReportingDisplay.jsx index 2faf3b1..4a12ce0 100644 --- a/client/src/pages-admin/Reporting/ReportingDisplay.jsx +++ b/client/src/pages-admin/Reporting/ReportingDisplay.jsx @@ -1,8 +1,3 @@ -/** - * - * - */ - import PropTypes from "prop-types"; import { useState, useEffect } from "react"; import { useOutletContext } from "react-router-dom"; @@ -10,6 +5,12 @@ import { validateAdminResponse } from "../../lib/response.js"; import { toast } from "react-hot-toast"; import parseMonthInput from "../../lib/parseMonthInput.js"; +/** + * A React component to fetch and render admin report data + * @param {{startMonth: string, endMonth: string, categoy: string}} params Parameters for admin report + * @returns {JSX.Element} + */ + function ReportingDisplay({ params }) { const { auth, apihost, setAuth } = useOutletContext(); const [report, setReport] = useState(null); diff --git a/client/src/pages-admin/Reporting/ReportingParams.jsx b/client/src/pages-admin/Reporting/ReportingParams.jsx index e13641e..5761841 100644 --- a/client/src/pages-admin/Reporting/ReportingParams.jsx +++ b/client/src/pages-admin/Reporting/ReportingParams.jsx @@ -2,6 +2,13 @@ import PropTypes from "prop-types"; import Form from "../../components/Form.jsx"; import SelectInput from "../../components/SelectInput.jsx"; +/** + * Component to capture admin report params user input + * @param {{startMonth: string, endMonth: string, category: string}} params Admin report params + * @param {function} updateParams Callback function to update report params + * @return {JSX.Element} + */ + const categories = [ { id: "type", value: "Types" }, { id: "format", value: "Formats" }, diff --git a/client/src/pages/Home.jsx b/client/src/pages/Home.jsx index 7f38f59..84a4d1f 100644 --- a/client/src/pages/Home.jsx +++ b/client/src/pages/Home.jsx @@ -7,6 +7,11 @@ import ErrorComponent from "../components/ErrorComponent.jsx"; import styles from "./Home.module.css"; +/** + * App homepage component + * @returns {JSX.Element} + */ + function Home() { const { apihost, options } = useOutletContext(); const [effectTrigger, setEffectTrigger] = useState(true); diff --git a/client/src/pages/Record.jsx b/client/src/pages/Record.jsx index cb5d4c2..f7a0c0a 100644 --- a/client/src/pages/Record.jsx +++ b/client/src/pages/Record.jsx @@ -15,6 +15,11 @@ const defaultFormState = { format: "", }; +/** + * App recording page component + * @returns {JSX.Element} + */ + function Record() { // Component state const { apihost, options } = useOutletContext(); diff --git a/client/src/pages/Report.jsx b/client/src/pages/Report.jsx index a54c86f..37e0567 100644 --- a/client/src/pages/Report.jsx +++ b/client/src/pages/Report.jsx @@ -26,6 +26,11 @@ import CountReport from "../components/CountReport"; import CardWrapper from "../components/CardWrapper"; import TabSelector from "../components/TabSelector.jsx"; +/** + * App report page component + * @returns {JSX.Element} + */ + function Report() { const defaultFormState = { start: "", From 8e05170df4c3901bef6d2de25c7ab02848e5e656 Mon Sep 17 00:00:00 2001 From: James Spears Date: Fri, 13 Dec 2024 10:58:23 -0500 Subject: [PATCH 07/16] Improve SelectInput and DateInput prop handling and typing Signed-off-by: James Spears --- client/src/components/Button.jsx | 2 +- client/src/components/DateInput.jsx | 22 ++++++++++---- client/src/components/SelectInput.jsx | 7 ++--- .../Reporting/ReportingDisplay.jsx | 3 +- .../pages-admin/Reporting/ReportingParams.jsx | 29 ++++++++----------- client/src/pages/Record.jsx | 3 ++ client/src/pages/Report.jsx | 9 ++++-- 7 files changed, 44 insertions(+), 31 deletions(-) diff --git a/client/src/components/Button.jsx b/client/src/components/Button.jsx index 66c1b48..e9857ef 100644 --- a/client/src/components/Button.jsx +++ b/client/src/components/Button.jsx @@ -5,7 +5,7 @@ import styles from "./Button.module.css"; * Custom button component * @param {string} id - prop to set `data-id` property of button * @param {string} text - button text property - * @param {() => void} action - button click callback function + * @param {(event) => void} action - button click callback function * @param {'primary' | 'danger'} variant - styled button variant * @param {'button' | 'submit'} type * @param {Object} [style] - Optional React style object diff --git a/client/src/components/DateInput.jsx b/client/src/components/DateInput.jsx index a54175d..75e64dd 100644 --- a/client/src/components/DateInput.jsx +++ b/client/src/components/DateInput.jsx @@ -3,16 +3,24 @@ import styles from "./DateInput.module.css"; /* Custom date input with label and callback */ -// todo: begin here with jsdoc task - -function DateInput({ label, value, handleChange }) { - const id = label.toLowerCase(); +/** + * Custom date input with label and callback + * @param id {string} + * @param label {string} + * @param type {'date' | 'month'} + * @param value {string} + * @param handleChange {(event) => void} + * @returns {JSX.Element} + */ +function DateInput({ id, label, type, value, handleChange }) { return (
- +