From 590607ac10b22803e2543362dda0fe2d49429410 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 25 Jun 2023 21:58:30 -0400 Subject: [PATCH 1/3] Get Email for Active System Owners --- admin/server/controllers/systems.js | 36 +++++++++++++++++++++++++++ admin/server/index.js | 1 + admin/src/ActiveUsers/ActiveUsers.js | 37 ++++++++++++++++++++++++++++ admin/src/App/App.js | 2 ++ admin/src/App/Navigation.js | 4 +++ admin/src/features/api/apiSlice.js | 6 ++++- 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 admin/src/ActiveUsers/ActiveUsers.js diff --git a/admin/server/controllers/systems.js b/admin/server/controllers/systems.js index 190f1ca..6075de4 100644 --- a/admin/server/controllers/systems.js +++ b/admin/server/controllers/systems.js @@ -13,6 +13,42 @@ exports.isLoggedIn = function (req, res, next) { // ------------------------------------------- +exports.listActiveUsers = async function (req,res,next) { + if (!req.user.admin) { + res.status(401); + res.json({ success: false, message: "Not Authorized" }); + return; + } + let users = {}; + let fromDate = new Date(Date.now() - 60 * 60 * 24 * 30 * 1000); + const systems = await System.find({lastActive: {$gte: fromDate}}).populate('userId', "screenName").catch( err => { + console.error("Error - get_systems: " + err.message); + res.json({ + success: false, + message: err + }); + return; + }); + for (let index = 0; index < systems.length; index++) { + const system = systems[index]; + + if (!users.hasOwnProperty(system.userId.toString())) { + const user = await User.findById(system.userId, "_id email firstName lastName lastLogin"); + users[system.userId.toString()] = user.email; + } + + } + + let list = []; + for (const id in users) { + list.push(users[id]); + } + + res.json(list); + return; +} + + exports.listUserSystems = async function (req,res,next) { if (!req.user.admin) { res.status(401); diff --git a/admin/server/index.js b/admin/server/index.js index d6ddb96..8033f75 100644 --- a/admin/server/index.js +++ b/admin/server/index.js @@ -153,6 +153,7 @@ app.get("/groups/:shortName/:groupId?", isLoggedIn, groups.getGroups); app.delete("/groups/:shortName/:groupId", isLoggedIn, groups.deleteGroup); app.get("/systems", isLoggedIn, systems.listSystems) app.get("/admin/systems", isAdmin, systems.listAllSystems) +app.get("/admin/users/active", isAdmin, systems.listActiveUsers) app.get("/admin/users", isAdmin, systems.listUserSystems) app.delete("/systems/:shortName", isLoggedIn, systems.deleteSystem) app.post("/systems/:shortName", [isLoggedIn, systems.ownSystem, systems.validateSystem, systems.updateSystem]) diff --git a/admin/src/ActiveUsers/ActiveUsers.js b/admin/src/ActiveUsers/ActiveUsers.js new file mode 100644 index 0000000..7facb25 --- /dev/null +++ b/admin/src/ActiveUsers/ActiveUsers.js @@ -0,0 +1,37 @@ +import { + Container, + Header, + Table, + Icon +} from "semantic-ui-react"; +import { useGetActiveUsersQuery, } from '../features/api/apiSlice' + + + +// ---------------------------------------------------- +const ActiveUsers = () => { + const { data: users, isSuccess } = useGetActiveUsersQuery(); + + +//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 + + return ( +
+ +
Email of Active Users
+ + + {users && + users.map((user) => + + {user}, + )} + +
+
+ ); + +} + +export default ActiveUsers; diff --git a/admin/src/App/App.js b/admin/src/App/App.js index 8d5a75e..e818162 100644 --- a/admin/src/App/App.js +++ b/admin/src/App/App.js @@ -9,6 +9,7 @@ import AllSystems from "../AllSystems/AllSystems" import ListSystems from "../System/ListSystems" import UpdateSystem from "../System/UpdateSystem" import CreateSystem from "../System/CreateSystem" +import ActiveUsers from "../ActiveUsers/ActiveUsers" const App = (params) => { @@ -22,6 +23,7 @@ const App = (params) => { } /> } /> } /> + } /> } /> (
Miss
)} /> diff --git a/admin/src/App/Navigation.js b/admin/src/App/Navigation.js index e58fc72..8fee9c1 100644 --- a/admin/src/App/Navigation.js +++ b/admin/src/App/Navigation.js @@ -24,6 +24,10 @@ const Navigation = (props) => { {process.env.REACT_APP_SITE_NAME} + {admin && + + Active Users + } {admin && All Systems diff --git a/admin/src/features/api/apiSlice.js b/admin/src/features/api/apiSlice.js index 3af13dc..2edd0a0 100644 --- a/admin/src/features/api/apiSlice.js +++ b/admin/src/features/api/apiSlice.js @@ -140,6 +140,10 @@ export const apiSlice = createApi({ // The URL for the request is '/fakeApi/posts' query: () => ({ url: `/admin/users`, credentials: "include" }) }), + getActiveUsers: builder.query({ + // The URL for the request is '/fakeApi/posts' + query: () => ({ url: `/admin/users/active`, credentials: "include" }) + }), createSystem: builder.mutation({ query: (system) => ({ url: `/systems`, @@ -202,4 +206,4 @@ export const apiSlice = createApi({ }) // Export the auto-generated hook for the `getPosts` query endpoint -export const { useGetGroupsQuery, useGetSystemsQuery, useGetAllSystemsQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice +export const { useGetGroupsQuery, useGetSystemsQuery, useGetAllSystemsQuery, useGetActiveUsersQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice From 29233a41cc9b2e33e172ea278a41d46441362157 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Fri, 30 Jun 2023 20:01:20 -0400 Subject: [PATCH 2/3] updating the About --- frontend/src/About/AboutComponent.js | 133 ++++++++++++++++++++++++--- 1 file changed, 122 insertions(+), 11 deletions(-) diff --git a/frontend/src/About/AboutComponent.js b/frontend/src/About/AboutComponent.js index 45db264..26945cf 100644 --- a/frontend/src/About/AboutComponent.js +++ b/frontend/src/About/AboutComponent.js @@ -1,9 +1,60 @@ -import React from "react"; +import React, {useState} from "react"; import { Link } from 'react-router-dom' -import { Container, Header, Divider, Icon, Menu, Button } from "semantic-ui-react"; +import { Container, Header, Divider, Icon, Menu, Button, Accordion, } from "semantic-ui-react"; const AboutComponent = () => { + const [activeIndex, setActiveIndex] = useState(-1); + const handleClick = (e, titleProps) => { + const { index } = titleProps + const newIndex = activeIndex === index ? -1 : index + setActiveIndex(newIndex); + } + + const panels = [ + { + key: 'what-is-dog', + title: 'What is a dog?', + content: [ + 'A dog is a type of domesticated animal. Known for its loyalty and faithfulness, it can be found as a welcome', + 'guest in many households across the world.', + ].join(' '), + }, + { + key: 'kinds-of-dogs', + title: 'What kinds of dogs are there?', + content: [ + 'There are many breeds of dogs. Each breed varies in size and temperament. Owners often select a breed of dog', + 'that they find to be compatible with their own lifestyle and desires from a companion.', + ].join(' '), + }, + { + key: 'acquire-dog', + title: 'How do you acquire a dog?', + content: { + content: ( +
+

+ Three common ways for a prospective owner to acquire a dog is from + pet shops, private owners, or shelters. +

+

+ A pet shop may be the most convenient way to buy a dog. Buying a dog + from a private owner allows you to assess the pedigree and + upbringing of your dog before choosing to take it home. Lastly, + finding your dog from a shelter, helps give a good home to a dog who + may not find one so readily. +

+
+ ), + }, + }, + ] + + const AccordionExampleStandardShorthand = () => ( + + ) + return (
@@ -11,30 +62,90 @@ const AboutComponent = () => { Listen -
About
+
About

I wrote Trunk Recorder because I was curious about what my local fire station was up to and I put together the original version of OpenMHz because I figured other people might want to listen to the recordings too.

-

The latest version of this site makes it easy for other people running Trunk Recorder to share their recrodings. +

The latest version of this site makes it easy for other people running Trunk Recorder to share their recordings. I am hoping that making it easier to listen to what our local fire, police and EMS have to go through everyday will - lead to greater appreciation for all the work they do that goes largely unseen. I am personally amazed by the number - of medical runs the DC's Fire and EMS department has to cover every day, and the shocking number of them that are ODs.

+ lead to a greater appreciation for all the work they do, which goes largely unseen.

-

Each system should have a 30 day archive. This is to help make sure things don't get overloaded and make sure I don't get a crazy bill - at the end of the month.

+

The audio from each system is archived for 30 days, so you can go back and listen to events you may have missed.

- Luke luke@robotastic.com

-
Open Source
-

Have changes you would like to suggest? Want to run your own server for your community? The source code for OpenMHz has been released:

+
100% Open Source
+

Have changes you would like to suggest? Want to run your own server for your community? All of the code for OpenMHz is available as open source:

Trunk Server

Support

If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.

-

+
FAQ
+ + + + How do I create an Event? + + +

+ A dog is a type of domesticated animal. Known for its loyalty and + faithfulness, it can be found as a welcome guest in many households + across the world. +

+
+ + + How can I add a radio system? + + +

+ A dog is a type of domesticated animal. Known for its loyalty and + faithfulness, it can be found as a welcome guest in many households + across the world. +

+
+ + + Where do the different feeds come from? + + +

+ It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture + local radio systems and sending the transmissions to OpenMHz. +

+
+ + + Where do the different feeds come from? + + +

+ It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture + local radio systems and sending the transmissions to OpenMHz. +

+
+
); From 476f35f3572f20f8e8b22e732238a486e5d0cf85 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 8 Jul 2023 09:52:51 -0400 Subject: [PATCH 3/3] Added some content --- frontend/src/About/AboutComponent.js | 103 ++------------------------- 1 file changed, 6 insertions(+), 97 deletions(-) diff --git a/frontend/src/About/AboutComponent.js b/frontend/src/About/AboutComponent.js index 26945cf..dbfa3d7 100644 --- a/frontend/src/About/AboutComponent.js +++ b/frontend/src/About/AboutComponent.js @@ -13,47 +13,14 @@ const AboutComponent = () => { const panels = [ { - key: 'what-is-dog', - title: 'What is a dog?', + key: 'feeds', + title: {content: (Where do the different feeds come from?)}, content: [ - 'A dog is a type of domesticated animal. Known for its loyalty and faithfulness, it can be found as a welcome', - 'guest in many households across the world.', + 'It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture local radio systems and sending the transmissions to OpenMHz.', ].join(' '), - }, - { - key: 'kinds-of-dogs', - title: 'What kinds of dogs are there?', - content: [ - 'There are many breeds of dogs. Each breed varies in size and temperament. Owners often select a breed of dog', - 'that they find to be compatible with their own lifestyle and desires from a companion.', - ].join(' '), - }, - { - key: 'acquire-dog', - title: 'How do you acquire a dog?', - content: { - content: ( -
-

- Three common ways for a prospective owner to acquire a dog is from - pet shops, private owners, or shelters. -

-

- A pet shop may be the most convenient way to buy a dog. Buying a dog - from a private owner allows you to assess the pedigree and - upbringing of your dog before choosing to take it home. Lastly, - finding your dog from a shelter, helps give a good home to a dog who - may not find one so readily. -

-
- ), - }, - }, + } ] - const AccordionExampleStandardShorthand = () => ( - - ) return (
@@ -86,66 +53,8 @@ const AboutComponent = () => {

FAQ
- - - - How do I create an Event? - - -

- A dog is a type of domesticated animal. Known for its loyalty and - faithfulness, it can be found as a welcome guest in many households - across the world. -

-
- - - How can I add a radio system? - - -

- A dog is a type of domesticated animal. Known for its loyalty and - faithfulness, it can be found as a welcome guest in many households - across the world. -

-
- - - Where do the different feeds come from? - - -

- It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture - local radio systems and sending the transmissions to OpenMHz. -

-
- - - Where do the different feeds come from? - - -

- It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture - local radio systems and sending the transmissions to OpenMHz. -

-
-
+ +
);