Feature/callpage #18
3 changed files with 218 additions and 93 deletions
|
|
@ -1,12 +1,16 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useCallFeed } from '../hooks/useCallFeed';
|
import { useCallFeed } from '../hooks/useCallFeed';
|
||||||
|
import { router } from 'expo-router';
|
||||||
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
|
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import {
|
import {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
PageFooter,
|
PageFooter,
|
||||||
} from '../components/generalHelpers.jsx';
|
} from '../components/generalHelpers.jsx';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { AccidentAndEmergency } from "healthicons-react-native/dist/outline";
|
||||||
import ActionSheet from 'react-native-actions-sheet';
|
import ActionSheet from 'react-native-actions-sheet';
|
||||||
|
|
||||||
const DepartmentActionSheet = styled(ActionSheet)``;
|
const DepartmentActionSheet = styled(ActionSheet)``;
|
||||||
|
|
@ -20,8 +24,18 @@ export default function Incidents() {
|
||||||
callIconMap,
|
callIconMap,
|
||||||
callDetails,
|
callDetails,
|
||||||
callColorSelector,
|
callColorSelector,
|
||||||
|
formatCallTimePast,
|
||||||
|
formatCallDateTime
|
||||||
} = callFeed;
|
} = callFeed;
|
||||||
|
|
||||||
|
const sortedAndFilteredCalls = callDetails
|
||||||
|
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
||||||
|
.filter((item, index, self) => {
|
||||||
|
return index === self.findIndex(i => {
|
||||||
|
return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
departmentTypeMap,
|
departmentTypeMap,
|
||||||
accountDetails,
|
accountDetails,
|
||||||
|
|
@ -33,55 +47,6 @@ export default function Incidents() {
|
||||||
selectedDepartmentColorPicker,
|
selectedDepartmentColorPicker,
|
||||||
} = departments;
|
} = departments;
|
||||||
|
|
||||||
const { Incident, Address, Person, Response } = callDetails;
|
|
||||||
const { CallThemes, InitList } = accountDetails;
|
|
||||||
const {
|
|
||||||
IncID,
|
|
||||||
IncNumber,
|
|
||||||
JurisdictionNumber,
|
|
||||||
ServiceNumber,
|
|
||||||
ServiceID,
|
|
||||||
IncDate,
|
|
||||||
IncNature,
|
|
||||||
IncNatureCode,
|
|
||||||
IncNatureCodeDesc,
|
|
||||||
Notes,
|
|
||||||
Status,
|
|
||||||
Origin,
|
|
||||||
} = Incident;
|
|
||||||
const {
|
|
||||||
StreetAddress,
|
|
||||||
AddressApartment,
|
|
||||||
Town,
|
|
||||||
State,
|
|
||||||
ZipCode,
|
|
||||||
Latitude,
|
|
||||||
Longitude,
|
|
||||||
County,
|
|
||||||
Intersection1,
|
|
||||||
Intersection2,
|
|
||||||
LocationName,
|
|
||||||
WeatherCondition,
|
|
||||||
} = Address;
|
|
||||||
const {
|
|
||||||
Name,
|
|
||||||
Age,
|
|
||||||
Gender,
|
|
||||||
Statement,
|
|
||||||
Conscious,
|
|
||||||
Breathing,
|
|
||||||
CallBackNumber,
|
|
||||||
} = Person;
|
|
||||||
const {
|
|
||||||
Units
|
|
||||||
} = Response;
|
|
||||||
const {
|
|
||||||
CriticalCallList,
|
|
||||||
HighCallList,
|
|
||||||
MediumCallList,
|
|
||||||
LowCallList,
|
|
||||||
} = CallThemes;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
|
|
@ -123,7 +88,148 @@ export default function Incidents() {
|
||||||
</View>
|
</View>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<SafeAreaView />
|
||||||
|
<View style={{ flexDirection: 'column', padding: 20 }}>
|
||||||
|
{sortedAndFilteredCalls?.length ? (
|
||||||
|
sortedAndFilteredCalls?.map((callItem, index) => {
|
||||||
|
const { data: call, timestamp } = callItem;
|
||||||
|
const { Incident, Response } = call;
|
||||||
|
const {
|
||||||
|
ServiceNumber,
|
||||||
|
IncDate,
|
||||||
|
IncNature,
|
||||||
|
IncNatureCode,
|
||||||
|
IncNatureCodeDesc,
|
||||||
|
Status,
|
||||||
|
} = Incident;
|
||||||
|
const {
|
||||||
|
ServiceName
|
||||||
|
} = Response;
|
||||||
|
const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency;
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={`callDetails${index}`}
|
||||||
|
style={{ padding: 2 }}
|
||||||
|
onPress={() => {
|
||||||
|
router.push({
|
||||||
|
pathname: '/landing',
|
||||||
|
params: {
|
||||||
|
callDetails: btoa(JSON.stringify(call))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
borderRadius: 12,
|
||||||
|
elevation: 3,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
shadowOffset: { width: 0, height: 0 },
|
||||||
|
shadowColor: callColorSelector(
|
||||||
|
IncNatureCode,
|
||||||
|
IncNature,
|
||||||
|
Status
|
||||||
|
),
|
||||||
|
shadowOpacity: 1,
|
||||||
|
shadowRadius: 5,
|
||||||
|
padding: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ flexDirection: 'column' }}>
|
||||||
|
<View key="callDateAndTime"
|
||||||
|
style={{
|
||||||
|
paddingBottom: 6,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{ fontSize: 12 }}>{formatCallDateTime(IncDate)}</Text>
|
||||||
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||||
|
<Text style={{ fontSize: 12 }}>{formatCallTimePast(IncDate)}</Text>
|
||||||
|
{Status !== 'CLOSED' ? (
|
||||||
|
<Ionicons
|
||||||
|
name="lock-closed-outline"
|
||||||
|
color='red'
|
||||||
|
style={{
|
||||||
|
shadowColor: 'black',
|
||||||
|
shadowOffset: 0,
|
||||||
|
shadowOpacity: 1,
|
||||||
|
shadowRadius: 10
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
<SelectedIcon
|
||||||
|
color={callColorSelector(
|
||||||
|
IncNatureCode,
|
||||||
|
IncNature,
|
||||||
|
Status
|
||||||
|
)}
|
||||||
|
opacity={0.3}
|
||||||
|
width={56}
|
||||||
|
height={56}
|
||||||
|
/>
|
||||||
|
<View style={{ flexDirection: 'column' }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: 'black',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: 16
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`${IncNature}`}
|
||||||
|
</Text>
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: 'black',
|
||||||
|
fontSize: 12,
|
||||||
|
textShadowColor: callColorSelector(
|
||||||
|
IncNatureCode,
|
||||||
|
IncNature,
|
||||||
|
Status
|
||||||
|
),
|
||||||
|
textShadowRadius: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`${IncNatureCodeDesc}`}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={{ padding: 0 }}>
|
||||||
|
<View>
|
||||||
|
<Ionicons name="chevron-forward-outline" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Service: {ServiceName}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
textAlign: 'right'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`Incident #: ${ServiceNumber}`}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
})) : (
|
||||||
|
<Text>There are no Calls</Text>
|
||||||
|
) }
|
||||||
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<PageFooter>
|
<PageFooter>
|
||||||
<View
|
<View
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } 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 } 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';
|
||||||
|
|
@ -16,14 +17,16 @@ import ActionSheet from 'react-native-actions-sheet';
|
||||||
const DepartmentActionSheet = styled(ActionSheet)``;
|
const DepartmentActionSheet = styled(ActionSheet)``;
|
||||||
|
|
||||||
export default function Landing() {
|
export default function Landing() {
|
||||||
|
const { callDetails } = useLocalSearchParams();
|
||||||
const actionSheetRef = useRef(null);
|
const actionSheetRef = useRef(null);
|
||||||
const callFeed = useCallFeed();
|
const callFeed = useCallFeed();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
departments,
|
departments,
|
||||||
callIconMap,
|
callIconMap,
|
||||||
callDetails,
|
|
||||||
callColorSelector,
|
callColorSelector,
|
||||||
|
formatCallTimePast,
|
||||||
|
formatCallDateTime
|
||||||
} = callFeed;
|
} = callFeed;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -36,7 +39,9 @@ export default function Landing() {
|
||||||
deptList,
|
deptList,
|
||||||
} = departments;
|
} = departments;
|
||||||
|
|
||||||
const { Incident, Address, Person, Response } = callDetails;
|
const decoded = atob(callDetails);
|
||||||
|
|
||||||
|
const { Incident, Address, Person, Response } = JSON.parse(decoded);
|
||||||
const { CallThemes } = accountDetails;
|
const { CallThemes } = accountDetails;
|
||||||
const {
|
const {
|
||||||
IncID,
|
IncID,
|
||||||
|
|
@ -101,45 +106,6 @@ export default function Landing() {
|
||||||
return filterItem;
|
return filterItem;
|
||||||
});;
|
});;
|
||||||
|
|
||||||
const formatCallTimePast = (callValue) => {
|
|
||||||
const initDate = new Date(callValue);
|
|
||||||
const currentTime = new Date();
|
|
||||||
|
|
||||||
const timeDifference = currentTime - initDate;
|
|
||||||
|
|
||||||
const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
|
|
||||||
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
||||||
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
|
|
||||||
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
|
|
||||||
if (days && days !== 0) {
|
|
||||||
return `${days} day${days === 1 ? '' : 's'} ago`;
|
|
||||||
} else if (hours && hours !== 0) {
|
|
||||||
return `${hours} hour${hours === 1 ? '' : 's'} ago`;
|
|
||||||
} else if (minutes && minutes !== 0) {
|
|
||||||
return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
|
|
||||||
} else if (seconds && seconds !== 0) {
|
|
||||||
return `${seconds} second${seconds === 1 ? '' : 's'} ago`;
|
|
||||||
}
|
|
||||||
return `Unknown Time Past`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatCallDateTime = (callValue) => {
|
|
||||||
const initDate = new Date(callValue);
|
|
||||||
if (initDate) {
|
|
||||||
const formattedDate = `${initDate.toLocaleDateString('en-US', {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric',
|
|
||||||
})}`;
|
|
||||||
const hours = initDate.getHours().toString().padStart(2, '0');
|
|
||||||
const minutes = initDate.getMinutes().toString().padStart(2, '0');
|
|
||||||
const formattedTime = `${hours}:${minutes}`;
|
|
||||||
|
|
||||||
return `${formattedDate} - ${formattedTime}`;
|
|
||||||
}
|
|
||||||
return 'Date Unavailable';
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatResponseTimes = (time) => {
|
const formatResponseTimes = (time) => {
|
||||||
if (time === null) {
|
if (time === null) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useDepartments } from '../useDepartments';
|
import { useDepartments } from '../useDepartments';
|
||||||
import { Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline";
|
import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline";
|
||||||
|
import { useWebSocketContext } from '../useWebSocketContext';
|
||||||
|
|
||||||
const callIconMap = {
|
const callIconMap = {
|
||||||
"CHEST PAIN|HEART PROBLEMS": Cardiology,
|
"CHEST PAIN|HEART PROBLEMS": Cardiology,
|
||||||
|
|
@ -149,8 +150,49 @@ const callDetails = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatCallTimePast = (callValue) => {
|
||||||
|
const initDate = new Date(callValue);
|
||||||
|
const currentTime = new Date();
|
||||||
|
|
||||||
|
const timeDifference = currentTime - initDate;
|
||||||
|
|
||||||
|
const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
|
||||||
|
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
|
||||||
|
if (days && days !== 0) {
|
||||||
|
return `${days} day${days === 1 ? '' : 's'} ago`;
|
||||||
|
} else if (hours && hours !== 0) {
|
||||||
|
return `${hours} hour${hours === 1 ? '' : 's'} ago`;
|
||||||
|
} else if (minutes && minutes !== 0) {
|
||||||
|
return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
|
||||||
|
} else if (seconds && seconds !== 0) {
|
||||||
|
return `${seconds} second${seconds === 1 ? '' : 's'} ago`;
|
||||||
|
}
|
||||||
|
return `Unknown Time Past`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatCallDateTime = (callValue) => {
|
||||||
|
const initDate = new Date(callValue);
|
||||||
|
if (initDate) {
|
||||||
|
const formattedDate = `${initDate.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})}`;
|
||||||
|
const hours = initDate.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = initDate.getMinutes().toString().padStart(2, '0');
|
||||||
|
const formattedTime = `${hours}:${minutes}`;
|
||||||
|
|
||||||
|
return `${formattedDate} - ${formattedTime}`;
|
||||||
|
}
|
||||||
|
return 'Date Unavailable';
|
||||||
|
}
|
||||||
|
|
||||||
export const useCallFeed = () => {
|
export const useCallFeed = () => {
|
||||||
const departments = useDepartments();
|
const departments = useDepartments();
|
||||||
|
const { lastMessage } = useWebSocketContext();
|
||||||
|
const [allCalls, setAllCalls] = useState([]);
|
||||||
const { CallThemes } = departments?.accountDetails;
|
const { CallThemes } = departments?.accountDetails;
|
||||||
const {
|
const {
|
||||||
CriticalCallList,
|
CriticalCallList,
|
||||||
|
|
@ -159,6 +201,15 @@ export const useCallFeed = () => {
|
||||||
LowCallList,
|
LowCallList,
|
||||||
} = CallThemes;
|
} = CallThemes;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lastMessage) {
|
||||||
|
const parsedMessage = JSON?.parse(lastMessage);
|
||||||
|
if (parsedMessage?.data) {
|
||||||
|
setAllCalls([...allCalls, parsedMessage]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [lastMessage]);
|
||||||
|
|
||||||
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
|
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
|
||||||
if (status === 'CLOSED') {
|
if (status === 'CLOSED') {
|
||||||
return '#0000CD';
|
return '#0000CD';
|
||||||
|
|
@ -177,7 +228,9 @@ export const useCallFeed = () => {
|
||||||
return {
|
return {
|
||||||
departments,
|
departments,
|
||||||
callIconMap,
|
callIconMap,
|
||||||
callDetails,
|
callDetails: allCalls,
|
||||||
callColorSelector,
|
callColorSelector,
|
||||||
|
formatCallTimePast,
|
||||||
|
formatCallDateTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue