diff --git a/admin/package.json b/admin/package.json
index 17c4e63..6d3d461 100644
--- a/admin/package.json
+++ b/admin/package.json
@@ -23,7 +23,8 @@
"mongoose": "7.0.0",
"multer": "1.4.5-lts.1",
"passport": "0.6.0",
- "passport-local": "1.0.0"
+ "passport-local": "1.0.0",
+ "redux-devtools-extension": "^2.13.9"
},
"devDependencies": {
"@reduxjs/toolkit": "^1.9.3",
diff --git a/admin/server/controllers/systems.js b/admin/server/controllers/systems.js
index 4e665ef..50047a9 100644
--- a/admin/server/controllers/systems.js
+++ b/admin/server/controllers/systems.js
@@ -84,86 +84,59 @@ exports.listSystems = async function (req, res, next) {
return;
};
-function remove_system(shortName) {
- /*
- var sysDir = config.mediaDirectory + "/" + shortName + "/"
- console.log("Removing DIR: " + sysDir);
- fs.remove(sysDir, function(err) {
- if (err) return console.error(err)
- });
+const remove_system = async (shortName) => {
- Transmission.remove({
- shortName: shortName
- }, function(err) {
- if (err) return console.error(err);
- // removed!
- });
-*/
- System.remove({
+ await System.deleteOne({
shortName: shortName
- },
- function (err) {
- if (err) return console.error(err);
- // removed!
- }
- );
+ }).catch( err => console.error(err));
- Talkgroup.remove({
+ await Talkgroup.deleteMany({
shortName: shortName
- },
- function (err) {
- if (err) return console.error(err);
- // removed!
- }
- );
+ }).catch( err => console.error(err));
- Group.remove({
+
+ await Group.deleteMany({
shortName: shortName
- },
- function (err) {
- if (err) return console.error(err);
- // removed!
- }
- );
+ }).catch(err => console.error(err));
}
-exports.deleteSystem = function (req, res, next) {
+exports.deleteSystem = async function (req, res, next) {
//router.get('/delete_system/:shortName', isLoggedIn, function(req, res) {
- System.findOne({
+ var system = await System.findOne({
shortName: req.params.shortName.toLowerCase()
- },
- function (err, system) {
- if (err) {
- res.json({
- success: false,
- message: err
- });
- return;
- }
- if (!system) {
- res.json({
- success: false,
- message: "That Short Name does not exist."
- });
- return;
- }
- if (!system.userId.equals(req.user._id)) {
- console.log("Error deleting syste - Wrong User - sys: " + system.userId + " profile: " + mongoose.Types.ObjectId(req.user._id));
- res.json({
- success: false,
- message: "You are not the user associated with this system."
- });
- return;
- }
- console.log("Removing: " + system.name);
- remove_system(system.shortName);
- res.json({
- success: true
- });
- return;
- }
- );
-};
+ }).catch(err => {
+ res.status(500);
+ res.json({
+ success: false,
+ message: err
+ });
+ return;
+ });
+ if (!system) {
+ res.status(500);
+ res.json({
+ success: false,
+ message: "That Short Name does not exist."
+ });
+ return;
+ }
+ if (!system.userId.equals(req.user._id)) {
+ console.log("Error deleting syste - Wrong User - sys: " + system.userId + " profile: " + mongoose.Types.ObjectId(req.user._id));
+ res.status(401);
+ res.json({
+ success: false,
+ message: "You are not the user associated with this system."
+ });
+ return;
+ }
+ console.log("Removing: " + system.name);
+ remove_system(system.shortName);
+ res.json({
+ success: true
+ });
+ return;
+}
+
// -------------------------------------------
exports.ownSystem = async function (req, res, next) {
@@ -245,7 +218,7 @@ exports.uniqueShortName = async function (req, res, next) {
} catch (err) {
console.error("ERROR: Unique ShortName - caught: " + err);
res.status(500);
- res.json({ message: "ERROR: Unique ShortName - caught: " + err});
+ res.json({ message: "ERROR: Unique ShortName - caught: " + err });
return;
}
@@ -267,45 +240,45 @@ exports.updateSystem = async function (req, res, next) {
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
- await res.locals.system.save().catch( err => {
- res.status(500);
- res.json({
- success: false,
- message: err
- });
- return;
+ await res.locals.system.save().catch(err => {
+ res.status(500);
+ res.json({
+ success: false,
+ message: err
});
- 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;
- }
+ 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;
+}
@@ -466,46 +439,46 @@ exports.createSystem = async function (req, res, next) {
}))(res.locals);
system.key = key;
system.userId = new mongoose.Types.ObjectId(req.user._id);
- const newSys = await System.create(system).catch( err => {
+ const newSys = await System.create(system).catch(err => {
- console.error(err);
- res.status(500)
- res.json({
- success: false,
- message: err
- });
- return;
+ console.error(err);
+ res.status(500)
+ res.json({
+ 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( returnSys);
- 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;
}
\ No newline at end of file
diff --git a/admin/src/Group/ListGroups.js b/admin/src/Group/ListGroups.js
index 3d2f94c..4be34f7 100644
--- a/admin/src/Group/ListGroups.js
+++ b/admin/src/Group/ListGroups.js
@@ -26,8 +26,15 @@ const ListGroups = (props) => {
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
const groups = props.groups;
+ let groupsDisplay = [];
if (groups) {
- let groupsDisplay = props.order.map( id => props.groups.find( group => group._id == id ));
+ // 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 (
@@ -40,6 +47,7 @@ const ListGroups = (props) => {
{groupsDisplay.map((group, i) => (
+
{group.groupName}
{group.talkgroups.length}
@@ -65,7 +73,9 @@ const ListGroups = (props) => {
/>
- ))}
+
+ )
+ )}
diff --git a/admin/src/System/System.js b/admin/src/System/System.js
index 004a93d..d986ca4 100644
--- a/admin/src/System/System.js
+++ b/admin/src/System/System.js
@@ -8,7 +8,7 @@ import UpdatePermissionModal from "../Permission/UpdatePermissionModalContainer.
import AddPermissionModal from "../Permission/AddPermissionModalContainer.js";
import ErrorChart from "./ResponsiveErrorChart"
import CallChart from "./ResponsiveCallChart"
-import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useImportTalkgroupsMutation, useSaveGroupOrderMutation } from '../features/api/apiSlice'
+import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useDeleteSystemMutation, useImportTalkgroupsMutation, useSaveGroupOrderMutation } from '../features/api/apiSlice'
import {
Button,
Confirm,
@@ -27,13 +27,15 @@ import {
const System = (props) => {
const { shortName } = useParams();
+ const navigate = useNavigate();
const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery();
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName);
const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(shortName);
const { data: errorsData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName);
const [deleteGroupAPI, { isLoading: isDeleting }] = useDeleteGroupMutation();
const [reorderGroupsAPI, { isLoading: isReordering }] = useSaveGroupOrderMutation();
- const [importTalkgroupsAPI, { isLoading: isImporting}] = useImportTalkgroupsMutation();
+ const [importTalkgroupsAPI, { isLoading: isImporting }] = useImportTalkgroupsMutation();
+ const [deleteSystemAPI ] = useDeleteSystemMutation();
const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false);
const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false);
const [openMessage, setOpenMessage] = useState(false);
@@ -98,32 +100,34 @@ const System = (props) => {
var minValue = 0;
var maxValue = 0;
var MS_PER_MINUTE = 60000;
- for (var j = 0; j < statistic.callTotals.length; j++) {
- var spotsBack = statistic.callTotals.length - j;
- var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
- if (time < minDate) minDate = time;
- if (statistic.callTotals[j] > maxValue)
- maxValue = statistic.callTotals[j];
- callTotals.push({ y: statistic.callTotals[j], x: time });
- }
- for (var j = 0; j < statistic.errorTotals.length; j++) {
- var spotsBack = statistic.errorTotals.length - j;
- var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
- if (time < minDate) minDate = time;
- if (statistic.errorTotals[j] > maxValue)
- maxValue = statistic.errorTotals[j];
- errorTotals.push({ x: time, y: statistic.errorTotals[j] });
- }
- const callData = {
- minDate: minDate,
- maxDate: maxDate,
- minValue: minValue,
- maxValue: maxValue,
- callTotals: callTotals,
- errorTotals: errorTotals
- };
+ if (statistic) {
+ for (var j = 0; j < statistic.callTotals.length; j++) {
+ var spotsBack = statistic.callTotals.length - j;
+ var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
+ if (time < minDate) minDate = time;
+ if (statistic.callTotals[j] > maxValue)
+ maxValue = statistic.callTotals[j];
+ callTotals.push({ y: statistic.callTotals[j], x: time });
+ }
+ for (var j = 0; j < statistic.errorTotals.length; j++) {
+ var spotsBack = statistic.errorTotals.length - j;
+ var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
+ if (time < minDate) minDate = time;
+ if (statistic.errorTotals[j] > maxValue)
+ maxValue = statistic.errorTotals[j];
+ errorTotals.push({ x: time, y: statistic.errorTotals[j] });
+ }
+ const callData = {
+ minDate: minDate,
+ maxDate: maxDate,
+ minValue: minValue,
+ maxValue: maxValue,
+ callTotals: callTotals,
+ errorTotals: errorTotals
+ };
- setCallData(callData);
+ setCallData(callData);
+ }
}
@@ -167,7 +171,7 @@ const System = (props) => {
}
}
- const reorderGroup = (oldIndex, newIndex) => {
+ const reorderGroup = (oldIndex, newIndex) => {
if ((newIndex < 0) || (newIndex > (groupOrder.length - 1))) {
return
}
@@ -187,28 +191,28 @@ const System = (props) => {
}
const saveGroupOrder = async () => {
- await reorderGroupsAPI({shortName: shortName, order: {groupOrder: JSON.stringify(groupOrder)}});
+ await reorderGroupsAPI({ shortName: shortName, order: { groupOrder: JSON.stringify(groupOrder) } });
}
const handleUpload = async (file) => {
const formData = new FormData();
- formData.append('file',file)
- await importTalkgroupsAPI({shortName:shortName, file:formData});
+ formData.append('file', file)
+ await importTalkgroupsAPI({ shortName: shortName, file: formData });
}
- const handleSystemDeleteConfirm = () => {
+ const handleSystemDeleteConfirm = async () => {
setOpenSystemDeleteConfirm(false);
- /*
- this.props.systemActions
- .deleteSystem(this.props.system.shortName)
- .then(requestMessage => {
- if (requestMessage) {
- // report to the user is there was a problem during login
- this.setState({ requestMessage: requestMessage, openMessage: true });
- }
- });*/
+ try {
+ const originalPromiseResult = await deleteSystemAPI(shortName).unwrap();
+ navigate("/list-systems")
+ } catch (error) {
+ const message = error.data.message;
+ console.log(message);
+ setRequestMessage(message);
+ }
+
};
const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false);
@@ -218,7 +222,7 @@ const System = (props) => {
system = systemsData.systems.find(sys => sys.shortName == shortName);
}
- useEffect(() => {
+ useLayoutEffect(() => {
if (groupsData) {
let newOrder = [];
groupsData.forEach(group => {
diff --git a/admin/src/features/api/apiSlice.js b/admin/src/features/api/apiSlice.js
index 83a5542..3c38fc4 100644
--- a/admin/src/features/api/apiSlice.js
+++ b/admin/src/features/api/apiSlice.js
@@ -90,7 +90,17 @@ export const apiSlice = createApi({
credentials: "include"
}),
- invalidatesTags: (result, error, group) => [{ type: 'Group', id: group._id }],
+ async onQueryStarted(group, { dispatch, queryFulfilled }) {
+ try {
+ await queryFulfilled
+ const patchResult = 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}) => ({
@@ -138,7 +148,8 @@ export const apiSlice = createApi({
try {
const { data: newSystem } = await queryFulfilled
const patchResult = dispatch(
- apiSlice.util.updateQueryData('getSystems', system.shortName, (systems) => {
+ apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
+ const systems = systemsData.systems;
systems.push(newSystem);
})
)
@@ -156,7 +167,8 @@ export const apiSlice = createApi({
try {
const { data: updatedSystem } = await queryFulfilled
const patchResult = dispatch(
- apiSlice.util.updateQueryData('getSystems', system.shortName, (systems) => {
+ apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
+ const systems = systemsData.systems;
const systemIndex = systems.findIndex((obj => obj.shortName == updatedSystem.shortName));
systems[systemIndex] = updatedSystem;
})
@@ -164,8 +176,27 @@ export const apiSlice = createApi({
} catch {}
},
}),
+ deleteSystem: builder.mutation({
+ query: (shortName) => ({
+ url: `/systems/${shortName}`,
+ method: 'DELETE',
+ credentials: "include"
+ }),
+ async onQueryStarted(shortName, { dispatch, queryFulfilled }) {
+ try {
+ await queryFulfilled
+ const patchResult = 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, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
+export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
diff --git a/admin/src/redux-router/configureStore.js b/admin/src/redux-router/configureStore.js
index 5313985..57ce03e 100644
--- a/admin/src/redux-router/configureStore.js
+++ b/admin/src/redux-router/configureStore.js
@@ -7,14 +7,16 @@ import { createRouterMiddleware } from '@lagunovsky/redux-react-router'
import createRootReducer from './reducers'
import thunk from 'redux-thunk';
import { apiSlice } from '../features/api/apiSlice'
+import { composeWithDevTools } from 'redux-devtools-extension'
+//const composedEnhancers = composeWithDevTools(...enhancers)
export const history = createBrowserHistory()
export default function configureStore(preloadedState) {
const store = createStore(
createRootReducer(history), // root reducer with router state
preloadedState,
- compose(
+ composeWithDevTools(
applyMiddleware(
createRouterMiddleware(history), // for dispatching history actions
thunk,
diff --git a/admin/yarn.lock b/admin/yarn.lock
index 8d4e193..af82f12 100644
--- a/admin/yarn.lock
+++ b/admin/yarn.lock
@@ -8392,6 +8392,11 @@ reduce-function-call@^1.0.1:
dependencies:
balanced-match "^1.0.0"
+redux-devtools-extension@^2.13.9:
+ version "2.13.9"
+ resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7"
+ integrity sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==
+
redux-thunk@2.4.2, redux-thunk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz"