cleaned up admin

This commit is contained in:
Luke Berndt
2023-04-05 21:16:53 -04:00
parent efe3794eee
commit f7f5265264
26 changed files with 149 additions and 304 deletions

View File

@@ -10,7 +10,8 @@ var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.e
var frontend_server = process.env['REACT_APP_FRONTEND_SERVER'] != null ? process.env['REACT_APP_FRONTEND_SERVER'] : 'https://openmhz.com'; var frontend_server = process.env['REACT_APP_FRONTEND_SERVER'] != null ? process.env['REACT_APP_FRONTEND_SERVER'] : 'https://openmhz.com';
var socket_server = process.env['REACT_APP_SOCKET_SERVER'] != null ? process.env['REACT_APP_SOCKET_SERVER'] : 'wss://socket.openmhz.com'; //'https://s3.amazonaws.com/robotastic'; var socket_server = process.env['REACT_APP_SOCKET_SERVER'] != null ? process.env['REACT_APP_SOCKET_SERVER'] : 'wss://socket.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : 'https://account.openmhz.com'; //'https://s3.amazonaws.com/robotastic'; var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : 'https://account.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
var dev_server = "http://account.openmhz.test:3000" var account_dev_server = "http://account.openmhz.test:3000"
var admin_dev_server = "http://admin.openmhz.test:3000"
module.exports = function(app, passport) { module.exports = function(app, passport) {
app.set("port", 3009) app.set("port", 3009)
@@ -65,7 +66,8 @@ module.exports = function(app, passport) {
allowedOrigins.push(frontend_server); allowedOrigins.push(frontend_server);
allowedOrigins.push(backend_server); allowedOrigins.push(backend_server);
allowedOrigins.push(socket_server); allowedOrigins.push(socket_server);
allowedOrigins.push(dev_server); allowedOrigins.push(admin_dev_server);
allowedOrigins.push(account_dev_server);
var origin = req.headers.origin; var origin = req.headers.origin;

View File

@@ -27,7 +27,7 @@ exports.isLoggedIn = function (req, res, next) {
exports.authenticated = function (req, res, next) { exports.authenticated = function (req, res, next) {
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate'); res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
console.log("account/server/controllers/users.js - exports.authenticated() Auth check for user: " + req.user + " is: " + req.isAuthenticated()); console.log("account/server/controllers/users.js - exports.authenticated() Auth check for user: " + req.user.email + " is: " + req.isAuthenticated());
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
var clientUser = (({ var clientUser = (({
firstName, firstName,

View File

@@ -1,4 +1,4 @@
import React, { Component } from "react" import React from "react"
import Navigation from "./Navigation" import Navigation from "./Navigation"
import { Routes, Route } from 'react-router-dom'; import { Routes, Route } from 'react-router-dom';
@@ -15,7 +15,6 @@ import ResetPassword from "../User/ResetPassword"
import Terms from "../User/Terms" import Terms from "../User/Terms"
const App = (params) => { const App = (params) => {
const { store } = params;
return ( return (
<div> <div>
<Navigation /> <Navigation />

View File

@@ -1,15 +1,10 @@
import { Link } from "react-router-dom";
import { Menu, Dropdown } from "semantic-ui-react"; import { Menu, Dropdown } from "semantic-ui-react";
import React, { useEffect } from "react";
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { logoutUser } from "../features/user/userSlice"; import { logoutUser } from "../features/user/userSlice";
const navStyle = { const navStyle = {
marginBottom: "30px" marginBottom: "30px"
}; };
const blankStyle = {
marginBottom: "50px"
};
const Navigation = (props) => { const Navigation = (props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@@ -1,21 +0,0 @@
import { connect } from "react-redux"
import * as userActions from "../User/user-actions"
import Navigation from "./Navigation"
// Function passed in to `connect` to subscribe to Redux store updates.
// Any time it updates, mapStateToProps is called.
const mapStateToProps = (state) => {
return {
user: state.user
}
}
// Connects React component to the redux store
// It does not modify the component class passed to it
// Instead, it returns a new, connected component class, for you to use.
export default connect(
mapStateToProps,
userActions
)(Navigation)

View File

@@ -43,7 +43,7 @@ const Login = (props) => {
if (result.success) { if (result.success) {
if (nextLocation) { if (nextLocation) {
switch (this.props.nextLocation) { switch (nextLocation) {
case "frontend": case "frontend":
window.location = process.env.REACT_APP_FRONTEND_SERVER; window.location = process.env.REACT_APP_FRONTEND_SERVER;
break; break;

View File

@@ -1,4 +1,3 @@
import React, { Component } from "react"
import Navigation from "./Navigation" import Navigation from "./Navigation"
import { Routes, Route } from 'react-router-dom'; import { Routes, Route } from 'react-router-dom';
@@ -12,7 +11,6 @@ import CreateSystem from "../System/CreateSystem"
const App = (params) => { const App = (params) => {
const {store} = params;
return ( return (
<div> <div>
<Navigation/> <Navigation/>

View File

@@ -1,15 +1,11 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Menu, Dropdown } from "semantic-ui-react"; import { Menu, Dropdown } from "semantic-ui-react";
import React, { useEffect } from "react";
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { logoutUser } from "../features/user/userSlice"; import { logoutUser } from "../features/user/userSlice";
const navStyle = { const navStyle = {
marginBottom: "30px" marginBottom: "30px"
}; };
const blankStyle = {
marginBottom: "50px"
};
const Navigation = (props) => { const Navigation = (props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@@ -2,7 +2,7 @@
import React, { useEffect} from "react"; import React, { useEffect} from "react";
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { authenticateUser } from "../features/user/userSlice"; import { authenticateUser } from "../features/user/userSlice";
import { useNavigate } from 'react-router-dom';
/** /**
* Higher-order component (HOC) to wrap restricted pages * Higher-order component (HOC) to wrap restricted pages
*/ */
@@ -10,8 +10,6 @@ import { useNavigate } from 'react-router-dom';
const Restricted = ({children }) => { const Restricted = ({children }) => {
const { authenticated, hasAuthenticated } = useSelector((state) => state.user); const { authenticated, hasAuthenticated } = useSelector((state) => state.user);
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => { useEffect(() => {
dispatch(authenticateUser({})); dispatch(authenticateUser({}));
},[]); },[]);
@@ -21,8 +19,7 @@ const Restricted = ({children }) => {
return children;; return children;;
} }
else { else {
window.location = process.env.REACT_APP_ACCOUNT_SERVER + "/login?nextLocation=admin";
navigate("/list-systems")
} }
} }
return <div/> return <div/>

View File

@@ -1,4 +1,3 @@
import React, { Component } from "react";
import { Container, Header } from "semantic-ui-react"; import { Container, Header } from "semantic-ui-react";
const About = () => { const About = () => {

View File

@@ -1,31 +1,21 @@
import React, { Component } from "react"; import React from "react";
import { Modal, Icon, Button, Header } from "semantic-ui-react"; import { Modal, Icon, Button, Header } from "semantic-ui-react";
class Message extends Component { const Message = (props) => {
constructor(props) {
super(props);
}
_logout = event => { return (
event.preventDefault(); <Modal open={props.open} onClose={props.onClose} basic size="small">
this.props.manualLogout(); <Header icon="info" content={props.title} />
}; <Modal.Content>
<p>{props.message}</p>
render() { </Modal.Content>
return ( <Modal.Actions>
<Modal open={this.props.open} onClose={this.props.onClose} basic size="small"> <Button color='green' onClick={props.onClose} inverted>
<Header icon="info" content={this.props.title} />
<Modal.Content>
<p>{this.props.message}</p>
</Modal.Content>
<Modal.Actions>
<Button color='green' onClick={this.props.onClose} inverted>
<Icon name='checkmark' /> Got it <Icon name='checkmark' /> Got it
</Button> </Button>
</Modal.Actions> </Modal.Actions>
</Modal> </Modal>
); );
} };
}
export default Message; export default Message;

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, } from "react"; import React, { useEffect, useState, } from "react";
import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useUpdateGroupMutation, useCreateGroupMutation, useDeleteGroupMutation, useSaveGroupOrderMutation} from '../features/api/apiSlice' import { useGetTalkgroupsQuery, useGetGroupsQuery, useUpdateGroupMutation, useCreateGroupMutation} from '../features/api/apiSlice'
import { import {
Grid, Grid,
@@ -21,15 +21,15 @@ const GroupModal = (props) => {
name: "", name: "",
items: [] items: []
} }
const [editGroup, setEditGroup] = useState(false);
const [groupName, setGroupName] = useState(""); const [groupName, setGroupName] = useState("");
const [group, setGroup] = useState(emptyGroup) const [group, setGroup] = useState(emptyGroup)
const [talkgroups, setTalkgroups] = useState(props.talkgroups ? props.talkgroups : []) const [talkgroups, setTalkgroups] = useState(props.talkgroups ? props.talkgroups : [])
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(props.shortName); const { data: talkgroupsData} = useGetTalkgroupsQuery(props.shortName);
const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(props.shortName); const { data: groupsData} = useGetGroupsQuery(props.shortName);
const [ updateGroup,{ isLoading: isUpdating } ] = useUpdateGroupMutation(); const [ updateGroup] = useUpdateGroupMutation();
const [ createGroup,{ isLoading: isCreating } ] = useCreateGroupMutation(); const [ createGroup] = useCreateGroupMutation();
useEffect(() => { useEffect(() => {
if (talkgroupsData && groupsData && props.editGroupId) { if (talkgroupsData && groupsData && props.editGroupId) {
@@ -66,7 +66,7 @@ const GroupModal = (props) => {
if (!props.editGroupId && talkgroupsData) { if (!props.editGroupId && talkgroupsData) {
setTalkgroups([...talkgroupsData]); setTalkgroups([...talkgroupsData]);
} }
},[talkgroupsData]); },[talkgroupsData, props.editGroupId]);
useEffect(() => { useEffect(() => {
@@ -75,15 +75,9 @@ const GroupModal = (props) => {
setGroup(emptyGroup); setGroup(emptyGroup);
setTalkgroups( [...props.talkgroups]); setTalkgroups( [...props.talkgroups]);
} }
},[props.editGroupId]); },[props.editGroupId, props.talkgroups]);
const removeTalkgroup = (index) => {
const removeTalkgroup = (event, index) => {
const item = { const item = {
num: group.items[index].num, num: group.items[index].num,
description: group.items[index].description description: group.items[index].description
@@ -106,7 +100,7 @@ const GroupModal = (props) => {
} }
const addTalkgroup = (event, index) => { const addTalkgroup = (index) => {
const item = { const item = {
num: talkgroupsData[index].num, num: talkgroupsData[index].num,
description: talkgroupsData[index].description description: talkgroupsData[index].description

View File

@@ -1,15 +1,4 @@
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Link } from 'react-router-dom'
import { import {
Container,
Header,
Form,
Grid,
Segment,
Input,
Button,
Message,
Icon, Icon,
Table Table
} from "semantic-ui-react"; } from "semantic-ui-react";
@@ -30,7 +19,7 @@ const ListGroups = (props) => {
if (groups) { if (groups) {
// if a group gets deleted, it will still be listed in the Order array for a little. // if a group gets deleted, it will still be listed in the Order array for a little.
for (const id of props.order) { for (const id of props.order) {
const group = props.groups.find( group => group._id == id ); const group = props.groups.find( group => group._id === id );
if (group) { if (group) {
groupsDisplay.push(group) groupsDisplay.push(group)
} }
@@ -58,13 +47,13 @@ const ListGroups = (props) => {
onClick={e => props.editGroup(group._id)} onClick={e => props.editGroup(group._id)}
/><Icon /><Icon
name="up arrow" name="up arrow"
link={i==0?false:true} link={i===0?false:true}
disabled={i==0?true:false} disabled={i===0?true:false}
onClick={e => props.reorderGroup(i, i-1)} onClick={e => props.reorderGroup(i, i-1)}
/><Icon /><Icon
name="down arrow" name="down arrow"
link={i==groupsDisplay.length-1?false:true} link={i===groupsDisplay.length-1?false:true}
disabled={i==groupsDisplay.length-1?true:false} disabled={i===groupsDisplay.length-1?true:false}
onClick={e => props.reorderGroup(i, i+1)} onClick={e => props.reorderGroup(i, i+1)}
/><Icon /><Icon
name="remove" name="remove"

View File

@@ -1,99 +1,77 @@
import React, { Component } from "react"; import React from "react";
import { Group } from '@visx/group'; import { Group } from '@visx/group';
import { curveStep} from '@visx/curve'; import { curveStep} from '@visx/curve';
import { LinePath } from '@visx/shape'; import { LinePath } from '@visx/shape';
import { scaleLinear, scaleBand, scaleTime } from '@visx/scale'; import { scaleLinear, scaleTime } from '@visx/scale';
import { timeParse, timeFormat } from 'd3-time-format'; import { timeParse, timeFormat } from 'd3-time-format';
import { AxisBottom, AxisLeft } from '@visx/axis'; import { AxisBottom, AxisLeft } from '@visx/axis';
import { Dimmer, Loader } from "semantic-ui-react"; import { Loader } from "semantic-ui-react";
const CallChart = (props) => {
class CallChart extends Component { var callData = props.data;
constructor(props) { if (!callData) {
super(props); return (
} <Loader size='large'>Loading</Loader>
)
}
render() { var callTotals = callData.callTotals;
var callData = this.props.data; var errorTotals = callData.errorTotals;
if (!callData) {
return (
<Loader size='large'>Loading</Loader> // Define the graph dimensions and margins
const width = props.width;
const height = 500;
const margin = { top: 20, bottom: 40, left: 40, right: 20 };
const parseDate = timeParse("%Y%m%d");
const format = timeFormat("%b %d");
const formatDate = (date) => format(parseDate(date));
) // Then we'll create some bounds
} const xMax = width - margin.left - margin.right;
var callTotals = []; const yMax = height - margin.top - margin.bottom;
var errorTotals = [];
// Define the graph dimensions and margins // accessors
const width = this.props.width; const x = d => d.x;
const height = 500; const y = d => d.y;
const margin = { top: 20, bottom: 40, left: 40, right: 20 };
const parseDate = timeParse("%Y%m%d");
const format = timeFormat("%b %d");
const formatDate = (date) => format(parseDate(date));
// Then we'll create some bounds var xScale, yScale;
const xMax = width - margin.left - margin.right; if (callData) {
const yMax = height - margin.top - margin.bottom; // We'll use some mock data from `@vx/mock-data` for this.
// scales
// accessors xScale = scaleTime({
const x = d => d.x; range: [0, xMax],
const y = d => d.y; domain: [callData.minDate, callData.maxDate],
tickFormat: () => (val) => formatDate(val)
});
yScale = scaleLinear({
// Compose together the scale and accessor functions to get point functions range: [yMax, 0],
const dotColor = n => { domain: [0, callData.maxValue],
var colores_g = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"]; });
return colores_g[n % colores_g.length]; }
}
const compose = (scale, accessor) => (data) => scale(accessor(data));
var xScale, yScale, xPoint, yPoint;
if (callData) {
// We'll use some mock data from `@vx/mock-data` for this.
callTotals = callData.callTotals;
errorTotals = callData.errorTotals;
// scales
xScale = scaleTime({
range: [0, xMax],
domain: [callData.minDate, callData.maxDate],
tickFormat: () => (val) => formatDate(val)
});
yScale = scaleLinear({
range: [yMax, 0],
domain: [0, callData.maxValue],
});
xPoint = compose(xScale, x);
yPoint = compose(yScale, y);
}
return ( return (
<svg width={width} height={height}> <svg width={width} height={height}>
<Group left={margin.left} top={margin.top} > <Group left={margin.left} top={margin.top}>
<AxisBottom top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} /> <AxisBottom top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} />
<AxisLeft scale={yScale} /> <AxisLeft scale={yScale} />
<LinePath <LinePath
data={callTotals} data={callTotals}
x={x} x={x}
y={y} y={y}
strokeWidth={2} strokeWidth={2}
curve={curveStep} curve={curveStep}
/> />
<LinePath <LinePath
data={errorTotals} data={errorTotals}
x={x} x={x}
y={y} y={y}
strokeWidth={2} strokeWidth={2}
stroke="red" stroke="red"
curve={curveStep} curve={curveStep}
/> />
</Group> </Group>
</svg> </svg>
); );
}
} }
export default CallChart; export default CallChart;

View File

@@ -3,36 +3,24 @@ import React, { useState } from "react";
import { import {
Container, Container,
Header, Header,
Form,
Grid,
Segment,
Input,
Button,
Divider, Divider,
Message,
Icon
} from "semantic-ui-react"; } from "semantic-ui-react";
import { useCreateSystemMutation} from '../features/api/apiSlice' import { useCreateSystemMutation} from '../features/api/apiSlice'
import { useSelector, useDispatch } from 'react-redux' import { useSelector } from 'react-redux'
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import SystemForm from "./SystemForm"; import SystemForm from "./SystemForm";
// ---------------------------------------------------- // ----------------------------------------------------
const requestMessageStyle = { const CreateSystem = () => {
color: "red"
};
// ----------------------------------------------------
const CreateSystem = (props) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [createSystem, { error }] = useCreateSystemMutation(); const [createSystem ] = useCreateSystemMutation();
const { screenName } = useSelector((state) => state.user); const { screenName } = useSelector((state) => state.user);
const [requestMessage, setRequestMessage] = useState(""); const [requestMessage, setRequestMessage] = useState("");
const handleSubmit = async (system) => { const handleSubmit = async (system) => {
try { try {
const originalPromiseResult = await createSystem(system).unwrap(); await createSystem(system).unwrap();
navigate("/list-systems") navigate("/list-systems")
} catch (error) { } catch (error) {
const message = error.data.message; const message = error.data.message;

View File

@@ -7,9 +7,7 @@ import { AxisBottom, AxisLeft } from '@visx/axis';
import { Loader } from "semantic-ui-react"; import { Loader } from "semantic-ui-react";
import { LegendOrdinal} from '@visx/legend'; import { LegendOrdinal} from '@visx/legend';
class ErrorChart extends Component { class ErrorChart extends Component {
constructor(props) {
super(props);
}
render() { render() {
var errorData = this.props.data; var errorData = this.props.data;
@@ -44,12 +42,11 @@ class ErrorChart extends Component {
return colores_g[n % colores_g.length]; return colores_g[n % colores_g.length];
} }
const compose = (scale, accessor) => (data) => scale(accessor(data));
var zScale = scaleOrdinal({ var zScale = scaleOrdinal({
domain: errorData.legend, domain: errorData.legend,
range: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"] range: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"]
}); });
var xScale, yScale, xPoint, yPoint; var xScale, yScale;
if (errorData) { if (errorData) {
// We'll use some mock data from `@vx/mock-data` for this. // We'll use some mock data from `@vx/mock-data` for this.
data = errorData.data; data = errorData.data;
@@ -67,8 +64,6 @@ class ErrorChart extends Component {
domain: errorData.legend, domain: errorData.legend,
range: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"] range: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"]
}); });
xPoint = compose(xScale, x);
yPoint = compose(yScale, y);
} }

View File

@@ -1,4 +1,3 @@
import { Component, useRef } from "react";
import SystemCard from "./SystemCard"; import SystemCard from "./SystemCard";
import { import {
Container, Container,
@@ -9,15 +8,12 @@ import {
Button, Button,
} from "semantic-ui-react"; } from "semantic-ui-react";
import { useGetSystemsQuery, } from '../features/api/apiSlice' import { useGetSystemsQuery, } from '../features/api/apiSlice'
import { useDispatch } from 'react-redux' import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
// ---------------------------------------------------- // ----------------------------------------------------
const ListSystems = (props) => { const ListSystems = () => {
const { data, isSuccess } = useGetSystemsQuery(); const { data, isSuccess } = useGetSystemsQuery();
const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
let systems = false; let systems = false;
if (isSuccess) { if (isSuccess) {
@@ -31,7 +27,6 @@ const ListSystems = (props) => {
<div> <div>
<Container> <Container>
<Header as="h1">Radio Systems</Header> <Header as="h1">Radio Systems</Header>
<Button onClick={(e) => navigate("/create-system")}><Icon name='plus' />Add System</Button> <Button onClick={(e) => navigate("/create-system")}><Icon name='plus' />Add System</Button>
<Divider /> <Divider />
<Card.Group itemsPerRow={4}> <Card.Group itemsPerRow={4}>

View File

@@ -1,4 +1,3 @@
import React, { Component } from "react"
import CallChart from "./CallChart" import CallChart from "./CallChart"
import { ParentSize } from "@visx/responsive"; import { ParentSize } from "@visx/responsive";

View File

@@ -1,4 +1,3 @@
import React, { Component } from "react"
import ErrorChart from "./ErrorChart" import ErrorChart from "./ErrorChart"
import { ParentSize } from "@visx/responsive"; import { ParentSize } from "@visx/responsive";

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'; import { Link, useParams, useNavigate } from 'react-router-dom';
import ListTalkgroups from "../Talkgroups/ListTalkgroups.js"; import ListTalkgroups from "../Talkgroups/ListTalkgroups.js";
import MessageBox from "../Components/Message.js"; import MessageBox from "../Components/Message.js";
import ListGroups from "../Group/ListGroups.js"; import ListGroups from "../Group/ListGroups.js";
@@ -19,23 +19,21 @@ import {
List, List,
Segment, Segment,
Tab, Tab,
Divider, Divider
Label
} from "semantic-ui-react"; } from "semantic-ui-react";
const System = (props) => { const System = () => {
const { shortName } = useParams(); const { shortName } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery(); const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery();
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName);
const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(shortName); const { data: groupsData } = useGetGroupsQuery(shortName);
const { data: errorsData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName); const { data: systemErrorData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName);
const [deleteGroupAPI, { isLoading: isDeleting }] = useDeleteGroupMutation(); const [deleteGroupAPI] = useDeleteGroupMutation();
const [reorderGroupsAPI, { isLoading: isReordering }] = useSaveGroupOrderMutation(); const [reorderGroupsAPI] = useSaveGroupOrderMutation();
const [importTalkgroupsAPI, { isLoading: isImporting }] = useImportTalkgroupsMutation(); const [importTalkgroupsAPI] = useImportTalkgroupsMutation();
const [deleteSystemAPI ] = useDeleteSystemMutation(); const [deleteSystemAPI ] = useDeleteSystemMutation();
const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false);
const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false);
const [openMessage, setOpenMessage] = useState(false); const [openMessage, setOpenMessage] = useState(false);
const [requestMessage, setRequestMessage] = useState(""); const [requestMessage, setRequestMessage] = useState("");
const [groupVisible, setGroupVisible] = useState(false); const [groupVisible, setGroupVisible] = useState(false);
@@ -99,17 +97,17 @@ const System = (props) => {
var maxValue = 0; var maxValue = 0;
var MS_PER_MINUTE = 60000; var MS_PER_MINUTE = 60000;
if (statistic) { if (statistic) {
for (var j = 0; j < statistic.callTotals.length; j++) { for (let j = 0; j < statistic.callTotals.length; j++) {
var spotsBack = statistic.callTotals.length - j; let spotsBack = statistic.callTotals.length - j;
var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
if (time < minDate) minDate = time; if (time < minDate) minDate = time;
if (statistic.callTotals[j] > maxValue) if (statistic.callTotals[j] > maxValue)
maxValue = statistic.callTotals[j]; maxValue = statistic.callTotals[j];
callTotals.push({ y: statistic.callTotals[j], x: time }); callTotals.push({ y: statistic.callTotals[j], x: time });
} }
for (var j = 0; j < statistic.errorTotals.length; j++) { for (let j = 0; j < statistic.errorTotals.length; j++) {
var spotsBack = statistic.errorTotals.length - j; let spotsBack = statistic.errorTotals.length - j;
var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
if (time < minDate) minDate = time; if (time < minDate) minDate = time;
if (statistic.errorTotals[j] > maxValue) if (statistic.errorTotals[j] > maxValue)
maxValue = statistic.errorTotals[j]; maxValue = statistic.errorTotals[j];
@@ -163,7 +161,7 @@ const System = (props) => {
const deleteGroup = async (groupId) => { const deleteGroup = async (groupId) => {
for (const num in groupsData) { for (const num in groupsData) {
if (groupsData[num]._id == groupId) { if (groupsData[num]._id === groupId) {
await deleteGroupAPI(groupsData[num]) await deleteGroupAPI(groupsData[num])
} }
} }
@@ -203,7 +201,7 @@ const System = (props) => {
const handleSystemDeleteConfirm = async () => { const handleSystemDeleteConfirm = async () => {
setOpenSystemDeleteConfirm(false); setOpenSystemDeleteConfirm(false);
try { try {
const originalPromiseResult = await deleteSystemAPI(shortName).unwrap(); await deleteSystemAPI(shortName).unwrap();
navigate("/list-systems") navigate("/list-systems")
} catch (error) { } catch (error) {
const message = error.data.message; const message = error.data.message;
@@ -217,7 +215,7 @@ const System = (props) => {
let system = false; let system = false;
if (isSystemsSuccess) { if (isSystemsSuccess) {
system = systemsData.systems.find(sys => sys.shortName == shortName); system = systemsData.systems.find(sys => sys.shortName === shortName);
} }
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -239,9 +237,9 @@ const System = (props) => {
useEffect(() => { useEffect(() => {
if (isErrorsSuccess) { if (isErrorsSuccess) {
processErrors(errorData) processErrors(systemErrorData)
} }
}, [isErrorsSuccess]); }, [systemErrorData]);
let fileInput = null; let fileInput = null;
var location = ""; var location = "";
@@ -289,6 +287,7 @@ const System = (props) => {
case "state": case "state":
location = system.state; location = system.state;
break; break;
default:
case "city": case "city":
location = system.city + ", " + system.state; location = system.city + ", " + system.state;
break; break;

View File

@@ -1,5 +1,4 @@
import React, { Component } from "react";
import { import {
Card, Card,
Icon, Icon,

View File

@@ -1,40 +0,0 @@
import { connect } from "react-redux"
import * as systemActionCreators from "./system-actions"
import * as talkgroupActionCreators from "../Talkgroups/talkgroup-actions"
import * as groupActionCreators from "../Group/group-actions"
import * as permissionActionCreators from "../Permission/permission-actions"
import * as errorActionCreators from "./error-actions"
import { bindActionCreators } from 'redux'
import System from "./System"
function mapStateToProps(state,props) {
return {
system: state.system.items.find((item) => item.shortName === props.match.params.shortName),
permissions: state.permission.items,
statistic: state.statistic[props.match.params.shortName],
systemIsWaiting: state.system.isWaiting,
talkgroupsIsWaiting: state.talkgroup.isWaiting,
groupsIsWaiting: state.group.isWaiting,
talkgroups: state.talkgroup.items[props.match.params.shortName],
orderChange: state.group.orderChange,
groups: state.group.items[props.match.params.shortName],
errors: state.error.items[props.match.params.shortName],
shortName: props.match.params.shortName,
activeTab: (props.match.params.tab ? props.match.params.tab : 0)
}
}
function mapDispatchToProps(dispatch) {
return {
groupActions: bindActionCreators(groupActionCreators, dispatch),
systemActions: bindActionCreators(systemActionCreators, dispatch),
talkgroupActions: bindActionCreators(talkgroupActionCreators, dispatch),
errorActions: bindActionCreators(errorActionCreators, dispatch),
permissionActions: bindActionCreators(permissionActionCreators, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(System)

View File

@@ -28,13 +28,11 @@ const SystemForm = (props) => {
const [nameError, setNameError] = useState(false); const [nameError, setNameError] = useState(false);
const [shortNameError, setShortNameError] = useState(false); const [shortNameError, setShortNameError] = useState(false);
const [descriptionError, setDescriptionError] = useState(false); const [descriptionError, setDescriptionError] = useState(false);
const [emailError, setEmailError] = useState(false);
const [systemTypeError, setSystemTypeError] = useState(false);
const [systemStateError, setSystemStateError] = useState(false); const [systemStateError, setSystemStateError] = useState(false);
const [cityError, setCityError] = useState(false); const [cityError, setCityError] = useState(false);
const [countyError, setCountyError] = useState(false); const [countyError, setCountyError] = useState(false);
const [countryError, setCountryError] = useState(false); const [countryError, setCountryError] = useState(false);
const [isEditing, setIsEditing] = useState(props.isEditing); const [isEditing] = useState(props.isEditing);
useEffect(() => { useEffect(() => {
if (props.isEditing && props.system) { if (props.isEditing && props.system) {
@@ -49,7 +47,7 @@ const SystemForm = (props) => {
setShowScreenName(props.system.showScreenName); setShowScreenName(props.system.showScreenName);
setIgnoreUnknownTalkgroup(props.system.ignoreUnknownTalkgroup); setIgnoreUnknownTalkgroup(props.system.ignoreUnknownTalkgroup);
} }
}, [props.system]); }, [props.system, props.isEditing]);
const checkInputs = () => { const checkInputs = () => {

View File

@@ -1,31 +1,31 @@
import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; import React, { useState } from "react";
import SystemForm from "./SystemForm"; import SystemForm from "./SystemForm";
import { import {
Container, Container,
Header Header
} from "semantic-ui-react"; } from "semantic-ui-react";
import { useUpdateSystemMutation, useGetSystemsQuery, } from '../features/api/apiSlice' import { useUpdateSystemMutation, useGetSystemsQuery, } from '../features/api/apiSlice'
import { useSelector, useDispatch } from 'react-redux' import { useSelector } from 'react-redux'
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
const UpdateSystem = (props) => { const UpdateSystem = (props) => {
const { screenName } = useSelector((state) => state.user); const { screenName } = useSelector((state) => state.user);
const { shortName } = useParams(); const { shortName } = useParams();
const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery(); const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery();
const [updateSystem, { error }] = useUpdateSystemMutation(); const [updateSystem] = useUpdateSystemMutation();
const [requestMessage, setRequestMessage] = useState(""); const [requestMessage, setRequestMessage] = useState("");
const navigate = useNavigate(); const navigate = useNavigate();
let system = false; let system = false;
if (isSystemsSuccess) { if (isSystemsSuccess) {
system = systemsData.systems.find(sys => sys.shortName == shortName); system = systemsData.systems.find(sys => sys.shortName === shortName);
} }
const handleSystemSubmit = async (system) => { const handleSystemSubmit = async (system) => {
try { try {
const originalPromiseResult = await updateSystem(system).unwrap(); await updateSystem(system).unwrap();
navigate("/system/" + shortName) navigate("/system/" + shortName)
} catch (error) { } catch (error) {
const message = error.data.message; const message = error.data.message;

View File

@@ -1,6 +1,5 @@
// Import the RTK Query methods from the React-specific entry point // Import the RTK Query methods from the React-specific entry point
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createSelector } from '@reduxjs/toolkit'
/* /*
const axiosBaseQuery = const axiosBaseQuery =
@@ -36,7 +35,7 @@ export const apiSlice = createApi({
getGroups: builder.query({ getGroups: builder.query({
// The URL for the request is '/fakeApi/posts' // The URL for the request is '/fakeApi/posts'
query: (shortName) => ({ url: `/groups/${shortName}`, credentials: "include" }), query: (shortName) => ({ url: `/groups/${shortName}`, credentials: "include" }),
providesTags: (result, error, arg) => providesTags: (result) =>
result result
? [...result.map(({ _id }) => ({ type: 'Group', _id })), 'Group'] ? [...result.map(({ _id }) => ({ type: 'Group', _id })), 'Group']
: ['Group'], : ['Group'],
@@ -56,9 +55,9 @@ export const apiSlice = createApi({
async onQueryStarted(group, { dispatch, queryFulfilled }) { async onQueryStarted(group, { dispatch, queryFulfilled }) {
try { try {
const { data: updatedGroup } = await queryFulfilled const { data: updatedGroup } = await queryFulfilled
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getGroups', updatedGroup.shortName, (groups) => { apiSlice.util.updateQueryData('getGroups', updatedGroup.shortName, (groups) => {
const groupIndex = groups.findIndex((obj => obj.groupId == updatedGroup.groupId)); const groupIndex = groups.findIndex((obj => obj.groupId === updatedGroup.groupId));
groups[groupIndex] = updatedGroup; groups[groupIndex] = updatedGroup;
}) })
) )
@@ -75,7 +74,7 @@ export const apiSlice = createApi({
async onQueryStarted(group, { dispatch, queryFulfilled }) { async onQueryStarted(group, { dispatch, queryFulfilled }) {
try { try {
const { data: newGroup } = await queryFulfilled; const { data: newGroup } = await queryFulfilled;
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getGroups', newGroup.shortName, (groups) => { apiSlice.util.updateQueryData('getGroups', newGroup.shortName, (groups) => {
groups.push(newGroup); groups.push(newGroup);
}) })
@@ -93,9 +92,9 @@ export const apiSlice = createApi({
async onQueryStarted(group, { dispatch, queryFulfilled }) { async onQueryStarted(group, { dispatch, queryFulfilled }) {
try { try {
await queryFulfilled await queryFulfilled
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getGroups', group.shortName, (groups) => { apiSlice.util.updateQueryData('getGroups', group.shortName, (groups) => {
const groupIndex = groups.findIndex((obj => obj.groupId == group.groupId)); const groupIndex = groups.findIndex((obj => obj.groupId === group.groupId));
groups.splice(groupIndex,1); groups.splice(groupIndex,1);
}) })
) )
@@ -123,7 +122,7 @@ export const apiSlice = createApi({
async onQueryStarted({shortName,file}, { dispatch, queryFulfilled }) { async onQueryStarted({shortName,file}, { dispatch, queryFulfilled }) {
try { try {
const { data: newTalkgroups } = await queryFulfilled; const { data: newTalkgroups } = await queryFulfilled;
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getTalkgroups', shortName, (talkgroups) => { apiSlice.util.updateQueryData('getTalkgroups', shortName, (talkgroups) => {
talkgroups.length = 0; // Clear your array talkgroups.length = 0; // Clear your array
talkgroups.push(...newTalkgroups); talkgroups.push(...newTalkgroups);
@@ -147,7 +146,7 @@ export const apiSlice = createApi({
async onQueryStarted(system, { dispatch, queryFulfilled }) { async onQueryStarted(system, { dispatch, queryFulfilled }) {
try { try {
const { data: newSystem } = await queryFulfilled const { data: newSystem } = await queryFulfilled
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => { apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
const systems = systemsData.systems; const systems = systemsData.systems;
systems.push(newSystem); systems.push(newSystem);
@@ -166,10 +165,10 @@ export const apiSlice = createApi({
async onQueryStarted(system, { dispatch, queryFulfilled }) { async onQueryStarted(system, { dispatch, queryFulfilled }) {
try { try {
const { data: updatedSystem } = await queryFulfilled const { data: updatedSystem } = await queryFulfilled
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => { apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
const systems = systemsData.systems; const systems = systemsData.systems;
const systemIndex = systems.findIndex((obj => obj.shortName == updatedSystem.shortName)); const systemIndex = systems.findIndex((obj => obj.shortName === updatedSystem.shortName));
systems[systemIndex] = updatedSystem; systems[systemIndex] = updatedSystem;
}) })
) )
@@ -185,10 +184,10 @@ export const apiSlice = createApi({
async onQueryStarted(shortName, { dispatch, queryFulfilled }) { async onQueryStarted(shortName, { dispatch, queryFulfilled }) {
try { try {
await queryFulfilled await queryFulfilled
const patchResult = dispatch( dispatch(
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => { apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
const systems = systemsData.systems; const systems = systemsData.systems;
const systemIndex = systems.findIndex((obj => obj.shortName == shortName)); const systemIndex = systems.findIndex((obj => obj.shortName === shortName));
systems.splice(systemIndex, 1); systems.splice(systemIndex, 1);
}) })
) )

View File

@@ -1,7 +1,5 @@
import { import {
createSlice, createSlice,
createEntityAdapter,
createSelector,
createAsyncThunk createAsyncThunk
} from '@reduxjs/toolkit' } from '@reduxjs/toolkit'