mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-11 00:07:30 -05:00
@@ -18,6 +18,7 @@ exports.getUserSystems = async function (_id) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -31,6 +32,7 @@ exports.getUserSystems = async function (_id) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
|
||||
@@ -10,6 +10,7 @@ var systemSchema = mongoose.Schema({
|
||||
city: String,
|
||||
state: String,
|
||||
description: String,
|
||||
status: String,
|
||||
showScreenName: Boolean,
|
||||
planType: {type: Number, default: 0},
|
||||
userId: mongoose.Schema.Types.ObjectId,
|
||||
|
||||
@@ -61,7 +61,7 @@ exports.listUserSystems = async function (req,res,next) {
|
||||
for (let index = 0; index < users.length; index++) {
|
||||
let user = users[index].toObject();
|
||||
const userId = new mongoose.Types.ObjectId(user._id);
|
||||
const systems = await System.find({ userId: userId }, "name shortName description systemType city state county country lastActive");
|
||||
const systems = await System.find({ userId: userId }, "name shortName description status systemType city state county country lastActive");
|
||||
|
||||
user["systems"] = systems;
|
||||
list.push(user);
|
||||
@@ -89,6 +89,7 @@ exports.listAllSystems = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -105,6 +106,7 @@ exports.listAllSystems = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -153,6 +155,7 @@ exports.listSystems = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -167,6 +170,7 @@ exports.listSystems = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -359,6 +363,7 @@ exports.updateSystem = async function (req, res, next) {
|
||||
res.locals.system.county = res.locals.county;
|
||||
res.locals.system.country = res.locals.country;
|
||||
res.locals.system.description = res.locals.description;
|
||||
res.locals.system.status = res.locals.status;
|
||||
res.locals.system.systemType = res.locals.systemType;
|
||||
res.locals.system.showScreenName = res.locals.showScreenName;
|
||||
res.locals.system.allowContact = res.locals.allowContact;
|
||||
@@ -377,6 +382,7 @@ exports.updateSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -391,6 +397,7 @@ exports.updateSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -428,7 +435,7 @@ exports.validateSystem = async function (req, res, next) {
|
||||
res.locals.name = req.body.name.replace(/[^\w\s\.\,\-\_]/gi, '');
|
||||
|
||||
if (!req.body.description || (req.body.description.length < 2)) {
|
||||
console.error("ERROR: Validate System - req.body.description");
|
||||
console.error("ERROR: Validate System - req.body.description: " + req.body.description);
|
||||
res.status(500)
|
||||
res.json({
|
||||
success: false,
|
||||
@@ -436,7 +443,9 @@ exports.validateSystem = async function (req, res, next) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.locals.description = req.body.description.replace(/[^\w\s\.\,\-\_]/gi, '');
|
||||
res.locals.description = req.body.description.replace(/[^\w\s\.\,\-\_\(\)\'\"\!\?\*\&\^\%\$\#\@\r\n\t\`\~\[\]\/\\]/gi, '');
|
||||
|
||||
res.locals.status = req.body.status.replace(/[^\w\s\.\,\-\_\(\)\'\"\!\?\*\&\^\%\$\#\@\r\n\t\`\~\[\]\/\\]/gi, '');
|
||||
|
||||
if (!req.body.shortName || (req.body.shortName.length < 2)) {
|
||||
console.error("ERROR: Validate System - req.body.shortName");
|
||||
@@ -546,6 +555,7 @@ exports.createSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -558,6 +568,7 @@ exports.createSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -584,6 +595,7 @@ exports.createSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
@@ -598,6 +610,7 @@ exports.createSystem = async function (req, res, next) {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state,
|
||||
|
||||
@@ -11,6 +11,7 @@ var systemSchema = mongoose.Schema({
|
||||
city: String,
|
||||
state: String,
|
||||
description: String,
|
||||
status: String,
|
||||
showScreenName: Boolean,
|
||||
allowContact: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -236,7 +236,6 @@ const System = () => {
|
||||
setGroupOrder(newOrder);
|
||||
}
|
||||
}, [groupsData])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSystemsSuccess && systemsData.stats[shortName]) {
|
||||
processStatistics(systemsData.stats[shortName])
|
||||
@@ -328,7 +327,12 @@ const System = () => {
|
||||
<Header size="small">Description</Header>
|
||||
{system.description}
|
||||
</Segment>
|
||||
|
||||
{system.status.length>0 &&
|
||||
<Segment>
|
||||
<Header size="small">Status Message</Header>
|
||||
{system.status}
|
||||
</Segment>
|
||||
}
|
||||
<Segment clearing={true}>
|
||||
<Button
|
||||
color="red"
|
||||
|
||||
@@ -18,6 +18,8 @@ const SystemForm = (props) => {
|
||||
const [name, setName] = useState("");
|
||||
const [shortName, setShortName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [status, setStatus] = useState("");
|
||||
const [showStatus, setShowStatus] = useState(false);
|
||||
const [systemType, setSystemType] = useState("state");
|
||||
const [systemState, setSystemState] = useState("");
|
||||
const [city, setCity] = useState("");
|
||||
@@ -29,6 +31,7 @@ const SystemForm = (props) => {
|
||||
const [nameError, setNameError] = useState(false);
|
||||
const [shortNameError, setShortNameError] = useState(false);
|
||||
const [descriptionError, setDescriptionError] = useState(false);
|
||||
const [statusError, setStatusError] = useState(false);
|
||||
const [systemStateError, setSystemStateError] = useState(false);
|
||||
const [cityError, setCityError] = useState(false);
|
||||
const [countyError, setCountyError] = useState(false);
|
||||
@@ -40,6 +43,8 @@ const SystemForm = (props) => {
|
||||
setName(props.system.name);
|
||||
setShortName(props.system.shortName);
|
||||
setDescription(props.system.description);
|
||||
setStatus(props.system.status);
|
||||
setShowStatus(props.system.status.length > 0);
|
||||
setSystemType(props.system.systemType);
|
||||
setCity(props.system.city);
|
||||
setSystemState(props.system.state)
|
||||
@@ -82,6 +87,14 @@ const SystemForm = (props) => {
|
||||
setDescriptionError(false);
|
||||
}
|
||||
|
||||
if (showStatus && status.length < 4) {
|
||||
setStatusError(true);
|
||||
messages.push("If showing a aStatus message it must be at least 4 characters");
|
||||
error = true;
|
||||
} else {
|
||||
setStatusError(false);
|
||||
}
|
||||
|
||||
if (
|
||||
systemType === "state" ||
|
||||
systemType === "city" ||
|
||||
@@ -130,12 +143,17 @@ const SystemForm = (props) => {
|
||||
return error;
|
||||
}
|
||||
|
||||
const handleShowStatusCheckboxChange = () => {
|
||||
if (showStatus) {
|
||||
setStatus("");
|
||||
}
|
||||
setShowStatus((current) => !current);
|
||||
}
|
||||
|
||||
const handleAllowContactCheckboxChange = () => {
|
||||
setAllowContact((current) => !current);
|
||||
}
|
||||
|
||||
|
||||
const handleScreenNameCheckboxChange = () => {
|
||||
setShowScreenName((current) => !current);
|
||||
}
|
||||
@@ -152,6 +170,7 @@ const SystemForm = (props) => {
|
||||
name,
|
||||
shortName,
|
||||
description,
|
||||
status,
|
||||
systemType,
|
||||
city,
|
||||
state: systemState,
|
||||
@@ -303,7 +322,24 @@ const SystemForm = (props) => {
|
||||
rows={2}
|
||||
placeholder="Description of system..."
|
||||
/>
|
||||
|
||||
<Divider horizontal> System Status </Divider>
|
||||
<Form.Field>
|
||||
<Checkbox
|
||||
onChange={handleShowStatusCheckboxChange}
|
||||
checked={showStatus}
|
||||
label="Show Status Message" />
|
||||
</Form.Field>
|
||||
<Form.TextArea
|
||||
label="Status Message"
|
||||
name="status"
|
||||
onChange={e => setStatus(e.target.value)}
|
||||
error={statusError}
|
||||
value={status}
|
||||
disabled={!showStatus}
|
||||
rows={1}
|
||||
placeholder="System status..."
|
||||
/>
|
||||
Only needed if things are not working. Provide notification on maintenance or is you know if some channels are not available.
|
||||
<Divider horizontal><Icon name="marker" />Location</Divider>
|
||||
<Form.Group inline>
|
||||
<label>System Type</label>
|
||||
|
||||
@@ -127,6 +127,7 @@ exports.get_systems = async function (req, res) {
|
||||
lastActive: results[result].lastActive,
|
||||
callAvg: results[result].callAvg,
|
||||
description: results[result].description,
|
||||
status: results[result].status,
|
||||
allowContact: results[result].allowContact,
|
||||
clientCount: clientCount
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ var systemSchema = mongoose.Schema({
|
||||
city: String,
|
||||
state: String,
|
||||
description: String,
|
||||
status: String,
|
||||
showScreenName: Boolean,
|
||||
allowContact: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useParams } from 'react-router-dom';
|
||||
import MediaPlayer from "./components/MediaPlayer";
|
||||
import SupportModal from "./components/SupportModal";
|
||||
import SupportModal from "../Common/SupportModal";
|
||||
import CallInfo from "./components/CallInfo";
|
||||
import ListCalls from "./components/ListCalls";
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
@@ -14,13 +14,18 @@ import {
|
||||
Sticky,
|
||||
Menu,
|
||||
Icon,
|
||||
Sidebar
|
||||
Sidebar,
|
||||
Message,
|
||||
MessageHeader,
|
||||
Button,
|
||||
ButtonContent
|
||||
} from "semantic-ui-react";
|
||||
import "./CallPlayer.css";
|
||||
import queryString from '../query-string';
|
||||
import io from 'socket.io-client';
|
||||
import { useCallLink } from "./components/CallLinks";
|
||||
import "./CallPlayer.css";
|
||||
import { set } from "date-fns";
|
||||
|
||||
|
||||
const socket = io(process.env.REACT_APP_BACKEND_SERVER);
|
||||
@@ -30,6 +35,7 @@ const socket = io(process.env.REACT_APP_BACKEND_SERVER);
|
||||
function CallPlayer(props) {
|
||||
|
||||
const { shortName } = useParams();
|
||||
const system = props.system;
|
||||
const selectCallId = props.selectCallId;
|
||||
const callsData = props.callsData;
|
||||
const handleNewer = props.handleNewer;
|
||||
@@ -45,6 +51,7 @@ function CallPlayer(props) {
|
||||
|
||||
const backgroundAutoplay = useSelector((state) => state.callPlayer.backgroundAutoplay);
|
||||
const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName);
|
||||
const [statusVisible, setStatusVisible] = useState(false);
|
||||
const [autoPlay, setAutoPlay] = useState(true);
|
||||
const [currentCall, setCurrentCall] = useState(false);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
@@ -160,15 +167,31 @@ useEffect(() => {
|
||||
}
|
||||
}, [selectCallId])
|
||||
|
||||
useEffect(() => {
|
||||
if (system.status && system.status.length > 0) {
|
||||
setStatusVisible(true);
|
||||
}
|
||||
}, [system])
|
||||
|
||||
const handleStatusDismiss = () => {
|
||||
setStatusVisible(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={positionRef}>
|
||||
<Container className="main" >
|
||||
{statusVisible &&
|
||||
<Sticky offset={60} context={positionRef}>
|
||||
<Message floating onDismiss={handleStatusDismiss} warning> <MessageHeader>System Status</MessageHeader>{system.status}</Message>
|
||||
</Sticky>
|
||||
}
|
||||
<Sidebar.Pushable>
|
||||
<Sidebar.Pusher
|
||||
style={{ minHeight: '100vh' }}
|
||||
>
|
||||
|
||||
<div ref={loadNewerRef} />
|
||||
|
||||
<ListCalls callsData={callsData} activeCallId={currentCallId} talkgroups={talkgroupsData ? talkgroupsData.talkgroups : false} playCall={playCall} />
|
||||
<div ref={loadOlderRef} style={{ height: 50 }} />
|
||||
</Sidebar.Pusher>
|
||||
@@ -184,7 +207,12 @@ useEffect(() => {
|
||||
<Menu.Item active={autoPlay} onClick={() => handleAutoPlay(autoPlay)}><Icon name="level up" /><span className="desktop-only">Autoplay</span></Menu.Item>
|
||||
<MediaPlayer call={currentCall} playSilence={silenceCount} onEnded={callEnded} onPlayPause={handlePlayPause} />
|
||||
<Menu.Menu position="right" className="desktop-only">
|
||||
<Menu.Item><SupportModal /></Menu.Item>
|
||||
<Menu.Item><SupportModal trigger={<Button color='grey' animated='fade'>
|
||||
<ButtonContent visible color="red">
|
||||
<Icon name='heart' /> Donate
|
||||
</ButtonContent>
|
||||
<ButtonContent hidden>Thank You</ButtonContent>
|
||||
</Button>} /></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>
|
||||
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
Label,
|
||||
Menu,
|
||||
Icon,
|
||||
Sidebar
|
||||
Sidebar,
|
||||
Message,
|
||||
Sticky
|
||||
} from "semantic-ui-react";
|
||||
import "./CallPlayer.css";
|
||||
import queryString from '../query-string';
|
||||
@@ -447,9 +449,10 @@ function Calls(props) {
|
||||
</Menu.Item>
|
||||
</Menu.Menu>
|
||||
</Menu>
|
||||
|
||||
{activityVisible
|
||||
? <Activity navigate={handleActivityNavigate} />
|
||||
: <CallPlayer callsData={callsData} selectCallId={selectCallId} initialCallId={initialCallId} handleNewer={handleNewer} handleOlder={handleOlder} />
|
||||
: <CallPlayer system={system} callsData={callsData} selectCallId={selectCallId} initialCallId={initialCallId} handleNewer={handleNewer} handleOlder={handleOlder} />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,9 +2,11 @@ import {useState} from 'react';
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
ButtonContent,
|
||||
ButtonGroup,
|
||||
Divider,
|
||||
Icon
|
||||
} from "semantic-ui-react";
|
||||
import "./FilterModal.css";
|
||||
|
||||
|
||||
function SupportModal(props) {
|
||||
@@ -14,16 +16,18 @@ function SupportModal(props) {
|
||||
const handleClose = () => props.onClose();
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={() => setOpen(false)} onOpen={() => setOpen(true)} trigger={<div><Icon name="coffee" />Support OpenMHz</div>} size='tiny' >
|
||||
<Modal open={open} onClose={() => setOpen(false)} onOpen={() => setOpen(true)} trigger={props.trigger} size='tiny' >
|
||||
<Modal.Header>Support OpenMHz</Modal.Header>
|
||||
<Modal.Content image>
|
||||
<Icon size='massive' name="coffee" />
|
||||
<Modal.Description>
|
||||
<p>If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.</p>
|
||||
<p>No worries if not, I am not having problems keeping it going.</p>
|
||||
<a href="https://github.com/sponsors/robotastic"><Button>
|
||||
Support
|
||||
</Button></a>
|
||||
|
||||
<Divider horizontal>Donate</Divider>
|
||||
<a href="https://github.com/sponsors/robotastic"><Button ><Icon name="github"/>GitHub Sponsors</Button></a>
|
||||
|
||||
<a href="https://patreon.com/OpenMHz"><Button ><Icon name="patreon"/>Patreon </Button></a>
|
||||
|
||||
</Modal.Description>
|
||||
|
||||
</Modal.Content>
|
||||
@@ -1,21 +1,21 @@
|
||||
import React, { useEffect, useLayoutEffect, useState, useRef } from "react";
|
||||
import { Link, useLocation, useParams, useNavigate } from 'react-router-dom';
|
||||
import { skipToken } from '@reduxjs/toolkit/query'
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import MediaPlayer from "../Call/components/MediaPlayer";
|
||||
|
||||
import SupportModal from "../Call/components/SupportModal";
|
||||
import SupportModal from "../Common/SupportModal";
|
||||
import EventCallInfo from "./EventCallInfo";
|
||||
import ListEventCalls from "./ListEventCalls";
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
import {
|
||||
Container,
|
||||
Label,
|
||||
Rail,
|
||||
Sticky,
|
||||
Menu,
|
||||
Icon,
|
||||
Sidebar
|
||||
Sidebar,
|
||||
Button,
|
||||
ButtonContent
|
||||
} from "semantic-ui-react";
|
||||
|
||||
|
||||
@@ -118,7 +118,12 @@ function EventPlayer(props) {
|
||||
<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><SupportModal trigger={<Button color='grey' animated='fade'>
|
||||
<ButtonContent visible color="red">
|
||||
<Icon name='heart' /> Donate
|
||||
</ButtonContent>
|
||||
<ButtonContent hidden>Thank You</ButtonContent>
|
||||
</Button>} /></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>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import SystemCard from "../System/SystemCard";
|
||||
import SupportModal from "../Common/SupportModal";
|
||||
import "./Main.css";
|
||||
import { createMedia } from "@artsy/fresnel";
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
ButtonContent,
|
||||
Container,
|
||||
Divider,
|
||||
Grid,
|
||||
@@ -40,7 +43,7 @@ const { Media } = AppMedia;
|
||||
* such things.
|
||||
*/
|
||||
const HomepageHeading = ({ mobile }) => (
|
||||
<Container text style={{paddingBottom: '0px'}} >
|
||||
<Container text style={{ paddingBottom: '0px' }} >
|
||||
<Header
|
||||
as='h1'
|
||||
content='Share the Air'
|
||||
@@ -83,52 +86,53 @@ General flow:
|
||||
- componentDidMount(): it will call fetchSystem() this will perform an HTTP request if Systems does not exist, and then add System to Props
|
||||
*/
|
||||
|
||||
const DesktopContainer = (props) => {
|
||||
const DesktopContainer = (props) => {
|
||||
const [fixed, setFixed] = useState(false);
|
||||
const hideFixedMenu = () => setFixed(false)
|
||||
const hideFixedMenu = () => setFixed(false)
|
||||
const showFixedMenu = () => setFixed(true)
|
||||
|
||||
const { children } = props
|
||||
|
||||
return (
|
||||
<Media greaterThanOrEqual="tablet">
|
||||
<div className="relative">
|
||||
const { children } = props
|
||||
|
||||
return (
|
||||
<Media greaterThanOrEqual="tablet">
|
||||
<div className="relative">
|
||||
<div className="static-gradient blue absolute z-0" >
|
||||
<div className="static-gradient-bg absolute z-5"></div>
|
||||
</div>
|
||||
<Segment
|
||||
textAlign='center'
|
||||
style={{ padding: '1em 0em',
|
||||
<Segment
|
||||
textAlign='center'
|
||||
style={{
|
||||
padding: '1em 0em',
|
||||
borderBottom: '0px',
|
||||
boxShadow: 'none',
|
||||
height: '400px'
|
||||
}}
|
||||
vertical
|
||||
}}
|
||||
vertical
|
||||
>
|
||||
|
||||
<Menu
|
||||
fixed={fixed ? 'top' : null}
|
||||
inverted={true}
|
||||
pointing={false}
|
||||
secondary={true}
|
||||
size='huge'
|
||||
style={{ marginRight: '0px' }}
|
||||
>
|
||||
<Container>
|
||||
<Menu.Item ><Header as='h3' inverted>{process.env.REACT_APP_SITE_NAME}</Header></Menu.Item>
|
||||
<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>
|
||||
</Container>
|
||||
</Menu>
|
||||
<HomepageHeading />
|
||||
</Segment>
|
||||
</div>
|
||||
|
||||
<Menu
|
||||
fixed={fixed ? 'top' : null}
|
||||
inverted={true}
|
||||
pointing={false}
|
||||
secondary={true}
|
||||
size='huge'
|
||||
style={{ marginRight: '0px'}}
|
||||
>
|
||||
<Container>
|
||||
<Menu.Item ><Header as='h3' inverted>{process.env.REACT_APP_SITE_NAME}</Header></Menu.Item>
|
||||
<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>
|
||||
</Container>
|
||||
</Menu>
|
||||
<HomepageHeading />
|
||||
</Segment>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</Media>
|
||||
)
|
||||
}
|
||||
{children}
|
||||
</Media>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
DesktopContainer.propTypes = {
|
||||
@@ -146,55 +150,57 @@ const MobileContainer = (props) => {
|
||||
const handleToggle = () => setSidebarOpened(!sidebarOpened)
|
||||
|
||||
|
||||
const { children } = props
|
||||
const { children } = props
|
||||
|
||||
return (
|
||||
<Media lessThan="tablet">
|
||||
<Sidebar.Pushable>
|
||||
<Sidebar as={Menu} animation='uncover' inverted vertical visible={sidebarOpened} id="menu-bar">
|
||||
<Menu.Item active>
|
||||
Home
|
||||
</Menu.Item>
|
||||
<Menu.Item ><Link to="/systems">Systems</Link></Menu.Item>
|
||||
<Menu.Item ><Link to="/about">About</Link></Menu.Item>
|
||||
</Sidebar>
|
||||
return (
|
||||
<Media lessThan="tablet">
|
||||
<Sidebar.Pushable>
|
||||
<Sidebar as={Menu} animation='uncover' inverted vertical visible={sidebarOpened} id="menu-bar">
|
||||
<Menu.Item active>
|
||||
Home
|
||||
</Menu.Item>
|
||||
<Menu.Item ><Link to="/systems">Systems</Link></Menu.Item>
|
||||
<Menu.Item ><Link to="/about">About</Link></Menu.Item>
|
||||
</Sidebar>
|
||||
|
||||
<Sidebar.Pusher
|
||||
dimmed={sidebarOpened}
|
||||
onClick={handlePusherClick}
|
||||
style={{ minHeight: '100vh' }}
|
||||
>
|
||||
<Sidebar.Pusher
|
||||
dimmed={sidebarOpened}
|
||||
onClick={handlePusherClick}
|
||||
style={{ minHeight: '100vh' }}
|
||||
>
|
||||
<div className='relative'>
|
||||
<div className="static-gradient blue absolute z-0">
|
||||
<div className="static-gradient-bg absolute"></div>
|
||||
</div>
|
||||
<div className="static-gradient blue absolute z-0">
|
||||
<div className="static-gradient-bg absolute"></div>
|
||||
</div>
|
||||
<Segment
|
||||
textAlign='center'
|
||||
style={{ minHeight: 350, padding: '1em 0em',
|
||||
borderBottom: '0px',
|
||||
boxShadow: 'none',
|
||||
height: '300px'}}
|
||||
style={{
|
||||
minHeight: 350, padding: '1em 0em',
|
||||
borderBottom: '0px',
|
||||
boxShadow: 'none',
|
||||
height: '300px'
|
||||
}}
|
||||
vertical
|
||||
>
|
||||
|
||||
<Container>
|
||||
<Container>
|
||||
|
||||
<Menu inverted secondary size='large'>
|
||||
<Menu.Item onClick={handleToggle}>
|
||||
<Icon name='sidebar' />
|
||||
</Menu.Item>
|
||||
<Menu.Item header>{process.env.REACT_APP_SITE_NAME}</Menu.Item>
|
||||
</Menu>
|
||||
</Container>
|
||||
<Menu inverted secondary size='large'>
|
||||
<Menu.Item onClick={handleToggle}>
|
||||
<Icon name='sidebar' />
|
||||
</Menu.Item>
|
||||
<Menu.Item header>{process.env.REACT_APP_SITE_NAME}</Menu.Item>
|
||||
</Menu>
|
||||
</Container>
|
||||
<HomepageHeading mobile />
|
||||
</Segment>
|
||||
</div>
|
||||
{children}
|
||||
</Sidebar.Pusher>
|
||||
</Sidebar.Pushable>
|
||||
</Media>
|
||||
)
|
||||
}
|
||||
{children}
|
||||
</Sidebar.Pusher>
|
||||
</Sidebar.Pushable>
|
||||
</Media>
|
||||
)
|
||||
}
|
||||
/*
|
||||
MobileContainer.propTypes = {
|
||||
children: PropTypes.node,
|
||||
@@ -213,16 +219,16 @@ const Main = (props) => {
|
||||
const [visible, setVisible] = useState(true);
|
||||
const [currentSystem, setCurrentSystem] = useState(0);
|
||||
const navigate = useNavigate();
|
||||
const {data:systems, isSuccess} = useGetSystemsQuery(); //= selectAllSystems();
|
||||
const { data: systems, isSuccess } = useGetSystemsQuery(); //= selectAllSystems();
|
||||
|
||||
function useInterval(callback, delay) {
|
||||
const savedCallback = useRef();
|
||||
|
||||
|
||||
// Remember the latest callback.
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
|
||||
// Set up the interval.
|
||||
useEffect(() => {
|
||||
function tick() {
|
||||
@@ -235,133 +241,159 @@ const Main = (props) => {
|
||||
}, [delay]);
|
||||
}
|
||||
|
||||
const advanceSystem = () => {
|
||||
if (isSuccess) {
|
||||
let nextSystem = currentSystem + 1;
|
||||
if (nextSystem > systems.systems.length) {
|
||||
nextSystem = 0;
|
||||
}
|
||||
setCurrentSystem(nextSystem);
|
||||
setVisible(!visible);
|
||||
const advanceSystem = () => {
|
||||
if (isSuccess) {
|
||||
let nextSystem = currentSystem + 1;
|
||||
if (nextSystem > systems.systems.length) {
|
||||
nextSystem = 0;
|
||||
}
|
||||
//this.setState({ visible: !this.state.visible, currentSystem });
|
||||
setCurrentSystem(nextSystem);
|
||||
setVisible(!visible);
|
||||
}
|
||||
|
||||
useInterval( ()=>{ advanceSystem()}, 3000)
|
||||
//this.setState({ visible: !this.state.visible, currentSystem });
|
||||
}
|
||||
|
||||
useInterval(() => { advanceSystem() }, 3000)
|
||||
|
||||
|
||||
|
||||
//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
|
||||
//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
|
||||
|
||||
let system = false;
|
||||
if (isSuccess) {
|
||||
system = systems.systems[currentSystem];
|
||||
system = systems.systems[currentSystem];
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<style>{mediaStyles}</style>
|
||||
<ResponsiveContainer>
|
||||
<div style={{ top: '-120px', position: 'relative' }}>
|
||||
<Segment style={{ padding: ' 0em', height: '350px', backgroudColor: '#FFF'}} vertical>
|
||||
<Grid columns='equal' stackable textAlign='center' style={{ height: '350px', marginRight: '0px' }}>
|
||||
<Grid.Row textAlign='center'>
|
||||
<Grid.Column style={{ paddingBottom: '4em', paddingTop: '2em', maxWidth: 450 }}>
|
||||
{ system && (<Transition visible={visible} animation='pulse' duration={500}>
|
||||
<>
|
||||
<style>{mediaStyles}</style>
|
||||
<ResponsiveContainer>
|
||||
<div style={{ top: '-120px', position: 'relative' }}>
|
||||
<Segment style={{ padding: ' 0em', height: '350px', backgroudColor: '#FFF' }} vertical basic>
|
||||
<Grid columns='equal' stackable textAlign='center' style={{ height: '350px', marginRight: '0px' }}>
|
||||
<Grid.Row textAlign='center'>
|
||||
<Grid.Column style={{ paddingBottom: '4em', paddingTop: '2em', maxWidth: 450 }}>
|
||||
{system && (<Transition visible={visible} animation='pulse' duration={500}>
|
||||
|
||||
<SystemCard keepShort={true} system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)}/>
|
||||
<SystemCard keepShort={true} system={system} key={system.shortName} onClick={(e) => navigate("/system/" + system.shortName)} />
|
||||
|
||||
</Transition>)}
|
||||
</Grid.Column>
|
||||
<Grid.Column style={{ paddingBottom: '0em', paddingTop: '6em', maxWidth: 450 }}>
|
||||
<Link to="/systems">
|
||||
<Button primary size='huge' id="listen" animated onClick={(e) => navigate("/system/" + system.shortName)}>
|
||||
<Button.Content visible><Icon name='headphones' />
|
||||
<Icon name='right arrow' /></Button.Content>
|
||||
<Button.Content hidden>Listen</Button.Content>
|
||||
</Button>
|
||||
</Link>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Segment>
|
||||
|
||||
<Segment style={{ padding: '0em' }} vertical>
|
||||
<Grid celled='internally' columns='equal' stackable>
|
||||
<Grid.Row textAlign='center'>
|
||||
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
<Icon color='orange' name='list' />All The Calls
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>Every call, on every talkgroup is recorded</p>
|
||||
</Grid.Column>
|
||||
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
<Icon color='orange' name='checked calendar' />
|
||||
Go back in time
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Missed a call? Not a problem, everything is archived!
|
||||
</p>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Segment>
|
||||
<Segment style={{ padding: '8em 0em' }} vertical>
|
||||
<Container text>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
Don't just scan, hear it all!
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Most large cities use trunked radio systems to get the most use out of their assigned radio spectrum. With trunked systems, the transmission are constantly hopping to different frequencies. Using a cheap Software Defined Radio (SDR), it is possible capture all of the transmission on a system.
|
||||
Instead of scanning to a single frequency, SDR capturea wide swathes of spectrum, covering all of the frequencies a system could use.
|
||||
</p>
|
||||
<Divider
|
||||
as='h4'
|
||||
className='header'
|
||||
horizontal
|
||||
style={{ margin: '3em 0em', textTransform: 'uppercase' }}
|
||||
>
|
||||
Get Involved
|
||||
</Divider>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
Want to record your local radio system?
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Join us! Record a local radio system and share it with the world. If you have a spare computer, it is easy to get started. With a $25 SDR or two, you can capture an entire system.
|
||||
It does take a little bit of work to get things setup, but we are here to help and answer questions.
|
||||
</p>
|
||||
<a href="https://github.com/robotastic/trunk-recorder/blob/master/README.md">
|
||||
<Button size='large'>
|
||||
Learn More
|
||||
</Button>
|
||||
</a>
|
||||
</Container>
|
||||
</Segment>
|
||||
</div>
|
||||
<Segment inverted vertical style={{ padding: '5em 0em' }} id="footer">
|
||||
<Container>
|
||||
<Grid divided inverted stackable>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={6}>
|
||||
|
||||
</Grid.Column>
|
||||
<Grid.Column width={4}>
|
||||
<Header as='h4' inverted>
|
||||
The End <Icon name='rocket' />
|
||||
</Header>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={6}>
|
||||
</Transition>)}
|
||||
</Grid.Column>
|
||||
<Grid.Column style={{ paddingBottom: '0em', paddingTop: '6em', maxWidth: 450 }}>
|
||||
<Link to="/systems">
|
||||
<Button primary size='huge' id="listen" animated onClick={(e) => navigate("/system/" + system.shortName)}>
|
||||
<Button.Content visible><Icon name='headphones' />
|
||||
<Icon name='right arrow' /></Button.Content>
|
||||
<Button.Content hidden>Listen</Button.Content>
|
||||
</Button>
|
||||
</Link>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Segment>
|
||||
<Segment style={{ padding: '0em' }} vertical>
|
||||
<Grid columns='equal' stackable textAlign='center' style={{ height: '150px', marginRight: '0px' }}>
|
||||
<Grid.Row textAlign='center'>
|
||||
<Grid.Column style={{ paddingBottom: '4em', paddingTop: '2em', maxWidth: 450 }}>
|
||||
<ButtonGroup size='large'>
|
||||
<SupportModal trigger={
|
||||
<Button color='red' animated='fade'>
|
||||
<ButtonContent visible>
|
||||
<Icon name='heart' /> Donate
|
||||
</ButtonContent>
|
||||
<ButtonContent hidden>Thank You</ButtonContent>
|
||||
</Button>
|
||||
} />
|
||||
<Button href="https://podcasts.apple.com/us/podcast/openmhz/id1675187518">
|
||||
<Icon name='podcast' /> Events Podcast
|
||||
</Button>
|
||||
<Button href="https://apps.apple.com/us/app/openmhz/id6466666994">
|
||||
<Icon name='app store ios' /> iOS App
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Segment>
|
||||
|
||||
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Segment>
|
||||
|
||||
</ResponsiveContainer>
|
||||
</>);
|
||||
<Segment style={{ padding: '0em' }} vertical>
|
||||
<Grid celled='internally' columns='equal' stackable>
|
||||
<Grid.Row textAlign='center'>
|
||||
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
<Icon color='orange' name='list' />All The Calls
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>Every call, on every talkgroup is recorded</p>
|
||||
</Grid.Column>
|
||||
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
<Icon color='orange' name='checked calendar' />
|
||||
Go back in time
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Missed a call? Not a problem, everything is archived!
|
||||
</p>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Segment>
|
||||
<Segment style={{ padding: '8em 0em' }} vertical>
|
||||
<Container text>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
Don't just scan, hear it all!
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Most large cities use trunked radio systems to get the most use out of their assigned radio spectrum. With trunked systems, the transmission are constantly hopping to different frequencies. Using a cheap Software Defined Radio (SDR), it is possible capture all of the transmission on a system.
|
||||
Instead of scanning to a single frequency, SDR capturea wide swathes of spectrum, covering all of the frequencies a system could use.
|
||||
</p>
|
||||
<Divider
|
||||
as='h4'
|
||||
className='header'
|
||||
horizontal
|
||||
style={{ margin: '3em 0em', textTransform: 'uppercase' }}
|
||||
>
|
||||
Get Involved
|
||||
</Divider>
|
||||
<Header as='h3' style={{ fontSize: '2em' }}>
|
||||
Want to record your local radio system?
|
||||
</Header>
|
||||
<p style={{ fontSize: '1.33em' }}>
|
||||
Join us! Record a local radio system and share it with the world. If you have a spare computer, it is easy to get started. With a $25 SDR or two, you can capture an entire system.
|
||||
It does take a little bit of work to get things setup, but we are here to help and answer questions.
|
||||
</p>
|
||||
<a href="https://github.com/robotastic/trunk-recorder/blob/master/README.md">
|
||||
<Button size='large'>
|
||||
Learn More
|
||||
</Button>
|
||||
</a>
|
||||
</Container>
|
||||
</Segment>
|
||||
</div>
|
||||
<Segment inverted vertical style={{ padding: '5em 0em' }} id="footer">
|
||||
<Container>
|
||||
<Grid divided inverted stackable>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={6}>
|
||||
|
||||
</Grid.Column>
|
||||
<Grid.Column width={4}>
|
||||
<Header as='h4' inverted>
|
||||
The End <Icon name='rocket' />
|
||||
</Header>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={6}>
|
||||
|
||||
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Segment>
|
||||
|
||||
</ResponsiveContainer>
|
||||
</>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,114 +3,141 @@ import {
|
||||
Card,
|
||||
Icon,
|
||||
Label,
|
||||
Grid
|
||||
Grid,
|
||||
Segment,
|
||||
Popup,
|
||||
Button
|
||||
} from "semantic-ui-react";
|
||||
|
||||
|
||||
function SystemCard (props) {
|
||||
function SystemCard(props) {
|
||||
|
||||
const { system, keepShort, onClick, onContactClick } = props;
|
||||
var location = "";
|
||||
var clientCount = "";
|
||||
var callAvg = "";
|
||||
let description = system.description;
|
||||
let statusMessage = "";
|
||||
|
||||
const {system, keepShort, onClick, onContactClick} = props;
|
||||
var location = "";
|
||||
var clientCount = "";
|
||||
var callAvg = "";
|
||||
let description = system.description;
|
||||
|
||||
|
||||
|
||||
const handleContactClick = (event) => {
|
||||
event.stopPropagation();
|
||||
onContactClick(system);
|
||||
if (system.status && system.status.length > 0) {
|
||||
statusMessage = system.status
|
||||
if (statusMessage.length > 25) {
|
||||
statusMessage = statusMessage.substring(0, 25) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
const onCardClick = (event) => {
|
||||
onClick();
|
||||
}
|
||||
|
||||
if (keepShort && (description.length > 100)) {
|
||||
description = description.substring(0, 96) + "...";
|
||||
}
|
||||
if (system.callAvg) {
|
||||
callAvg = Math.round(system.callAvg * 10) / 10 + "/min"
|
||||
}
|
||||
if (system.clientCount === 1) {
|
||||
clientCount = "1 listener";
|
||||
} else if (system.clientCount > 1) {
|
||||
clientCount = system.clientCount + " listeners"
|
||||
}
|
||||
|
||||
let status = (<></>)
|
||||
|
||||
if (system.active) {
|
||||
status = (
|
||||
<Grid >
|
||||
const handleContactClick = (event) => {
|
||||
event.stopPropagation();
|
||||
onContactClick(system);
|
||||
}
|
||||
|
||||
const onCardClick = (event) => {
|
||||
onClick();
|
||||
}
|
||||
|
||||
if (keepShort && (description.length > 100)) {
|
||||
description = description.substring(0, 96) + "...";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (keepShort) {
|
||||
statusMessage = "";
|
||||
}
|
||||
|
||||
if (system.callAvg) {
|
||||
callAvg = Math.round(system.callAvg * 10) / 10 + "/min"
|
||||
}
|
||||
if (system.clientCount === 1) {
|
||||
clientCount = "1 listener";
|
||||
} else if (system.clientCount > 1) {
|
||||
clientCount = system.clientCount + " listeners"
|
||||
}
|
||||
|
||||
let status = (<></>)
|
||||
|
||||
if (system.active) {
|
||||
status = (
|
||||
<Grid >
|
||||
<Grid.Row>
|
||||
<Grid.Column width="8">
|
||||
{clientCount && <div><Icon name='headphones' />{clientCount}</div>}
|
||||
</Grid.Column>
|
||||
<Grid.Column width="8" textAlign='right'>
|
||||
{callAvg && <div><Icon name='file audio outline' />{callAvg}</div>}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
)
|
||||
} else {
|
||||
const lastActive = new Date(system.lastActive);
|
||||
status = (
|
||||
<Grid >
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
<Icon name="calendar times outline"/> Last Active {lastActive.toLocaleDateString()}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
)
|
||||
} else {
|
||||
const lastActive = new Date(system.lastActive);
|
||||
status = (
|
||||
<Grid >
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
<Icon name="calendar times outline" /> Last Active {lastActive.toLocaleDateString()}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
|
||||
if (system) {
|
||||
switch (system.systemType) {
|
||||
case "state":
|
||||
location = system.state;
|
||||
break;
|
||||
case "city":
|
||||
location = system.city + ", " + system.state;
|
||||
break;
|
||||
case "county":
|
||||
location = system.county + ", " + system.state;
|
||||
break;
|
||||
case "international":
|
||||
location = system.country;
|
||||
break;
|
||||
default:
|
||||
location = system.city + ", " + system.state;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (system) {
|
||||
switch (system.systemType) {
|
||||
case "state":
|
||||
location = system.state;
|
||||
break;
|
||||
case "city":
|
||||
location = system.city + ", " + system.state;
|
||||
break;
|
||||
case "county":
|
||||
location = system.county + ", " + system.state;
|
||||
break;
|
||||
case "international":
|
||||
location = system.country;
|
||||
break;
|
||||
default:
|
||||
location = system.city + ", " + system.state;
|
||||
}
|
||||
return (
|
||||
<Card link onClick={onCardClick} raised={true} centered={true} >
|
||||
<Card.Content>
|
||||
<Card.Header >{system.name}</Card.Header>
|
||||
<Card.Meta>{location} </Card.Meta>
|
||||
<Card.Description>
|
||||
<Grid >
|
||||
}
|
||||
return (
|
||||
<Card link onClick={onCardClick} raised={true} centered={true} >
|
||||
<Card.Content>
|
||||
<Card.Header >{system.name}</Card.Header>
|
||||
<Card.Meta>{location} </Card.Meta>
|
||||
<Card.Description>
|
||||
<Grid >
|
||||
<Grid.Row>
|
||||
<Grid.Column >
|
||||
<Grid.Column >
|
||||
{system.screenName && <Label size="mini"><Icon name='user' /> {system.screenName}</Label>}
|
||||
{system.allowContact && <Label size="mini" onClick={handleContactClick}><Icon name='mail' />Message</Label>}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
{description}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
{statusMessage &&
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
{description}
|
||||
<Popup content={system.status} trigger={<Button icon labelPosition='left' size='mini'><Icon name="info"/>{statusMessage}</Button>} />
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Card.Description>
|
||||
</Card.Content>
|
||||
<Card.Content extra>
|
||||
{status}
|
||||
</Card.Content>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
</Grid>
|
||||
</Card.Description>
|
||||
</Card.Content>
|
||||
<Card.Content extra>
|
||||
{status}
|
||||
</Card.Content>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default SystemCard;
|
||||
|
||||
@@ -4,6 +4,7 @@ server {
|
||||
|
||||
server_name ${DOMAIN_NAME} www.${DOMAIN_NAME};
|
||||
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user