Tones/app/incidents.jsx
2025-08-25 10:35:59 -04:00

421 lines
14 KiB
JavaScript

import React, { useState, useRef, useEffect } from "react";
import styled from "styled-components";
import { useCallFeed } from "@/hooks";
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)``;
function toBase64Unicode(str) {
return btoa(
new TextEncoder()
.encode(str)
.reduce((data, byte) => data + String.fromCharCode(byte), "")
);
}
export default function Incidents() {
const actionSheetRef = useRef(null);
const callFeed = useCallFeed();
const {
departments,
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?.incident?.incID}${i?.response?.serviceName}` ===
`${item?.incident?.incID}${item?.response?.serviceName}`
);
})
);
})
?.map((item) => {
return { ...item, timestamp: item?.incident?.incDate };
}) || [];
const {
departmentTypeMap,
accountDetails,
deptList,
setDeptList,
selectedDepartment,
setSelectedDepartment,
updateSelectedDepartment,
selectedDepartmentColorPicker,
} = departments;
return (
<React.Fragment>
<PageHeader
centerHeader={
<View style={{ flex: 1, alignItems: "center" }}>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 },
shadowColor: "#333",
shadowOpacity: 0.8,
shadowRadius: 2,
paddingHorizontal: 10,
paddingVertical: 2,
}}
onPress={() => {
actionSheetRef.current?.show();
}}
>
<Text
style={{
color: selectedDepartmentColorPicker(
selectedDepartment?.type
),
fontWeight: 600,
fontSize: 14,
}}
>
{selectedDepartment?.deptAbv}
</Text>
</TouchableOpacity>
</View>
}
/>
<ScrollView>
<StatusBar style="dark" />
<SafeAreaView />
<View style={{ flexDirection: "column", padding: 20 }}>
{sortedAndFilteredCalls?.length ? (
sortedAndFilteredCalls?.map((callItem, index) => {
const { incident, address, response, timestamp } = callItem;
const {
serviceNumber,
incDate,
incNature,
incNatureCode,
incNatureCodeDesc,
status,
} = incident;
const {
streetAddress,
addressApartment,
town,
state,
locationName,
} = address;
const { serviceName } = response;
const SelectedIcon =
callIconMap[incNature] || AccidentAndEmergency;
return (
<TouchableOpacity
key={`callDetails - ${timestamp}`}
style={{ paddingBottom: 15 }}
onPress={() => {
router.push({
pathname: "/call",
params: {
callDetails: toBase64Unicode(JSON.stringify(callItem)),
},
});
}}
>
<View
style={{
borderRadius: 12,
elevation: 3,
backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 },
shadowColor: callColorSelector(
incNatureCodeDesc,
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" }}
>
<SelectedIcon
color={callColorSelector(
incNatureCodeDesc,
incNature,
status
)}
opacity={0.3}
width={56}
height={56}
/>
<View
style={{
flex: 1,
flexDirection: "column",
alignItems: "flex-start",
paddingHorizontal: 2,
maxWidth: "80%",
}}
>
{locationName ? (
<Text
style={{
color: "black",
fontWeight: 600,
fontSize: 16,
}}
>
{`${locationName}`}
</Text>
) : (
<View style={{ flexDirection: "row" }}>
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
{`${streetAddress?.split(",")[0]}`}
</Text>
{addressApartment ? (
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
{` - ${addressApartment}`}
</Text>
) : null}
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
{` ${town}, ${state}`}
</Text>
</View>
)}
<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={{
paddingTop: 5,
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
style={{
flexDirection: "column",
height: 100,
alignItems: "center",
justifyContent: "flex-start",
paddingTop: 7,
}}
/>
</PageFooter>
<DepartmentActionSheet
ref={actionSheetRef}
gestureEnabled
containerStyle={{
height: "50%",
width: "100%",
backgroundColor: "#ECEDEE",
}}
>
<View style={{ flexDirection: "column", padding: 20 }}>
{deptList?.map((item) => {
return (
<View style={{ padding: 2 }} key={item?.deptId}>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: item?.selected ? "grey" : "#fff",
shadowOffset: { width: 1, height: 1 },
shadowColor: "#333",
shadowOpacity: 0.3,
shadowRadius: 2,
marginHorizontal: 20,
marginVertical: 6,
padding: 10,
}}
onPress={() => {
actionSheetRef.current?.hide();
return updateSelectedDepartment(
selectedDepartment?.deptId,
item?.deptId
);
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text
style={{
color: selectedDepartmentColorPicker(item?.type),
fontWeight: 600,
fontSize: 16,
}}
>
{item?.dept}
</Text>
{item?.primary ? (
<Ionicons
name="star"
size={16}
color="yellow"
style={{
paddingLeft: 20,
shadowColor: "#333",
shadowOffset: 1,
shadowOpacity: 1,
shadowRadius: 6,
}}
/>
) : null}
</View>
<Text>{`${item?.deptAbv} - ${
departmentTypeMap[item?.type]
}`}</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</DepartmentActionSheet>
</React.Fragment>
);
}