@@ -2,7 +2,8 @@ import React, { Component, Fragment } from 'react';
22import moment from 'moment' ;
33
44import { WithTheme , withTheme } from '@material-ui/core/styles' ;
5- import { Avatar , Divider , List , ListItem , ListItemAvatar , ListItemText } from '@material-ui/core' ;
5+ import { Avatar , Divider , List , ListItem , ListItemAvatar , ListItemText , Button } from '@material-ui/core' ;
6+ import { Dialog , DialogTitle , DialogContent , DialogActions , Box , TextField } from '@material-ui/core' ;
67
78import SwapVerticalCircleIcon from '@material-ui/icons/SwapVerticalCircle' ;
89import AccessTimeIcon from '@material-ui/icons/AccessTime' ;
@@ -11,18 +12,116 @@ import UpdateIcon from '@material-ui/icons/Update';
1112import AvTimerIcon from '@material-ui/icons/AvTimer' ;
1213import RefreshIcon from '@material-ui/icons/Refresh' ;
1314
14- import { RestFormProps , FormActions , FormButton , HighlightAvatar } from '../components' ;
15-
15+ import { RestFormProps , FormButton , HighlightAvatar } from '../components' ;
1616import { isNtpActive , ntpStatusHighlight , ntpStatus } from './NTPStatus' ;
17- import { formatIsoDateTime } from './TimeFormat' ;
18- import { NTPStatus } from './types' ;
17+ import { formatIsoDateTime , formatLocalDateTime } from './TimeFormat' ;
18+ import { NTPStatus , Time } from './types' ;
19+ import { redirectingAuthorizedFetch , withAuthenticatedContext , AuthenticatedContextProps } from '../authentication' ;
20+ import { TIME_ENDPOINT } from '../api' ;
21+
22+ type NTPStatusFormProps = RestFormProps < NTPStatus > & WithTheme & AuthenticatedContextProps ;
1923
20- type NTPStatusFormProps = RestFormProps < NTPStatus > & WithTheme ;
24+ interface NTPStatusFormState {
25+ settingTime : boolean ;
26+ localTime : string ;
27+ processing : boolean ;
28+ }
2129
22- class NTPStatusForm extends Component < NTPStatusFormProps > {
30+ class NTPStatusForm extends Component < NTPStatusFormProps , NTPStatusFormState > {
31+
32+ constructor ( props : NTPStatusFormProps ) {
33+ super ( props ) ;
34+ this . state = {
35+ settingTime : false ,
36+ localTime : '' ,
37+ processing : false
38+ } ;
39+ }
40+
41+ updateLocalTime = ( event : React . ChangeEvent < HTMLInputElement > ) => {
42+ this . setState ( { localTime : event . target . value } ) ;
43+ }
44+
45+ openSetTime = ( ) => {
46+ this . setState ( { localTime : formatLocalDateTime ( moment ( ) ) , settingTime : true , } ) ;
47+ }
48+
49+ closeSetTime = ( ) => {
50+ this . setState ( { settingTime : false } ) ;
51+ }
52+
53+ createAdjustedTime = ( ) : Time => {
54+ const currentLocalTime = moment ( this . props . data . time_local ) ;
55+ const newLocalTime = moment ( this . state . localTime ) ;
56+ newLocalTime . subtract ( currentLocalTime . utcOffset ( ) )
57+ newLocalTime . milliseconds ( 0 ) ;
58+ newLocalTime . utc ( ) ;
59+ return {
60+ time_utc : newLocalTime . format ( )
61+ }
62+ }
63+
64+ configureTime = ( ) => {
65+ this . setState ( { processing : true } ) ;
66+ redirectingAuthorizedFetch ( TIME_ENDPOINT ,
67+ {
68+ method : 'POST' ,
69+ body : JSON . stringify ( this . createAdjustedTime ( ) ) ,
70+ headers : {
71+ 'Content-Type' : 'application/json'
72+ }
73+ } )
74+ . then ( response => {
75+ if ( response . status === 200 ) {
76+ this . props . enqueueSnackbar ( "Time set successfully" , { variant : 'success' } ) ;
77+ this . setState ( { processing : false , settingTime : false } , this . props . loadData ) ;
78+ } else {
79+ throw Error ( "Error setting time, status code: " + response . status ) ;
80+ }
81+ } )
82+ . catch ( error => {
83+ this . props . enqueueSnackbar ( error . message || "Problem setting the time" , { variant : 'error' } ) ;
84+ this . setState ( { processing : false , settingTime : false } ) ;
85+ } ) ;
86+ }
87+
88+ renderSetTimeDialog ( ) {
89+ return (
90+ < Dialog
91+ open = { this . state . settingTime }
92+ onClose = { this . closeSetTime }
93+ >
94+ < DialogTitle > Set Time</ DialogTitle >
95+ < DialogContent dividers >
96+ < Box mb = { 2 } > Enter local date and time below to set the device's time.</ Box >
97+ < TextField
98+ label = "Local Time"
99+ type = "datetime-local"
100+ value = { this . state . localTime }
101+ onChange = { this . updateLocalTime }
102+ disabled = { this . state . processing }
103+ variant = "outlined"
104+ fullWidth
105+ InputLabelProps = { {
106+ shrink : true ,
107+ } }
108+ />
109+ </ DialogContent >
110+ < DialogActions >
111+ < Button variant = "contained" onClick = { this . closeSetTime } color = "secondary" >
112+ Cancel
113+ </ Button >
114+ < Button startIcon = { < AccessTimeIcon /> } variant = "contained" onClick = { this . configureTime } disabled = { this . state . processing } color = "primary" autoFocus >
115+ Set Time
116+ </ Button >
117+ </ DialogActions >
118+ </ Dialog >
119+ )
120+ }
23121
24122 render ( ) {
25123 const { data, theme } = this . props
124+ const me = this . props . authenticatedContext . me ;
26125 return (
27126 < Fragment >
28127 < List >
@@ -40,30 +139,30 @@ class NTPStatusForm extends Component<NTPStatusFormProps> {
40139 < ListItem >
41140 < ListItemAvatar >
42141 < Avatar >
43- < AccessTimeIcon />
44- </ Avatar >
45- </ ListItemAvatar >
46- < ListItemText primary = "Local Time" secondary = { formatIsoDateTime ( data . time_local ) } />
47- </ ListItem >
48- < Divider variant = "inset" component = "li" />
49- < ListItem >
50- < ListItemAvatar >
51- < Avatar >
52- < SwapVerticalCircleIcon />
142+ < DNSIcon />
53143 </ Avatar >
54144 </ ListItemAvatar >
55- < ListItemText primary = "UTC Time " secondary = { formatIsoDateTime ( data . time_utc ) } />
145+ < ListItemText primary = "NTP Server " secondary = { data . server } />
56146 </ ListItem >
57147 < Divider variant = "inset" component = "li" />
58148 </ Fragment >
59149 ) }
60150 < ListItem >
61151 < ListItemAvatar >
62152 < Avatar >
63- < DNSIcon />
153+ < AccessTimeIcon />
64154 </ Avatar >
65155 </ ListItemAvatar >
66- < ListItemText primary = "NTP Server" secondary = { data . server } />
156+ < ListItemText primary = "Local Time" secondary = { formatIsoDateTime ( data . time_local ) } />
157+ </ ListItem >
158+ < Divider variant = "inset" component = "li" />
159+ < ListItem >
160+ < ListItemAvatar >
161+ < Avatar >
162+ < SwapVerticalCircleIcon />
163+ </ Avatar >
164+ </ ListItemAvatar >
165+ < ListItemText primary = "UTC Time" secondary = { formatIsoDateTime ( data . time_utc ) } />
67166 </ ListItem >
68167 < Divider variant = "inset" component = "li" />
69168 < ListItem >
@@ -76,14 +175,24 @@ class NTPStatusForm extends Component<NTPStatusFormProps> {
76175 </ ListItem >
77176 < Divider variant = "inset" component = "li" />
78177 </ List >
79- < FormActions >
80- < FormButton startIcon = { < RefreshIcon /> } variant = "contained" color = "secondary" onClick = { this . props . loadData } >
81- Refresh
82- </ FormButton >
83- </ FormActions >
178+ < Box display = "flex" flexWrap = "wrap" >
179+ < Box flexGrow = { 1 } padding = { 1 } >
180+ < FormButton startIcon = { < RefreshIcon /> } variant = "contained" color = "secondary" onClick = { this . props . loadData } >
181+ Refresh
182+ </ FormButton >
183+ </ Box >
184+ { me . admin && ! isNtpActive ( data ) && (
185+ < Box flexWrap = "none" padding = { 1 } whiteSpace = "nowrap" >
186+ < Button onClick = { this . openSetTime } variant = "contained" color = "primary" startIcon = { < AccessTimeIcon /> } >
187+ Set Time
188+ </ Button >
189+ </ Box >
190+ ) }
191+ </ Box >
192+ { this . renderSetTimeDialog ( ) }
84193 </ Fragment >
85194 ) ;
86195 }
87196}
88197
89- export default withTheme ( NTPStatusForm ) ;
198+ export default withAuthenticatedContext ( withTheme ( NTPStatusForm ) ) ;
0 commit comments