Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add healthy habit tracking form list #95

Merged
merged 8 commits into from
Jan 11, 2025
12 changes: 0 additions & 12 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,5 @@ module.exports = {
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"no-restricted-imports": [
"error",
{
patterns: [
{
group: [".*"],
message:
'Please do not use relative imports. Use "@/..." instead. If this breaks something, disable the rule for that file only.',
},
],
},
],
},
};
2 changes: 1 addition & 1 deletion src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import NextAuth from "next-auth";

import authOptions from "@/app/api/auth/[...nextauth]/options";
import authOptions from "./options";

const handler = NextAuth(authOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Typography } from "@mui/material";

type FormResponse = {
label: string;
value: string;
value: string | number;
isListItem?: boolean;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";

import DeleteIcon from "@mui/icons-material/Delete";
import DescriptionIcon from "@mui/icons-material/Description";
import {
Avatar,
Box,
IconButton,
ListItem,
ListItemAvatar,
ListItemText,
} from "@mui/material";
import { useState } from "react";

import HealthyHabitsTrackingFormModal from "@/components/ClientDashboard/HealthyHabits/HealthyHabitsHistory/HealthyHabitsTrackingFormList/HealthyHabitsTrackingFormModal";
import { HealthyHabitsTrackingForm } from "@/types";
import dayjsUtil from "@/utils/dayjsUtil";

type HealthyHabitsTrackingFormListItemProps = {
form: HealthyHabitsTrackingForm;
handleDelete: (form: HealthyHabitsTrackingForm) => void;
};

export default function HealthyHabitsTrackingFormListItem({
form,
handleDelete,
}: HealthyHabitsTrackingFormListItemProps) {
const [open, setOpen] = useState(false);

return (
<ListItem
secondaryAction={
<Box sx={{ display: "flex", gap: 2 }}>
<IconButton edge="end" aria-label="info">
<HealthyHabitsTrackingFormModal
form={form}
open={open}
setOpen={setOpen}
/>
</IconButton>
<IconButton edge="end" onClick={() => handleDelete(form)}>
<DeleteIcon />
</IconButton>
</Box>
}
>
<ListItemAvatar>
<Avatar>
<DescriptionIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={dayjsUtil.utc(form.submittedDate).format("MM/DD/YYYY")}
/>
</ListItem>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"use client";

import InfoIcon from "@mui/icons-material/Info";
import { Box, Button, Fade, Modal, Typography } from "@mui/material";
import { Dispatch, SetStateAction } from "react";

import FormResponse from "@/components/AdminDashboard/PendingApplicationDashboard/PendingApplicationInfoModal/FormResponse";
import { HealthyHabitsTrackingForm } from "@/types";
import dayjsUtil from "@/utils/dayjsUtil";

const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "40vw",
maxHeight: "80vh",
overflowY: "auto",
bgcolor: "background.paper",
boxShadow: 2,
p: 4,
display: "flex",
flexDirection: "column",
gap: 2,
};

type HealthyHabitsTrackingFormModalProps = {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
form: HealthyHabitsTrackingForm;
};

export default function HealthyHabitsTrackingFormModal({
form,
open,
setOpen,
}: HealthyHabitsTrackingFormModalProps) {
return (
<>
<InfoIcon onClick={() => setOpen(true)} />

<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={() => setOpen(false)}
closeAfterTransition
>
<Fade in={open}>
<Box sx={style}>
<Typography id="transition-modal-title" variant="h4">
{dayjsUtil.utc(form.submittedDate).format("MM/DD/YYYY")}
</Typography>

<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<FormResponse
label="Health Conditions"
value={form.healthConditions}
/>
<Typography variant="h6">Devices</Typography>
<FormResponse
label="Scale"
value={form.devices.hasScale ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Blood Pressure Cuff"
value={form.devices.hasBloodPressureCuff ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Glucose Monitor"
value={form.devices.hasGlucoseMonitor ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="A1c Home Test"
value={form.devices.hasA1cHomeTest ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Fitness Tracker"
value={form.devices.hasFitnessTracker ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Body Tape Measure"
value={form.devices.hasBodyTapeMeasure ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Resistance Bands"
value={form.devices.hasResistanceBands ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="Other Exercise Equipment"
value={form.devices.hasOtherExerciseEquipment ? "Yes" : "No"}
isListItem={true}
/>
<FormResponse
label="None of the Above"
value={form.devices.noneOfTheAbove ? "Yes" : "No"}
isListItem={true}
/>

<Typography variant="h6">Measurements</Typography>
<FormResponse
label="Weight"
value={`${form.weight} lbs`}
isListItem={true}
/>
<FormResponse
label="Blood Pressure"
value={`${form.systolicBloodPressure}/${form.diastolicBloodPressure}`}
isListItem={true}
/>
<FormResponse
label="Blood Glucose"
value={form.bloodGlucose}
isListItem={true}
/>

<Typography variant="h6">Daily Activity</Typography>
<FormResponse
label="Movement Minutes"
value={form.movementMinutes}
isListItem={true}
/>

<Typography variant="h6">Wellness Rankings</Typography>
<FormResponse
label="Sleep"
value={form.sleepRanking}
isListItem={true}
/>
<FormResponse
label="Energy"
value={form.energyRanking}
isListItem={true}
/>
<FormResponse
label="Emotional Health"
value={form.emotionalHealthRanking}
isListItem={true}
/>

<Typography variant="h6">Goals</Typography>
<FormResponse
label="Qualitative Goals"
value={form.qualitativeGoals}
isListItem={true}
/>
</Box>

<Button
variant="outlined"
onClick={() => setOpen(false)}
sx={{ mt: 3 }}
>
Close
</Button>
</Box>
</Fade>
</Modal>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import { Box, List, Snackbar, Typography } from "@mui/material";
import { Dispatch, SetStateAction, useState } from "react";

import HealthyHabitsTrackingFormListItem from "@/components/ClientDashboard/HealthyHabits/HealthyHabitsHistory/HealthyHabitsTrackingFormList/HealthyHabitsTrackingFormListItem";
import { handleHealthyHabitsTrackingFormDeletion } from "@/server/api/healthy-habits-tracking-forms/public-mutations";
import { ClientUser, HealthyHabitsTrackingForm } from "@/types";

type HealthyHabitsTrackingFormListProps = {
trackingForms: HealthyHabitsTrackingForm[];
setTrackingForms: Dispatch<SetStateAction<HealthyHabitsTrackingForm[]>>;
user: ClientUser;
};

export default function HealthyHabitsTrackingFormList({
trackingForms,
setTrackingForms,
user,
}: HealthyHabitsTrackingFormListProps) {
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");

const handleDelete = async (form: HealthyHabitsTrackingForm) => {
const confirm = window.confirm(
"Are you sure you want to delete this health tracking record?",
);

if (!confirm) {
return;
}

setTrackingForms((prevForms) =>
prevForms.filter((prevForm) => prevForm._id !== form._id),
);

const [, error] = await handleHealthyHabitsTrackingFormDeletion(form, user);

if (error !== null) {
setSnackbarMessage("An unexpected error occurred.");
} else {
setSnackbarMessage("Form deleted successfully.");
}

setSnackbarOpen(true);
};

return (
<>
<Snackbar
open={snackbarOpen}
autoHideDuration={3000}
onClose={() => setSnackbarOpen(false)}
message={snackbarMessage}
/>
<Box sx={{ width: "100%" }}>
<Typography variant="h6">Previous forms</Typography>
<List>
{trackingForms.map((form) => (
<HealthyHabitsTrackingFormListItem
key={form._id}
form={form}
handleDelete={handleDelete}
/>
))}
</List>
</Box>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ export default function ModularBarChart({
const getChartData = () => {
return trackingForms
.sort((a, b) =>
dayjsUtil(a.submittedDate, "MM/DD/YYYY").diff(
dayjsUtil(b.submittedDate, "MM/DD/YYYY"),
),
dayjsUtil.utc(a.submittedDate).diff(dayjsUtil.utc(b.submittedDate)),
)
.map((form) => ({
value: Number(form[dataKey]) || 0,
label: `Week of ${dayjsUtil(form.submittedDate, "MM/DD/YYYY").format("MM/DD/YY")}`,
label: `Week of ${dayjsUtil.utc(form.submittedDate).format("MM/DD/YY")}`,
}));
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ export default function ModularLineChart({

const getChartDataAndSeries = (): ChartConfig => {
const sortedData = trackingForms.sort((a, b) =>
dayjsUtil(a.submittedDate, "MM/DD/YYYY").diff(
dayjsUtil(b.submittedDate, "MM/DD/YYYY"),
),
dayjsUtil.utc(a.submittedDate).diff(dayjsUtil.utc(b.submittedDate)),
);

const chartData: RegularChartDataPoint[] = sortedData.map((entry) => ({
x: dayjsUtil(entry.submittedDate, "MM/DD/YYYY").toDate(),
x: dayjsUtil.utc(entry.submittedDate).toDate(),
y: entry[dataKey] as number,
...(dataKey2 && { y2: entry[dataKey2] as number }),
}));
Expand Down Expand Up @@ -96,7 +94,7 @@ export default function ModularLineChart({
label: "Date",
scaleType: "time",
valueFormatter: (value) =>
`Week of ${dayjsUtil(value).format("MM/DD/YYYY")}`,
`Week of ${dayjsUtil.utc(value).format("MM/DD/YYYY")}`,
tickInterval: chartData.map((item) => item.x),
},
]}
Expand Down
Loading
Loading