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

[Closes #116] Create a patients details page #149

Merged
merged 23 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e6d7314
Create skeleton of each section for patient page
samau3 Oct 28, 2024
db23f88
Style patient info section to match Figma more closely
samau3 Oct 28, 2024
9562f51
Style section titles
samau3 Oct 28, 2024
dfd6be8
Style contact information section
samau3 Oct 28, 2024
549665e
Display medical information for a patient
samau3 Oct 28, 2024
2890e25
Include an empty array state for each medical section
samau3 Oct 29, 2024
0110dc9
Style Contact info to be two columns with aligned rows
samau3 Oct 29, 2024
0f2966c
Add missing key prop
samau3 Oct 29, 2024
08f66ee
Consolidate some CSS rules
samau3 Oct 29, 2024
6c5fcf8
Remove console logs
samau3 Oct 29, 2024
391d4f4
Format file
samau3 Oct 29, 2024
d0e9396
Remove unused variable
samau3 Oct 29, 2024
83cd395
Refactor PatientDetails into smaller components
samau3 Oct 29, 2024
870edbe
Add JSDoc comment and prop types
samau3 Oct 29, 2024
10a5adc
Clean up imports
samau3 Oct 29, 2024
860ab8a
Allow Pill text highlighting
samau3 Oct 29, 2024
fa5bf99
Display dashes if any information is blank
samau3 Oct 30, 2024
1de0930
Utilize consistent syntax for fallback text rendering
samau3 Oct 30, 2024
ceb975c
Redirect register patient form after successful submission to patient…
samau3 Oct 31, 2024
4ed82b7
Merge branch 'dev' into issue-116
francisli Nov 1, 2024
a9c158f
Display QR code to link to patient details page
samau3 Nov 1, 2024
36e4d78
Increase size of QR code to become scanable
samau3 Nov 2, 2024
ae6b881
Display a bigger QR code
francisli Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>SF Life line</title>
<title>SF Life Line</title>
</head>
<body>
<div id="root"></div>
Expand Down
60 changes: 45 additions & 15 deletions client/src/pages/patients/patient-details/PatientDetails.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { useParams, useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
import { useParams, useNavigate, useLocation } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import LifelineAPI from '../LifelineAPI.js';
import { StatusCodes } from 'http-status-codes';
import { useEffect } from 'react';
import { Loader } from '@mantine/core';
import { humanize } from 'inflection';
import { QRCode } from 'react-qrcode-logo';
import { Container, Grid, Loader, Text, Title } from '@mantine/core';

import LifelineAPI from '../LifelineAPI.js';
import ContactInfo from './components/ContactInfo.jsx';
import MedicalInfo from './components/MedicalInfo.jsx';
import Preferences from './components/Preferences.jsx';

import classes from './PatientDetails.module.css';

/**
*
Expand All @@ -12,8 +20,9 @@ import { Loader } from '@mantine/core';
export default function PatientDetails() {
const { patientId } = useParams();
const navigate = useNavigate();
const location = useLocation();

const { data, isSuccess, isError, isLoading } = useQuery({
const { data, isError, isLoading } = useQuery({
queryKey: ['patient'],
queryFn: async () => {
const res = await LifelineAPI.getPatient(patientId);
Expand All @@ -23,11 +32,7 @@ export default function PatientDetails() {
throw new Error('Failed to fetch patient.');
}
},

retry: false,
refetchOnWindowFocus: false,
});
console.log(data, isSuccess, isError, isLoading);

useEffect(() => {
if (isError) {
Expand All @@ -43,12 +48,37 @@ export default function PatientDetails() {
}

return (
<main>
<h1>Patient</h1>
<p>This is the patient page</p>
<p>Patient ID: {data?.id}</p>
<p>Patient First Name: {data?.firstName}</p>
<p>Patient Last Name: {data?.lastName}</p>
<main className={classes.details}>
<Container style={{ marginBottom: '2rem' }}>
<Grid my="2rem">
<Grid.Col span={{ base: 12, md: 8 }}>
<Title mb="1rem">
{data?.firstName} {data?.lastName}
</Title>
<section className={classes.patientInfoContainer}>
<Text>Date of birth</Text>
<Text>Gender</Text>
<Text>Preferred language</Text>
<Text>{data?.dateOfBirth}</Text>
<Text>{humanize(data?.gender)}</Text>
<Text>{humanize(data?.language)}</Text>
</section>
</Grid.Col>
<Grid.Col display={{ base: 'none', md: 'block' }} span={4} ta="right">
<QRCode value={`${window.location.origin}${location.pathname}`} />
</Grid.Col>
</Grid>
<ContactInfo
emergencyContact={data?.emergencyContact}
physician={data?.physician}
/>
<MedicalInfo
allergies={data?.allergies}
medications={data?.medications}
conditions={data?.conditions}
/>
<Preferences codeStatus={data?.codeStatus} hospital={data?.hospital} />
</Container>
</main>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.boldText {
font-weight: 600;
margin-top: var(--mantine-spacing-sm);
margin-bottom: var(--mantine-spacing-sm);
}

.patientInfoContainer {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto;
text-align: center;
gap: var(--mantine-spacing-sm);
}

.patientInfoContainer p:nth-child(-n + 3) {
font-weight: 600;
margin-bottom: 0;
}

.patientInfoContainer p:nth-child(n + 4) {
margin-top: 0;
}

.sectionTitle {
font-size: var(--mantine-font-size-xl);
font-weight: 600;
margin-top: var(--mantine-spacing-md);
margin-bottom: var(--mantine-spacing-md);
}

.titleRow {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--mantine-spacing-xl);
margin-bottom: var(--mantine-spacing-md);
}

.twoColumnGrid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--mantine-spacing-xl);
}

.contactRow {
gap: var(--mantine-spacing-md);
margin-bottom: var(--mantine-spacing-xs);
}

.contactInfoColumnTitle {
font-weight: 600;
font-size: var(--mantine-font-size-lg);
}

.medicalInfoPills {
margin-right: var(--mantine-spacing-sm);
margin-bottom: var(--mantine-spacing-sm);
user-select: text;
}

@media (min-width: 768px) {
.patientInfoContainer {
text-align: left;
width: 70%;
}

.details {
max-height: 100vh;
max-height: 100dvh;
overflow-y: auto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import PropTypes from 'prop-types';

import { Paper, Text } from '@mantine/core';
import { humanize } from 'inflection';
import classes from '../PatientDetails.module.css';

const contactInfoProps = {
emergencyContact: PropTypes.object,
physician: PropTypes.object,
};

ContactInfo.propTypes = contactInfoProps;

/**
*
* @param {PropTypes.InferProps<typeof contactInfoProps>} props
*/
export default function ContactInfo({ emergencyContact, physician }) {
return (
<section>
<Text className={classes.sectionTitle}> Contact Information</Text>
<Paper shadow="xs" p="md" radius="md" withBorder>
<div className={classes.titleRow}>
<Text className={classes.contactInfoColumnTitle}>
Emergency Contact
</Text>
<Text className={classes.contactInfoColumnTitle}>
Primary care physician (PCP) contact
</Text>
</div>
<div className={classes.twoColumnGrid}>
<section>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Name</Text>
<Text>
{emergencyContact
? `${emergencyContact?.firstName} ${emergencyContact?.lastName}`
: '-'}
</Text>
</div>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Phone</Text>
<Text>
{emergencyContact?.phone ? emergencyContact?.phone : '-'}
</Text>
</div>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Relationship</Text>
<Text>
{emergencyContact?.relationship
? humanize(emergencyContact?.relationship)
: '-'}
</Text>
</div>
</section>
<section>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Name</Text>
<Text>
{physician
? `${physician?.firstName} ${physician?.lastName}`
: '-'}
</Text>
</div>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Phone</Text>
<Text>{physician?.phone ? physician?.phone : '-'}</Text>
</div>
<div className={classes.contactRow}>
<Text className={classes.boldText}>Hospital</Text>
<Text>
{physician?.hospitals[0]?.name
? physician?.hospitals[0]?.name
: '-'}
</Text>
</div>
</section>
</div>
</Paper>
</section>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import PropTypes from 'prop-types';

import { Paper, Text, Pill } from '@mantine/core';
import classes from '../PatientDetails.module.css';

const medicalInfoProps = {
allergies: PropTypes.array,
medications: PropTypes.array,
conditions: PropTypes.array,
};

MedicalInfo.propTypes = medicalInfoProps;

/**
*
* @param {PropTypes.InferProps<typeof medicalInfoProps>} props
*/
export default function MedicalInfo({ allergies, medications, conditions }) {
return (
<section>
<Text className={classes.sectionTitle}>Medical Information</Text>
<Paper shadow="xs" p="md" radius="md" withBorder>
<section>
<Text className={classes.boldText}>Allergies</Text>
{allergies.length === 0 ? (
<Text>None</Text>
) : (
allergies.map((entry) => (
<Pill
size="md"
key={entry.allergy.id}
className={classes.medicalInfoPills}
>
{entry.allergy.name}
</Pill>
))
)}
</section>
<section>
<Text className={classes.boldText}>Medications</Text>
{medications.length === 0 ? (
<Text>None</Text>
) : (
medications.map((entry) => (
<Pill
size="md"
key={entry.medication.id}
className={classes.medicalInfoPills}
>
{entry.medication.name}
</Pill>
))
)}
</section>
<section>
<Text className={classes.boldText}>Conditions</Text>
{conditions?.length === 0 ? (
<Text>None</Text>
) : (
<ul>
{conditions.map((entry) => (
<li key={entry.condition.id}>{entry.condition.name}</li>
))}
</ul>
)}
</section>
</Paper>
</section>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import PropTypes from 'prop-types';
import { Paper, Text } from '@mantine/core';
import classes from '../PatientDetails.module.css';
import { humanize } from 'inflection';
const preferencesProps = {
codeStatus: PropTypes.string,
hospital: PropTypes.object,
};

Preferences.propTypes = preferencesProps;

/**
* Preferences section of patient details
* @param {PropTypes.InferProps<typeof preferencesProps>} props
*/
export default function Preferences({ codeStatus, hospital }) {
return (
<section>
<Text className={classes.sectionTitle}>Preferences</Text>
<Paper shadow="xs" p="md" radius="md" withBorder>
<section>
<Text className={classes.boldText}>Code status</Text>
<Text>{codeStatus ? humanize(codeStatus) : 'Not provided'}</Text>
<Text className={classes.boldText}>Hospital</Text>
<Text>{hospital ? hospital.name : 'Not provided'}</Text>
</section>
</Paper>
</section>
);
}
4 changes: 2 additions & 2 deletions client/src/pages/patients/register/PatientRegistration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export default function PatientRegistration() {
);
if (updateRes.status === StatusCodes.OK) {
showSuccessNotification('Successfully registered patient.');
navigate('/dashboard', { replace: true });
navigate(`/patients/${patientId}`, { replace: true });
return;
}
}
Expand All @@ -299,7 +299,7 @@ export default function PatientRegistration() {
showSuccessNotification(
'Patient basic information has been successfully updated.',
);
navigate('/dashboard', { replace: true });
navigate(`/patients/${patientId}`, { replace: true });
return;
}
}
Expand Down