Feature/notification system #26
4 changed files with 86 additions and 63 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useCallFeed } from '../hooks/useCallFeed';
|
import { useCallFeed } from '../hooks/useCallFeed';
|
||||||
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
|
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
|
||||||
import { useLocalSearchParams, router } from 'expo-router';
|
import { useLocalSearchParams } from 'expo-router';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
|
@ -11,7 +11,7 @@ import { Phone } from "healthicons-react-native/dist/filled";
|
||||||
import {
|
import {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
PageFooter,
|
PageFooter,
|
||||||
} from '../components/generalHelpers.jsx';
|
} from '@/components/generalHelpers.jsx';
|
||||||
import ActionSheet from 'react-native-actions-sheet';
|
import ActionSheet from 'react-native-actions-sheet';
|
||||||
|
|
||||||
const DepartmentActionSheet = styled(ActionSheet)``;
|
const DepartmentActionSheet = styled(ActionSheet)``;
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,13 @@ export default function Incidents() {
|
||||||
formatCallDateTime
|
formatCallDateTime
|
||||||
} = callFeed;
|
} = callFeed;
|
||||||
|
|
||||||
const sortedAndFilteredCalls = callDetails
|
const sortedAndFilteredCalls = callDetails.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
||||||
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
|
||||||
.filter((item, index, self) => {
|
.filter((item, index, self) => {
|
||||||
return index === self.findIndex(i => {
|
return index === self.findIndex(i => {
|
||||||
return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}`
|
return `${i?.incident?.incID}${i?.response?.serviceName}` === `${item?.incident?.incID}${item?.response?.serviceName}`
|
||||||
});
|
});
|
||||||
});
|
})?.map(item => { return {...item, timestamp: item?.incident?.incDate} })
|
||||||
|
|| [];
|
||||||
|
|
||||||
const {
|
const {
|
||||||
departmentTypeMap,
|
departmentTypeMap,
|
||||||
|
|
@ -93,31 +93,30 @@ export default function Incidents() {
|
||||||
<View style={{ flexDirection: 'column', padding: 20 }}>
|
<View style={{ flexDirection: 'column', padding: 20 }}>
|
||||||
{sortedAndFilteredCalls?.length ? (
|
{sortedAndFilteredCalls?.length ? (
|
||||||
sortedAndFilteredCalls?.map((callItem, index) => {
|
sortedAndFilteredCalls?.map((callItem, index) => {
|
||||||
const { data: call, timestamp } = callItem;
|
const { incident, address, response, timestamp } = callItem;
|
||||||
const { Incident, Address, Response } = call;
|
|
||||||
const {
|
const {
|
||||||
ServiceNumber,
|
serviceNumber,
|
||||||
IncDate,
|
incDate,
|
||||||
IncNature,
|
incNature,
|
||||||
IncNatureCode,
|
incNatureCode,
|
||||||
IncNatureCodeDesc,
|
incNatureCodeDesc,
|
||||||
Status,
|
status,
|
||||||
} = Incident;
|
} = incident;
|
||||||
const {
|
const {
|
||||||
StreetAddress,
|
streetAddress,
|
||||||
AddressApartment,
|
addressApartment,
|
||||||
Town,
|
town,
|
||||||
State,
|
state,
|
||||||
LocationName,
|
locationName,
|
||||||
} = Address;
|
} = address;
|
||||||
const {
|
const {
|
||||||
ServiceName
|
serviceName
|
||||||
} = Response;
|
} = response;
|
||||||
const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency;
|
const SelectedIcon = callIconMap[incNature] || AccidentAndEmergency;
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={`callDetails - ${timestamp}`}
|
key={`callDetails - ${timestamp}`}
|
||||||
style={{ padding: 2 }}
|
style={{ paddingBottom: 15 }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: '/call',
|
pathname: '/call',
|
||||||
|
|
@ -134,9 +133,9 @@ export default function Incidents() {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
shadowOffset: { width: 0, height: 0 },
|
shadowOffset: { width: 0, height: 0 },
|
||||||
shadowColor: callColorSelector(
|
shadowColor: callColorSelector(
|
||||||
IncNatureCode,
|
incNatureCodeDesc,
|
||||||
IncNature,
|
incNature,
|
||||||
Status
|
status
|
||||||
),
|
),
|
||||||
shadowOpacity: 1,
|
shadowOpacity: 1,
|
||||||
shadowRadius: 5,
|
shadowRadius: 5,
|
||||||
|
|
@ -151,10 +150,10 @@ export default function Incidents() {
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={{ fontSize: 12 }}>{formatCallDateTime(IncDate)}</Text>
|
<Text style={{ fontSize: 12 }}>{formatCallDateTime(incDate)}</Text>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||||
<Text style={{ fontSize: 12 }}>{formatCallTimePast(IncDate)}</Text>
|
<Text style={{ fontSize: 12 }}>{formatCallTimePast(incDate)}</Text>
|
||||||
{Status === 'CLOSED' ? (
|
{status === 'CLOSED' ? (
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="lock-closed-outline"
|
name="lock-closed-outline"
|
||||||
color='red'
|
color='red'
|
||||||
|
|
@ -171,16 +170,16 @@ export default function Incidents() {
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<SelectedIcon
|
<SelectedIcon
|
||||||
color={callColorSelector(
|
color={callColorSelector(
|
||||||
IncNatureCode,
|
incNatureCodeDesc,
|
||||||
IncNature,
|
incNature,
|
||||||
Status
|
status
|
||||||
)}
|
)}
|
||||||
opacity={0.3}
|
opacity={0.3}
|
||||||
width={56}
|
width={56}
|
||||||
height={56}
|
height={56}
|
||||||
/>
|
/>
|
||||||
<View style={{ flexDirection: 'column' }}>
|
<View style={{ flexDirection: 'column' }}>
|
||||||
{LocationName ? (
|
{locationName ? (
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
color: 'black',
|
color: 'black',
|
||||||
|
|
@ -188,7 +187,7 @@ export default function Incidents() {
|
||||||
fontSize: 16
|
fontSize: 16
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`${LocationName}`}
|
{`${locationName}`}
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<View style={{ flexDirection: 'row' }}>
|
<View style={{ flexDirection: 'row' }}>
|
||||||
|
|
@ -199,9 +198,9 @@ export default function Incidents() {
|
||||||
fontWeight: 600
|
fontWeight: 600
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
{`${StreetAddress}`}
|
{`${streetAddress?.split(',')[0]}`}
|
||||||
</Text>
|
</Text>
|
||||||
{AddressApartment ? (
|
{addressApartment ? (
|
||||||
<Text
|
<Text
|
||||||
style={[{
|
style={[{
|
||||||
color: 'black',
|
color: 'black',
|
||||||
|
|
@ -209,7 +208,7 @@ export default function Incidents() {
|
||||||
fontWeight: 600
|
fontWeight: 600
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
{` - ${AddressApartment}`}
|
{` - ${addressApartment}`}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
<Text
|
<Text
|
||||||
|
|
@ -219,7 +218,7 @@ export default function Incidents() {
|
||||||
fontWeight: 600
|
fontWeight: 600
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
{` ${Town}, ${State}`}
|
{` ${town}, ${state}`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
@ -230,7 +229,7 @@ export default function Incidents() {
|
||||||
fontSize: 16
|
fontSize: 16
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`${IncNature}`}
|
{`${incNature}`}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
<Text
|
<Text
|
||||||
|
|
@ -238,14 +237,14 @@ export default function Incidents() {
|
||||||
color: 'black',
|
color: 'black',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
textShadowColor: callColorSelector(
|
textShadowColor: callColorSelector(
|
||||||
IncNatureCode,
|
incNatureCode,
|
||||||
IncNature,
|
incNature,
|
||||||
Status
|
status
|
||||||
),
|
),
|
||||||
textShadowRadius: 1
|
textShadowRadius: 1
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`${IncNatureCodeDesc}`}
|
{`${incNatureCodeDesc}`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -261,7 +260,7 @@ export default function Incidents() {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Service: {ServiceName}
|
Service: {serviceName}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -269,7 +268,7 @@ export default function Incidents() {
|
||||||
textAlign: 'right'
|
textAlign: 'right'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`Incident #: ${ServiceNumber}`}
|
{`Incident #: ${serviceNumber}`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -86,16 +86,16 @@ const formatCallDateTime = (callValue) => {
|
||||||
const getIncidents = async (departments, incidentStatus) => {
|
const getIncidents = async (departments, incidentStatus) => {
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${process.env.EXPO_PUBLIC_DATABASE_URL}/api/getRecentCalls`, {
|
response = await fetch(`${process.env.EXPO_PUBLIC_DATABASE_URL}/getRecentCalls/${5}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
|
||||||
apiKey: process.env.EXPO_PUBLIC_DATABASE_API_KEY
|
apiKey: process.env.EXPO_PUBLIC_DATABASE_API_KEY
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
// body: JSON.stringify({
|
||||||
departments,
|
// callCount: 25,
|
||||||
status: incidentStatus,
|
// departments,
|
||||||
}),
|
// status: incidentStatus,
|
||||||
|
// }),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to fetch initial calls:", e);
|
console.error("Failed to fetch initial calls:", e);
|
||||||
|
|
@ -114,9 +114,12 @@ export const useCallFeed = () => {
|
||||||
const [selectedStatus, setSelectedStatus] = useState({id: 'all', value: 'All Incidents'});
|
const [selectedStatus, setSelectedStatus] = useState({id: 'all', value: 'All Incidents'});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const incidents = getIncidents(InitList?.map((dept) => { return dept?.id }), selectedStatus?.id);
|
async function fetchData() {
|
||||||
setAllCalls(incidents);
|
const incidents = await getIncidents(InitList?.map((dept) => { return dept?.id }), selectedStatus?.id);
|
||||||
setInit(false);
|
setAllCalls(incidents);
|
||||||
|
setInit(false);
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -129,14 +132,17 @@ export const useCallFeed = () => {
|
||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!init) {
|
async function fetchData() {
|
||||||
const incidents = getIncidents(InitList?.map((dept) => { return dept?.id }), selectedStatus?.id);
|
const incidents = await getIncidents(InitList?.map((dept) => { return dept?.id }), selectedStatus?.id);
|
||||||
setAllCalls(incidents);
|
setAllCalls(incidents);
|
||||||
}
|
}
|
||||||
|
if (!init) {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
}, [selectedStatus]);
|
}, [selectedStatus]);
|
||||||
|
|
||||||
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
|
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
|
||||||
if (status === "CLOSED") {
|
if (status?.toLowerCase() === "closed") {
|
||||||
return "#0000CD";
|
return "#0000CD";
|
||||||
} else if (CriticalCallList.includes(cardiacArrestCall)) {
|
} else if (CriticalCallList.includes(cardiacArrestCall)) {
|
||||||
return "#8B0000";
|
return "#8B0000";
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,32 @@ const accountDetails = {
|
||||||
"CARDIAC ARREST|DEATH",
|
"CARDIAC ARREST|DEATH",
|
||||||
],
|
],
|
||||||
"HighCallList": [
|
"HighCallList": [
|
||||||
"ALS"
|
"EMS ALS PRIORITY (ALS)",
|
||||||
|
"EMS Advanced Life Support life threatening response (ALS)",
|
||||||
|
"EMS ALS Intercept Required",
|
||||||
|
"EMS ALS Response – Cardiac Related",
|
||||||
|
"EMS ALS Response – Respiratory Distress",
|
||||||
|
"EMS ALS Response – Stroke Alert",
|
||||||
|
"EMS Basic Life Support with Paramedic Assist (BLS)",
|
||||||
|
"EMS ALS Tiered Response with Fire",
|
||||||
|
"EMS Response – Behavioral Emergency (ALS)",
|
||||||
|
"EMS Response – Obstetric Emergency (ALS)",
|
||||||
|
"EMS ALS Response – Overdose / Poisoning",
|
||||||
|
"EMS ALS Priority Interfacility Transfer",
|
||||||
|
"EMS Response – Unresponsive / Unconscious Patient (ALS)"
|
||||||
],
|
],
|
||||||
"MediumCallList": [
|
"MediumCallList": [
|
||||||
"ALS-STANDARD",
|
"ALS-STANDARD",
|
||||||
"BLS-PRIORITY"
|
"BLS-PRIORITY",
|
||||||
|
"EMS BLS Priority Response",
|
||||||
|
"EMS BLS Response – Fall Injury"
|
||||||
],
|
],
|
||||||
"LowCallList": [
|
"LowCallList": [
|
||||||
"BLS-STANDARD"
|
"EMS Standard Basic Life Support Response (BLS)",
|
||||||
|
"EMS BLS Non-Emergency Transport",
|
||||||
|
"EMS BLS Standby – Event Coverage",
|
||||||
|
"EMS Public Assist – Lift Only (BLS)",
|
||||||
|
"EMS BLS Scheduled Interfacility Transport"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"InitList": [
|
"InitList": [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue