Separate time and window

Co-authored-by: Alex Paxton <thealexpaxton@gmail.com>
Co-authored-by: Daniel Campbell <metalwhirlwind@gmail.com>
pull/10616/head
Alex P 2018-07-10 16:25:37 -07:00
parent 6a35203fad
commit ff0b3ce15f
8 changed files with 109 additions and 215 deletions

View File

@ -21,7 +21,13 @@ import {
// getLogConfig as getLogConfigAJAX,
// updateLogConfig as updateLogConfigAJAX,
} from 'src/logs/api'
import {LogsState, Filter, TableData, LogConfig} from 'src/types/logs'
import {
LogsState,
Filter,
TableData,
LogConfig,
TimeWindow,
} from 'src/types/logs'
const defaultTableData: TableData = {
columns: [
@ -132,7 +138,7 @@ interface SetTimeRangeAction {
interface SetTimeWindowAction {
type: ActionTypes.SetTimeWindow
payload: {
timeWindow: string
timeWindow: TimeWindow
}
}
@ -251,7 +257,7 @@ const setHistogramData = (data): SetHistogramData => ({
payload: {data},
})
export const setTimeWindow = (timeWindow: string): SetTimeWindowAction => ({
export const setTimeWindow = (timeWindow: TimeWindow): SetTimeWindowAction => ({
type: ActionTypes.SetTimeWindow,
payload: {timeWindow},
})
@ -472,10 +478,9 @@ export const setTimeRangeAsync = (timeRange: TimeRange) => async (
dispatch(setTableQueryConfigAsync())
}
export const setTimeWindowAsync = (timeWindow: string) => async (
export const setTimeWindowAsync = (timeWindow: TimeWindow) => async (
dispatch
): Promise<void> => {
console.log(timeWindow)
dispatch({
type: ActionTypes.SetTimeWindow,
payload: {

View File

@ -9,7 +9,7 @@ import PageHeaderTitle from 'src/reusable_ui/components/page_layout/PageHeaderTi
import TimeRangeDropdown from 'src/logs/components/TimeRangeDropdown'
import WindowSelectorDropdown from 'src/logs/components/window_selector_dropdown/WindowSelectorDropdown'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import {TimeRange} from 'src/types'
import {TimeWindow, TimeWindowOption} from 'src/types/logs'
interface SourceItem {
id: string
@ -21,15 +21,14 @@ interface Props {
availableSources: Source[]
currentSource: Source | null
currentNamespaces: Namespace[]
timeRange: TimeRange
liveUpdating: boolean
onChooseSource: (sourceID: string) => void
onChooseNamespace: (namespace: Namespace) => void
onChooseTimerange: (timeRange: TimeRange) => void
onChooseTime: (time: string) => void
onChangeLiveUpdatingStatus: () => void
onShowOptionsOverlay: () => void
timeWindow: string
onChangeTimeWindow: (timeWindow: string) => void
timeWindow: TimeWindow
onChangeTimeWindow: (timeWindow: TimeWindowOption) => void
}
class LogViewerHeader extends PureComponent<Props> {
@ -54,10 +53,10 @@ class LogViewerHeader extends PureComponent<Props> {
private get optionsComponents(): JSX.Element {
const {
timeRange,
onShowOptionsOverlay,
timeWindow,
onChangeTimeWindow,
onChooseTime,
} = this.props
return (
@ -80,8 +79,8 @@ class LogViewerHeader extends PureComponent<Props> {
onChangeWindow={onChangeTimeWindow}
/>
<TimeRangeDropdown
onChooseTimeRange={this.handleChooseTimeRange}
selected={timeRange}
onChooseTime={onChooseTime}
selectedTime={timeWindow.timeOption}
/>
<Authorized requiredRole={EDITOR_ROLE}>
<button
@ -116,10 +115,6 @@ class LogViewerHeader extends PureComponent<Props> {
)
}
private handleChooseTimeRange = (timerange: TimeRange) => {
this.props.onChooseTimerange(timerange)
}
private handleChooseSource = (item: SourceItem) => {
this.props.onChooseSource(item.id)
}

View File

@ -1,185 +1,33 @@
import React, {Component} from 'react'
import classnames from 'classnames'
import moment from 'moment'
import _ from 'lodash'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import timeRanges from 'src/logs/data/timeRanges'
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {ClickOutside} from 'src/shared/components/ClickOutside'
import CustomTimeRange from 'src/shared/components/CustomTimeRange'
import {TimeRange} from 'src/types'
const dateFormat = 'YYYY-MM-DD HH:mm'
const emptyTime = {lower: '', upper: ''}
const format = t => moment(t.replace(/\'/g, '')).format(dateFormat)
import Dropdown from 'src/shared/components/Dropdown'
interface Props {
selected: {
lower: string
upper?: string
}
onChooseTimeRange: (timeRange: TimeRange) => void
preventCustomTimeRange?: boolean
page?: string
selectedTime: string
onChooseTime: (time: string) => void
}
interface State {
autobind: boolean
isOpen: boolean
isCustomTimeRangeOpen: boolean
customTimeRange: TimeRange
}
@ErrorHandling
class TimeRangeDropdown extends Component<Props, State> {
public static defaultProps = {
page: 'default',
}
constructor(props) {
super(props)
const {lower, upper} = props.selected
const isTimeValid = moment(upper).isValid() && moment(lower).isValid()
const customTimeRange = isTimeValid ? {lower, upper} : emptyTime
this.state = {
autobind: false,
isOpen: false,
isCustomTimeRangeOpen: false,
customTimeRange,
}
}
class TimeRangeDropdown extends Component<Props> {
public render() {
const {selected, preventCustomTimeRange, page} = this.props
const {customTimeRange, isCustomTimeRangeOpen} = this.state
const {selectedTime} = this.props
const items = [{text: 'now'}, {text: '2018-07-10T22:22:21.769Z'}]
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className="time-range-dropdown">
<div className={this.dropdownClassName}>
<div
className="btn btn-sm btn-default dropdown-toggle"
onClick={this.toggleMenu}
>
<span className="icon clock" />
<span className="dropdown-selected">
{this.findTimeRangeInputValue(selected)}
</span>
<span className="caret" />
</div>
<ul className="dropdown-menu">
<FancyScrollbar
autoHide={false}
autoHeight={true}
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
>
{preventCustomTimeRange ? null : (
<div>
<li className="dropdown-header">Absolute Time</li>
<li
className={
isCustomTimeRangeOpen
? 'active dropdown-item custom-timerange'
: 'dropdown-item custom-timerange'
}
>
<a href="#" onClick={this.showCustomTimeRange}>
Date Picker
</a>
</li>
</div>
)}
<li className="dropdown-header">
{preventCustomTimeRange ? '' : 'Relative '}Time
</li>
{timeRanges.map(item => {
return (
<li className="dropdown-item" key={item.menuOption}>
<a href="#" onClick={this.handleSelection(item)}>
{item.menuOption}
</a>
</li>
)
})}
</FancyScrollbar>
</ul>
</div>
{isCustomTimeRangeOpen ? (
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
<div className="custom-time--overlay">
<CustomTimeRange
onApplyTimeRange={this.handleApplyCustomTimeRange}
timeRange={customTimeRange}
onClose={this.handleCloseCustomTimeRange}
isVisible={isCustomTimeRangeOpen}
timeInterval={60}
page={page}
/>
</div>
</ClickOutside>
) : null}
</div>
</ClickOutside>
<Dropdown
onChoose={this.handleChoose}
buttonSize="btn-sm"
buttonColor="btn-default"
selected={selectedTime}
items={items}
/>
)
}
private get dropdownClassName(): string {
const {isOpen} = this.state
private handleChoose = time => {
const {onChooseTime} = this.props
const {text} = time
const {lower, upper} = _.get(this.props, 'selected', {upper: '', lower: ''})
const absoluteTimeRange = !_.isEmpty(lower) && !_.isEmpty(upper)
return classnames('dropdown', {
'dropdown-290': absoluteTimeRange,
'dropdown-120': !absoluteTimeRange,
open: isOpen,
})
}
private findTimeRangeInputValue = ({upper, lower}: TimeRange) => {
if (upper && lower) {
if (upper === 'now()') {
return `${format(lower)} - Now`
}
return `${format(lower)} - ${format(upper)}`
}
const selected = timeRanges.find(range => range.lower === lower)
return selected ? selected.inputValue : 'Custom'
}
private handleClickOutside = () => {
this.setState({isOpen: false})
}
private handleSelection = timeRange => () => {
this.props.onChooseTimeRange(timeRange)
this.setState({customTimeRange: emptyTime, isOpen: false})
}
private toggleMenu = () => {
this.setState({isOpen: !this.state.isOpen})
}
private showCustomTimeRange = () => {
this.setState({isCustomTimeRangeOpen: true})
}
private handleApplyCustomTimeRange = customTimeRange => {
this.props.onChooseTimeRange({...customTimeRange})
this.setState({customTimeRange, isOpen: false})
}
private handleCloseCustomTimeRange = () => {
this.setState({isCustomTimeRangeOpen: false})
onChooseTime(text)
}
}
export default TimeRangeDropdown

View File

@ -1,33 +1,28 @@
import React, {Component} from 'react'
import Dropdown from 'src/shared/components/Dropdown'
import {TIME_RANGE_VALUES} from 'src/logs/constants'
import {TimeWindow, TimeWindowOption} from 'src/types/logs'
interface Props {
onChangeWindow: (timeWindow: string) => void
selectedTimeWindow: string
onChangeWindow: (timeWindow: TimeWindowOption) => void
selectedTimeWindow: TimeWindow
}
class WindowSelectorDropdown extends Component<Props> {
public render() {
const {selectedTimeWindow} = this.props
const {selectedTimeWindow, onChangeWindow} = this.props
const {windowOption} = selectedTimeWindow
return (
<Dropdown
selected={selectedTimeWindow}
onChoose={this.handleChoose}
selected={windowOption}
onChoose={onChangeWindow}
buttonSize="btn-sm"
buttonColor="btn-default"
items={TIME_RANGE_VALUES}
/>
)
}
public handleChoose = time => {
const {onChangeWindow} = this.props
const {text} = time
onChangeWindow(text)
}
}
export default WindowSelectorDropdown

View File

@ -147,8 +147,8 @@ export enum EncodingVisibilityOptions {
}
export const TIME_RANGE_VALUES = [
{text: '1m'},
{text: '5m'},
{text: '10m'},
{text: '15m'},
{text: '1m', seconds: 60},
{text: '5m', seconds: 300},
{text: '10m', seconds: 600},
{text: '15m', seconds: 900},
]

View File

@ -1,5 +1,6 @@
import React, {PureComponent} from 'react'
import uuid from 'uuid'
import moment from 'moment'
import _ from 'lodash'
import {connect} from 'react-redux'
import {AutoSizer} from 'react-virtualized'
@ -46,6 +47,8 @@ import {
LogsTableColumn,
LogConfig,
TableData,
TimeWindow,
TimeWindowOption,
} from 'src/types/logs'
// Mock
@ -59,7 +62,7 @@ interface Props {
getSource: (sourceID: string) => void
getSources: () => void
setTimeRangeAsync: (timeRange: TimeRange) => void
setTimeWindowAsync: (timeWindow: string) => void
setTimeWindowAsync: (timeWindow: TimeWindow) => void
setNamespaceAsync: (namespace: Namespace) => void
changeZoomAsync: (timeRange: TimeRange) => void
executeQueriesAsync: () => void
@ -71,7 +74,7 @@ interface Props {
getConfig: (url: string) => Promise<void>
updateConfig: (url: string, config: LogConfig) => Promise<void>
timeRange: TimeRange
timeWindow: string
timeWindow: TimeWindow
histogramData: HistogramData
tableData: TableData
searchTerm: string
@ -257,7 +260,6 @@ class LogsPage extends PureComponent<Props, State> {
currentSource,
currentNamespaces,
currentNamespace,
timeRange,
timeWindow,
} = this.props
@ -266,13 +268,12 @@ class LogsPage extends PureComponent<Props, State> {
return (
<LogViewerHeader
timeWindow={timeWindow}
onChangeTimeWindow={this.handleChooseTimeWindow}
onChangeTimeWindow={this.transformWindowToRange}
onChooseTime={this.transformTimeToRange}
liveUpdating={liveUpdating && !this.isSpecificTimeRange}
availableSources={sources}
timeRange={timeRange}
onChooseSource={this.handleChooseSource}
onChooseNamespace={this.handleChooseNamespace}
onChooseTimerange={this.handleChooseTimerange}
currentSource={currentSource}
currentNamespaces={currentNamespaces}
currentNamespace={currentNamespace}
@ -313,12 +314,43 @@ class LogsPage extends PureComponent<Props, State> {
this.props.executeQueriesAsync()
}
private handleChooseTimerange = (timeRange: TimeRange) => {
this.props.setTimeRangeAsync(timeRange)
this.fetchNewDataset()
// private handleChooseTimerange = (timeRange: TimeRange) => {
// console.log('TIME RANGE', timeRange)
// this.props.setTimeRangeAsync(timeRange)
// this.fetchNewDataset()
// }
private transformTimeToRange = (timeOption: string) => {
const {seconds, windowOption} = this.props.timeWindow
let lower = `now() - ${windowOption}`
let upper = null
if (timeOption !== 'now') {
const numberTimeOption = moment(timeOption).valueOf()
const milliseconds = seconds * 10 / 2
lower = moment(numberTimeOption - milliseconds).format()
upper = moment(numberTimeOption + milliseconds).format()
}
const timeWindow = {
...this.props.timeWindow,
lower,
upper,
timeOption,
}
this.props.setTimeWindowAsync(timeWindow)
}
private handleChooseTimeWindow = (timeWindow: string) => {
private transformWindowToRange = (timeWindowOption: TimeWindowOption) => {
const {text, seconds} = timeWindowOption
const timeWindow = {
...this.props.timeWindow,
seconds,
windowOption: text,
}
this.props.setTimeWindowAsync(timeWindow)
}

View File

@ -19,7 +19,13 @@ export const defaultState: LogsState = {
currentSource: null,
currentNamespaces: [],
timeRange: {lower: 'now() - 1m', upper: null},
timeWindow: '1m',
timeWindow: {
upper: null,
lower: 'now() - 1m',
seconds: 60,
windowOption: '1m',
timeOption: 'now',
},
currentNamespace: null,
histogramQueryConfig: null,
tableQueryConfig: null,

View File

@ -20,7 +20,7 @@ export interface LogsState {
currentNamespaces: Namespace[]
currentNamespace: Namespace | null
timeRange: TimeRange
timeWindow: string
timeWindow: TimeWindow
histogramQueryConfig: QueryConfig | null
histogramData: object[]
tableQueryConfig: QueryConfig | null
@ -65,3 +65,16 @@ export interface ServerEncoding {
type: string
value: string
}
export interface TimeWindow {
upper?: string
lower: string
seconds?: number
windowOption: string
timeOption: string
}
export interface TimeWindowOption {
seconds: number
text: string
}