diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a4b74cc --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,42 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Existing Docker Compose (Extend)", + + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. + "dockerComposeFile": [ + "../docker-compose.yml", + "../test-compose.yml", + "docker-compose.yml" + ], + + // The 'service' property is the name of the service for the container that VS Code should + // use. Update this value and .devcontainer/docker-compose.yml to the real service name. + "service": "admin", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..93bf054 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + admin: + # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer + # folder. Note that the path of the Dockerfile and context is relative to the *primary* + # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile" + # array). The sample below assumes your primary file is in the root of your project. + # + # build: + # context: . + # dockerfile: .devcontainer/Dockerfile + + volumes: + # Update this to wherever you want VS Code to mount the folder of your project + - ..:/workspaces:cached + + # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. + # cap_add: + # - SYS_PTRACE + # security_opt: + # - seccomp:unconfined + + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" + diff --git a/admin/server/controllers/talkgroups.js b/admin/server/controllers/talkgroups.js index 4077116..2172048 100644 --- a/admin/server/controllers/talkgroups.js +++ b/admin/server/controllers/talkgroups.js @@ -40,6 +40,7 @@ exports.exportTalkgroups = function (req, res, next) { } res.statusCode = 200; res.setHeader("Content-Type", "text/csv"); + res.write("Decimal, Alpha Tag, Description, Priority\r\n"); talkgroups.forEach(function (item) { /* res.write('"' + item.num + '", "' + item.mode + '", "' @@ -51,12 +52,7 @@ exports.exportTalkgroups = function (req, res, next) { if (item) { var sending = ""; sending = item.num + ", "; - if (item.mode) { - sending = sending + item.mode.toString().replace(/\,/g, "") + ", "; - } else { - sending = sending + " ,"; - } - if (item.alpa) { + if (item.alpha) { sending = sending + item.alpha.toString().replace(/\,/g, "") + ", "; } else { sending = sending + " ,"; @@ -66,16 +62,6 @@ exports.exportTalkgroups = function (req, res, next) { } 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 { @@ -97,15 +83,7 @@ async function csv_import(shortName, filename, callback) { var parser = parse( { - columns: [ - "num", - "mode", - "alpha", - "description", - "tag", - "group", - "priority" - ] + columns: true, ltrim: true, rtrim: true }, async function (err, data) { if (err) { @@ -113,27 +91,31 @@ async function csv_import(shortName, filename, callback) { 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(); + console.log(row) + if (!( "Decimal" in row) || !("Description" in row) || !("Alpha Tag" in row)) { + console.error("Error: csv import column header error"); + callback("Column Headers are required - Required Columns: 'Decimal' 'Decscription' 'Alpha Tag' Optional Column: 'Priority' ", null); + return + } + + 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.num = parseInt(row["Decimal"]); + newTg.alpha = row["Alpha Tag"].toLowerCase(); + newTg.description = row["Description"]; + newTg.priority = row["Priority"]; newTg.shortName = shortName; await newTg.save().catch(err => { console.log("Error: CSV import: " + err); callback(err, null); + return }); - response.push(row); + response.push({num: newTg.num, alpha: newTg.alpha, description: newTg.description}); } callback(null, response); } @@ -147,34 +129,23 @@ async function csv_import(shortName, filename, callback) { 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 }); + res.status(500).send("Error - importTalkgroups: " + 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." - }); + res.status(404).send("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." - }); + console.error("Error - importTalkgroups: you are not the user for the system: " + req.user._id); + res.status(401).send("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." }); + res.status(500).send( "Please select a file." ); return; } @@ -183,9 +154,7 @@ exports.importTalkgroups = function (req, res, next) { 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(); + res.status(500).send(err); return; } else { res.json( data ); diff --git a/admin/src/System/System.js b/admin/src/System/System.js index c39616e..1274d28 100644 --- a/admin/src/System/System.js +++ b/admin/src/System/System.js @@ -13,9 +13,9 @@ import { Icon, Form, TextArea, + Message, Header, Container, - Message, List, Segment, Tab, @@ -31,7 +31,7 @@ const System = () => { //const { data: systemErrorData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName); const [deleteGroupAPI] = useDeleteGroupMutation(); const [reorderGroupsAPI] = useSaveGroupOrderMutation(); - const [importTalkgroupsAPI] = useImportTalkgroupsMutation(); + const [importTalkgroupsAPI, { error: importError }] = useImportTalkgroupsMutation(); const [deleteSystemAPI ] = useDeleteSystemMutation(); const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openMessage, setOpenMessage] = useState(false); @@ -401,22 +401,13 @@ const System = () => { - Number - The talkgroup number + Decimal + The talkgroup number in decimal - Mode - - The mode for the talkgroup, it can be: 'A' = analog, 'D' = - P25 Digital, 'E' = Encrypted - - - - - - Alpha + Alpha Tag Up to 12 charecter description of the talkgroup @@ -430,31 +421,6 @@ const System = () => { - - - Tag - - The type of talkgroup, eg: Dispatch, Fireground - - - - - - Group - - The department that uses this talkgroup - - - - - - Priority - - The priority for recording this talkgroup, 100 or over - will be skipped - - - @@ -483,6 +449,12 @@ const System = () => { Export Talkgroups + {importError ? ( +
+ {importError.data} +
+ ) : (
)} + ) diff --git a/admin/src/Talkgroups/ListTalkgroups.js b/admin/src/Talkgroups/ListTalkgroups.js index a63be18..da1816a 100644 --- a/admin/src/Talkgroups/ListTalkgroups.js +++ b/admin/src/Talkgroups/ListTalkgroups.js @@ -20,16 +20,15 @@ class ListTalkgroups extends Component { - Mode - Number - Alpha + + Decimal + Alpha Tag Description {talkgroups.map((talkgroup, i) => ( - {talkgroup.mode} {talkgroup.num} {talkgroup.alpha} {talkgroup.description} diff --git a/admin/src/features/api/apiSlice.js b/admin/src/features/api/apiSlice.js index 2edd0a0..410f1a3 100644 --- a/admin/src/features/api/apiSlice.js +++ b/admin/src/features/api/apiSlice.js @@ -94,7 +94,7 @@ export const apiSlice = createApi({ await queryFulfilled dispatch( apiSlice.util.updateQueryData('getGroups', group.shortName, (groups) => { - const groupIndex = groups.findIndex((obj => obj.groupId === group.groupId)); + const groupIndex = groups.findIndex((obj => obj._id === group._id)); groups.splice(groupIndex,1); }) ) diff --git a/backend/controllers/talkgroups.js b/backend/controllers/talkgroups.js index 81d78af..2835f61 100644 --- a/backend/controllers/talkgroups.js +++ b/backend/controllers/talkgroups.js @@ -10,7 +10,8 @@ exports.get_talkgroups = async function (req, res) { var talkgroups = {}; for (var tg in tg_results) { - talkgroups[tg_results[tg].num] = tg_results[tg]; + var talkgroup = {num: tg_results[tg].num, alpha: tg_results[tg].alpha, description: tg_results[tg].description} + talkgroups[tg_results[tg].num] = talkgroup; } res.contentType('json'); res.send(JSON.stringify({