mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-13 17:27:37 -05:00
Merge pull request #147 from openmhz/feature/contact
Send messages to system owners
This commit is contained in:
@@ -13,6 +13,7 @@ exports.isLoggedIn = function (req, res, next) {
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
exports.listActiveUsers = async function (req,res,next) {
|
exports.listActiveUsers = async function (req,res,next) {
|
||||||
if (!req.user.admin) {
|
if (!req.user.admin) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
@@ -72,9 +73,9 @@ exports.listUserSystems = async function (req,res,next) {
|
|||||||
|
|
||||||
exports.listAllSystems = async function (req, res, next) {
|
exports.listAllSystems = async function (req, res, next) {
|
||||||
if (!req.user.admin) {
|
if (!req.user.admin) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
res.json({ success: false, message: "Not Authorized" });
|
res.json({ success: false, message: "Not Authorized" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const systems = await System.find().exec();
|
const systems = await System.find().exec();
|
||||||
@@ -97,6 +98,7 @@ exports.listAllSystems = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup,
|
ignoreUnknownTalkgroup,
|
||||||
lastActive,
|
lastActive,
|
||||||
active
|
active
|
||||||
@@ -112,6 +114,7 @@ exports.listAllSystems = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup,
|
ignoreUnknownTalkgroup,
|
||||||
lastActive,
|
lastActive,
|
||||||
active
|
active
|
||||||
@@ -159,6 +162,7 @@ exports.listSystems = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}) => ({
|
}) => ({
|
||||||
name,
|
name,
|
||||||
@@ -172,6 +176,7 @@ exports.listSystems = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}))(obj);
|
}))(obj);
|
||||||
if (obj.showScreenName) {
|
if (obj.showScreenName) {
|
||||||
@@ -345,6 +350,8 @@ exports.uniqueShortName = async function (req, res, next) {
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
|
// All the values you want to use here need to be copied over in validateSystem()
|
||||||
|
|
||||||
exports.updateSystem = async function (req, res, next) {
|
exports.updateSystem = async function (req, res, next) {
|
||||||
|
|
||||||
res.locals.system.name = res.locals.name;
|
res.locals.system.name = res.locals.name;
|
||||||
@@ -355,6 +362,7 @@ exports.updateSystem = async function (req, res, next) {
|
|||||||
res.locals.system.description = res.locals.description;
|
res.locals.system.description = res.locals.description;
|
||||||
res.locals.system.systemType = res.locals.systemType;
|
res.locals.system.systemType = res.locals.systemType;
|
||||||
res.locals.system.showScreenName = res.locals.showScreenName;
|
res.locals.system.showScreenName = res.locals.showScreenName;
|
||||||
|
res.locals.system.allowContact = res.locals.allowContact;
|
||||||
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
|
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
|
||||||
|
|
||||||
|
|
||||||
@@ -378,6 +386,7 @@ exports.updateSystem = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}) => ({
|
}) => ({
|
||||||
name,
|
name,
|
||||||
@@ -391,6 +400,7 @@ exports.updateSystem = async function (req, res, next) {
|
|||||||
userId,
|
userId,
|
||||||
key,
|
key,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}))(res.locals.system);
|
}))(res.locals.system);
|
||||||
returnSys.id = res.locals.system._id;
|
returnSys.id = res.locals.system._id;
|
||||||
@@ -406,6 +416,7 @@ 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;
|
||||||
|
res.locals.allowContact = req.body.allowContact;
|
||||||
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.status(500)
|
||||||
@@ -542,6 +553,7 @@ exports.createSystem = async function (req, res, next) {
|
|||||||
county,
|
county,
|
||||||
country,
|
country,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}) => ({
|
}) => ({
|
||||||
name,
|
name,
|
||||||
@@ -553,6 +565,7 @@ exports.createSystem = async function (req, res, next) {
|
|||||||
county,
|
county,
|
||||||
country,
|
country,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
}))(res.locals);
|
}))(res.locals);
|
||||||
system.key = key;
|
system.key = key;
|
||||||
@@ -579,6 +592,7 @@ exports.createSystem = async function (req, res, next) {
|
|||||||
country,
|
country,
|
||||||
userId,
|
userId,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup,
|
ignoreUnknownTalkgroup,
|
||||||
key
|
key
|
||||||
}) => ({
|
}) => ({
|
||||||
@@ -592,6 +606,7 @@ exports.createSystem = async function (req, res, next) {
|
|||||||
country,
|
country,
|
||||||
userId,
|
userId,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup,
|
ignoreUnknownTalkgroup,
|
||||||
key
|
key
|
||||||
}))(newSys);
|
}))(newSys);
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ var systemSchema = mongoose.Schema({
|
|||||||
state: String,
|
state: String,
|
||||||
description: String,
|
description: String,
|
||||||
showScreenName: Boolean,
|
showScreenName: Boolean,
|
||||||
|
allowContact: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
ignoreUnknownTalkgroup : Boolean,
|
ignoreUnknownTalkgroup : Boolean,
|
||||||
active: {type: Boolean, default: false},
|
active: {type: Boolean, default: false},
|
||||||
lastActive: Date,
|
lastActive: Date,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const SystemForm = (props) => {
|
|||||||
const [county, setCounty] = useState("");
|
const [county, setCounty] = useState("");
|
||||||
const [country, setCountry] = useState("");
|
const [country, setCountry] = useState("");
|
||||||
const [showScreenName, setShowScreenName] = useState(false);
|
const [showScreenName, setShowScreenName] = useState(false);
|
||||||
|
const [allowContact, setAllowContact] = useState(true);
|
||||||
const [ignoreUnknownTalkgroup, setIgnoreUnknownTalkgroup] = useState(false);
|
const [ignoreUnknownTalkgroup, setIgnoreUnknownTalkgroup] = useState(false);
|
||||||
const [nameError, setNameError] = useState(false);
|
const [nameError, setNameError] = useState(false);
|
||||||
const [shortNameError, setShortNameError] = useState(false);
|
const [shortNameError, setShortNameError] = useState(false);
|
||||||
@@ -45,6 +46,7 @@ const SystemForm = (props) => {
|
|||||||
setCounty(props.system.county);
|
setCounty(props.system.county);
|
||||||
setCountry(props.system.country);
|
setCountry(props.system.country);
|
||||||
setShowScreenName(props.system.showScreenName);
|
setShowScreenName(props.system.showScreenName);
|
||||||
|
setAllowContact(props.system.allowContact);
|
||||||
setIgnoreUnknownTalkgroup(props.system.ignoreUnknownTalkgroup);
|
setIgnoreUnknownTalkgroup(props.system.ignoreUnknownTalkgroup);
|
||||||
}
|
}
|
||||||
}, [props.system, props.isEditing]);
|
}, [props.system, props.isEditing]);
|
||||||
@@ -128,6 +130,12 @@ const SystemForm = (props) => {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleAllowContactCheckboxChange = () => {
|
||||||
|
setAllowContact((current) => !current);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleScreenNameCheckboxChange = () => {
|
const handleScreenNameCheckboxChange = () => {
|
||||||
setShowScreenName((current) => !current);
|
setShowScreenName((current) => !current);
|
||||||
}
|
}
|
||||||
@@ -150,6 +158,7 @@ const SystemForm = (props) => {
|
|||||||
county,
|
county,
|
||||||
country,
|
country,
|
||||||
showScreenName,
|
showScreenName,
|
||||||
|
allowContact,
|
||||||
ignoreUnknownTalkgroup
|
ignoreUnknownTalkgroup
|
||||||
};
|
};
|
||||||
props.onSubmit(system);
|
props.onSubmit(system);
|
||||||
@@ -272,6 +281,12 @@ const SystemForm = (props) => {
|
|||||||
checked={showScreenName}
|
checked={showScreenName}
|
||||||
label="Display Screen Name in System Listing" />
|
label="Display Screen Name in System Listing" />
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Checkbox
|
||||||
|
onChange={handleAllowContactCheckboxChange}
|
||||||
|
checked={allowContact}
|
||||||
|
label="Allow Users to Contact you via a Web Form. They will not get your email address unless you write them back." />
|
||||||
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={handleTalkgroupCheckboxChange}
|
onChange={handleTalkgroupCheckboxChange}
|
||||||
|
|||||||
@@ -1,11 +1,107 @@
|
|||||||
var mongoose = require("mongoose");
|
var mongoose = require("mongoose");
|
||||||
var User = require("../models/user");
|
var User = require("../models/user");
|
||||||
var System = require("../models/system");
|
var System = require("../models/system");
|
||||||
|
const Mailjet = require('node-mailjet');
|
||||||
|
|
||||||
|
var admin_email = process.env['REACT_APP_ADMIN_EMAIL'] != null ? process.env['REACT_APP_ADMIN_EMAIL'] : "luke@openmhz.com";
|
||||||
|
var admin_server = process.env['REACT_APP_ADMIN_SERVER'] != null ? process.env['REACT_APP_ADMIN_SERVER'] : "https://admin.openmhz.com";
|
||||||
|
var site_name = process.env['REACT_APP_SITE_NAME'] != null ? process.env['REACT_APP_SITE_NAME'] : "OpenMHz";
|
||||||
|
|
||||||
|
const mailjet = new Mailjet({
|
||||||
|
apiKey: process.env['MAILJET_KEY'],
|
||||||
|
apiSecret: process.env['MAILJET_SECRET']
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.contact_system = async function(req, res) {
|
||||||
|
var system = await System.findOne({
|
||||||
|
shortName: req.params.shortName.toLowerCase()
|
||||||
|
}).catch(err => {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
if (!system) {
|
||||||
|
console.error("[contact_system] The Short Name does not exist: " + req.params.shortName.toLowerCase());
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "That Short Name does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!system.allowContact) {
|
||||||
|
console.error("[contact_system] This System does not allow user to contact the owner: " + req.params.shortName.toLowerCase());
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "This System does not allow user to contact the owner."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const user = await User.findById(system.userId).exec();
|
||||||
|
if (!user) {
|
||||||
|
console.error("[contact_system] System User does not exist: " + req.params.shortName.toLowerCase());
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: "System User does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(user);
|
||||||
|
let message = "Thank you for contributing a feed to OpenMHz. A user has sent in a message about this feed:\n\n-------------------------------\n"
|
||||||
|
message = message + "User Name: " + req.body.name + "\n";
|
||||||
|
message = message + "User Email: " + req.body.email + "\n-------------------------------\n";
|
||||||
|
message = message + "Message:\n" + req.body.message + "\n-------------------------------\n\n";
|
||||||
|
message = message + "If you wish to stop receiving User Messages for this System:\n - goto the Admin for " + site_name + ": " + admin_server + "\n - find this System\n - Update it and turn off this Allow Contact option."
|
||||||
|
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
|
||||||
|
},{
|
||||||
|
Email: admin_email,
|
||||||
|
Name: site_name + " Admin"
|
||||||
|
}],
|
||||||
|
Subject: site_name + " - " + system.shortName + " - " + " User Comment",
|
||||||
|
TextPart: message
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
request.then(result => {
|
||||||
|
console.log(`User Comment sent to: ${user.email} for shortName: ${system.shortName}`)
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err.statusCode);
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
exports.get_systems = async function (req, res) {
|
exports.get_systems = async function (req, res) {
|
||||||
let fromDate = new Date(Date.now() - 60 * 60 * 24 * 30 * 1000);
|
let fromDate = new Date(Date.now() - 60 * 60 * 24 * 30 * 1000);
|
||||||
const results = await System.find({lastActive: {$gte: fromDate}}).populate('userId', "screenName").catch( err => {
|
const results = await System.find({lastActive: {$gte: fromDate}}).populate('userId', "screenName").catch( err => {
|
||||||
console.error("Error - get_systems: " + err.message);
|
console.error("Error - get_systems: " + err.message);
|
||||||
|
res.status(500);
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err
|
message: err
|
||||||
@@ -31,6 +127,7 @@ exports.get_systems = async function (req, res) {
|
|||||||
lastActive: results[result].lastActive,
|
lastActive: results[result].lastActive,
|
||||||
callAvg: results[result].callAvg,
|
callAvg: results[result].callAvg,
|
||||||
description: results[result].description,
|
description: results[result].description,
|
||||||
|
allowContact: results[result].allowContact,
|
||||||
clientCount: clientCount
|
clientCount: clientCount
|
||||||
}
|
}
|
||||||
if (results[result].showScreenName && results[result].userId) {
|
if (results[result].showScreenName && results[result].userId) {
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ app.post('/:shortName/upload', upload.single('call'), uploads.upload, async func
|
|||||||
|
|
||||||
/*------ SYSTEMS ----------*/
|
/*------ SYSTEMS ----------*/
|
||||||
app.get('/systems', addSystemClients, systems.get_systems);
|
app.get('/systems', addSystemClients, systems.get_systems);
|
||||||
|
app.post('/:shortName/contact', systems.contact_system);
|
||||||
|
|
||||||
/*------ TALKGROUPS ----------*/
|
/*------ TALKGROUPS ----------*/
|
||||||
app.get('/:shortName/talkgroups', talkgroups.get_talkgroups);
|
app.get('/:shortName/talkgroups', talkgroups.get_talkgroups);
|
||||||
@@ -249,6 +250,13 @@ io.sockets.on('connection', function (client) {
|
|||||||
clients[client.id].filterType = String(data.filterType);
|
clients[client.id].filterType = String(data.filterType);
|
||||||
clients[client.id].talkgroupNums = [];
|
clients[client.id].talkgroupNums = [];
|
||||||
clients[client.id].timestamp = new Date();
|
clients[client.id].timestamp = new Date();
|
||||||
|
|
||||||
|
if (typeof clients[client.id].filterCode != "string") {
|
||||||
|
console.error("Error - Socket - Invalid filterCode: " + data.filterCode + " ShortName: " + data.shortName.toLowerCase());
|
||||||
|
delete clients[client.id];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((data.filterType == "group") && (data.filterCode.indexOf(',') == -1)) {
|
if ((data.filterType == "group") && (data.filterCode.indexOf(',') == -1)) {
|
||||||
if (!ObjectId.isValid(data.filterCode)) {
|
if (!ObjectId.isValid(data.filterCode)) {
|
||||||
console.error("Error - Socket - Invalid Group ID: " + data.filterCode);
|
console.error("Error - Socket - Invalid Group ID: " + data.filterCode);
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ var systemSchema = mongoose.Schema({
|
|||||||
state: String,
|
state: String,
|
||||||
description: String,
|
description: String,
|
||||||
showScreenName: Boolean,
|
showScreenName: Boolean,
|
||||||
|
allowContact: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
callAvg: Number,
|
callAvg: Number,
|
||||||
callCount: Number,
|
callCount: Number,
|
||||||
ignoreUnknownTalkgroup : Boolean,
|
ignoreUnknownTalkgroup : Boolean,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"ffprobe-static": "3.1.0",
|
"ffprobe-static": "3.1.0",
|
||||||
"find-remove": "4.1.0",
|
"find-remove": "4.1.0",
|
||||||
"get-folder-size": "^4.0.0",
|
"get-folder-size": "^4.0.0",
|
||||||
|
"node-mailjet": "6.0.2",
|
||||||
"mkdirp": "^2.1.6",
|
"mkdirp": "^2.1.6",
|
||||||
"mongodb": "5.2.0",
|
"mongodb": "5.2.0",
|
||||||
"mongoose": "7.0.3",
|
"mongoose": "7.0.3",
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ services:
|
|||||||
pid: "host"
|
pid: "host"
|
||||||
environment:
|
environment:
|
||||||
- MONGO_NODE_DRIVER_HOST=mongo
|
- MONGO_NODE_DRIVER_HOST=mongo
|
||||||
|
- MAILJET_KEY
|
||||||
|
- MAILJET_SECRET
|
||||||
|
- REACT_APP_ADMIN_EMAIL
|
||||||
|
- REACT_APP_SITE_NAME
|
||||||
- NODE_ENV
|
- NODE_ENV
|
||||||
- REACT_APP_BACKEND_SERVER
|
- REACT_APP_BACKEND_SERVER
|
||||||
- REACT_APP_ADMIN_SERVER
|
- REACT_APP_ADMIN_SERVER
|
||||||
|
|||||||
97
frontend/src/System/ContactModal.js
Normal file
97
frontend/src/System/ContactModal.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import {useState} from 'react';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
List,
|
||||||
|
Form,
|
||||||
|
Grid
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import { useContactSystemMutation } from '../features/api/apiSlice'
|
||||||
|
|
||||||
|
function ContactModal(props) {
|
||||||
|
|
||||||
|
const [contactSystem, { isLoading }] = useContactSystemMutation()
|
||||||
|
const {system, onClose} = props;
|
||||||
|
const [email, setEmail] = useState(false);
|
||||||
|
const [name, setName] = useState(false);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [message, setMessage] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if ((!name || name.length < 3) || (!email || email.length < 3) || (!message || message.length < 3)) {
|
||||||
|
setError("Please fill out your Name, Email and a Message with something useful");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
message
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const shortName = system.shortName;
|
||||||
|
const returned = await contactSystem({body,shortName}).unwrap();
|
||||||
|
console.log(returned);
|
||||||
|
setName(false);
|
||||||
|
setEmail(false);
|
||||||
|
setMessage(false);
|
||||||
|
onClose();
|
||||||
|
} catch (submitError) {
|
||||||
|
console.error(error)
|
||||||
|
setError(submitError);
|
||||||
|
// you can handle errors here if you want to
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={onClose} size='large' >
|
||||||
|
<Modal.Header>Contact Feed Contributer</Modal.Header>
|
||||||
|
<Modal.Content>
|
||||||
|
<Modal.Description>
|
||||||
|
<Grid columns={2} divided>
|
||||||
|
<Grid.Row>
|
||||||
|
<Grid.Column width={6}>
|
||||||
|
<p>The radio systems on OpenMHz are contributed by people from around the world.</p> <p>Use this form to contact the contributer for the <span style={{fontWeight: "bold"}}>{system.name}</span> system regarding:
|
||||||
|
<List as='ul'>
|
||||||
|
<List.Item as='li'>Outages</List.Item>
|
||||||
|
<List.Item as='li'>Audio quality</List.Item>
|
||||||
|
<List.Item as='li'>Talkgroup updates</List.Item>
|
||||||
|
<List.Item as='li'>Thanks & appreciation</List.Item>
|
||||||
|
</List>
|
||||||
|
</p>
|
||||||
|
<p>If you have general questions about OpenMHz send me an email, </p>
|
||||||
|
<p> - Luke <a href="mailto:luke@robotastic.com?Subject=OpenMHz" target="_top">luke@robotastic.com</a> </p>
|
||||||
|
|
||||||
|
</Grid.Column>
|
||||||
|
<Grid.Column width={10}>
|
||||||
|
<Form>
|
||||||
|
<Form.Group widths='equal'>
|
||||||
|
<Form.Input fluid label='Name' placeholder='Name' onChange={e => setName(e.target.value)}/>
|
||||||
|
<Form.Input fluid label='Email' placeholder='Email' onChange={e => setEmail(e.target.value)} />
|
||||||
|
</Form.Group>
|
||||||
|
<Form.TextArea label='Message' placeholder='Provide feedback to the Feed Contributer' onChange={e => setMessage(e.target.value)} />
|
||||||
|
</Form>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid.Row>
|
||||||
|
</Grid>
|
||||||
|
</Modal.Description>
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Actions>
|
||||||
|
<Button onClick={(e) => onClose()} >
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={(e) => {handleSubmit(); e.stopPropagation();}} >
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
|
</Modal.Actions>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContactModal;
|
||||||
@@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import SystemCard from "./SystemCard";
|
import SystemCard from "./SystemCard";
|
||||||
|
|
||||||
|
|
||||||
const InternationList = (other) => {
|
const InternationList = (other, onContactClick) => {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
let international = [];
|
let international = [];
|
||||||
@@ -15,7 +15,7 @@ const InternationList = (other) => {
|
|||||||
<Card.Group itemsPerRow={4} stackable={true}>
|
<Card.Group itemsPerRow={4} stackable={true}>
|
||||||
{other &&
|
{other &&
|
||||||
other.map((system) => {
|
other.map((system) => {
|
||||||
return <SystemCard system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)} />
|
return <SystemCard system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)} onContactClick={onContactClick} />
|
||||||
})}
|
})}
|
||||||
</Card.Group>
|
</Card.Group>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, useRef } from "react";
|
import { Component, useRef, useState } from "react";
|
||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
|
import ContactModal from "./ContactModal";
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
@@ -24,7 +24,8 @@ import InternationList from "./InternationList";
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|
||||||
const ListSystems = (props) => {
|
const ListSystems = (props) => {
|
||||||
|
const [contactVisible, setContactVisible] = useState(false);
|
||||||
|
const [contactSystem, setContactSystem] = useState(false);
|
||||||
const { data: systems, isSuccess } = useGetSystemsQuery();
|
const { data: systems, isSuccess } = useGetSystemsQuery();
|
||||||
//const systems = useSelector(selectActiveSystems)
|
//const systems = useSelector(selectActiveSystems)
|
||||||
const contextRef = useRef();
|
const contextRef = useRef();
|
||||||
@@ -45,13 +46,19 @@ const ListSystems = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleContactClick = (system) => {
|
||||||
|
setContactSystem(system);
|
||||||
|
setContactVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
const stateList = StateLinkList(states);
|
const stateList = StateLinkList(states);
|
||||||
const systemsByState = SystemsByState(states);
|
const systemsByState = SystemsByState(states, handleContactClick);
|
||||||
const international = InternationList(other);
|
const international = InternationList(other, handleContactClick);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={contextRef}>
|
<div ref={contextRef}>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
|
<ContactModal system={contactSystem} open={contactVisible} onClose={()=> setContactVisible(false)}/>
|
||||||
<Container >
|
<Container >
|
||||||
<Divider horizontal style={{ paddingTop: "5em", paddingBottom: "2em" }}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
<Divider horizontal style={{ paddingTop: "5em", paddingBottom: "2em" }}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
||||||
<Grid centered columns={2}>
|
<Grid centered columns={2}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Icon,
|
Icon,
|
||||||
@@ -7,15 +7,26 @@ import {
|
|||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
|
||||||
|
|
||||||
class SystemCard extends Component {
|
function SystemCard (props) {
|
||||||
|
|
||||||
render() {
|
const {system, keepShort, onClick, onContactClick} = props;
|
||||||
var location = "";
|
var location = "";
|
||||||
const system = this.props.system;
|
|
||||||
var clientCount = "";
|
var clientCount = "";
|
||||||
var callAvg = "";
|
var callAvg = "";
|
||||||
var description = system.description;
|
const description = system.description;
|
||||||
if (this.props.keepShort && (description.length > 100)) {
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleContactClick = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
onContactClick(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCardClick = (event) => {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keepShort && (description.length > 100)) {
|
||||||
description = description.substring(0, 96) + "...";
|
description = description.substring(0, 96) + "...";
|
||||||
}
|
}
|
||||||
if (system.callAvg) {
|
if (system.callAvg) {
|
||||||
@@ -75,7 +86,7 @@ class SystemCard extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Card link onClick={this.props.onClick} raised={true} centered={true} >
|
<Card link onClick={onCardClick} raised={true} centered={true} >
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Card.Header >{system.name}</Card.Header>
|
<Card.Header >{system.name}</Card.Header>
|
||||||
<Card.Meta>{location} </Card.Meta>
|
<Card.Meta>{location} </Card.Meta>
|
||||||
@@ -84,6 +95,7 @@ class SystemCard extends Component {
|
|||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Column >
|
<Grid.Column >
|
||||||
{system.screenName && <Label size="mini"><Icon name='user' /> {system.screenName}</Label>}
|
{system.screenName && <Label size="mini"><Icon name='user' /> {system.screenName}</Label>}
|
||||||
|
{/*system.allowContact && <Label size="mini" onClick={handleContactClick}><Icon name='mail' />Message</Label>*/}
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
@@ -99,7 +111,6 @@ class SystemCard extends Component {
|
|||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SystemCard;
|
export default SystemCard;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import SystemCard from "./SystemCard";
|
import SystemCard from "./SystemCard";
|
||||||
|
|
||||||
|
|
||||||
const SystemsByState = (states) => {
|
const SystemsByState = (states, onContactClick) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
let systemsByState = [];
|
let systemsByState = [];
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ const SystemsByState = (states) => {
|
|||||||
<Card.Group key={"group-" +state} itemsPerRow={4} stackable={true}>
|
<Card.Group key={"group-" +state} itemsPerRow={4} stackable={true}>
|
||||||
{states[state] &&
|
{states[state] &&
|
||||||
states[state].map((system) => {
|
states[state].map((system) => {
|
||||||
return <SystemCard system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)} />
|
return <SystemCard system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)} onContactClick={onContactClick} />
|
||||||
})}
|
})}
|
||||||
</Card.Group>
|
</Card.Group>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -54,10 +54,19 @@ export const apiSlice = createApi({
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: initialEvent
|
body: initialEvent
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
contactSystem: builder.mutation({
|
||||||
|
query: (args) => {
|
||||||
|
const {shortName, body} = args;
|
||||||
|
return {
|
||||||
|
url: `/${shortName}/contact`,
|
||||||
|
method: 'POST',
|
||||||
|
body: body
|
||||||
|
}}
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Export the auto-generated hook for the `getPosts` query endpoint
|
// Export the auto-generated hook for the `getPosts` query endpoint
|
||||||
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetStatsQuery, useGetEventsQuery, useGetEventQuery, useAddNewEventMutation } = apiSlice
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetStatsQuery, useGetEventsQuery, useGetEventQuery, useAddNewEventMutation, useContactSystemMutation } = apiSlice
|
||||||
|
|||||||
Reference in New Issue
Block a user