mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-12 16:57:39 -05:00
Admin is mostly working
This commit is contained in:
100
admin/src/System/CallChart.js
Normal file
100
admin/src/System/CallChart.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { Component } from "react";
|
||||
import { Group } from '@vx/group';
|
||||
import { curveStep} from '@vx/curve';
|
||||
import { LinePath } from '@vx/shape';
|
||||
import { scaleLinear, scaleBand, scaleTime } from '@vx/scale';
|
||||
import { timeParse, timeFormat } from 'd3-time-format';
|
||||
import { AxisBottom, AxisLeft } from '@vx/axis';
|
||||
import { Dimmer, Loader } from "semantic-ui-react";
|
||||
|
||||
|
||||
class CallChart extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
var callData = this.props.data;
|
||||
if (!callData) {
|
||||
return (
|
||||
|
||||
<Loader size='large'>Loading</Loader>
|
||||
|
||||
)
|
||||
}
|
||||
var callTotals = [];
|
||||
var errorTotals = [];
|
||||
|
||||
// Define the graph dimensions and margins
|
||||
const width = this.props.width;
|
||||
const height = 500;
|
||||
const margin = { top: 20, bottom: 40, left: 40, right: 20 };
|
||||
|
||||
// Then we'll create some bounds
|
||||
const xMax = width - margin.left - margin.right;
|
||||
const yMax = height - margin.top - margin.bottom;
|
||||
|
||||
// accessors
|
||||
const x = d => d.x;
|
||||
const y = d => d.y;
|
||||
|
||||
|
||||
|
||||
// Compose together the scale and accessor functions to get point functions
|
||||
const dotColor = n => {
|
||||
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 (
|
||||
<svg width={width} height={height}>
|
||||
<Group left={margin.left} top={margin.top} >
|
||||
<AxisBottom top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} />
|
||||
<AxisLeft scale={yScale} />
|
||||
<LinePath
|
||||
data={callTotals}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
x={x}
|
||||
y={y}
|
||||
strokeWidth={2}
|
||||
curve={curveStep}
|
||||
/>
|
||||
<LinePath
|
||||
data={errorTotals}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
x={x}
|
||||
y={y}
|
||||
strokeWidth={2}
|
||||
stroke="red"
|
||||
curve={curveStep}
|
||||
/>
|
||||
</Group>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export default CallChart;
|
||||
82
admin/src/System/CreateSystem.js
Normal file
82
admin/src/System/CreateSystem.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {
|
||||
Container,
|
||||
Header,
|
||||
Form,
|
||||
Grid,
|
||||
Segment,
|
||||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
Message,
|
||||
Icon
|
||||
} from "semantic-ui-react";
|
||||
|
||||
import SystemForm from "./SystemForm";
|
||||
|
||||
// ----------------------------------------------------
|
||||
const requestMessageStyle = {
|
||||
color: "red"
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
class CreateSystem extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
|
||||
}
|
||||
|
||||
state = {
|
||||
requestMessage: ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
handleSubmit(system) {
|
||||
this.props
|
||||
.createSystem(system)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during registration
|
||||
this.setState({
|
||||
requestMessage
|
||||
});
|
||||
} else {
|
||||
this.props.changeUrl("/list-systems")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Container text>
|
||||
<Header as="h2">Join Us!</Header>
|
||||
<p>
|
||||
Record the your community's radio systems and share them! All it
|
||||
takes is a spare computer and a cheap SDR. The software you need,
|
||||
along with an explanation of how to set it up, is available{" "}
|
||||
<a href="https://github.com/robotastic/trunk-recorder">here</a>.
|
||||
</p>
|
||||
<p>
|
||||
After you have the Trunk Recorder software up and running, come
|
||||
back here and sign-up for an account so you can share your
|
||||
recordings
|
||||
</p>
|
||||
<p> - Luke</p>
|
||||
<Divider />
|
||||
<p></p>
|
||||
</Container>
|
||||
|
||||
<Container text>
|
||||
<Header as="h1">Create System</Header>
|
||||
<SystemForm onSubmit={this.handleSubmit} requestMessage={this.state.requestMessage} screenName={this.props.user.screenName}/>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CreateSystem;
|
||||
14
admin/src/System/CreateSystemContainer.js
Normal file
14
admin/src/System/CreateSystemContainer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { connect } from "react-redux"
|
||||
import * as systemActions from "./system-actions"
|
||||
import CreateSystem from "./CreateSystem"
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
user: state.user
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
systemActions
|
||||
)(CreateSystem)
|
||||
133
admin/src/System/ErrorChart.js
Normal file
133
admin/src/System/ErrorChart.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { Component } from "react";
|
||||
import { Group } from '@vx/group';
|
||||
import { GlyphCircle } from '@vx/glyph';
|
||||
import { scaleLinear, scaleBand, scaleTime, scaleOrdinal } from '@vx/scale';
|
||||
import { timeParse, timeFormat } from 'd3-time-format';
|
||||
import { AxisBottom, AxisLeft } from '@vx/axis';
|
||||
import { Dimmer, Loader } from "semantic-ui-react";
|
||||
import {
|
||||
LegendOrdinal
|
||||
|
||||
} from '@vx/legend';
|
||||
class ErrorChart extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
var errorData = this.props.data;
|
||||
if (!errorData) {
|
||||
return (
|
||||
<Loader size='large'>Loading</Loader>
|
||||
)
|
||||
}
|
||||
var data = [];
|
||||
const parseDate = timeParse("%Y%m%d");
|
||||
const format = timeFormat("%b %d");
|
||||
const formatDate = (date) => format(parseDate(date));
|
||||
|
||||
// Define the graph dimensions and margins
|
||||
const width = this.props.width;
|
||||
const height = 500;
|
||||
const margin = { top: 20, bottom: 40, left: 40, right: 20 };
|
||||
|
||||
// Then we'll create some bounds
|
||||
const xMax = width - margin.left - margin.right;
|
||||
const yMax = height - margin.top - margin.bottom;
|
||||
|
||||
// accessors
|
||||
const x = d => d.x;
|
||||
const y = d => d.y;
|
||||
|
||||
|
||||
|
||||
// Compose together the scale and accessor functions to get point functions
|
||||
const dotColor = n => {
|
||||
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 zScale = scaleOrdinal({
|
||||
domain: errorData.legend,
|
||||
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;
|
||||
if (errorData) {
|
||||
// We'll use some mock data from `@vx/mock-data` for this.
|
||||
data = errorData.data;
|
||||
// scales
|
||||
xScale = scaleTime({
|
||||
range: [0, xMax],
|
||||
domain: [errorData.minDate, errorData.maxDate],
|
||||
tickFormat: () => (val) => formatDate(val)
|
||||
});
|
||||
yScale = scaleLinear({
|
||||
range: [yMax, 0],
|
||||
domain: [0, errorData.maxValue],
|
||||
});
|
||||
zScale = scaleOrdinal({
|
||||
domain: errorData.legend,
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
width: 900
|
||||
}}>
|
||||
<svg width={width} height={height} style={{
|
||||
float: "left"
|
||||
}}>
|
||||
<Group left={margin.left} top={margin.top} >
|
||||
<AxisBottom top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} />
|
||||
<AxisLeft scale={yScale} />
|
||||
{data.map((band, i) => {
|
||||
return (
|
||||
<Group key={`band-${i}`}>
|
||||
{band.data.map((point, j) => {
|
||||
return (
|
||||
<GlyphCircle
|
||||
className="dot"
|
||||
key={`point-${point.x}-${j}`}
|
||||
fill={dotColor(i)}
|
||||
left={xScale(x(point))}
|
||||
top={yScale(y(point))}
|
||||
size={6.0}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Group>
|
||||
);
|
||||
})}
|
||||
</Group>
|
||||
</svg>
|
||||
<div
|
||||
style={{
|
||||
float: "right"
|
||||
}}
|
||||
>
|
||||
<LegendOrdinal
|
||||
scale={zScale}
|
||||
labelMargin="0 15px 0 0"
|
||||
onMouseOver={data => event => {
|
||||
console.log(
|
||||
`mouse over: ${data.text}`,
|
||||
`index: ${data.index}`,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export default ErrorChart;
|
||||
44
admin/src/System/ListSystems.js
Normal file
44
admin/src/System/ListSystems.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import React, { Component } from "react";
|
||||
import SystemCard from "./SystemCard";
|
||||
import {
|
||||
Container,
|
||||
Divider,
|
||||
Header,
|
||||
Card,
|
||||
Icon,
|
||||
Button,
|
||||
} from "semantic-ui-react";
|
||||
|
||||
|
||||
// ----------------------------------------------------
|
||||
class ListSystems extends Component {
|
||||
|
||||
state = {
|
||||
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.fetchSystems();
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux
|
||||
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
||||
render() {
|
||||
const systems = this.props.system.items;
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
<Header as="h1">Radio Systems</Header>
|
||||
<Button onClick={(e) => this.props.changeUrl("/create-system")}><Icon name='plus' />Add System</Button>
|
||||
<Divider />
|
||||
<Card.Group itemsPerRow={4}>
|
||||
{systems.map((system) =>
|
||||
<SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
||||
)}
|
||||
</Card.Group>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ListSystems;
|
||||
16
admin/src/System/ListSystemsContainer.js
Normal file
16
admin/src/System/ListSystemsContainer.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { connect } from "react-redux"
|
||||
import * as systemActions from "./system-actions"
|
||||
import ListSystems from "./ListSystems"
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
system: state.system
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
systemActions
|
||||
)(ListSystems)
|
||||
72
admin/src/System/PreCreateSystem.js
Normal file
72
admin/src/System/PreCreateSystem.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {
|
||||
Container,
|
||||
Header,
|
||||
Form,
|
||||
Grid,
|
||||
Segment,
|
||||
Input,
|
||||
Button,
|
||||
Message,
|
||||
Step,
|
||||
Icon
|
||||
} from "semantic-ui-react";
|
||||
|
||||
import SystemForm from "./SystemForm";
|
||||
|
||||
// ----------------------------------------------------
|
||||
const requestMessageStyle = {
|
||||
color: "red"
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
class PreCreateSystem extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
|
||||
state = {
|
||||
requestMessage: ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Step.Group attached='top' widths={3}>
|
||||
<Step>
|
||||
<Icon name='wifi' />
|
||||
<Step.Content>
|
||||
<Step.Title>Trunk Recorder</Step.Title>
|
||||
<Step.Description>Choose your shipping options</Step.Description>
|
||||
</Step.Content>
|
||||
</Step>
|
||||
|
||||
<Step active>
|
||||
<Icon name='payment' />
|
||||
<Step.Content>
|
||||
<Step.Title>Billing</Step.Title>
|
||||
<Step.Description>Enter billing information</Step.Description>
|
||||
</Step.Content>
|
||||
</Step>
|
||||
|
||||
<Step disabled>
|
||||
<Icon name='info' />
|
||||
<Step.Content>
|
||||
<Step.Title>Confirm Order</Step.Title>
|
||||
</Step.Content>
|
||||
</Step>
|
||||
</Step.Group>
|
||||
|
||||
<Segment attached>
|
||||
<Image src='https://react.semantic-ui.com/images/wireframe/paragraph.png' />
|
||||
</Segment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PreCreateSystem;
|
||||
27
admin/src/System/ResponsiveCallChart.js
Normal file
27
admin/src/System/ResponsiveCallChart.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { Component } from "react"
|
||||
import CallChart from "./CallChart"
|
||||
import { ParentSize } from "@vx/responsive";
|
||||
|
||||
class ResponsiveCallChart extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<ParentSize className="graph-container">
|
||||
{({ width: w, height: h }) => {
|
||||
return (
|
||||
<CallChart
|
||||
width={w}
|
||||
height={h}
|
||||
data={this.props.data}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</ParentSize>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ResponsiveCallChart
|
||||
27
admin/src/System/ResponsiveErrorChart.js
Normal file
27
admin/src/System/ResponsiveErrorChart.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { Component } from "react"
|
||||
import ErrorChart from "./ErrorChart"
|
||||
import { ParentSize } from "@vx/responsive";
|
||||
|
||||
class ResponsiveErrorChart extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<ParentSize className="graph-container">
|
||||
{({ width: w, height: h }) => {
|
||||
return (
|
||||
<ErrorChart
|
||||
width={w}
|
||||
height={h}
|
||||
data={this.props.data}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</ParentSize>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ResponsiveErrorChart
|
||||
679
admin/src/System/System.js
Normal file
679
admin/src/System/System.js
Normal file
@@ -0,0 +1,679 @@
|
||||
import React, {Component} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import ListTalkgroups from "../Talkgroups/ListTalkgroups.js";
|
||||
import MessageBox from "../components/pure/Message.js";
|
||||
import ListGroups from "../Group/ListGroups.js";
|
||||
import GroupModal from "../Group/GroupModalContainer.js";
|
||||
import UpdatePermissionModal from "../Permission/UpdatePermissionModalContainer.js";
|
||||
import AddPermissionModal from "../Permission/AddPermissionModalContainer.js";
|
||||
import ErrorChart from "./ResponsiveErrorChart"
|
||||
import CallChart from "./ResponsiveCallChart"
|
||||
import {
|
||||
Button,
|
||||
Confirm,
|
||||
Icon,
|
||||
Form,
|
||||
TextArea,
|
||||
Header,
|
||||
Container,
|
||||
Message,
|
||||
List,
|
||||
Segment,
|
||||
Tab,
|
||||
Divider,
|
||||
Label
|
||||
} from "semantic-ui-react";
|
||||
|
||||
function planName(planType) {
|
||||
switch (planType) {
|
||||
case freePlanValue:
|
||||
return "Free";
|
||||
case proPlanValue:
|
||||
return "Pro";
|
||||
}
|
||||
}
|
||||
|
||||
class System extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleUpload = this.handleUpload.bind(this);
|
||||
this.reorderGroup = this.reorderGroup.bind(this);
|
||||
this.deleteGroup = this.deleteGroup.bind(this);
|
||||
this.editGroup = this.editGroup.bind(this);
|
||||
this.saveGroupOrder = this.saveGroupOrder.bind(this);
|
||||
this.showSystemDeleteConfirm = this.showSystemDeleteConfirm.bind(this);
|
||||
this.showPermissionDeleteConfirm = this.showPermissionDeleteConfirm.bind(
|
||||
this
|
||||
);
|
||||
this.updatePermission = this.updatePermission.bind(this);
|
||||
this.handlePermissionDeleteConfirm = this.handlePermissionDeleteConfirm.bind(
|
||||
this
|
||||
);
|
||||
this.handlePermissionDeleteCancel = this.handlePermissionDeleteCancel.bind(
|
||||
this
|
||||
);
|
||||
this.handleSystemDeleteConfirm = this.handleSystemDeleteConfirm.bind(this);
|
||||
this.handleSystemDeleteCancel = this.handleSystemDeleteCancel.bind(this);
|
||||
this.handleUpgradePlan = this.handleUpgradePlan.bind(this);
|
||||
this.handleUpdatePermissionClose = this.handleUpdatePermissionClose.bind(
|
||||
this
|
||||
);
|
||||
this.handleGroupToggle = this.handleGroupToggle.bind(this);
|
||||
this.handleGroupClose = this.handleGroupClose.bind(this);
|
||||
this.handleAddPermissionToggle = this.handleAddPermissionToggle.bind(this);
|
||||
this.handleAddPermissionClose = this.handleAddPermissionClose.bind(this);
|
||||
this.processErrors = this.processErrors.bind(this);
|
||||
this.processStatistics = this.processStatistics.bind(this);
|
||||
this.state = {
|
||||
openSystemDeleteConfirm: false,
|
||||
openPermissionDeleteConfirm: false,
|
||||
openMessage: false,
|
||||
requestMessage: "",
|
||||
groupVisible: false,
|
||||
editGroupId: false,
|
||||
selectedPermission: false,
|
||||
addPermissionVisible: false,
|
||||
updatePermissionVisible: false,
|
||||
updatePermissionId: false
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
this.setState({ errorData: errorData });
|
||||
}
|
||||
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;
|
||||
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] });
|
||||
}
|
||||
var callData = {
|
||||
minDate: minDate,
|
||||
maxDate: maxDate,
|
||||
minValue: minValue,
|
||||
maxValue: maxValue,
|
||||
callTotals: callTotals,
|
||||
errorTotals: errorTotals
|
||||
};
|
||||
|
||||
this.setState({ callData: 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);
|
||||
this.props.permissionActions.fetch(this.props.shortName);
|
||||
if (this.props.statistic) {
|
||||
this.processStatistics(this.props.statistic);
|
||||
}
|
||||
if (this.props.errors) {
|
||||
this.processErrors(this.props.errors);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProp) {
|
||||
if (!this.props.errors && nextProp.errors) {
|
||||
this.processErrors(nextProp.errors);
|
||||
}
|
||||
if (!this.props.statistic && nextProp.statistic) {
|
||||
this.processStatistics(nextProp.statistic);
|
||||
}
|
||||
}
|
||||
|
||||
updatePermission(permissionId) {
|
||||
this.setState({
|
||||
updatePermissionId: permissionId,
|
||||
updatePermissionVisible: !this.state.updatePermissionVisible
|
||||
});
|
||||
}
|
||||
|
||||
editGroup(groupId) {
|
||||
this.setState({
|
||||
editGroupId: groupId,
|
||||
groupVisible: !this.state.groupVisible
|
||||
});
|
||||
}
|
||||
|
||||
deleteGroup(groupId) {
|
||||
this.props.groupActions.deleteGroup(this.props.shortName, groupId);
|
||||
}
|
||||
|
||||
reorderGroup(oldIndex, newIndex) {
|
||||
this.props.groupActions.reorderGroup(
|
||||
this.props.shortName,
|
||||
oldIndex,
|
||||
newIndex
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdatePermissionClose(didUpdate) {
|
||||
this.setState({
|
||||
updatePermissionVisible: !this.state.updatePermissionVisible,
|
||||
updatePermissionId: false
|
||||
});
|
||||
}
|
||||
|
||||
handleAddPermissionClose(didUpdate) {
|
||||
this.setState({
|
||||
addPermissionVisible: !this.state.addPermissionVisible
|
||||
});
|
||||
}
|
||||
|
||||
handleAddPermissionToggle = () =>
|
||||
this.setState({
|
||||
addPermissionVisible: !this.state.addPermissionVisible
|
||||
});
|
||||
|
||||
handleGroupClose(didUpdate) {
|
||||
this.setState({
|
||||
groupVisible: !this.state.groupVisible,
|
||||
editGroupId: false
|
||||
});
|
||||
}
|
||||
handleGroupToggle = () => this.setState({
|
||||
editGroupId: false,
|
||||
groupVisible: !this.state.groupVisible
|
||||
})
|
||||
handleExport() {
|
||||
this.props.talkgroupActions
|
||||
.exportTalkgroups(this.props.system.shortName)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during registration
|
||||
this.setState({ requestMessage });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveGroupOrder() {
|
||||
const order = this.props.groups.map(x => x._id);
|
||||
const data = {
|
||||
groupOrder: JSON.stringify(order)
|
||||
};
|
||||
this.props.groupActions
|
||||
.saveGroupOrder(this.props.system.shortName, data)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleUpgradePlan() {
|
||||
window.location = process.env.REACT_APP_ACCOUNT_SERVER + "/update-plans";
|
||||
}
|
||||
|
||||
handleUpload(file) {
|
||||
this.props.talkgroupActions
|
||||
.importTalkgroups(this.props.system.shortName, file)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during registration
|
||||
this.setState({ requestMessage });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/Semantic-Org/Semantic-UI-React/issues/2103
|
||||
|
||||
showPermissionDeleteConfirm = permission =>
|
||||
this.setState({
|
||||
openPermissionDeleteConfirm: true,
|
||||
selectedPermission: permission
|
||||
});
|
||||
showSystemDeleteConfirm = () =>
|
||||
this.setState({ openSystemDeleteConfirm: true });
|
||||
|
||||
handlePermissionDeleteConfirm = permissionId => {
|
||||
this.setState({ openPermissionDeleteConfirm: false });
|
||||
this.props.permissionActions
|
||||
.deletePermission({
|
||||
shortName: this.props.system.shortName,
|
||||
permissionId: this.state.selectedPermission._id
|
||||
})
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during login
|
||||
this.setState({ requestMessage: requestMessage, openMessage: true });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleSystemDeleteConfirm = () => {
|
||||
this.setState({ openSystemDeleteConfirm: false });
|
||||
this.props.systemActions
|
||||
.deleteSystem(this.props.system.shortName)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during login
|
||||
this.setState({ requestMessage: requestMessage, openMessage: true });
|
||||
}
|
||||
});
|
||||
};
|
||||
handleSystemDeleteCancel = () =>
|
||||
this.setState({ openSystemDeleteConfirm: false });
|
||||
handlePermissionDeleteCancel = () =>
|
||||
this.setState({ openPermissionDeleteConfirm: false });
|
||||
render() {
|
||||
const system = this.props.system;
|
||||
let fileInput = null;
|
||||
var location = "";
|
||||
var example = "";
|
||||
var plan = "";
|
||||
var groupRender = () => {};
|
||||
|
||||
if (system) {
|
||||
if (this.props.talkgroups && this.props.talkgroups.length) {
|
||||
groupRender = () => (
|
||||
<div>
|
||||
<Button onClick={this.handleGroupToggle}>Create Group</Button>
|
||||
<Button onClick={this.saveGroupOrder} disabled={!this.props.orderChange}> Save Order</Button>
|
||||
<ListGroups
|
||||
groups={this.props.groups}
|
||||
reorderGroup={this.reorderGroup}
|
||||
deleteGroup={this.deleteGroup}
|
||||
editGroup={this.editGroup}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
groupRender = () => (
|
||||
<Container text>
|
||||
<Message
|
||||
icon="upload"
|
||||
header="Import Talkgroups"
|
||||
content="Before you can create groups of Talkgroups, you need to import some talkgroups."
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
switch (system.planType) {
|
||||
case freePlanValue:
|
||||
const tempPlan = planName(system.planType) + " plan";
|
||||
plan = (
|
||||
<div>
|
||||
{tempPlan}
|
||||
<Label as="a" onClick={this.handleUpgradePlan}>
|
||||
{" "}
|
||||
<Icon name="plus" />
|
||||
Upgrade Plan
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
|
||||
case proPlanValue:
|
||||
plan = planName(system.planType) + " plan";
|
||||
break;
|
||||
}
|
||||
|
||||
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: () => (
|
||||
<div>
|
||||
<Container text>
|
||||
<Segment.Group>
|
||||
<Segment>
|
||||
<Header size="small">Location</Header>
|
||||
{location}
|
||||
</Segment>
|
||||
<Segment>
|
||||
<Header size="small">Short Name</Header>
|
||||
{this.props.system.shortName}
|
||||
</Segment>
|
||||
<Segment>
|
||||
<Header size="small">Description</Header>
|
||||
{this.props.system.description}
|
||||
</Segment>
|
||||
|
||||
<Segment clearing={true}>
|
||||
<Button
|
||||
color="red"
|
||||
onClick={this.showSystemDeleteConfirm}
|
||||
disabled={this.props.systemisWaiting}
|
||||
content="Delete"
|
||||
floated="right"
|
||||
/>
|
||||
<Button
|
||||
as={Link}
|
||||
to={"/update-system/" + this.props.system.shortName}
|
||||
content="Update"
|
||||
disabled={this.props.systemisWaiting}
|
||||
floated="left"
|
||||
/>
|
||||
</Segment>
|
||||
</Segment.Group>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
menuItem: "Config",
|
||||
render: () => (
|
||||
<div>
|
||||
<Container text>
|
||||
<Header as="h2">Configure Trunk Recorder</Header>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<Segment.Group>
|
||||
<Segment>
|
||||
<Header size="small">API Key</Header>
|
||||
{this.props.system.key}
|
||||
</Segment>
|
||||
<Segment>
|
||||
<Header size="small">Short Name</Header>
|
||||
{this.props.system.shortName}
|
||||
</Segment>
|
||||
<Segment>
|
||||
<Header size="small">Upload Server</Header>
|
||||
{process.env.REACT_APP_BACKEND_SERVER}
|
||||
</Segment>
|
||||
<Segment>
|
||||
<Header size="small">Example</Header>
|
||||
<Form>
|
||||
<TextArea rows="10" readOnly="readOnly" value={example} />
|
||||
</Form>
|
||||
</Segment>
|
||||
</Segment.Group>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
menuItem: "Talkgroups",
|
||||
render: () => (
|
||||
<div>
|
||||
<Container text>
|
||||
<p>
|
||||
<em>
|
||||
This is the same .csv talkgroup file from Trunk Recorder...
|
||||
except the Hex column has been deleted
|
||||
</em>
|
||||
</p>
|
||||
<p>The columns for the file are:</p>
|
||||
<List ordered celled>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Number</List.Header>
|
||||
<List.Description>The talkgroup number</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Mode</List.Header>
|
||||
<List.Description>
|
||||
The mode for the talkgroup, it can be: 'A' = analog, 'D' =
|
||||
P25 Digital, 'E' = Encrypted
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Alpha</List.Header>
|
||||
<List.Description>
|
||||
Up to 12 charecter description of the talkgroup
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Description</List.Header>
|
||||
<List.Description>
|
||||
The full title for the talkgroup
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Tag</List.Header>
|
||||
<List.Description>
|
||||
The type of talkgroup, eg: Dispatch, Fireground
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Group</List.Header>
|
||||
<List.Description>
|
||||
The department that uses this talkgroup
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<List.Content>
|
||||
<List.Header>Priority</List.Header>
|
||||
<List.Description>
|
||||
The priority for recording this talkgroup, 100 or over
|
||||
will be skipped
|
||||
</List.Description>
|
||||
</List.Content>
|
||||
</List.Item>
|
||||
</List>
|
||||
</Container>
|
||||
<Divider />
|
||||
<span>
|
||||
<label htmlFor="upload-button" className="ui icon button">
|
||||
<i className="upload icon" />
|
||||
Import Talkgroups
|
||||
</label>
|
||||
<input
|
||||
type="file"
|
||||
id="upload-button"
|
||||
style={{
|
||||
display: "none"
|
||||
}}
|
||||
onChange={() => {
|
||||
this.handleUpload(fileInput.files[0]);
|
||||
}}
|
||||
ref={input => {
|
||||
fileInput = input;
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<a href={"/talkgroups/" + this.props.system.shortName + "/export"}>
|
||||
<Button>
|
||||
<i className="upload icon" />
|
||||
Export Talkgroups
|
||||
</Button>
|
||||
</a>
|
||||
<ListTalkgroups talkgroups={this.props.talkgroups} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
menuItem: "Groups",
|
||||
render: groupRender
|
||||
},
|
||||
/*{
|
||||
menuItem: "Users",
|
||||
render: () => (<Tab.Pane>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<Button onClick={this.handleAddPermissionToggle} >
|
||||
Add User
|
||||
</Button>
|
||||
<ListPermissions permissions={this.props.permissions} updatePermission={this.updatePermission} deletePermission={this.showPermissionDeleteConfirm}/>
|
||||
</Tab.Pane>)
|
||||
},*/ {
|
||||
menuItem: "Stats",
|
||||
render: () => (
|
||||
<div>
|
||||
<Divider horizontal>
|
||||
<Icon name="microphone" />
|
||||
24-Hour Call History
|
||||
</Divider>
|
||||
<CallChart data={this.state.callData} />
|
||||
<Divider horizontal>
|
||||
<Icon name="microphone" />
|
||||
Hourly Vocoder Error Percentage
|
||||
</Divider>
|
||||
<ErrorChart data={this.state.errorData} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AddPermissionModal
|
||||
shortName={this.props.shortName}
|
||||
open={this.state.addPermissionVisible}
|
||||
onClose={this.handleAddPermissionClose}
|
||||
/>
|
||||
|
||||
<GroupModal
|
||||
shortName={this.props.shortName}
|
||||
editGroupId={this.state.editGroupId}
|
||||
open={this.state.groupVisible}
|
||||
onClose={this.handleGroupClose}
|
||||
/>
|
||||
<UpdatePermissionModal
|
||||
shortName={this.props.shortName}
|
||||
updatePermissionId={this.state.updatePermissionId}
|
||||
open={this.state.updatePermissionVisible}
|
||||
onClose={this.handleUpdatePermissionClose}
|
||||
/>
|
||||
{system ? (
|
||||
<Container>
|
||||
<MessageBox
|
||||
open={this.state.openMessage}
|
||||
message={this.state.requestMessage}
|
||||
title="Delete Failed"
|
||||
onClose={() => this.setState({ openMessage: false })}
|
||||
/>
|
||||
<Confirm
|
||||
open={this.state.openSystemDeleteConfirm}
|
||||
header="Are you sure you want to?"
|
||||
content={"Delete system - " + this.props.system.shortName}
|
||||
onCancel={this.handleSystemDeleteCancel}
|
||||
onConfirm={this.handleSystemDeleteConfirm}
|
||||
/>
|
||||
<Confirm
|
||||
open={this.state.openPermissionDeleteConfirm}
|
||||
header="Are you sure you want to?"
|
||||
content={
|
||||
"Remove access for: " + this.state.selectedPermission.email
|
||||
}
|
||||
onCancel={this.handlePermissionDeleteCancel}
|
||||
onConfirm={this.handlePermissionDeleteConfirm}
|
||||
/>
|
||||
|
||||
<Header as="h1">
|
||||
<Header.Content>
|
||||
{this.props.system.name}
|
||||
{/* <Header.Subheader>{plan}</Header.Subheader> */}
|
||||
</Header.Content>
|
||||
</Header>
|
||||
|
||||
<Tab
|
||||
menu={{
|
||||
secondary: true,
|
||||
pointing: true
|
||||
}}
|
||||
panes={panes}
|
||||
defaultActiveIndex={this.props.activeTab}
|
||||
/>
|
||||
</Container>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default System;
|
||||
62
admin/src/System/SystemCard.js
Normal file
62
admin/src/System/SystemCard.js
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
import React, { Component } from "react";
|
||||
import {
|
||||
Card,
|
||||
Icon,
|
||||
Header
|
||||
} from "semantic-ui-react";
|
||||
|
||||
|
||||
|
||||
|
||||
class SystemCard extends Component {
|
||||
|
||||
render() {
|
||||
var location = "";
|
||||
const system = this.props.system;
|
||||
|
||||
|
||||
if (system) {
|
||||
|
||||
|
||||
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;
|
||||
default:
|
||||
location = "unknown";
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Card link onClick={this.props.onClick}>
|
||||
|
||||
<Card.Content>
|
||||
<Header size='medium'>
|
||||
<Header.Content>
|
||||
{system.name}
|
||||
<Header.Subheader>{system.screenName}</Header.Subheader>
|
||||
</Header.Content>
|
||||
</Header>
|
||||
</Card.Content><Card.Content>
|
||||
|
||||
<Card.Description>{system.description}</Card.Description>
|
||||
</Card.Content>
|
||||
<Card.Content extra>
|
||||
<Icon name='marker' />
|
||||
{location}
|
||||
</Card.Content>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SystemCard;
|
||||
40
admin/src/System/SystemContainer.js
Normal file
40
admin/src/System/SystemContainer.js
Normal file
@@ -0,0 +1,40 @@
|
||||
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)
|
||||
429
admin/src/System/SystemForm.js
Normal file
429
admin/src/System/SystemForm.js
Normal file
@@ -0,0 +1,429 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {
|
||||
Container,
|
||||
Header,
|
||||
Dropdown,
|
||||
Divider,
|
||||
TextArea,
|
||||
Form,
|
||||
Grid,
|
||||
Segment,
|
||||
Input,
|
||||
Button,
|
||||
Message,
|
||||
Checkbox,
|
||||
Icon
|
||||
} from "semantic-ui-react";
|
||||
|
||||
// ----------------------------------------------------
|
||||
const checkInputMessagestyle = {
|
||||
color: "red"
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
class SystemForm extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.checkInputs = this.checkInputs.bind(this);
|
||||
this.render = this.render.bind(this);
|
||||
|
||||
|
||||
this.state = {
|
||||
checkInputMessages: [],
|
||||
name: "",
|
||||
shortName: "",
|
||||
description: "",
|
||||
systemType: "state",
|
||||
state: "",
|
||||
city: "",
|
||||
county: "",
|
||||
country: "",
|
||||
showScreenName: false,
|
||||
nameError: false,
|
||||
shortNameError: false,
|
||||
descriptionError: false,
|
||||
emailError: false,
|
||||
systemTypeError: false,
|
||||
stateError: false,
|
||||
cityErorr: false,
|
||||
countyError: false,
|
||||
countryError: false,
|
||||
isEditing: this.props.isEditing
|
||||
};
|
||||
if (this.props.isEditing && this.props.system)
|
||||
{
|
||||
this.state.name = this.props.system.name;
|
||||
this.state.shortName = this.props.system.shortName;
|
||||
this.state.description = this.props.system.description;
|
||||
this.state.systemType = this.props.system.systemType;
|
||||
this.state.city = this.props.system.city;
|
||||
this.state.state = this.props.system.state;
|
||||
this.state.county = this.props.system.county;
|
||||
this.state.country = this.props.system.country;
|
||||
this.state.showScreenName = this.props.system.showScreenName;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.isEditing && nextProps.system) {
|
||||
this.setState({
|
||||
name: nextProps.system.name,
|
||||
shortName: nextProps.system.shortName,
|
||||
description: nextProps.system.description,
|
||||
systemType: nextProps.system.systemType,
|
||||
state: nextProps.system.state,
|
||||
county: nextProps.system.county,
|
||||
country: nextProps.system.country,
|
||||
showScreenName: nextProps.system.showScreenName,
|
||||
});
|
||||
}
|
||||
}
|
||||
checkInputs() {
|
||||
let error = false;
|
||||
var checkInputMessages = [];
|
||||
|
||||
if (this.state.name === "") {
|
||||
this.setState({ nameError: true });
|
||||
checkInputMessages.push("System Name is required");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ nameError: false });
|
||||
}
|
||||
|
||||
if (this.state.shortName.length == 0 || this.state.shortName.length > 10) {
|
||||
this.setState({ shortNameError: true });
|
||||
checkInputMessages.push(
|
||||
"Short Name for the system has to be 4-10 charecters"
|
||||
);
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ shortNameError: false });
|
||||
}
|
||||
|
||||
if (this.state.description === "") {
|
||||
this.setState({ descriptionError: true });
|
||||
checkInputMessages.push("System description is required");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ descriptionError: false });
|
||||
}
|
||||
|
||||
if (
|
||||
this.state.systemType === "state" ||
|
||||
this.state.systemType === "city" ||
|
||||
this.state.systemType === "county"
|
||||
) {
|
||||
if (this.state.state === "") {
|
||||
this.setState({ stateError: true });
|
||||
checkInputMessages.push("Select a state");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ stateError: false });
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.systemType === "city") {
|
||||
if (this.state.city === "") {
|
||||
this.setState({ cityError: true });
|
||||
checkInputMessages.push("Enter a city");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ cityError: false });
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.systemType === "county") {
|
||||
if (this.state.county === "") {
|
||||
this.setState({ countyError: true });
|
||||
checkInputMessages.push("Enter a city");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ countyError: false });
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.systemType === "international") {
|
||||
if (this.state.country === "") {
|
||||
this.setState({ countryError: true });
|
||||
checkInputMessages.push("Enter a city");
|
||||
error = true;
|
||||
} else {
|
||||
this.setState({ countryError: false });
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ checkInputMessages: checkInputMessages });
|
||||
return error;
|
||||
}
|
||||
|
||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
||||
handleCheckboxChange() {
|
||||
this.setState({showScreenName: !this.state.showScreenName});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
let inputError = this.checkInputs();
|
||||
|
||||
if (!inputError) {
|
||||
const system = (({
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
county,
|
||||
country,
|
||||
showScreenName
|
||||
}) => ({
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
county,
|
||||
country,
|
||||
showScreenName
|
||||
}))(this.state);
|
||||
this.props.onSubmit(system);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
var requestMessage = "";
|
||||
var floatStyle = {
|
||||
overflow: "auto"
|
||||
};
|
||||
|
||||
const { isEditing } = this.props;
|
||||
const stateOptions = [
|
||||
{ value: "AL", text: "Alabama" },
|
||||
{ value: "AK", text: "Alaska" },
|
||||
{ value: "AZ", text: "Arizona" },
|
||||
{ value: "AR", text: "Arkansas" },
|
||||
{ value: "CA", text: "California" },
|
||||
{ value: "CO", text: "Colorado" },
|
||||
{ value: "CT", text: "Connecticut" },
|
||||
{ value: "DE", text: "Delaware" },
|
||||
{ value: "DC", text: "District Of Columbia" },
|
||||
{ value: "FL", text: "Florida" },
|
||||
{ value: "GA", text: "Georgia" },
|
||||
{ value: "HI", text: "Hawaii" },
|
||||
{ value: "ID", text: "Idaho" },
|
||||
{ value: "IL", text: "Illinois" },
|
||||
{ value: "IN", text: "Indiana" },
|
||||
{ value: "IA", text: "Iowa" },
|
||||
{ value: "KS", text: "Kansas" },
|
||||
{ value: "KY", text: "Kentucky" },
|
||||
{ value: "LA", text: "Louisiana" },
|
||||
{ value: "ME", text: "Maine" },
|
||||
{ value: "MD", text: "Maryland" },
|
||||
{ value: "MA", text: "Massachusetts" },
|
||||
{ value: "MI", text: "Michigan" },
|
||||
{ value: "MN", text: "Minnesota" },
|
||||
{ value: "MS", text: "Mississippi" },
|
||||
{ value: "MO", text: "Missouri" },
|
||||
{ value: "MT", text: "Montana" },
|
||||
{ value: "NE", text: "Nebraska" },
|
||||
{ value: "NV", text: "Nevada" },
|
||||
{ value: "NH", text: "New Hampshire" },
|
||||
{ value: "NJ", text: "New Jersey" },
|
||||
{ value: "NM", text: "New Mexico" },
|
||||
{ value: "NY", text: "New York" },
|
||||
{ value: "NC", text: "North Carolina" },
|
||||
{ value: "ND", text: "North Dakota" },
|
||||
{ value: "OH", text: "Ohio" },
|
||||
{ value: "OK", text: "Oklahoma" },
|
||||
{ value: "OR", text: "Oregon" },
|
||||
{ value: "PA", text: "Pennsylvania" },
|
||||
{ value: "RI", text: "Rhode Island" },
|
||||
{ value: "SC", text: "South Carolina" },
|
||||
{ value: "SD", text: "South Dakota" },
|
||||
{ value: "TN", text: "Tennessee" },
|
||||
{ value: "TX", text: "Texas" },
|
||||
{ value: "UT", text: "Utah" },
|
||||
{ value: "VT", text: "Vermont" },
|
||||
{ value: "VA", text: "Virginia" },
|
||||
{ value: "WA", text: "Washington" },
|
||||
{ value: "WV", text: "West Virginia" },
|
||||
{ value: "WI", text: "Wisconsin" },
|
||||
{ value: "WY", text: "Wyoming" }
|
||||
];
|
||||
if (
|
||||
this.state.checkInputMessages.length ||
|
||||
this.props.requestMessage.length
|
||||
) {
|
||||
var counter = 0;
|
||||
requestMessage = (
|
||||
<Message compact warning icon>
|
||||
<Icon name="lightning" />
|
||||
<Message.Content>
|
||||
<Message.Header>Problems...</Message.Header>
|
||||
<ul>
|
||||
{this.state.checkInputMessages.map(function(listValue) {
|
||||
return <li key={`ListItem_${counter++}`}>{listValue}</li>;
|
||||
})}
|
||||
{this.props.requestMessage.length > 0 && (
|
||||
<li key="req-mess">{this.props.requestMessage}</li>
|
||||
)}
|
||||
</ul>
|
||||
</Message.Content>
|
||||
</Message>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Form className="raised padding segment" onSubmit={this.handleSubmit}>
|
||||
<Form.Group widths="equal">
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
type="text"
|
||||
name="name"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.nameError}
|
||||
value={this.state.name}
|
||||
label="System name"
|
||||
placeholder="System Name..."
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
type="text"
|
||||
name="shortName"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.shortNameError}
|
||||
value={this.state.shortName}
|
||||
disabled={isEditing}
|
||||
label="System Short Name (4-10 characters)"
|
||||
placeholder="Short Name..."
|
||||
/>
|
||||
</Form.Field>
|
||||
</Form.Group>
|
||||
<Divider horizontal />
|
||||
<Form.Field>
|
||||
|
||||
<p>Your Screen Name: {this.props.screenName}</p>
|
||||
<Checkbox
|
||||
onChange={this.handleCheckboxChange}
|
||||
checked={this.state.showScreenName}
|
||||
label="Show in System Listing" />
|
||||
|
||||
</Form.Field>
|
||||
<Divider horizontal />
|
||||
<Form.TextArea
|
||||
label="Description"
|
||||
name="description"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.descriptionError}
|
||||
value={this.state.description}
|
||||
rows={2}
|
||||
placeholder="Description of system..."
|
||||
/>
|
||||
|
||||
<Divider horizontal><Icon name="marker" />Location</Divider>
|
||||
<Form.Group inline>
|
||||
<label>System Type</label>
|
||||
<Form.Radio
|
||||
label="State"
|
||||
name="systemType"
|
||||
value="state"
|
||||
checked={this.state.systemType === "state"}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
<Form.Radio
|
||||
label="County"
|
||||
name="systemType"
|
||||
value="county"
|
||||
checked={this.state.systemType === "county"}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
<Form.Radio
|
||||
label="City"
|
||||
name="systemType"
|
||||
value="city"
|
||||
checked={this.state.systemType === "city"}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
<Form.Radio
|
||||
label="International"
|
||||
name="systemType"
|
||||
value="international"
|
||||
checked={this.state.systemType === "international"}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group widths="equal">
|
||||
|
||||
{this.state.systemType === "city" && (
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
type="text"
|
||||
name="city"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.cityError}
|
||||
value={this.state.city}
|
||||
label="City"
|
||||
placeholder="City..."
|
||||
/>
|
||||
</Form.Field>
|
||||
)}
|
||||
{this.state.systemType === "county" && (
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
type="text"
|
||||
name="county"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.countyError}
|
||||
value={this.state.county}
|
||||
label="County"
|
||||
placeholder="County..."
|
||||
/>
|
||||
</Form.Field>
|
||||
)}
|
||||
{this.state.systemType === "international" ? (
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
type="text"
|
||||
name="country"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.countryError}
|
||||
value={this.state.country}
|
||||
label="Country"
|
||||
placeholder="Country..."
|
||||
/>
|
||||
</Form.Field>
|
||||
) : (
|
||||
<Form.Field>
|
||||
<Form.Dropdown
|
||||
name="state"
|
||||
onChange={this.handleInputChange}
|
||||
error={this.state.stateError}
|
||||
value={this.state.state}
|
||||
options={stateOptions}
|
||||
label="State"
|
||||
placeholder="Select State..."
|
||||
/>
|
||||
</Form.Field>
|
||||
)}
|
||||
|
||||
</Form.Group>
|
||||
|
||||
<div style={floatStyle}>
|
||||
<Button type="submit" size="large" floated="right" value="Login">
|
||||
{isEditing != true ? "Create" : "Update"}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
{requestMessage}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SystemForm;
|
||||
48
admin/src/System/UpdateSystem.js
Normal file
48
admin/src/System/UpdateSystem.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import React, { Component } from "react"
|
||||
import SystemForm from "./SystemForm";
|
||||
import {
|
||||
Container,
|
||||
Header
|
||||
} from "semantic-ui-react";
|
||||
|
||||
class UpdateSystem extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSystemSubmit = this.handleSystemSubmit.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
requestMessage: ""
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { dispatch } = this.props
|
||||
this.props.fetchSystems()
|
||||
}
|
||||
|
||||
handleSystemSubmit(system) {
|
||||
this.props
|
||||
.updateSystem(system)
|
||||
.then(requestMessage => {
|
||||
if (requestMessage) {
|
||||
// report to the user is there was a problem during registration
|
||||
this.setState({
|
||||
requestMessage
|
||||
});
|
||||
} else {
|
||||
this.props.changeUrl("/system/"+system.shortName)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Container text>
|
||||
<Header as="h1">Update System</Header>
|
||||
<SystemForm onSubmit={this.handleSystemSubmit} system={this.props.system} isEditing={true} requestMessage={this.state.requestMessage} screenName={this.props.user.screenName}/>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default UpdateSystem
|
||||
18
admin/src/System/UpdateSystemContainer.js
Normal file
18
admin/src/System/UpdateSystemContainer.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { connect } from "react-redux"
|
||||
import UpdateSystem from "./UpdateSystem"
|
||||
import * as systemActions from "./system-actions"
|
||||
|
||||
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
|
||||
return {
|
||||
system: state.system.items.find((item) => item.shortName === props.match.params.shortName),
|
||||
user: state.user
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
systemActions
|
||||
)(UpdateSystem)
|
||||
63
admin/src/System/error-actions.js
Normal file
63
admin/src/System/error-actions.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import axios from "axios";
|
||||
//import { browserHistory } from "react-router"
|
||||
import * as types from "./error-constants";
|
||||
import { push } from 'connected-react-router';
|
||||
|
||||
|
||||
function beginFetch() {
|
||||
return { type: types.FETCH };
|
||||
}
|
||||
|
||||
function fetchSuccess(data) {
|
||||
return { type: types.FETCH_SUCCESS,
|
||||
data };
|
||||
}
|
||||
|
||||
function fetchError(data) {
|
||||
return { type: types.FETCH_ERROR,
|
||||
data };
|
||||
}
|
||||
|
||||
function makeUserRequest(method, data, api) {
|
||||
// returns a Promise
|
||||
return axios({
|
||||
method: method,
|
||||
url: api,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function changeUrl(url) {
|
||||
return dispatch => {
|
||||
dispatch(push(url));
|
||||
};
|
||||
}
|
||||
|
||||
export function fetch(shortName) {
|
||||
return (dispatch, getState) => {
|
||||
|
||||
dispatch(beginFetch());
|
||||
|
||||
return axios
|
||||
.get(process.env.REACT_APP_BACKEND_SERVER + "/" + shortName + "/errors")
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
var data ={
|
||||
shortName: shortName,
|
||||
errors: response.data
|
||||
}
|
||||
dispatch(fetchSuccess(data));
|
||||
} else {
|
||||
dispatch(fetchError());
|
||||
let registerMessage = response.data.message;
|
||||
return registerMessage;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
3
admin/src/System/error-constants.js
Normal file
3
admin/src/System/error-constants.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export const FETCH = "FETCH"
|
||||
export const FETCH_SUCCESS = "FETCH_SUCCESS"
|
||||
export const FETCH_ERROR = "FETCH_ERROR"
|
||||
42
admin/src/System/error-reducer.js
Normal file
42
admin/src/System/error-reducer.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
FETCH,
|
||||
FETCH_SUCCESS,
|
||||
FETCH_ERROR
|
||||
} from "./error-constants";
|
||||
|
||||
|
||||
|
||||
const error = (
|
||||
state = {
|
||||
isWaiting: false,
|
||||
items: {}
|
||||
},
|
||||
action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
|
||||
case FETCH:
|
||||
|
||||
return Object.assign({}, state, { isWaiting: true});
|
||||
|
||||
|
||||
case FETCH_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isWaiting: false,
|
||||
items: {
|
||||
...state.items,
|
||||
[action.data.shortName]: action.data.errors
|
||||
}
|
||||
});
|
||||
|
||||
case FETCH_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
isWaiting: false
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default error;
|
||||
20
admin/src/System/statistic-reducer.js
Normal file
20
admin/src/System/statistic-reducer.js
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
const FETCH_SUCCESS_STATISTIC = "FETCH_SUCCESS_STATISTIC"
|
||||
|
||||
const statistic = (
|
||||
state = {},
|
||||
action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
|
||||
|
||||
case FETCH_SUCCESS_STATISTIC:
|
||||
return Object.assign({}, state, {...action.data});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default statistic;
|
||||
231
admin/src/System/system-actions.js
Normal file
231
admin/src/System/system-actions.js
Normal file
@@ -0,0 +1,231 @@
|
||||
import axios from "axios";
|
||||
//import { browserHistory } from "react-router"
|
||||
import * as types from "./system-constants";
|
||||
import { push } from 'connected-react-router';
|
||||
|
||||
|
||||
// "Log In" action creators
|
||||
function beginCreateSystem() {
|
||||
return { type: types.CREATE_SYSTEM };
|
||||
}
|
||||
|
||||
function createSystemSuccess(data) {
|
||||
return {
|
||||
type: types.CREATE_SUCCESS_SYSTEM,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
function createSystemError(data) {
|
||||
return {
|
||||
type: types.CREATE_ERROR_SYSTEM,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
function beginUpdateSystem() {
|
||||
return { type: types.UPDATE_SYSTEM };
|
||||
}
|
||||
|
||||
function updateSystemSuccess(data) {
|
||||
return { type: types.UPDATE_SUCCESS_SYSTEM, data };
|
||||
}
|
||||
|
||||
function updateSystemError(data) {
|
||||
return { type: types.UPDATE_ERROR_SYSTEM, data };
|
||||
}
|
||||
|
||||
function beginDeleteSystem() {
|
||||
return { type: types.DELETE_SYSTEM };
|
||||
}
|
||||
|
||||
function deleteSystemSuccess(data) {
|
||||
return { type: types.DELETE_SUCCESS_SYSTEM,
|
||||
data };
|
||||
}
|
||||
|
||||
function deleteSystemError() {
|
||||
return { type: types.DELETE_ERROR_SYSTEM };
|
||||
}
|
||||
|
||||
function beginFetchSystem() {
|
||||
return { type: types.FETCH_SYSTEM };
|
||||
}
|
||||
|
||||
function fetchSystemSuccess(data) {
|
||||
return { type: types.FETCH_SUCCESS_SYSTEM,
|
||||
data };
|
||||
}
|
||||
|
||||
function fetchStatisticSuccess(data) {
|
||||
return { type: types.FETCH_SUCCESS_STATISTIC,
|
||||
data };
|
||||
}
|
||||
|
||||
function fetchSystemError(data) {
|
||||
return { type: types.FETCH_ERROR_SYSTEM,
|
||||
data };
|
||||
}
|
||||
|
||||
function beginFetchError() {
|
||||
return { type: types.FETCH_ERROR };
|
||||
}
|
||||
|
||||
function fetchErrorsSuccess(data) {
|
||||
return { type: types.FETCH_SUCCESS_ERROR,
|
||||
data };
|
||||
}
|
||||
|
||||
function fetchErrorsError(data) {
|
||||
return { type: types.FETCH_ERROR_ERROR,
|
||||
data };
|
||||
}
|
||||
|
||||
function makeUserRequest(method, data, api = "/login") {
|
||||
// returns a Promise
|
||||
return axios({
|
||||
method: method,
|
||||
url: api,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function changeUrl(url) {
|
||||
return dispatch => {
|
||||
dispatch(push(url));
|
||||
};
|
||||
}
|
||||
|
||||
// Example of an Async Action Creator
|
||||
// http://redux.js.org/docs/advanced/AsyncActions.html
|
||||
|
||||
export function updateSystem(data) {
|
||||
return dispatch => {
|
||||
dispatch(beginUpdateSystem());
|
||||
|
||||
return makeUserRequest("post", data, process.env.REACT_APP_BACKEND_SERVER + "/systems/" + data.shortName)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
dispatch(updateSystemSuccess(response.data.system));
|
||||
} else {
|
||||
dispatch(updateSystemError());
|
||||
return response.data.message;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function shouldFetchSystems(state, subreddit) {
|
||||
if ((!state.system.items) || (state.system.items.length <1)) {
|
||||
return true
|
||||
} else if (state.system.isWaiting) {
|
||||
return false
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchErrors(shortName) {
|
||||
return (dispatch, getState) => {
|
||||
|
||||
dispatch(beginFetchError());
|
||||
|
||||
return axios
|
||||
.get(process.env.REACT_APP_BACKEND_SERVER + "/" + shortName + "/systems")
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
dispatch(fetchErrorsSuccess(shortName, response.data));
|
||||
} else {
|
||||
dispatch(fetchErrorsError());
|
||||
let registerMessage = response.data.message;
|
||||
return registerMessage;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSystems() {
|
||||
return (dispatch, getState) => {
|
||||
if (shouldFetchSystems(getState())) {
|
||||
dispatch(beginFetchSystem());
|
||||
|
||||
return axios
|
||||
.get(process.env.REACT_APP_ADMIN_SERVER + "/systems")
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
dispatch(fetchSystemSuccess(response.data.systems));
|
||||
dispatch(fetchStatisticSuccess(response.data.stats));
|
||||
} else {
|
||||
dispatch(fetchSystemError());
|
||||
let registerMessage = response.data.message;
|
||||
return registerMessage;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteSystem(shortName) {
|
||||
return dispatch => {
|
||||
dispatch(beginDeleteSystem());
|
||||
|
||||
return makeUserRequest("delete", null, process.env.REACT_APP_BACKEND_SERVER + "/systems/"+shortName)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
dispatch(deleteSystemSuccess({shortName: shortName}));
|
||||
dispatch(push("/list-systems"));
|
||||
} else {
|
||||
dispatch(deleteSystemError());
|
||||
let registerMessage = response.data.message;
|
||||
return registerMessage;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createSystem(data) {
|
||||
return dispatch => {
|
||||
dispatch(beginCreateSystem());
|
||||
|
||||
return makeUserRequest("post", data, process.env.REACT_APP_BACKEND_SERVER + "/systems")
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
dispatch(createSystemSuccess(response.data.system));
|
||||
} else {
|
||||
dispatch(createSystemError());
|
||||
let registerMessage = response.data.message;
|
||||
return registerMessage;
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
if (response instanceof Error) {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", response.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
16
admin/src/System/system-constants.js
Normal file
16
admin/src/System/system-constants.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export const CREATE_SYSTEM = "CREATE_SYSTEM"
|
||||
export const CREATE_SUCCESS_SYSTEM = "CREATE_SUCCESS_SYSTEM"
|
||||
export const CREATE_ERROR_SYSTEM = "CREATE_ERROR_SYSTEM"
|
||||
export const UPDATE_SYSTEM = "UPDATE_SYSTEM"
|
||||
export const UPDATE_SUCCESS_SYSTEM = "UPDATE_SUCCESS_SYSTEM"
|
||||
export const UPDATE_ERROR_SYSTEM = "UPDATE_ERROR_SYSTEM"
|
||||
export const DELETE_SYSTEM = "DELETE_SYSTEM"
|
||||
export const DELETE_SUCCESS_SYSTEM = "DELETE_SUCCESS_SYSTEM"
|
||||
export const DELETE_ERROR_SYSTEM = "DELETE_ERROR_SYSTEM"
|
||||
export const FETCH_SYSTEM = "FETCH_SYSTEM"
|
||||
export const FETCH_SUCCESS_SYSTEM = "FETCH_SUCCESS_SYSTEM"
|
||||
export const FETCH_ERROR_SYSTEM = "FETCH_ERROR_SYSTEM"
|
||||
export const FETCH_SUCCESS_STATISTIC = "FETCH_SUCCESS_STATISTIC"
|
||||
export const FETCH_ERROR = "FETCH_ERROR"
|
||||
export const FETCH_SUCCESS_ERROR = "FETCH_SUCCESS_ERROR"
|
||||
export const FETCH_ERROR_ERROR = "FETCH_ERROR_ERROR"
|
||||
91
admin/src/System/system-reducer.js
Normal file
91
admin/src/System/system-reducer.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
CREATE_SYSTEM,
|
||||
CREATE_SUCCESS_SYSTEM,
|
||||
CREATE_ERROR_SYSTEM,
|
||||
UPDATE_SYSTEM,
|
||||
UPDATE_SUCCESS_SYSTEM,
|
||||
UPDATE_ERROR_SYSTEM,
|
||||
DELETE_SYSTEM,
|
||||
DELETE_SUCCESS_SYSTEM,
|
||||
DELETE_ERROR_SYSTEM,
|
||||
FETCH_SYSTEM,
|
||||
FETCH_SUCCESS_SYSTEM,
|
||||
FETCH_ERROR_SYSTEM
|
||||
} from "./system-constants";
|
||||
|
||||
function updateObjectInArray(array, shortName, updatedItem) {
|
||||
return array.map( item => {
|
||||
if(item.shortName !== shortName) {
|
||||
// This isn't the item we care about - keep it as-is
|
||||
return item;
|
||||
}
|
||||
|
||||
// Otherwise, this is the one we want - return an updated value
|
||||
return {
|
||||
...item,
|
||||
...updatedItem
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const system = (
|
||||
state = {
|
||||
isWaiting: false,
|
||||
items: []},
|
||||
action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
|
||||
case DELETE_SYSTEM:
|
||||
case UPDATE_SYSTEM:
|
||||
case FETCH_SYSTEM:
|
||||
case CREATE_SYSTEM:
|
||||
|
||||
return Object.assign({}, state, { isWaiting: true});
|
||||
|
||||
case DELETE_SUCCESS_SYSTEM:
|
||||
return Object.assign({}, state, {
|
||||
items: state.items.filter(item => item.shortName !== action.data.shortName)
|
||||
});
|
||||
|
||||
case UPDATE_SUCCESS_SYSTEM:
|
||||
return Object.assign({}, state, {
|
||||
items: updateObjectInArray(state.items, action.data.shortName, action.data)
|
||||
});
|
||||
case FETCH_SUCCESS_SYSTEM:
|
||||
return Object.assign({}, state, {
|
||||
isWaiting: false,
|
||||
items: action.data
|
||||
});
|
||||
case CREATE_SUCCESS_SYSTEM:
|
||||
const newItems = state.items.concat({
|
||||
name: action.data.name,
|
||||
shortName: action.data.shortName,
|
||||
description: action.data.description,
|
||||
systemType: action.data.systemType,
|
||||
city: action.data.city,
|
||||
state: action.data.state,
|
||||
county: action.data.county,
|
||||
country: action.data.country,
|
||||
userId: action.data.userId,
|
||||
id: action.data.id,
|
||||
showScreenName: action.data.showScreenName
|
||||
});
|
||||
return Object.assign({}, state, { isWaiting: false,
|
||||
items: newItems});
|
||||
|
||||
case DELETE_ERROR_SYSTEM:
|
||||
case UPDATE_ERROR_SYSTEM:
|
||||
case FETCH_ERROR_SYSTEM:
|
||||
case CREATE_ERROR_SYSTEM:
|
||||
return Object.assign({}, state, {
|
||||
isWaiting: false
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default system;
|
||||
Reference in New Issue
Block a user