Probably should normalize the System query

This commit is contained in:
Luke Berndt
2023-03-22 09:52:48 -04:00
parent d474659c40
commit 24d2bc5706
7 changed files with 223 additions and 197 deletions

View File

@@ -23,7 +23,8 @@
"mongoose": "7.0.0", "mongoose": "7.0.0",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"passport": "0.6.0", "passport": "0.6.0",
"passport-local": "1.0.0" "passport-local": "1.0.0",
"redux-devtools-extension": "^2.13.9"
}, },
"devDependencies": { "devDependencies": {
"@reduxjs/toolkit": "^1.9.3", "@reduxjs/toolkit": "^1.9.3",

View File

@@ -84,86 +84,59 @@ exports.listSystems = async function (req, res, next) {
return; 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
}, function(err) {
if (err) return console.error(err);
// removed!
});
*/
System.remove({
shortName: shortName shortName: shortName
}, }).catch( err => console.error(err));
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({
await Group.deleteMany({
shortName: shortName shortName: shortName
}, }).catch(err => console.error(err));
function (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) {
@@ -245,7 +218,7 @@ 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.status(500); res.status(500);
res.json({ message: "ERROR: Unique ShortName - caught: " + err}); res.json({ message: "ERROR: Unique ShortName - caught: " + err });
return; return;
} }
@@ -267,45 +240,45 @@ exports.updateSystem = async function (req, res, next) {
res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup; res.locals.system.ignoreUnknownTalkgroup = res.locals.ignoreUnknownTalkgroup;
await res.locals.system.save().catch( err => { await res.locals.system.save().catch(err => {
res.status(500); res.status(500);
res.json({ res.json({
success: false, success: false,
message: err message: err
});
return;
}); });
var returnSys = (({ return;
name, });
shortName, var returnSys = (({
description, name,
systemType, shortName,
city, description,
state, systemType,
county, city,
country, state,
userId, county,
key, country,
showScreenName, userId,
ignoreUnknownTalkgroup key,
}) => ({ showScreenName,
name, ignoreUnknownTalkgroup
shortName, }) => ({
description, name,
systemType, shortName,
city, description,
state, systemType,
county, city,
country, state,
userId, county,
key, country,
showScreenName, userId,
ignoreUnknownTalkgroup key,
}))(res.locals.system); showScreenName,
returnSys.id = res.locals.system._id; ignoreUnknownTalkgroup
res.json(returnSys); }))(res.locals.system);
return; returnSys.id = res.locals.system._id;
} res.json(returnSys);
return;
}
@@ -466,46 +439,46 @@ exports.createSystem = async function (req, res, next) {
}))(res.locals); }))(res.locals);
system.key = key; system.key = key;
system.userId = new mongoose.Types.ObjectId(req.user._id); 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); console.error(err);
res.status(500) res.status(500)
res.json({ res.json({
success: false, success: false,
message: err message: err
}); });
return; return;
}); });
var returnSys = (({ var returnSys = (({
name, name,
shortName, shortName,
description, description,
systemType, systemType,
city, city,
state, state,
county, county,
country, country,
userId, userId,
showScreenName, showScreenName,
ignoreUnknownTalkgroup, ignoreUnknownTalkgroup,
key key
}) => ({ }) => ({
name, name,
shortName, shortName,
description, description,
systemType, systemType,
city, city,
state, state,
county, county,
country, country,
userId, userId,
showScreenName, showScreenName,
ignoreUnknownTalkgroup, ignoreUnknownTalkgroup,
key key
}))(newSys); }))(newSys);
returnSys.id = newSys._id; returnSys.id = newSys._id;
res.json( returnSys); res.json(returnSys);
return; return;
} }

View File

