Skip to content

Commit

Permalink
Merge pull request #16 from TritonSE/DynamicMedicalEmergencyPage
Browse files Browse the repository at this point in the history
Merge dynamic medical emergency page to resolve issue #9
  • Loading branch information
r800360 authored Feb 26, 2024
2 parents 5280f5e + 7eedb71 commit 544cc09
Show file tree
Hide file tree
Showing 8 changed files with 558 additions and 103 deletions.
4 changes: 2 additions & 2 deletions dfm-sideline-sidekick-app/ConditionSectionStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default StyleSheet.create({
alignItems: "flex-start",
justifyContent: "space-evenly",
width: "100%",
marginTop: 45,
marginTop: 16,
},
menuText: {
fontFamily: "Roboto-Regular",
Expand Down Expand Up @@ -73,7 +73,7 @@ export default StyleSheet.create({
},
information: {
marginLeft: 16,
marginTop: 20,
marginTop: 0,
marginRight: 16,
},
overview: {},
Expand Down
191 changes: 116 additions & 75 deletions dfm-sideline-sidekick-app/ConditionsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
// import { useRoute } from "@react-navigation/native";
import { ParamListBase, RouteProp } from "@react-navigation/native";
import { StackNavigationProp } from "@react-navigation/stack";
import * as Font from "expo-font";
import { useEffect, useState } from "react";
import { Image, Pressable, SafeAreaView, ScrollView, Text, View } from "react-native";

import styles from "./ConditionSectionStyles";
import StringRenderer from "./components/StringRenderer";
// import { getEmergency } from "./emergencies";

export default function ConditionsSection() {
import type { Emergency } from "./emergencies";

export type RootStackParamList = {
// Define the parameters for your screens here
Conditions: { emergency: Emergency }; // Example parameter
} & ParamListBase;

// Define the type for the route parameters
type ConditionsScreenRouteProp = RouteProp<RootStackParamList, "Conditions">;

// Define the type for the navigation object
type ConditionsScreenNavigationProp = StackNavigationProp<RootStackParamList, "Conditions">;

type Props = {
route: ConditionsScreenRouteProp;
navigation: ConditionsScreenNavigationProp;
};

type StringValue = string | string[] | { [key: string]: StringValue };

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function ConditionsSection({ route, navigation }: Props) {
const [isOverviewPressed, setIsOverviewPressed] = useState<boolean>(true);
const [isTreatmentPressed, setIsTreatmentPressed] = useState<boolean>(false);
const [isFontsLoaded, setIsFontsLoaded] = useState<boolean>(false);
const [overviewHeaders, setOverviewHeaders] = useState<string[]>([]);
const [overviewValues, setOverviewValues] = useState<StringValue[]>([]);
const [treatmentHeaders, setTreatmentHeaders] = useState<string[]>([]);
const [treatmentValues, setTreatmentValues] = useState<StringValue[]>([]);
const [contentHeaders, setContentHeaders] = useState<string[]>([]);
const [contentValues, setContentValues] = useState<StringValue[]>([]);

const { params } = route; // Destructure params from the route object
const [emergency, setEmergency] = useState<Emergency>();

useEffect(() => {
async function loadFont() {
Expand All @@ -27,20 +62,37 @@ export default function ConditionsSection() {
void loadFont();
}, []);

type BulletListProps = {
items: string[];
};
useEffect(() => {
// if (params?.emergencyObjectId) {
// // Check if params and emergencyObjectId exist
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
// getEmergency(params.emergencyObjectId).then((result) => {
// if (result.success) {
// setEmergency(result.data);
// } else {
// console.error("Error fetching emergency data:", result.error);
// }
// });
// }
setEmergency(params.emergency);
}, [params]); // Include params in the dependency array

useEffect(() => {
if (emergency?.overview && typeof emergency.overview === "object") {
setOverviewHeaders(Object.keys(emergency.overview));
setOverviewValues(Object.values(emergency.overview) as StringValue[]);
}

const BulletList = ({ items }: BulletListProps) => (
<View style={styles.list}>
{items.map((item: string, index: number) => (
<View key={index} style={styles.listItem}>
<Text style={styles.bullet}>{"\u2022"}</Text>
<Text style={styles.itemText}>{item}</Text>
</View>
))}
</View>
);
if (emergency?.treatment && typeof emergency.treatment === "object") {
setTreatmentHeaders(Object.keys(emergency.treatment));
setTreatmentValues(Object.values(emergency.treatment) as StringValue[]);
}

if (emergency?.content && typeof emergency.content === "object") {
setContentHeaders(Object.keys(emergency.content));
setContentValues(Object.values(emergency.content) as StringValue[]);
}
}, [emergency]);

function onOverviewPress() {
if (!isOverviewPressed) {
Expand All @@ -67,7 +119,7 @@ export default function ConditionsSection() {
<Image style={styles.image} source={require("./assets/ic_caretleft.png")} />
<View style={styles.margin}>
<Text style={styles.subtitle}>Medical Emergency</Text>
<Text style={styles.title}>Cervical Spine Injury</Text>
{emergency && <Text style={styles.title}>{emergency.title}</Text>}
</View>

<View style={styles.menu}>
Expand All @@ -91,69 +143,58 @@ export default function ConditionsSection() {

<View style={styles.information}>
<View style={isOverviewPressed ? styles.overview : styles.overviewHidden}>
<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Importance</Text>
<Text style={styles.descriptionInfo}>
The cervical spine is not stabilized or protected by ribs or other surrounding
structures, so fractures are more common and can be unstable. This creates risk for
potential damage to the spinal cord resulting in quadriplegia and death and could be
made worse by moving patients without proper immobilization
</Text>
</View>

<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Mechanism of Injury</Text>
<BulletList
items={[
"Direct blow to head/neck",
"Axial loading to spine, esp. w/neck in flexion",
]}
/>
</View>

<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Diagnosis</Text>
<BulletList items={["Local pain", "Ecchymosis, and swelling"]} />
</View>

<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Physical Exam</Text>
<BulletList items={["TTP over spinous process or vertebral bodies"]} />
</View>
{emergency?.overview && typeof emergency.overview === "string" && (
<View style={styles.infoSection}>
<Text style={styles.descriptionInfo}>{emergency?.overview}</Text>
</View>
)}

{emergency?.overview && typeof emergency.overview === "object" && (
<View style={styles.infoSection}>
{overviewHeaders.map((header, index) => (
<View key={index}>
<Text style={styles.descriptionTitle}>{header}</Text>
<StringRenderer data={overviewValues[index]} />
</View>
))}
</View>
)}
</View>

<View style={isTreatmentPressed ? styles.howToTreat : styles.howToTreatHidden}>
<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Acute Management</Text>
<BulletList
items={[
"Immobilize with spine board, cervical-collar, and barriers to lateral head movement (or whole body vacuum splint)",
"If this is not available, immobilize by placing hands on patient shoulders and using forearms to immobilize head",
]}
/>
</View>

<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Dispo</Text>
<BulletList items={["Emergency transport to ED for CT (most accurate) +/- XR"]} />
</View>

<View style={styles.infoSection}>
<Text style={styles.descriptionTitle}>Considerations</Text>
<Text style={styles.descriptionInfo}>
If any suspicion for injury, send to ED. However, less likely if the following
criteria are met:
</Text>
<BulletList
items={[
"No cervical spine tenderness",
"Normal alertness/consciousness/GCS 15",
"No major distracting injuries",
"Normal neurologic status (full strength/sensation in all extremities)",
"Ability to actively rotate neck to 45 degrees laterally in both directions",
]}
/>
</View>
{emergency?.treatment && typeof emergency.treatment === "string" && (
<View style={styles.infoSection}>
<Text style={styles.descriptionInfo}>{emergency?.treatment}</Text>
</View>
)}

{emergency?.treatment && typeof emergency.treatment === "object" && (
<View style={styles.infoSection}>
{treatmentHeaders.map((header, index) => (
<View key={index}>
<Text style={styles.descriptionTitle}>{header}</Text>
<StringRenderer data={treatmentValues[index]} />
</View>
))}
</View>
)}

{emergency?.content && typeof emergency.content === "string" && (
<View style={styles.infoSection}>
<Text style={styles.descriptionInfo}>{emergency?.content}</Text>
</View>
)}

{emergency?.content && typeof emergency.content === "object" && (
<View style={styles.infoSection}>
{contentHeaders.map((header, index) => (
<View key={index}>
<Text style={styles.descriptionTitle}>{header}</Text>
<StringRenderer data={contentValues[index]} />
</View>
))}
</View>
)}
</View>
</View>
</ScrollView>
Expand Down
48 changes: 48 additions & 0 deletions dfm-sideline-sidekick-app/HomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useNavigation } from "@react-navigation/native";
import { useEffect, useState } from "react";
import { Pressable, Text } from "react-native";

import { getEmergency } from "./emergencies";

export default function HomeScreen() {
const navigation = useNavigation();
const [emergency, setEmergency] = useState(null);

useEffect(() => {
async function fetchEmergency() {
try {
//It seems to work on Android and iPad
//Test Case: Cervical Spine Injury - demonstrate recursive rendering
const emergencyObjectId = "65b36d110c9c60394b37f7a1";
//Separate Test Case here: To Be Deleted Emergency - demonstrates textual rendering
//const emergencyObjectId = "65b369a8e8fe96a404d4fd6b";
//Test Case: New Emergency Placeholder Four - demonstrates blank rendering (only title in db)
//const emergencyObjectId = "65c2ef26b87b638ac61beb09";
//Test Case: Cervical Strain - demonstrates simple placeholder headers
//const emergencyObjectId = "65b36f12640d62464e0dd129";
const result = await getEmergency(emergencyObjectId);
if (result.success) {
setEmergency(result.data);
} else {
console.error("Error fetching emergency data:", result.error);
}
} catch (error) {
console.error("Error fetching emergency data:", error);
}
}

void fetchEmergency();
}, []);

const handlePress = () => {
if (emergency !== null) {
navigation.navigate("Conditions", { emergency });
}
};

return (
<Pressable onPress={handlePress}>
<Text>Navigate to ConditionsSection</Text>
</Pressable>
);
}
43 changes: 43 additions & 0 deletions dfm-sideline-sidekick-app/components/StringRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import { Text, View } from "react-native";

import styles from "../ConditionSectionStyles";

type StringValue = string | string[] | { [key: string]: StringValue } | undefined;

type Props = {
data: StringValue;
};

const BulletList: React.FC<{ items: string[] }> = ({ items }) => (
<View style={styles.list}>
{items.map((item: string, index: number) => (
<View key={index} style={styles.listItem}>
<Text style={styles.bullet}>{"\u2022"}</Text>
<Text style={styles.itemText}>{item}</Text>
</View>
))}
</View>
);

const StringRenderer: React.FC<Props> = ({ data }) => {
if (typeof data === "string") {
return <Text style={styles.descriptionInfo}>{data}</Text>;
} else if (Array.isArray(data) && data.every((item) => typeof item === "string")) {
return <BulletList items={data} />;
} else if (typeof data === "object") {
return (
<View>
{Object.keys(data).map((key, index) => (
<View key={index}>
<Text style={styles.descriptionTitle}>{key}</Text>
<StringRenderer data={data[key] as StringValue} />
</View>
))}
</View>
);
}
return null;
};

export default StringRenderer;
Loading

0 comments on commit 544cc09

Please sign in to comment.