import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'; import ListTalkgroups from "../Talkgroups/ListTalkgroups.js"; import MessageBox from "../Components/Message.js"; import ListGroups from "../Group/ListGroups.js"; import GroupModal from "../Group/GroupModal.js"; import ErrorChart from "./ResponsiveErrorChart" import CallChart from "./ResponsiveCallChart" import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useDeleteSystemMutation, useImportTalkgroupsMutation, useSaveGroupOrderMutation } from '../features/api/apiSlice' import { Button, Confirm, Icon, Form, TextArea, Header, Container, Message, List, Segment, Tab, Divider, Label } from "semantic-ui-react"; const System = (props) => { const { shortName } = useParams(); const navigate = useNavigate(); const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery(); const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(shortName); const { data: errorsData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName); const [deleteGroupAPI, { isLoading: isDeleting }] = useDeleteGroupMutation(); const [reorderGroupsAPI, { isLoading: isReordering }] = useSaveGroupOrderMutation(); const [importTalkgroupsAPI, { isLoading: isImporting }] = useImportTalkgroupsMutation(); const [deleteSystemAPI ] = useDeleteSystemMutation(); const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false); const [openMessage, setOpenMessage] = useState(false); const [requestMessage, setRequestMessage] = useState(""); const [groupVisible, setGroupVisible] = useState(false); const [editGroupId, setEditGroupId] = useState(false); const [errorData, setErrorData] = useState({}); const [callData, setCallData] = useState({}); const [groupOrder, setGroupOrder] = useState([]); const processErrors = (errors) => { var allData = []; var num = 0; const now = new Date(); var maxDate = now; var minDate = now; var minValue = 0; var maxValue = 0; var legend = []; for (var j = 0; j < errors.length; j++) { var data = []; for (var i = 0; i < errors[j].values.length; i++) { var time = new Date(errors[j].values[i].time); if (errors[j].values[i].errors > maxValue) maxValue = errors[j].values[i].errors; if (time < minDate) minDate = time; var value = { id: num++, y: errors[j].values[i].errors, x: time }; data.push(value); } const id = Math.floor(errors[j]._id / 1000) / 1000 + "MHz"; legend.push(id); var obj = { id: id, data: data }; allData.push(obj); } const errorData = { data: allData, minDate: minDate, maxDate: maxDate, minValue: minValue, maxValue: maxValue, legend: legend }; setErrorData(errorData); }; const processStatistics = (statistic) => { var callTotals = []; var errorTotals = []; const now = new Date(); var maxDate = now; var minDate = now; var minValue = 0; var maxValue = 0; var MS_PER_MINUTE = 60000; if (statistic) { for (var j = 0; j < statistic.callTotals.length; j++) { var spotsBack = statistic.callTotals.length - j; var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); if (time < minDate) minDate = time; if (statistic.callTotals[j] > maxValue) maxValue = statistic.callTotals[j]; callTotals.push({ y: statistic.callTotals[j], x: time }); } for (var j = 0; j < statistic.errorTotals.length; j++) { var spotsBack = statistic.errorTotals.length - j; var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); if (time < minDate) minDate = time; if (statistic.errorTotals[j] > maxValue) maxValue = statistic.errorTotals[j]; errorTotals.push({ x: time, y: statistic.errorTotals[j] }); } const callData = { minDate: minDate, maxDate: maxDate, minValue: minValue, maxValue: maxValue, callTotals: callTotals, errorTotals: errorTotals }; setCallData(callData); } } /* componentDidMount() { this.props.systemActions.fetchSystems(); this.props.talkgroupActions.fetchTalkgroups(this.props.shortName); this.props.groupActions.fetchGroups(this.props.shortName); this.props.errorActions.fetch(this.props.shortName); if (this.props.statistic) { this.processStatistics(this.props.statistic); } if (this.props.errors) { this.processErrors(this.props.errors); } } componentDidUpdate(prevProp) { // If we just received the error statistics for a system, for the first time, proccess them. if (this.props.errors && !prevProp.errors) { this.processErrors(this.props.errors); } // If we just received the call statistics for a system, for the first time, proccess them. if (this.props.statistic && !prevProp.statistic) { this.processStatistics(this.props.statistic); } } */ const editGroup = (groupId) => { setEditGroupId(groupId) setGroupVisible((state) => !state) } const deleteGroup = async (groupId) => { for (const num in groupsData) { if (groupsData[num]._id == groupId) { await deleteGroupAPI(groupsData[num]) } } } const reorderGroup = (oldIndex, newIndex) => { if ((newIndex < 0) || (newIndex > (groupOrder.length - 1))) { return } const moveOrder = groupOrder.slice(); moveOrder.splice(newIndex, 0, moveOrder.splice(oldIndex, 1)[0]); setGroupOrder(moveOrder); } const handleGroupClose = () => { setEditGroupId(false) setGroupVisible(false) } const handleGroupToggle = () => { setEditGroupId(false) setGroupVisible((state) => !state) } const saveGroupOrder = async () => { await reorderGroupsAPI({ shortName: shortName, order: { groupOrder: JSON.stringify(groupOrder) } }); } const handleUpload = async (file) => { const formData = new FormData(); formData.append('file', file) await importTalkgroupsAPI({ shortName: shortName, file: formData }); } const handleSystemDeleteConfirm = async () => { setOpenSystemDeleteConfirm(false); try { const originalPromiseResult = await deleteSystemAPI(shortName).unwrap(); navigate("/list-systems") } catch (error) { const message = error.data.message; console.log(message); setRequestMessage(message); } }; const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false); let system = false; if (isSystemsSuccess) { system = systemsData.systems.find(sys => sys.shortName == shortName); } useLayoutEffect(() => { if (groupsData) { let newOrder = []; groupsData.forEach(group => { newOrder.push(group._id); }); setGroupOrder(newOrder); } }, [groupsData]) useEffect(() => { if (isSystemsSuccess) { processStatistics(systemsData.stats[shortName]) } }, [isSystemsSuccess]); useEffect(() => { if (isErrorsSuccess) { processErrors(errorData) } }, [isErrorsSuccess]); let fileInput = null; var location = ""; var example = ""; var groupRender = () => { }; if (system) { if (isTalkgroupsSuccess && talkgroupsData.length) { groupRender = () => (
); } else { groupRender = () => ( ); } example = '{\n\t"systems": [\n\t\t{\n\t\t\t"shortName": "' + system.shortName + '",\n\t\t\t"apiKey": "' + system.key + '"\n\t\t}\n\t],\n\t"uploadServer": "' + process.env.REACT_APP_BACKEND_SERVER + '"\n}'; switch (system.systemType) { case "state": location = system.state; break; case "city": location = system.city + ", " + system.state; break; case "county": location = system.county + ", " + system.state; break; case "international": location = system.country; break; } } const panes = [ { menuItem: "Overview", render: () => (
Location
{location}
Short Name
{system.shortName}
Description
{system.description}
) }, { menuItem: "Config", render: () => (
Configure Trunk Recorder

Below are the parameters you will need to configure Trunk Recorder. The example below shows how they should be added to the config.json file.

API Key
{system.key}
Short Name
{system.shortName}
Upload Server
{process.env.REACT_APP_BACKEND_SERVER}
Example