From 8f7f4fae37ee149fa723207db64b0e1e138df6aa Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 18 Mar 2023 12:46:31 -0400 Subject: [PATCH] it works - the group stuff! --- admin/server/controllers/groups.js | 135 ++++++++------- admin/server/index.js | 1 + admin/src/Group/GroupModal.js | 266 +++++++++++++---------------- admin/src/Group/ListGroups.js | 28 ++- admin/src/System/ListSystems.js | 10 +- admin/src/System/System.js | 200 ++++++++++++---------- admin/src/features/api/apiSlice.js | 148 +++++++++------- 7 files changed, 400 insertions(+), 388 deletions(-) diff --git a/admin/server/controllers/groups.js b/admin/server/controllers/groups.js index d001b15..127ee3f 100644 --- a/admin/server/controllers/groups.js +++ b/admin/server/controllers/groups.js @@ -41,7 +41,7 @@ exports.getGroups = async function (req, res, next) { return; } } else { - const group = await Group.findOne( + const group = await Group.find( { shortName: req.params.shortName.toLowerCase() }).sort("position").exec(); @@ -63,57 +63,56 @@ exports.getGroups = async function (req, res, next) { ); }; -exports.deleteGroup = function (req, res, next) { - //router.get('/system/:shortName/remove_group/:groupId', isLoggedIn, function(req, res) { - 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! - }); +exports.deleteGroup = async function (req, res, next) { + const system = await System.findOne({ 'shortName': req.params.shortName.toLowerCase() }).catch(err => { + 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) { - process.nextTick(async function () { + process.nextTick(async function () { const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() }); if (!system) { @@ -132,7 +131,15 @@ exports.reorderGroups = function (req, res, next) { }); 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++) { @@ -166,14 +173,15 @@ exports.reorderGroups = function (req, res, next) { res.json({ success: true }); - } - ); + }); }; exports.upsertGroup = function (req, res, next) { + console.log(req.body) //router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) { process.nextTick(async function () { - let system = System.findOne({ shortName: req.params.shortName.toLowerCase() }); + console.log(req.body) + let system = await System.findOne({ shortName: req.params.shortName.toLowerCase() }); if (!system) { res.status(404); @@ -213,7 +221,7 @@ exports.upsertGroup = function (req, res, next) { var groupId = req.params.groupId; - let group = Group.findOneAndUpdate( + let group = await Group.findOneAndUpdate( { _id: new mongoose.Types.ObjectId(groupId), shortName: req.params.shortName.toLowerCase() @@ -225,20 +233,17 @@ exports.upsertGroup = function (req, res, next) { { new: true, upsert: true + }).catch(err => { + res.status(500); + res.json({ + success: false, + message: err + }); + return; }); + res.json(group); + return; - - if (!group) { - res.status(500); - res.json({ - success: false, - message: err - }); - return; - } else { - res.json(group); - return; - } } ); } diff --git a/admin/server/index.js b/admin/server/index.js index 917155c..2335f75 100644 --- a/admin/server/index.js +++ b/admin/server/index.js @@ -128,6 +128,7 @@ function isAdmin(req, res, next) { } function isLoggedIn(req, res, next) { + console.log(req.body); if (req.isAuthenticated()) return next(); res.status(401).send({ success: false, diff --git a/admin/src/Group/GroupModal.js b/admin/src/Group/GroupModal.js index e84a330..20e9640 100644 --- a/admin/src/Group/GroupModal.js +++ b/admin/src/Group/GroupModal.js @@ -1,4 +1,6 @@ -import React, { Component } from "react"; +import React, { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from "react"; +import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useUpdateGroupMutation, useCreateGroupMutation, useDeleteGroupMutation, useSaveGroupOrderMutation} from '../features/api/apiSlice' + import { Grid, Modal, @@ -13,84 +15,81 @@ import { // ---------------------------------------------------- -class GroupModal extends Component { - constructor(props) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleClose = this.handleClose.bind(this); +const GroupModal = (props) => { - // Set initial state - this.state = { - editGroup: false, - groupName: "", - group: { - name: "", - items: [] - }, - talkgroups: this.props.talkgroups ? this.props.talkgroups : [] - }; + const emptyGroup = { + name: "", + items: [] } + const [editGroup, setEditGroup] = useState(false); + const [groupName, setGroupName] = useState(""); + const [group, setGroup] = useState(emptyGroup) + const [talkgroups, setTalkgroups] = useState(props.talkgroups ? props.talkgroups : []) - componentDidMount() { - //this.props.groupActions.fetchGroups(this.props.shortName); + const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(props.shortName); + const { data: groupsData, isSuccess: isGroupsSuccess } = useGetGroupsQuery(props.shortName); + const [ updateGroup,{ isLoading: isUpdating } ] = useUpdateGroupMutation(); + const [ createGroup,{ isLoading: isCreating } ] = useCreateGroupMutation(); - this.props.talkgroupActions.fetchTalkgroups(this.props.shortName); - } + useEffect(() => { + if (talkgroupsData && groupsData && props.editGroupId) { + let editGroup = groupsData.find( + group => group._id === props.editGroupId + ); + let items = []; - componentDidUpdate(prevProps) { - - // This determines if you are trying to edit a group. - // In this case, the value of editGroupID will chane to the group being edited - if (this.props.talkgroups && this.props.groups && this.props.editGroupId && !prevProps.editGroupId) { - var editGroup = this.props.groups.find( - group => group._id === this.props.editGroupId - ); - var items = []; - - var talkgroups = this.props.talkgroups; - for (var i = 0; i < editGroup.talkgroups.length; i++) { - const talkgroupId = editGroup.talkgroups[i]; - const index = talkgroups.findIndex(tg => tg.num === talkgroupId); - if (index >= 0) { - const item = { - num: talkgroups[index].num, - description: talkgroups[index].description - }; - items.push(item); - talkgroups = talkgroups - .slice(0, index) - .concat(talkgroups.slice(index + 1)); - } + let talkgroups = talkgroupsData.slice(0); //copy + for (var i = 0; i < editGroup.talkgroups.length; i++) { + const talkgroupNum = editGroup.talkgroups[i]; + const index = talkgroups.findIndex(tg => tg.num === talkgroupNum); + if (index >= 0) { + 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 }); } - - if (this.props.talkgroups && !prevProps.talkgroups) { - // If we are not editing, make sure there is the full list on the left. - this.setState({ talkgroups: [...this.props.talkgroups] }) + // we want to remove that are part of the Group from the list on the Left + const group = { name: editGroup.groupName, items: items } + setGroup(group); + 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)) { - this.setState({ - groupName: "", group: { - name: "", - items: [] - }, - talkgroups: [...this.props.talkgroups] - }); + + useEffect(() => { + if (!props.editGroupId && talkgroupsData) { + setTalkgroups([...talkgroupsData]); } - } + },[talkgroupsData]); - removeTalkgroup(event, index) { + + useEffect(() => { + if (!props.editGroupId && props.talkgroups) { + setGroupName(""); + setGroup(emptyGroup); + setTalkgroups( [...props.talkgroups]); + } + },[props.editGroupId]); + + + + + + + + const removeTalkgroup = (event, index) => { const item = { - num: this.state.group.items[index].num, - description: this.state.group.items[index].description + num: group.items[index].num, + 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++) { if (item.num < talkgroups[i].num) { @@ -98,110 +97,76 @@ class GroupModal extends Component { break; } } - this.setState({ - group: { - items: this.state.group.items - .slice(0, index) - .concat(this.state.group.items.slice(index + 1)) - }, - talkgroups: talkgroups + setGroup( { + items: this.state.group.items + .slice(0, index) + .concat(this.state.group.items.slice(index + 1)) }); + setTalkgroups(talkgroups); } - handleInputChange = (e, { name, value }) => this.setState({ [name]: value }); + const handleInputChange = (e, { name, value }) => this.setState({ [name]: value }); - addTalkgroup(event, index) { + const addTalkgroup = (event, index) => { const item = { - num: this.state.talkgroups[index].num, - description: this.state.talkgroups[index].description + num: talkgroupsData[index].num, + description: talkgroupsData[index].description }; - this.setState({ - group: { items: this.state.group.items.concat(item) }, - talkgroups: this.state.talkgroups - .slice(0, index) - .concat(this.state.talkgroups.slice(index + 1)) - }); + setGroup({ items: group.items.concat(item) }) + setTalkgroups(talkgroups.slice(0, index).concat(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 = { - _id: this.props.editGroupId, - shortName: this.props.shortName, - groupName: this.state.groupName, + _id: props.editGroupId, + shortName: props.shortName, + groupName: groupName, talkgroups: JSON.stringify(talkgroupNums) }; - this.setState({ - groupName: "", group: { - name: "", - items: [] - }, - 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); - } - }); + setGroupName(""); + setGroup(emptyGroup); + setTalkgroups([...talkgroupsData]); + await updateGroup(data); + props.onClose(true); } else { const data = { - shortName: this.props.shortName, - groupName: this.state.groupName, + shortName: props.shortName, + groupName: groupName, talkgroups: JSON.stringify(talkgroupNums) }; - this.setState({ - groupName: "", group: { - name: "", - items: [] - }, - talkgroups: [...this.props.talkgroups] - }); + setGroupName(""); + setGroup(emptyGroup); + setTalkgroups([...talkgroupsData]); + + await createGroup(data); + 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 = { top: "15px", minHeight: "200px" }; return ( - + - {this.props.editGroupId ? "Edit Group" : "Create Group"} + {props.editGroupId ? "Edit Group" : "Create Group"} @@ -227,7 +192,7 @@ class GroupModal extends Component { this.addTalkgroup(e, i)} + onClick={e => addTalkgroup(e, i)} /> @@ -238,26 +203,26 @@ class GroupModal extends Component { setGroupName(e.target.value)} placeholder="Group Name" - value={this.state.groupName} + value={groupName} /> - {group.map((talkgroup, i) => ( + {group.items.map((talkgroup, i) => ( this.removeTalkgroup(e, i)} + onClick={e => removeTalkgroup(e, i)} /> {talkgroup.description} ))} - @@ -266,14 +231,13 @@ class GroupModal extends Component { - ); } -} export default GroupModal; diff --git a/admin/src/Group/ListGroups.js b/admin/src/Group/ListGroups.js index ef9420a..3d2f94c 100644 --- a/admin/src/Group/ListGroups.js +++ b/admin/src/Group/ListGroups.js @@ -19,18 +19,15 @@ import { // ---------------------------------------------------- -class ListGroups extends Component { - constructor(props) { - super(props); - - } +const ListGroups = (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 groups = this.props.groups; + + const groups = props.groups; if (groups) { + let groupsDisplay = props.order.map( id => props.groups.find( group => group._id == id )); return ( @@ -42,7 +39,7 @@ class ListGroups extends Component { - {groups.map((group, i) => ( + {groupsDisplay.map((group, i) => ( {group.groupName} {group.talkgroups.length} @@ -50,19 +47,21 @@ class ListGroups extends Component { this.props.editGroup(group._id)} + onClick={e => props.editGroup(group._id)} /> this.props.reorderGroup(i, i-1)} + link={i==0?false:true} + disabled={i==0?true:false} + onClick={e => props.reorderGroup(i, i-1)} /> this.props.reorderGroup(i, i+1)} + link={i==groupsDisplay.length-1?false:true} + disabled={i==groupsDisplay.length-1?true:false} + onClick={e => props.reorderGroup(i, i+1)} /> this.props.deleteGroup(group._id)} + onClick={e => props.deleteGroup(group._id)} /> @@ -77,6 +76,5 @@ class ListGroups extends Component { ); } } -} export default ListGroups; diff --git a/admin/src/System/ListSystems.js b/admin/src/System/ListSystems.js index 1904065..610e1af 100644 --- a/admin/src/System/ListSystems.js +++ b/admin/src/System/ListSystems.js @@ -10,14 +10,15 @@ import { } from "semantic-ui-react"; import { useGetSystemsQuery, } from '../features/api/apiSlice' import { useDispatch } from 'react-redux' -import { push } from '@lagunovsky/redux-react-router' + +import { useLocation, useNavigate, useParams } from 'react-router-dom'; // ---------------------------------------------------- const ListSystems = (props) => { const { data, isSuccess } = useGetSystemsQuery(); const dispatch = useDispatch(); - + const navigate = useNavigate(); let systems = false; if (isSuccess) { systems = data.systems; @@ -30,12 +31,13 @@ const ListSystems = (props) => {
Radio Systems
- + + {systems && systems.map((system) => - dispatch(push("/system/" + system.shortName))}/> + navigate("/system/" + system.shortName)}/> )}
diff --git a/admin/src/System/System.js b/admin/src/System/System.js index 1689c89..8992917 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} from '../features/api/apiSlice' +import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useSaveGroupOrderMutation } from '../features/api/apiSlice' import { Button, Confirm, @@ -31,6 +31,8 @@ const System = (props) => { 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 [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openPermissionDeleteConfirm, setOpenPermissionDeleteConfirm] = useState(false); const [openMessage, setOpenMessage] = useState(false); @@ -39,6 +41,7 @@ const System = (props) => { const [editGroupId, setEditGroupId] = useState(false); const [errorData, setErrorData] = useState({}); const [callData, setCallData] = useState({}); + const [groupOrder, setGroupOrder] = useState([]); const processErrors = (errors) => { @@ -149,109 +152,123 @@ const System = (props) => { } } */ - - const editGroup = (groupId) => { - setEditGroupId(groupId) - setGroupVisible((state)=> !state) - } - - const deleteGroup = (groupId) => { - //this.props.groupActions.deleteGroup(this.props.shortName, groupId); - } - - const reorderGroup = (oldIndex, newIndex) => { - /*this.props.groupActions.reorderGroup( - this.props.shortName, - oldIndex, - newIndex - );*/ - } - - - + const editGroup = (groupId) => { + setEditGroupId(groupId) + setGroupVisible((state) => !state) + } - const handleGroupClose = () => { - setEditGroupId(false) - setGroupVisible(false) + const deleteGroup = async (groupId) => { + for (const num in groupsData) { + if (groupsData[num]._id == groupId) { + await deleteGroupAPI(groupsData[num]) + } } - - const handleGroupToggle = () => { - setEditGroupId(false) - setGroupVisible((state)=> !state) + } + + const reorderGroup = (oldIndex, newIndex) => { + if ((newIndex < 0) || (newIndex > (groupOrder.length - 1))) { + return } - - - - const handleExport = () => { - /* this.props.talkgroupActions - .exportTalkgroups(this.props.system.shortName) - .then(requestMessage => { - if (requestMessage) { - // report to the user is there was a problem during registration - this.setState({ requestMessage }); - } - });*/ - } - - const saveGroupOrder = () => { - /*const order = this.props.groups.map(x => x._id); - const data = { - groupOrder: JSON.stringify(order) - }; - this.props.groupActions - .saveGroupOrder(this.props.system.shortName, data) - .then(requestMessage => { - if (requestMessage) { - } - });*/ - } - - - const handleUpload = (file) => { - /*this.props.talkgroupActions - .importTalkgroups(this.props.system.shortName, file) - .then(requestMessage => { - if (requestMessage) { - // report to the user is there was a problem during registration - this.setState({ requestMessage }); - } - });*/ - } - - - - const handleSystemDeleteConfirm = () => { - 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 }); - } - });*/ + const moveOrder = groupOrder.slice(); + moveOrder.splice(newIndex, 0, moveOrder.splice(oldIndex, 1)[0]); + setGroupOrder(moveOrder); + } + + const handleGroupClose = () => { + setEditGroupId(false) + setGroupVisible(false) + } + + const handleGroupToggle = () => { + setEditGroupId(false) + setGroupVisible((state) => !state) + } + + + + const handleExport = () => { + /* this.props.talkgroupActions + .exportTalkgroups(this.props.system.shortName) + .then(requestMessage => { + if (requestMessage) { + // report to the user is there was a problem during registration + this.setState({ requestMessage }); + } + });*/ + } + + const saveGroupOrder = async () => { + await reorderGroupsAPI({shortName: shortName, order: {groupOrder: JSON.stringify(groupOrder)}}); + + /*const order = this.props.groups.map(x => x._id); + const data = { + groupOrder: JSON.stringify(order) }; - const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false); + this.props.groupActions + .saveGroupOrder(this.props.system.shortName, data) + .then(requestMessage => { + if (requestMessage) { + } + });*/ + } - let system = false; + const handleUpload = (file) => { + /*this.props.talkgroupActions + .importTalkgroups(this.props.system.shortName, file) + .then(requestMessage => { + if (requestMessage) { + // report to the user is there was a problem during registration + this.setState({ requestMessage }); + } + });*/ + } + + + + const handleSystemDeleteConfirm = () => { + 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 }); + } + });*/ + }; + const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false); + + + let system = false; if (isSystemsSuccess) { - system = systemsData.systems.find(sys=>sys.shortName == shortName); + system = systemsData.systems.find(sys => sys.shortName == shortName); } useEffect(() => { - if (isSystemsSuccess) { - processStatistics(systemsData.stats[shortName]) - } - },[isSystemsSuccess]); + if (groupsData) { + let newOrder = []; + groupsData.forEach(group => { + + newOrder.push(group._id); + }); + setGroupOrder(newOrder); + } + }, [groupsData]) + + useEffect(() => { + if (isSystemsSuccess) { + processStatistics(systemsData.stats[shortName]) + } + }, [isSystemsSuccess]); useEffect(() => { if (isErrorsSuccess) { processErrors(errorData) } - },[isErrorsSuccess]); + }, [isErrorsSuccess]); let fileInput = null; var location = ""; @@ -266,6 +283,7 @@ const System = (props) => { {