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

Dynamic medical emergency page #16

Merged
merged 10 commits into from
Feb 26, 2024
102 changes: 39 additions & 63 deletions dfm-sideline-sidekick-app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,51 @@
import { NavigationContainer, useNavigation } from "@react-navigation/native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { StackNavigationProp } from "@react-navigation/stack";
import { StatusBar } from "expo-status-bar";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React from "react";
import { StyleSheet } from "react-native";
// import { StatusBar } from "expo-status-bar";
// import { StyleSheet, Text, View } from "react-native";
// import { StyleSheet } from "react-native";
// import { NativeRouter, Routes, Route } from "react-router-native";

import { BottomNavBar, NavItem } from "./components/bar";
import BookmarkPage from "./pages/BookmarkPage";
import SearchPage from "./pages/SearchPage";
import TabPage from "./pages/TabPage";
import ConditionsSection from "./ConditionsSection";
import HomeScreen from "./HomeScreen";

type RootStackParamList = {
Bookmark: undefined;
Search: undefined;
Tab: undefined;
};
// In your App.tsx or where your navigation setup resides

type StackNavigation = StackNavigationProp<RootStackParamList>;
const Stack = createNativeStackNavigator<RootStackParamList>();
const Stack = createNativeStackNavigator();

const BottomNavBarComponent = () => {
const navigation = useNavigation<StackNavigation>();

const navigationItems: NavItem[] = [
{
id: 1,
icon: "bookmark",
onClick: () => {
navigation.navigate("Bookmark");
},
},
{
id: 2,
icon: "search",
onClick: () => {
navigation.navigate("Search");
},
},
{
id: 3,
icon: "principles",
onClick: () => {
navigation.navigate("Tab");
},
},
];

return <BottomNavBar items={navigationItems} />;
};

export default function App() {
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Search">
<Stack.Screen name="Bookmark" component={BookmarkPage} options={{ headerShown: false }} />
<Stack.Screen name="Search" component={SearchPage} options={{ headerShown: false }} />
<Stack.Screen name="Tab" component={TabPage} options={{ headerShown: false }} />
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Conditions" component={ConditionsSection} />
</Stack.Navigator>
<BottomNavBarComponent />
<StatusBar style="auto" />
</NavigationContainer>
);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
export default App;

// export default function App() {
// return (
// // eslint-disable-next-line @typescript-eslint/no-use-before-define
// <View style={styles.container}>
// {/* <Text>Open up App.js to start working on your app!</Text> */}
// {/* <ConditionsSection /> */}
// <NativeRouter>
// <Routes>
// <Route exact path="/" element={<ConditionsSection />} />
// <Route path="/emergencies/:emergencyObjectId" element={<ConditionsSection />} />
// </Routes>
// </NativeRouter>
// <StatusBar style="auto" />
// </View>
// );
// }
// const styles = StyleSheet.create({
// container: {
// flex: 1,
// backgroundColor: "#fff",
// alignItems: "center",
// justifyContent: "center",
// },
// });
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great overall, make sure to get rid of any commented code. Also, note that the Condiitons Section will be showing data that is fetched from the device's storage, not from the MongoDB database. So for the scope of this pull request, just don't worry about fetching the data, and just make the data be passed in as a prop to the component. In the future, we will have a dedicated file that fetches the correct data for a specific emergency/condition, and passes it into the Conditions Section component

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, the progress you have made on emergencies.ts and the fetching will be useful later, especially for admin mode stuff, so great work and feel free to keep this! Just make sure the ConditionsSection.tsx only focuses on showing data that is passed in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes as discussed here have been addressed with the commits below by having ConditionsSection take in an Emergency as input as opposed to an id. Tests have been reconfigured accordingly.

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
Loading
Loading