mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-14 09:48:10 -05:00
Events is functional!!!
Just need to clean some things up and test
This commit is contained in:
@@ -4,12 +4,34 @@ var mongoose = require("mongoose");
|
|||||||
var Event = require("../models/event");
|
var Event = require("../models/event");
|
||||||
var { callModel: Call, callSchema } = require("../models/call");
|
var { callModel: Call, callSchema } = require("../models/call");
|
||||||
|
|
||||||
/*
|
exports.getEvent = async function (req, res, next) {
|
||||||
var oids = [];
|
|
||||||
ids.forEach(function(item){
|
try {
|
||||||
oids.push(new ObjectId(item));
|
var objectId = req.params.id;
|
||||||
});
|
const event = await Event.findById(objectId).exec();
|
||||||
*/
|
res.contentType('json');
|
||||||
|
res.send(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.send(`Error fetching Event ${req.params.id}: ` + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getEvents = async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
let events = await Event.find({},["title", "description", "startTime", "endTime", "expireTime", "numCalls", "createdAt"]).exec();
|
||||||
|
res.contentType('json');
|
||||||
|
res.send(JSON.stringify(events));
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500);
|
||||||
|
res.send("Error fetching Events" + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const compareCalls = (a, b) => {
|
const compareCalls = (a, b) => {
|
||||||
const aTimestamp = new Date(a.time).getTime()
|
const aTimestamp = new Date(a.time).getTime()
|
||||||
const bTimestamp = new Date(b.time).getTime()
|
const bTimestamp = new Date(b.time).getTime()
|
||||||
@@ -45,8 +67,8 @@ exports.addNewEvent = async function (req, res, next) {
|
|||||||
res.send("Not all of the Calls were found");
|
res.send("Not all of the Calls were found");
|
||||||
}
|
}
|
||||||
calls = calls.sort(compareCalls);
|
calls = calls.sort(compareCalls);
|
||||||
event.startTime = new Date(calls[0].time).getTime();
|
event.startTime = new Date(calls[0].time);
|
||||||
event.endTime = new Date(calls[calls.length - 1].time).getTime();
|
event.endTime = new Date(calls[calls.length - 1].time);
|
||||||
event.expireTime = event.startTime;
|
event.expireTime = event.startTime;
|
||||||
calls.forEach((call) => {
|
calls.forEach((call) => {
|
||||||
if (event.shortNames.indexOf(call.shortName) === -1) {
|
if (event.shortNames.indexOf(call.shortName) === -1) {
|
||||||
@@ -54,6 +76,7 @@ exports.addNewEvent = async function (req, res, next) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
event.calls = calls;
|
event.calls = calls;
|
||||||
|
event.numCalls = calls.length;
|
||||||
console.log(await event.save());
|
console.log(await event.save());
|
||||||
res.contentType('json');
|
res.contentType('json');
|
||||||
res.send(JSON.stringify({
|
res.send(JSON.stringify({
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ app.get('/:shortName/groups', groups.get_groups);
|
|||||||
|
|
||||||
/*------ EVENTS ----------*/
|
/*------ EVENTS ----------*/
|
||||||
app.post('/events', events.addNewEvent);
|
app.post('/events', events.addNewEvent);
|
||||||
|
app.get('/events', events.getEvents);
|
||||||
|
app.get('/events/:id', events.getEvent);
|
||||||
|
|
||||||
/*------ STATS ----------*/
|
/*------ STATS ----------*/
|
||||||
app.get('/:shortName/errors', stats.get_errors);
|
app.get('/:shortName/errors', stats.get_errors);
|
||||||
|
|||||||
@@ -2,18 +2,22 @@ var mongoose = require('mongoose');
|
|||||||
var {callSchema} = require('./call');
|
var {callSchema} = require('./call');
|
||||||
console.log(callSchema)
|
console.log(callSchema)
|
||||||
|
|
||||||
var groupSchema = mongoose.Schema({
|
var eventSchema = mongoose.Schema({
|
||||||
title: String,
|
title: String,
|
||||||
description: String,
|
description: String,
|
||||||
expireTime: Number,
|
expireTime: Date,
|
||||||
startTime: Number,
|
startTime: Date,
|
||||||
endTime: Number,
|
endTime: Date,
|
||||||
shortNames: [String],
|
shortNames: [String],
|
||||||
calls: [callSchema]
|
calls: [callSchema],
|
||||||
|
numCalls: Number
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true
|
timestamps: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add virtual if You want
|
||||||
module.exports = mongoose.model('Event', groupSchema);
|
eventSchema.virtual('callCount').get(function () {
|
||||||
|
return this.calls.length;
|
||||||
|
});
|
||||||
|
module.exports = mongoose.model('Event', eventSchema);
|
||||||
|
|||||||
@@ -366,6 +366,7 @@ function Calls(props) {
|
|||||||
<span> </span><Icon name="close" inverted={true} link={true} size='small' /></Menu.Item>
|
<span> </span><Icon name="close" inverted={true} link={true} size='small' /></Menu.Item>
|
||||||
<Link to="/"><Menu.Item link>Home</Menu.Item></Link>
|
<Link to="/"><Menu.Item link>Home</Menu.Item></Link>
|
||||||
<Link to="/systems"><Menu.Item link>Systems</Menu.Item></Link>
|
<Link to="/systems"><Menu.Item link>Systems</Menu.Item></Link>
|
||||||
|
<Link to="/events"><Menu.Item link>Events</Menu.Item></Link>
|
||||||
<Link to="/about"><Menu.Item link>About</Menu.Item></Link>
|
<Link to="/about"><Menu.Item link>About</Menu.Item></Link>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<Menu fixed="top">
|
<Menu fixed="top">
|
||||||
|
|||||||
@@ -7,16 +7,19 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
import { addStar, removeStar } from "../../features/calls/callsSlice";
|
import { addStar, removeStar } from "../../features/calls/callsSlice";
|
||||||
|
import { useGetTalkgroupsQuery } from '../../features/api/apiSlice'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch } from 'react-redux'
|
||||||
|
|
||||||
const CallItem = (props) => {
|
const CallItem = (props) => {
|
||||||
|
const call = props.call;
|
||||||
|
const activeCall = props.activeCall;
|
||||||
const [starVisible, setStarVisible] = useState(false);
|
const [starVisible, setStarVisible] = useState(false);
|
||||||
const [starClicked, setStarClicked] = useState(false);
|
const [starClicked, setStarClicked] = useState(false);
|
||||||
|
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(call.shortName);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const call = props.call;
|
|
||||||
const time = new Date(call.time);
|
const time = new Date(call.time);
|
||||||
const activeCall = props.activeCall;
|
|
||||||
|
|
||||||
const handleStarClicked = (e) => {
|
const handleStarClicked = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -79,10 +82,10 @@ const CallItem = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let talkgroup;
|
let talkgroup;
|
||||||
if ((typeof props.talkgroups == 'undefined') || (typeof props.talkgroups[call.talkgroupNum] == 'undefined')) {
|
if ((typeof talkgroupsData == 'undefined') || (typeof talkgroupsData.talkgroups[call.talkgroupNum] == 'undefined')) {
|
||||||
talkgroup = call.talkgroupNum;
|
talkgroup = call.talkgroupNum;
|
||||||
} else {
|
} else {
|
||||||
talkgroup = props.talkgroups[call.talkgroupNum].description;
|
talkgroup = talkgroupsData.talkgroups[call.talkgroupNum].description;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Table.Row draggable="true" onClick={(e) => props.onClick({ call: call }, e)} {...rowSelected} onDragStart={onDragStart} data-callid={call._id}>
|
<Table.Row draggable="true" onClick={(e) => props.onClick({ call: call }, e)} {...rowSelected} onDragStart={onDragStart} data-callid={call._id}>
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ const ListCalls = (props) => {
|
|||||||
{
|
{
|
||||||
props.callsData.ids.map((callId, index) => {
|
props.callsData.ids.map((callId, index) => {
|
||||||
if (callId === props.activeCallId) {
|
if (callId === props.activeCallId) {
|
||||||
return (<Ref innerRef={activeCallRef} key={index} ><CallItem activeCall={true} call={props.callsData.entities[callId]} talkgroups={props.talkgroups} key={index} onClick={props.playCall}/></Ref>)
|
return (<Ref innerRef={activeCallRef} key={index} ><CallItem activeCall={true} call={props.callsData.entities[callId]} key={index} onClick={props.playCall}/></Ref>)
|
||||||
} else {
|
} else {
|
||||||
return <CallItem activeCall={false} call={props.callsData.entities[callId]} talkgroups={props.talkgroups} key={index} onClick={props.playCall}/>
|
return <CallItem activeCall={false} call={props.callsData.entities[callId]} key={index} onClick={props.playCall}/>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
27
frontend/src/Common/NavBar.js
Normal file
27
frontend/src/Common/NavBar.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Header,
|
||||||
|
Card,
|
||||||
|
Icon,
|
||||||
|
Menu,
|
||||||
|
Divider
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
|
||||||
|
const NavBar = (props) => {
|
||||||
|
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<Menu fixed="top">
|
||||||
|
<Link to="/"><Menu.Item link><Icon name='arrow left' /> Home</Menu.Item></Link>
|
||||||
|
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
||||||
|
<Link to="/events"><Menu.Item link>Events</Menu.Item></Link>
|
||||||
|
<Link to="/about"><Menu.Item link>About</Menu.Item></Link>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default NavBar;
|
||||||
133
frontend/src/Event/EventPlayer.js
Normal file
133
frontend/src/Event/EventPlayer.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import React, { useEffect, useLayoutEffect, useState, useRef } from "react";
|
||||||
|
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import {skipToken} from '@reduxjs/toolkit/query'
|
||||||
|
import MediaPlayer from "../Call/components/MediaPlayer";
|
||||||
|
|
||||||
|
import SupportModal from "../Call/components/SupportModal";
|
||||||
|
|
||||||
|
import CallInfo from "../Call/components/CallInfo";
|
||||||
|
import ListEventCalls from "./ListEventCalls";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Label,
|
||||||
|
Rail,
|
||||||
|
Sticky,
|
||||||
|
Menu,
|
||||||
|
Icon,
|
||||||
|
Sidebar
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
|
||||||
|
|
||||||
|
import { useCallLink } from "../Call/components/CallLinks";
|
||||||
|
import "../Call/CallPlayer.css";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
function EventPlayer(props) {
|
||||||
|
|
||||||
|
|
||||||
|
const eventData = props.eventData;
|
||||||
|
const selectCallId = props.selectCallId;
|
||||||
|
|
||||||
|
|
||||||
|
const [autoPlay, setAutoPlay] = useState(true);
|
||||||
|
const [currentCall, setCurrentCall] = useState(false);
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
const {callLink,callDownload,callTweet} = useCallLink(currentCall)
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const positionRef = useRef(); // lets us get the Y Scroll offset for the Call List
|
||||||
|
const shouldPlayAddCallRef = useRef(); // we need to do this to make the current value of isPlaying available in the socket message callback
|
||||||
|
shouldPlayAddCallRef.current = (!isPlaying && autoPlay)?true:false;
|
||||||
|
|
||||||
|
let currentCallId = false;
|
||||||
|
|
||||||
|
if (currentCall) {
|
||||||
|
currentCallId = currentCall._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePlayPause = (playing) => {
|
||||||
|
setIsPlaying(playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleAutoPlay = (currentAutoPlay) => {
|
||||||
|
setAutoPlay(!currentAutoPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
const playCall = (data) => {
|
||||||
|
setCurrentCall(data.call);
|
||||||
|
setIsPlaying(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const callEnded = () => {
|
||||||
|
if (eventData) {
|
||||||
|
const currentIndex = eventData.calls.findIndex(call => call._id === currentCallId);
|
||||||
|
if (autoPlay && (currentIndex > 0)) {
|
||||||
|
|
||||||
|
const nextCall = eventData.calls[currentIndex - 1];
|
||||||
|
|
||||||
|
setCurrentCall(nextCall);
|
||||||
|
setIsPlaying(true);
|
||||||
|
} else {
|
||||||
|
setIsPlaying(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Somehow called, callEnded() but callsData was false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ( selectCallId && eventData && !currentCall) {
|
||||||
|
const call = eventData.calls.find( (call) => call._id == selectCallId);
|
||||||
|
if (call) {
|
||||||
|
setCurrentCall(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectCallId, eventData])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={positionRef}>
|
||||||
|
<Container className="main" >
|
||||||
|
<Sidebar.Pushable>
|
||||||
|
<Sidebar.Pusher
|
||||||
|
style={{ minHeight: '100vh' }}
|
||||||
|
>
|
||||||
|
|
||||||
|
<ListEventCalls eventData={eventData} activeCallId={isPlaying?currentCallId:false} playCall={playCall} />
|
||||||
|
|
||||||
|
</Sidebar.Pusher>
|
||||||
|
</Sidebar.Pushable>
|
||||||
|
<Rail position='right' className="desktop-only" >
|
||||||
|
<Sticky offset={60} context={positionRef}>
|
||||||
|
<CallInfo call={currentCall} />
|
||||||
|
</Sticky>
|
||||||
|
</Rail>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Menu fixed="bottom" compact inverted >
|
||||||
|
<Menu.Item active={autoPlay} onClick={() => handleAutoPlay(autoPlay)}><Icon name="level up" /><span className="desktop-only">Autoplay</span></Menu.Item>
|
||||||
|
<MediaPlayer call={currentCall} onEnded={callEnded} onPlayPause={handlePlayPause} />
|
||||||
|
<Menu.Menu position="right" className="desktop-only">
|
||||||
|
<Menu.Item><SupportModal /></Menu.Item>
|
||||||
|
<Menu.Item><a href={callDownload}><Icon name="download" />Download</a></Menu.Item>
|
||||||
|
<Menu.Item><a href={callLink}><Icon name="at" />Link</a></Menu.Item>
|
||||||
|
</Menu.Menu>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default EventPlayer;
|
||||||
59
frontend/src/Event/ListEventCalls.js
Normal file
59
frontend/src/Event/ListEventCalls.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import CallItem from "../Call/components/CallItem";
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Table,
|
||||||
|
Ref
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import "../Call/CallPlayer.css";
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
const ListEventCalls = (props) => {
|
||||||
|
const activeCallRef = useRef();
|
||||||
|
|
||||||
|
//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
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeCallRef.current) {
|
||||||
|
activeCallRef.current.scrollIntoView({
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<Table id="calls" unstackable >
|
||||||
|
<Table.Header >
|
||||||
|
<Table.Row>
|
||||||
|
<Table.HeaderCell>Len</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>Talkgroup</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>Time</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell><Icon name='star' /></Table.HeaderCell>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
{props.eventData &&
|
||||||
|
<Table.Body>
|
||||||
|
|
||||||
|
{ props.eventData.calls.map((call, index) => {
|
||||||
|
if (call._id === props.activeCallId) {
|
||||||
|
return (<Ref innerRef={activeCallRef} key={index} ><CallItem activeCall={true} call={call} key={index} onClick={props.playCall}/></Ref>)
|
||||||
|
} else {
|
||||||
|
return <CallItem activeCall={false} call={call} key={index} onClick={props.playCall}/>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</Table.Body>
|
||||||
|
}
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListEventCalls;
|
||||||
63
frontend/src/Event/ListEvents.js
Normal file
63
frontend/src/Event/ListEvents.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Header,
|
||||||
|
Card,
|
||||||
|
Icon,
|
||||||
|
Menu,
|
||||||
|
Divider,
|
||||||
|
Table
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import NavBar from "../Common/NavBar"
|
||||||
|
import { useGetEventsQuery } from "../features/api/apiSlice";
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
const ListEvents = (props) => {
|
||||||
|
|
||||||
|
const dateRange = (start, end) => {
|
||||||
|
if (start.getYear() != end.getYear()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
} else if (start.getMonth() != end.getMonth()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
} else if (start.getDay() != end.getDay()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const {data:events, isSuccess} = useGetEventsQuery();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<NavBar/>
|
||||||
|
<Container >
|
||||||
|
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Events<Icon name='rss' /></Header></Divider>
|
||||||
|
<Table basic="very">
|
||||||
|
<Table.Body>
|
||||||
|
|
||||||
|
{isSuccess&&events.map((event, index) =>
|
||||||
|
<Table.Row key={index}>
|
||||||
|
<Table.Cell><Link to={"/events/"+event._id}><Header as='h3'>{event.title}</Header></Link></Table.Cell>
|
||||||
|
<Table.Cell>{event.description}</Table.Cell>
|
||||||
|
<Table.Cell>{event.numCalls} Calls</Table.Cell>
|
||||||
|
<Table.Cell>{dateRange(new Date(event.startTime),new Date(event.endTime))}</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
)}
|
||||||
|
</Table.Body>
|
||||||
|
</Table>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default ListEvents;
|
||||||
152
frontend/src/Event/ViewEvent.js
Normal file
152
frontend/src/Event/ViewEvent.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import React, { useEffect, useLayoutEffect, useState, useRef } from "react";
|
||||||
|
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import EventPlayer from "./EventPlayer";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
import { useGetEventQuery } from '../features/api/apiSlice'
|
||||||
|
import queryString from '../query-string';
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Label,
|
||||||
|
Menu,
|
||||||
|
Icon,
|
||||||
|
Sidebar,
|
||||||
|
Grid,
|
||||||
|
Statistic,
|
||||||
|
Header
|
||||||
|
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import "../Call/CallPlayer.css";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
function ViewEvent(props) {
|
||||||
|
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const { data: eventData, isSuccess: isEventSuccess } = useGetEventQuery(id);
|
||||||
|
const [autoPlay, setAutoPlay] = useState(true);
|
||||||
|
const [currentCall, setCurrentCall] = useState(false);
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
|
const [sidebarOpened, setSidebarOpened] = useState(false);
|
||||||
|
const [selectCallId, setSelectCallId] = useState(false);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const uri = queryString.parse(useLocation().search);
|
||||||
|
const pathname = useLocation().pathname;
|
||||||
|
|
||||||
|
let currentCallId = false;
|
||||||
|
|
||||||
|
if (currentCall) {
|
||||||
|
currentCallId = currentCall._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handlePusherClick = () => {
|
||||||
|
if (sidebarOpened) setSidebarOpened(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSidebarToggle = () => setSidebarOpened(!sidebarOpened);
|
||||||
|
|
||||||
|
|
||||||
|
const setStateFromUri = async () => {
|
||||||
|
|
||||||
|
// is this just for one call?
|
||||||
|
if (uri.hasOwnProperty('call-id')) {
|
||||||
|
const _id = uri['call-id'];
|
||||||
|
setSelectCallId(_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateRange = (start, end) => {
|
||||||
|
if (start.getYear() != end.getYear()) {
|
||||||
|
return {"part1": start.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString(), "part2": end.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString()};
|
||||||
|
} else if (start.getMonth() != end.getMonth()) {
|
||||||
|
return {"part1": start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString(), "part2": end.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString()};
|
||||||
|
|
||||||
|
} else if (start.getDay() != end.getDay()) {
|
||||||
|
return {"part1": start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString(), "part2": end.toLocaleDateString('en-us', { weekday:"short", day:"numeric"}) + " " + end.toLocaleTimeString()};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return {"part1": start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString(), "part2": end.toLocaleTimeString()};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dates = {part1: "", part2: ""}
|
||||||
|
if (eventData) {
|
||||||
|
dates = dateRange(new Date(eventData.startTime),new Date(eventData.endTime))
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
const dateRange = (start, end) => {
|
||||||
|
if (start.getYear() != end.getYear()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", year:"numeric", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
} else if (start.getMonth() != end.getMonth()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
} else if (start.getDay() != end.getDay()) {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleDateString('en-us', { weekday:"short", day:"numeric"}) + " " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return start.toLocaleDateString('en-us', { weekday:"short", month:"short", day:"numeric"}) + " " + start.toLocaleTimeString() + " - " + end.toLocaleTimeString();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dates = "";
|
||||||
|
if (eventData) {
|
||||||
|
dates = dateRange(new Date(eventData.startTime),new Date(eventData.endTime));
|
||||||
|
}*/
|
||||||
|
return (
|
||||||
|
<div >
|
||||||
|
<Sidebar as={Menu} animation='overlay' inverted vertical visible={sidebarOpened}
|
||||||
|
onClick={handlePusherClick} duration={50} width='thin'>
|
||||||
|
<Menu.Item onClick={handlePusherClick} >
|
||||||
|
<span> </span><Icon name="close" inverted={true} link={true} size='small' /></Menu.Item>
|
||||||
|
<Link to="/"><Menu.Item link>Home</Menu.Item></Link>
|
||||||
|
<Link to="/systems"><Menu.Item link>Systems</Menu.Item></Link>
|
||||||
|
<Link to="/events"><Menu.Item link>Events</Menu.Item></Link>
|
||||||
|
<Link to="/about"><Menu.Item link>About</Menu.Item></Link>
|
||||||
|
</Sidebar>
|
||||||
|
<Menu fixed="top">
|
||||||
|
<Menu.Item onClick={handleSidebarToggle}>
|
||||||
|
<Icon name='sidebar' />
|
||||||
|
</Menu.Item>
|
||||||
|
<Container className="desktop-only" fluid textAlign='left' style={{ fontSize: '1.5rem', paddingLeft: '0.5em', paddingTop: '.5em' }}>
|
||||||
|
{eventData && eventData.title}
|
||||||
|
</Container>
|
||||||
|
</Menu>
|
||||||
|
<Grid style={{ paddingTop: '4em' }}>
|
||||||
|
<Grid.Column mobile={0} tablet={0} computer={1} ></Grid.Column>
|
||||||
|
<Grid.Column mobile={16} tablet={8} computer={4}>
|
||||||
|
<Container text >
|
||||||
|
{eventData && eventData.description}
|
||||||
|
</Container>
|
||||||
|
</Grid.Column>
|
||||||
|
<Grid.Column mobile={8} tablet={8} computer={4}>
|
||||||
|
<Header as="h2">{dates.part1} → <br/>{dates.part2}</Header>
|
||||||
|
</Grid.Column>
|
||||||
|
<Grid.Column mobile={8} tablet={8} computer={2}>
|
||||||
|
|
||||||
|
<Statistic>
|
||||||
|
<Statistic.Value>{eventData.numCalls}</Statistic.Value>
|
||||||
|
<Statistic.Label>Calls</Statistic.Label>
|
||||||
|
</Statistic>
|
||||||
|
</Grid.Column>
|
||||||
|
|
||||||
|
<Grid.Column mobile={0} tablet={0} computer={5}>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid>
|
||||||
|
<EventPlayer eventData={eventData} selectCallId={selectCallId} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default ViewEvent;
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
Divider
|
Divider
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
import NavBar from "../Common/NavBar"
|
||||||
import { selectAllSystems, selectActiveSystems, useGetSystemsQuery } from "../features/api/apiSlice";
|
import { selectAllSystems, selectActiveSystems, useGetSystemsQuery } from "../features/api/apiSlice";
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
@@ -21,10 +21,7 @@ const ListSystems = (props) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Menu fixed="top">
|
<NavBar/>
|
||||||
<Link to="/"><Menu.Item link><Icon name='arrow left' /> Home</Menu.Item></Link>
|
|
||||||
<Link to="/about"><Menu.Item link>About</Menu.Item></Link>
|
|
||||||
</Menu>
|
|
||||||
<Container >
|
<Container >
|
||||||
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
||||||
<Card.Group itemsPerRow={4} stackable={true}>
|
<Card.Group itemsPerRow={4} stackable={true}>
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ export const apiSlice = createApi({
|
|||||||
// The URL for the request is '/fakeApi/posts'
|
// The URL for the request is '/fakeApi/posts'
|
||||||
query: () => ({ url: '/systems' })
|
query: () => ({ url: '/systems' })
|
||||||
}),
|
}),
|
||||||
|
getEvents: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: () => ({ url: '/events' })
|
||||||
|
}),
|
||||||
|
getEvent: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: (eventId) => ({ url: `/events/${eventId}` })
|
||||||
|
}),
|
||||||
getTalkgroups: builder.query({
|
getTalkgroups: builder.query({
|
||||||
// The URL for the request is '/fakeApi/posts'
|
// The URL for the request is '/fakeApi/posts'
|
||||||
query: (shortName) => ({ url: `/${shortName}/talkgroups` })
|
query: (shortName) => ({ url: `/${shortName}/talkgroups` })
|
||||||
@@ -52,4 +60,4 @@ export const selectActiveSystems = createSelector(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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, useAddNewEventMutation } = apiSlice
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetEventsQuery, useGetEventQuery, useAddNewEventMutation } = apiSlice
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import Calls from "./Call/Calls"
|
|||||||
|
|
||||||
import AboutComponent from "./About/AboutComponent"
|
import AboutComponent from "./About/AboutComponent"
|
||||||
|
|
||||||
|
// Event
|
||||||
|
|
||||||
|
import ListEvents from "./Event/ListEvents"
|
||||||
|
import ViewEvent from "./Event/ViewEvent"
|
||||||
|
|
||||||
const store = configureStore(/* provide initial state if any */)
|
const store = configureStore(/* provide initial state if any */)
|
||||||
|
|
||||||
@@ -33,6 +37,8 @@ root.render(
|
|||||||
<Route exact path="/" element={<Main/>} />
|
<Route exact path="/" element={<Main/>} />
|
||||||
<Route exact path="/systems" element={<ListSystems/>} />
|
<Route exact path="/systems" element={<ListSystems/>} />
|
||||||
<Route exact path="/system/:shortName" element={<Calls/>} />
|
<Route exact path="/system/:shortName" element={<Calls/>} />
|
||||||
|
<Route exact path="/events" element={<ListEvents/>} />
|
||||||
|
<Route exact path="/events/:id" element={<ViewEvent/>} />
|
||||||
<Route exact path="/about" element={<AboutComponent/>} />
|
<Route exact path="/about" element={<AboutComponent/>} />
|
||||||
|
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
Reference in New Issue
Block a user