This commit is contained in:
Matt DiMeglio 2025-08-25 10:35:59 -04:00
parent 64856b0695
commit 067c3e9df1

View file

@ -1,22 +1,30 @@
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'; import { useCallFeed } from "@/hooks";
import { router } from 'expo-router'; 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 { import {
PageHeader, Platform,
PageFooter, Linking,
} from '@/components/generalHelpers.jsx'; View,
import { Ionicons } from '@expo/vector-icons'; 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 { 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)``;
function toBase64Unicode(str) { function toBase64Unicode(str) {
return btoa(new TextEncoder().encode(str).reduce((data, byte) => data + String.fromCharCode(byte), '')); return btoa(
new TextEncoder()
.encode(str)
.reduce((data, byte) => data + String.fromCharCode(byte), "")
);
} }
export default function Incidents() { export default function Incidents() {
@ -29,16 +37,26 @@ export default function Incidents() {
callDetails, callDetails,
callColorSelector, callColorSelector,
formatCallTimePast, formatCallTimePast,
formatCallDateTime formatCallDateTime,
} = callFeed; } = callFeed;
const sortedAndFilteredCalls = callDetails.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)) const sortedAndFilteredCalls =
.filter((item, index, self) => { callDetails
return index === self.findIndex(i => { .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
return `${i?.incident?.incID}${i?.response?.serviceName}` === `${item?.incident?.incID}${item?.response?.serviceName}` .filter((item, index, self) => {
}); return (
})?.map(item => { return {...item, timestamp: item?.incident?.incDate} }) index ===
|| []; self.findIndex((i) => {
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,
@ -54,37 +72,43 @@ export default function Incidents() {
return ( return (
<React.Fragment> <React.Fragment>
<PageHeader <PageHeader
centerHeader={<TouchableOpacity centerHeader={
style={{ <View style={{ flex: 1, alignItems: "center" }}>
borderRadius: 6, <TouchableOpacity
elevation: 3,
backgroundColor: '#fff',
shadowOffset: { width: 0, height: 0 },
shadowColor: '#333',
shadowOpacity: .8,
shadowRadius: 2,
paddingHorizontal: 10,
paddingVertical: 2,
}}
onPress={() => {
actionSheetRef.current?.show();
}}
>
<Text
style={{ style={{
color: selectedDepartmentColorPicker(selectedDepartment?.type), borderRadius: 6,
fontWeight: 600, elevation: 3,
fontSize: 14 backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 },
shadowColor: "#333",
shadowOpacity: 0.8,
shadowRadius: 2,
paddingHorizontal: 10,
paddingVertical: 2,
}}
onPress={() => {
actionSheetRef.current?.show();
}} }}
> >
{selectedDepartment?.deptAbv} <Text
</Text> style={{
</TouchableOpacity>} color: selectedDepartmentColorPicker(
selectedDepartment?.type
),
fontWeight: 600,
fontSize: 14,
}}
>
{selectedDepartment?.deptAbv}
</Text>
</TouchableOpacity>
</View>
}
/> />
<ScrollView> <ScrollView>
<StatusBar style="dark" /> <StatusBar style="dark" />
<SafeAreaView /> <SafeAreaView />
<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 { incident, address, response, timestamp } = callItem; const { incident, address, response, timestamp } = callItem;
@ -103,28 +127,27 @@ export default function Incidents() {
state, state,
locationName, locationName,
} = address; } = address;
const { const { serviceName } = response;
serviceName const SelectedIcon =
} = response; callIconMap[incNature] || AccidentAndEmergency;
const SelectedIcon = callIconMap[incNature] || AccidentAndEmergency;
return ( return (
<TouchableOpacity <TouchableOpacity
key={`callDetails - ${timestamp}`} key={`callDetails - ${timestamp}`}
style={{ paddingBottom: 15 }} style={{ paddingBottom: 15 }}
onPress={() => { onPress={() => {
router.push({ router.push({
pathname: '/call', pathname: "/call",
params: { params: {
callDetails: toBase64Unicode(JSON.stringify(callItem)) callDetails: toBase64Unicode(JSON.stringify(callItem)),
} },
}) });
}} }}
> >
<View <View
style={{ style={{
borderRadius: 12, borderRadius: 12,
elevation: 3, elevation: 3,
backgroundColor: '#fff', backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowColor: callColorSelector( shadowColor: callColorSelector(
incNatureCodeDesc, incNatureCodeDesc,
@ -136,32 +159,41 @@ export default function Incidents() {
padding: 10, padding: 10,
}} }}
> >
<View style={{ flexDirection: 'column' }}> <View style={{ flexDirection: "column" }}>
<View key="callDateAndTime" <View
key="callDateAndTime"
style={{ style={{
paddingBottom: 6, paddingBottom: 6,
flexDirection: 'row', flexDirection: "row",
justifyContent: 'space-between' justifyContent: "space-between",
}} }}
> >
<Text style={{ fontSize: 12 }}>{formatCallDateTime(incDate)}</Text> <Text style={{ fontSize: 12 }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}> {formatCallDateTime(incDate)}
<Text style={{ fontSize: 12 }}>{formatCallTimePast(incDate)}</Text> </Text>
{status === 'CLOSED' ? ( <View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Text style={{ fontSize: 12 }}>
{formatCallTimePast(incDate)}
</Text>
{status === "CLOSED" ? (
<Ionicons <Ionicons
name="lock-closed-outline" name="lock-closed-outline"
color='red' color="red"
style={{ style={{
shadowColor: 'black', shadowColor: "black",
shadowOffset: 0, shadowOffset: 0,
shadowOpacity: 1, shadowOpacity: 1,
shadowRadius: 10 shadowRadius: 10,
}} }}
/> />
) : null} ) : null}
</View> </View>
</View> </View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}> <View
style={{ flexDirection: "row", alignItems: "center" }}
>
<SelectedIcon <SelectedIcon
color={callColorSelector( color={callColorSelector(
incNatureCodeDesc, incNatureCodeDesc,
@ -172,45 +204,59 @@ export default function Incidents() {
width={56} width={56}
height={56} height={56}
/> />
<View style={{ flex: 1, flexDirection: 'column', alignItems: 'flex-start', paddingHorizontal: 2, maxWidth: '80%' }}> <View
style={{
flex: 1,
flexDirection: "column",
alignItems: "flex-start",
paddingHorizontal: 2,
maxWidth: "80%",
}}
>
{locationName ? ( {locationName ? (
<Text <Text
style={{ style={{
color: 'black', color: "black",
fontWeight: 600, fontWeight: 600,
fontSize: 16 fontSize: 16,
}} }}
> >
{`${locationName}`} {`${locationName}`}
</Text> </Text>
) : ( ) : (
<View style={{ flexDirection: 'row' }}> <View style={{ flexDirection: "row" }}>
<Text <Text
style={[{ style={[
color: 'black', {
fontSize: 12, color: "black",
fontWeight: 600 fontSize: 12,
}]} fontWeight: 600,
},
]}
> >
{`${streetAddress?.split(',')[0]}`} {`${streetAddress?.split(",")[0]}`}
</Text> </Text>
{addressApartment ? ( {addressApartment ? (
<Text <Text
style={[{ style={[
color: 'black', {
fontSize: 12, color: "black",
fontWeight: 600 fontSize: 12,
}]} fontWeight: 600,
},
]}
> >
{` - ${addressApartment}`} {` - ${addressApartment}`}
</Text> </Text>
) : null} ) : null}
<Text <Text
style={[{ style={[
color: 'black', {
fontSize: 12, color: "black",
fontWeight: 600 fontSize: 12,
}]} fontWeight: 600,
},
]}
> >
{` ${town}, ${state}`} {` ${town}, ${state}`}
</Text> </Text>
@ -218,24 +264,29 @@ export default function Incidents() {
)} )}
<Text <Text
style={{ style={{
color: 'black', color: "black",
fontWeight: 600, fontWeight: 600,
fontSize: 16 fontSize: 16,
}} }}
> >
{`${incNature}`} {`${incNature}`}
</Text> </Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> <View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text <Text
style={{ style={{
color: 'black', color: "black",
fontSize: 12, fontSize: 12,
textShadowColor: callColorSelector( textShadowColor: callColorSelector(
incNatureCode, incNatureCode,
incNature, incNature,
status status
), ),
textShadowRadius: 1 textShadowRadius: 1,
}} }}
> >
{`${incNatureCodeDesc}`} {`${incNatureCodeDesc}`}
@ -248,7 +299,13 @@ export default function Incidents() {
</View> </View>
</View> </View>
</View> </View>
<View style={{ paddingTop: 5, flexDirection: 'row', justifyContent: 'space-between' }}> <View
style={{
paddingTop: 5,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text <Text
style={{ style={{
fontSize: 12, fontSize: 12,
@ -259,7 +316,7 @@ export default function Incidents() {
<Text <Text
style={{ style={{
fontSize: 12, fontSize: 12,
textAlign: 'right' textAlign: "right",
}} }}
> >
{`Incident #: ${serviceNumber}`} {`Incident #: ${serviceNumber}`}
@ -268,8 +325,9 @@ export default function Incidents() {
</View> </View>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
) );
})) : ( })
) : (
<Text>There are no Calls</Text> <Text>There are no Calls</Text>
)} )}
</View> </View>
@ -277,11 +335,11 @@ export default function Incidents() {
<PageFooter> <PageFooter>
<View <View
style={{ style={{
flexDirection: 'column', flexDirection: "column",
height: 100, height: 100,
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-start', justifyContent: "flex-start",
paddingTop: 7 paddingTop: 7,
}} }}
/> />
</PageFooter> </PageFooter>
@ -291,10 +349,10 @@ export default function Incidents() {
containerStyle={{ containerStyle={{
height: "50%", height: "50%",
width: "100%", width: "100%",
backgroundColor: '#ECEDEE', backgroundColor: "#ECEDEE",
}} }}
> >
<View style={{ flexDirection: 'column', padding: 20 }}> <View style={{ flexDirection: "column", padding: 20 }}>
{deptList?.map((item) => { {deptList?.map((item) => {
return ( return (
<View style={{ padding: 2 }} key={item?.deptId}> <View style={{ padding: 2 }} key={item?.deptId}>
@ -302,44 +360,56 @@ export default function Incidents() {
style={{ style={{
borderRadius: 6, borderRadius: 6,
elevation: 3, elevation: 3,
backgroundColor: item?.selected ? 'grey' : '#fff', backgroundColor: item?.selected ? "grey" : "#fff",
shadowOffset: { width: 1, height: 1 }, shadowOffset: { width: 1, height: 1 },
shadowColor: '#333', shadowColor: "#333",
shadowOpacity: 0.3, shadowOpacity: 0.3,
shadowRadius: 2, shadowRadius: 2,
marginHorizontal: 20, marginHorizontal: 20,
marginVertical: 6, marginVertical: 6,
padding: 10 padding: 10,
}} }}
onPress={() => { onPress={() => {
actionSheetRef.current?.hide(); actionSheetRef.current?.hide();
return updateSelectedDepartment( return updateSelectedDepartment(
selectedDepartment?.deptId, selectedDepartment?.deptId,
item?.deptId item?.deptId
) );
}} }}
> >
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> <View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text <Text
style={{ style={{
color: selectedDepartmentColorPicker( color: selectedDepartmentColorPicker(item?.type),
item?.type
),
fontWeight: 600, fontWeight: 600,
fontSize: 16 fontSize: 16,
}} }}
> >
{item?.dept} {item?.dept}
</Text> </Text>
{item?.primary ? <Ionicons name="star" size={16} color="yellow" style={{ {item?.primary ? (
paddingLeft: 20, <Ionicons
shadowColor: '#333', name="star"
shadowOffset: 1, size={16}
shadowOpacity: 1, color="yellow"
shadowRadius: 6 style={{
}} /> : null} paddingLeft: 20,
shadowColor: "#333",
shadowOffset: 1,
shadowOpacity: 1,
shadowRadius: 6,
}}
/>
) : null}
</View> </View>
<Text>{`${item?.deptAbv} - ${departmentTypeMap[item?.type]}`}</Text> <Text>{`${item?.deptAbv} - ${
departmentTypeMap[item?.type]
}`}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
@ -347,5 +417,5 @@ export default function Incidents() {
</View> </View>
</DepartmentActionSheet> </DepartmentActionSheet>
</React.Fragment> </React.Fragment>
) );
} }