mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-14 01:37:48 -05:00
Merge in upstream changes.
This commit is contained in:
@@ -216,6 +216,14 @@ Rough Playbook (use common sense, I may not have this exact):
|
|||||||
- get into the shell of the mongo container
|
- get into the shell of the mongo container
|
||||||
- `mongorestore --db scanner --drop /data/db/backup/`
|
- `mongorestore --db scanner --drop /data/db/backup/`
|
||||||
|
|
||||||
|
### Privlege Port Error
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Error response from daemon: driver failed programming external connectivity on endpoint trunk-server-nginx-1 (ec995a6333ce5b9869b9070e380d01b81333fdb3f2734bb93922c1ef7d84314d): Error starting userland proxy: error while calling PortManager.AddPort(): cannot expose privileged port 443, you can add 'net.ipv4.ip_unprivileged_port_start=443' to /etc/sysctl.conf (currently 1024), or set CAP_NET_BIND_SERVICE on rootlesskit binary, or choose a larger port number (>= 1024): listen tcp4 0.0.0.0:443: bind: permission denied
|
||||||
|
```
|
||||||
|
|
||||||
|
https://docs.docker.com/engine/security/rootless/#exposing-privileged-ports
|
||||||
|
|
||||||
## Setting up Logging
|
## Setting up Logging
|
||||||
I have had good luck with Loggly. Their free tier provides enough capabilities for most small sites.
|
I have had good luck with Loggly. Their free tier provides enough capabilities for most small sites.
|
||||||
The Doocker Logging Driver works well and is easy to install:
|
The Doocker Logging Driver works well and is easy to install:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# build environment
|
# build environment
|
||||||
FROM node:13.12.0-alpine as build
|
FROM node:19-alpine3.16 as build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV PATH /app/node_modules/.bin:$PATH
|
ENV PATH /app/node_modules/.bin:$PATH
|
||||||
ARG REACT_APP_BACKEND_SERVER
|
ARG REACT_APP_BACKEND_SERVER
|
||||||
@@ -16,7 +16,7 @@ RUN npm run build
|
|||||||
RUN env
|
RUN env
|
||||||
|
|
||||||
# production environment
|
# production environment
|
||||||
FROM node:13.12.0-alpine
|
FROM node:19-alpine3.16
|
||||||
RUN mkdir -p /app/public
|
RUN mkdir -p /app/public
|
||||||
COPY ./package.json /tmp
|
COPY ./package.json /tmp
|
||||||
RUN cd /tmp && npm install
|
RUN cd /tmp && npm install
|
||||||
|
|||||||
20140
account/package-lock.json
generated
20140
account/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,19 +4,18 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@reduxjs/toolkit": "^1.1.0",
|
"@reduxjs/toolkit": "^1.1.0",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^14.4.3",
|
||||||
"axios": "0.21.1",
|
"axios": "1.3.4",
|
||||||
"connected-react-router": "6.8.0",
|
"react": "^18.2.0",
|
||||||
"react": "^17.0.1",
|
"react-dom": "^18.2.0",
|
||||||
"react-dom": "^17.0.1",
|
"react-redux": "^8.0.5",
|
||||||
"react-redux": "^7.1.3",
|
"react-router-dom": "6.9.0",
|
||||||
"react-router-dom": "5.2.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-scripts": "4.0.1",
|
"redux-thunk": "2.4.2",
|
||||||
"redux-thunk": "2.3.0",
|
"semantic-ui-css": "2.5.0",
|
||||||
"semantic-ui-css": "2.4.1",
|
"semantic-ui-react": "2.1.4"
|
||||||
"semantic-ui-react": "2.0.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
@@ -40,13 +39,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "5.0.0",
|
"bcrypt": "5.1.0",
|
||||||
"connect-mongo": "3.2.0",
|
"connect-mongo": "5.0.0",
|
||||||
"express": "4.17.1",
|
"express": "4.18.2",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.3",
|
||||||
"mongoose": "5.11.10",
|
"mongoose": "7.0.3",
|
||||||
"node-mailjet": "3.3.1",
|
"node-mailjet": "6.0.2",
|
||||||
"passport": "0.4.1",
|
"passport": "0.6.0",
|
||||||
"passport-local": "1.0.0",
|
"passport-local": "1.0.0",
|
||||||
"passport-twitter": "1.0.4"
|
"passport-twitter": "1.0.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ const path = require("path");
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const session = require("express-session");
|
const session = require("express-session");
|
||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const connectMongo = require("connect-mongo");
|
const MongoStore = require("connect-mongo");
|
||||||
const secrets = require("./secrets");
|
const secrets = require("./secrets");
|
||||||
|
|
||||||
const MongoStore = connectMongo(session)
|
|
||||||
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||||
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
|
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
|
||||||
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 = frontend_server + ":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)
|
||||||
@@ -26,8 +26,6 @@ module.exports = function(app, passport) {
|
|||||||
app.use(express.static(path.join(process.cwd(), 'public')));
|
app.use(express.static(path.join(process.cwd(), 'public')));
|
||||||
|
|
||||||
const sess = {
|
const sess = {
|
||||||
/*saveUninitialized: false,
|
|
||||||
resave: false,*/
|
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
secret: secrets.sessionSecret,
|
secret: secrets.sessionSecret,
|
||||||
@@ -38,9 +36,12 @@ module.exports = function(app, passport) {
|
|||||||
secure: false,
|
secure: false,
|
||||||
domain: cookie_domain
|
domain: cookie_domain
|
||||||
},
|
},
|
||||||
store: new MongoStore({
|
store: MongoStore.create({
|
||||||
url: secrets.db,
|
mongoUrl: secrets.db,
|
||||||
autoReconnect: true
|
autoReconnect: true,
|
||||||
|
autoRemove: 'interval',
|
||||||
|
autoRemoveInterval: 240,
|
||||||
|
touchAfter: 24 * 3600 // time period in seconds
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,34 +5,38 @@
|
|||||||
|
|
||||||
const LocalStrategy = require("passport-local").Strategy;
|
const LocalStrategy = require("passport-local").Strategy;
|
||||||
const User = require("../../models/user");
|
const User = require("../../models/user");
|
||||||
/*
|
/*
|
||||||
By default, LocalStrategy expects to find credentials in parameters named username and password.
|
By default, LocalStrategy expects to find credentials in parameters named username and password.
|
||||||
If your site prefers to name these fields differently, options are available to change the defaults.
|
If your site prefers to name these fields differently, options are available to change the defaults.
|
||||||
*/
|
*/
|
||||||
const local = new LocalStrategy({
|
const local = new LocalStrategy({
|
||||||
usernameField: "email"
|
usernameField: "email"
|
||||||
}, (email, password, done) => {
|
}, async (email, password, done) => {
|
||||||
User.findOne({
|
console.log(`${email} ${password}`);
|
||||||
$or: [{
|
const user = await User.findOne({
|
||||||
email
|
$or: [{
|
||||||
}, {
|
email
|
||||||
local: {
|
}, {
|
||||||
email
|
local: {
|
||||||
}
|
email
|
||||||
}]
|
}
|
||||||
}, (err, user) => {
|
}]
|
||||||
if(!user) {
|
});
|
||||||
console.error("Auth Error - user not found: " + email);
|
if (!user) {
|
||||||
return done(null, false, { message: `Invalid email or password` })
|
console.error("Auth Error - user not found: " + email);
|
||||||
|
return done(null, false, { message: `Invalid email or password` })
|
||||||
|
}
|
||||||
|
if (!user.confirmEmail) {
|
||||||
|
console.error("Auth Error - user has not confirmed email: " + email);
|
||||||
|
return done(null, false, { message: `User's email is not confirmed` })
|
||||||
|
}
|
||||||
|
user.comparePassword(password, (err, isMatch) => {
|
||||||
|
if (isMatch) {
|
||||||
|
return done(null, user)
|
||||||
|
} else {
|
||||||
|
console.error("Auth Error - password mismatch: " + email);
|
||||||
|
return done(null, false, { message: "Invalid email or password" })
|
||||||
}
|
}
|
||||||
user.comparePassword(password, (err, isMatch) => {
|
})
|
||||||
if (isMatch) {
|
})
|
||||||
return done(null, user)
|
|
||||||
} else {
|
|
||||||
console.error("Auth Error - password mismatch: " + email);
|
|
||||||
return done(null, false, { message: "Invalid email or password" })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
module.exports = local;
|
module.exports = local;
|
||||||
@@ -14,10 +14,13 @@ module.exports = function (app, passport) {
|
|||||||
done(null, user.id)
|
done(null, user.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
passport.deserializeUser((id, done) => {
|
passport.deserializeUser(async (id, done) => {
|
||||||
User.findById(id, (err, user) => {
|
const user = await User.findById(id).exec();
|
||||||
done(err, user)
|
if (user) {
|
||||||
})
|
done(null,user);
|
||||||
|
} else {
|
||||||
|
done("User not found", null);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// use the following strategies
|
// use the following strategies
|
||||||
|
|||||||
@@ -5,58 +5,52 @@ const System = require("../models/system");
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.getUserSystems = function(_id) {
|
exports.getUserSystems = async function (_id) {
|
||||||
console.log("Listing Systems for: " + _id);
|
console.log("Listing Systems for: " + _id);
|
||||||
System.find(
|
const userId = new mongoose.Types.ObjectId(req.user._id);
|
||||||
{
|
const systems = await System.find({ userId: userId });
|
||||||
userId: mongoose.Types.ObjectId(_id)
|
if (systems == null) {
|
||||||
},
|
return ({ success: false, message: err });
|
||||||
function(err, systems) {
|
}
|
||||||
if (err) {
|
|
||||||
console.error("Error - ListSystems: " + err);
|
var returnSys = systems.map(obj => {
|
||||||
return({ success: false, message: err });
|
var rObj = (({
|
||||||
}
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
planType,
|
||||||
|
showScreenName
|
||||||
|
}) => ({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
planType,
|
||||||
|
showScreenName
|
||||||
|
}))(obj);
|
||||||
|
rObj.id = obj._id;
|
||||||
|
return rObj;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var returnSys = systems.map(obj => {
|
return ({ success: true, systems: returnSys });
|
||||||
var rObj = (({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
planType,
|
|
||||||
showScreenName
|
|
||||||
}) => ({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
planType,
|
|
||||||
showScreenName
|
|
||||||
}))(obj);
|
|
||||||
rObj.id = obj._id;
|
|
||||||
return rObj;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return({ success: true, systems: returnSys });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.listSystems = function(req, res, next) {
|
|
||||||
return listSystems(req.user._id);
|
exports.listSystems = function (req, res, next) {
|
||||||
}
|
return listSystems(req.user._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
const passport = require("passport");
|
const passport = require("passport");
|
||||||
const User = require("../models/user");
|
const User = require("../models/user");
|
||||||
|
const Mailjet = require('node-mailjet');
|
||||||
|
|
||||||
const mailjet = require("node-mailjet").connect(
|
|
||||||
process.env['MAILJET_KEY'],
|
|
||||||
process.env['MAILJET_SECRET']
|
|
||||||
);
|
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
|
|
||||||
var admin_email = process.env['REACT_APP_ADMIN_EMAIL'] != null ? process.env['REACT_APP_ADMIN_EMAIL'] : "luke@openmhz.com";
|
var admin_email = process.env['REACT_APP_ADMIN_EMAIL'] != null ? process.env['REACT_APP_ADMIN_EMAIL'] : "luke@openmhz.com";
|
||||||
@@ -12,16 +9,25 @@ var site_name = process.env['REACT_APP_SITE_NAME'] != null ? process.env['REACT_
|
|||||||
var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : "https://account.openmhz.com";
|
var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : "https://account.openmhz.com";
|
||||||
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||||
|
|
||||||
exports.isLoggedIn = function(req, res, next) {
|
|
||||||
|
const mailjet = new Mailjet({
|
||||||
|
apiKey: process.env['MAILJET_KEY'],
|
||||||
|
apiSecret: process.env['MAILJET_SECRET']
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exports.isLoggedIn = function (req, res, next) {
|
||||||
if (req.isAuthenticated()) return next();
|
if (req.isAuthenticated()) return next();
|
||||||
res.redirect("/login");
|
res.redirect("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
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,
|
||||||
@@ -54,11 +60,13 @@ exports.authenticated = function(req, res, next) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.login = function(req, res, next) {
|
exports.login = function (req, res, next) {
|
||||||
// Do email and password validation for the server
|
// Do email and password validation for the server
|
||||||
passport.authenticate("local", function(err, user, info) {
|
|
||||||
|
passport.authenticate("local", function (err, user, info) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
console.log("No user");
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: info.message
|
message: info.message
|
||||||
@@ -75,6 +83,7 @@ exports.login = function(req, res, next) {
|
|||||||
req.login(user, loginErr => {
|
req.login(user, loginErr => {
|
||||||
|
|
||||||
if (loginErr) {
|
if (loginErr) {
|
||||||
|
console.log("error")
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: loginErr
|
message: loginErr
|
||||||
@@ -82,10 +91,14 @@ exports.login = function(req, res, next) {
|
|||||||
}
|
}
|
||||||
if (!user.confirmEmail) {
|
if (!user.confirmEmail) {
|
||||||
req.logout();
|
req.logout();
|
||||||
return res.json({
|
req.logout(function (err) {
|
||||||
success: false,
|
if (err) { return next(err); }
|
||||||
message: "unconfirmed email",
|
res.clearCookie('sessionId', { domain: cookie_domain, path: '/' });
|
||||||
userId: user.id
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
message: "unconfirmed email",
|
||||||
|
userId: user.id
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//console.log("account/server/controllers/users.js - req.login() Authenicated: " + user.email);
|
//console.log("account/server/controllers/users.js - req.login() Authenicated: " + user.email);
|
||||||
@@ -118,199 +131,217 @@ exports.login = function(req, res, next) {
|
|||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.confirmEmail = function(req, res, next) {
|
exports.confirmEmail = async function (req, res, next) {
|
||||||
const userId = req.params["userId"];
|
const userId = req.params["userId"];
|
||||||
const token = req.params["token"];
|
const token = req.params["token"];
|
||||||
|
|
||||||
User.findById(userId, (err, user) => {
|
let user = await User.findById(userId).catch(err => {
|
||||||
if (err) {
|
console.error(err);
|
||||||
console.error(err);
|
res.status(404);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (user.confirmEmail) {
|
|
||||||
console.log("User already confirmed email: " + userId);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "already confirmed email"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const today = new Date();
|
|
||||||
if (user.confirmEmailTTL < today) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "token expired"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (user.confirmEmailToken != token) {
|
|
||||||
console.log(
|
|
||||||
"Token Mismatch DB: " + user.confirmEmailToken + " submitted: " + token
|
|
||||||
);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "token mismatch"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
user.confirmEmail = true;
|
|
||||||
user.confirmEmailToken = "";
|
|
||||||
user.save(err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("User: " + user.email + " confirmed email address");
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.error("User not found: " + userId);
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "User not found"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user.confirmEmail) {
|
||||||
|
console.log("User already confirmed email: " + userId);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "already confirmed email"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const today = new Date();
|
||||||
|
if (user.confirmEmailTTL < today) {
|
||||||
|
res.status(500);
|
||||||
|
console.error("Expired token for confirming email: " + userId);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "token expired"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user.confirmEmailToken != token) {
|
||||||
|
console.error(
|
||||||
|
"Token Mismatch DB: " + user.confirmEmailToken + " submitted: " + token
|
||||||
|
);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "token mismatch"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
user.confirmEmail = true;
|
||||||
|
user.confirmEmailToken = "";
|
||||||
|
await user.save().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("User: " + user.email + " confirmed email address");
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.resetPassword = function(req, res, next) {
|
exports.resetPassword = async function (req, res, next) {
|
||||||
const userId = req.params["userId"];
|
const userId = req.params["userId"];
|
||||||
const token = req.params["token"];
|
const token = req.params["token"];
|
||||||
|
|
||||||
User.findById(userId, (err, user) => {
|
let user = await User.findById(userId).catch(err => {
|
||||||
if (err) {
|
console.error(err);
|
||||||
console.error(err);
|
res.status(500);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const today = new Date();
|
|
||||||
if (user.resetPasswordTTL < today) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "token expired"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (user.resetPasswordToken != token) {
|
|
||||||
console.log(
|
|
||||||
"Token Mismatch DB: " + user.resetPasswordToken + " submitted: " + token
|
|
||||||
);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "token mismatch"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.password = req.body.password
|
|
||||||
user.confirmEmail = true;
|
|
||||||
user.confirmEmailToken = "";
|
|
||||||
user.resetPasswordToken = "";
|
|
||||||
user.save(err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.error("User not found: " + userId);
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "User not found"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const today = new Date();
|
||||||
|
if (user.resetPasswordTTL < today) {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "token expired"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user.resetPasswordToken != token) {
|
||||||
|
console.log(
|
||||||
|
"Token Mismatch DB: " + user.resetPasswordToken + " submitted: " + token
|
||||||
|
);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "token mismatch"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.password = req.body.password
|
||||||
|
user.confirmEmail = true;
|
||||||
|
user.confirmEmailToken = "";
|
||||||
|
user.resetPasswordToken = "";
|
||||||
|
await user.save().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
exports.sendResetPassword = function(req, res, next) {
|
exports.sendResetPassword = async function (req, res, next) {
|
||||||
User.findOne({
|
let user = await User.findOne({
|
||||||
email: req.body.email
|
email: req.body.email
|
||||||
}, (err, user) => {
|
}).catch(err => {
|
||||||
if (err) {
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
console.error("Reset password failed. No user: " + req.body.email);
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "No account register for " + req.body.email
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const buffer = crypto.randomBytes(20);
|
||||||
|
const token = buffer.toString("hex");
|
||||||
|
var tomorrow = new Date();
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
user.resetPasswordToken = token;
|
||||||
|
user.resetPasswordTTL = tomorrow;
|
||||||
|
await user.save().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
const request = mailjet.post("send", {
|
||||||
|
version: "v3.1"
|
||||||
|
}).request({
|
||||||
|
Messages: [{
|
||||||
|
From: {
|
||||||
|
Email: admin_email,
|
||||||
|
Name: site_name + " Admin"
|
||||||
|
},
|
||||||
|
To: [{
|
||||||
|
Email: user.email,
|
||||||
|
Name: user.firstName + " " + user.lastName
|
||||||
|
}],
|
||||||
|
Subject: site_name + " - Password Reset",
|
||||||
|
TextPart: "It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset. /r" + account_server + "/reset-password/" + user.id + "/" + token,
|
||||||
|
HTMLPart: "<h3>Thanks for using " + site_name + "!</h3><br />It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset.<p>" + account_server + "/reset-password/" + user.id + "/" + token + "</p>"
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
request
|
||||||
|
.then(result => {
|
||||||
|
console.log(`Password reset sent to: ${user.email}`)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err.statusCode);
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if (!user) {
|
|
||||||
console.error("Reset password failed. No user: " + req.body.email);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "No account register for " + req.body.email
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const buffer = crypto.randomBytes(20);
|
|
||||||
const token = buffer.toString("hex");
|
|
||||||
var tomorrow = new Date();
|
|
||||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
||||||
user.resetPasswordToken = token;
|
|
||||||
user.resetPasswordTTL = tomorrow;
|
|
||||||
user.save(err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = mailjet.post("send", {
|
|
||||||
version: "v3.1"
|
|
||||||
}).request({
|
|
||||||
Messages: [{
|
|
||||||
From: {
|
|
||||||
Email: admin_email,
|
|
||||||
Name: site_name + " Admin"
|
|
||||||
},
|
|
||||||
To: [{
|
|
||||||
Email: user.email,
|
|
||||||
Name: user.firstName + " " + user.lastName
|
|
||||||
}],
|
|
||||||
Subject: site_name + " - Password Reset",
|
|
||||||
TextPart: "It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset. /r" + account_server + "/reset-password/" + user.id + "/" + token,
|
|
||||||
HTMLPart: "<h3>Thanks for using " + site_name + "!</h3><br />It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset.<p>" + account_server + "/reset-password/" + user.id + "/" + token + "</p>"
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
request
|
|
||||||
.then(result => {
|
|
||||||
console.log(`Password reset sent to: ${user.email}`)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err.statusCode);
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleSendConfirmEmail(user) {
|
function handleSendConfirmEmail(user) {
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
if (user.confirmEmail) {
|
if (user.confirmEmail) {
|
||||||
console.log("Error - Send Confirm Email - User already confirmed email: " + user.email);
|
console.log("Error - Send Confirm Email - User already confirmed email: " + user.email);
|
||||||
reject({
|
reject({
|
||||||
@@ -326,24 +357,23 @@ function handleSendConfirmEmail(user) {
|
|||||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
user.confirmEmailToken = token;
|
user.confirmEmailToken = token;
|
||||||
user.confirmEmailTTL = tomorrow;
|
user.confirmEmailTTL = tomorrow;
|
||||||
user.save().then(function(user) {
|
await user.save();
|
||||||
return mailjet.post("send", {
|
mailjet.post("send", {
|
||||||
version: "v3.1"
|
version: "v3.1"
|
||||||
}).request({
|
}).request({
|
||||||
Messages: [{
|
Messages: [{
|
||||||
From: {
|
From: {
|
||||||
Email: admin_email,
|
Email: admin_email,
|
||||||
Name: site_name + " Admin"
|
Name: site_name + " Admin"
|
||||||
},
|
},
|
||||||
To: [{
|
To: [{
|
||||||
Email: user.email,
|
Email: user.email,
|
||||||
Name: user.firstName + " " + user.lastName
|
Name: user.firstName + " " + user.lastName
|
||||||
}],
|
}],
|
||||||
Subject: "Confirm " + site_name + " Account",
|
Subject: "Confirm " + site_name + " Account",
|
||||||
TextPart: "Thanks for signing up for " + site_name + ". We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address: " + account_server + "/confirm-email/" + user._id + "/" + token,
|
TextPart: "Thanks for signing up for " + site_name + ". We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address: " + account_server + "/confirm-email/" + user._id + "/" + token,
|
||||||
HTMLPart: "<h3>Thanks for signing up for " + site_name + "</h3><br />We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address:<p> " + account_server + "/confirm-email/" + user._id + "/" + token + "</p>"
|
HTMLPart: "<h3>Thanks for signing up for " + site_name + "</h3><br />We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address:<p> " + account_server + "/confirm-email/" + user._id + "/" + token + "</p>"
|
||||||
}]
|
}]
|
||||||
});
|
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
console.log("Confirm email sent to: " + user.email);
|
console.log("Confirm email sent to: " + user.email);
|
||||||
resolve({
|
resolve({
|
||||||
@@ -353,103 +383,110 @@ function handleSendConfirmEmail(user) {
|
|||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error("Admin Email: " + admin_email + " User Email: " + user.email);
|
console.error("Admin Email: " + admin_email + " User Email: " + user.email);
|
||||||
console.error("Error - Send Confirm Email - caught: " + err);
|
console.error("Error - Send Confirm Email - caught: " + err);
|
||||||
|
res.status(500);
|
||||||
reject({
|
reject({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
exports.sendConfirmEmail = function(req, res, next) {
|
exports.sendConfirmEmail = async function (req, res, next) {
|
||||||
const userId = req.params["userId"];
|
const userId = req.params["userId"];
|
||||||
User.findById(userId, (err, user) => {
|
let user = await User.findById(userId).catch(err => {
|
||||||
if (err) {
|
|
||||||
console.error("Error - Send Confirm Email: " + err);
|
console.error("Error - Send Confirm Email: " + err);
|
||||||
res.json({
|
res.status(500);
|
||||||
success: false,
|
res.json({
|
||||||
message: err
|
success: false,
|
||||||
});
|
message: err
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
if (!user) {
|
});
|
||||||
console.error("Error - Send Confirm Email: User not found " + userId);
|
if (!user) {
|
||||||
res.json({
|
console.error("Error - Send Confirm Email: User not found " + userId);
|
||||||
success: false,
|
res.status(404);
|
||||||
message: "user not found"
|
res.json({
|
||||||
});
|
success: false,
|
||||||
return;
|
message: "user not found"
|
||||||
}
|
});
|
||||||
handleSendConfirmEmail(user).then(function(result) {
|
return;
|
||||||
res.json(result);
|
}
|
||||||
return;
|
handleSendConfirmEmail(user).then(function (result) {
|
||||||
}).catch(function(error) {
|
res.json(result);
|
||||||
res.json(error);
|
return;
|
||||||
return;
|
}).catch(function (error) {
|
||||||
|
res.json(error);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
exports.logout = function (req, res, next) {
|
||||||
|
// the logout method is added to the request object automatically by Passport
|
||||||
|
req.logout(function (err) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
|
||||||
|
res.clearCookie('sessionId', { domain: cookie_domain, path: '/' });
|
||||||
|
return res.json({
|
||||||
|
success: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------
|
exports.terms = async function (req, res, next) {
|
||||||
|
const userId = req.params["userId"];
|
||||||
|
let user = await User.findById(userId).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
exports.logout = function(req, res, next) {
|
if (req.user.id != userId) {
|
||||||
// the logout method is added to the request object automatically by Passport
|
console.log(
|
||||||
req.logout();
|
"Logged in user's ID: " +
|
||||||
res.clearCookie('sessionId', {domain: cookie_domain, path:'/'});
|
req.user.id +
|
||||||
return res.json({
|
" does not match Param: " +
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "UserID incorrect"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.terms = 1.1; //User.termsVer;
|
||||||
|
await user.save().catch(err => {
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
res.json({
|
||||||
success: true
|
success: true
|
||||||
});
|
});
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
exports.terms = function(req, res, next) {
|
|
||||||
const userId = req.params["userId"];
|
|
||||||
User.findById(userId, (err, user) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (req.user.id != userId) {
|
|
||||||
console.log(
|
|
||||||
"Logged in user's ID: " +
|
|
||||||
req.user.id +
|
|
||||||
" does not match Param: " +
|
|
||||||
userId
|
|
||||||
);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "UserID incorrect"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.terms = 1.1; //User.termsVer;
|
|
||||||
user.save(err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
exports.validateProfile = function(req, res, next) {
|
exports.validateProfile = function (req, res, next) {
|
||||||
console.log("Validating user profile: "+ req.body.email);
|
console.log("Validating user profile: " + req.body.email);
|
||||||
if (!req.body.firstName || (req.body.firstName.length < 2)) {
|
if (!req.body.firstName || (req.body.firstName.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.firstName");
|
console.error("ERROR: Validate System - req.body.firstName");
|
||||||
res.json({
|
res.json({
|
||||||
@@ -486,68 +523,97 @@ exports.validateProfile = function(req, res, next) {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateProfile = function(req, res, next) {
|
exports.updateProfile = async function (req, res, next) {
|
||||||
const userId = req.params["userId"];
|
const userId = req.params["userId"];
|
||||||
|
|
||||||
User.findById(userId, (err, user) => {
|
user = await User.findById(userId).catch(err => {
|
||||||
// is email address already in use?
|
console.error(err);
|
||||||
if (req.user.id != userId) {
|
res.status(500);
|
||||||
console.log(
|
res.json({
|
||||||
"ERROR: Logged in user's ID: " +
|
success: false,
|
||||||
req.user.id +
|
message: err
|
||||||
" does not match Param: " +
|
|
||||||
userId
|
|
||||||
);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "UserID incorrect"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// go ahead and create the new user
|
|
||||||
user.firstName = res.locals.firstName
|
|
||||||
user.lastName = res.locals.lastName
|
|
||||||
user.screenName = res.locals.screenName
|
|
||||||
user.location = res.locals.location
|
|
||||||
|
|
||||||
|
|
||||||
user.save(err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var clientUser = (({
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
screenName,
|
|
||||||
location
|
|
||||||
}) => ({
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
screenName,
|
|
||||||
location
|
|
||||||
}))(
|
|
||||||
user
|
|
||||||
);
|
|
||||||
console.log("Updated Profile: " + userId)
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
user: clientUser
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (req.user.id != userId) {
|
||||||
|
console.log(
|
||||||
|
"ERROR: Logged in user's ID: " +
|
||||||
|
req.user.id +
|
||||||
|
" does not match Param: " +
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "UserID incorrect"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lets make sure someone else isn't using this screenName
|
||||||
|
screenNameUser = await User.findOne({ screenName: req.body.screenName }).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Did we find a user with the screeName, is it not us?
|
||||||
|
if (screenNameUser && (screenNameUser.userId != user.userId)) {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Screen Name already in use"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// go ahead and create the new user
|
||||||
|
user.firstName = res.locals.firstName
|
||||||
|
user.lastName = res.locals.lastName
|
||||||
|
user.screenName = res.locals.screenName
|
||||||
|
user.location = res.locals.location
|
||||||
|
|
||||||
|
|
||||||
|
await user.save().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
var clientUser = (({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
screenName,
|
||||||
|
location
|
||||||
|
}) => ({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
screenName,
|
||||||
|
location
|
||||||
|
}))(
|
||||||
|
user
|
||||||
|
);
|
||||||
|
console.log("Updated Profile: " + userId)
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
user: clientUser
|
||||||
|
});
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.register = function(req, res, next) {
|
exports.register = async function (req, res, next) {
|
||||||
console.log("Registration request for: " + req.body.email);
|
console.log("Registration request for: " + req.body.email);
|
||||||
|
|
||||||
User.findOne({
|
let user = await User.findOne({
|
||||||
$or: [{
|
$or: [{
|
||||||
email: req.body.email
|
email: req.body.email
|
||||||
}, {
|
}, {
|
||||||
@@ -555,49 +621,79 @@ exports.register = function(req, res, next) {
|
|||||||
email: req.body.email
|
email: req.body.email
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}, (err, user) => {
|
}).catch(err => {
|
||||||
// is email address already in use?
|
console.error(err);
|
||||||
if (user) {
|
res.status(500);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Email already in use"
|
message: err
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// go ahead and create the new user
|
|
||||||
var user = (({
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
screenName,
|
|
||||||
location,
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
}) => ({
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
screenName,
|
|
||||||
location,
|
|
||||||
|
|
||||||
}))(
|
|
||||||
res.locals
|
|
||||||
);
|
|
||||||
user.password = req.body.password;
|
|
||||||
user.email = req.body.email;
|
|
||||||
|
|
||||||
User.create(user).then(function(dbUser) {
|
|
||||||
console.log("Successfully registered: " + dbUser.email + ", now sending confirmation email.");
|
|
||||||
// Since regiestration worked, send a confirmation email.
|
|
||||||
return handleSendConfirmEmail(dbUser);
|
|
||||||
}).then(function(result) {
|
|
||||||
res.json(result);
|
|
||||||
return;
|
|
||||||
}).catch(function(err) {
|
|
||||||
console.error("Error creating user: " + user.email + " Error: " +err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
};
|
// is email address already in use?
|
||||||
|
if (user) {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Email already in use"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
user = await User.findOne({ screenName: req.body.screenName }).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
// is email address already in use?
|
||||||
|
if (user) {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Screen Name already in use"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// go ahead and create the new user
|
||||||
|
user = (({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
screenName,
|
||||||
|
location,
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
}) => ({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
screenName,
|
||||||
|
location,
|
||||||
|
|
||||||
|
}))(
|
||||||
|
res.locals
|
||||||
|
);
|
||||||
|
user.password = req.body.password;
|
||||||
|
user.email = req.body.email;
|
||||||
|
|
||||||
|
let savedUser = await User.create(user)
|
||||||
|
|
||||||
|
console.log("Successfully registered: " + user.email + ", now sending confirmation email.");
|
||||||
|
// Since registration worked, send a confirmation email.
|
||||||
|
handleSendConfirmEmail(savedUser).then(function (result) {
|
||||||
|
console.log("Confirmation email sent to: " + user.email);
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: result
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.error("Error creating user: " + user.email + " Error: " + err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -15,39 +15,39 @@ const app = express()
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
async function connect() {
|
const connect = async () => {
|
||||||
|
|
||||||
// Demonstrate the readyState and on event emitters
|
// Demonstrate the readyState and on event emitters
|
||||||
console.log(mongoose.connection.readyState); //logs 0
|
console.log(mongoose.connection.readyState); //logs 0
|
||||||
mongoose.connection.on('connecting', () => {
|
mongoose.connection.on('connecting', () => {
|
||||||
console.log('Mongoose is connecting')
|
console.log('Mongoose is connecting')
|
||||||
console.log(mongoose.connection.readyState); //logs 2
|
console.log(mongoose.connection.readyState); //logs 2
|
||||||
});
|
});
|
||||||
mongoose.connection.on('connected', () => {
|
mongoose.connection.on('connected', () => {
|
||||||
console.log('Mongoose is connected');
|
console.log('Mongoose is connected');
|
||||||
console.log(mongoose.connection.readyState); //logs 1
|
console.log(mongoose.connection.readyState); //logs 1
|
||||||
});
|
});
|
||||||
mongoose.connection.on('disconnecting', () => {
|
mongoose.connection.on('disconnecting', () => {
|
||||||
console.log('Mongoose is disconnecting');
|
console.log('Mongoose is disconnecting');
|
||||||
console.log(mongoose.connection.readyState); // logs 3
|
console.log(mongoose.connection.readyState); // logs 3
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect to a MongoDB server running on 'localhost:27017' and use the
|
// Connect to a MongoDB server running on 'localhost:27017' and use the
|
||||||
// 'test' database.
|
// 'test' database.
|
||||||
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }, (err, res) => {
|
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true }).catch(err => {
|
||||||
if (err) {
|
|
||||||
console.log(`Mongoose - Error connecting to ${secrets.db}. ${err}`)
|
console.error(`Mongoose - Error connecting to ${secrets.db}. ${err}`)
|
||||||
} else {
|
|
||||||
console.log(`Mongoose Successfully connected to ${secrets.db}.`)
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log("All Done");
|
console.log("All Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
connect();
|
connect();
|
||||||
|
|
||||||
mongoose.connection.on('error', err => {
|
mongoose.connection.on('error', err => {
|
||||||
console.error("Mongoose Error!")
|
console.error("Mongoose Error!")
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
mongoose.connection.on('disconnected', () => {
|
mongoose.connection.on('disconnected', () => {
|
||||||
console.log('Mongoose disconnected');
|
console.log('Mongoose disconnected');
|
||||||
console.log(mongoose.connection.readyState); //logs 0
|
console.log(mongoose.connection.readyState); //logs 0
|
||||||
@@ -72,16 +72,16 @@ configurePassport(app, passport)
|
|||||||
configureExpress(app, passport)
|
configureExpress(app, passport)
|
||||||
|
|
||||||
|
|
||||||
app.all('*', function(req, res, next) {
|
app.all('*', function (req, res, next) {
|
||||||
var allowedOrigins = [account_server, admin_server, frontend_server];
|
var allowedOrigins = [account_server, admin_server, frontend_server];
|
||||||
var origin = req.headers.origin;
|
var origin = req.headers.origin;
|
||||||
if(allowedOrigins.indexOf(origin) > -1){
|
if (allowedOrigins.indexOf(origin) > -1) {
|
||||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||||
}
|
}
|
||||||
res.header("Access-Control-Allow-Credentials", "true");
|
res.header("Access-Control-Allow-Credentials", "true");
|
||||||
res.header("Access-Control-Allow-Headers", "X-Requested-With");
|
res.header("Access-Control-Allow-Headers", "X-Requested-With");
|
||||||
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
@@ -94,8 +94,8 @@ app.post("/users/:userId/reset-password/:token", users.resetPassword)
|
|||||||
app.post("/api/send-reset-password", users.sendResetPassword)
|
app.post("/api/send-reset-password", users.sendResetPassword)
|
||||||
app.post("/users/:userId", users.isLoggedIn, users.validateProfile, users.updateProfile)
|
app.post("/users/:userId", users.isLoggedIn, users.validateProfile, users.updateProfile)
|
||||||
app.post("/users/:userId/terms", users.isLoggedIn, users.terms)
|
app.post("/users/:userId/terms", users.isLoggedIn, users.terms)
|
||||||
app.post("/users/:userId/send-confirm", users.sendConfirmEmail)
|
app.post("/users/:userId/send-confirm", users.sendConfirmEmail)
|
||||||
app.post("/users/:userId/confirm/:token", users.confirmEmail)
|
app.post("/users/:userId/confirm/:token", users.confirmEmail)
|
||||||
|
|
||||||
app.get("*", (req, res, next) => {
|
app.get("*", (req, res, next) => {
|
||||||
res.sendFile(__dirname + '/public/index.html');
|
res.sendFile(__dirname + '/public/index.html');
|
||||||
|
|||||||
@@ -1,15 +1,38 @@
|
|||||||
import React, { Component } from "react"
|
import React from "react"
|
||||||
import NavigationContainer from "./NavigationContainer"
|
import Navigation from "./Navigation"
|
||||||
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
class App extends Component {
|
import Restricted from "./Restricted"
|
||||||
render() {
|
|
||||||
return(
|
import Login from "../User/Login"
|
||||||
<div>
|
import Register from "../User/Register"
|
||||||
<NavigationContainer />
|
import Profile from "../User/Profile"
|
||||||
{this.props.children}
|
import ConfirmEmail from "../User/ConfirmEmail"
|
||||||
</div>
|
import WaitConfirmEmail from "../User/WaitConfirmEmail"
|
||||||
)
|
import SentConfirmEmail from "../User/SentConfirmEmail"
|
||||||
}
|
import SendResetPassword from "../User/SendResetPassword"
|
||||||
}
|
import ResetPassword from "../User/ResetPassword"
|
||||||
|
import Terms from "../User/Terms"
|
||||||
|
|
||||||
|
const App = (params) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navigation />
|
||||||
|
<Routes>
|
||||||
|
<Route exact path="/" element={<Restricted><Profile /></Restricted>} />
|
||||||
|
<Route path="/login" element={<Login />} />
|
||||||
|
<Route path="/register" element={<Register />} />
|
||||||
|
<Route path="/wait-confirm-email" element={<WaitConfirmEmail />} />
|
||||||
|
<Route path="/sent-confirm-email" element={<SentConfirmEmail />} />
|
||||||
|
<Route path="/send-reset-password" element={<SendResetPassword />} />
|
||||||
|
<Route path="/reset-password/:userId/:token" element={<ResetPassword />} />
|
||||||
|
<Route path="/confirm-email/:userId/:token" element={<ConfirmEmail />} />
|
||||||
|
<Route path="/terms" element={<Restricted><Terms /></Restricted>} />
|
||||||
|
<Route path="/profile" element={<Restricted><Profile /></Restricted>} />
|
||||||
|
<Route render={() => (<div>Miss</div>)} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|||||||
@@ -1,53 +1,39 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Menu, Dropdown } from "semantic-ui-react";
|
import { Menu, Dropdown } from "semantic-ui-react";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
import { logoutUser } from "../features/user/userSlice";
|
||||||
|
|
||||||
const navStyle = {
|
const navStyle = {
|
||||||
marginBottom: "30px"
|
marginBottom: "30px"
|
||||||
};
|
};
|
||||||
|
|
||||||
class Navigation extends Component {
|
const Navigation = (props) => {
|
||||||
constructor(props) {
|
const dispatch = useDispatch();
|
||||||
super(props);
|
const { email } = useSelector((state) => state.user);
|
||||||
this._logout = this._logout.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logout = event => {
|
const logout = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.manualLogout();
|
dispatch(logoutUser({}));
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
var profileLink = process.env.REACT_APP_ACCOUNT_SERVER + "/profile";
|
||||||
var profileLink = process.env.REACT_APP_ACCOUNT_SERVER + "/profile";
|
return (
|
||||||
return (
|
<div>
|
||||||
<div>
|
<Menu style={navStyle}>
|
||||||
{this.props.user.authenticated ? (
|
<Menu.Item name="systems" header>
|
||||||
<Menu style={navStyle}>
|
{process.env.REACT_APP_SITE_NAME}
|
||||||
<Menu.Item name="systems" header>
|
</Menu.Item>
|
||||||
{process.env.REACT_APP_SITE_NAME}
|
<Menu.Menu position="right">
|
||||||
</Menu.Item>
|
<Dropdown item text={ email }>
|
||||||
<Menu.Menu position="right">
|
<Dropdown.Menu>
|
||||||
{/* <Link to="/update-plans">
|
<Dropdown.Item href={profileLink}>Profile</Dropdown.Item>
|
||||||
<Menu.Item name="systems">Update Plans</Menu.Item>
|
<Dropdown.Item onClick={logout}>Logout</Dropdown.Item>
|
||||||
</Link> */}
|
</Dropdown.Menu>
|
||||||
<Dropdown item text={this.props.user.email}>
|
</Dropdown>
|
||||||
<Dropdown.Menu>
|
</Menu.Menu>
|
||||||
<Dropdown.Item href={profileLink}>Profile</Dropdown.Item>
|
</Menu>
|
||||||
{/* <Dropdown.Item href={billingLink}>Billing</Dropdown.Item> */}
|
</div>
|
||||||
<Dropdown.Item onClick={this._logout}>Logout</Dropdown.Item>
|
);
|
||||||
</Dropdown.Menu>
|
|
||||||
</Dropdown>
|
|
||||||
</Menu.Menu>
|
|
||||||
</Menu>
|
|
||||||
) : (
|
|
||||||
<Menu style={navStyle}>
|
|
||||||
<Menu.Item name="systems" header>
|
|
||||||
OpenMHz
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default Navigation;
|
export default Navigation;
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -1,55 +1,31 @@
|
|||||||
// in src/restricted.js
|
// in src/restricted.js
|
||||||
import React, { Component } from 'react';
|
import React, { useEffect } from "react";
|
||||||
import { withRouter } from 'react-router-dom';
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
import * as types from "../User/user-constants";
|
import { authenticateUser } from "../features/user/userSlice";
|
||||||
import axios from "axios";
|
import { useNavigate } from 'react-router-dom';
|
||||||
/**
|
/**
|
||||||
* Higher-order component (HOC) to wrap restricted pages
|
* Higher-order component (HOC) to wrap restricted pages
|
||||||
*/
|
*/
|
||||||
const restricted = (BaseComponent, store) => {
|
|
||||||
class Restricted extends Component {
|
|
||||||
componentWillMount() {
|
|
||||||
this.checkAuthentication(this.props);
|
|
||||||
}
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if (nextProps.location !== this.props.location) {
|
|
||||||
this.checkAuthentication(nextProps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAuthentication(params) {
|
const Restricted = ({ children }) => {
|
||||||
const { history } = params;
|
const { authenticated, hasAuthenticated } = useSelector((state) => state.user);
|
||||||
const authenticated = store.getState().user.authenticated;
|
const dispatch = useDispatch();
|
||||||
if (!authenticated) {
|
const navigate = useNavigate();
|
||||||
axios
|
|
||||||
.get("/authenticated")
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
const user = response.data.user
|
|
||||||
store.dispatch({
|
|
||||||
type: types.LOGIN_SUCCESS_USER,
|
|
||||||
data: user
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
history.replace({ pathname: '/login',
|
|
||||||
state: { nextPathname: params.location.pathname } });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(response => {
|
|
||||||
if (response instanceof Error) {
|
|
||||||
// Something happened during logout that triggered an Error
|
|
||||||
history.replace({ pathname: '/login',
|
|
||||||
state: { nextPathname: params.location.pathname } });
|
|
||||||
console.log("Error", response.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
useEffect(() => {
|
||||||
}
|
dispatch(authenticateUser({}));
|
||||||
render() {
|
}, []);
|
||||||
return <BaseComponent {...this.props} />;
|
|
||||||
}
|
if (hasAuthenticated) {
|
||||||
|
if (authenticated) {
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
return withRouter(Restricted);
|
else {
|
||||||
}
|
navigate("/login")
|
||||||
export default restricted
|
}
|
||||||
|
}
|
||||||
|
return <div />
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Restricted
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Dimmer,
|
Dimmer,
|
||||||
@@ -7,72 +7,65 @@ import {
|
|||||||
Message,
|
Message,
|
||||||
Icon
|
Icon
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
|
import { confirmEmail } from "../features/user/userSlice";
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
|
||||||
class ConfirmEmail extends Component {
|
const ConfirmEmail = (props) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [confirmMessage, setConfirmMessage] = useState({ success: false, message: "" });
|
||||||
|
const { userId, token } = useParams();
|
||||||
|
|
||||||
state = {
|
|
||||||
loading: true,
|
|
||||||
confirmMessage: { success: false, message:""}
|
|
||||||
};
|
|
||||||
componentDidMount() {
|
|
||||||
const { match: { params } } = this.props;
|
|
||||||
|
|
||||||
this.props
|
const checkConfirmation = async(userId, token) => {
|
||||||
.confirmEmail(params.userId, params.token)
|
const result = await dispatch(confirmEmail({ userId, token })).unwrap();
|
||||||
.then(confirmMessage => {
|
if (result) {
|
||||||
if (confirmMessage) {
|
setConfirmMessage(result);
|
||||||
// report to the user is there was a problem during login
|
setLoading(false);
|
||||||
this.setState({
|
}
|
||||||
confirmMessage: confirmMessage, loading: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
var dimmerProps = { };
|
checkConfirmation(userId, token);
|
||||||
if (this.state.loading) {
|
}, [userId, token]);
|
||||||
dimmerProps["active"] = true;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
|
|
||||||
<Container text>
|
const dimmerProps = { active: loading };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container text>
|
||||||
<Dimmer {...dimmerProps}>
|
<Dimmer {...dimmerProps}>
|
||||||
<Loader indeterminate>Confirming Email Address</Loader>
|
<Loader indeterminate>Confirming Email Address</Loader>
|
||||||
</Dimmer>
|
</Dimmer>
|
||||||
|
|
||||||
|
{confirmMessage.success ? (
|
||||||
{this.state.confirmMessage.success ? (
|
<Message icon>
|
||||||
<Message icon>
|
<Icon name='check' />
|
||||||
|
<Message.Content>
|
||||||
<Icon name='check'/>
|
<Message.Header>Success</Message.Header>
|
||||||
<Message.Content>
|
<p>You have successfully confirmed you email address.</p>
|
||||||
<Message.Header>Success</Message.Header>
|
<Link to="/login">
|
||||||
<p>You have successfully confirmed you email address.</p>
|
|
||||||
<Link to="/">
|
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
content="Continue to Login"
|
content="Continue to Login"
|
||||||
fluid
|
fluid
|
||||||
/></Link>
|
/></Link>
|
||||||
|
</Message.Content>
|
||||||
</Message.Content>
|
</Message>
|
||||||
</Message>
|
) : (
|
||||||
) : (
|
<Message icon>
|
||||||
<Message icon>
|
<Icon name='exclamation' />
|
||||||
<Icon name='exclamation'/>
|
|
||||||
<Message.Content>
|
<Message.Content>
|
||||||
<Message.Header>Error</Message.Header>
|
<Message.Header>Error</Message.Header>
|
||||||
<p>{this.state.confirmMessage.message}</p>
|
<p>{confirmMessage.message}</p>
|
||||||
</Message.Content>
|
</Message.Content>
|
||||||
|
</Message>
|
||||||
|
)}
|
||||||
|
|
||||||
</Message>
|
</Container>
|
||||||
)}
|
);
|
||||||
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfirmEmail;
|
export default ConfirmEmail;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import ConfirmEmail from "./ConfirmEmail"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(ConfirmEmail)
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate, useSearchParams, Link } from 'react-router-dom';
|
||||||
|
import { loginUser } from "../features/user/userSlice";
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Header,
|
Header,
|
||||||
@@ -26,74 +28,54 @@ const forgotStyle = {
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class Login extends Component {
|
const Login = (props) => {
|
||||||
constructor(props) {
|
const [loginMessage, setLoginMessage] = useState("");
|
||||||
super(props);
|
const [email, setEmail] = useState("");
|
||||||
this._onLoginSubmit = this._onLoginSubmit.bind(this);
|
const [password, setPassword] = useState("");
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
const navigate = useNavigate();
|
||||||
this.handleRegisterClick = this.handleRegisterClick.bind(this);
|
let [searchParams] = useSearchParams();
|
||||||
}
|
const nextLocation = searchParams.get("nextLocation");
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
state = {
|
const loginSubmit = async (event) => {
|
||||||
loginMessage: "",
|
|
||||||
email: "",
|
|
||||||
password: ""
|
|
||||||
};
|
|
||||||
handleRegisterClick = () => {
|
|
||||||
this.props.changeUrl("/register");
|
|
||||||
};
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
|
||||||
_onLoginSubmit(event) {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const { email, password } = this.state;
|
const result = await dispatch(loginUser({email,password})).unwrap();
|
||||||
var success = { type: "path", nextPathname: "/" };
|
if (result.success) {
|
||||||
if (this.props.nextPathname) {
|
|
||||||
success.nextPathname = this.props.nextPathname;
|
if (nextLocation) {
|
||||||
}
|
switch (nextLocation) {
|
||||||
if (this.props.nextLocation) {
|
case "frontend":
|
||||||
success.type = "location";
|
window.location = process.env.REACT_APP_FRONTEND_SERVER;
|
||||||
switch (this.props.nextLocation) {
|
break;
|
||||||
case "frontend":
|
default:
|
||||||
success.nextLocation = process.env.REACT_APP_FRONTEND_SERVER;
|
case "admin":
|
||||||
break;
|
window.location = process.env.REACT_APP_ADMIN_SERVER;
|
||||||
default:
|
break;
|
||||||
case "admin":
|
}
|
||||||
success.nextLocation = process.env.REACT_APP_ADMIN_SERVER;
|
} else {
|
||||||
break;
|
navigate("/");
|
||||||
|
}
|
||||||
|
console.log(result)
|
||||||
|
} else {
|
||||||
|
console.error(result);
|
||||||
|
setLoginMessage(result.message);
|
||||||
|
if (result.message === "unconfirmed email") {
|
||||||
|
/*data.userId = response.data.userId;
|
||||||
|
dispatch(loginEmailError(data));*/
|
||||||
|
navigate("/wait-confirm-email");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passed in via react-redux. Returns a promise.
|
|
||||||
this.props
|
|
||||||
.manualLogin(
|
|
||||||
{
|
|
||||||
// this function is passed in via react-redux
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
},
|
|
||||||
success
|
|
||||||
|
|
||||||
) // holds the path to redirect to after login (if any)
|
|
||||||
.then(loginMessage => {
|
|
||||||
if (loginMessage) {
|
|
||||||
// report to the user is there was a problem during login
|
|
||||||
this.setState({
|
|
||||||
loginMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
let loginMessageDisplay =(<></>)
|
||||||
var loginMessage = "";
|
|
||||||
|
|
||||||
if (this.state.loginMessage) {
|
if (loginMessage) {
|
||||||
loginMessage = (
|
loginMessageDisplay = (
|
||||||
<Message icon>
|
<Message icon>
|
||||||
<Icon name="lemon" />
|
<Icon name="lemon" />
|
||||||
<Message.Content>
|
<Message.Content>
|
||||||
<Message.Header>Problems...</Message.Header>
|
<Message.Header>Problems...</Message.Header>
|
||||||
{this.state.loginMessage}
|
{loginMessage}
|
||||||
</Message.Content>
|
</Message.Content>
|
||||||
</Message>
|
</Message>
|
||||||
);
|
);
|
||||||
@@ -147,12 +129,12 @@ class Login extends Component {
|
|||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
content="Register"
|
content="Register"
|
||||||
onClick={this.handleRegisterClick}
|
onClick={()=>navigate("/register")}
|
||||||
fluid
|
fluid
|
||||||
/>
|
/>
|
||||||
<Divider style={dividerStyle} horizontal>Or</Divider>
|
<Divider style={dividerStyle} horizontal>Or</Divider>
|
||||||
<Header as="h3">Login</Header>
|
<Header as="h3">Login</Header>
|
||||||
<Form onSubmit={this._onLoginSubmit}>
|
<Form onSubmit={loginSubmit}>
|
||||||
<Segment padded>
|
<Segment padded>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
@@ -160,7 +142,8 @@ class Login extends Component {
|
|||||||
iconPosition="left"
|
iconPosition="left"
|
||||||
type="text"
|
type="text"
|
||||||
name="email"
|
name="email"
|
||||||
onChange={this.handleInputChange}
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
value={email}
|
||||||
placeholder="E-mail address"
|
placeholder="E-mail address"
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
@@ -170,7 +153,8 @@ class Login extends Component {
|
|||||||
iconPosition="left"
|
iconPosition="left"
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
onChange={this.handleInputChange}
|
value={password}
|
||||||
|
onChange={e => setPassword(e.target.value)}
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
@@ -178,7 +162,7 @@ class Login extends Component {
|
|||||||
<Button type="submit" size="large" value="Login" fluid>
|
<Button type="submit" size="large" value="Login" fluid>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
{loginMessage}
|
{loginMessageDisplay}
|
||||||
</Segment>
|
</Segment>
|
||||||
</Form>
|
</Form>
|
||||||
<div style={forgotStyle} >
|
<div style={forgotStyle} >
|
||||||
@@ -190,6 +174,6 @@ class Login extends Component {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default Login;
|
export default Login;
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
import Login from "./Login"
|
|
||||||
import queryString from '../query-string';
|
|
||||||
|
|
||||||
|
|
||||||
// Function passed in to `connect` to subscribe to Redux store updates.
|
|
||||||
// Any time it updates, mapStateToProps is called.
|
|
||||||
// The second argument "ownProps" contains props passed to the component
|
|
||||||
const mapStateToProps = (state, props) => {
|
|
||||||
|
|
||||||
let nextPathname = false;
|
|
||||||
|
|
||||||
try {nextPathname = props.location.state.nextPathname}
|
|
||||||
catch(err) {}
|
|
||||||
const uri = queryString.parse(props.location.search);
|
|
||||||
let nextLocation = uri['nextLocation'];
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
user: state.user,
|
|
||||||
nextPathname, // this prop passed in by React Router
|
|
||||||
nextLocation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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
|
|
||||||
)(Login)
|
|
||||||
@@ -1,45 +1,36 @@
|
|||||||
import React, { Component } from "react"
|
import { useState, } from "react";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
import UserForm from "./UserForm";
|
import UserForm from "./UserForm";
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Header
|
Header
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
import { updateProfile } from "../features/user/userSlice";
|
||||||
|
|
||||||
class Profile extends Component {
|
const Profile = (props) => {
|
||||||
constructor(props) {
|
const [message, setMessage] = useState("");
|
||||||
super(props);
|
const user = useSelector((state) => state.user);
|
||||||
this.handleProfileSubmit = this.handleProfileSubmit.bind(this);
|
const dispatch = useDispatch();
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
const handleProfileSubmit = async (updatedUser) => {
|
||||||
requestMessage: ""
|
updatedUser.userId = user.userId
|
||||||
|
|
||||||
|
const result = await dispatch(updateProfile(updatedUser)).unwrap();
|
||||||
|
if (result.success) {
|
||||||
|
console.log(result)
|
||||||
|
} else {
|
||||||
|
setMessage(result.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleProfileSubmit(user) {
|
|
||||||
user.userId = this.props.user.userId
|
|
||||||
this.props
|
|
||||||
.updateProfile(user)
|
|
||||||
.then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// The Key on UserForm get updated when the user is loaded. This forces the Component to be completely redrawn, otherwise the changes in the Prop are not copied to State.
|
// The Key on UserForm get updated when the user is loaded. This forces the Component to be completely redrawn, otherwise the changes in the Prop are not copied to State.
|
||||||
return(
|
return(
|
||||||
<Container text>
|
<Container text>
|
||||||
<Header as="h1">Update Profile</Header>
|
<Header as="h1">Update Profile</Header>
|
||||||
<UserForm onSubmit={this.handleProfileSubmit} user={this.props.user} isEditing={true} requestMessage={this.state.requestMessage} isWaiting={this.props.isWaiting} key={this.props.user.userId}/>
|
<UserForm onSubmit={handleProfileSubmit} user={user} isEditing={true} requestMessage={message} isWaiting={props.isWaiting} key={user.userId}/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Profile
|
export default Profile
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import Profile from "./Profile"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user,
|
|
||||||
isWaiting: state.user.isWaiting
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(Profile)
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { useState } from "react";
|
||||||
|
import { registerUser } from "../features/user/userSlice";
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Header,
|
Header,
|
||||||
@@ -8,53 +11,39 @@ import {
|
|||||||
|
|
||||||
import UserForm from "./UserForm";
|
import UserForm from "./UserForm";
|
||||||
|
|
||||||
// ----------------------------------------------------
|
function Register(props) {
|
||||||
class Register extends Component {
|
const [requestMessage, setRequestMessage] = useState("");
|
||||||
constructor(props) {
|
const dispatch = useDispatch();
|
||||||
super(props);
|
const navigate = useNavigate();
|
||||||
this._onRegisterSubmit = this._onRegisterSubmit.bind(this);
|
const onRegisterSubmit = async (user) => {
|
||||||
}
|
const result = await dispatch(registerUser(user)).unwrap();
|
||||||
|
if (result.success) {
|
||||||
|
navigate("/sent-confirm-email");
|
||||||
|
} else {
|
||||||
|
console.error(result);
|
||||||
|
setRequestMessage(result.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
state = {
|
return (
|
||||||
requestMessage: ""
|
<div>
|
||||||
}
|
<Container text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_onRegisterSubmit(user) {
|
|
||||||
this.props
|
|
||||||
.manualRegister(user)
|
|
||||||
.then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Container text>
|
|
||||||
<Message icon>
|
<Message icon>
|
||||||
<Icon name='trophy'/>
|
<Icon name='trophy' />
|
||||||
<Message.Content>
|
<Message.Content>
|
||||||
<Message.Header>Welcome!</Message.Header>
|
<Message.Header>Welcome!</Message.Header>
|
||||||
<p>Creating an account on OpenMHz is the first step in sharing radio system recordings using Trunk Recorder.</p>
|
<p>Creating an account on OpenMHz is the first step in sharing radio system recordings using Trunk Recorder.</p>
|
||||||
<p>After creating an account, head over <a href="https://github.com/robotastic/trunk-recorder">here</a> to learn more about Trunk Recorder</p>
|
<p>After creating an account, head over <a href="https://github.com/robotastic/trunk-recorder">here</a> to learn more about Trunk Recorder</p>
|
||||||
<p> - Luke</p>
|
<p> - Luke</p>
|
||||||
</Message.Content>
|
</Message.Content>
|
||||||
</Message>
|
</Message>
|
||||||
</Container>
|
</Container>
|
||||||
<Container text>
|
<Container text>
|
||||||
<Header style={{ paddingTop: '20px' }} as="h1">Register</Header>
|
<Header style={{ paddingTop: '20px' }} as="h1">Register</Header>
|
||||||
<UserForm onSubmit={this._onRegisterSubmit} requestMessage={this.state.requestMessage} isWaiting={this.props.isWaiting}/>
|
<UserForm onSubmit={onRegisterSubmit} requestMessage={requestMessage} isWaiting={props.isWaiting} />
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Register;
|
export default Register;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
import Register from "./Register"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
isWaiting: state.user.isWaiting
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(Register)
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { resetPassword } from "../features/user/userSlice";
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Form,
|
Form,
|
||||||
@@ -6,101 +9,100 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
|
||||||
class ResetPassword extends Component {
|
const ResetPassword = (props) => {
|
||||||
constructor(props) {
|
const dispatch = useDispatch();
|
||||||
super(props);
|
const navigate = useNavigate();
|
||||||
this.handleResetPassword = this.handleResetPassword.bind(this);
|
const [password, setPassword] = useState("");
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
}
|
const [passwordError, setPasswordError] = useState(false);
|
||||||
state = {
|
const [confirmPasswordError, setConfirmPasswordError] = useState(false);
|
||||||
checkInputMessages: [],
|
const [checkInputMessages, setCheckInputMessages] = useState([]);
|
||||||
password: "",
|
const { userId, token } = useParams();
|
||||||
confirmPassword: "",
|
|
||||||
passwordError: false,
|
|
||||||
confirmPasswordError: false,
|
|
||||||
sentReset: false
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const handleResetPassword = async () => {
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
|
||||||
|
|
||||||
handleResetPassword(event) {
|
|
||||||
let error = false;
|
let error = false;
|
||||||
var checkInputMessages = [];
|
var inputMessages = [];
|
||||||
|
|
||||||
if (this.state.password === "") {
|
if (password === "") {
|
||||||
this.setState({ passwordError: true });
|
setPasswordError(true);
|
||||||
checkInputMessages.push("Password is required");
|
inputMessages.push("Password is required");
|
||||||
error = true;
|
error = true;
|
||||||
} if (this.state.password.length < 7) {
|
} else if (password.length < 7) {
|
||||||
this.setState({ passwordError: true });
|
setPasswordError(true);
|
||||||
checkInputMessages.push("Password must be 7 charecters or more");
|
inputMessages.push("Password must be 7 characters or more");
|
||||||
error = true;
|
|
||||||
}else {
|
|
||||||
this.setState({ passwordError: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.password !== this.state.confirmPassword) {
|
|
||||||
this.setState({ confirmPasswordError: true });
|
|
||||||
checkInputMessages.push("The passwords did not match");
|
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ confirmPasswordError: false });
|
setPasswordError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { match: { params } } = this.props;
|
if (password !== confirmPassword) {
|
||||||
this.setState({ checkInputMessages: checkInputMessages });
|
setConfirmPasswordError(true);
|
||||||
|
inputMessages.push("The passwords did not match");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
setConfirmPasswordError(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckInputMessages(inputMessages);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.props.resetPassword(params.userId, params.token, this.state.password).then(resetSent => {
|
const result = await dispatch(resetPassword({ userId, token, password })).unwrap();
|
||||||
this.setState({resetSent: true});
|
if (result.success) {
|
||||||
});
|
navigate("/login");
|
||||||
|
} else {
|
||||||
|
console.error(result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
render() {
|
<div>
|
||||||
return (
|
<Container text>
|
||||||
<div>
|
|
||||||
<Container text>
|
|
||||||
<Segment>
|
<Segment>
|
||||||
<Form
|
<Form className="raised padding segment">
|
||||||
className="raised padding segment"
|
<Form.Group widths="equal">
|
||||||
onSubmit={this.handleSubmit}
|
<Form.Field>
|
||||||
>
|
<Form.Input
|
||||||
<Form.Group widths="equal">
|
name="password"
|
||||||
<Form.Field>
|
type="password"
|
||||||
<Form.Input
|
onChange={e => setPassword(e.target.value)}
|
||||||
name="password"
|
error={passwordError}
|
||||||
type="password"
|
label="Password"
|
||||||
onChange={this.handleInputChange}
|
placeholder="Password..."
|
||||||
error={this.state.passwordError}
|
/>
|
||||||
label="Password"
|
</Form.Field>
|
||||||
placeholder="Password..."
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
name="confirmPassword"
|
||||||
|
type="password"
|
||||||
|
onChange={e => setConfirmPassword(e.target.value)}
|
||||||
|
error={confirmPasswordError}
|
||||||
|
label="Confirm Password"
|
||||||
|
placeholder="Confirm Password..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
content="Reset Password"
|
||||||
|
onClick={handleResetPassword}
|
||||||
|
fluid
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
{checkInputMessages.length > 0 && (
|
||||||
<Form.Field>
|
<div style={{ marginTop: "10px" }}>
|
||||||
<Form.Input
|
{checkInputMessages.map((msg) => (
|
||||||
name="confirmPassword"
|
<p key={msg} style={{ color: "red" }}>
|
||||||
type="password"
|
{msg}
|
||||||
onChange={this.handleInputChange}
|
</p>
|
||||||
error={this.state.confirmPasswordError}
|
))}
|
||||||
label="Confirm Password"
|
</div>
|
||||||
placeholder="Confirm Password..."
|
)}
|
||||||
/>
|
</Form>
|
||||||
</Form.Field>
|
</Segment>
|
||||||
</Form.Group>
|
|
||||||
<Button
|
|
||||||
size="large"
|
|
||||||
content="Reset Password"
|
|
||||||
onClick={this.handleResetPassword}
|
|
||||||
fluid
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Segment>
|
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default ResetPassword;
|
export default ResetPassword;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import ResetPassword from "./ResetPassword"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(ResetPassword)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
Grid,
|
Grid,
|
||||||
@@ -7,106 +7,97 @@ import {
|
|||||||
Message,
|
Message,
|
||||||
Icon
|
Icon
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
import { sendResetPassword } from "../features/user/userSlice";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
|
||||||
const returnStyle = {
|
|
||||||
marginTop: "30px"
|
|
||||||
};
|
|
||||||
|
|
||||||
class SendResetPassword extends Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
const SendResetPassword = () => {
|
||||||
super(props);
|
const dispatch = useDispatch();
|
||||||
this.handleSendResetPassword = this.handleSendResetPassword.bind(this);
|
const [checkInputMessages, setCheckInputMessages] = useState("");
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
const [email, setEmail] = useState("");
|
||||||
}
|
const [emailError, setEmailError] = useState(false);
|
||||||
state = {
|
const [resetSent, setResetSent] = useState(false);
|
||||||
checkInputMessages: "",
|
|
||||||
email: "",
|
const returnStyle = {
|
||||||
emailError: false,
|
marginTop: "30px"
|
||||||
sentReset: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
const handleSendResetPassword = async () => {
|
||||||
|
if (email === "") {
|
||||||
handleSendResetPassword(event) {
|
setEmailError(true);
|
||||||
|
setCheckInputMessages("Email is required");
|
||||||
|
|
||||||
if (this.state.email === "") {
|
|
||||||
this.setState({ emailError: true, checkInputMessages: "Email is required"});
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.setState({ emailError: false });
|
const result = await dispatch(sendResetPassword({ email })).unwrap();
|
||||||
|
if (result.success) {
|
||||||
this.props.sendResetPassword({email: this.state.email}).then(resetMessage => {
|
setResetSent(true);
|
||||||
if (resetMessage) {
|
setEmailError(true);
|
||||||
// report to the user is there was a problem during login
|
} else {
|
||||||
|
setCheckInputMessages(result.message);
|
||||||
this.setState({resetSent: false, checkInputMessages: resetMessage, emailError: true});
|
console.error(result);
|
||||||
} else {
|
|
||||||
this.setState({resetSent: true});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
render() {
|
|
||||||
var resetMessage = "";
|
|
||||||
if (this.state.checkInputMessages.length) {
|
|
||||||
resetMessage = (
|
|
||||||
<Message compact warning icon>
|
|
||||||
<Icon name="lightning" />
|
|
||||||
<Message.Content>
|
|
||||||
<Message.Header>Problems...</Message.Header>
|
|
||||||
|
|
||||||
{this.state.checkInputMessages}
|
var resetMessage = "";
|
||||||
|
if (checkInputMessages.length) {
|
||||||
</Message.Content>
|
resetMessage = (
|
||||||
</Message>
|
<Message compact warning icon>
|
||||||
);
|
<Icon name="lightning" />
|
||||||
}
|
<Message.Content>
|
||||||
return (
|
<Message.Header>Problems...</Message.Header>
|
||||||
|
{checkInputMessages}
|
||||||
|
</Message.Content>
|
||||||
<Grid verticalAlign='middle' centered>
|
</Message>
|
||||||
<Grid.Row>
|
|
||||||
|
|
||||||
<Grid.Column width="6">
|
|
||||||
{ !this.state.resetSent ? (
|
|
||||||
<div>
|
|
||||||
<h2>Forgot your password?</h2>
|
|
||||||
<Form className="raised padding segment" onSubmit={this.handleSubmit}>
|
|
||||||
Enter the email address for your account.
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
type="text"
|
|
||||||
name="email"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.emailError}
|
|
||||||
label="Email"
|
|
||||||
placeholder="Email..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Button
|
|
||||||
size="large"
|
|
||||||
content="Reset Password"
|
|
||||||
onClick={this.handleSendResetPassword}
|
|
||||||
fluid
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
{resetMessage}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
:( <Segment raised padded='very'>
|
|
||||||
<h2>Reset Link Sent </h2>
|
|
||||||
<div>An email with a link to reset your password was sent to: {this.state.email}</div>
|
|
||||||
<Link to="/" style={returnStyle}><Icon name="left arrow"/>Back to OpenMHz</Link>
|
|
||||||
</Segment>
|
|
||||||
)}
|
|
||||||
</Grid.Column>
|
|
||||||
</Grid.Row>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid verticalAlign="middle" centered>
|
||||||
|
<Grid.Row>
|
||||||
|
<Grid.Column width={6}>
|
||||||
|
{!resetSent ? (
|
||||||
|
<div>
|
||||||
|
<h2>Forgot your password?</h2>
|
||||||
|
<Form className="raised padding segment">
|
||||||
|
Enter the email address for your account.
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="email"
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
value={email}
|
||||||
|
error={emailError}
|
||||||
|
label="Email"
|
||||||
|
placeholder="Email..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
content="Reset Password"
|
||||||
|
onClick={handleSendResetPassword}
|
||||||
|
fluid
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
{resetMessage}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Segment raised padded="very">
|
||||||
|
<h2>Reset Link Sent </h2>
|
||||||
|
<div>
|
||||||
|
An email with a link to reset your password was sent to: {email}
|
||||||
|
</div>
|
||||||
|
<Link to="/" style={returnStyle}>
|
||||||
|
<Icon name="left arrow" />
|
||||||
|
Back to OpenMHz
|
||||||
|
</Link>
|
||||||
|
</Segment>
|
||||||
|
)}
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid.Row>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SendResetPassword;
|
export default SendResetPassword;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import SendResetPassword from "./SendResetPassword"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(SendResetPassword)
|
|
||||||
@@ -1,28 +1,25 @@
|
|||||||
import React, { Component } from "react"
|
import React from "react";
|
||||||
import {
|
import { Container, Message, Icon } from "semantic-ui-react";
|
||||||
Container,
|
import { useSelector } from 'react-redux'
|
||||||
Message,
|
|
||||||
Icon
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
|
|
||||||
class SentConfirmEmail extends Component {
|
const SentConfirmEmail = ({ user }) => {
|
||||||
|
const { email } = useSelector((state) => state.user);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Container text>
|
||||||
|
<Message icon>
|
||||||
|
<Icon name='envelope outline' />
|
||||||
|
<Message.Content>
|
||||||
|
<Message.Header>Confirmation email sent!</Message.Header>
|
||||||
|
<p>
|
||||||
|
We sent an email to the address you gave us: {email}<br />
|
||||||
|
Please click on the link in the email to verify the email address.
|
||||||
|
</p>
|
||||||
|
</Message.Content>
|
||||||
|
</Message>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
export default SentConfirmEmail;
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Container text>
|
|
||||||
<Message icon>
|
|
||||||
<Icon name='envelope outline' />
|
|
||||||
<Message.Content>
|
|
||||||
<Message.Header>Confirmation email sent!</Message.Header>
|
|
||||||
<p>We sent an email to the address you gave us: {this.props.user.email}<br />
|
|
||||||
Please click on the link in the email to verify the email address.</p>
|
|
||||||
</Message.Content>
|
|
||||||
</Message>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SentConfirmEmail
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import SentConfirmEmail from "./SentConfirmEmail"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(SentConfirmEmail)
|
|
||||||
@@ -1,84 +1,63 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import { Segment, Container, Button } from "semantic-ui-react";
|
||||||
Segment,
|
|
||||||
Container,
|
|
||||||
Button
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
|
|
||||||
class Terms extends Component {
|
const Terms = ({ user, acceptTerms }) => {
|
||||||
constructor(props) {
|
const [requestMessage, setRequestMessage] = useState(null);
|
||||||
super(props);
|
|
||||||
this.handleAccept = this.handleAccept.bind(this);
|
|
||||||
this.handleDecline = this.handleDecline.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleAccept = () => {
|
||||||
|
acceptTerms(user.userId).then((requestMessage) => {
|
||||||
|
if (requestMessage) {
|
||||||
|
setRequestMessage(requestMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDecline = () => {};
|
||||||
|
|
||||||
handleAccept() {
|
return (
|
||||||
this.props
|
<div>
|
||||||
.acceptTerms(this.props.user.userId)
|
<Container text>
|
||||||
.then(requestMessage => {
|
<Segment attached>
|
||||||
if (requestMessage) {
|
The agreement is useful to The Guardian to set the rules and guidelines for visitors, users and customers:
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDecline() {
|
Registration: inform users that they must agree to a set of rules when they register on your website or mobile app
|
||||||
|
Disable or terminate accounts: inform users that their accounts can be disabled or terminated if abuses happen on your website or mobile app, e.g. spamming, swearing etc.
|
||||||
|
Owner of content: notify users that you, the company, is the owner of the content appearing on your website or mobile app – except in cases where other users can post content (upload, share, create etc.) where the users are the owners of such content
|
||||||
|
Changes to the agreement: users should be informed about upcoming changes to the agreement before the changes are applied.
|
||||||
|
DMCA: The Guardian is also informing owners of copyright content that they can submit a DMCA notice to the website if they found infringing content in their DMCA clause.
|
||||||
|
And more
|
||||||
|
These are only a few examples of what kind of clauses and disclosures you can add in this kind of agreement.
|
||||||
|
|
||||||
}
|
What is a Terms of Use
|
||||||
|
While a Terms of Use is recommended to have, it’s not mandatory by law as the Privacy Policy agreement is required.
|
||||||
|
|
||||||
render() {
|
This agreement serves a single purpose: to inform users of rules that they must agree to. A Privacy Policy must inform users about your privacy practices (what data you collect, why, how, etc.)
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<Container text>
|
The definition of a Terms of Use agreement is simple:
|
||||||
<Segment attached>
|
|
||||||
The agreement is useful to The Guardian to set the rules and guidelines for visitors, users and customers:
|
|
||||||
|
|
||||||
Registration: inform users that they must agree to a set of rules when they register on your website or mobile app
|
Terms of service (also known as terms of use and terms and conditions, commonly abbreviated as ToS or TOS and TOU) are rules which one must agree to abide by in order to use a service.
|
||||||
Disable or terminate accounts: inform users that their accounts can be disabled or terminated if abuses happen on your website or mobile app, e.g. spamming, swearing etc.
|
|
||||||
Owner of content: notify users that you, the company, is the owner of the content appearing on your website or mobile app – except in cases where other users can post content (upload, share, create etc.) where the users are the owners of such content
|
|
||||||
Changes to the agreement: users should be informed about upcoming changes to the agreement before the changes are applied.
|
|
||||||
DMCA: The Guardian is also informing owners of copyright content that they can submit a DMCA notice to the website if they found infringing content in their DMCA clause.
|
|
||||||
And more
|
|
||||||
These are only a few examples of what kind of clauses and disclosures you can add in this kind of agreement.
|
|
||||||
|
|
||||||
What is a Terms of Use
|
Wikipedia
|
||||||
While a Terms of Use is recommended to have, it’s not mandatory by law as the Privacy Policy agreement is required.
|
Because this agreement simply acts as a contract between you, the company, and the users using or accessing your website or mobile app, the agreement can be named as you’d like:
|
||||||
|
|
||||||
This agreement serves a single purpose: to inform users of rules that they must agree to. A Privacy Policy must inform users about your privacy practices (what data you collect, why, how, etc.)
|
Terms of Use (ToU)
|
||||||
|
Terms of Service (ToS)
|
||||||
The definition of a Terms of Use agreement is simple:
|
Terms and Conditions (T&C)
|
||||||
|
User Agreement
|
||||||
Terms of service (also known as terms of use and terms and conditions, commonly abbreviated as ToS or TOS and TOU) are rules which one must agree to abide by in order to use a service.
|
Conditions of Use
|
||||||
|
And so on
|
||||||
Wikipedia
|
The “Conditions of Use” of Amazon could be named “Terms of Use”:
|
||||||
Because this agreement simply acts as a contract between you, the company, and the users using or accessing your website or mobile app, the agreement can be named as you’d like:
|
|
||||||
|
|
||||||
Terms of Use (ToU)
|
|
||||||
Terms of Service (ToS)
|
|
||||||
Terms and Conditions (T&C)
|
|
||||||
User Agreement
|
|
||||||
Conditions of Use
|
|
||||||
And so on
|
|
||||||
The “Conditions of Use” of Amazon could be named “Terms of Use”:
|
|
||||||
</Segment>
|
</Segment>
|
||||||
<Button.Group attached="bottom">
|
<Button.Group attached="bottom">
|
||||||
<Button
|
<Button onClick={handleDecline}>Decline</Button>
|
||||||
onClick={this.handleDecline}
|
<Button.Or />
|
||||||
>Decline</Button>
|
<Button positive onClick={handleAccept}>
|
||||||
<Button.Or />
|
Accept
|
||||||
<Button positive
|
</Button>
|
||||||
onClick={this.handleAccept}
|
</Button.Group>
|
||||||
>Accept</Button>
|
</Container>
|
||||||
</Button.Group>
|
</div>
|
||||||
</Container>
|
);
|
||||||
</div>
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default Terms;
|
export default Terms;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
import Terms from "./Terms"
|
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(Terms)
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
import React, { useEffect, useState, } from "react";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
Dimmer,
|
Dimmer,
|
||||||
@@ -10,270 +11,266 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class UserForm extends Component {
|
const UserForm = (props) => {
|
||||||
constructor(props) {
|
const [checkInputMessages, setCheckInputMessages] = useState([]);
|
||||||
super(props);
|
const [firstName, setFirstName] = useState("");
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
const [lastName, setLastName] = useState("");
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
const [screenName, setScreenName] = useState("");
|
||||||
this.checkInputs = this.checkInputs.bind(this);
|
const [email, setEmail] = useState("");
|
||||||
this.render = this.render.bind(this)
|
const [location, setLocation] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
|
const [firstNameError, setFirstNameError] = useState(false);
|
||||||
|
const [lastNameError, setLastNameError] = useState(false);
|
||||||
|
const [screenNameError, setScreenNameError] = useState(false);
|
||||||
|
const [emailError, setEmailError] = useState(false);
|
||||||
|
const [locationError, setLocationError] = useState(false);
|
||||||
|
const [passwordError, setPasswordError] = useState(false);
|
||||||
|
const [confirmPasswordError, setConfirmPasswordError] = useState(false);
|
||||||
|
const [isEditing] = useState(props.isEditing);
|
||||||
|
const [changed, setChanged] = useState(false);
|
||||||
|
const requestMessage = props.requestMessage;
|
||||||
|
|
||||||
this.state = {
|
useEffect(() => {
|
||||||
checkInputMessages: [],
|
if (props.isEditing) {
|
||||||
registerMessages: "",
|
setFirstName(props.user.firstName);
|
||||||
firstName: "",
|
setLastName(props.user.lastName);
|
||||||
lastName: "",
|
setScreenName(props.user.screenName);
|
||||||
screenName: "",
|
setEmail(props.user.email);
|
||||||
email: "",
|
setLocation(props.user.location);
|
||||||
location: "",
|
setChanged(false);
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
firstNameError: false,
|
|
||||||
lastNameError: false,
|
|
||||||
screenNameError: false,
|
|
||||||
emailError: false,
|
|
||||||
locationError: false,
|
|
||||||
passwordError: false,
|
|
||||||
confirmPasswordError: false,
|
|
||||||
isEditing: this.props.isEditing
|
|
||||||
};
|
|
||||||
if (this.props.isEditing) {
|
|
||||||
this.state.firstName = this.props.user.firstName;
|
|
||||||
this.state.lastName = this.props.user.lastName;
|
|
||||||
this.state.screenName = this.props.user.screenName;
|
|
||||||
this.state.email = this.props.user.email;
|
|
||||||
this.state.location = this.props.user.location;
|
|
||||||
}
|
}
|
||||||
}
|
}, [props.isEditing, props.user]);
|
||||||
|
|
||||||
|
const checkInputs = () => {
|
||||||
|
|
||||||
checkInputs() {
|
|
||||||
let error = false;
|
let error = false;
|
||||||
var checkInputMessages = [];
|
let inputMessages = [];
|
||||||
|
|
||||||
if (this.state.firstName === "") {
|
if (firstName === "") {
|
||||||
this.setState({ firstNameError: true });
|
setFirstNameError(true);
|
||||||
checkInputMessages.push("First Name is required");
|
inputMessages.push("First Name is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ firstNameError: false });
|
setFirstNameError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.lastName === "") {
|
if (lastName === "") {
|
||||||
this.setState({ lastNameError: true });
|
setLastNameError(true);
|
||||||
checkInputMessages.push("Last Name is required");
|
inputMessages.push("Last Name is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ lastNameError: false });
|
setLastNameError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.email === "") {
|
if (email === "") {
|
||||||
this.setState({ emailError: true });
|
setEmailError(true);
|
||||||
checkInputMessages.push("Email is required");
|
inputMessages.push("Email is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ emailError: false });
|
setEmailError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.location === "") {
|
if (location === "") {
|
||||||
this.setState({ locationError: true });
|
setLocationError(true);
|
||||||
checkInputMessages.push("Location is required");
|
inputMessages.push("Location is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ locationError: false });
|
setLocationError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.screenName === "") {
|
if (screenName === "") {
|
||||||
this.setState({ screenNameError: true });
|
setScreenNameError(true);
|
||||||
checkInputMessages.push("Screen Name is required");
|
inputMessages.push("Screen Name is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ screenNameError: false });
|
setScreenNameError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.isEditing) {
|
if (props.isEditing) {
|
||||||
this.setState({ checkInputMessages: checkInputMessages });
|
setCheckInputMessages(checkInputMessages);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.password === "") {
|
if (password === "") {
|
||||||
this.setState({ passwordError: true });
|
setPasswordError(true);
|
||||||
checkInputMessages.push("Password is required");
|
inputMessages.push("Password is required");
|
||||||
error = true;
|
error = true;
|
||||||
} if (this.state.password.length < 7) {
|
} if (password.length < 7) {
|
||||||
this.setState({ passwordError: true });
|
setPasswordError(true);
|
||||||
checkInputMessages.push("Password must be 7 charecters or more");
|
inputMessages.push("Password must be 7 charecters or more");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ passwordError: false });
|
setPasswordError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.password !== this.state.confirmPassword) {
|
if (password !== confirmPassword) {
|
||||||
this.setState({ confirmPasswordError: true });
|
setConfirmPasswordError(true);
|
||||||
checkInputMessages.push("The passwords did not match");
|
inputMessages.push("The passwords did not match");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ confirmPasswordError: false });
|
setConfirmPasswordError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ checkInputMessages: checkInputMessages });
|
setCheckInputMessages(inputMessages);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
|
||||||
|
|
||||||
handleSubmit(event) {
|
const handleSubmit = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let inputError = this.checkInputs();
|
let inputError = checkInputs();
|
||||||
|
|
||||||
if (!inputError) {
|
if (!inputError) {
|
||||||
const user = (({ firstName, lastName, screenName, location, email, password }) => ({ firstName, lastName, screenName, location, email, password }))(this.state);
|
const user ={ firstName, lastName, screenName, location, email, password };
|
||||||
this.props.onSubmit(user);
|
setChanged(false);
|
||||||
|
props.onSubmit(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
var registerMessage = "";
|
|
||||||
var floatStyle = {
|
|
||||||
overflow: 'auto'
|
|
||||||
}
|
|
||||||
var dimmerProps = {};
|
|
||||||
if (this.props.isWaiting) {
|
|
||||||
dimmerProps["active"] = true;
|
|
||||||
}
|
|
||||||
const { isEditing } = this.props;
|
|
||||||
if (this.state.checkInputMessages.length || this.props.requestMessage.length) {
|
|
||||||
var counter = 0;
|
|
||||||
registerMessage = (
|
|
||||||
<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}
|
|
||||||
>
|
|
||||||
<Dimmer {...dimmerProps}>
|
|
||||||
<Loader indeterminate>Updating Profile</Loader>
|
|
||||||
</Dimmer>
|
|
||||||
|
|
||||||
<Form.Group widths="equal">
|
var registerMessage = "";
|
||||||
<Form.Field>
|
var floatStyle = {
|
||||||
<Form.Input
|
overflow: 'auto'
|
||||||
type="text"
|
}
|
||||||
name="firstName"
|
var dimmerProps = {};
|
||||||
onChange={this.handleInputChange}
|
if (props.isWaiting) {
|
||||||
error={this.state.firstNameError}
|
dimmerProps["active"] = true;
|
||||||
defaultValue={this.state.firstName}
|
}
|
||||||
label="First name"
|
|
||||||
placeholder="First Name..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
type="text"
|
|
||||||
name="lastName"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.lastNameError}
|
|
||||||
defaultValue={this.state.lastName}
|
|
||||||
label="Last name"
|
|
||||||
placeholder="Last Name..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form.Group>
|
|
||||||
<Form.Group widths="equal">
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
type="text"
|
|
||||||
name="email"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.emailError}
|
|
||||||
defaultValue={this.state.email}
|
|
||||||
disabled={isEditing}
|
|
||||||
label="Email"
|
|
||||||
placeholder="Email..."
|
|
||||||
/>
|
|
||||||
|
|
||||||
</Form.Field>
|
if (checkInputMessages.length || requestMessage.length) {
|
||||||
<Form.Field>
|
var counter = 0;
|
||||||
<Form.Input
|
registerMessage = (
|
||||||
type="text"
|
<Message compact warning icon>
|
||||||
name="screenName"
|
<Icon name="lightning" />
|
||||||
onChange={this.handleInputChange}
|
<Message.Content>
|
||||||
error={this.state.screenNameError}
|
<Message.Header>Problems...</Message.Header>
|
||||||
defaultValue={this.state.screenName}
|
<ul>
|
||||||
label="Screen Name"
|
{checkInputMessages.map(function (listValue) {
|
||||||
placeholder="Screen Name..."
|
return <li key={`ListItem_${counter++}`}>{listValue}</li>;
|
||||||
/>
|
})}
|
||||||
<p style={{ fontStyle: "italic" }}>
|
{requestMessage.length > 0 &&
|
||||||
Shown on OpenMHz for each of your systems
|
<li key="req-mess">{requestMessage}</li>
|
||||||
</p>
|
}
|
||||||
</Form.Field>
|
</ul>
|
||||||
</Form.Group>
|
</Message.Content>
|
||||||
<Form.Group widths="equal">
|
</Message>
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
type="text"
|
|
||||||
name="location"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.locationError}
|
|
||||||
defaultValue={this.state.location}
|
|
||||||
label="General Location"
|
|
||||||
placeholder="City, State..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form.Group>
|
|
||||||
{isEditing !== true && (
|
|
||||||
<Form.Group widths="equal">
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.passwordError}
|
|
||||||
disabled={isEditing}
|
|
||||||
label="Password"
|
|
||||||
placeholder="Password..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
name="confirmPassword"
|
|
||||||
type="password"
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
error={this.state.confirmPasswordError}
|
|
||||||
disabled={isEditing}
|
|
||||||
label="Confirm Password"
|
|
||||||
placeholder="Confirm Password..."
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form.Group>
|
|
||||||
)}
|
|
||||||
<div style={floatStyle}>
|
|
||||||
<Button type="submit" size="large" floated="right" value="Login" color='blue'>
|
|
||||||
{isEditing !== true ? 'Register' : 'Update'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
{registerMessage}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form
|
||||||
|
className="raised padding segment"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Dimmer {...dimmerProps}>
|
||||||
|
<Loader indeterminate>Updating Profile</Loader>
|
||||||
|
</Dimmer>
|
||||||
|
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="firstName"
|
||||||
|
onChange={e => {setFirstName(e.target.value); setChanged(true)}}
|
||||||
|
error={firstNameError}
|
||||||
|
value={firstName}
|
||||||
|
label="First name"
|
||||||
|
placeholder="First Name..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="lastName"
|
||||||
|
onChange={e => {setLastName(e.target.value); setChanged(true)}}
|
||||||
|
error={lastNameError}
|
||||||
|
value={lastName}
|
||||||
|
label="Last name"
|
||||||
|
placeholder="Last Name..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="email"
|
||||||
|
onChange={e => {setEmail(e.target.value); setChanged(true)}}
|
||||||
|
error={emailError}
|
||||||
|
value={email}
|
||||||
|
disabled={isEditing}
|
||||||
|
label="Email"
|
||||||
|
placeholder="Email..."
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="screenName"
|
||||||
|
onChange={e => {setScreenName(e.target.value); setChanged(true)}}
|
||||||
|
error={screenNameError}
|
||||||
|
value={screenName}
|
||||||
|
label="Screen Name"
|
||||||
|
placeholder="Screen Name..."
|
||||||
|
/>
|
||||||
|
<p style={{ fontStyle: "italic" }}>
|
||||||
|
Shown on OpenMHz for each of your systems
|
||||||
|
</p>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="location"
|
||||||
|
onChange={e => {setLocation(e.target.value); setChanged(true)}}
|
||||||
|
error={locationError}
|
||||||
|
value={location}
|
||||||
|
label="General Location"
|
||||||
|
placeholder="City, State..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
{isEditing !== true && (
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
onChange={e => {setPassword(e.target.value); setChanged(true)}}
|
||||||
|
value={password}
|
||||||
|
error={passwordError}
|
||||||
|
disabled={isEditing}
|
||||||
|
label="Password"
|
||||||
|
placeholder="Password..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
name="confirmPassword"
|
||||||
|
type="password"
|
||||||
|
onChange={e => {setConfirmPassword(e.target.value); setChanged(true)}}
|
||||||
|
value={confirmPassword}
|
||||||
|
error={confirmPasswordError}
|
||||||
|
disabled={isEditing}
|
||||||
|
label="Confirm Password"
|
||||||
|
placeholder="Confirm Password..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
)}
|
||||||
|
<div style={floatStyle}>
|
||||||
|
<Button type="submit" size="large" floated="right" value="Login" color='blue' disabled={!changed}>
|
||||||
|
{isEditing !== true ? 'Register' : 'Update'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
{registerMessage}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default UserForm;
|
export default UserForm;
|
||||||
|
|||||||
@@ -1,52 +1,42 @@
|
|||||||
import React, { Component } from "react"
|
import { sendConfirmEmail } from "../features/user/userSlice";
|
||||||
import {
|
import { useNavigate } from 'react-router-dom';
|
||||||
Container,
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
Header,
|
import { Container, Header, Button, Message, Icon } from "semantic-ui-react";
|
||||||
Button,
|
|
||||||
Message,
|
|
||||||
Icon
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
class WaitConfirmEmail extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleSendConfirmEmail = this.handleSendConfirmEmail.bind(this);
|
|
||||||
|
|
||||||
}
|
const WaitConfirmEmail = () => {
|
||||||
handleSendConfirmEmail(event) {
|
const dispatch = useDispatch();
|
||||||
this.props.sendConfirmEmail(this.props.user.userId).then(loginMessage => {
|
const navigate = useNavigate();
|
||||||
if (loginMessage) {
|
const { email, userId } = useSelector((state) => state.user);
|
||||||
// report to the user is there was a problem during login
|
|
||||||
this.setState({
|
const handleSendConfirmEmail = async () => {
|
||||||
loginMessage
|
await dispatch(sendConfirmEmail(userId)).unwrap();
|
||||||
});
|
navigate("/sent-confirm-email")
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
return(
|
<Container text>
|
||||||
<Container text>
|
<Message icon>
|
||||||
<Message icon>
|
<Icon name="envelope outline" />
|
||||||
<Icon name='envelope outline'/>
|
<Message.Content>
|
||||||
<Message.Content>
|
<Message.Header>Confirm your email address</Message.Header>
|
||||||
<Message.Header>Confirm your email address</Message.Header>
|
<p>
|
||||||
<p>We sent an email to the address you gave us: {this.props.user.email}<br/>
|
We sent an email to the address you gave us: {email}
|
||||||
Please click on the link in the email to verify the email address.</p>
|
<br />
|
||||||
<Header as='h3'>Didn't get an email?</Header>
|
Please click on the link in the email to verify the email address.
|
||||||
<p>So, you have waited a bit and haven't gotten anything?</p>
|
</p>
|
||||||
|
<Header as="h3">Didn't get an email?</Header>
|
||||||
|
<p>So, you have waited a bit and haven't gotten anything?</p>
|
||||||
<p>...and you checked your spam folder?</p>
|
<p>...and you checked your spam folder?</p>
|
||||||
<p>Well, click below.</p>
|
<p>Well, click below.</p>
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
content="Resend Email Confirmation"
|
content="Resend Email Confirmation"
|
||||||
onClick={this.handleSendConfirmEmail}
|
onClick={handleSendConfirmEmail}
|
||||||
/>
|
/>
|
||||||
</Message.Content>
|
</Message.Content>
|
||||||
</Message>
|
</Message>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
</Container>
|
export default WaitConfirmEmail;
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WaitConfirmEmail
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import WaitConfirmEmail from "./WaitConfirmEmail"
|
|
||||||
import * as userActions from "./user-actions"
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
user: state.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
userActions
|
|
||||||
)(WaitConfirmEmail)
|
|
||||||
185
account/src/features/api/apiSlice.js
Normal file
185
account/src/features/api/apiSlice.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// Import the RTK Query methods from the React-specific entry point
|
||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Define our single API slice object
|
||||||
|
export const apiSlice = createApi({
|
||||||
|
// The cache reducer expects to be added at `state.api` (already default - this is optional)
|
||||||
|
reducerPath: 'api',
|
||||||
|
// All of our requests will have URLs starting with '/fakeApi'
|
||||||
|
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_ADMIN_SERVER }),
|
||||||
|
// The "endpoints" represent operations and requests for this server
|
||||||
|
endpoints: builder => ({
|
||||||
|
// The `getPosts` endpoint is a "query" operation that returns data
|
||||||
|
getTalkgroups: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `/talkgroups/${shortName}`, credentials: "include" })
|
||||||
|
}),
|
||||||
|
getGroups: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `/groups/${shortName}`, credentials: "include" }),
|
||||||
|
providesTags: (result, error, arg) =>
|
||||||
|
result
|
||||||
|
? [...result.map(({ _id }) => ({ type: 'Group', _id })), 'Group']
|
||||||
|
: ['Group'],
|
||||||
|
}),
|
||||||
|
|
||||||
|
getErrors: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `${process.env.REACT_APP_BACKEND_SERVER}/${shortName}/errors` })
|
||||||
|
}),
|
||||||
|
updateGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}/${group._id}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: group,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: updatedGroup } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', updatedGroup.shortName, (groups) => {
|
||||||
|
const groupIndex = groups.findIndex((obj => obj.groupId === updatedGroup.groupId));
|
||||||
|
groups[groupIndex] = updatedGroup;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: group,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newGroup } = await queryFulfilled;
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', newGroup.shortName, (groups) => {
|
||||||
|
groups.push(newGroup);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
deleteGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}/${group._id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials: "include"
|
||||||
|
}),
|
||||||
|
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', group.shortName, (groups) => {
|
||||||
|
const groupIndex = groups.findIndex((obj => obj.groupId === group.groupId));
|
||||||
|
groups.splice(groupIndex,1);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
saveGroupOrder: builder.mutation({
|
||||||
|
query: ({shortName, order}) => ({
|
||||||
|
url: `/groups/${shortName}/reorder`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: order,
|
||||||
|
}),
|
||||||
|
|
||||||
|
//invalidatesTags: (result, error, group) => ['Group'],
|
||||||
|
}),
|
||||||
|
importTalkgroups: builder.mutation({
|
||||||
|
query: ({shortName,file}) => ({
|
||||||
|
url: `${process.env.REACT_APP_ADMIN_SERVER}/talkgroups/${shortName}/import`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: file,
|
||||||
|
}),
|
||||||
|
|
||||||
|
async onQueryStarted({shortName,file}, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newTalkgroups } = await queryFulfilled;
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getTalkgroups', shortName, (talkgroups) => {
|
||||||
|
talkgroups.length = 0; // Clear your array
|
||||||
|
talkgroups.push(...newTalkgroups);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
//invalidatesTags: (result, error, group) => ['Group'],
|
||||||
|
}),
|
||||||
|
getSystems: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: () => ({ url: `/systems`, credentials: "include" })
|
||||||
|
}),
|
||||||
|
createSystem: builder.mutation({
|
||||||
|
query: (system) => ({
|
||||||
|
url: `/systems`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: system,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(system, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newSystem } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
systems.push(newSystem);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
updateSystem: builder.mutation({
|
||||||
|
query: (system) => ({
|
||||||
|
url: `/systems/${system.shortName}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: system,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(system, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: updatedSystem } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
const systemIndex = systems.findIndex((obj => obj.shortName === updatedSystem.shortName));
|
||||||
|
systems[systemIndex] = updatedSystem;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
deleteSystem: builder.mutation({
|
||||||
|
query: (shortName) => ({
|
||||||
|
url: `/systems/${shortName}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials: "include"
|
||||||
|
}),
|
||||||
|
async onQueryStarted(shortName, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
const systemIndex = systems.findIndex((obj => obj.shortName === shortName));
|
||||||
|
systems.splice(systemIndex, 1);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Export the auto-generated hook for the `getPosts` query endpoint
|
||||||
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
|
||||||
256
account/src/features/user/userSlice.js
Normal file
256
account/src/features/user/userSlice.js
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import {
|
||||||
|
createSlice,
|
||||||
|
createAsyncThunk
|
||||||
|
} from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
|
||||||
|
export const authenticateUser = createAsyncThunk(
|
||||||
|
'user/authenticate',
|
||||||
|
async ({ getState }) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/authenticated"
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: "include"
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const loginUser = createAsyncThunk(
|
||||||
|
'user/login',
|
||||||
|
async (data) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/login"
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const registerUser = createAsyncThunk(
|
||||||
|
'user/register',
|
||||||
|
async (data) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/register"
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const updateProfile = createAsyncThunk(
|
||||||
|
'user/updateProfile',
|
||||||
|
async (user) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + user.userId;
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(user)
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const logoutUser = createAsyncThunk(
|
||||||
|
'user/logout',
|
||||||
|
async ({ getState }) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/logout"
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: "include"
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
export const confirmEmail = createAsyncThunk(
|
||||||
|
'user/confirmEmail',
|
||||||
|
async ({userId, token}) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + userId + "/confirm/" + token;
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST'
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const sendConfirmEmail = createAsyncThunk(
|
||||||
|
'user/sendConfirmEmail',
|
||||||
|
async (userId) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + userId + "/send-confirm";
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const resetPassword = createAsyncThunk(
|
||||||
|
'user/resetPassword',
|
||||||
|
async (data) => {
|
||||||
|
const {userId, token, password} = data;
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + userId + "/reset-password/" + token
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({password})
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const sendResetPassword = createAsyncThunk(
|
||||||
|
'user/sendResetPassword',
|
||||||
|
async (data) => {
|
||||||
|
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/api/send-reset-password"
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(
|
||||||
|
(data) => data.json()
|
||||||
|
)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const userSlice = createSlice({
|
||||||
|
name: 'user',
|
||||||
|
initialState: {
|
||||||
|
isWaiting: false,
|
||||||
|
authenticated: false,
|
||||||
|
hasAuthenticated: false,
|
||||||
|
userId: "",
|
||||||
|
admin: false,
|
||||||
|
email: "",
|
||||||
|
firstName: "",
|
||||||
|
lastName: "",
|
||||||
|
location: "",
|
||||||
|
screenName: ""
|
||||||
|
},
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
increment: (state) => {
|
||||||
|
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
||||||
|
// doesn't actually mutate the state because it uses the Immer library,
|
||||||
|
// which detects changes to a "draft state" and produces a brand new
|
||||||
|
// immutable state based off those changes
|
||||||
|
state.value += 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: {
|
||||||
|
[loginUser.fulfilled]: (state, data) => {
|
||||||
|
const {payload} = data;
|
||||||
|
if (payload.success) {
|
||||||
|
state.authenticated = true;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
state.userId = payload.user.userId;
|
||||||
|
state.admin = payload.user.admin;
|
||||||
|
state.email = payload.user.email;
|
||||||
|
state.firstName = payload.user.firstName;
|
||||||
|
state.lastName = payload.user.lastName;
|
||||||
|
state.location = payload.user.location;
|
||||||
|
state.screenName = payload.user.screenName;
|
||||||
|
} else {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateProfile.fulfilled]: (state, data) => {
|
||||||
|
const {payload} = data;
|
||||||
|
if (payload.success) {
|
||||||
|
state.firstName = payload.user.firstName;
|
||||||
|
state.lastName = payload.user.lastName;
|
||||||
|
state.location = payload.user.location;
|
||||||
|
state.screenName = payload.user.screenName;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loginUser.rejected]: (state, { payload }) => {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
},
|
||||||
|
[authenticateUser.fulfilled]: (state, data) => {
|
||||||
|
const {payload} = data;
|
||||||
|
if (payload.success) {
|
||||||
|
state.authenticated = true;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
state.userId = payload.user.userId;
|
||||||
|
state.admin = payload.user.admin;
|
||||||
|
state.email = payload.user.email;
|
||||||
|
state.firstName = payload.user.firstName;
|
||||||
|
state.lastName = payload.user.lastName;
|
||||||
|
state.location = payload.user.location;
|
||||||
|
state.screenName = payload.user.screenName;
|
||||||
|
} else {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[authenticateUser.rejected]: (state, { payload }) => {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.hasAuthenticated = true;
|
||||||
|
|
||||||
|
},
|
||||||
|
[logoutUser.fulfilled]: (state, { payload }) => {
|
||||||
|
if (payload.success) {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.userId = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loginUser.fulfilled]: (state, { payload }) => {
|
||||||
|
if (payload.success) {
|
||||||
|
state.authenticated = false;
|
||||||
|
state.userId = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Extract the action creators object and the reducer
|
||||||
|
export const userReducer = userSlice.reducer;
|
||||||
@@ -1,48 +1,22 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { Route, Switch } from 'react-router' // react-router v4/v5
|
import { createRoot } from 'react-dom/client';
|
||||||
import { ConnectedRouter } from 'connected-react-router'
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import configureStore, { history } from './redux-router/configureStore'
|
import setupStore from './redux-router/configureStore'
|
||||||
import 'semantic-ui-css/semantic.min.css'
|
import 'semantic-ui-css/semantic.min.css'
|
||||||
import App from "./App/App"
|
import App from "./App/App"
|
||||||
import Restricted from "./App/Restricted"
|
|
||||||
|
|
||||||
import LoginContainer from "./User/LoginContainer"
|
|
||||||
import RegisterContainer from "./User/RegisterContainer"
|
|
||||||
import ProfileContainer from "./User/ProfileContainer"
|
|
||||||
import ConfirmEmailContainer from "./User/ConfirmEmailContainer"
|
|
||||||
import WaitConfirmEmailContainer from "./User/WaitConfirmEmailContainer"
|
|
||||||
import SentConfirmEmailContainer from "./User/SentConfirmEmailContainer"
|
|
||||||
import SendResetPasswordContainer from "./User/SendResetPasswordContainer"
|
|
||||||
import ResetPasswordContainer from "./User/ResetPasswordContainer"
|
|
||||||
import TermsContainer from "./User/TermsContainer"
|
|
||||||
|
|
||||||
const store = configureStore(/* provide initial state if any */)
|
const store = setupStore(/* provide initial state if any */)
|
||||||
|
|
||||||
console.log(process.env);
|
|
||||||
//This thssi
|
|
||||||
ReactDOM.render(
|
const container = document.getElementById('root');
|
||||||
|
const root = createRoot(container);
|
||||||
|
root.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConnectedRouter history={history}> { /* place ConnectedRouter under Provider */ }
|
<BrowserRouter>
|
||||||
<>
|
<App />
|
||||||
<App>
|
</BrowserRouter>
|
||||||
<Switch>
|
</Provider>
|
||||||
<Route exact path="/" component={Restricted(ProfileContainer, store)} />
|
|
||||||
<Route path="/login" component={LoginContainer} />
|
|
||||||
<Route path="/register" component={RegisterContainer} />
|
|
||||||
<Route path="/wait-confirm-email" component={WaitConfirmEmailContainer} />
|
|
||||||
<Route path="/sent-confirm-email" component={SentConfirmEmailContainer} />
|
|
||||||
<Route path="/send-reset-password" component={SendResetPasswordContainer} />
|
|
||||||
<Route path="/reset-password/:userId/:token" component={ResetPasswordContainer} />
|
|
||||||
<Route path="/confirm-email/:userId/:token" component={ConfirmEmailContainer} />
|
|
||||||
<Route path="/terms" component={Restricted(TermsContainer, store)} />
|
|
||||||
<Route path="/profile" component={Restricted(ProfileContainer, store)} />
|
|
||||||
|
|
||||||
</Switch>
|
|
||||||
</App>
|
|
||||||
</>
|
|
||||||
</ConnectedRouter>
|
|
||||||
</Provider>,
|
|
||||||
document.getElementById('root')
|
|
||||||
)
|
)
|
||||||
@@ -1,26 +1,20 @@
|
|||||||
// https://github.com/supasate/connected-react-router
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
// configureStore.js
|
import { userReducer } from '../features/user/userSlice'
|
||||||
|
import { apiSlice } from "../features/api/apiSlice"
|
||||||
|
|
||||||
import { createBrowserHistory } from 'history'
|
const setupStore = (preloadedState) => {
|
||||||
import { applyMiddleware, compose, createStore } from 'redux'
|
const store = configureStore({
|
||||||
import { routerMiddleware } from 'connected-react-router'
|
reducer: {
|
||||||
import createRootReducer from './reducers'
|
user: userReducer,
|
||||||
import thunk from 'redux-thunk';
|
[apiSlice.reducerPath]: apiSlice.reducer,
|
||||||
|
},
|
||||||
export const history = createBrowserHistory()
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
// adding the api middleware enables caching, invalidation, polling and other features of `rtk-query`
|
||||||
export default function configureStore(preloadedState) {
|
getDefaultMiddleware().concat(apiSlice.middleware),
|
||||||
const store = createStore(
|
|
||||||
createRootReducer(history), // root reducer with router state
|
|
||||||
preloadedState,
|
preloadedState,
|
||||||
compose(
|
})
|
||||||
applyMiddleware(
|
|
||||||
routerMiddleware(history), // for dispatching history actions
|
|
||||||
thunk,
|
|
||||||
// ... other middlewares ...
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default setupStore;
|
||||||
13754
account/yarn.lock
13754
account/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
# build environment
|
# build environment
|
||||||
FROM node:13.12.0-alpine as build
|
FROM node:19-alpine3.16 as build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV PATH /app/node_modules/.bin:$PATH
|
ENV PATH /app/node_modules/.bin:$PATH
|
||||||
ARG REACT_APP_BACKEND_SERVER
|
ARG REACT_APP_BACKEND_SERVER
|
||||||
@@ -15,7 +15,7 @@ COPY . ./
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# production environment
|
# production environment
|
||||||
FROM node:13.12.0-alpine
|
FROM node:19-alpine3.16
|
||||||
RUN mkdir -p /app/public
|
RUN mkdir -p /app/public
|
||||||
COPY ./package.json /tmp
|
COPY ./package.json /tmp
|
||||||
RUN cd /tmp && npm install
|
RUN cd /tmp && npm install
|
||||||
|
|||||||
@@ -3,41 +3,42 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@visx/axis": "1.4.0",
|
"@visx/axis": "3.0.1",
|
||||||
"@visx/curve": "1.0.0",
|
"@visx/curve": "3.0.0",
|
||||||
"@visx/glyph": "1.0.0",
|
"@visx/glyph": "3.0.0",
|
||||||
"@visx/group": "1.0.0",
|
"@visx/group": "3.0.0",
|
||||||
"@visx/legend": "1.4.0",
|
"@visx/legend": "3.0.0",
|
||||||
"@visx/responsive": "1.3.0",
|
"@visx/responsive": "3.0.0",
|
||||||
"@visx/scale": "1.4.0",
|
"@visx/scale": "3.0.0",
|
||||||
"@visx/shape": "1.4.0",
|
"@visx/shape": "3.0.0",
|
||||||
"bcrypt": "5.0.0",
|
"bcrypt": "5.1.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"connect-mongo": "3.2.0",
|
"connect-mongo": "^4.6.0",
|
||||||
"csv-parse": "4.14.2",
|
"csv-parse": "5.3.6",
|
||||||
"d3-time-format": "3.0.0",
|
"d3-time-format": "4.1.0",
|
||||||
"express": "4.17.1",
|
"express": "4.18.2",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.3",
|
||||||
"mongoose": "5.11.11",
|
"history": "^5.3.0",
|
||||||
"multer": "1.4.2",
|
"mongoose": "7.0.0",
|
||||||
"passport": "0.4.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"passport-local": "1.0.0"
|
"passport": "0.6.0",
|
||||||
|
"passport-local": "1.0.0",
|
||||||
|
"redux-devtools-extension": "^2.13.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@reduxjs/toolkit": "^1.1.0",
|
"@reduxjs/toolkit": "^1.9.3",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^14.4.3",
|
||||||
"axios": "0.21.1",
|
"axios": "1.3.4",
|
||||||
"connected-react-router": "6.8.0",
|
"react": "^18.2.0",
|
||||||
"react": "^17.0.1",
|
"react-dom": "^18.2.0",
|
||||||
"react-dom": "^17.0.1",
|
"react-redux": "^8.0.5",
|
||||||
"react-redux": "^7.1.3",
|
"react-router-dom": "6.8.2",
|
||||||
"react-router-dom": "5.2.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-scripts": "4.0.1",
|
"redux-thunk": "2.4.2",
|
||||||
"redux-thunk": "2.3.0",
|
"semantic-ui-css": "2.5.0",
|
||||||
"semantic-ui-css": "2.4.1",
|
"semantic-ui-react": "2.1.4"
|
||||||
"semantic-ui-react": "2.0.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|||||||
@@ -2,16 +2,15 @@ const path = require("path");
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const session = require("express-session");
|
const session = require("express-session");
|
||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const connectMongo = require("connect-mongo");
|
const MongoStore = require("connect-mongo");
|
||||||
const secrets = require("./secrets");
|
const secrets = require("./secrets");
|
||||||
|
|
||||||
const MongoStore = connectMongo(session)
|
|
||||||
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||||
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
|
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
|
||||||
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 = frontend_server + ":3000"
|
var dev_server = "http://admin.openmhz.test:3000"
|
||||||
|
|
||||||
module.exports = function(app, passport) {
|
module.exports = function(app, passport) {
|
||||||
app.set("port", 3008)
|
app.set("port", 3008)
|
||||||
@@ -36,9 +35,12 @@ module.exports = function(app, passport) {
|
|||||||
secure: false,
|
secure: false,
|
||||||
domain: cookie_domain
|
domain: cookie_domain
|
||||||
},
|
},
|
||||||
store: new MongoStore({
|
store: MongoStore.create({
|
||||||
url: secrets.db,
|
mongoUrl: secrets.db,
|
||||||
autoReconnect: true
|
autoReconnect: true,
|
||||||
|
autoRemove: 'interval',
|
||||||
|
autoRemoveInterval: 240,
|
||||||
|
touchAfter: 24 * 3600 // time period in seconds
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ module.exports = function (app, passport) {
|
|||||||
done(null, user.id)
|
done(null, user.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
passport.deserializeUser((id, done) => {
|
passport.deserializeUser(async (id, done) => {
|
||||||
User.findById(id, (err, user) => {
|
const user = await User.findById(id).exec();
|
||||||
done(err, user)
|
if (user) {
|
||||||
})
|
done(null,user);
|
||||||
|
} else {
|
||||||
|
done("User not found", null);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,286 +4,246 @@ const mongoose = require("mongoose");
|
|||||||
|
|
||||||
//router.get('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
//router.get('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
||||||
|
|
||||||
exports.getGroups = function(req, res, next) {
|
exports.getGroups = async function (req, res, next) {
|
||||||
process.nextTick(function() {
|
process.nextTick(async function () {
|
||||||
System.findOne(
|
const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||||
{
|
if (!system) {
|
||||||
shortName: req.params.shortName.toLowerCase()
|
res.status(404);
|
||||||
},
|
res.json({ success: false });
|
||||||
function(err, system) {
|
return;
|
||||||
if (err) {
|
}
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That Short Name does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system.userId.equals(req.user._id)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.params.groupId) {
|
if (!system.userId.equals(req.user._id)) {
|
||||||
var groupId = req.params.groupId.toLowerCase();
|
res.status(401)
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Group.findOne(
|
if (req.params.groupId) {
|
||||||
{
|
var groupId = req.params.groupId.toLowerCase();
|
||||||
_id: new mongoose.Types.ObjectId(groupId),
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
const group = await Group.findOne(
|
||||||
},
|
{
|
||||||
{ sort: { position: -1 } },
|
_id: new mongoose.Types.ObjectId(groupId),
|
||||||
function(err, group) {
|
shortName: req.params.shortName.toLowerCase()
|
||||||
if (err) {
|
},
|
||||||
res.json({
|
{ sort: { position: -1 } });
|
||||||
success: false,
|
if (!group) {
|
||||||
message: err
|
res.status(404);
|
||||||
});
|
res.json({ success: false });
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
res.json({
|
} else {
|
||||||
success: true,
|
res.json(group);
|
||||||
group: group
|
return;
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Group.find(
|
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
}).sort("position").exec(
|
|
||||||
function(err, groups) {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
groups: groups
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
} else {
|
||||||
});
|
const group = await Group.find(
|
||||||
|
{
|
||||||
|
shortName: req.params.shortName.toLowerCase()
|
||||||
|
}).sort("position").exec();
|
||||||
|
if (!group) {
|
||||||
|
res.status(404);
|
||||||
|
res.json({ success: false });
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let result = group;
|
||||||
|
if (!Array.isArray(group)) {
|
||||||
|
result = [group]
|
||||||
|
}
|
||||||
|
res.json(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.deleteGroup = function(req, res, next) {
|
exports.deleteGroup = async function (req, res, next) {
|
||||||
//router.get('/system/:shortName/remove_group/:groupId', isLoggedIn, function(req, res) {
|
const system = await System.findOne({ 'shortName': req.params.shortName.toLowerCase() }).catch(err => {
|
||||||
System.findOne({
|
|
||||||
'shortName': req.params.shortName.toLowerCase()
|
|
||||||
}, function(err, system) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That Short Name does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system.userId.equals(req.user._id)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Group.findOneAndRemove({
|
|
||||||
shortName: req.params.shortName.toLowerCase(),
|
|
||||||
'_id': new mongoose.Types.ObjectId(req.params.groupId)
|
|
||||||
}, function(err) {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// removed!
|
|
||||||
});
|
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({ success: false, message: err });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
if (!system) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "That Short Name does not exist."
|
||||||
});
|
});
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
if (!system.userId.equals(req.user._id)) {
|
||||||
|
res.status(401);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await Group.findOneAndRemove({
|
||||||
|
shortName: req.params.shortName.toLowerCase(),
|
||||||
|
'_id': new mongoose.Types.ObjectId(req.params.groupId)
|
||||||
|
}).catch(err => {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
exports.reorderGroups = function(req, res, next) {
|
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exports.reorderGroups = function (req, res, next) {
|
||||||
|
console.log(req.body)
|
||||||
//router.post('/system/:shortName/group_order', isLoggedIn, function(req, res) {
|
//router.post('/system/:shortName/group_order', isLoggedIn, function(req, res) {
|
||||||
process.nextTick(function() {
|
process.nextTick(async function () {
|
||||||
System.findOne(
|
const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
},
|
|
||||||
function(err, system) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That Short Name does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system.userId.equals(req.user._id)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupOrder = JSON.parse(req.body.groupOrder);
|
if (!system) {
|
||||||
for (var i = 0; i < groupOrder.length; i++) {
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "That Short Name does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!system.userId.equals(req.user._id)) {
|
||||||
|
res.status(401);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(req.body)
|
||||||
|
if (!req.body.groupOrder) {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Order data is wrong."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var groupOrder = JSON.parse(req.body.groupOrder);
|
||||||
|
for (var i = 0; i < groupOrder.length; i++) {
|
||||||
|
|
||||||
console.log("in group: " + groupOrder[i] + " position: " + i);
|
console.log("in group: " + groupOrder[i] + " position: " + i);
|
||||||
Group.findOneAndUpdate(
|
const group = await Group.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: new mongoose.Types.ObjectId(groupOrder[i]),
|
_id: new mongoose.Types.ObjectId(groupOrder[i]),
|
||||||
shortName: req.params.shortName.toLowerCase()
|
shortName: req.params.shortName.toLowerCase()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
position: i
|
position: i
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true,
|
new: true,
|
||||||
upsert: false
|
upsert: false
|
||||||
},
|
|
||||||
function(err, group) {
|
|
||||||
console.log("Save group: " + group._id + " position: " + group.position);
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"Error: Group Order - " +
|
|
||||||
err +
|
|
||||||
" Shortname: " +
|
|
||||||
req.params.shortName.toLowerCase()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
});
|
||||||
|
if (!group) {
|
||||||
|
res.status(404)
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Error: Group Order - Shortname: " +
|
||||||
|
req.params.shortName.toLowerCase()
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.upsertGroup = function(req, res, next) {
|
exports.upsertGroup = function (req, res, next) {
|
||||||
|
console.log(req.body)
|
||||||
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
||||||
process.nextTick(function() {
|
process.nextTick(async function () {
|
||||||
System.findOne(
|
console.log(req.body)
|
||||||
|
let system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||||
|
|
||||||
|
if (!system) {
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "That Short Name does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!system.userId.equals(req.user._id)) {
|
||||||
|
res.status(401);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var message = "";
|
||||||
|
var groupName = req.body.groupName.replace(/[^\w\s]/g, "");
|
||||||
|
var talkgroups = JSON.parse(req.body.talkgroups);
|
||||||
|
|
||||||
|
if (!talkgroups.length || !Array.isArray(talkgroups)) {
|
||||||
|
message = "Talkgroups need to be selected, group can not be empty ";
|
||||||
|
}
|
||||||
|
if (!groupName.length) {
|
||||||
|
message = message + "Group Name is required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.length) {
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if we got here, that means that everything is good for updating/inserting
|
||||||
|
|
||||||
|
var groupId = req.params.groupId;
|
||||||
|
|
||||||
|
let group = await Group.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
|
_id: new mongoose.Types.ObjectId(groupId),
|
||||||
shortName: req.params.shortName.toLowerCase()
|
shortName: req.params.shortName.toLowerCase()
|
||||||
},
|
},
|
||||||
function(err, system) {
|
{
|
||||||
if (err) {
|
groupName: groupName,
|
||||||
console.error(err);
|
talkgroups: talkgroups
|
||||||
res.json({ success: false, message: err });
|
},
|
||||||
return;
|
{
|
||||||
}
|
new: true,
|
||||||
if (!system) {
|
upsert: true
|
||||||
console.error(err);
|
}).catch(err => {
|
||||||
res.json({
|
res.status(500);
|
||||||
success: false,
|
res.json({
|
||||||
message: "That Short Name does not exist."
|
success: false,
|
||||||
});
|
message: err
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
if (!system.userId.equals(req.user._id)) {
|
});
|
||||||
res.json({
|
res.json(group);
|
||||||
success: false,
|
return;
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var message = "";
|
|
||||||
var groupName = req.body.groupName.replace(/[^\w\s]/g, "");
|
|
||||||
var talkgroups = JSON.parse(req.body.talkgroups);
|
|
||||||
|
|
||||||
if (!talkgroups.length || !Array.isArray(talkgroups)) {
|
}
|
||||||
message = "Talkgroups need to be selected, group can not be empty ";
|
);
|
||||||
}
|
}
|
||||||
if (!groupName.length) {
|
|
||||||
message = message + "Group Name is required";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.length) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: message
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if we got here, that means that everything is good for updating/inserting
|
|
||||||
|
|
||||||
var groupId = req.params.groupId;
|
|
||||||
|
|
||||||
Group.findOneAndUpdate(
|
|
||||||
{
|
|
||||||
_id: new mongoose.Types.ObjectId(groupId),
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
groupName: groupName,
|
|
||||||
talkgroups: talkgroups
|
|
||||||
},
|
|
||||||
{
|
|
||||||
new: true,
|
|
||||||
upsert: true
|
|
||||||
},
|
|
||||||
function(err, group) {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
group: group
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
const mongoose = require("mongoose");
|
|
||||||
const Permission = require("../models/permission");
|
|
||||||
const System = require("../models/system");
|
|
||||||
const User = require("../models/user");
|
|
||||||
|
|
||||||
exports.fetchPermissions = function(req, res, next) {
|
|
||||||
Permission.find(
|
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
},
|
|
||||||
function(err, permissions) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var userIds = permissions.map(permission => {return permission.userId});
|
|
||||||
User.find(
|
|
||||||
{
|
|
||||||
_id: {
|
|
||||||
$in: userIds
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(err, users) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var returnUsers = users.map(user =>{
|
|
||||||
var permission = permissions.find(obj => {
|
|
||||||
|
|
||||||
if (obj.userId.toString() == user._id.toString()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (!permission) {
|
|
||||||
console.error("Error - Permission not found for user: " + user._id);
|
|
||||||
res.json({ success: false, message: "Permission not found" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return {_id: permission._id, userId: user._id, firstName: user.firstName, lastName: user.lastName, email: user.email, role: permission.role };
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({ success: true, permissions: returnUsers });
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
exports.addPermission = async (req, res, next) => {
|
|
||||||
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
|
||||||
|
|
||||||
|
|
||||||
let role = req.body.role;
|
|
||||||
var system = false;
|
|
||||||
var user = false;
|
|
||||||
var existing = false;
|
|
||||||
var saved = false;
|
|
||||||
if (!role || (role >= 20) || (role <1)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "Invalid Role"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("role looks good");
|
|
||||||
try {
|
|
||||||
system = await System.findOne(
|
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
});
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!system) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That System does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("system looks good")
|
|
||||||
try {
|
|
||||||
user = await User.findOne(
|
|
||||||
{
|
|
||||||
email: req.body.email
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
res.json({ success: false, message: "User not found" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("user looks good")
|
|
||||||
try {
|
|
||||||
existing = await Permission.findOne({userId: user._id, systemId: system._id});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
res.json({ success: false, message: "User already has acccess" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("no existing")
|
|
||||||
var permission = new Permission({
|
|
||||||
userId: user._id,
|
|
||||||
systemId: system._id,
|
|
||||||
shortName: system.shortName,
|
|
||||||
role: role
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
saved = await permission.save()
|
|
||||||
} catch(err) {
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("saved it")
|
|
||||||
var newPermission = saved.toObject();
|
|
||||||
newPermission.firstName = user.firstName;
|
|
||||||
newPermission.lastName = user.lastName;
|
|
||||||
newPermission.email = user.email;
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Add Permission to: " + system.shortName + " for: " + newPermission.email + " by: " + req.user.email);
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
permission: newPermission
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
exports.updatePermission = function(req, res, next) {
|
|
||||||
process.nextTick(function() {
|
|
||||||
Permission.findOne(
|
|
||||||
{
|
|
||||||
_id: new mongoose.Types.ObjectId(req.params.permissionId),
|
|
||||||
shortName: req.params.shortName.toLowerCase(req.params.shortName)
|
|
||||||
},
|
|
||||||
|
|
||||||
function(err, permission) {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (permission.role >= 20) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "Cannot change permissions of system owner"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var newRole = req.body.role;
|
|
||||||
if (!newRole || (newRole >= 20) || (newRole <1)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "Invalid Role"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
permission.role = newRole;
|
|
||||||
permission.save(function(err, saved) {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
permission: saved.toJSON()
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
exports.deletePermission = function(req, res, next) {
|
|
||||||
//router.get('/system/:shortName/remove_group/:groupId', isLoggedIn, function(req, res) {
|
|
||||||
|
|
||||||
Permission.findOneAndRemove({
|
|
||||||
shortName: req.params.shortName.toLowerCase(),
|
|
||||||
'_id': new mongoose.Types.ObjectId(req.params.permissionId),
|
|
||||||
role: { $lt : 20}
|
|
||||||
}, function(err, permission) {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!permission) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "Permission not found"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// removed!
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
@@ -12,173 +12,132 @@ exports.isLoggedIn = function (req, res, next) {
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.listSystems = function (req, res, next) {
|
exports.listSystems = async function (req, res, next) {
|
||||||
console.log("Listing Systems for: " + req.user._id);
|
console.log("Listing Systems for: " + req.user._id);
|
||||||
System.find({
|
const userId = new mongoose.Types.ObjectId(req.user._id);
|
||||||
userId: mongoose.Types.ObjectId(req.user._id)
|
const systems = await System.find({ userId: userId }).exec();
|
||||||
},
|
if (systems == null) {
|
||||||
function (err, systems) {
|
res.status(400);
|
||||||
if (err) {
|
res.json({ success: false, message: err });
|
||||||
console.error("Error - ListSystems: " + err);
|
return;
|
||||||
res.json({
|
}
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var shortNames = new Array();
|
let shortNames = new Array();
|
||||||
for (var i = 0; i < systems.length; i++) {
|
for (var i = 0; i < systems.length; i++) {
|
||||||
shortNames.push(systems[i].shortName);
|
shortNames.push(systems[i].shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemStat.find({
|
|
||||||
shortName: {
|
|
||||||
$in: shortNames
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (err, stats) {
|
|
||||||
var returnSys = systems.map(obj => {
|
|
||||||
var rObj = (({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup
|
|
||||||
}) => ({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup
|
|
||||||
}))(obj);
|
|
||||||
if (obj.showScreenName) {
|
|
||||||
rObj.screenName = req.user.screenName;
|
|
||||||
} else {
|
|
||||||
rObj.screenName = null;
|
|
||||||
}
|
|
||||||
rObj.id = obj._id;
|
|
||||||
return rObj;
|
|
||||||
});
|
|
||||||
var sys_stats = {}
|
|
||||||
var MS_PER_MINUTE = 60000;
|
|
||||||
var now = new Date();
|
|
||||||
for (var i = 0; i < stats.length; i++) {
|
|
||||||
sys_stats[stats[i].shortName] = {};
|
|
||||||
|
|
||||||
sys_stats[stats[i].shortName].callTotals = stats[i].callTotals;
|
|
||||||
sys_stats[stats[i].shortName].errorTotals = stats[i].errorTotals;
|
|
||||||
sys_stats[stats[i].shortName].errorStats = stats[i].errorStats;
|
|
||||||
sys_stats[stats[i].shortName].usageBytes = stats[i].usageBytes;
|
|
||||||
sys_stats[stats[i].shortName].usageMb = stats[i].usageMb;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
let returnSys = systems.map(obj => {
|
||||||
success: true,
|
var rObj = (({
|
||||||
systems: returnSys,
|
name,
|
||||||
stats: sys_stats
|
shortName,
|
||||||
});
|
description,
|
||||||
return;
|
systemType,
|
||||||
}
|
city,
|
||||||
);
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup
|
||||||
|
}) => ({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup
|
||||||
|
}))(obj);
|
||||||
|
if (obj.showScreenName) {
|
||||||
|
rObj.screenName = req.user.screenName;
|
||||||
|
} else {
|
||||||
|
rObj.screenName = null;
|
||||||
}
|
}
|
||||||
);
|
rObj.id = obj._id;
|
||||||
|
return rObj;
|
||||||
|
});
|
||||||
|
|
||||||
|
const stats = await SystemStat.find({ shortName: { $in: shortNames } }).exec();
|
||||||
|
var sys_stats = {}
|
||||||
|
for (var i = 0; i < stats.length; i++) {
|
||||||
|
sys_stats[stats[i].shortName] = {};
|
||||||
|
|
||||||
|
sys_stats[stats[i].shortName].callTotals = stats[i].callTotals;
|
||||||
|
sys_stats[stats[i].shortName].uploadErrors = stats[i].uploadErrors;
|
||||||
|
sys_stats[stats[i].shortName].talkgroupStats= stats[i].talkgroupStats;
|
||||||
|
sys_stats[stats[i].shortName].decodeErrorsFreq = stats[i].decodeErrorsFreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
systems: returnSys,
|
||||||
|
stats: sys_stats
|
||||||
|
});
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
function remove_system(shortName) {
|
const remove_system = async (shortName) => {
|
||||||
/*
|
|
||||||
var sysDir = config.mediaDirectory + "/" + shortName + "/"
|
|
||||||
console.log("Removing DIR: " + sysDir);
|
|
||||||
fs.remove(sysDir, function(err) {
|
|
||||||
if (err) return console.error(err)
|
|
||||||
});
|
|
||||||
|
|
||||||
Transmission.remove({
|
await System.deleteOne({
|
||||||
shortName: shortName
|
shortName: shortName
|
||||||
}, function(err) {
|
}).catch(err => console.error(err));
|
||||||
if (err) return console.error(err);
|
|
||||||
// removed!
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
System.remove({
|
|
||||||
shortName: shortName
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
if (err) return console.error(err);
|
|
||||||
// removed!
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Talkgroup.remove({
|
await Talkgroup.deleteMany({
|
||||||
shortName: shortName
|
shortName: shortName
|
||||||
},
|
}).catch(err => console.error(err));
|
||||||
function (err) {
|
|
||||||
if (err) return console.error(err);
|
|
||||||
// removed!
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Group.remove({
|
|
||||||
shortName: shortName
|
await Group.deleteMany({
|
||||||
},
|
shortName: shortName
|
||||||
function (err) {
|
}).catch(err => console.error(err));
|
||||||
if (err) return console.error(err);
|
|
||||||
// removed!
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteSystem = function (req, res, next) {
|
exports.deleteSystem = async function (req, res, next) {
|
||||||
//router.get('/delete_system/:shortName', isLoggedIn, function(req, res) {
|
//router.get('/delete_system/:shortName', isLoggedIn, function(req, res) {
|
||||||
System.findOne({
|
var system = await System.findOne({
|
||||||
shortName: req.params.shortName.toLowerCase()
|
shortName: req.params.shortName.toLowerCase()
|
||||||
},
|
}).catch(err => {
|
||||||
function (err, system) {
|
res.status(500);
|
||||||
if (err) {
|
res.json({
|
||||||
res.json({
|
success: false,
|
||||||
success: false,
|
message: err
|
||||||
message: err
|
});
|
||||||
});
|
return;
|
||||||
return;
|
});
|
||||||
}
|
if (!system) {
|
||||||
if (!system) {
|
res.status(500);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "That Short Name does not exist."
|
message: "That Short Name does not exist."
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!system.userId.equals(req.user._id)) {
|
if (!system.userId.equals(req.user._id)) {
|
||||||
console.log("Error deleting syste - Wrong User - sys: " + system.userId + " profile: " + mongoose.Types.ObjectId(req.user._id));
|
console.log("Error deleting syste - Wrong User - sys: " + system.userId + " profile: " + mongoose.Types.ObjectId(req.user._id));
|
||||||
res.json({
|
res.status(401);
|
||||||
success: false,
|
res.json({
|
||||||
message: "You are not the user associated with this system."
|
success: false,
|
||||||
});
|
message: "You are not the user associated with this system."
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
console.log("Removing: " + system.name);
|
}
|
||||||
remove_system(system.shortName);
|
console.log("Removing: " + system.name);
|
||||||
res.json({
|
remove_system(system.shortName);
|
||||||
success: true
|
res.json({
|
||||||
});
|
success: true
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
exports.ownSystem = async function (req, res, next) {
|
exports.ownSystem = async function (req, res, next) {
|
||||||
@@ -189,6 +148,7 @@ exports.ownSystem = async function (req, res, next) {
|
|||||||
|
|
||||||
if (!system) {
|
if (!system) {
|
||||||
console.error("ERROR: OwnSystem - system does not exist: " + req.body.shortName.toLowerCase());
|
console.error("ERROR: OwnSystem - system does not exist: " + req.body.shortName.toLowerCase());
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "That System does not exist."
|
message: "That System does not exist."
|
||||||
@@ -197,7 +157,7 @@ exports.ownSystem = async function (req, res, next) {
|
|||||||
}
|
}
|
||||||
if (!system.userId.equals(req.user._id)) {
|
if (!system.userId.equals(req.user._id)) {
|
||||||
console.error("ERROR: OwnSystem - not users system " + req.body.shortName.toLowerCase());
|
console.error("ERROR: OwnSystem - not users system " + req.body.shortName.toLowerCase());
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "You are not the user associated with this system."
|
message: "You are not the user associated with this system."
|
||||||
@@ -210,6 +170,7 @@ exports.ownSystem = async function (req, res, next) {
|
|||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR: ownSystem - caught: " + err);
|
console.error("ERROR: ownSystem - caught: " + err);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
@@ -224,12 +185,13 @@ exports.uniqueShortName = async function (req, res, next) {
|
|||||||
|
|
||||||
var shortName = res.locals.shortName;
|
var shortName = res.locals.shortName;
|
||||||
if (typeof shortName == 'undefined') {
|
if (typeof shortName == 'undefined') {
|
||||||
console.error("locals: " + res.locals.shortName + " body: " + req.body.shortName )
|
console.error("locals: " + res.locals.shortName + " body: " + req.body.shortName)
|
||||||
console.error("ShortName is not in res.local, pulling from req.body instead")
|
console.error("ShortName is not in res.local, pulling from req.body instead")
|
||||||
shortName = req.body.shortName;
|
shortName = req.body.shortName;
|
||||||
}
|
}
|
||||||
if (typeof shortName == 'undefined') {
|
if (typeof shortName == 'undefined') {
|
||||||
console.error("ShortName is not in req.body instead")
|
console.error("ShortName is not in req.body instead")
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Short Name not provided in form"
|
message: "Short Name not provided in form"
|
||||||
@@ -243,6 +205,7 @@ exports.uniqueShortName = async function (req, res, next) {
|
|||||||
|
|
||||||
if (system) {
|
if (system) {
|
||||||
console.error("ERROR: Unique Shortname already in use: " + req.body.shortName.toLowerCase());
|
console.error("ERROR: Unique Shortname already in use: " + req.body.shortName.toLowerCase());
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Short Name already in use"
|
message: "Short Name already in use"
|
||||||
@@ -255,10 +218,8 @@ exports.uniqueShortName = async function (req, res, next) {
|
|||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR: Unique ShortName - caught: " + err);
|
console.error("ERROR: Unique ShortName - caught: " + err);
|
||||||
res.json({
|
res.status(500);
|
||||||
success: false,
|
res.json({ message: "ERROR: Unique ShortName - caught: " + err });
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,59 +240,58 @@ exports.updateSystem = async function (req, res, next) {
|
|||||||
res.locals.system.showScreenName = res.locals.showScreenName;
|
res.locals.system.showScreenName = res.locals.showScreenName;
|
||||||
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
|
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
|
||||||
|
|
||||||
res.locals.system.save(function (err) {
|
|
||||||
if (err) {
|
await res.locals.system.save().catch(err => {
|
||||||
res.json({
|
res.status(500);
|
||||||
success: false,
|
res.json({
|
||||||
message: err
|
success: false,
|
||||||
});
|
message: err
|
||||||
return;
|
});
|
||||||
} else {
|
return;
|
||||||
var returnSys = (({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup
|
|
||||||
}) => ({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
key,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup
|
|
||||||
}))(res.locals.system);
|
|
||||||
returnSys.id = res.locals.system._id;
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
system: returnSys
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
var returnSys = (({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup
|
||||||
|
}) => ({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
key,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup
|
||||||
|
}))(res.locals.system);
|
||||||
|
returnSys.id = res.locals.system._id;
|
||||||
|
res.json(returnSys);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.validateSystem = async function (req, res, next) {
|
exports.validateSystem = async function (req, res, next) {
|
||||||
try {
|
try {
|
||||||
res.locals.showScreenName = req.body.showScreenName;
|
res.locals.showScreenName = req.body.showScreenName;
|
||||||
res.locals.ignoreUnknownTalkgroup = req.body.ignoreUnknownTalkgroup ;
|
res.locals.ignoreUnknownTalkgroup = req.body.ignoreUnknownTalkgroup;
|
||||||
if (!req.body.name || (req.body.name.length < 2)) {
|
if (!req.body.name || (req.body.name.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.name");
|
console.error("ERROR: Validate System - req.body.name");
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "System Name is Required"
|
message: "System Name is Required"
|
||||||
@@ -342,6 +302,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
|
|
||||||
if (!req.body.description || (req.body.description.length < 2)) {
|
if (!req.body.description || (req.body.description.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.description");
|
console.error("ERROR: Validate System - req.body.description");
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "System Description is Required"
|
message: "System Description is Required"
|
||||||
@@ -352,6 +313,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
|
|
||||||
if (!req.body.shortName || (req.body.shortName.length < 2)) {
|
if (!req.body.shortName || (req.body.shortName.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.shortName");
|
console.error("ERROR: Validate System - req.body.shortName");
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "System ShortName is Required"
|
message: "System ShortName is Required"
|
||||||
@@ -367,6 +329,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
// make sure it is one of the expected types
|
// make sure it is one of the expected types
|
||||||
if ((req.body.systemType != "state") && (req.body.systemType != "city") && (req.body.systemType != "county") && (req.body.systemType != "international")) {
|
if ((req.body.systemType != "state") && (req.body.systemType != "city") && (req.body.systemType != "county") && (req.body.systemType != "international")) {
|
||||||
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
|
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Select system location"
|
message: "Select system location"
|
||||||
@@ -383,6 +346,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
req.body.systemType === "county") {
|
req.body.systemType === "county") {
|
||||||
if (!req.body.state || (req.body.state === "") || (req.body.state.length < 2)) {
|
if (!req.body.state || (req.body.state === "") || (req.body.state.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.state: " + req.body.state);
|
console.error("ERROR: Validate System - req.body.state: " + req.body.state);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Select the State for the System"
|
message: "Select the State for the System"
|
||||||
@@ -395,6 +359,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
if (req.body.systemType === "city") {
|
if (req.body.systemType === "city") {
|
||||||
if (!req.body.city || (req.body.city === "") || (req.body.city.length < 2)) {
|
if (!req.body.city || (req.body.city === "") || (req.body.city.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
|
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Enter the city for the System"
|
message: "Enter the city for the System"
|
||||||
@@ -407,6 +372,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
if (req.body.systemType === "county") {
|
if (req.body.systemType === "county") {
|
||||||
if (!req.body.county || (req.body.county === "") || (req.body.county.length < 2)) {
|
if (!req.body.county || (req.body.county === "") || (req.body.county.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.county: " + req.body.county);
|
console.error("ERROR: Validate System - req.body.county: " + req.body.county);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Enter the county for the System"
|
message: "Enter the county for the System"
|
||||||
@@ -419,6 +385,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
if (req.body.systemType === "international") {
|
if (req.body.systemType === "international") {
|
||||||
if (!req.body.country || (req.body.country === "") || (req.body.country.length < 2)) {
|
if (!req.body.country || (req.body.country === "") || (req.body.country.length < 2)) {
|
||||||
console.error("ERROR: Validate System - req.body.country: " + req.body.country);
|
console.error("ERROR: Validate System - req.body.country: " + req.body.country);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Enter the country for the System"
|
message: "Enter the country for the System"
|
||||||
@@ -431,6 +398,7 @@ exports.validateSystem = async function (req, res, next) {
|
|||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR: Validate System - caught: " + err);
|
console.error("ERROR: Validate System - caught: " + err);
|
||||||
|
res.status(500)
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
@@ -471,48 +439,47 @@ exports.createSystem = async function (req, res, next) {
|
|||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}))(res.locals);
|
}))(res.locals);
|
||||||
system.key = key;
|
system.key = key;
|
||||||
system.userId = mongoose.Types.ObjectId(req.user._id);
|
system.userId = new mongoose.Types.ObjectId(req.user._id);
|
||||||
System.create(system, function (err, newSys) {
|
const newSys = await System.create(system).catch(err => {
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.json({
|
res.status(500)
|
||||||
success: false,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var returnSys = (({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup ,
|
|
||||||
key
|
|
||||||
}) => ({
|
|
||||||
name,
|
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
userId,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup ,
|
|
||||||
key
|
|
||||||
}))(newSys);
|
|
||||||
returnSys.id = newSys._id;
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: false,
|
||||||
system: returnSys
|
message: err
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var returnSys = (({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup,
|
||||||
|
key
|
||||||
|
}) => ({
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
description,
|
||||||
|
systemType,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
county,
|
||||||
|
country,
|
||||||
|
userId,
|
||||||
|
showScreenName,
|
||||||
|
ignoreUnknownTalkgroup,
|
||||||
|
key
|
||||||
|
}))(newSys);
|
||||||
|
returnSys.id = newSys._id;
|
||||||
|
res.json(returnSys);
|
||||||
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,255 +1,220 @@
|
|||||||
const Talkgroup = require("../models/talkgroup");
|
const Talkgroup = require("../models/talkgroup");
|
||||||
const System = require("../models/system");
|
const System = require("../models/system");
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const {parse} = require('csv-parse');
|
||||||
|
|
||||||
const crypto = require("crypto");
|
exports.isLoggedIn = function (req, res, next) {
|
||||||
var parse = require('csv-parse');
|
|
||||||
|
|
||||||
exports.isLoggedIn = function(req, res, next) {
|
|
||||||
if (req.isAuthenticated()) return next();
|
if (req.isAuthenticated()) return next();
|
||||||
res.redirect(account_server + "/login");
|
res.redirect(account_server + "/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
//router.get("/tg_export/:shortName", isLoggedIn, function(req, res) {
|
//router.get("/tg_export/:shortName", isLoggedIn, function(req, res) {
|
||||||
exports.exportTalkgroups = function(req, res, next) {
|
exports.exportTalkgroups = function (req, res, next) {
|
||||||
process.nextTick(function() {
|
process.nextTick(async function () {
|
||||||
System.findOne(
|
const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||||
{
|
if (!system) {
|
||||||
shortName: req.params.shortName.toLowerCase()
|
res.status(404);
|
||||||
},
|
res.json({
|
||||||
function(err, system) {
|
success: false,
|
||||||
if (err) {
|
message: "That Short Name does not exist."
|
||||||
console.error(err);
|
});
|
||||||
res.json({ success: false, message: err });
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That Short Name does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system.userId.equals(req.user._id)) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Talkgroup.find(
|
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
},
|
|
||||||
function(err, talkgroups) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!talkgroups || !Array.isArray(talkgroups)) {
|
|
||||||
console.error(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "Talkgroups were not found."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.statusCode = 200;
|
|
||||||
res.setHeader("Content-Type", "text/csv");
|
|
||||||
talkgroups.forEach(function(item) {
|
|
||||||
/* res.write('"' + item.num + '", "'
|
|
||||||
+ item.mode + '", "'
|
|
||||||
+ item.alpha + '", "'
|
|
||||||
+ item.description + '", "'
|
|
||||||
+ item.tag + '", "'
|
|
||||||
+ item.group + '", "'
|
|
||||||
+ item.priority+ '"\r\n');*/
|
|
||||||
if (item) {
|
|
||||||
var sending = "";
|
|
||||||
sending = item.num + ", ";
|
|
||||||
if (item.mode) {
|
|
||||||
sending = sending + item.mode.toString().replace(/\,/g, "") + ", ";
|
|
||||||
} else {
|
|
||||||
sending = sending + " ,";
|
|
||||||
}
|
|
||||||
if (item.alpa) {
|
|
||||||
sending = sending + item.alpha.toString().replace(/\,/g, "") + ", ";
|
|
||||||
} else {
|
|
||||||
sending = sending + " ,";
|
|
||||||
}
|
|
||||||
if (item.description) {
|
|
||||||
sending = sending + item.description.toString().replace(/\,/g, "") + ", ";
|
|
||||||
} else {
|
|
||||||
sending = sending + " ,";
|
|
||||||
}
|
|
||||||
if (item.tag) {
|
|
||||||
sending = sending + item.tag.toString().replace(/\,/g, "") + ", ";
|
|
||||||
} else {
|
|
||||||
sending = sending + " ,";
|
|
||||||
}
|
|
||||||
if (item.group) {
|
|
||||||
sending = sending + item.group.toString().replace(/\,/g, "") + ", ";
|
|
||||||
} else {
|
|
||||||
sending = sending + " ,";
|
|
||||||
}
|
|
||||||
if (item.priority) {
|
|
||||||
sending = sending + item.priority.toString().replace(/\,/g, "") + "\r\n";
|
|
||||||
} else {
|
|
||||||
sending = sending + "\r\n";
|
|
||||||
}
|
|
||||||
res.write(sending);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function csv_import(shortName, filename, callback) {
|
|
||||||
var talkgroups = [];
|
|
||||||
Talkgroup.remove(
|
|
||||||
{
|
|
||||||
shortName: shortName
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Error: csv import: " + err);
|
|
||||||
callback(err, null);
|
|
||||||
} else {
|
|
||||||
var parser = parse(
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
"num",
|
|
||||||
"mode",
|
|
||||||
"alpha",
|
|
||||||
"description",
|
|
||||||
"tag",
|
|
||||||
"group",
|
|
||||||
"priority"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
async function(err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Error: csv import 2: " + err);
|
|
||||||
callback(err, null);
|
|
||||||
} else {
|
|
||||||
var response = [];
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var row = data[i];
|
|
||||||
row.num = parseInt(row.num);
|
|
||||||
row.tag = row.tag.toLowerCase();
|
|
||||||
row.group = row.group.toLowerCase();
|
|
||||||
var newTg = new Talkgroup();
|
|
||||||
newTg.num = row.num;
|
|
||||||
newTg.alpha = row.alpha;
|
|
||||||
newTg.mode = row.mode;
|
|
||||||
newTg.description = row.description;
|
|
||||||
newTg.tag = row.tag;
|
|
||||||
newTg.group = row.group;
|
|
||||||
newTg.priority = row.priority;
|
|
||||||
newTg.shortName = shortName;
|
|
||||||
|
|
||||||
await newTg.save(function(err) {
|
|
||||||
if (err) {
|
|
||||||
console.log("Error: CSV import: " + err);
|
|
||||||
callback(err, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
response.push(row);
|
|
||||||
}
|
|
||||||
callback(null, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
fs.createReadStream(filename).pipe(parser);
|
|
||||||
// removed!
|
|
||||||
}
|
}
|
||||||
|
if (!system.userId.equals(req.user._id)) {
|
||||||
|
res.status(401);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const talkgroups = await Talkgroup.find({ shortName: req.params.shortName.toLowerCase() })
|
||||||
|
|
||||||
|
if (!talkgroups || !Array.isArray(talkgroups)) {
|
||||||
|
console.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Talkgroups were not found."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.statusCode = 200;
|
||||||
|
res.setHeader("Content-Type", "text/csv");
|
||||||
|
talkgroups.forEach(function (item) {
|
||||||
|
/* res.write('"' + item.num + '", "'
|
||||||
|
+ item.mode + '", "'
|
||||||
|
+ item.alpha + '", "'
|
||||||
|
+ item.description + '", "'
|
||||||
|
+ item.tag + '", "'
|
||||||
|
+ item.group + '", "'
|
||||||
|
+ item.priority+ '"\r\n');*/
|
||||||
|
if (item) {
|
||||||
|
var sending = "";
|
||||||
|
sending = item.num + ", ";
|
||||||
|
if (item.mode) {
|
||||||
|
sending = sending + item.mode.toString().replace(/\,/g, "") + ", ";
|
||||||
|
} else {
|
||||||
|
sending = sending + " ,";
|
||||||
|
}
|
||||||
|
if (item.alpa) {
|
||||||
|
sending = sending + item.alpha.toString().replace(/\,/g, "") + ", ";
|
||||||
|
} else {
|
||||||
|
sending = sending + " ,";
|
||||||
|
}
|
||||||
|
if (item.description) {
|
||||||
|
sending = sending + item.description.toString().replace(/\,/g, "") + ", ";
|
||||||
|
} else {
|
||||||
|
sending = sending + " ,";
|
||||||
|
}
|
||||||
|
if (item.tag) {
|
||||||
|
sending = sending + item.tag.toString().replace(/\,/g, "") + ", ";
|
||||||
|
} else {
|
||||||
|
sending = sending + " ,";
|
||||||
|
}
|
||||||
|
if (item.group) {
|
||||||
|
sending = sending + item.group.toString().replace(/\,/g, "") + ", ";
|
||||||
|
} else {
|
||||||
|
sending = sending + " ,";
|
||||||
|
}
|
||||||
|
if (item.priority) {
|
||||||
|
sending = sending + item.priority.toString().replace(/\,/g, "") + "\r\n";
|
||||||
|
} else {
|
||||||
|
sending = sending + "\r\n";
|
||||||
|
}
|
||||||
|
res.write(sending);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//router.post("/tg_import/:shortName", isLoggedIn, upload.single('file'), function(req, res, next) {
|
async function csv_import(shortName, filename, callback) {
|
||||||
exports.importTalkgroups = function(req, res, next) {
|
var talkgroups = [];
|
||||||
process.nextTick(function() {
|
const count = await Talkgroup.deleteMany({ shortName: shortName });
|
||||||
System.findOne(
|
|
||||||
{
|
|
||||||
shortName: req.params.shortName.toLowerCase()
|
|
||||||
},
|
|
||||||
function(err, system) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Error - importTalkgroups: " + err);
|
|
||||||
res.json({ success: false, message: err });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system) {
|
|
||||||
console.error("Error - importTalkgroups: System not found - " + req.params.shortName.toLowerCase());
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "That Short Name does not exist."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!system.userId.equals(req.user._id)) {
|
|
||||||
console.error("Error - importTalkgroups: you are not the user for hte system: " + req.user._id);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
message: "You are not the user associated with this system."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!req.file || req.file.size === 0) {
|
|
||||||
res.json({ success: false, message: "Please select a file." });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.exists(req.file.path, function(exists) {
|
var parser = parse(
|
||||||
if (exists) {
|
{
|
||||||
csv_import(system.shortName, req.file.path, function(err, data) {
|
columns: [
|
||||||
if (err) {
|
"num",
|
||||||
console.error("Erorr - CSV import, callback: " + err);
|
"mode",
|
||||||
res.json({ success: false, message: err });
|
"alpha",
|
||||||
res.end();
|
"description",
|
||||||
return;
|
"tag",
|
||||||
} else {
|
"group",
|
||||||
res.json({ success: true, talkgroups: data });
|
"priority"
|
||||||
return;
|
]
|
||||||
}
|
},
|
||||||
});
|
async function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.error("Error: csv import 2: " + err);
|
||||||
|
callback(err, null);
|
||||||
|
} else {
|
||||||
|
var response = [];
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var row = data[i];
|
||||||
|
row.num = parseInt(row.num);
|
||||||
|
row.tag = row.tag.toLowerCase();
|
||||||
|
row.group = row.group.toLowerCase();
|
||||||
|
var newTg = new Talkgroup();
|
||||||
|
newTg.num = row.num;
|
||||||
|
newTg.alpha = row.alpha;
|
||||||
|
newTg.mode = row.mode;
|
||||||
|
newTg.description = row.description;
|
||||||
|
newTg.tag = row.tag;
|
||||||
|
newTg.group = row.group;
|
||||||
|
newTg.priority = row.priority;
|
||||||
|
newTg.shortName = shortName;
|
||||||
|
|
||||||
|
await newTg.save().catch(err => {
|
||||||
|
console.log("Error: CSV import: " + err);
|
||||||
|
callback(err, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
response.push(row);
|
||||||
|
}
|
||||||
|
callback(null, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
fs.createReadStream(filename).pipe(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//router.post("/tg_import/:shortName", isLoggedIn, upload.single('file'), function(req, res, next) {
|
||||||
|
exports.importTalkgroups = function (req, res, next) {
|
||||||
|
process.nextTick(async function () {
|
||||||
|
const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() }).catch(err => {
|
||||||
|
res.status(500);
|
||||||
|
console.error("Error - importTalkgroups: " + err);
|
||||||
|
res.json({ success: false, message: err });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!system) {
|
||||||
|
console.error("Error - importTalkgroups: System not found - " + req.params.shortName.toLowerCase());
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "That Short Name does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!system.userId.equals(req.user._id)) {
|
||||||
|
console.error("Error - importTalkgroups: you are not the user for hte system: " + req.user._id);
|
||||||
|
res.status(401);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "You are not the user associated with this system."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!req.file || req.file.size === 0) {
|
||||||
|
console.error("Error - File is bad. Size");
|
||||||
|
res.status(500);
|
||||||
|
res.json({ success: false, message: "Please select a file." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.exists(req.file.path, function (exists) {
|
||||||
|
if (exists) {
|
||||||
|
csv_import(system.shortName, req.file.path, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.error("Erorr - CSV import, callback: " + err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({ success: false, message: err });
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.error("Error - importTalkgroups: no file");
|
res.json( data );
|
||||||
res.end(
|
return;
|
||||||
"Well, there is no magic for those who don’t believe in it!"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
console.error("Error - importTalkgroups: no file");
|
||||||
|
res.end(
|
||||||
|
"Well, there is no magic for those who don’t believe in it!"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.fetchTalkgroups = function(req, res, next) {
|
exports.fetchTalkgroups = async function (req, res, next) {
|
||||||
Talkgroup.find(
|
const talkgroups = await Talkgroup.find({ shortName: req.params.shortName.toLowerCase() }).catch(err => {
|
||||||
{
|
console.error(err);
|
||||||
shortName: req.params.shortName.toLowerCase()
|
res.status(500);
|
||||||
},
|
res.json({ success: false, message: err });
|
||||||
function(err, talkgroups) {
|
return;
|
||||||
if (err) {
|
});
|
||||||
console.error(err);
|
|
||||||
res.json({ success: false, message: err });
|
res.json(talkgroups );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.json({ success: true, talkgroups: talkgroups });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const configureExpress = require("./config/express");
|
|||||||
const systems = require("./controllers/systems");
|
const systems = require("./controllers/systems");
|
||||||
const groups = require("./controllers/groups");
|
const groups = require("./controllers/groups");
|
||||||
const talkgroups = require("./controllers/talkgroups");
|
const talkgroups = require("./controllers/talkgroups");
|
||||||
const Permission = require("./models/permission");
|
|
||||||
require("./models/user");
|
require("./models/user");
|
||||||
const multer = require('multer');
|
const multer = require('multer');
|
||||||
|
|
||||||
@@ -42,13 +42,7 @@ async function connect() {
|
|||||||
|
|
||||||
// Connect to a MongoDB server running on 'localhost:27017' and use the
|
// Connect to a MongoDB server running on 'localhost:27017' and use the
|
||||||
// 'test' database.
|
// 'test' database.
|
||||||
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }, (err, res) => {
|
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true });
|
||||||
if (err) {
|
|
||||||
console.log(`Mongoose - Error connecting to ${secrets.db}. ${err}`)
|
|
||||||
} else {
|
|
||||||
console.log(`Mongoose Successfully connected to ${secrets.db}.`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log("All Done");
|
console.log("All Done");
|
||||||
}
|
}
|
||||||
connect();
|
connect();
|
||||||
@@ -66,26 +60,6 @@ mongoose.connection.on('disconnected', () => {
|
|||||||
const isDev = process.env.NODE_ENV === "development"
|
const isDev = process.env.NODE_ENV === "development"
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
async function getRole(req) {
|
|
||||||
let promise = new Promise((resolve, reject) => {
|
|
||||||
if (req.params.shortName && req.user) {
|
|
||||||
var short_name = req.params.shortName.toLowerCase();
|
|
||||||
var userId = req.user._id;
|
|
||||||
Permission.findOne({'userId': userId, 'shortName': short_name}, function(err, permission) {
|
|
||||||
if (err) {
|
|
||||||
console.error('Error - getRole userId: ' + userId + ' shortName: ' + short_name + ' error: ' + err);
|
|
||||||
reject(-1);
|
|
||||||
}
|
|
||||||
console.log('Found - getRole userId: ' + userId + ' shortName: ' + short_name + ' role: ' + permission.role);
|
|
||||||
|
|
||||||
resolve(permission.role);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(-1);
|
|
||||||
}});
|
|
||||||
return promise;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,6 +115,22 @@ function isLoggedIn(req, res, next) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function uploadFile(req, res, next) {
|
||||||
|
const upload = multer({dest: 'uploads/'}).single('file');
|
||||||
|
|
||||||
|
upload(req, res, function (err) {
|
||||||
|
if (err instanceof multer.MulterError) {
|
||||||
|
console.error("Multer Error: " + err)
|
||||||
|
} else if (err) {
|
||||||
|
console.error(err);
|
||||||
|
// An unknown error occurred when uploading.
|
||||||
|
}
|
||||||
|
console.log("Things look good for Multer")
|
||||||
|
// Everything went fine.
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
configurePassport(app, passport)
|
configurePassport(app, passport)
|
||||||
@@ -150,13 +140,8 @@ configureExpress(app, passport)
|
|||||||
app.use(express.static(path.join(__dirname, "public")));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
app.get("")
|
app.get("")
|
||||||
|
|
||||||
//app.get("/permissions/:shortname", isOwner, permissions.fetchPermissions)
|
|
||||||
/*app.get("/permissions/:shortName", permissions.fetchPermissions);
|
|
||||||
app.delete("/permissions/:shortName/:permissionId", isAdmin, permissions.deletePermission);
|
|
||||||
app.post("/permissions/:shortName/:permissionId", isAdmin, permissions.updatePermission);
|
|
||||||
app.post("/permissions/:shortName", isAdmin, permissions.addPermission);*/
|
|
||||||
app.get("/talkgroups/:shortName", isLoggedIn, talkgroups.fetchTalkgroups)
|
app.get("/talkgroups/:shortName", isLoggedIn, talkgroups.fetchTalkgroups)
|
||||||
app.post("/talkgroups/:shortName/import", isLoggedIn, upload.single('file'), talkgroups.importTalkgroups)
|
app.post("/talkgroups/:shortName/import", isLoggedIn, uploadFile, talkgroups.importTalkgroups)
|
||||||
app.get("/talkgroups/:shortName/export", isLoggedIn, talkgroups.exportTalkgroups)
|
app.get("/talkgroups/:shortName/export", isLoggedIn, talkgroups.exportTalkgroups)
|
||||||
app.post("/groups/:shortName/reorder", isLoggedIn, groups.reorderGroups);
|
app.post("/groups/:shortName/reorder", isLoggedIn, groups.reorderGroups);
|
||||||
app.post("/groups/:shortName/:groupId?", isLoggedIn, groups.upsertGroup);
|
app.post("/groups/:shortName/:groupId?", isLoggedIn, groups.upsertGroup);
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
// Defining a User Model in mongoose
|
|
||||||
// Code modified from https://github.com/sahat/hackathon-starter
|
|
||||||
|
|
||||||
var mongoose = require("mongoose");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const PermissionSchema = new mongoose.Schema({
|
|
||||||
userId: mongoose.Schema.Types.ObjectId,
|
|
||||||
systemId: mongoose.Schema.Types.ObjectId,
|
|
||||||
shortName: String,
|
|
||||||
role: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
ver: {
|
|
||||||
type: Number,
|
|
||||||
default: 1.2
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports=mongoose.model("Permission", PermissionSchema)
|
|
||||||
@@ -1,15 +1,30 @@
|
|||||||
import React, { Component } from "react"
|
import Navigation from "./Navigation"
|
||||||
import NavigationContainer from "./NavigationContainer"
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
class App extends Component {
|
import Restricted from "./Restricted"
|
||||||
render() {
|
|
||||||
return(
|
import About from "../Components/About"
|
||||||
|
import System from "../System/System"
|
||||||
|
import ListSystems from "../System/ListSystems"
|
||||||
|
import UpdateSystem from "../System/UpdateSystem"
|
||||||
|
import CreateSystem from "../System/CreateSystem"
|
||||||
|
|
||||||
|
|
||||||
|
const App = (params) => {
|
||||||
|
return (
|
||||||
<div>
|
<div>
|
||||||
<NavigationContainer />
|
<Navigation/>
|
||||||
{this.props.children}
|
<Routes>
|
||||||
|
<Route path="/about" element={ <Restricted><About/></Restricted>} />
|
||||||
|
<Route path="/system/:shortName" element={ <Restricted><System/></Restricted>} />
|
||||||
|
<Route path="/update-system/:shortName" element={ <Restricted><UpdateSystem/></Restricted>} />
|
||||||
|
<Route path="/create-system" element={ <Restricted><CreateSystem/></Restricted>} />
|
||||||
|
<Route path="/list-systems" element={ <Restricted><ListSystems/></Restricted>} />
|
||||||
|
<Route path="/" element={ <Restricted><ListSystems/></Restricted>} />
|
||||||
|
<Route render={() => (<div>Miss</div>)} />
|
||||||
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|||||||
@@ -1,55 +1,46 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
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 { useSelector, useDispatch } from 'react-redux'
|
||||||
|
import { logoutUser } from "../features/user/userSlice";
|
||||||
|
|
||||||
const navStyle = {
|
const navStyle = {
|
||||||
marginBottom: "30px"
|
marginBottom: "30px"
|
||||||
};
|
};
|
||||||
const blankStyle = {
|
|
||||||
marginBottom: "50px"
|
|
||||||
};
|
|
||||||
|
|
||||||
class Navigation extends Component {
|
const Navigation = (props) => {
|
||||||
constructor(props) {
|
const dispatch = useDispatch();
|
||||||
super(props);
|
const { email } = useSelector((state) => state.user);
|
||||||
this._logout = this._logout.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logout = event => {
|
const logout = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.manualLogout();
|
dispatch(logoutUser({}));
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
var profileLink = process.env.REACT_APP_ACCOUNT_SERVER + "/profile";
|
||||||
var profileLink = process.env.REACT_APP_ACCOUNT_SERVER + "/profile";
|
return (
|
||||||
return (
|
<div>
|
||||||
<div>
|
<Menu style={navStyle}>
|
||||||
{this.props.user.authenticated ? (
|
<Menu.Item name="systems" header>
|
||||||
<Menu style={navStyle}>
|
{process.env.REACT_APP_SITE_NAME}
|
||||||
<Menu.Item name="systems" header>
|
</Menu.Item>
|
||||||
{process.env.REACT_APP_SITE_NAME}
|
<Menu.Menu position="right">
|
||||||
</Menu.Item>
|
<Link to="/list-systems">
|
||||||
<Menu.Menu position="right">
|
<Menu.Item name="systems"> Systems</Menu.Item>
|
||||||
<Link to="/list-systems">
|
</Link>
|
||||||
<Menu.Item name="systems"> Systems</Menu.Item>
|
<Link to="/about">
|
||||||
</Link>
|
<Menu.Item name="about">About</Menu.Item>
|
||||||
<Link to="/about">
|
</Link>
|
||||||
<Menu.Item name="about">About</Menu.Item>
|
<Dropdown item text={ email }>
|
||||||
</Link>
|
<Dropdown.Menu>
|
||||||
<Dropdown item text={this.props.user.email}>
|
<Dropdown.Item href={profileLink}>Profile</Dropdown.Item>
|
||||||
<Dropdown.Menu>
|
<Dropdown.Item onClick={logout}>Logout</Dropdown.Item>
|
||||||
<Dropdown.Item href={profileLink}>Profile</Dropdown.Item>
|
</Dropdown.Menu>
|
||||||
<Dropdown.Item onClick={this._logout}>Logout</Dropdown.Item>
|
</Dropdown>
|
||||||
</Dropdown.Menu>
|
</Menu.Menu>
|
||||||
</Dropdown>
|
</Menu>
|
||||||
</Menu.Menu>
|
</div>
|
||||||
</Menu>
|
);
|
||||||
) : (
|
|
||||||
<div style={blankStyle} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default Navigation;
|
export default Navigation;
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -1,54 +1,29 @@
|
|||||||
// in src/restricted.js
|
// in src/restricted.js
|
||||||
import React, { Component } from 'react';
|
import React, { useEffect} from "react";
|
||||||
import { withRouter } from 'react-router-dom';
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
import * as types from "../User/user-constants";
|
import { authenticateUser } from "../features/user/userSlice";
|
||||||
import axios from "axios";
|
|
||||||
/**
|
/**
|
||||||
* Higher-order component (HOC) to wrap restricted pages
|
* Higher-order component (HOC) to wrap restricted pages
|
||||||
*/
|
*/
|
||||||
const restricted = (BaseComponent, store) => {
|
|
||||||
class Restricted extends Component {
|
|
||||||
componentDidMount() {
|
|
||||||
this.checkAuthentication(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
const Restricted = ({children }) => {
|
||||||
if (prevProps.location !== this.props.location) {
|
const { authenticated, hasAuthenticated } = useSelector((state) => state.user);
|
||||||
this.checkAuthentication(this.props);
|
const dispatch = useDispatch();
|
||||||
}
|
useEffect(() => {
|
||||||
}
|
dispatch(authenticateUser({}));
|
||||||
|
},[]);
|
||||||
|
|
||||||
checkAuthentication(params) {
|
if (hasAuthenticated) {
|
||||||
const authenticated = store.getState().user.authenticated;
|
if (authenticated) {
|
||||||
if (!authenticated) {
|
return children;;
|
||||||
axios
|
|
||||||
.get(process.env.REACT_APP_ACCOUNT_SERVER + "/authenticated", { withCredentials: true })
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
const user = response.data.user
|
|
||||||
store.dispatch({
|
|
||||||
type: types.LOGIN_SUCCESS_USER,
|
|
||||||
data: user
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
window.location = process.env.REACT_APP_ACCOUNT_SERVER + "/login?nextLocation=admin";
|
|
||||||
/*history.replace({ pathname: '/login',
|
|
||||||
state: { nextPathname: params.location.pathname } });*/
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(response => {
|
|
||||||
if (response instanceof Error) {
|
|
||||||
window.location = process.env.REACT_APP_ACCOUNT_SERVER + "/login?nextLocation=admin";
|
|
||||||
// Something happened during logout that triggered an Error
|
|
||||||
console.log("Error", response.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
return <BaseComponent {...this.props} />;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
window.location = process.env.REACT_APP_ACCOUNT_SERVER + "/login?nextLocation=admin";
|
||||||
}
|
}
|
||||||
return withRouter(Restricted);
|
|
||||||
}
|
}
|
||||||
export default restricted
|
return <div/>
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Restricted
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Container, Header } from "semantic-ui-react";
|
import { Container, Header } from "semantic-ui-react";
|
||||||
|
|
||||||
class About extends Component {
|
const About = () => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Container text>
|
<Container text>
|
||||||
<Header size='large'>Share the Air</Header>
|
<Header size='large'>Share the Air</Header>
|
||||||
@@ -18,7 +17,7 @@ class About extends Component {
|
|||||||
<p> - Luke <a href="mailto:{process.env.REACT_APP_ADMIN_EMAIL}?Subject={process.env.REACT_APP_SITE_NAME}" target="_top">{process.env.REACT_APP_ADMIN_EMAIL}</a> </p>
|
<p> - Luke <a href="mailto:{process.env.REACT_APP_ADMIN_EMAIL}?Subject={process.env.REACT_APP_SITE_NAME}" target="_top">{process.env.REACT_APP_ADMIN_EMAIL}</a> </p>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default About;
|
export default About;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, useState, } from "react";
|
||||||
|
import { useGetTalkgroupsQuery, useGetGroupsQuery, useUpdateGroupMutation, useCreateGroupMutation} from '../features/api/apiSlice'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
Modal,
|
Modal,
|
||||||
@@ -13,84 +15,75 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class GroupModal extends Component {
|
const GroupModal = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
this.handleClose = this.handleClose.bind(this);
|
|
||||||
|
|
||||||
// Set initial state
|
const emptyGroup = {
|
||||||
this.state = {
|
name: "",
|
||||||
editGroup: false,
|
items: []
|
||||||
groupName: "",
|
|
||||||
group: {
|
|
||||||
name: "",
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
talkgroups: this.props.talkgroups ? this.props.talkgroups : []
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
const [groupName, setGroupName] = useState("");
|
||||||
//this.props.groupActions.fetchGroups(this.props.shortName);
|
const [group, setGroup] = useState(emptyGroup)
|
||||||
|
const [talkgroups, setTalkgroups] = useState(props.talkgroups ? props.talkgroups : [])
|
||||||
|
|
||||||
this.props.talkgroupActions.fetchTalkgroups(this.props.shortName);
|
const { data: talkgroupsData} = useGetTalkgroupsQuery(props.shortName);
|
||||||
}
|
const { data: groupsData} = useGetGroupsQuery(props.shortName);
|
||||||
|
const [ updateGroup] = useUpdateGroupMutation();
|
||||||
|
const [ createGroup] = useCreateGroupMutation();
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
useEffect(() => {
|
||||||
|
if (talkgroupsData && groupsData && props.editGroupId) {
|
||||||
|
let editGroup = groupsData.find(
|
||||||
|
group => group._id === props.editGroupId
|
||||||
|
);
|
||||||
|
let items = [];
|
||||||
|
|
||||||
// This determines if you are trying to edit a group.
|
let talkgroups = talkgroupsData.slice(0); //copy
|
||||||
// In this case, the value of editGroupID will chane to the group being edited
|
for (var i = 0; i < editGroup.talkgroups.length; i++) {
|
||||||
if (this.props.talkgroups && this.props.groups && this.props.editGroupId && !prevProps.editGroupId) {
|
const talkgroupNum = editGroup.talkgroups[i];
|
||||||
var editGroup = this.props.groups.find(
|
const index = talkgroups.findIndex(tg => tg.num === talkgroupNum);
|
||||||
group => group._id === this.props.editGroupId
|
if (index >= 0) {
|
||||||
);
|
const item = {
|
||||||
var items = [];
|
num: talkgroups[index].num,
|
||||||
|
description: talkgroups[index].description
|
||||||
var talkgroups = this.props.talkgroups;
|
};
|
||||||
for (var i = 0; i < editGroup.talkgroups.length; i++) {
|
items.push(item);
|
||||||
const talkgroupId = editGroup.talkgroups[i];
|
talkgroups = talkgroups
|
||||||
const index = talkgroups.findIndex(tg => tg.num === talkgroupId);
|
.slice(0, index)
|
||||||
if (index >= 0) {
|
.concat(talkgroups.slice(index + 1));
|
||||||
const item = {
|
|
||||||
num: talkgroups[index].num,
|
|
||||||
description: talkgroups[index].description
|
|
||||||
};
|
|
||||||
items.push(item);
|
|
||||||
talkgroups = talkgroups
|
|
||||||
.slice(0, index)
|
|
||||||
.concat(talkgroups.slice(index + 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// we want to remove that are part of the Group from the list on the Left
|
|
||||||
const group = { name: editGroup.groupName, items: items }
|
|
||||||
this.setState({ group: group, talkgroups: talkgroups, groupName: editGroup.groupName });
|
|
||||||
}
|
}
|
||||||
|
// we want to remove that are part of the Group from the list on the Left
|
||||||
if (this.props.talkgroups && !prevProps.talkgroups) {
|
const group = { name: editGroup.groupName, items: items }
|
||||||
// If we are not editing, make sure there is the full list on the left.
|
setGroup(group);
|
||||||
this.setState({ talkgroups: [...this.props.talkgroups] })
|
setTalkgroups(talkgroups);
|
||||||
|
setGroupName(editGroup.groupName);
|
||||||
}
|
}
|
||||||
|
},[groupsData,talkgroupsData, props.editGroupId]);
|
||||||
|
|
||||||
// If we are done editing, clear out the modal
|
|
||||||
if (!this.props.editGroupId && (this.props.editGroupId !== prevProps.editGroupId)) {
|
useEffect(() => {
|
||||||
this.setState({
|
if (!props.editGroupId && talkgroupsData) {
|
||||||
groupName: "", group: {
|
setTalkgroups([...talkgroupsData]);
|
||||||
name: "",
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
talkgroups: [...this.props.talkgroups]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
},[talkgroupsData, props.editGroupId]);
|
||||||
|
|
||||||
removeTalkgroup(event, index) {
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!props.editGroupId && props.talkgroups) {
|
||||||
|
setGroupName("");
|
||||||
|
setGroup(emptyGroup);
|
||||||
|
setTalkgroups( [...props.talkgroups]);
|
||||||
|
}
|
||||||
|
},[props.editGroupId, props.talkgroups]);
|
||||||
|
|
||||||
|
const removeTalkgroup = (index) => {
|
||||||
const item = {
|
const item = {
|
||||||
num: this.state.group.items[index].num,
|
num: group.items[index].num,
|
||||||
description: this.state.group.items[index].description
|
description: group.items[index].description
|
||||||
};
|
};
|
||||||
|
|
||||||
const talkgroups = this.state.talkgroups.slice(0); // copy
|
let talkgroups = talkgroupsData.slice(0); // copy
|
||||||
|
|
||||||
for (var i = 0, len = talkgroups.length; i < len; i++) {
|
for (var i = 0, len = talkgroups.length; i < len; i++) {
|
||||||
if (item.num < talkgroups[i].num) {
|
if (item.num < talkgroups[i].num) {
|
||||||
@@ -98,110 +91,75 @@ class GroupModal extends Component {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
setGroup( {
|
||||||
group: {
|
items: this.state.group.items
|
||||||
items: this.state.group.items
|
.slice(0, index)
|
||||||
.slice(0, index)
|
.concat(this.state.group.items.slice(index + 1))
|
||||||
.concat(this.state.group.items.slice(index + 1))
|
|
||||||
},
|
|
||||||
talkgroups: talkgroups
|
|
||||||
});
|
});
|
||||||
|
setTalkgroups(talkgroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
|
||||||
|
|
||||||
addTalkgroup(event, index) {
|
const addTalkgroup = (event, index) => {
|
||||||
const item = {
|
const item = {
|
||||||
num: this.state.talkgroups[index].num,
|
num: talkgroupsData[index].num,
|
||||||
description: this.state.talkgroups[index].description
|
description: talkgroupsData[index].description
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({
|
setGroup({ items: group.items.concat(item) })
|
||||||
group: { items: this.state.group.items.concat(item) },
|
setTalkgroups(talkgroups.slice(0, index).concat(talkgroups.slice(index + 1)));
|
||||||
talkgroups: this.state.talkgroups
|
|
||||||
.slice(0, index)
|
|
||||||
.concat(this.state.talkgroups.slice(index + 1))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
handleClose = () => {
|
|
||||||
this.setState({
|
|
||||||
groupName: "", group: {
|
|
||||||
name: "",
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
talkgroups: [...this.props.talkgroups]
|
|
||||||
});
|
|
||||||
this.props.onClose(false);
|
|
||||||
}
|
|
||||||
handleSubmit() {
|
|
||||||
var talkgroupNums = this.state.group.items.map(a => a.num);
|
|
||||||
|
|
||||||
if (this.props.editGroupId) {
|
const handleClose = () => {
|
||||||
|
setGroupName("");
|
||||||
|
setGroup(emptyGroup);
|
||||||
|
setTalkgroups([...talkgroupsData]);
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const talkgroupNums = group.items.map(a => a.num);
|
||||||
|
|
||||||
|
if (props.editGroupId) {
|
||||||
const data = {
|
const data = {
|
||||||
_id: this.props.editGroupId,
|
_id: props.editGroupId,
|
||||||
shortName: this.props.shortName,
|
shortName: props.shortName,
|
||||||
groupName: this.state.groupName,
|
groupName: groupName,
|
||||||
talkgroups: JSON.stringify(talkgroupNums)
|
talkgroups: JSON.stringify(talkgroupNums)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({
|
setGroupName("");
|
||||||
groupName: "", group: {
|
setGroup(emptyGroup);
|
||||||
name: "",
|
setTalkgroups([...talkgroupsData]);
|
||||||
items: []
|
await updateGroup(data);
|
||||||
},
|
props.onClose(true);
|
||||||
talkgroups: [...this.props.talkgroups]
|
|
||||||
});
|
|
||||||
this.props.groupActions.updateGroup(data).then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
this.props.onClose(false);
|
|
||||||
} else {
|
|
||||||
this.props.onClose(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
const data = {
|
const data = {
|
||||||
shortName: this.props.shortName,
|
shortName: props.shortName,
|
||||||
groupName: this.state.groupName,
|
groupName: groupName,
|
||||||
talkgroups: JSON.stringify(talkgroupNums)
|
talkgroups: JSON.stringify(talkgroupNums)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({
|
setGroupName("");
|
||||||
groupName: "", group: {
|
setGroup(emptyGroup);
|
||||||
name: "",
|
setTalkgroups([...talkgroupsData]);
|
||||||
items: []
|
|
||||||
},
|
await createGroup(data);
|
||||||
talkgroups: [...this.props.talkgroups]
|
props.onClose();
|
||||||
});
|
|
||||||
|
|
||||||
this.props.groupActions.createGroup(data).then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
this.props.onClose(false);
|
|
||||||
} else {
|
|
||||||
this.props.onClose(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
const talkgroups = this.state.talkgroups;
|
|
||||||
const group = this.state.group.items;
|
|
||||||
const railStyle = {
|
const railStyle = {
|
||||||
top: "15px",
|
top: "15px",
|
||||||
minHeight: "200px"
|
minHeight: "200px"
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Modal open={this.props.open} onClose={this.handleClose} centered={false}>
|
<Modal open={props.open} onClose={handleClose} centered={false}>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
{this.props.editGroupId ? "Edit Group" : "Create Group"}
|
{props.editGroupId ? "Edit Group" : "Create Group"}
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Content scrolling>
|
<Modal.Content scrolling>
|
||||||
<Modal.Description>
|
<Modal.Description>
|
||||||
@@ -227,7 +185,7 @@ class GroupModal extends Component {
|
|||||||
<Icon
|
<Icon
|
||||||
link
|
link
|
||||||
name="plus"
|
name="plus"
|
||||||
onClick={e => this.addTalkgroup(e, i)}
|
onClick={e => addTalkgroup(e, i)}
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
@@ -238,26 +196,26 @@ class GroupModal extends Component {
|
|||||||
<Segment style={railStyle}>
|
<Segment style={railStyle}>
|
||||||
<Input
|
<Input
|
||||||
name="groupName"
|
name="groupName"
|
||||||
onChange={this.handleInputChange}
|
onChange={e => setGroupName(e.target.value)}
|
||||||
placeholder="Group Name"
|
placeholder="Group Name"
|
||||||
value={this.state.groupName}
|
value={groupName}
|
||||||
/>
|
/>
|
||||||
<List divided relaxed>
|
<List divided relaxed>
|
||||||
{group.map((talkgroup, i) => (
|
{group.items.map((talkgroup, i) => (
|
||||||
<List.Item key={"Group-" + i}>
|
<List.Item key={"Group-" + i}>
|
||||||
<List.Icon
|
<List.Icon
|
||||||
name="remove"
|
name="remove"
|
||||||
verticalAlign="middle"
|
verticalAlign="middle"
|
||||||
link={true}
|
link={true}
|
||||||
onClick={e => this.removeTalkgroup(e, i)}
|
onClick={e => removeTalkgroup(e, i)}
|
||||||
/>
|
/>
|
||||||
<List.Content>{talkgroup.description}</List.Content>
|
<List.Content>{talkgroup.description}</List.Content>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
<Button onClick={this.handleSubmit}>
|
<Button onClick={handleSubmit}>
|
||||||
<Icon name="checkmark" />{" "}
|
<Icon name="checkmark" />{" "}
|
||||||
{this.props.editGroupId ? "Update" : "Save"}
|
{props.editGroupId ? "Update" : "Save"}
|
||||||
</Button>
|
</Button>
|
||||||
</Segment>
|
</Segment>
|
||||||
</Rail>
|
</Rail>
|
||||||
@@ -266,14 +224,13 @@ class GroupModal extends Component {
|
|||||||
</Modal.Description>
|
</Modal.Description>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Actions>
|
<Modal.Actions>
|
||||||
<Button onClick={this.handleSubmit}>
|
<Button onClick={handleSubmit}>
|
||||||
<Icon name="checkmark" />{" "}
|
<Icon name="checkmark" />{" "}
|
||||||
{this.props.editGroupId ? "Update" : "Save"}
|
{props.editGroupId ? "Update" : "Save"}
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Actions>
|
</Modal.Actions>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default GroupModal;
|
export default GroupModal;
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as groupActionCreators from "./group-actions"
|
|
||||||
import * as talkgroupActionCreators from "../Talkgroups/talkgroup-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import GroupModal from "./GroupModal"
|
|
||||||
|
|
||||||
//groups: state.group.items[props.match.params.shortName],
|
|
||||||
function mapStateToProps(state,props) {
|
|
||||||
return {
|
|
||||||
talkgroups: state.talkgroup.items[props.shortName],
|
|
||||||
groups: state.group.items[props.shortName],
|
|
||||||
shortName: props.shortName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
groupActions: bindActionCreators(groupActionCreators,dispatch),
|
|
||||||
talkgroupActions: bindActionCreators(talkgroupActionCreators, dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(GroupModal)
|
|
||||||
@@ -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";
|
||||||
@@ -19,18 +8,22 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class ListGroups extends Component {
|
const ListGroups = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux
|
//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
|
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
||||||
render() {
|
|
||||||
const groups = this.props.groups;
|
const groups = props.groups;
|
||||||
|
let groupsDisplay = [];
|
||||||
if (groups) {
|
if (groups) {
|
||||||
|
// if a group gets deleted, it will still be listed in the Order array for a little.
|
||||||
|
for (const id of props.order) {
|
||||||
|
const group = props.groups.find( group => group._id === id );
|
||||||
|
if (group) {
|
||||||
|
groupsDisplay.push(group)
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<Table >
|
<Table >
|
||||||
@@ -42,7 +35,8 @@ class ListGroups extends Component {
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{groups.map((group, i) => (
|
{groupsDisplay.map((group, i) => (
|
||||||
|
|
||||||
<Table.Row key={ "Group-" + i}>
|
<Table.Row key={ "Group-" + i}>
|
||||||
<Table.Cell>{group.groupName}</Table.Cell>
|
<Table.Cell>{group.groupName}</Table.Cell>
|
||||||
<Table.Cell>{group.talkgroups.length}</Table.Cell>
|
<Table.Cell>{group.talkgroups.length}</Table.Cell>
|
||||||
@@ -50,23 +44,27 @@ class ListGroups extends Component {
|
|||||||
<Icon
|
<Icon
|
||||||
name="pencil alternate"
|
name="pencil alternate"
|
||||||
link={true}
|
link={true}
|
||||||
onClick={e => this.props.editGroup(group._id)}
|
onClick={e => props.editGroup(group._id)}
|
||||||
/><Icon
|
/><Icon
|
||||||
name="up arrow"
|
name="up arrow"
|
||||||
link={true}
|
link={i===0?false:true}
|
||||||
onClick={e => this.props.reorderGroup(i, i-1)}
|
disabled={i===0?true:false}
|
||||||
|
onClick={e => props.reorderGroup(i, i-1)}
|
||||||
/><Icon
|
/><Icon
|
||||||
name="down arrow"
|
name="down arrow"
|
||||||
link={true}
|
link={i===groupsDisplay.length-1?false:true}
|
||||||
onClick={e => this.props.reorderGroup(i, i+1)}
|
disabled={i===groupsDisplay.length-1?true:false}
|
||||||
|
onClick={e => props.reorderGroup(i, i+1)}
|
||||||
/><Icon
|
/><Icon
|
||||||
name="remove"
|
name="remove"
|
||||||
link={true}
|
link={true}
|
||||||
onClick={e => this.props.deleteGroup(group._id)}
|
onClick={e => props.deleteGroup(group._id)}
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
))}
|
|
||||||
|
)
|
||||||
|
)}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
@@ -77,6 +75,5 @@ class ListGroups extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListGroups;
|
export default ListGroups;
|
||||||
|
|||||||
@@ -1,245 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
//import { browserHistory } from "react-router"
|
|
||||||
import * as types from "./group-constants";
|
|
||||||
import { push } from 'connected-react-router';
|
|
||||||
|
|
||||||
// "Log In" action creators
|
|
||||||
function beginCreateGroup() {
|
|
||||||
return { type: types.CREATE_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function createGroupSuccess(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_SUCCESS_GROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createGroupError(data) {
|
|
||||||
return {
|
|
||||||
type: types.CREATE_ERROR_GROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function beginUpdateGroup() {
|
|
||||||
return { type: types.UPDATE_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateGroupSuccess(data) {
|
|
||||||
return { type: types.UPDATE_SUCCESS_GROUP, data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateGroupError() {
|
|
||||||
return { type: types.UPDATE_ERROR_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function beginDeleteGroup() {
|
|
||||||
return { type: types.DELETE_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteGroupSuccess(data) {
|
|
||||||
return { type: types.DELETE_SUCCESS_GROUP, data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteGroupError() {
|
|
||||||
return { type: types.DELETE_ERROR_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginFetchGroup() {
|
|
||||||
return { type: types.FETCH_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchGroupSuccess(data) {
|
|
||||||
return {
|
|
||||||
type: types.FETCH_SUCCESS_GROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchGroupError(data) {
|
|
||||||
return {
|
|
||||||
type: types.FETCH_ERROR_GROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveGroupOrderSuccess() {
|
|
||||||
return { type: types.SAVE_ORDER_SUCCESS_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveGroupOrderError() {
|
|
||||||
return { type: types.SAVE_ORDER_ERROR_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeUserRequest(method, data, api = "/login") {
|
|
||||||
// returns a Promise
|
|
||||||
return axios({
|
|
||||||
method: method,
|
|
||||||
url: api,
|
|
||||||
data: data,
|
|
||||||
withCredentials: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 updateGroup(data) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginUpdateGroup());
|
|
||||||
|
|
||||||
return makeUserRequest("post", data, process.env.REACT_APP_ADMIN_SERVER + "/groups/" + data.shortName + "/" + data._id)
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(updateGroupSuccess(response.data.group));
|
|
||||||
} else {
|
|
||||||
dispatch(updateGroupError());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldFetchGroups(state,shortName) {
|
|
||||||
if (!state.group.items || !state.group.items[shortName]) {
|
|
||||||
return true;
|
|
||||||
} else if (state.group.isWaiting) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reorderGroup(shortName, oldIndex, newIndex) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const data = {
|
|
||||||
shortName: shortName,
|
|
||||||
oldIndex: oldIndex,
|
|
||||||
newIndex: newIndex
|
|
||||||
};
|
|
||||||
dispatch({
|
|
||||||
type: types.REORDER_GROUP,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchGroups(shortName) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (shouldFetchGroups(getState(), shortName)) {
|
|
||||||
dispatch(beginFetchGroup());
|
|
||||||
return axios
|
|
||||||
.get(process.env.REACT_APP_ADMIN_SERVER + "/groups/" + shortName, {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
const data = { shortName: shortName, groups: response.data.groups };
|
|
||||||
dispatch(fetchGroupSuccess(data));
|
|
||||||
} else {
|
|
||||||
dispatch(fetchGroupError());
|
|
||||||
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 saveGroupOrder(shortName, data) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch({
|
|
||||||
type: types.SAVE_ORDER_GROUP,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
|
|
||||||
return makeUserRequest(
|
|
||||||
"post",
|
|
||||||
data,
|
|
||||||
process.env.REACT_APP_ADMIN_SERVER + "/groups/" + shortName + "/reorder"
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(saveGroupOrderSuccess());
|
|
||||||
} else {
|
|
||||||
dispatch(saveGroupOrderError());
|
|
||||||
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 deleteGroup(shortName, groupId) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginDeleteGroup());
|
|
||||||
|
|
||||||
return makeUserRequest("delete", null, process.env.REACT_APP_ADMIN_SERVER + "/groups/" + shortName + "/" + groupId)
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
const data = {shortName: shortName, groupId: groupId}
|
|
||||||
dispatch(deleteGroupSuccess(data));
|
|
||||||
} else {
|
|
||||||
dispatch(deleteGroupError());
|
|
||||||
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 createGroup(data) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginCreateGroup());
|
|
||||||
|
|
||||||
return makeUserRequest("post", data, process.env.REACT_APP_ADMIN_SERVER + "/groups/" + data.shortName)
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(createGroupSuccess(response.data.group));
|
|
||||||
} else {
|
|
||||||
dispatch(createGroupError());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
export const CREATE_GROUP = "CREATE_GROUP"
|
|
||||||
export const CREATE_SUCCESS_GROUP = "CREATE_SUCCESS_GROUP"
|
|
||||||
export const CREATE_ERROR_GROUP = "CREATE_ERROR_GROUP"
|
|
||||||
export const UPDATE_GROUP = "UPDATE_GROUP"
|
|
||||||
export const UPDATE_SUCCESS_GROUP = "UPDATE_SUCCESS_GROUP"
|
|
||||||
export const UPDATE_ERROR_GROUP = "UPDATE_ERROR_GROUP"
|
|
||||||
export const DELETE_GROUP = "DELETE_GROUP"
|
|
||||||
export const DELETE_SUCCESS_GROUP = "DELETE_SUCCESS_GROUP"
|
|
||||||
export const DELETE_ERROR_GROUP = "DELETE_ERROR_GROUP"
|
|
||||||
export const FETCH_GROUP = "FETCH_GROUP"
|
|
||||||
export const FETCH_SUCCESS_GROUP = "FETCH_SUCCESS_GROUP"
|
|
||||||
export const FETCH_ERROR_GROUP = "FETCH_ERROR_GROUP"
|
|
||||||
export const REORDER_GROUP = "REORDER_GROUP"
|
|
||||||
export const SAVE_ORDER_GROUP = "SAVE_ORDER_GROUP"
|
|
||||||
export const SAVE_ORDER_SUCCESS_GROUP = "SAVE_ORDER_SUCCESS_GROUP"
|
|
||||||
export const SAVE_ORDER_ERROR_GROUP = "SAVE_ORDER_ERROR_GROUP"
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
import {
|
|
||||||
CREATE_GROUP,
|
|
||||||
CREATE_SUCCESS_GROUP,
|
|
||||||
CREATE_ERROR_GROUP,
|
|
||||||
DELETE_GROUP,
|
|
||||||
DELETE_SUCCESS_GROUP,
|
|
||||||
DELETE_ERROR_GROUP,
|
|
||||||
UPDATE_GROUP,
|
|
||||||
UPDATE_SUCCESS_GROUP,
|
|
||||||
UPDATE_ERROR_GROUP,
|
|
||||||
FETCH_GROUP,
|
|
||||||
FETCH_SUCCESS_GROUP,
|
|
||||||
FETCH_ERROR_GROUP,
|
|
||||||
REORDER_GROUP,
|
|
||||||
SAVE_ORDER_GROUP,
|
|
||||||
SAVE_ORDER_ERROR_GROUP,
|
|
||||||
SAVE_ORDER_SUCCESS_GROUP
|
|
||||||
} from "./group-constants";
|
|
||||||
|
|
||||||
|
|
||||||
function updateItemInArray(array, updatedItem) {
|
|
||||||
const updatedItems = array.map(item => {
|
|
||||||
if(item._id !== updatedItem._id) {
|
|
||||||
// Since we only want to update one item, preserve all others as they are now
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return updatedItem;
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = (
|
|
||||||
state = {
|
|
||||||
orderChange: false,
|
|
||||||
isWaiting: false,
|
|
||||||
items: {}},
|
|
||||||
action
|
|
||||||
) => {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case SAVE_ORDER_GROUP:
|
|
||||||
case CREATE_GROUP:
|
|
||||||
case FETCH_GROUP:
|
|
||||||
case DELETE_GROUP:
|
|
||||||
case UPDATE_GROUP:
|
|
||||||
return Object.assign({}, state, { isWaiting: true});
|
|
||||||
|
|
||||||
case UPDATE_SUCCESS_GROUP:
|
|
||||||
const updatedGroups = updateItemInArray(state.items[action.data.shortName], action.data);
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: updatedGroups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
case CREATE_SUCCESS_GROUP:
|
|
||||||
const newGroups = state.items[action.data.shortName].concat(action.data);
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: newGroups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
case DELETE_SUCCESS_GROUP:
|
|
||||||
const delGroups = state.items[action.data.shortName].filter(x => x._id !== action.data.groupId);
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: delGroups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
case FETCH_SUCCESS_GROUP:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: action.data.groups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
case SAVE_ORDER_SUCCESS_GROUP:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
orderChange: false,
|
|
||||||
isWaiting: false
|
|
||||||
});
|
|
||||||
|
|
||||||
case SAVE_ORDER_ERROR_GROUP:
|
|
||||||
case FETCH_ERROR_GROUP:
|
|
||||||
case CREATE_ERROR_GROUP:
|
|
||||||
case DELETE_ERROR_GROUP:
|
|
||||||
case UPDATE_ERROR_GROUP:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false
|
|
||||||
});
|
|
||||||
|
|
||||||
case REORDER_GROUP:
|
|
||||||
if ((action.data.newIndex<0)||(action.data.newIndex>(state.items[action.data.shortName].length-1))) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
const moveGroups = state.items[action.data.shortName].slice();
|
|
||||||
moveGroups.splice(action.data.newIndex, 0, moveGroups.splice(action.data.oldIndex, 1)[0]);
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
orderChange: true,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: moveGroups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default group;
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Checkbox,
|
|
||||||
Header,
|
|
||||||
Form,
|
|
||||||
Grid,
|
|
||||||
Modal,
|
|
||||||
Rail,
|
|
||||||
Segment,
|
|
||||||
List,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Message,
|
|
||||||
Icon,
|
|
||||||
Table
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
const requestMessageStyle = {
|
|
||||||
color: "red"
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
class AddPermissionModal extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
this.handleClose = this.handleClose.bind(this);
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
|
||||||
// Set initial state
|
|
||||||
this.state = {
|
|
||||||
role: 5,
|
|
||||||
email: "",
|
|
||||||
error: ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
|
||||||
handleChange = (e, { newRole }) => this.setState({ newRole })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleClose = () => {
|
|
||||||
this.setState({
|
|
||||||
error: ""
|
|
||||||
});
|
|
||||||
this.props.onClose(false);
|
|
||||||
}
|
|
||||||
handleSubmit() {
|
|
||||||
//if (this.props.updatePermissionId) {
|
|
||||||
const data = {email: this.state.email, shortName: this.props.shortName, role: this.state.role }
|
|
||||||
|
|
||||||
this.props.permissionActions.addPermission(data).then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
error: requestMessage
|
|
||||||
});
|
|
||||||
//this.props.onClose(false);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
error: ""
|
|
||||||
});
|
|
||||||
this.props.onClose(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const railStyle = {
|
|
||||||
top: "15px",
|
|
||||||
minHeight: "200px"
|
|
||||||
};
|
|
||||||
var errorMessage = "";
|
|
||||||
|
|
||||||
if (this.state.error) {
|
|
||||||
errorMessage = (
|
|
||||||
<Message negative>
|
|
||||||
<Message.Header>That didn't work...</Message.Header>
|
|
||||||
<p>{this.state.error}</p>
|
|
||||||
</Message>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal size='tiny' open={this.props.open} onClose={this.handleClose} centered={false}>
|
|
||||||
<Modal.Header>Select Role</Modal.Header>
|
|
||||||
<Modal.Content >
|
|
||||||
<Modal.Description>
|
|
||||||
<Form>
|
|
||||||
<Form.Field>
|
|
||||||
Add User Access to System: {this.props.shortName}
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Input label="User's email" placeholder='user@domain.com' name='email' onChange={this.handleInputChange} />
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Checkbox
|
|
||||||
radio
|
|
||||||
label='User'
|
|
||||||
name='role'
|
|
||||||
value={5}
|
|
||||||
checked={(this.state.role >= 0) && (this.state.role < 10)}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Checkbox
|
|
||||||
radio
|
|
||||||
label='Admin'
|
|
||||||
name='role'
|
|
||||||
value={15}
|
|
||||||
checked={(this.state.role >= 10) && (this.state.role < 20)}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form>
|
|
||||||
{errorMessage}
|
|
||||||
</Modal.Description>
|
|
||||||
</Modal.Content>
|
|
||||||
<Modal.Actions>
|
|
||||||
<Button onClick={this.handleClose} >
|
|
||||||
<Icon name='remove' /> Cancel
|
|
||||||
</Button>
|
|
||||||
<Button onClick={this.handleSubmit} >
|
|
||||||
<Icon name='plus' /> Add
|
|
||||||
</Button>
|
|
||||||
</Modal.Actions>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AddPermissionModal;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as permissionActionCreators from "./permission-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import AddPermissionModal from "./AddPermissionModal"
|
|
||||||
|
|
||||||
//groups: state.group.items[props.match.params.shortName],
|
|
||||||
function mapStateToProps(state,props) {
|
|
||||||
return {
|
|
||||||
permissions: state.permission.items,
|
|
||||||
shortName: props.shortName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
permissionActions: bindActionCreators(permissionActionCreators,dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(AddPermissionModal)
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Header,
|
|
||||||
Form,
|
|
||||||
Grid,
|
|
||||||
Segment,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Message,
|
|
||||||
Icon,
|
|
||||||
Table
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
class ListPermissions extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//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 permissions = this.props.permissions;
|
|
||||||
if (permissions) {
|
|
||||||
return (
|
|
||||||
|
|
||||||
<Table >
|
|
||||||
<Table.Header>
|
|
||||||
<Table.Row>
|
|
||||||
<Table.HeaderCell>Name</Table.HeaderCell>
|
|
||||||
<Table.HeaderCell>Email</Table.HeaderCell>
|
|
||||||
<Table.HeaderCell>Role</Table.HeaderCell>
|
|
||||||
<Table.HeaderCell></Table.HeaderCell>
|
|
||||||
</Table.Row>
|
|
||||||
</Table.Header>
|
|
||||||
<Table.Body>
|
|
||||||
{permissions.map((permission, i) => (
|
|
||||||
<Table.Row key={ "Group-" + i}>
|
|
||||||
<Table.Cell>{permission.firstName} {permission.lastName}</Table.Cell>
|
|
||||||
<Table.Cell>{permission.email}</Table.Cell>
|
|
||||||
<Table.Cell>{permission.role}</Table.Cell>
|
|
||||||
<Table.Cell><Icon
|
|
||||||
name="pencil alternate"
|
|
||||||
link={true}
|
|
||||||
onClick={e => this.props.updatePermission(permission._id)}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
name="remove"
|
|
||||||
link={true}
|
|
||||||
onClick={e => this.props.deletePermission(permission)}
|
|
||||||
/>
|
|
||||||
</Table.Cell>
|
|
||||||
</Table.Row>
|
|
||||||
))}
|
|
||||||
</Table.Body>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListPermissions;
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Checkbox,
|
|
||||||
Header,
|
|
||||||
Form,
|
|
||||||
Grid,
|
|
||||||
Modal,
|
|
||||||
Rail,
|
|
||||||
Segment,
|
|
||||||
List,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Message,
|
|
||||||
Icon,
|
|
||||||
Table
|
|
||||||
} from "semantic-ui-react";
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
const requestMessageStyle = {
|
|
||||||
color: "red"
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
class UpdatePermissionModal extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
this.handleClose = this.handleClose.bind(this);
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
|
||||||
// Set initial state
|
|
||||||
this.state = {
|
|
||||||
newRole: false,
|
|
||||||
permission: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
|
|
||||||
if (nextProps.permissions && nextProps.updatePermissionId) {
|
|
||||||
var updatePermission = nextProps.permissions.find(permission=> permission._id === nextProps.updatePermissionId);
|
|
||||||
|
|
||||||
if (updatePermission) {
|
|
||||||
this.setState({permission: updatePermission, newRole: updatePermission.role});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleChange = (e, { name, value }) => this.setState({ newRole: value })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleClose = () => this.props.onClose(false);
|
|
||||||
handleSubmit() {
|
|
||||||
if (this.props.updatePermissionId) {
|
|
||||||
const data = {permissionId: this.props.updatePermissionId, shortName: this.props.shortName, role: this.state.newRole }
|
|
||||||
|
|
||||||
this.props.permissionActions.update(data).then(requestMessage => {
|
|
||||||
if (requestMessage) {
|
|
||||||
// report to the user is there was a problem during registration
|
|
||||||
this.setState({
|
|
||||||
requestMessage
|
|
||||||
});
|
|
||||||
this.props.onClose(false);
|
|
||||||
} else {
|
|
||||||
this.props.onClose(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const railStyle = {
|
|
||||||
top: "15px",
|
|
||||||
minHeight: "200px"
|
|
||||||
};
|
|
||||||
var user = ""
|
|
||||||
if (this.state.permission) {
|
|
||||||
user = this.state.permission.email
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Modal size='tiny' open={this.props.open} onClose={this.handleClose} centered={false}>
|
|
||||||
<Modal.Header>Select Role</Modal.Header>
|
|
||||||
<Modal.Content >
|
|
||||||
<Modal.Description>
|
|
||||||
<Form>
|
|
||||||
<Form.Field>
|
|
||||||
Set role for <b>{user}</b>:
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Checkbox
|
|
||||||
radio
|
|
||||||
label='User'
|
|
||||||
name='checkboxRadioGroup'
|
|
||||||
value={5}
|
|
||||||
checked={(this.state.newRole >= 0) && (this.state.newRole < 10)}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Checkbox
|
|
||||||
radio
|
|
||||||
label='Admin'
|
|
||||||
name='checkboxRadioGroup'
|
|
||||||
value={15}
|
|
||||||
checked={(this.state.newRole >= 10) && (this.state.newRole < 20)}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
</Modal.Description>
|
|
||||||
</Modal.Content>
|
|
||||||
<Modal.Actions>
|
|
||||||
<Button onClick={this.handleSubmit} >
|
|
||||||
<Icon name='checkmark' /> Update
|
|
||||||
</Button>
|
|
||||||
</Modal.Actions>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UpdatePermissionModal;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as permissionActionCreators from "./permission-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import UpdatePermissionModal from "./UpdatePermissionModal"
|
|
||||||
|
|
||||||
//groups: state.group.items[props.match.params.shortName],
|
|
||||||
function mapStateToProps(state,props) {
|
|
||||||
return {
|
|
||||||
permissions: state.permission.items,
|
|
||||||
shortName: props.shortName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
permissionActions: bindActionCreators(permissionActionCreators,dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(UpdatePermissionModal)
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
//import { browserHistory } from "react-router"
|
|
||||||
import * as types from "./permission-constants";
|
|
||||||
|
|
||||||
function beginAddPermission() {
|
|
||||||
return { type: types.ADD_PERMISSION };
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPermissionSuccess(data) {
|
|
||||||
return { type: types.ADD_PERMISSION_SUCCESS, data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPermissionError() {
|
|
||||||
return { type: types.ADD_PERMISSION_ERROR };
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginDeletePermission() {
|
|
||||||
return { type: types.DELETE_PERMISSION };
|
|
||||||
}
|
|
||||||
|
|
||||||
function deletePermissionSuccess(data) {
|
|
||||||
return { type: types.DELETE_PERMISSION_SUCCESS, data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function deletePermissionError() {
|
|
||||||
return { type: types.DELETE_PERMISSION_ERROR };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function beginUpdateRole() {
|
|
||||||
return { type: types.UPDATE_ROLE };
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRoleSuccess(data) {
|
|
||||||
return { type: types.UPDATE_ROLE_SUCCESS, data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRoleError() {
|
|
||||||
return { type: types.UPDATE_ROLE_ERROR };
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Log In" action creators
|
|
||||||
function beginFetchPermission() {
|
|
||||||
return { type: types.FETCH_PERMISSION };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPermissionError(data) {
|
|
||||||
return { type: types.FETCH_ERROR_PERMISSION,
|
|
||||||
data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPermissionSuccess(data) {
|
|
||||||
return { type: types.FETCH_SUCCESS_PERMISSION,
|
|
||||||
data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldFetch(state) {
|
|
||||||
if ((!state.permission.items) || (state.permission.items.length <1)) {
|
|
||||||
return true
|
|
||||||
} else if (state.permission.isWaiting) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetch(shortName) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (shouldFetch(getState())) {
|
|
||||||
dispatch(beginFetchPermission());
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(process.env.REACT_APP_ADMIN_SERVER + "/permissions/"+shortName, {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(fetchPermissionSuccess(response.data.permissions));
|
|
||||||
} else {
|
|
||||||
dispatch(fetchPermissionError());
|
|
||||||
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 update(data) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
|
|
||||||
dispatch(beginUpdateRole());
|
|
||||||
var url = process.env.REACT_APP_ADMIN_SERVER + "/permissions/" + data.shortName + "/" + data.permissionId;
|
|
||||||
return axios.post( url, data, {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(updateRoleSuccess(response.data.permission));
|
|
||||||
} else {
|
|
||||||
dispatch(updateRoleError());
|
|
||||||
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 addPermission(data) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch(beginAddPermission());
|
|
||||||
var url = process.env.REACT_APP_ADMIN_SERVER + "/permissions/" + data.shortName;
|
|
||||||
return axios.post( url, data, {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(addPermissionSuccess(response.data.permission));
|
|
||||||
} else {
|
|
||||||
dispatch(addPermissionError());
|
|
||||||
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 deletePermission(data) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
|
|
||||||
dispatch(beginDeletePermission());
|
|
||||||
var url = process.env.REACT_APP_ADMIN_SERVER + "/permissions/" + data.shortName + "/" + data.permissionId;
|
|
||||||
return axios.delete( url, {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(deletePermissionSuccess(data.permissionId));
|
|
||||||
} else {
|
|
||||||
dispatch(deletePermissionError());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
export const ADD_PERMISSION = "ADD_PERMISSION"
|
|
||||||
export const ADD_PERMISSION_ERROR = "ADD_PERMISSION_ERROR"
|
|
||||||
export const ADD_PERMISSION_SUCCESS = "ADD_PERMISSION_SUCCESS"
|
|
||||||
export const FETCH_PERMISSION = "FETCH_PERMISSION"
|
|
||||||
export const FETCH_ERROR_PERMISSION = "FETCH_ERROR_PERMISSION"
|
|
||||||
export const FETCH_SUCCESS_PERMISSION = "FETCH_SUCCESS_PERMISSION"
|
|
||||||
export const UPDATE_ROLE = "UPDATE_ROLE"
|
|
||||||
export const UPDATE_ROLE_ERROR = "UPDATE_ROLE_ERROR"
|
|
||||||
export const UPDATE_ROLE_SUCCESS = "UPDATE_ROLE SUCCESS"
|
|
||||||
export const DELETE_PERMISSION = "DELETE_PERMISSION"
|
|
||||||
export const DELETE_PERMISSION_ERROR = "DELETE_PERMISSION_ERROR"
|
|
||||||
export const DELETE_PERMISSION_SUCCESS = "DELETE_PERMISSION_SUCCESS"
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
import {
|
|
||||||
ADD_PERMISSION,
|
|
||||||
ADD_PERMISSION_SUCCESS,
|
|
||||||
ADD_PERMISSION_ERROR,
|
|
||||||
DELETE_PERMISSION,
|
|
||||||
DELETE_PERMISSION_SUCCESS,
|
|
||||||
DELETE_PERMISSION_ERROR,
|
|
||||||
FETCH_PERMISSION,
|
|
||||||
FETCH_SUCCESS_PERMISSION,
|
|
||||||
FETCH_ERROR_PERMISSION,
|
|
||||||
UPDATE_ROLE,
|
|
||||||
UPDATE_ROLE_ERROR,
|
|
||||||
UPDATE_ROLE_SUCCESS
|
|
||||||
} from "./permission-constants";
|
|
||||||
|
|
||||||
function updateRoleInArray(array, updatedItem) {
|
|
||||||
const updatedItems = array.map(item => {
|
|
||||||
if(item._id !== updatedItem._id) {
|
|
||||||
// Since we only want to update one item, preserve all others as they are now
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
item.role = updatedItem.role;
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteItemInArray(array, _id) {
|
|
||||||
const updatedItems = array.filter(function (item) {
|
|
||||||
return item._id !== _id;
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = (
|
|
||||||
state = {
|
|
||||||
isWaiting: false,
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
action
|
|
||||||
) => {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
|
|
||||||
case ADD_PERMISSION_SUCCESS:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: [...state.items, action.data]
|
|
||||||
});
|
|
||||||
case DELETE_PERMISSION_SUCCESS:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: deleteItemInArray(state.items, action.data)
|
|
||||||
});
|
|
||||||
case UPDATE_ROLE_SUCCESS:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: updateRoleInArray(state.items, action.data)
|
|
||||||
});
|
|
||||||
case FETCH_SUCCESS_PERMISSION:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: action.data
|
|
||||||
});
|
|
||||||
|
|
||||||
case DELETE_PERMISSION_ERROR:
|
|
||||||
case ADD_PERMISSION_ERROR:
|
|
||||||
case UPDATE_ROLE_ERROR:
|
|
||||||
case FETCH_ERROR_PERMISSION:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false
|
|
||||||
});
|
|
||||||
|
|
||||||
case DELETE_PERMISSION:
|
|
||||||
case ADD_PERMISSION:
|
|
||||||
case UPDATE_ROLE:
|
|
||||||
case FETCH_PERMISSION:
|
|
||||||
return Object.assign({}, state, { isWaiting: true});
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default permission;
|
|
||||||
@@ -1,103 +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}
|
||||||
xScale={xScale}
|
x={x}
|
||||||
yScale={yScale}
|
y={y}
|
||||||
x={x}
|
strokeWidth={2}
|
||||||
y={y}
|
curve={curveStep}
|
||||||
strokeWidth={2}
|
/>
|
||||||
curve={curveStep}
|
<LinePath
|
||||||
/>
|
data={errorTotals}
|
||||||
<LinePath
|
x={x}
|
||||||
data={errorTotals}
|
y={y}
|
||||||
xScale={xScale}
|
strokeWidth={2}
|
||||||
yScale={yScale}
|
stroke="red"
|
||||||
x={x}
|
curve={curveStep}
|
||||||
y={y}
|
/>
|
||||||
strokeWidth={2}
|
</Group>
|
||||||
stroke="red"
|
</svg>
|
||||||
curve={curveStep}
|
);
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
export default CallChart;
|
export default CallChart;
|
||||||
|
|||||||
@@ -1,55 +1,34 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
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 { useSelector } from 'react-redux'
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import SystemForm from "./SystemForm";
|
import SystemForm from "./SystemForm";
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
const requestMessageStyle = {
|
const CreateSystem = () => {
|
||||||
color: "red"
|
const navigate = useNavigate();
|
||||||
};
|
const [createSystem ] = useCreateSystemMutation();
|
||||||
|
const { screenName } = useSelector((state) => state.user);
|
||||||
|
|
||||||
// ----------------------------------------------------
|
const [requestMessage, setRequestMessage] = useState("");
|
||||||
class CreateSystem extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
|
|
||||||
|
const handleSubmit = async (system) => {
|
||||||
|
try {
|
||||||
|
await createSystem(system).unwrap();
|
||||||
|
navigate("/list-systems")
|
||||||
|
} catch (error) {
|
||||||
|
const message = error.data.message;
|
||||||
|
console.log(message);
|
||||||
|
setRequestMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container text>
|
<Container text>
|
||||||
@@ -72,11 +51,11 @@ class CreateSystem extends Component {
|
|||||||
|
|
||||||
<Container text>
|
<Container text>
|
||||||
<Header as="h1">Create System</Header>
|
<Header as="h1">Create System</Header>
|
||||||
<SystemForm onSubmit={this.handleSubmit} requestMessage={this.state.requestMessage} screenName={this.props.user.screenName}/>
|
<SystemForm onSubmit={handleSubmit} requestMessage={requestMessage} screenName={screenName}/>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateSystem;
|
export default CreateSystem;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import SystemCard from "./SystemCard";
|
import SystemCard from "./SystemCard";
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
@@ -8,37 +7,38 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Button,
|
Button,
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
import { useGetSystemsQuery, } from '../features/api/apiSlice'
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class ListSystems extends Component {
|
const ListSystems = () => {
|
||||||
|
const { data, isSuccess } = useGetSystemsQuery();
|
||||||
state = {
|
const navigate = useNavigate();
|
||||||
|
let systems = false;
|
||||||
|
if (isSuccess) {
|
||||||
|
systems = data.systems;
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchSystems();
|
|
||||||
}
|
|
||||||
|
|
||||||
//https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux
|
//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
|
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
||||||
render() {
|
|
||||||
const systems = this.props.system.items;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container>
|
<Container>
|
||||||
<Header as="h1">Radio Systems</Header>
|
<Header as="h1">Radio Systems</Header>
|
||||||
<Button onClick={(e) => this.props.changeUrl("/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}>
|
||||||
{systems.map((system) =>
|
{systems &&
|
||||||
<SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
systems.map((system) =>
|
||||||
|
<SystemCard system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)}/>
|
||||||
)}
|
)}
|
||||||
</Card.Group>
|
</Card.Group>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ListSystems;
|
export default ListSystems;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,27 +1,22 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import CallChart from "./CallChart"
|
import CallChart from "./CallChart"
|
||||||
import { ParentSize } from "@visx/responsive";
|
import { ParentSize } from "@visx/responsive";
|
||||||
|
|
||||||
class ResponsiveCallChart extends Component {
|
const ResponsiveCallChart = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
return(
|
<ParentSize className="graph-container">
|
||||||
<ParentSize className="graph-container">
|
{({ width: w, height: h }) => {
|
||||||
{({ width: w, height: h }) => {
|
return (
|
||||||
return (
|
<CallChart
|
||||||
<CallChart
|
width={w}
|
||||||
width={w}
|
height={h}
|
||||||
height={h}
|
data={props.data}
|
||||||
data={this.props.data}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
}}
|
||||||
}}
|
</ParentSize>
|
||||||
</ParentSize>
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default ResponsiveCallChart
|
export default ResponsiveCallChart
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import ErrorChart from "./ErrorChart"
|
import ErrorChart from "./ErrorChart"
|
||||||
import { ParentSize } from "@visx/responsive";
|
import { ParentSize } from "@visx/responsive";
|
||||||
|
|
||||||
class ResponsiveErrorChart extends Component {
|
const ResponsiveErrorChart = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
return(
|
<ParentSize className="graph-container">
|
||||||
<ParentSize className="graph-container">
|
{({ width: w, height: h }) => {
|
||||||
{({ width: w, height: h }) => {
|
return (
|
||||||
return (
|
<ErrorChart
|
||||||
<ErrorChart
|
width={w}
|
||||||
width={w}
|
height={h}
|
||||||
height={h}
|
data={props.data}
|
||||||
data={this.props.data}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
}}
|
||||||
}}
|
</ParentSize>
|
||||||
</ParentSize>
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ResponsiveErrorChart
|
export default ResponsiveErrorChart
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
import React, { Component } from "react";
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Icon,
|
Icon,
|
||||||
@@ -9,11 +8,9 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SystemCard extends Component {
|
const SystemCard = (props) => {
|
||||||
|
|
||||||
render() {
|
|
||||||
var location = "";
|
var location = "";
|
||||||
const system = this.props.system;
|
const { system } = props;
|
||||||
|
|
||||||
|
|
||||||
if (system) {
|
if (system) {
|
||||||
@@ -37,26 +34,26 @@ render() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Card link onClick={this.props.onClick}>
|
<Card link onClick={props.onClick}>
|
||||||
|
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Header size='medium'>
|
<Header size='medium'>
|
||||||
<Header.Content>
|
<Header.Content>
|
||||||
{system.name}
|
{system.name}
|
||||||
<Header.Subheader>{system.screenName}</Header.Subheader>
|
<Header.Subheader>{system.screenName}</Header.Subheader>
|
||||||
</Header.Content>
|
</Header.Content>
|
||||||
</Header>
|
</Header>
|
||||||
</Card.Content><Card.Content>
|
</Card.Content><Card.Content>
|
||||||
|
|
||||||
<Card.Description>{system.description}</Card.Description>
|
<Card.Description>{system.description}</Card.Description>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
<Card.Content extra>
|
<Card.Content extra>
|
||||||
<Icon name='marker' />
|
<Icon name='marker' />
|
||||||
{location}
|
{location}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SystemCard;
|
export default SystemCard;
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Divider,
|
Divider,
|
||||||
Form,
|
Form,
|
||||||
@@ -9,421 +9,385 @@ import {
|
|||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class SystemForm extends Component {
|
const SystemForm = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
|
||||||
this.handleScreenNameCheckboxChange = this.handleScreenNameCheckboxChange.bind(this);
|
|
||||||
this.handleTalkgroupCheckboxChange = this.handleTalkgroupCheckboxChange.bind(this);
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
this.checkInputs = this.checkInputs.bind(this);
|
|
||||||
this.render = this.render.bind(this);
|
|
||||||
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
checkInputMessages: [],
|
|
||||||
name: "",
|
const [checkInputMessages, setCheckInputMessage] = useState([]);
|
||||||
shortName: "",
|
const [name, setName] = useState("");
|
||||||
description: "",
|
const [shortName, setShortName] = useState("");
|
||||||
systemType: "state",
|
const [description, setDescription] = useState("");
|
||||||
state: "",
|
const [systemType, setSystemType] = useState("state");
|
||||||
city: "",
|
const [systemState, setSystemState] = useState("");
|
||||||
county: "",
|
const [city, setCity] = useState("");
|
||||||
country: "",
|
const [county, setCounty] = useState("");
|
||||||
showScreenName: false,
|
const [country, setCountry] = useState("");
|
||||||
ignoreUnknownTalkgroup: false,
|
const [showScreenName, setShowScreenName] = useState(false);
|
||||||
nameError: false,
|
const [ignoreUnknownTalkgroup, setIgnoreUnknownTalkgroup] = useState(false);
|
||||||
shortNameError: false,
|
const [nameError, setNameError] = useState(false);
|
||||||
descriptionError: false,
|
const [shortNameError, setShortNameError] = useState(false);
|
||||||
emailError: false,
|
const [descriptionError, setDescriptionError] = useState(false);
|
||||||
systemTypeError: false,
|
const [systemStateError, setSystemStateError] = useState(false);
|
||||||
stateError: false,
|
const [cityError, setCityError] = useState(false);
|
||||||
cityErorr: false,
|
const [countyError, setCountyError] = useState(false);
|
||||||
countyError: false,
|
const [countryError, setCountryError] = useState(false);
|
||||||
countryError: false,
|
const [isEditing] = useState(props.isEditing);
|
||||||
isEditing: this.props.isEditing
|
|
||||||
};
|
useEffect(() => {
|
||||||
if (this.props.isEditing && this.props.system) {
|
if (props.isEditing && props.system) {
|
||||||
this.state.name = this.props.system.name;
|
setName(props.system.name);
|
||||||
this.state.shortName = this.props.system.shortName;
|
setShortName(props.system.shortName);
|
||||||
this.state.description = this.props.system.description;
|
setDescription(props.system.description);
|
||||||
this.state.systemType = this.props.system.systemType;
|
setSystemType(props.system.systemType);
|
||||||
this.state.city = this.props.system.city;
|
setCity(props.system.city);
|
||||||
this.state.state = this.props.system.state;
|
setSystemState(props.system.state)
|
||||||
this.state.county = this.props.system.county;
|
setCounty(props.system.county);
|
||||||
this.state.country = this.props.system.country;
|
setCountry(props.system.country);
|
||||||
this.state.showScreenName = this.props.system.showScreenName;
|
setShowScreenName(props.system.showScreenName);
|
||||||
this.state.ignoreUnknownTalkgroup = this.props.system.ignoreUnknownTalkgroup;
|
setIgnoreUnknownTalkgroup(props.system.ignoreUnknownTalkgroup);
|
||||||
}
|
}
|
||||||
}
|
}, [props.system, props.isEditing]);
|
||||||
// If we directly to an updating url, the system fetch will come back after the constructer, so we need to catch this and update the values
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
if (this.props.isEditing && this.props.system && !prevProps.system) {
|
const checkInputs = () => {
|
||||||
this.setState({
|
|
||||||
name: this.props.system.name,
|
|
||||||
shortName: this.props.system.shortName,
|
|
||||||
description: this.props.system.description,
|
|
||||||
systemType: this.props.system.systemType,
|
|
||||||
city: this.props.system.city,
|
|
||||||
state: this.props.system.state,
|
|
||||||
county: this.props.system.county,
|
|
||||||
country: this.props.system.country,
|
|
||||||
showScreenName: this.props.system.showScreenName,
|
|
||||||
ignoreUnknownTalkgroup: this.props.system.ignoreUnknownTalkgroup,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkInputs() {
|
|
||||||
let error = false;
|
let error = false;
|
||||||
var checkInputMessages = [];
|
var messages = [];
|
||||||
|
|
||||||
if (this.state.name === "") {
|
if (name === "") {
|
||||||
this.setState({ nameError: true });
|
setNameError(true);
|
||||||
checkInputMessages.push("System Name is required");
|
messages.push("System Name is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ nameError: false });
|
setNameError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.shortName.length === 0 || this.state.shortName.length > 10) {
|
if (shortName.length < 4 || shortName.length > 10) {
|
||||||
this.setState({ shortNameError: true });
|
setShortNameError(true)
|
||||||
checkInputMessages.push(
|
messages.push(
|
||||||
"Short Name for the system has to be 4-10 charecters"
|
"Short Name for the system has to be 4-10 charecters"
|
||||||
);
|
);
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ shortNameError: false });
|
setShortNameError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.description === "") {
|
if (description === "") {
|
||||||
this.setState({ descriptionError: true });
|
setDescriptionError(true);
|
||||||
checkInputMessages.push("System description is required");
|
messages.push("System description is required");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ descriptionError: false });
|
setDescriptionError(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.state.systemType === "state" ||
|
systemType === "state" ||
|
||||||
this.state.systemType === "city" ||
|
systemType === "city" ||
|
||||||
this.state.systemType === "county"
|
systemType === "county"
|
||||||
) {
|
) {
|
||||||
if (this.state.state === "") {
|
if (systemState === "") {
|
||||||
this.setState({ stateError: true });
|
setSystemStateError(true);
|
||||||
checkInputMessages.push("Select a state");
|
messages.push("Select a state");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ stateError: false });
|
setSystemStateError(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.systemType === "city") {
|
if (systemType === "city") {
|
||||||
if (this.state.city === "") {
|
if (city === "") {
|
||||||
this.setState({ cityError: true });
|
setCityError(true);
|
||||||
checkInputMessages.push("Enter a city");
|
messages.push("Enter a city");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ cityError: false });
|
setCityError(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.systemType === "county") {
|
if (systemType === "county") {
|
||||||
if (this.state.county === "") {
|
if (county === "") {
|
||||||
this.setState({ countyError: true });
|
setCountyError(true)
|
||||||
checkInputMessages.push("Enter a city");
|
messages.push("Enter a county");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ countyError: false });
|
setCountyError(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.systemType === "international") {
|
if (systemType === "international") {
|
||||||
if (this.state.country === "") {
|
if (country === "") {
|
||||||
this.setState({ countryError: true });
|
setCountryError(true);
|
||||||
checkInputMessages.push("Enter a city");
|
messages.push("Enter a country");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
this.setState({ countryError: false });
|
setCountryError(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ checkInputMessages: checkInputMessages });
|
setCheckInputMessage(messages);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
|
const handleScreenNameCheckboxChange = () => {
|
||||||
handleScreenNameCheckboxChange() {
|
setShowScreenName((current) => !current);
|
||||||
this.setState({ showScreenName: !this.state.showScreenName });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTalkgroupCheckboxChange() {
|
const handleTalkgroupCheckboxChange = () => {
|
||||||
this.setState({ ignoreUnknownTalkgroup: !this.state.ignoreUnknownTalkgroup });
|
setIgnoreUnknownTalkgroup((current) => !current);
|
||||||
}
|
}
|
||||||
handleSubmit(event) {
|
const handleSubmit = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let inputError = this.checkInputs();
|
let inputError = checkInputs();
|
||||||
|
|
||||||
if (!inputError) {
|
if (!inputError) {
|
||||||
const system = (({
|
const system = {
|
||||||
name,
|
name,
|
||||||
shortName,
|
shortName,
|
||||||
description,
|
description,
|
||||||
systemType,
|
systemType,
|
||||||
city,
|
city,
|
||||||
state,
|
state: systemState,
|
||||||
county,
|
county,
|
||||||
country,
|
country,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}) => ({
|
};
|
||||||
name,
|
props.onSubmit(system);
|
||||||
shortName,
|
|
||||||
description,
|
|
||||||
systemType,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
county,
|
|
||||||
country,
|
|
||||||
showScreenName,
|
|
||||||
ignoreUnknownTalkgroup
|
|
||||||
}))(this.state);
|
|
||||||
this.props.onSubmit(system);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
var requestMessage = "";
|
|
||||||
var floatStyle = {
|
|
||||||
overflow: "auto"
|
|
||||||
};
|
|
||||||
|
|
||||||
const { isEditing } = this.props;
|
var requestMessage = "";
|
||||||
const stateOptions = [
|
var floatStyle = {
|
||||||
{ value: "AL", text: "Alabama" },
|
overflow: "auto"
|
||||||
{ 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>
|
|
||||||
<Checkbox
|
|
||||||
onChange={this.handleScreenNameCheckboxChange}
|
|
||||||
checked={this.state.showScreenName}
|
|
||||||
label="Display Screen Name in System Listing" />
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Checkbox
|
|
||||||
onChange={this.handleTalkgroupCheckboxChange}
|
|
||||||
checked={this.state.ignoreUnknownTalkgroup}
|
|
||||||
label="Ignore Unknown Talkgroups" />
|
|
||||||
</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>
|
const stateOptions = [
|
||||||
<Form.Group inline>
|
{ value: "AL", text: "Alabama" },
|
||||||
<label>System Type</label>
|
{ value: "AK", text: "Alaska" },
|
||||||
<Form.Radio
|
{ value: "AZ", text: "Arizona" },
|
||||||
label="State"
|
{ value: "AR", text: "Arkansas" },
|
||||||
name="systemType"
|
{ value: "CA", text: "California" },
|
||||||
value="state"
|
{ value: "CO", text: "Colorado" },
|
||||||
checked={this.state.systemType === "state"}
|
{ value: "CT", text: "Connecticut" },
|
||||||
onChange={this.handleInputChange}
|
{ value: "DE", text: "Delaware" },
|
||||||
/>
|
{ value: "DC", text: "District Of Columbia" },
|
||||||
<Form.Radio
|
{ value: "FL", text: "Florida" },
|
||||||
label="County"
|
{ value: "GA", text: "Georgia" },
|
||||||
name="systemType"
|
{ value: "HI", text: "Hawaii" },
|
||||||
value="county"
|
{ value: "ID", text: "Idaho" },
|
||||||
checked={this.state.systemType === "county"}
|
{ value: "IL", text: "Illinois" },
|
||||||
onChange={this.handleInputChange}
|
{ value: "IN", text: "Indiana" },
|
||||||
/>
|
{ value: "IA", text: "Iowa" },
|
||||||
<Form.Radio
|
{ value: "KS", text: "Kansas" },
|
||||||
label="City"
|
{ value: "KY", text: "Kentucky" },
|
||||||
name="systemType"
|
{ value: "LA", text: "Louisiana" },
|
||||||
value="city"
|
{ value: "ME", text: "Maine" },
|
||||||
checked={this.state.systemType === "city"}
|
{ value: "MD", text: "Maryland" },
|
||||||
onChange={this.handleInputChange}
|
{ value: "MA", text: "Massachusetts" },
|
||||||
/>
|
{ value: "MI", text: "Michigan" },
|
||||||
<Form.Radio
|
{ value: "MN", text: "Minnesota" },
|
||||||
label="International"
|
{ value: "MS", text: "Mississippi" },
|
||||||
name="systemType"
|
{ value: "MO", text: "Missouri" },
|
||||||
value="international"
|
{ value: "MT", text: "Montana" },
|
||||||
checked={this.state.systemType === "international"}
|
{ value: "NE", text: "Nebraska" },
|
||||||
onChange={this.handleInputChange}
|
{ value: "NV", text: "Nevada" },
|
||||||
/>
|
{ value: "NH", text: "New Hampshire" },
|
||||||
</Form.Group>
|
{ value: "NJ", text: "New Jersey" },
|
||||||
<Form.Group widths="equal">
|
{ value: "NM", text: "New Mexico" },
|
||||||
|
{ value: "NY", text: "New York" },
|
||||||
{this.state.systemType === "city" && (
|
{ value: "NC", text: "North Carolina" },
|
||||||
<Form.Field>
|
{ value: "ND", text: "North Dakota" },
|
||||||
<Form.Input
|
{ value: "OH", text: "Ohio" },
|
||||||
type="text"
|
{ value: "OK", text: "Oklahoma" },
|
||||||
name="city"
|
{ value: "OR", text: "Oregon" },
|
||||||
onChange={this.handleInputChange}
|
{ value: "PA", text: "Pennsylvania" },
|
||||||
error={this.state.cityError}
|
{ value: "RI", text: "Rhode Island" },
|
||||||
value={this.state.city}
|
{ value: "SC", text: "South Carolina" },
|
||||||
label="City"
|
{ value: "SD", text: "South Dakota" },
|
||||||
placeholder="City..."
|
{ value: "TN", text: "Tennessee" },
|
||||||
/>
|
{ value: "TX", text: "Texas" },
|
||||||
</Form.Field>
|
{ 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 (
|
||||||
|
checkInputMessages.length ||
|
||||||
|
props.requestMessage.length
|
||||||
|
) {
|
||||||
|
var counter = 0;
|
||||||
|
requestMessage = (
|
||||||
|
<Message compact warning icon>
|
||||||
|
<Icon name="lightning" />
|
||||||
|
<Message.Content>
|
||||||
|
<Message.Header>Problems...</Message.Header>
|
||||||
|
<ul>
|
||||||
|
{checkInputMessages.map(function (listValue) {
|
||||||
|
return <li key={`ListItem_${counter++}`}>{listValue}</li>;
|
||||||
|
})}
|
||||||
|
{props.requestMessage.length > 0 && (
|
||||||
|
<li key="req-mess">{props.requestMessage}</li>
|
||||||
)}
|
)}
|
||||||
{this.state.systemType === "county" && (
|
</ul>
|
||||||
<Form.Field>
|
</Message.Content>
|
||||||
<Form.Input
|
</Message>
|
||||||
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}
|
|
||||||
scrolling
|
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form className="raised padding segment" onSubmit={handleSubmit}>
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
onChange={e => setName(e.target.value)}
|
||||||
|
error={nameError}
|
||||||
|
value={name}
|
||||||
|
label="System name"
|
||||||
|
placeholder="System Name..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="shortName"
|
||||||
|
onChange={e => setShortName(e.target.value)}
|
||||||
|
error={shortNameError}
|
||||||
|
value={shortName}
|
||||||
|
disabled={isEditing}
|
||||||
|
label="System Short Name (4-10 characters)"
|
||||||
|
placeholder="Short Name..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form.Group>
|
||||||
|
<Divider horizontal />
|
||||||
|
<Form.Field>
|
||||||
|
<Checkbox
|
||||||
|
onChange={handleScreenNameCheckboxChange}
|
||||||
|
checked={showScreenName}
|
||||||
|
label="Display Screen Name in System Listing" />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Checkbox
|
||||||
|
onChange={handleTalkgroupCheckboxChange}
|
||||||
|
checked={ignoreUnknownTalkgroup}
|
||||||
|
label="Ignore Unknown Talkgroups" />
|
||||||
|
</Form.Field>
|
||||||
|
<Divider horizontal />
|
||||||
|
<Form.TextArea
|
||||||
|
label="Description"
|
||||||
|
name="description"
|
||||||
|
onChange={e => setDescription(e.target.value)}
|
||||||
|
error={descriptionError}
|
||||||
|
value={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={systemType === "state"}
|
||||||
|
onChange={e => setSystemType("state")}
|
||||||
|
/>
|
||||||
|
<Form.Radio
|
||||||
|
label="County"
|
||||||
|
name="systemType"
|
||||||
|
value="county"
|
||||||
|
checked={systemType === "county"}
|
||||||
|
onChange={e => setSystemType("county")}
|
||||||
|
/>
|
||||||
|
<Form.Radio
|
||||||
|
label="City"
|
||||||
|
name="systemType"
|
||||||
|
value="city"
|
||||||
|
checked={systemType === "city"}
|
||||||
|
onChange={e => setSystemType("city")}
|
||||||
|
/>
|
||||||
|
<Form.Radio
|
||||||
|
label="International"
|
||||||
|
name="systemType"
|
||||||
|
value="international"
|
||||||
|
checked={systemType === "international"}
|
||||||
|
onChange={e => setSystemType("international")}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths="equal">
|
||||||
|
|
||||||
|
{systemType === "city" && (
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="city"
|
||||||
|
onChange={e => setCity(e.target.value)}
|
||||||
|
error={cityError}
|
||||||
|
value={city}
|
||||||
|
label="City"
|
||||||
|
placeholder="City..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
)}
|
||||||
|
{systemType === "county" && (
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="county"
|
||||||
|
onChange={e => setCounty(e.target.value)}
|
||||||
|
error={countyError}
|
||||||
|
value={county}
|
||||||
|
label="County"
|
||||||
|
placeholder="County..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
)}
|
||||||
|
{systemType === "international" ? (
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
type="text"
|
||||||
|
name="country"
|
||||||
|
onChange={e => setCountry(e.target.value)}
|
||||||
|
error={countryError}
|
||||||
|
value={country}
|
||||||
|
label="Country"
|
||||||
|
placeholder="Country..."
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
) : (
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Dropdown
|
||||||
|
name="state"
|
||||||
|
onChange={(e, data ) => {setSystemState(data.value); console.log(data.value)}}
|
||||||
|
error={systemStateError}
|
||||||
|
value={systemState}
|
||||||
|
options={stateOptions}
|
||||||
|
scrolling
|
||||||
|
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;
|
export default SystemForm;
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
import React, { Component } 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 { useSelector } from 'react-redux'
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
class UpdateSystem extends Component {
|
const UpdateSystem = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
const { screenName } = useSelector((state) => state.user);
|
||||||
this.handleSystemSubmit = this.handleSystemSubmit.bind(this);
|
const { shortName } = useParams();
|
||||||
|
const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery();
|
||||||
|
const [updateSystem] = useUpdateSystemMutation();
|
||||||
|
const [requestMessage, setRequestMessage] = useState("");
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
let system = false;
|
||||||
|
if (isSystemsSuccess) {
|
||||||
|
system = systemsData.systems.find(sys => sys.shortName === shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
|
||||||
requestMessage: ""
|
const handleSystemSubmit = async (system) => {
|
||||||
|
try {
|
||||||
|
await updateSystem(system).unwrap();
|
||||||
|
navigate("/system/" + shortName)
|
||||||
|
} catch (error) {
|
||||||
|
const message = error.data.message;
|
||||||
|
console.log(message);
|
||||||
|
setRequestMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchSystems()
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSystemSubmit(system) {
|
return (
|
||||||
this.props
|
<Container text>
|
||||||
.updateSystem(system)
|
<Header as="h1">Update System</Header>
|
||||||
.then(requestMessage => {
|
<SystemForm onSubmit={handleSystemSubmit} system={system} isEditing={true} requestMessage={requestMessage} screenName={screenName} />
|
||||||
if (requestMessage) {
|
</Container>
|
||||||
// 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
|
export default UpdateSystem
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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 };
|
|
||||||
}
|
|
||||||
|
|
||||||
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", {withCredentials: true})
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const FETCH = "FETCH"
|
|
||||||
export const FETCH_SUCCESS = "FETCH_SUCCESS"
|
|
||||||
export const FETCH_ERROR = "FETCH_ERROR"
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
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,
|
|
||||||
withCredentials: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_ADMIN_SERVER + "/systems/" + data.shortName, {withCredentials: true})
|
|
||||||
.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", {withCredentials: true})
|
|
||||||
.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", {withCredentials: true})
|
|
||||||
.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_ADMIN_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_ADMIN_SERVER + "/systems", {withCredentials: true})
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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"
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
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,
|
|
||||||
key: action.data.key,
|
|
||||||
userId: action.data.userId,
|
|
||||||
id: action.data.id,
|
|
||||||
showScreenName: action.data.showScreenName,
|
|
||||||
ignoreUnknownTalkgroup : action.data.ignoreUnknownTalkgroup
|
|
||||||
});
|
|
||||||
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;
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
import axios, { post } from 'axios';
|
|
||||||
//import { browserHistory } from "react-router"
|
|
||||||
import * as types from "./talkgroup-constants";
|
|
||||||
import { push } from 'connected-react-router';
|
|
||||||
|
|
||||||
// "Log In" action creators
|
|
||||||
function beginImportTalkgroup() {
|
|
||||||
return { type: types.IMPORT_TALKGROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function importTalkgroupSuccess(data) {
|
|
||||||
return {
|
|
||||||
type: types.IMPORT_SUCCESS_TALKGROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function importTalkgroupError(data) {
|
|
||||||
return {
|
|
||||||
type: types.IMPORT_ERROR_TALKGROUP,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginExportTalkgroup() {
|
|
||||||
return { type: types.EXPORT_TALKGROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginFetchTalkgroup() {
|
|
||||||
return { type: types.FETCH_TALKGROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchTalkgroupSuccess(data) {
|
|
||||||
return { type: types.FETCH_SUCCESS_TALKGROUP,
|
|
||||||
data };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchTalkgroupError(data) {
|
|
||||||
return { type: types.FETCH_ERROR_TALKGROUP,
|
|
||||||
data };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeUrl(url) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(push(url));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/41878838/how-do-i-set-multipart-in-axios-with-react
|
|
||||||
function fileUpload(shortName, file){
|
|
||||||
const url = process.env.REACT_APP_ADMIN_SERVER + "/talkgroups/" + shortName + "/import";
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file',file)
|
|
||||||
const config = {
|
|
||||||
headers: {
|
|
||||||
'content-type': 'multipart/form-data'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
}
|
|
||||||
return post(url, formData,config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example of an Async Action Creator
|
|
||||||
// http://redux.js.org/docs/advanced/AsyncActions.html
|
|
||||||
export function importTalkgroups(
|
|
||||||
shortName, file,
|
|
||||||
successPath // path to redirect to upon successful log in
|
|
||||||
) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginImportTalkgroup());
|
|
||||||
|
|
||||||
return fileUpload(shortName, file)
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
var data = {};
|
|
||||||
data.shortName = shortName;
|
|
||||||
data.talkgroups = response.data.talkgroups
|
|
||||||
dispatch(importTalkgroupSuccess(data));
|
|
||||||
// use browserHistory singleton to control navigation. Will generate a
|
|
||||||
// state change for time-traveling as we are using the react-router-redux package
|
|
||||||
//browserHistory.push(successPath)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
dispatch(importTalkgroupError(response.data.message));
|
|
||||||
return response.data.message;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function(response) {
|
|
||||||
if (response instanceof Error) {
|
|
||||||
// Something happened in setting up the request that triggered an Error
|
|
||||||
console.log("Error", response.message);
|
|
||||||
return response.message;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function exportTalkgroups(
|
|
||||||
shortName
|
|
||||||
) {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginExportTalkgroup());
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(process.env.REACT_APP_ADMIN_SERVER + "/talkgroups/" + shortName + "/export", { withCredentials: true })
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
var data = {shortName: shortName, talkgroups: response.data.talkgroups}
|
|
||||||
dispatch(fetchTalkgroupSuccess( data));
|
|
||||||
} else {
|
|
||||||
dispatch(fetchTalkgroupError());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function shouldFetchTalkgroups(state, shortName) {
|
|
||||||
if (!state.talkgroup.items.hasOwnProperty(shortName)) {
|
|
||||||
return true
|
|
||||||
} else if (state.talkgroup.isWaiting) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchTalkgroups(shortName) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (shouldFetchTalkgroups(getState(), shortName)) {
|
|
||||||
dispatch(beginFetchTalkgroup());
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(process.env.REACT_APP_ADMIN_SERVER + "/talkgroups/"+shortName, { withCredentials: true })
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
var data = {shortName: shortName, talkgroups: response.data.talkgroups}
|
|
||||||
dispatch(fetchTalkgroupSuccess( data));
|
|
||||||
} else {
|
|
||||||
dispatch(fetchTalkgroupError());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export const IMPORT_TALKGROUP = "IMPORT_TALKGROUP"
|
|
||||||
export const IMPORT_SUCCESS_TALKGROUP = "IMPORT_SUCCESS_TALKGROUP"
|
|
||||||
export const IMPORT_ERROR_TALKGROUP = "IMPORT_ERROR_TALKGROUP"
|
|
||||||
export const EXPORT_TALKGROUP = "EXPORT_TALKGROUP"
|
|
||||||
export const EXPORT_SUCCESS_TALKGROUP = "EXPORT_SUCCESS_TALKGROUP"
|
|
||||||
export const EXPORT_ERROR_TALKGROUP = "EXPORT_ERROR_TALKGROUP"
|
|
||||||
export const FETCH_TALKGROUP = "FETCH_TALKGROUP"
|
|
||||||
export const FETCH_SUCCESS_TALKGROUP = "FETCH_SUCCESS_TALKGROUP"
|
|
||||||
export const FETCH_ERROR_TALKGROUP = "FETCH_ERROR_TALKGROUP"
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import {
|
|
||||||
IMPORT_TALKGROUP,
|
|
||||||
IMPORT_SUCCESS_TALKGROUP,
|
|
||||||
IMPORT_ERROR_TALKGROUP,
|
|
||||||
EXPORT_TALKGROUP,
|
|
||||||
EXPORT_SUCCESS_TALKGROUP,
|
|
||||||
EXPORT_ERROR_TALKGROUP,
|
|
||||||
FETCH_TALKGROUP,
|
|
||||||
FETCH_SUCCESS_TALKGROUP,
|
|
||||||
FETCH_ERROR_TALKGROUP
|
|
||||||
} from "./talkgroup-constants";
|
|
||||||
|
|
||||||
const talkgroup = (
|
|
||||||
state = {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {}},
|
|
||||||
action
|
|
||||||
) => {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case FETCH_TALKGROUP:
|
|
||||||
case IMPORT_TALKGROUP:
|
|
||||||
case EXPORT_TALKGROUP:
|
|
||||||
return Object.assign({}, state, { isWaiting: true});
|
|
||||||
|
|
||||||
case IMPORT_SUCCESS_TALKGROUP:
|
|
||||||
case FETCH_SUCCESS_TALKGROUP:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
items: {
|
|
||||||
...state.items,
|
|
||||||
[action.data.shortName]: action.data.talkgroups
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
case FETCH_ERROR_TALKGROUP:
|
|
||||||
case IMPORT_ERROR_TALKGROUP:
|
|
||||||
case EXPORT_ERROR_TALKGROUP:
|
|
||||||
case EXPORT_SUCCESS_TALKGROUP:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false
|
|
||||||
});
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default talkgroup;
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
//import { browserHistory } from "react-router"
|
|
||||||
import * as types from "./user-constants";
|
|
||||||
import { push } from 'connected-react-router';
|
|
||||||
|
|
||||||
|
|
||||||
// "Log Out" action creators
|
|
||||||
function beginLogout() {
|
|
||||||
return { type: types.LOGOUT_USER };
|
|
||||||
}
|
|
||||||
|
|
||||||
function logoutSuccess() {
|
|
||||||
return { type: types.LOGOUT_SUCCESS_USER };
|
|
||||||
}
|
|
||||||
|
|
||||||
function logoutError() {
|
|
||||||
return { type: types.LOGOUT_ERROR_USER };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example of an Async Action Creator
|
|
||||||
// http://redux.js.org/docs/advanced/AsyncActions.html
|
|
||||||
export function manualLogout() {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch(beginLogout());
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(process.env.REACT_APP_ACCOUNT_SERVER + "/logout", {withCredentials: true})
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.success) {
|
|
||||||
dispatch(logoutSuccess());
|
|
||||||
// use browserHistory singleton to control navigation. Will generate a
|
|
||||||
// state change for time-traveling as we are using the react-router-redux package
|
|
||||||
//browserHistory.push("/") // logout to home page
|
|
||||||
dispatch(push("/"));
|
|
||||||
} else {
|
|
||||||
dispatch(logoutError());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(response => {
|
|
||||||
if (response instanceof Error) {
|
|
||||||
// Something happened during logout that triggered an Error
|
|
||||||
console.log("Error", response.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export const LOGIN_SUCCESS_USER = "LOGIN_SUCCESS_USER"
|
|
||||||
export const LOGIN_EMAIL_ERROR_USER = "LOGIN_EMAIL_ERROR_USER"
|
|
||||||
export const LOGIN_ERROR_USER = "LOGIN_ERROR_USER"
|
|
||||||
export const LOGOUT_USER = "LOGOUT_USER"
|
|
||||||
export const LOGOUT_SUCCESS_USER = "LOGOUT_SUCCESS_USER"
|
|
||||||
export const LOGOUT_ERROR_USER = "LOGOUT_ERROR_USER"
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import {
|
|
||||||
LOGOUT_USER,
|
|
||||||
LOGOUT_SUCCESS_USER,
|
|
||||||
LOGOUT_ERROR_USER,
|
|
||||||
LOGIN_SUCCESS_USER,
|
|
||||||
LOGIN_ERROR_USER,
|
|
||||||
LOGIN_EMAIL_ERROR_USER
|
|
||||||
} from "./user-constants";
|
|
||||||
|
|
||||||
const user = (
|
|
||||||
state = {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: false,
|
|
||||||
userId: ""
|
|
||||||
},
|
|
||||||
action
|
|
||||||
) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case LOGIN_SUCCESS_USER:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: true,
|
|
||||||
...action.data
|
|
||||||
});
|
|
||||||
case LOGIN_EMAIL_ERROR_USER:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: false,
|
|
||||||
userId: action.data.userId
|
|
||||||
});
|
|
||||||
case LOGIN_ERROR_USER:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: false
|
|
||||||
});
|
|
||||||
case LOGOUT_USER:
|
|
||||||
return Object.assign({}, state, { isWaiting: true });
|
|
||||||
case LOGOUT_SUCCESS_USER:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: false,
|
|
||||||
email: "",
|
|
||||||
userId: ""
|
|
||||||
});
|
|
||||||
case LOGOUT_ERROR_USER:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
isWaiting: false,
|
|
||||||
authenticated: true
|
|
||||||
});
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default user;
|
|
||||||
201
admin/src/features/api/apiSlice.js
Normal file
201
admin/src/features/api/apiSlice.js
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// Import the RTK Query methods from the React-specific entry point
|
||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||||
|
|
||||||
|
/*
|
||||||
|
const axiosBaseQuery =
|
||||||
|
({ baseUrl } = { baseUrl: '' }) =>
|
||||||
|
async ({ url, method, data, params }) => {
|
||||||
|
try {
|
||||||
|
const result = await axios({ url: baseUrl + url, method, data, params })
|
||||||
|
return { data: result.data }
|
||||||
|
} catch (axiosError) {
|
||||||
|
let err = axiosError
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
status: err.response?.status,
|
||||||
|
data: err.response?.data || err.message,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Define our single API slice object
|
||||||
|
export const apiSlice = createApi({
|
||||||
|
// The cache reducer expects to be added at `state.api` (already default - this is optional)
|
||||||
|
reducerPath: 'api',
|
||||||
|
// All of our requests will have URLs starting with '/fakeApi'
|
||||||
|
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_ADMIN_SERVER }),
|
||||||
|
// The "endpoints" represent operations and requests for this server
|
||||||
|
endpoints: builder => ({
|
||||||
|
// The `getPosts` endpoint is a "query" operation that returns data
|
||||||
|
getTalkgroups: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `/talkgroups/${shortName}`, credentials: "include" })
|
||||||
|
}),
|
||||||
|
getGroups: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `/groups/${shortName}`, credentials: "include" }),
|
||||||
|
providesTags: (result) =>
|
||||||
|
result
|
||||||
|
? [...result.map(({ _id }) => ({ type: 'Group', _id })), 'Group']
|
||||||
|
: ['Group'],
|
||||||
|
}),
|
||||||
|
|
||||||
|
getErrors: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (shortName) => ({ url: `${process.env.REACT_APP_BACKEND_SERVER}/${shortName}/errors` })
|
||||||
|
}),
|
||||||
|
updateGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}/${group._id}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: group,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: updatedGroup } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', updatedGroup.shortName, (groups) => {
|
||||||
|
const groupIndex = groups.findIndex((obj => obj.groupId === updatedGroup.groupId));
|
||||||
|
groups[groupIndex] = updatedGroup;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: group,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newGroup } = await queryFulfilled;
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', newGroup.shortName, (groups) => {
|
||||||
|
groups.push(newGroup);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
deleteGroup: builder.mutation({
|
||||||
|
query: (group) => ({
|
||||||
|
url: `/groups/${group.shortName}/${group._id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials: "include"
|
||||||
|
}),
|
||||||
|
|
||||||
|
async onQueryStarted(group, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getGroups', group.shortName, (groups) => {
|
||||||
|
const groupIndex = groups.findIndex((obj => obj.groupId === group.groupId));
|
||||||
|
groups.splice(groupIndex,1);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
saveGroupOrder: builder.mutation({
|
||||||
|
query: ({shortName, order}) => ({
|
||||||
|
url: `/groups/${shortName}/reorder`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: order,
|
||||||
|
}),
|
||||||
|
|
||||||
|
//invalidatesTags: (result, error, group) => ['Group'],
|
||||||
|
}),
|
||||||
|
importTalkgroups: builder.mutation({
|
||||||
|
query: ({shortName,file}) => ({
|
||||||
|
url: `${process.env.REACT_APP_ADMIN_SERVER}/talkgroups/${shortName}/import`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: file,
|
||||||
|
}),
|
||||||
|
|
||||||
|
async onQueryStarted({shortName,file}, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newTalkgroups } = await queryFulfilled;
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getTalkgroups', shortName, (talkgroups) => {
|
||||||
|
talkgroups.length = 0; // Clear your array
|
||||||
|
talkgroups.push(...newTalkgroups);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
//invalidatesTags: (result, error, group) => ['Group'],
|
||||||
|
}),
|
||||||
|
getSystems: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: () => ({ url: `/systems`, credentials: "include" })
|
||||||
|
}),
|
||||||
|
createSystem: builder.mutation({
|
||||||
|
query: (system) => ({
|
||||||
|
url: `/systems`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: system,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(system, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: newSystem } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
systems.push(newSystem);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
updateSystem: builder.mutation({
|
||||||
|
query: (system) => ({
|
||||||
|
url: `/systems/${system.shortName}`,
|
||||||
|
method: 'POST',
|
||||||
|
credentials: "include",
|
||||||
|
body: system,
|
||||||
|
}),
|
||||||
|
async onQueryStarted(system, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: updatedSystem } = await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
const systemIndex = systems.findIndex((obj => obj.shortName === updatedSystem.shortName));
|
||||||
|
systems[systemIndex] = updatedSystem;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
deleteSystem: builder.mutation({
|
||||||
|
query: (shortName) => ({
|
||||||
|
url: `/systems/${shortName}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials: "include"
|
||||||
|
}),
|
||||||
|
async onQueryStarted(shortName, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
await queryFulfilled
|
||||||
|
dispatch(
|
||||||
|
apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
|
||||||
|
const systems = systemsData.systems;
|
||||||
|
const systemIndex = systems.findIndex((obj => obj.shortName === shortName));
|
||||||
|
systems.splice(systemIndex, 1);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Export the auto-generated hook for the `getPosts` query endpoint
|
||||||
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
|
||||||
import {
|
|
||||||
decrement,
|
|
||||||
increment,
|
|
||||||
incrementByAmount,
|
|
||||||
incrementAsync,
|
|
||||||
selectCount,
|
|
||||||
} from './counterSlice';
|
|
||||||
import styles from './Counter.module.css';
|
|
||||||
|
|
||||||
export function Counter() {
|
|
||||||
const count = useSelector(selectCount);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [incrementAmount, setIncrementAmount] = useState('2');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Increment value"
|
|
||||||
onClick={() => dispatch(increment())}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<span className={styles.value}>{count}</span>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Decrement value"
|
|
||||||
onClick={() => dispatch(decrement())}
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<input
|
|
||||||
className={styles.textbox}
|
|
||||||
aria-label="Set increment amount"
|
|
||||||
value={incrementAmount}
|
|
||||||
onChange={e => setIncrementAmount(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
onClick={() =>
|
|
||||||
dispatch(incrementByAmount(Number(incrementAmount) || 0))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Add Amount
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className={styles.asyncButton}
|
|
||||||
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Async
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:not(:last-child) {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 78px;
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
margin-top: 2px;
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
font-size: 32px;
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 12px;
|
|
||||||
outline: none;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
color: rgb(112, 76, 182);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(112, 76, 182, 0.1);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textbox {
|
|
||||||
font-size: 32px;
|
|
||||||
padding: 2px;
|
|
||||||
width: 64px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover, .button:focus {
|
|
||||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:active {
|
|
||||||
background-color: rgba(112, 76, 182, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton {
|
|
||||||
composes: button;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:after {
|
|
||||||
content: "";
|
|
||||||
background-color: rgba(112, 76, 182, 0.15);
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:active:after {
|
|
||||||
width: 0%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: 0s
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export const counterSlice = createSlice({
|
|
||||||
name: 'counter',
|
|
||||||
initialState: {
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
reducers: {
|
|
||||||
increment: state => {
|
|
||||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
|
||||||
// doesn't actually mutate the state because it uses the Immer library,
|
|
||||||
// which detects changes to a "draft state" and produces a brand new
|
|
||||||
// immutable state based off those changes
|
|
||||||
state.value += 1;
|
|
||||||
},
|
|
||||||
decrement: state => {
|
|
||||||
state.value -= 1;
|
|
||||||
},
|
|
||||||
incrementByAmount: (state, action) => {
|
|
||||||
state.value += action.payload;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
|
|
||||||
|
|
||||||
// The function below is called a thunk and allows us to perform async logic. It
|
|
||||||
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
|
|
||||||
// will call the thunk with the `dispatch` function as the first argument. Async
|
|
||||||
// code can then be executed and other actions can be dispatched
|
|
||||||
export const incrementAsync = amount => dispatch => {
|
|
||||||
setTimeout(() => {
|
|
||||||
dispatch(incrementByAmount(amount));
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The function below is called a selector and allows us to select a value from
|
|
||||||
// the state. Selectors can also be defined inline where they're used instead of
|
|
||||||
// in the slice file. For example: `useSelector((state) => state.counter.value)`
|
|
||||||
export const selectCount = state => state.counter.value;
|
|
||||||
|
|
||||||
export default counterSlice.reducer;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user