From ce5c88275e945a16102933822b7d6d8e61f6a878 Mon Sep 17 00:00:00 2001 From: benjaminjohnson2204 Date: Thu, 28 Mar 2024 23:54:11 -0700 Subject: [PATCH] Implement tablet & mobile versions of VSR table page --- frontend/src/app/login/page.tsx | 4 +- frontend/src/app/staff/vsr/page.module.css | 45 +++++- frontend/src/app/staff/vsr/page.tsx | 26 ++- frontend/src/app/vsr/page.tsx | 4 +- .../components/VSRTable/PageTitle/index.tsx | 6 +- .../VSRTable/PageTitle/styles.module.css | 16 +- .../components/VSRTable/VSRTable/index.tsx | 152 ++++++++++-------- .../src/components/shared/HeaderBar/index.tsx | 10 +- .../shared/HeaderBar/styles.module.css | 12 ++ .../shared/StatusChip/styles.module.css | 8 + .../shared/input/Dropdown/index.tsx | 6 +- frontend/src/util/useScreenSizes.ts | 14 ++ 12 files changed, 212 insertions(+), 91 deletions(-) create mode 100644 frontend/src/util/useScreenSizes.ts diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 44278e5..d4dcdbd 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -8,7 +8,7 @@ import styles from "@/app/login/page.module.css"; import { signInWithEmailAndPassword } from "firebase/auth"; import { initFirebase } from "@/firebase/firebase"; import { useRouter } from "next/navigation"; -import { useMediaQuery } from "@mui/material"; +import { useScreenSizes } from "@/util/useScreenSizes"; const Login = () => { const [email, setEmail] = useState(""); @@ -18,7 +18,7 @@ const Login = () => { const router = useRouter(); - const isMobile = useMediaQuery("@media screen and (max-width: 550px)"); + const { isMobile } = useScreenSizes(); const sendTokenToBackend = async (token: string) => { try { diff --git a/frontend/src/app/staff/vsr/page.module.css b/frontend/src/app/staff/vsr/page.module.css index 0920eaf..3818081 100644 --- a/frontend/src/app/staff/vsr/page.module.css +++ b/frontend/src/app/staff/vsr/page.module.css @@ -7,10 +7,9 @@ display: flex; flex-direction: column; justify-content: center; - margin: 105px auto 0; + margin: 105px 120px 0; padding: 0px; gap: 40px; - max-width: 1200px; } .flex { @@ -22,7 +21,7 @@ .button_row { display: flex; flex-direction: row; - align-items: flex-start; + align-items: center; justify-content: space-between; } @@ -30,7 +29,7 @@ gap: 40px; display: flex; flex-direction: row; - align-items: flex-start; + align-items: center; } .statusContainer { @@ -53,14 +52,14 @@ } .statusWrapper { - min-width: 240px; + min-width: 230px; } .row_right { gap: 40px; display: flex; flex-direction: row; - align-items: flex-end; + align-items: center; } .buttons { @@ -95,3 +94,37 @@ align-items: center; width: 100%; } + +/* shrink the margins at a screen size larger than tablet to avoid overflow */ +@media screen and (max-width: 1150px) { + .column { + margin: 52px 48px 0; + } +} + +@media screen and (max-width: 600px) { + .row_right { + gap: 4px; + } +} + +/* mobile version */ +@media screen and (max-width: 550px) { + .column { + margin: 37px 24px 0; + } + + .statusWrapper { + min-width: 190px; + } + + .statusLabel { + font-size: 12px; + } +} + +@media screen and (max-width: 475px) { + .statusWrapper { + min-width: unset; + } +} diff --git a/frontend/src/app/staff/vsr/page.tsx b/frontend/src/app/staff/vsr/page.tsx index 716a2fa..f34a762 100644 --- a/frontend/src/app/staff/vsr/page.tsx +++ b/frontend/src/app/staff/vsr/page.tsx @@ -8,8 +8,13 @@ import HeaderBar from "@/components/shared/HeaderBar"; import Image from "next/image"; import React from "react"; import { StatusDropdown } from "@/components/VSRIndividual"; +import { useMediaQuery } from "@mui/material"; export default function VSRTableView() { + const searchOnOwnRow = useMediaQuery("@media screen and (max-width: 1000px)"); + const buttonIconsOnly = useMediaQuery("@media screen and (max-width: 700px)"); + const buttonIconSize = buttonIconsOnly ? 16 : 24; + return (
@@ -17,7 +22,7 @@ export default function VSRTableView() {
- + {searchOnOwnRow ? null : }

Status:

@@ -28,15 +33,26 @@ export default function VSRTableView() {
+ {searchOnOwnRow ? : null}
diff --git a/frontend/src/app/vsr/page.tsx b/frontend/src/app/vsr/page.tsx index 9ac0e0b..268573b 100644 --- a/frontend/src/app/vsr/page.tsx +++ b/frontend/src/app/vsr/page.tsx @@ -8,10 +8,10 @@ import Dropdown from "@/components/shared/input/Dropdown"; import HeaderBar from "@/components/shared/HeaderBar"; import PageNumber from "@/components/VSRForm/PageNumber"; import { createVSR, CreateVSRRequest, FurnitureInput } from "@/api/VSRs"; -import { useMediaQuery } from "@mui/material"; import { FurnitureItem, getFurnitureItems } from "@/api/FurnitureItems"; import BinaryChoice from "@/components/shared/input/BinaryChoice"; import { FurnitureItemSelection } from "@/components/VeteranForm/FurnitureItemSelection"; +import { useScreenSizes } from "@/util/useScreenSizes"; interface IFormInput { name: string; @@ -253,7 +253,7 @@ const VeteranServiceRequest: React.FC = () => { })); }; - const isMobile = useMediaQuery("@media screen and (max-width: 550px)"); + const { isMobile } = useScreenSizes(); // Execute when submit button is pressed const onSubmit: SubmitHandler = async (data) => { diff --git a/frontend/src/components/VSRTable/PageTitle/index.tsx b/frontend/src/components/VSRTable/PageTitle/index.tsx index cdf9491..f52bbde 100644 --- a/frontend/src/components/VSRTable/PageTitle/index.tsx +++ b/frontend/src/components/VSRTable/PageTitle/index.tsx @@ -2,9 +2,5 @@ import React from "react"; import styles from "@/components/VSRTable/PageTitle/styles.module.css"; export default function PageTitle() { - return ( -
- Service Requests -
- ); + return Service Requests; } diff --git a/frontend/src/components/VSRTable/PageTitle/styles.module.css b/frontend/src/components/VSRTable/PageTitle/styles.module.css index 3665bbe..3d4f4ad 100644 --- a/frontend/src/components/VSRTable/PageTitle/styles.module.css +++ b/frontend/src/components/VSRTable/PageTitle/styles.module.css @@ -3,10 +3,22 @@ color: var(--color-tse-secondary-1); font-family: var(--font-title); font-size: 40px; - font-style: normal; font-weight: 700; - line-height: 51.2px; display: flex; flex-direction: row; align-items: flex-start; } + +/* tablet version */ +@media screen and (max-width: 850px) { + .items { + font-size: 36px; + } +} + +/* mobile version */ +@media screen and (max-width: 550px) { + .items { + font-size: 28px; + } +} diff --git a/frontend/src/components/VSRTable/VSRTable/index.tsx b/frontend/src/components/VSRTable/VSRTable/index.tsx index 29d692e..b51fe37 100644 --- a/frontend/src/components/VSRTable/VSRTable/index.tsx +++ b/frontend/src/components/VSRTable/VSRTable/index.tsx @@ -11,80 +11,102 @@ import moment from "moment"; import { useRouter } from "next/navigation"; import { StatusChip } from "@/components/shared/StatusChip"; import { STATUS_OPTIONS } from "@/components/shared/StatusDropdown"; +import { useScreenSizes } from "@/util/useScreenSizes"; const formatDateReceived = (dateReceived: Date) => { // Return the empty string on a falsy date received, instead of defaulting to today's date return dateReceived ? moment(dateReceived).format("MMMM D, YYYY") : ""; }; -const columns: GridColDef[] = [ - { - ...GRID_CHECKBOX_SELECTION_COL_DEF, - width: 72, - headerClassName: "header", - }, - { - field: "_id", - headerName: "Case ID", - type: "string", - flex: 1, - headerAlign: "left", - headerClassName: "header", - disableColumnMenu: true, - hideSortIcons: true, - }, - { - field: "militaryID", - headerName: "Military ID (Last 4)", - type: "string", - flex: 1, - headerClassName: "header", - disableColumnMenu: true, - hideSortIcons: true, - }, - { - field: "name", - headerName: "Name", - flex: 1, - headerClassName: "header", - disableColumnMenu: true, - hideSortIcons: true, - }, - - { - field: "dateReceived", - headerName: "Date Received", - type: "date", - sortable: true, - flex: 1, - headerClassName: "header", - disableColumnMenu: true, - hideSortIcons: true, - valueFormatter: (params) => formatDateReceived(params?.value), - }, - { - field: "status", - headerName: "Status", - type: "string", - flex: 1, - headerClassName: "header", - disableColumnMenu: true, - hideSortIcons: true, - renderCell: (params) => ( - statusOption.value === params.value) ?? - STATUS_OPTIONS[0] - } - /> - ), - }, -]; - export default function VSRTable() { const [vsrs, setVsrs] = React.useState(); const router = useRouter(); + const { isMobile, isTablet } = useScreenSizes(); + + const columns: GridColDef[] = React.useMemo(() => { + const result = [ + { + ...GRID_CHECKBOX_SELECTION_COL_DEF, + width: 72, + headerClassName: "header", + }, + ]; + + if (!isMobile) { + result.push({ + field: "_id", + headerName: "Case ID", + type: "string", + flex: 1, + headerAlign: "left", + headerClassName: "header", + disableColumnMenu: true, + hideSortIcons: true, + width: 100, + }); + } + + if (!isTablet) { + result.push({ + field: "militaryID", + headerName: "Military ID (Last 4)", + type: "string", + flex: 1, + headerClassName: "header", + disableColumnMenu: true, + hideSortIcons: true, + width: 100, + }); + } + + result.push({ + field: "name", + headerName: "Name", + flex: 1, + headerClassName: "header", + disableColumnMenu: true, + hideSortIcons: true, + width: 100, + }); + + if (!isTablet) { + result.push({ + field: "dateReceived", + headerName: "Date Received", + type: "date", + sortable: true, + flex: 1, + headerClassName: "header", + disableColumnMenu: true, + hideSortIcons: true, + valueFormatter: (params) => formatDateReceived(params?.value), + width: 100, + }); + } + + result.push({ + field: "status", + headerName: "Status", + type: "string", + flex: 1, + headerClassName: "header", + disableColumnMenu: true, + hideSortIcons: true, + renderCell: (params) => ( + statusOption.value === params.value) ?? + STATUS_OPTIONS[0] + } + /> + ), + width: 100, + }); + + return result; + }, [isMobile, isTablet]); + useEffect(() => { getAllVSRs().then((result) => { if (result.success) { @@ -121,7 +143,7 @@ export default function VSRTable() { cursor: "pointer", }, ".MuiDataGrid-cellContent": { - fontSize: "1rem", + fontSize: isMobile ? "0.75rem" : isTablet ? "0.875rem" : "1rem", }, "&.MuiDataGrid-root": { border: "none", diff --git a/frontend/src/components/shared/HeaderBar/index.tsx b/frontend/src/components/shared/HeaderBar/index.tsx index 4bc1507..d5b927d 100644 --- a/frontend/src/components/shared/HeaderBar/index.tsx +++ b/frontend/src/components/shared/HeaderBar/index.tsx @@ -1,11 +1,19 @@ import Image from "next/image"; import React from "react"; import styles from "@/components/shared/HeaderBar/styles.module.css"; +import { useScreenSizes } from "@/util/useScreenSizes"; const HeaderBar = () => { + const { isTablet } = useScreenSizes(); return (
- logo + logo
); }; diff --git a/frontend/src/components/shared/HeaderBar/styles.module.css b/frontend/src/components/shared/HeaderBar/styles.module.css index c71779b..07117de 100644 --- a/frontend/src/components/shared/HeaderBar/styles.module.css +++ b/frontend/src/components/shared/HeaderBar/styles.module.css @@ -12,3 +12,15 @@ margin-top: 27px; margin-left: 63px; } + +/* tablet version */ +@media screen and (max-width: 850px) { + .headerBar { + height: 115px; + } + + .logo { + margin-top: 52px; + margin-left: 24px; + } +} diff --git a/frontend/src/components/shared/StatusChip/styles.module.css b/frontend/src/components/shared/StatusChip/styles.module.css index 6cb88b8..f870e0a 100644 --- a/frontend/src/components/shared/StatusChip/styles.module.css +++ b/frontend/src/components/shared/StatusChip/styles.module.css @@ -3,4 +3,12 @@ padding: 2px 8px; border-radius: 8px; width: max-content; + font-size: 16px; +} + +/* mobile version */ +@media screen and (max-width: 550px) { + .statusChip { + font-size: 12px; + } } diff --git a/frontend/src/components/shared/input/Dropdown/index.tsx b/frontend/src/components/shared/input/Dropdown/index.tsx index ccec258..e0ed938 100644 --- a/frontend/src/components/shared/input/Dropdown/index.tsx +++ b/frontend/src/components/shared/input/Dropdown/index.tsx @@ -1,7 +1,8 @@ import React from "react"; import styles from "@/components/shared/input/Dropdown/styles.module.css"; -import { FormControl, Select, MenuItem, SelectChangeEvent, useMediaQuery } from "@mui/material"; +import { FormControl, Select, MenuItem, SelectChangeEvent } from "@mui/material"; import { FormField } from "../FormField"; +import { useScreenSizes } from "@/util/useScreenSizes"; export interface DropDownProps { label: string; @@ -24,8 +25,7 @@ const Dropdown = ({ helperText, placeholder, }: DropDownProps) => { - const isMobile = useMediaQuery("@media screen and (max-width: 550px)"); - const isTablet = useMediaQuery("@media screen and (max-width: 850px)"); + const { isMobile, isTablet } = useScreenSizes(); return ( diff --git a/frontend/src/util/useScreenSizes.ts b/frontend/src/util/useScreenSizes.ts new file mode 100644 index 0000000..01dee83 --- /dev/null +++ b/frontend/src/util/useScreenSizes.ts @@ -0,0 +1,14 @@ +import { useMediaQuery } from "@mui/material"; + +/** + * A hook to determine the screen size (desktop vs. tablet vs. mobile) + */ +export const useScreenSizes = () => { + const isMobile = useMediaQuery("@media screen and (max-width: 550px)"); + const isTablet = useMediaQuery("@media screen and (max-width: 850px)"); + + return { + isMobile, + isTablet, + }; +};