Skip to content

Commit

Permalink
Merge pull request #102241 from zachlite/backport23.1.0-101752
Browse files Browse the repository at this point in the history
release-23.1.0: ui: display configurable timezone in DB Console
  • Loading branch information
zachlite authored Apr 25, 2023
2 parents e0f36a2 + 08433db commit f468bd8
Show file tree
Hide file tree
Showing 75 changed files with 716 additions and 296 deletions.
3 changes: 2 additions & 1 deletion pkg/ui/workspaces/cluster-ui/src/columnsSelector/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import { isEmpty } from "lodash";
import { ColumnDescriptor } from "src/sortedtable/sortedtable";

// We show a column if:
Expand All @@ -20,7 +21,7 @@ export const isSelectedColumn = (
c: ColumnDescriptor<unknown>,
): boolean => {
return (
(selectedColumns == null && c.showByDefault !== false) ||
(isEmpty(selectedColumns) && c.showByDefault !== false) ||
(selectedColumns !== null && selectedColumns.includes(c.name)) ||
c.alwaysShow === true
);
Expand Down
4 changes: 2 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/contexts/timezoneContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { createContext, useContext } from "react";
export const CoordinatedUniversalTime = "Etc/UTC";
export const TimezoneContext = createContext<string>(CoordinatedUniversalTime);

interface WithTimezoneProps {
export interface WithTimezoneProps {
timezone: string;
}

Expand All @@ -23,7 +23,7 @@ interface WithTimezoneProps {
export function WithTimezone<T>(
Component: React.ComponentType<T & WithTimezoneProps>,
) {
return (props: T) => {
return (props: React.PropsWithChildren<T>) => {
// This lambda is a React function component.
// It is safe to call a hook here.
// eslint-disable-next-line
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import React from "react";
import React, { useContext } from "react";
import { Link, RouteComponentProps } from "react-router-dom";
import { Tooltip } from "antd";
import "antd/lib/tooltip/style";
Expand Down Expand Up @@ -51,6 +51,7 @@ import {
} from "src/queryFilter";
import { UIConfigState } from "src/store";
import { TableStatistics } from "src/tableStatistics";
import { Timestamp, Timezone } from "../timestamp";

const cx = classNames.bind(styles);
const sortableTableCx = classNames.bind(sortableTableStyles);
Expand Down Expand Up @@ -611,13 +612,16 @@ export class DatabaseDetailsPage extends React.Component<
placement="bottom"
title="The last time table statistics were created or updated."
>
Table Stats Last Updated (UTC)
Table Stats Last Updated <Timezone />
</Tooltip>
),
cell: table =>
!table.details.statsLastUpdated
? "No table statistics found"
: table.details.statsLastUpdated.format(DATE_FORMAT),
cell: table => (
<Timestamp
time={table.details.statsLastUpdated}
format={DATE_FORMAT}
fallback={"No table statistics found"}
/>
),
sort: table => table.details.statsLastUpdated,
className: cx("database-table__col--table-stats"),
name: "tableStatsUpdated",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
import * as format from "src/util/format";
import {
DATE_FORMAT,
DATE_FORMAT_24_UTC,
DATE_FORMAT_24_TZ,
EncodeDatabaseTableUri,
EncodeDatabaseUri,
EncodeUriName,
Expand Down Expand Up @@ -61,6 +61,7 @@ import LoadingError from "../sqlActivity/errorComponent";
import { Loading } from "../loading";
import { UIConfigState } from "../store";
import { QuoteIdentifier } from "../api/safesql";
import { Timestamp, Timezone } from "../timestamp";

const cx = classNames.bind(styles);
const booleanSettingCx = classnames.bind(booleanSettingStyles);
Expand Down Expand Up @@ -303,24 +304,31 @@ export class DatabaseTablePage extends React.Component<
history.replace(history.location);
}

private getLastResetString() {
private getLastReset() {
const lastReset = this.props.indexStats.lastReset;
if (lastReset.isSame(this.minDate)) {
return "Last reset: Never";
return <>Last reset: Never</>;
} else {
return "Last reset: " + lastReset.format(DATE_FORMAT_24_UTC);
return (
<>
Last reset: <Timestamp time={lastReset} format={DATE_FORMAT_24_TZ} />
</>
);
}
}

private getLastUsedString(indexStat: IndexStat) {
private getLastUsed(indexStat: IndexStat) {
// This case only occurs when we have no reads, resets, or creation time on
// the index.
if (indexStat.lastUsed.isSame(this.minDate)) {
return "Never";
return <>Never</>;
}
return `Last ${indexStat.lastUsedType}: ${indexStat.lastUsed.format(
DATE_FORMAT,
)}`;
return (
<>
Last {indexStat.lastUsedType}:{" "}
<Timestamp time={indexStat.lastUsed} format={DATE_FORMAT} />
</>
);
}

private renderIndexRecommendations = (
Expand Down Expand Up @@ -420,10 +428,14 @@ export class DatabaseTablePage extends React.Component<
},
{
name: "last used",
title: "Last Used (UTC)",
title: (
<>
Last Used <Timezone />
</>
),
hideTitleUnderline: true,
className: cx("index-stats-table__col-last-used"),
cell: indexStat => this.getLastUsedString(indexStat),
cell: indexStat => this.getLastUsed(indexStat),
sort: indexStat => indexStat.lastUsed,
},
{
Expand Down Expand Up @@ -566,9 +578,12 @@ export class DatabaseTablePage extends React.Component<
{this.props.details.statsLastUpdated && (
<SummaryCardItem
label="Table Stats Last Updated"
value={this.props.details.statsLastUpdated.format(
DATE_FORMAT_24_UTC,
)}
value={
<Timestamp
time={this.props.details.statsLastUpdated}
format={DATE_FORMAT_24_TZ}
/>
}
/>
)}
{this.props.automaticStatsCollectionEnabled !=
Expand Down Expand Up @@ -640,7 +655,7 @@ export class DatabaseTablePage extends React.Component<
"underline",
)}
>
{this.getLastResetString()}
{this.getLastReset()}
</div>
</Tooltip>
{hasAdminRole && (
Expand Down
19 changes: 13 additions & 6 deletions pkg/ui/workspaces/cluster-ui/src/dateRangeMenu/dateRangeMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import React, { useState } from "react";
import React, { useContext, useState } from "react";
import { Alert, DatePicker, Icon, TimePicker } from "antd";
import "antd/lib/time-picker/style";
import "antd/lib/icon/style";
Expand All @@ -21,6 +21,8 @@ import { Button } from "src/button";
import { Text, TextTypes } from "src/text";

import styles from "./dateRangeMenu.module.scss";
import { TimezoneContext } from "../contexts";
import { Timezone } from "src/timestamp";

const cx = classNames.bind(styles);

Expand All @@ -44,6 +46,8 @@ export function DateRangeMenu({
onCancel,
onReturnToPresetOptionsClick,
}: DateRangeMenuProps): React.ReactElement {
const timezone = useContext(TimezoneContext);

/**
* Local startMoment and endMoment state are stored here so that users can change the time before clicking "Apply".
* They are re-initialized to startInit and endInit by re-mounting this component. It is thus the responsibility of
Expand All @@ -66,9 +70,11 @@ export function DateRangeMenu({
* the parent component to re-initialize this.
*/
const [startMoment, setStartMoment] = useState<Moment>(
startInit || moment.utc(),
startInit ? startInit.tz(timezone) : moment.tz(timezone),
);
const [endMoment, setEndMoment] = useState<Moment>(
endInit ? endInit.tz(timezone) : moment.tz(timezone),
);
const [endMoment, setEndMoment] = useState<Moment>(endInit || moment.utc());

const onChangeStart = (m?: Moment) => {
m && setStartMoment(m);
Expand Down Expand Up @@ -99,7 +105,8 @@ export function DateRangeMenu({
const isValid = errorMessage === undefined;

const onApply = (): void => {
onSubmit(startMoment, endMoment);
// Idempotently set the start and end moments to UTC.
onSubmit(startMoment.utc(), endMoment.utc());
};

return (
Expand All @@ -111,7 +118,7 @@ export function DateRangeMenu({
</a>
</div>
<Text className={cx("label")} textType={TextTypes.BodyStrong}>
Start (UTC)
Start <Timezone />
</Text>
<DatePicker
disabledDate={isDisabled}
Expand All @@ -130,7 +137,7 @@ export function DateRangeMenu({
/>
<div className={cx("divider")} />
<Text className={cx("label")} textType={TextTypes.BodyStrong}>
End (UTC)
End <Timezone />
</Text>
<DatePicker
allowClear={false}
Expand Down
5 changes: 4 additions & 1 deletion pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/bars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ export const getStackedBarOpts = (
yAxisDomain: AxisDomain,
yyAxisUnits: AxisUnits,
colourPalette = seriesPalette,
timezone: string,
): Options => {
const options = getBarChartOpts(
userOptions,
xAxisDomain,
yAxisDomain,
yyAxisUnits,
colourPalette,
timezone,
);

options.bands = getStackedBands(unstackedData, () => false);
Expand Down Expand Up @@ -141,6 +143,7 @@ export const getBarChartOpts = (
yAxisDomain: AxisDomain,
yAxisUnits: AxisUnits,
colourPalette = seriesPalette,
timezone: string,
): Options => {
const { series, ...providedOpts } = userOptions;
const defaultBars = getBarsBuilder(0.9, 80);
Expand Down Expand Up @@ -191,7 +194,7 @@ export const getBarChartOpts = (
...s,
})),
],
plugins: [barTooltipPlugin(yAxisUnits)],
plugins: [barTooltipPlugin(yAxisUnits, timezone)],
};

const combinedOpts = merge(opts, providedOpts);
Expand Down
6 changes: 5 additions & 1 deletion pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import React, { useEffect, useRef } from "react";
import React, { useContext, useEffect, useRef } from "react";
import classNames from "classnames/bind";
import { getStackedBarOpts, stack } from "./bars";
import uPlot, { AlignedData } from "uplot";
Expand All @@ -20,6 +20,7 @@ import {
calculateYAxisDomain,
} from "../utils/domain";
import { Options } from "uplot";
import { TimezoneContext } from "../../contexts";

const cx = classNames.bind(styles);

Expand All @@ -46,6 +47,7 @@ export const BarGraphTimeSeries: React.FC<BarGraphTimeSeriesProps> = ({
const graphRef = useRef<HTMLDivElement>(null);
const samplingIntervalMillis =
alignedData[0].length > 1 ? alignedData[0][1] - alignedData[0][0] : 1e3;
const timezone = useContext(TimezoneContext);

useEffect(() => {
if (!alignedData) return;
Expand All @@ -69,6 +71,7 @@ export const BarGraphTimeSeries: React.FC<BarGraphTimeSeriesProps> = ({
yAxisDomain,
yAxisUnits,
colourPalette,
timezone,
);

const plot = new uPlot(opts, stackedData, graphRef.current);
Expand All @@ -82,6 +85,7 @@ export const BarGraphTimeSeries: React.FC<BarGraphTimeSeriesProps> = ({
uPlotOptions,
yAxisUnits,
samplingIntervalMillis,
timezone,
]);

return (
Expand Down
20 changes: 16 additions & 4 deletions pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@
// licenses/APL.txt.

import uPlot, { Plugin } from "uplot";
import { AxisUnits, formatTimeStamp } from "../utils/domain";
import { Bytes, Duration, Percentage, Count } from "../../util";
import { AxisUnits } from "../utils/domain";
import {
Bytes,
Duration,
Percentage,
Count,
FormatWithTimezone,
DATE_WITH_SECONDS_FORMAT_24_TZ,
} from "../../util";
import moment from "moment-timezone";

// Fallback color for series stroke if one is not defined.
const DEFAULT_STROKE = "#7e89a9";
Expand Down Expand Up @@ -96,7 +104,7 @@ function getFormattedValue(value: number, yAxisUnits: AxisUnits): string {
}

// Tooltip legend plugin for bar charts.
export function barTooltipPlugin(yAxis: AxisUnits): Plugin {
export function barTooltipPlugin(yAxis: AxisUnits, timezone: string): Plugin {
const cursorToolTip = {
tooltip: document.createElement("div"),
timeStamp: document.createElement("div"),
Expand All @@ -110,7 +118,11 @@ export function barTooltipPlugin(yAxis: AxisUnits): Plugin {
// get the current timestamp from the x axis and formatting as
// the Tooltip header.
const closestDataPointTimeMillis = u.data[0][u.posToIdx(left)];
timeStamp.textContent = formatTimeStamp(closestDataPointTimeMillis);
timeStamp.textContent = FormatWithTimezone(
moment(closestDataPointTimeMillis),
DATE_WITH_SECONDS_FORMAT_24_TZ,
timezone,
);

// Generating the series legend based on current state of µPlot
generateSeriesLegend(u, seriesLegend, yAxis);
Expand Down
Loading

0 comments on commit f468bd8

Please sign in to comment.