@@ -26,8 +26,15 @@ const ListGroups = (props) => {
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter //https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
const groups = props.groups; const groups = props.groups;
let groupsDisplay = [];
if (groups) { 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 ( return (
<Table > <Table >
@@ -40,6 +47,7 @@ const ListGroups = (props) => {
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
{groupsDisplay.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>
@@ -65,7 +73,9 @@ const ListGroups = (props) => {
/> />
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
))}
)
)}
</Table.Body> </Table.Body>
</Table> </Table>

View File

@@ -8,7 +8,7 @@ import UpdatePermissionModal from "../Permission/UpdatePermissionModalContainer.
import AddPermissionModal from "../Permission/AddPermissionModalContainer.js"; import AddPermissionModal from "../Permission/AddPermissionModalContainer.js";
import ErrorChart from "./ResponsiveErrorChart" import ErrorChart from "./ResponsiveErrorChart"
import CallChart from "./ResponsiveCallChart" 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 { import {
Button, Button,
Confirm, Confirm,
@@ -27,13 +27,15 @@ import {
const System = (props) => { const System = (props) => {
const { shortName } = useParams(); const { shortName } = useParams();
const navigate = useNavigate();
const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery(); const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery();
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName);
const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(shortName); const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(shortName);
const { data: errorsData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName); const { data: errorsData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName);
const [deleteGroupAPI, { isLoading: isDeleting }] = useDeleteGroupMutation(); const [deleteGroupAPI, { isLoading: isDeleting }] = useDeleteGroupMutation();
const [reorderGroupsAPI, { isLoading: isReordering }] = useSaveGroupOrderMutation(); const [reorderGroupsAPI, { isLoading: isReordering }] = useSaveGroupOrderMutation();
const [importTalkgroupsAPI, { isLoading: isImporting}] = useImportTalkgroupsMutation(); const [importTalkgroupsAPI, { isLoading: isImporting }] = useImportTalkgroupsMutation();
const [deleteSystemAPI ] = useDeleteSystemMutation();
const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false);
const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false); const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false);
const [openMessage, setOpenMessage] = useState(false); const [openMessage, setOpenMessage] = useState(false);
@@ -98,32 +100,34 @@ const System = (props) => {
var minValue = 0; var minValue = 0;
var maxValue = 0; var maxValue = 0;
var MS_PER_MINUTE = 60000; var MS_PER_MINUTE = 60000;
for (var j = 0; j < statistic.callTotals.length; j++) { if (statistic) {
var spotsBack = statistic.callTotals.length - j; for (var j = 0; j < statistic.callTotals.length; j++) {
var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); var spotsBack = statistic.callTotals.length - j;
if (time < minDate) minDate = time; var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
if (statistic.callTotals[j] > maxValue) if (time < minDate) minDate = time;
maxValue = statistic.callTotals[j]; if (statistic.callTotals[j] > maxValue)
callTotals.push({ y: statistic.callTotals[j], x: time }); 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; for (var j = 0; j < statistic.errorTotals.length; j++) {
var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); var spotsBack = statistic.errorTotals.length - j;
if (time < minDate) minDate = time; var time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
if (statistic.errorTotals[j] > maxValue) if (time < minDate) minDate = time;
maxValue = statistic.errorTotals[j]; if (statistic.errorTotals[j] > maxValue)
errorTotals.push({ x: time, y: statistic.errorTotals[j] }); maxValue = statistic.errorTotals[j];
} errorTotals.push({ x: time, y: statistic.errorTotals[j] });
const callData = { }
minDate: minDate, const callData = {
maxDate: maxDate, minDate: minDate,
minValue: minValue, maxDate: maxDate,
maxValue: maxValue, minValue: minValue,
callTotals: callTotals, maxValue: maxValue,
errorTotals: errorTotals 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))) { if ((newIndex < 0) || (newIndex > (groupOrder.length - 1))) {
return return
} }
@@ -187,28 +191,28 @@ const System = (props) => {
} }
const saveGroupOrder = async () => { 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 handleUpload = async (file) => {
const formData = new FormData(); const formData = new FormData();
formData.append('file',file) formData.append('file', file)
await importTalkgroupsAPI({shortName:shortName, file:formData}); await importTalkgroupsAPI({ shortName: shortName, file: formData });
} }
const handleSystemDeleteConfirm = () => { const handleSystemDeleteConfirm = async () => {
setOpenSystemDeleteConfirm(false); setOpenSystemDeleteConfirm(false);
/* try {
this.props.systemActions const originalPromiseResult = await deleteSystemAPI(shortName).unwrap();
.deleteSystem(this.props.system.shortName) navigate("/list-systems")
.then(requestMessage => { } catch (error) {
if (requestMessage) { const message = error.data.message;
// report to the user is there was a problem during login console.log(message);
this.setState({ requestMessage: requestMessage, openMessage: true }); setRequestMessage(message);
} }
});*/
}; };
const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false); const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false);
@@ -218,7 +222,7 @@ const System = (props) => {
system = systemsData.systems.find(sys => sys.shortName == shortName); system = systemsData.systems.find(sys => sys.shortName == shortName);
} }
useEffect(() => { useLayoutEffect(() => {
if (groupsData) { if (groupsData) {
let newOrder = []; let newOrder = [];
groupsData.forEach(group => { groupsData.forEach(group => {

View File

@@ -90,7 +90,17 @@ export const apiSlice = createApi({
credentials: "include" 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({ saveGroupOrder: builder.mutation({
query: ({shortName, order}) => ({ query: ({shortName, order}) => ({
@@ -138,7 +148,8 @@ export const apiSlice = createApi({
try { try {
const { data: newSystem } = await queryFulfilled const { data: newSystem } = await queryFulfilled
const patchResult = dispatch( const patchResult = dispatch(
apiSlice.util.updateQueryData('getSystems', system.shortName, (systems) => { apiSlice.util.updateQueryData('getSystems', undefined, (systemsData) => {
const systems = systemsData.systems;
systems.push(newSystem); systems.push(newSystem);
}) })
) )
@@ -156,7 +167,8 @@ export const apiSlice = createApi({
try { try {
const { data: updatedSystem } = await queryFulfilled const { data: updatedSystem } = await queryFulfilled
const patchResult = dispatch( 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)); const systemIndex = systems.findIndex((obj => obj.shortName == updatedSystem.shortName));
systems[systemIndex] = updatedSystem; systems[systemIndex] = updatedSystem;
}) })
@@ -164,8 +176,27 @@ export const apiSlice = createApi({
} catch {} } 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 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

View File

@@ -7,14 +7,16 @@ import { createRouterMiddleware } from '@lagunovsky/redux-react-router'
import createRootReducer from './reducers' import createRootReducer from './reducers'
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { apiSlice } from '../features/api/apiSlice' import { apiSlice } from '../features/api/apiSlice'
import { composeWithDevTools } from 'redux-devtools-extension'
//const composedEnhancers = composeWithDevTools(...enhancers)
export const history = createBrowserHistory() export const history = createBrowserHistory()
export default function configureStore(preloadedState) { export default function configureStore(preloadedState) {
const store = createStore( const store = createStore(
createRootReducer(history), // root reducer with router state createRootReducer(history), // root reducer with router state
preloadedState, preloadedState,
compose( composeWithDevTools(
applyMiddleware( applyMiddleware(
createRouterMiddleware(history), // for dispatching history actions createRouterMiddleware(history), // for dispatching history actions
thunk, thunk,

View File

@@ -8392,6 +8392,11 @@ reduce-function-call@^1.0.1:
dependencies: dependencies:
balanced-match "^1.0.0" 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: redux-thunk@2.4.2, redux-thunk@^2.4.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz" resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz"