Allow selection of single time from log viewer
parent
b5a07a5854
commit
0e0a263993
|
@ -62,7 +62,6 @@ export enum ActionTypes {
|
||||||
ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS',
|
ConcatMoreLogs = 'LOGS_CONCAT_MORE_LOGS',
|
||||||
SetConfig = 'SET_CONFIG',
|
SetConfig = 'SET_CONFIG',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConcatMoreLogsAction {
|
export interface ConcatMoreLogsAction {
|
||||||
type: ActionTypes.ConcatMoreLogs
|
type: ActionTypes.ConcatMoreLogs
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -445,15 +444,17 @@ export const setNamespaces = (
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const setTimeRange = timeRange => ({
|
||||||
|
type: ActionTypes.SetTimeRange,
|
||||||
|
payload: {
|
||||||
|
timeRange,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export const setTimeRangeAsync = (timeRange: TimeRange) => async (
|
export const setTimeRangeAsync = (timeRange: TimeRange) => async (
|
||||||
dispatch
|
dispatch
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
dispatch({
|
dispatch(setTimeRange(timeRange))
|
||||||
type: ActionTypes.SetTimeRange,
|
|
||||||
payload: {
|
|
||||||
timeRange,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
dispatch(setHistogramQueryConfigAsync())
|
dispatch(setHistogramQueryConfigAsync())
|
||||||
dispatch(setTableQueryConfigAsync())
|
dispatch(setTableQueryConfigAsync())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ import {getDeep} from 'src/utils/wrappers'
|
||||||
|
|
||||||
import {colorForSeverity} from 'src/logs/utils/colors'
|
import {colorForSeverity} from 'src/logs/utils/colors'
|
||||||
import {
|
import {
|
||||||
|
ROW_HEIGHT,
|
||||||
|
calculateRowCharWidth,
|
||||||
|
calculateMessageHeight,
|
||||||
getColumnFromData,
|
getColumnFromData,
|
||||||
getValueFromData,
|
getValueFromData,
|
||||||
getValuesFromData,
|
getValuesFromData,
|
||||||
|
@ -36,8 +39,6 @@ import {
|
||||||
SeverityLevelColor,
|
SeverityLevelColor,
|
||||||
} from 'src/types/logs'
|
} from 'src/types/logs'
|
||||||
|
|
||||||
const ROW_HEIGHT = 26
|
|
||||||
const CHAR_WIDTH = 9
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: TableData
|
data: TableData
|
||||||
isScrolledToTop: boolean
|
isScrolledToTop: boolean
|
||||||
|
@ -50,6 +51,8 @@ interface Props {
|
||||||
tableColumns: LogsTableColumn[]
|
tableColumns: LogsTableColumn[]
|
||||||
severityFormat: SeverityFormat
|
severityFormat: SeverityFormat
|
||||||
severityLevelColors: SeverityLevelColor[]
|
severityLevelColors: SeverityLevelColor[]
|
||||||
|
scrollToRow?: number
|
||||||
|
hasScrolled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -64,13 +67,35 @@ interface State {
|
||||||
|
|
||||||
class LogsTable extends Component<Props, State> {
|
class LogsTable extends Component<Props, State> {
|
||||||
public static getDerivedStateFromProps(props, state): State {
|
public static getDerivedStateFromProps(props, state): State {
|
||||||
const {isScrolledToTop} = props
|
const {
|
||||||
|
isScrolledToTop,
|
||||||
|
scrollToRow,
|
||||||
|
data,
|
||||||
|
tableColumns,
|
||||||
|
severityFormat,
|
||||||
|
hasScrolled,
|
||||||
|
} = props
|
||||||
|
const currentMessageWidth = getMessageWidth(
|
||||||
|
data,
|
||||||
|
tableColumns,
|
||||||
|
severityFormat
|
||||||
|
)
|
||||||
|
|
||||||
let lastQueryTime = _.get(state, 'lastQueryTime', null)
|
let lastQueryTime = _.get(state, 'lastQueryTime', null)
|
||||||
let scrollTop = _.get(state, 'scrollTop', 0)
|
let scrollTop = _.get(state, 'scrollTop', 0)
|
||||||
if (isScrolledToTop) {
|
if (isScrolledToTop) {
|
||||||
lastQueryTime = null
|
lastQueryTime = null
|
||||||
scrollTop = 0
|
scrollTop = 0
|
||||||
|
} else if (scrollToRow && !hasScrolled) {
|
||||||
|
const rowCharLimit = calculateRowCharWidth(currentMessageWidth)
|
||||||
|
|
||||||
|
scrollTop = _.reduce(
|
||||||
|
_.range(0, scrollToRow),
|
||||||
|
(acc, index) => {
|
||||||
|
return acc + calculateMessageHeight(index, data, rowCharLimit)
|
||||||
|
},
|
||||||
|
0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollLeft = _.get(state, 'scrollLeft', 0)
|
const scrollLeft = _.get(state, 'scrollLeft', 0)
|
||||||
|
@ -90,11 +115,7 @@ class LogsTable extends Component<Props, State> {
|
||||||
scrollTop,
|
scrollTop,
|
||||||
scrollLeft,
|
scrollLeft,
|
||||||
currentRow: -1,
|
currentRow: -1,
|
||||||
currentMessageWidth: getMessageWidth(
|
currentMessageWidth,
|
||||||
props.data,
|
|
||||||
props.tableColumns,
|
|
||||||
props.severityFormat
|
|
||||||
),
|
|
||||||
isMessageVisible,
|
isMessageVisible,
|
||||||
visibleColumnsCount,
|
visibleColumnsCount,
|
||||||
}
|
}
|
||||||
|
@ -204,21 +225,13 @@ class LogsTable extends Component<Props, State> {
|
||||||
autoHide={false}
|
autoHide={false}
|
||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
height={height}
|
{...this.gridProperties(
|
||||||
rowHeight={this.calculateRowHeight}
|
width,
|
||||||
rowCount={getValuesFromData(this.props.data).length}
|
height,
|
||||||
width={width}
|
onRowsRendered,
|
||||||
scrollLeft={this.state.scrollLeft}
|
columnCount,
|
||||||
scrollTop={this.state.scrollTop}
|
registerChild
|
||||||
cellRenderer={this.cellRenderer}
|
)}
|
||||||
onSectionRendered={this.handleRowRender(onRowsRendered)}
|
|
||||||
onScroll={this.handleGridScroll}
|
|
||||||
columnCount={columnCount}
|
|
||||||
columnWidth={this.getColumnWidth}
|
|
||||||
ref={(ref: Grid) => {
|
|
||||||
registerChild(ref)
|
|
||||||
this.grid = ref
|
|
||||||
}}
|
|
||||||
style={{
|
style={{
|
||||||
height: this.calculateTotalHeight(),
|
height: this.calculateTotalHeight(),
|
||||||
overflowY: 'hidden',
|
overflowY: 'hidden',
|
||||||
|
@ -233,6 +246,40 @@ class LogsTable extends Component<Props, State> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private gridProperties = (
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
onRowsRendered: (params: {startIndex: number; stopIndex: number}) => void,
|
||||||
|
columnCount: number,
|
||||||
|
registerChild: (g: Grid) => void
|
||||||
|
) => {
|
||||||
|
const {hasScrolled, scrollToRow} = this.props
|
||||||
|
const {scrollLeft, scrollTop} = this.state
|
||||||
|
const result: {scrollToRow?: number} & any = {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
rowHeight: this.calculateRowHeight,
|
||||||
|
rowCount: getValuesFromData(this.props.data).length,
|
||||||
|
scrollLeft,
|
||||||
|
scrollTop,
|
||||||
|
cellRenderer: this.cellRenderer,
|
||||||
|
onSectionRendered: this.handleRowRender(onRowsRendered),
|
||||||
|
onScroll: this.handleGridScroll,
|
||||||
|
columnCount,
|
||||||
|
columnWidth: this.getColumnWidth,
|
||||||
|
ref: (ref: Grid) => {
|
||||||
|
registerChild(ref)
|
||||||
|
this.grid = ref
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasScrolled && scrollToRow) {
|
||||||
|
result.scrollToRow = scrollToRow
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private handleGridScroll = ({scrollLeft}) => {
|
private handleGridScroll = ({scrollLeft}) => {
|
||||||
this.handleScroll({scrollLeft})
|
this.handleScroll({scrollLeft})
|
||||||
}
|
}
|
||||||
|
@ -346,8 +393,7 @@ class LogsTable extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get rowCharLimit(): number {
|
private get rowCharLimit(): number {
|
||||||
const {currentMessageWidth} = this.state
|
return calculateRowCharWidth(this.state.currentMessageWidth)
|
||||||
return Math.floor(currentMessageWidth / CHAR_WIDTH)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateTotalHeight = (): number => {
|
private calculateTotalHeight = (): number => {
|
||||||
|
@ -356,29 +402,17 @@ class LogsTable extends Component<Props, State> {
|
||||||
return _.reduce(
|
return _.reduce(
|
||||||
data,
|
data,
|
||||||
(acc, __, index) => {
|
(acc, __, index) => {
|
||||||
return acc + this.calculateMessageHeight(index)
|
return (
|
||||||
|
acc +
|
||||||
|
calculateMessageHeight(index, this.props.data, this.rowCharLimit)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateMessageHeight = (index: number): number => {
|
private calculateRowHeight = ({index}: {index: number}): number =>
|
||||||
const columns = getColumnsFromData(this.props.data)
|
calculateMessageHeight(index, this.props.data, this.rowCharLimit)
|
||||||
const columnIndex = columns.indexOf('message')
|
|
||||||
const value = getValueFromData(this.props.data, index, columnIndex)
|
|
||||||
|
|
||||||
if (_.isEmpty(value)) {
|
|
||||||
return ROW_HEIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = Math.ceil(value.length / (this.rowCharLimit * 0.95))
|
|
||||||
|
|
||||||
return Math.max(lines, 1) * ROW_HEIGHT + 4
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateRowHeight = ({index}: {index: number}): number => {
|
|
||||||
return this.calculateMessageHeight(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
private headerRenderer = ({key, style, columnIndex}) => {
|
private headerRenderer = ({key, style, columnIndex}) => {
|
||||||
const column = getColumnFromData(this.props.data, columnIndex)
|
const column = getColumnFromData(this.props.data, columnIndex)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import timeRanges from 'src/logs/data/timeRanges'
|
||||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||||
import CustomTimeRange from 'src/shared/components/CustomTimeRange'
|
import CustomSingularTime from 'src/shared/components/CustomSingularTime'
|
||||||
|
|
||||||
import {TimeRange} from 'src/types'
|
import {TimeRange} from 'src/types'
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {selected, preventCustomTimeRange, page} = this.props
|
const {selected, preventCustomTimeRange} = this.props
|
||||||
const {customTimeRange, isCustomTimeRangeOpen} = this.state
|
const {customTimeRange, isCustomTimeRangeOpen} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -113,13 +113,11 @@ class TimeRangeDropdown extends Component<Props, State> {
|
||||||
{isCustomTimeRangeOpen ? (
|
{isCustomTimeRangeOpen ? (
|
||||||
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
|
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
|
||||||
<div className="custom-time--overlay">
|
<div className="custom-time--overlay">
|
||||||
<CustomTimeRange
|
<CustomSingularTime
|
||||||
onApplyTimeRange={this.handleApplyCustomTimeRange}
|
time={customTimeRange.lower}
|
||||||
timeRange={customTimeRange}
|
onSelected={this.handleApplyCustomTimeRange}
|
||||||
onClose={this.handleCloseCustomTimeRange}
|
onClose={this.handleCloseCustomTimeRange}
|
||||||
isVisible={isCustomTimeRangeOpen}
|
timeInterval={300}
|
||||||
timeInterval={60}
|
|
||||||
page={page}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ClickOutside>
|
</ClickOutside>
|
||||||
|
|
|
@ -80,6 +80,7 @@ interface State {
|
||||||
liveUpdating: boolean
|
liveUpdating: boolean
|
||||||
isOverlayVisible: boolean
|
isOverlayVisible: boolean
|
||||||
histogramColors: HistogramColor[]
|
histogramColors: HistogramColor[]
|
||||||
|
hasScrolled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class LogsPage extends PureComponent<Props, State> {
|
class LogsPage extends PureComponent<Props, State> {
|
||||||
|
@ -106,6 +107,7 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
liveUpdating: false,
|
liveUpdating: false,
|
||||||
isOverlayVisible: false,
|
isOverlayVisible: false,
|
||||||
histogramColors: [],
|
histogramColors: [],
|
||||||
|
hasScrolled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +165,7 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
tableColumns={this.tableColumns}
|
tableColumns={this.tableColumns}
|
||||||
severityFormat={this.severityFormat}
|
severityFormat={this.severityFormat}
|
||||||
severityLevelColors={this.severityLevelColors}
|
severityLevelColors={this.severityLevelColors}
|
||||||
|
hasScrolled={this.state.hasScrolled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -183,7 +186,10 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
const updatedColumns: string[] = _.get(orderedData, '0', [])
|
const updatedColumns: string[] = _.get(orderedData, '0', [])
|
||||||
const updatedValues = _.slice(orderedData, 1)
|
const updatedValues = _.slice(orderedData, 1)
|
||||||
|
|
||||||
return {columns: updatedColumns, values: updatedValues}
|
return {
|
||||||
|
columns: updatedColumns,
|
||||||
|
values: updatedValues,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get logConfigLink(): string {
|
private get logConfigLink(): string {
|
||||||
|
@ -219,8 +225,8 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
private handleVerticalScroll = () => {
|
private handleVerticalScroll = () => {
|
||||||
if (this.state.liveUpdating) {
|
if (this.state.liveUpdating) {
|
||||||
clearInterval(this.interval)
|
clearInterval(this.interval)
|
||||||
this.setState({liveUpdating: false})
|
|
||||||
}
|
}
|
||||||
|
this.setState({liveUpdating: false, hasScrolled: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTagSelection = (selection: {tag: string; key: string}) => {
|
private handleTagSelection = (selection: {tag: string; key: string}) => {
|
||||||
|
@ -299,8 +305,8 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
const {liveUpdating} = this.state
|
const {liveUpdating} = this.state
|
||||||
|
|
||||||
if (liveUpdating) {
|
if (liveUpdating) {
|
||||||
clearInterval(this.interval)
|
|
||||||
this.setState({liveUpdating: false})
|
this.setState({liveUpdating: false})
|
||||||
|
clearInterval(this.interval)
|
||||||
} else {
|
} else {
|
||||||
this.startUpdating()
|
this.startUpdating()
|
||||||
}
|
}
|
||||||
|
@ -351,8 +357,8 @@ class LogsPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchNewDataset() {
|
private fetchNewDataset() {
|
||||||
this.props.executeQueriesAsync()
|
|
||||||
this.setState({liveUpdating: true})
|
this.setState({liveUpdating: true})
|
||||||
|
this.props.executeQueriesAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleToggleOverlay = (): void => {
|
private handleToggleOverlay = (): void => {
|
||||||
|
|
|
@ -144,6 +144,8 @@ const computeSeconds = (range: TimeRange) => {
|
||||||
|
|
||||||
if (seconds) {
|
if (seconds) {
|
||||||
return seconds
|
return seconds
|
||||||
|
} else if (upper && upper.match(/now/) && lower) {
|
||||||
|
return moment().unix() - moment(lower).unix()
|
||||||
} else if (upper && lower) {
|
} else if (upper && lower) {
|
||||||
return moment(upper).unix() - moment(lower).unix()
|
return moment(upper).unix() - moment(lower).unix()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {getDeep} from 'src/utils/wrappers'
|
||||||
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
import {TableData, LogsTableColumn, SeverityFormat} from 'src/types/logs'
|
||||||
import {SeverityFormatOptions} from 'src/logs/constants'
|
import {SeverityFormatOptions} from 'src/logs/constants'
|
||||||
|
|
||||||
|
export const ROW_HEIGHT = 26
|
||||||
const CHAR_WIDTH = 9
|
const CHAR_WIDTH = 9
|
||||||
|
|
||||||
export const getValuesFromData = (data: TableData): string[][] =>
|
export const getValuesFromData = (data: TableData): string[][] =>
|
||||||
|
@ -69,6 +70,27 @@ export const getColumnWidth = (column: string): number => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const calculateRowCharWidth = (currentMessageWidth: number): number =>
|
||||||
|
Math.floor(currentMessageWidth / CHAR_WIDTH)
|
||||||
|
|
||||||
|
export const calculateMessageHeight = (
|
||||||
|
index: number,
|
||||||
|
data: TableData,
|
||||||
|
rowCharLimit: number
|
||||||
|
): number => {
|
||||||
|
const columns = getColumnsFromData(data)
|
||||||
|
const columnIndex = columns.indexOf('message')
|
||||||
|
const value = getValueFromData(data, index, columnIndex)
|
||||||
|
|
||||||
|
if (_.isEmpty(value)) {
|
||||||
|
return ROW_HEIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = Math.ceil(value.length / (rowCharLimit * 0.95))
|
||||||
|
|
||||||
|
return Math.max(lines, 1) * ROW_HEIGHT + 4
|
||||||
|
}
|
||||||
|
|
||||||
export const getMessageWidth = (
|
export const getMessageWidth = (
|
||||||
data: TableData,
|
data: TableData,
|
||||||
tableColumns: LogsTableColumn[],
|
tableColumns: LogsTableColumn[],
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
import React, {Component} from 'react'
|
||||||
|
import rome from 'rome'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import {formatTimeRange} from 'src/shared/utils/time'
|
||||||
|
|
||||||
|
import {TimeRange} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onSelected: (timeRange: TimeRange) => void
|
||||||
|
time: string
|
||||||
|
timeInterval?: number
|
||||||
|
onClose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class CustomSingularTime extends Component<Props, State> {
|
||||||
|
private calendar?: any
|
||||||
|
private containerRef: React.RefObject<HTMLDivElement> = React.createRef<
|
||||||
|
HTMLDivElement
|
||||||
|
>()
|
||||||
|
private inputRef: React.RefObject<HTMLInputElement> = React.createRef<
|
||||||
|
HTMLInputElement
|
||||||
|
>()
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
time: props.time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
const {time, timeInterval} = this.props
|
||||||
|
|
||||||
|
this.calendar = rome(this.inputRef.current, {
|
||||||
|
appendTo: this.containerRef.current,
|
||||||
|
initialValue: formatTimeRange(time),
|
||||||
|
autoClose: false,
|
||||||
|
autoHideOnBlur: false,
|
||||||
|
autoHideOnClick: false,
|
||||||
|
timeInterval,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.calendar.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div className="custom-time-container">
|
||||||
|
<div className="custom-time--wrap">
|
||||||
|
<div className="custom-time--dates">
|
||||||
|
<div
|
||||||
|
className="custom-time--lower-container"
|
||||||
|
ref={this.containerRef}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref={this.inputRef}
|
||||||
|
className="custom-time--lower form-control input-sm"
|
||||||
|
onKeyUp={this.handleRefreshCalendar}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="custom-time--apply btn btn-sm btn-primary"
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRefreshCalendar = () => {
|
||||||
|
if (this.calendar) {
|
||||||
|
this.calendar.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = () => {
|
||||||
|
const date = this.calendar.getDate()
|
||||||
|
if (date) {
|
||||||
|
const lower = date.toISOString()
|
||||||
|
this.props.onSelected({lower, upper: 'now()'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.onClose) {
|
||||||
|
this.props.onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomSingularTime
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||||
import rome from 'rome'
|
import rome from 'rome'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import {formatTimeRange} from 'shared/utils/time'
|
||||||
import shortcuts from 'shared/data/timeRangeShortcuts'
|
import shortcuts from 'shared/data/timeRangeShortcuts'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
const dateFormat = 'YYYY-MM-DD HH:mm'
|
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||||
|
@ -90,21 +91,7 @@ class CustomTimeRange extends Component {
|
||||||
* before passing the string to be parsed.
|
* before passing the string to be parsed.
|
||||||
*/
|
*/
|
||||||
_formatTimeRange = timeRange => {
|
_formatTimeRange = timeRange => {
|
||||||
if (!timeRange) {
|
return formatTimeRange(timeRange)
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeRange === 'now()') {
|
|
||||||
return moment(new Date()).format(dateFormat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the given time range is relative, create a fixed timestamp based on its value
|
|
||||||
if (timeRange.match(/^now/)) {
|
|
||||||
const [, duration, unitOfTime] = timeRange.match(/(\d+)(\w+)/)
|
|
||||||
moment().subtract(duration, unitOfTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
return moment(timeRange.replace(/\'/g, '')).format(dateFormat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
const dateFormat = 'YYYY-MM-DD HH:mm'
|
||||||
|
|
||||||
|
export const formatTimeRange = (timeRange: string | null): string => {
|
||||||
|
if (!timeRange) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRange === 'now()') {
|
||||||
|
return moment(new Date()).format(dateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRange.match(/^now/)) {
|
||||||
|
const [, duration, unitOfTime] = timeRange.match(/(\d+)(\w+)/)
|
||||||
|
const d = duration as moment.unitOfTime.DurationConstructor
|
||||||
|
|
||||||
|
moment().subtract(d, unitOfTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return moment(timeRange.replace(/\'/g, '')).format(dateFormat)
|
||||||
|
}
|
Loading…
Reference in New Issue