Allow time & date to be configured manually when NTP is inactive (#153)

* Allow time to be configured manually when NTP is not active
* Standarize on primary button on the outside of dialog boxes
This commit is contained in:
rjwats
2020-06-16 22:24:15 +01:00
committed by GitHub
parent 003fd8af28
commit 55511e0611
8 changed files with 187 additions and 37 deletions

View File

@ -3,6 +3,7 @@ import { ENDPOINT_ROOT } from './Env';
export const FEATURES_ENDPOINT = ENDPOINT_ROOT + "features";
export const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + "ntpStatus";
export const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "ntpSettings";
export const TIME_ENDPOINT = ENDPOINT_ROOT + "time";
export const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "apSettings";
export const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + "apStatus";
export const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "scanNetworks";

View File

@ -2,7 +2,8 @@ import React, { Component, Fragment } from 'react';
import moment from 'moment';
import { WithTheme, withTheme } from '@material-ui/core/styles';
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core';
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core';
import { Dialog, DialogTitle, DialogContent, DialogActions, Box, TextField } from '@material-ui/core';
import SwapVerticalCircleIcon from '@material-ui/icons/SwapVerticalCircle';
import AccessTimeIcon from '@material-ui/icons/AccessTime';
@ -11,18 +12,116 @@ import UpdateIcon from '@material-ui/icons/Update';
import AvTimerIcon from '@material-ui/icons/AvTimer';
import RefreshIcon from '@material-ui/icons/Refresh';
import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components';
import { RestFormProps, FormButton, HighlightAvatar } from '../components';
import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus';
import { formatIsoDateTime } from './TimeFormat';
import { NTPStatus } from './types';
import { formatIsoDateTime, formatLocalDateTime } from './TimeFormat';
import { NTPStatus, Time } from './types';
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
import { TIME_ENDPOINT } from '../api';
type NTPStatusFormProps = RestFormProps<NTPStatus> & WithTheme;
type NTPStatusFormProps = RestFormProps<NTPStatus> & WithTheme & AuthenticatedContextProps;
class NTPStatusForm extends Component<NTPStatusFormProps> {
interface NTPStatusFormState {
settingTime: boolean;
localTime: string;
processing: boolean;
}
class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
constructor(props: NTPStatusFormProps) {
super(props);
this.state = {
settingTime: false,
localTime: '',
processing: false
};
}
updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ localTime: event.target.value });
}
openSetTime = () => {
this.setState({ localTime: formatLocalDateTime(moment()), settingTime: true, });
}
closeSetTime = () => {
this.setState({ settingTime: false });
}
createAdjustedTime = (): Time => {
const currentLocalTime = moment(this.props.data.time_local);
const newLocalTime = moment(this.state.localTime);
newLocalTime.subtract(currentLocalTime.utcOffset())
newLocalTime.milliseconds(0);
newLocalTime.utc();
return {
time_utc: newLocalTime.format()
}
}
configureTime = () => {
this.setState({ processing: true });
redirectingAuthorizedFetch(TIME_ENDPOINT,
{
method: 'POST',
body: JSON.stringify(this.createAdjustedTime()),
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
if (response.status === 200) {
this.props.enqueueSnackbar("Time set successfully", { variant: 'success' });
this.setState({ processing: false, settingTime: false }, this.props.loadData);
} else {
throw Error("Error setting time, status code: " + response.status);
}
})
.catch(error => {
this.props.enqueueSnackbar(error.message || "Problem setting the time", { variant: 'error' });
this.setState({ processing: false, settingTime: false });
});
}
renderSetTimeDialog() {
return (
<Dialog
open={this.state.settingTime}
onClose={this.closeSetTime}
>
<DialogTitle>Set Time</DialogTitle>
<DialogContent dividers>
<Box mb={2}>Enter local date and time below to set the device's time.</Box>
<TextField
label="Local Time"
type="datetime-local"
value={this.state.localTime}
onChange={this.updateLocalTime}
disabled={this.state.processing}
variant="outlined"
fullWidth
InputLabelProps={{
shrink: true,
}}
/>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={this.closeSetTime} color="secondary">
Cancel
</Button>
<Button startIcon={<AccessTimeIcon />} variant="contained" onClick={this.configureTime} disabled={this.state.processing} color="primary" autoFocus>
Set Time
</Button>
</DialogActions>
</Dialog>
)
}
render() {
const { data, theme } = this.props
const me = this.props.authenticatedContext.me;
return (
<Fragment>
<List>
@ -40,19 +139,10 @@ class NTPStatusForm extends Component<NTPStatusFormProps> {
<ListItem>
<ListItemAvatar>
<Avatar>
<AccessTimeIcon />
<DNSIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Local Time" secondary={formatIsoDateTime(data.time_local)} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<SwapVerticalCircleIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="UTC Time" secondary={formatIsoDateTime(data.time_utc)} />
<ListItemText primary="NTP Server" secondary={data.server} />
</ListItem>
<Divider variant="inset" component="li" />
</Fragment>
@ -60,10 +150,19 @@ class NTPStatusForm extends Component<NTPStatusFormProps> {
<ListItem>
<ListItemAvatar>
<Avatar>
<DNSIcon />
<AccessTimeIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="NTP Server" secondary={data.server} />
<ListItemText primary="Local Time" secondary={formatIsoDateTime(data.time_local)} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<SwapVerticalCircleIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="UTC Time" secondary={formatIsoDateTime(data.time_utc)} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
@ -76,14 +175,24 @@ class NTPStatusForm extends Component<NTPStatusFormProps> {
</ListItem>
<Divider variant="inset" component="li" />
</List>
<FormActions>
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
Refresh
</FormButton>
</FormActions>
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1} padding={1}>
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
Refresh
</FormButton>
</Box>
{me.admin && !isNtpActive(data) && (
<Box flexWrap="none" padding={1} whiteSpace="nowrap">
<Button onClick={this.openSetTime} variant="contained" color="primary" startIcon={<AccessTimeIcon />}>
Set Time
</Button>
</Box>
)}
</Box>
{this.renderSetTimeDialog()}
</Fragment>
);
}
}
export default withTheme(NTPStatusForm);
export default withAuthenticatedContext(withTheme(NTPStatusForm));

View File

@ -1,3 +1,5 @@
import moment from 'moment';
import moment, { Moment } from 'moment';
export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss');
export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm');

View File

@ -17,3 +17,7 @@ export interface NTPSettings {
tz_label: string;
tz_format: string;
}
export interface Time {
time_utc: string;
}

View File

@ -70,12 +70,12 @@ class UserForm extends React.Component<UserFormProps> {
/>
</DialogContent>
<DialogActions>
<FormButton variant="contained" color="primary" type="submit" onClick={this.submit}>
Done
</FormButton>
<FormButton variant="contained" color="secondary" onClick={onCancelEditing}>
Cancel
</FormButton>
<FormButton variant="contained" color="primary" type="submit" onClick={this.submit}>
Done
</FormButton>
</DialogActions>
</Dialog>
</ValidatorForm>

View File

@ -118,12 +118,12 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
Are you sure you want to restart the device?
</DialogContent>
<DialogActions>
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" onClick={this.onRestartConfirmed} disabled={this.state.processing} color="primary" autoFocus>
Restart
</Button>
<Button variant="contained" onClick={this.onRestartRejected} color="secondary">
Cancel
</Button>
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" onClick={this.onRestartConfirmed} disabled={this.state.processing} color="primary" autoFocus>
Restart
</Button>
</DialogActions>
</Dialog>
)
@ -165,12 +165,12 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
Are you sure you want to reset the device to its factory defaults?
</DialogContent>
<DialogActions>
<ErrorButton startIcon={<SettingsBackupRestoreIcon />} variant="contained" onClick={this.onFactoryResetConfirmed} disabled={this.state.processing} autoFocus>
Factory Reset
</ErrorButton>
<Button variant="contained" onClick={this.onFactoryResetRejected} color="secondary">
Cancel
</Button>
<ErrorButton startIcon={<SettingsBackupRestoreIcon />} variant="contained" onClick={this.onFactoryResetConfirmed} disabled={this.state.processing} autoFocus>
Factory Reset
</ErrorButton>
</DialogActions>
</Dialog>
)