diff options
Diffstat (limited to 'web/src')
-rw-r--r-- | web/src/App.jsx | 15 | ||||
-rw-r--r-- | web/src/actions/timezone.js | 20 | ||||
-rw-r--r-- | web/src/containers/build/Summary.jsx | 6 | ||||
-rw-r--r-- | web/src/containers/timezone/SelectTz.jsx | 142 | ||||
-rw-r--r-- | web/src/pages/Builds.jsx | 17 | ||||
-rw-r--r-- | web/src/pages/Status.jsx | 11 | ||||
-rw-r--r-- | web/src/reducers/index.js | 12 | ||||
-rw-r--r-- | web/src/reducers/timezone.js | 22 |
8 files changed, 228 insertions, 17 deletions
diff --git a/web/src/App.jsx b/web/src/App.jsx index 619cc499e..1b412f572 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -31,12 +31,12 @@ import { import * as moment from 'moment' import ErrorBoundary from './containers/ErrorBoundary' +import SelectTz from './containers/timezone/SelectTz' import logo from './images/logo.png' -import { routes } from './routes' +import { clearError } from './actions/errors' import { fetchConfigErrorsAction } from './actions/configErrors' +import { routes } from './routes' import { setTenantAction } from './actions/tenant' -import { clearError } from './actions/errors' - class App extends React.Component { static propTypes = { @@ -44,6 +44,7 @@ class App extends React.Component { configErrors: PropTypes.array, info: PropTypes.object, tenant: PropTypes.object, + timezone: PropTypes.string, location: PropTypes.object, history: PropTypes.object, dispatch: PropTypes.func @@ -166,7 +167,7 @@ class App extends React.Component { type='error' onDismiss={() => {this.props.dispatch(clearError(error.id))}} > - <span title={moment(error.date).format()}> + <span title={moment.utc(error.date).tz(this.props.timezone).format()}> <strong>{error.text}</strong> ({error.status}) {error.url} </span> @@ -273,6 +274,9 @@ class App extends React.Component { </Link> </li> )} + <li> + <SelectTz/> + </li> </ul> {showErrors && this.renderConfigErrors(configErrors)} </div> @@ -299,6 +303,7 @@ export default withRouter(connect( errors: state.errors, configErrors: state.configErrors, info: state.info, - tenant: state.tenant + tenant: state.tenant, + timezone: state.timezone }) )(App)) diff --git a/web/src/actions/timezone.js b/web/src/actions/timezone.js new file mode 100644 index 000000000..738eb6884 --- /dev/null +++ b/web/src/actions/timezone.js @@ -0,0 +1,20 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +export const TIMEZONE_SET = 'TIMEZONE_SET' + +export function setTimezoneAction (name) { + return { + type: TIMEZONE_SET, + timezone: name + } +} diff --git a/web/src/containers/build/Summary.jsx b/web/src/containers/build/Summary.jsx index c03f24c3c..dbccdbc59 100644 --- a/web/src/containers/build/Summary.jsx +++ b/web/src/containers/build/Summary.jsx @@ -28,6 +28,7 @@ class Summary extends React.Component { static propTypes = { build: PropTypes.object, tenant: PropTypes.object, + timezone: PropTypes.string, } render () { @@ -75,6 +76,9 @@ class Summary extends React.Component { value = 'false' } } + if (column === 'start_time' || column === 'end_time') { + value = moment.utc(value).tz(this.props.timezone).format('YYYY-MM-DD HH:mm:ss') + } if (column === 'duration') { value = moment.duration(value, 'seconds') .format('h [hr] m [min] s [sec]') @@ -124,4 +128,4 @@ class Summary extends React.Component { } -export default connect(state => ({tenant: state.tenant}))(Summary) +export default connect(state => ({tenant: state.tenant, timezone: state.timezone}))(Summary) diff --git a/web/src/containers/timezone/SelectTz.jsx b/web/src/containers/timezone/SelectTz.jsx new file mode 100644 index 000000000..78092d2ab --- /dev/null +++ b/web/src/containers/timezone/SelectTz.jsx @@ -0,0 +1,142 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import PropTypes from 'prop-types' +import React from 'react' +import Select from 'react-select' +import moment from 'moment-timezone' +import { Icon } from 'patternfly-react' +import { connect } from 'react-redux' +import { setTimezoneAction } from '../../actions/timezone' + +class SelectTz extends React.Component { + static propTypes = { + dispatch: PropTypes.func + } + + state = { + availableTz: moment.tz.names().map(item => ({value: item, label: item})), + defaultValue: {value: 'UTC', label: 'UTC'} + } + + componentDidMount () { + this.loadState() + } + + handleChange = (selectedTz) => { + const tz = selectedTz.value + + this.setCookie('zuul_tz_string', tz) + this.updateState(tz) + } + + setCookie (name, value) { + document.cookie = name + '=' + value + '; path=/' + } + + loadState = () => { + function readCookie (name, defaultValue) { + let nameEQ = name + '=' + let ca = document.cookie.split(';') + for (let i = 0; i < ca.length; i++) { + let c = ca[i] + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length) + } + if (c.indexOf(nameEQ) === 0) { + return c.substring(nameEQ.length, c.length) + } + } + return defaultValue + } + let tz = readCookie('zuul_tz_string', '') + if (tz) { + this.updateState(tz) + } + } + + updateState = (tz) => { + + this.setState({ + currentValue: {value: tz, label: tz} + }) + + let timezoneAction = setTimezoneAction(tz) + this.props.dispatch(timezoneAction) + } + + render() { + const textColor = '#d1d1d1' + const containerStyles= { + border: 'solid #2b2b2b', + borderWidth: '0 0 0 1px', + cursor: 'pointer', + display: 'initial', + fontSize: '11px', + padding: '6px' + } + const iconStyles = { + padding: '5px' + } + const customStyles = { + container: () => ({ + display: 'inline-block', + }), + control: () => ({ + width: 'auto', + display: 'flex' + }), + singleValue: () => ({ + color: textColor, + }), + input: (provided) => ({ + ...provided, + color: textColor + }), + dropdownIndicator:(provided) => ({ + ...provided, + padding: '3px' + }), + indicatorSeparator: () => {}, + menu: (provided) => ({ + ...provided, + width: 'auto', + right: '0', + top: '22px', + }) + } + return ( + <div style={containerStyles}> + <Icon style={iconStyles} type="fa" name="clock-o" /> + <Select + styles={customStyles} + value={this.state.currentValue} + onChange={this.handleChange} + options={this.state.availableTz} + noOptionsMessage={() => 'No api found'} + placeholder={'Select Tz'} + defaultValue={this.state.defaultValue} + theme={(theme) => ({ + ...theme, + borderRadius: 0, + spacing: { + ...theme.spacing, + baseUnit: 2, + }, + })} + /> + </div> + ) + } +} + +export default connect()(SelectTz) diff --git a/web/src/pages/Builds.jsx b/web/src/pages/Builds.jsx index 694608373..6dc0a4836 100644 --- a/web/src/pages/Builds.jsx +++ b/web/src/pages/Builds.jsx @@ -17,7 +17,7 @@ import PropTypes from 'prop-types' import { connect } from 'react-redux' import { Link } from 'react-router-dom' import { Table } from 'patternfly-react' -import * as moment from 'moment' +import * as moment from 'moment-timezone' import 'moment-duration-format' import { fetchBuilds } from '../api' @@ -26,12 +26,12 @@ import TableFilters from '../containers/TableFilters' class BuildsPage extends TableFilters { static propTypes = { - tenant: PropTypes.object + tenant: PropTypes.object, + timezone: PropTypes.string } constructor () { super() - this.prepareTableHeaders() this.state = { builds: null, @@ -60,7 +60,8 @@ class BuildsPage extends TableFilters { } componentDidUpdate (prevProps) { - if (this.props.tenant.name !== prevProps.tenant.name) { + if (this.props.tenant.name !== prevProps.tenant.name || + this.props.timezone !== prevProps.timezone) { this.updateData(this.getFilterFromUrl()) } } @@ -84,6 +85,11 @@ class BuildsPage extends TableFilters { {moment.duration(value, 'seconds').format('h [hr] m [min] s [sec]')} </Table.Cell> ) + const timeFormat = (value) => ( + <Table.Cell> + {moment.utc(value).tz(this.props.timezone).format('YYYY-MM-DD HH:mm:ss')} + </Table.Cell> + ) this.columns = [] this.filterTypes = [] const myColumns = [ @@ -103,6 +109,7 @@ class BuildsPage extends TableFilters { prop = 'job_name' } else if (column === 'start time') { prop = 'start_time' + formatter = timeFormat } else if (column === 'change') { prop = 'change' formatter = linkChangeFormat @@ -169,4 +176,4 @@ class BuildsPage extends TableFilters { } } -export default connect(state => ({tenant: state.tenant}))(BuildsPage) +export default connect(state => ({tenant: state.tenant, timezone: state.timezone}))(BuildsPage) diff --git a/web/src/pages/Status.jsx b/web/src/pages/Status.jsx index ad169e556..84555b5d2 100644 --- a/web/src/pages/Status.jsx +++ b/web/src/pages/Status.jsx @@ -13,6 +13,7 @@ // License for the specific language governing permissions and limitations // under the License. +import * as moment from 'moment-timezone' import * as React from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' @@ -33,6 +34,7 @@ class StatusPage extends Refreshable { static propTypes = { location: PropTypes.object, tenant: PropTypes.object, + timezone: PropTypes.string, remoteData: PropTypes.object, dispatch: PropTypes.func } @@ -111,6 +113,12 @@ class StatusPage extends Refreshable { this.visibilityChangeEvent, this.visibilityListener) } + componentDidUpdate (prevProps) { + if (this.props.timezone !== prevProps.timezo) { + this.loadState() + } + } + setFilter = (filter) => { this.filter.value = filter this.setState({filter: filter}) @@ -184,7 +192,7 @@ class StatusPage extends Refreshable { <p>Zuul version: <span>{status.zuul_version}</span></p> {status.last_reconfigured ? ( <p>Last reconfigured: <span> - {new Date(status.last_reconfigured).toString()} + {moment.utc(status.last_reconfigured).tz(this.props.timezone).format('llll')} </span></p>) : ''} </React.Fragment> ) @@ -258,5 +266,6 @@ class StatusPage extends Refreshable { export default connect(state => ({ tenant: state.tenant, + timezone: state.timezone, remoteData: state.status, }))(StatusPage) diff --git a/web/src/reducers/index.js b/web/src/reducers/index.js index 18abb44e3..84fe8e6fd 100644 --- a/web/src/reducers/index.js +++ b/web/src/reducers/index.js @@ -24,30 +24,32 @@ import jobs from './jobs' import labels from './labels' import logfile from './logfile' import nodes from './nodes' +import openapi from './openapi' import project from './project' import projects from './projects' import status from './status' import tenant from './tenant' import tenants from './tenants' -import openapi from './openapi' +import timezone from './timezone' const reducers = { - change, build, + change, + configErrors, + errors, info, job, jobs, labels, logfile, nodes, + openapi, project, projects, - configErrors, - errors, status, tenant, tenants, - openapi, + timezone, } export default combineReducers(reducers) diff --git a/web/src/reducers/timezone.js b/web/src/reducers/timezone.js new file mode 100644 index 000000000..c382ab0d3 --- /dev/null +++ b/web/src/reducers/timezone.js @@ -0,0 +1,22 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import { TIMEZONE_SET } from '../actions/timezone' + +export default (state = 'UTC', action) => { + switch (action.type) { + case TIMEZONE_SET: + return action.timezone + default: + return state + } + } |