Feature/callpage #18

Merged
mattdimegs merged 17 commits from feature/callpage into main 2025-04-19 16:48:15 +00:00
3 changed files with 218 additions and 93 deletions
Showing only changes of commit e445d8d6d5 - Show all commits

View file

@ -1,12 +1,16 @@
import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { useCallFeed } from '../hooks/useCallFeed';
import { router } from 'expo-router';
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 {
PageHeader,
PageFooter,
} from '../components/generalHelpers.jsx';
import { Ionicons } from '@expo/vector-icons';
import { AccidentAndEmergency } from "healthicons-react-native/dist/outline";
import ActionSheet from 'react-native-actions-sheet';
const DepartmentActionSheet = styled(ActionSheet)``;
@ -20,8 +24,18 @@ export default function Incidents() {
callIconMap,
callDetails,
callColorSelector,
formatCallTimePast,
formatCallDateTime
} = 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 {
departmentTypeMap,
accountDetails,
@ -33,55 +47,6 @@ export default function Incidents() {
selectedDepartmentColorPicker,
} = 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 (
<React.Fragment>
<PageHeader>
@ -123,7 +88,148 @@ export default function Incidents() {
</View>
</PageHeader>
<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>
<PageFooter>
<View

View file

@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { useCallFeed } from '../hooks/useCallFeed';
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
@ -16,14 +17,16 @@ import ActionSheet from 'react-native-actions-sheet';
const DepartmentActionSheet = styled(ActionSheet)``;
export default function Landing() {
const { callDetails } = useLocalSearchParams();
const actionSheetRef = useRef(null);
const callFeed = useCallFeed();
const {
departments,
callIconMap,
callDetails,
callColorSelector,
formatCallTimePast,
formatCallDateTime
} = callFeed;
const {
@ -36,7 +39,9 @@ export default function Landing() {
deptList,
} = departments;
const { Incident, Address, Person, Response } = callDetails;
const decoded = atob(callDetails);
const { Incident, Address, Person, Response } = JSON.parse(decoded);
const { CallThemes } = accountDetails;
const {
IncID,
@ -101,45 +106,6 @@ export default function Landing() {
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) => {
if (time === null) {
return '';

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
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 = {
"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 = () => {
const departments = useDepartments();
const { lastMessage } = useWebSocketContext();
const [allCalls, setAllCalls] = useState([]);
const { CallThemes } = departments?.accountDetails;
const {
CriticalCallList,
@ -159,6 +201,15 @@ export const useCallFeed = () => {
LowCallList,
} = CallThemes;
useEffect(() => {
if (lastMessage) {
const parsedMessage = JSON?.parse(lastMessage);
if (parsedMessage?.data) {
setAllCalls([...allCalls, parsedMessage]);
}
}
}, [lastMessage]);
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
if (status === 'CLOSED') {
return '#0000CD';
@ -177,7 +228,9 @@ export const useCallFeed = () => {
return {
departments,
callIconMap,
callDetails,
callDetails: allCalls,
callColorSelector,
formatCallTimePast,
formatCallDateTime
}
}