From 0aeeafba7c1ea1e5f77288a4ee3949f01f7c803c Mon Sep 17 00:00:00 2001 From: Zach Lite Date: Tue, 28 Mar 2023 15:01:55 -0400 Subject: [PATCH] ui: display configurable timezone in DB Console This commit makes it so all timestamps displayed in DB Console (with the exception of Advanced Debug pages) use the timezone set by the cluster setting `ui.display_timezone`. Only Coordinated Universal Time and America/New_York are supported. Supporting additional timezones can be achieved by adding more timezones to the enum passed to `ui.display_timezone` when the setting is registered, and modifying the webpack configs to make sure the relevant timezone data is included. See #99848 for more details. Epic: https://cockroachlabs.atlassian.net/browse/CRDB-5536 Release note (ui): Added the ability for users to view timestamps in DB Console in their preferred timezone via the cluster setting `ui.display_timezone`. Currently supported timezones are Coordinated Universal Time and America/New_York. --- .../src/contexts/timezoneContext.tsx | 4 +- .../databaseDetailsPage.tsx | 16 +- .../databaseTablePage/databaseTablePage.tsx | 34 +- .../src/dateRangeMenu/dateRangeMenu.tsx | 23 +- .../cluster-ui/src/graphs/bargraph/bars.ts | 5 +- .../cluster-ui/src/graphs/bargraph/index.tsx | 6 +- .../cluster-ui/src/graphs/bargraph/plugins.ts | 20 +- .../cluster-ui/src/graphs/utils/domain.ts | 45 ++- pkg/ui/workspaces/cluster-ui/src/index.ts | 1 + .../src/indexDetailsPage/indexDetailsPage.tsx | 10 +- .../insightDetailsTables.tsx | 15 +- .../statementInsightDetailsOverviewTab.tsx | 21 +- .../transactionInsightDetailsOverviewTab.tsx | 25 +- .../transactionInsightDetailsStmtsTab.tsx | 25 +- .../statementInsightsTable.tsx | 12 +- .../transactionInsightsTable.tsx | 16 +- .../workloadInsights/util/insightsColumns.tsx | 4 +- .../src/jobs/jobDetailsPage/jobDetails.tsx | 23 +- .../src/jobs/jobsPage/jobsPage.spec.tsx | 4 +- .../cluster-ui/src/jobs/jobsPage/jobsPage.tsx | 17 +- .../src/jobs/jobsPage/jobsTable.tsx | 30 +- .../src/jobs/util/highwaterTimestamp.tsx | 7 +- .../src/recentExecutions/execTableCommon.tsx | 9 +- .../execContentionTable.tsx | 5 +- .../scheduleDetailsPage/scheduleDetails.tsx | 25 +- .../schedules/schedulesPage/scheduleTable.tsx | 21 +- .../schedulesPage/schedulesPage.spec.tsx | 4 +- .../src/sessions/sessionDetails.tsx | 26 +- .../cluster-ui/src/sessions/sessionsTable.tsx | 16 +- .../diagnostics/diagnosticsView.tsx | 8 +- .../planDetails/planDetails.tsx | 12 +- .../planDetails/plansTable.tsx | 13 +- .../recentStatementDetailsOverviewTab.tsx | 12 +- .../src/statementDetails/statementDetails.tsx | 29 +- .../src/statementsPage/statementsPage.tsx | 4 +- .../src/statementsTable/statementsTable.tsx | 11 +- .../src/statsTableUtil/statsTableUtil.tsx | 6 +- .../src/timeScaleDropdown/rangeSelect.tsx | 9 +- .../timeScaleDropdown.spec.tsx | 9 +- .../timeScaleDropdown/timeScaleDropdown.tsx | 16 +- .../timeScaleDropdown/timeScaleToString.tsx | 38 ++ .../cluster-ui/src/timeScaleDropdown/utils.ts | 15 - .../cluster-ui/src/timestamp/index.ts | 11 + .../cluster-ui/src/timestamp/timestamp.tsx | 25 ++ .../recentTransactionDetails.tsx | 12 +- .../transactionDetails/transactionDetails.tsx | 4 +- .../src/transactionsPage/transactionsPage.tsx | 4 +- .../workspaces/cluster-ui/src/util/format.ts | 11 +- pkg/ui/workspaces/cluster-ui/yarn.lock | 371 ++++++++++++++++++ .../cluster/components/linegraph/index.tsx | 13 +- .../components/linegraph/linegraph.spec.tsx | 23 +- .../cluster/containers/events/events.spec.tsx | 2 +- .../views/cluster/containers/events/index.tsx | 55 +-- .../nodeGraphs/dashboards/changefeeds.tsx | 2 +- .../dashboards/crossClusterReplication.tsx | 2 +- .../nodeGraphs/dashboards/distributed.tsx | 2 +- .../nodeGraphs/dashboards/hardware.tsx | 2 +- .../nodeGraphs/dashboards/overload.tsx | 2 +- .../nodeGraphs/dashboards/overview.tsx | 2 +- .../nodeGraphs/dashboards/queues.tsx | 2 +- .../nodeGraphs/dashboards/replication.tsx | 2 +- .../nodeGraphs/dashboards/requests.tsx | 2 +- .../nodeGraphs/dashboards/runtime.tsx | 2 +- .../containers/nodeGraphs/dashboards/sql.tsx | 2 +- .../nodeGraphs/dashboards/storage.tsx | 2 +- .../containers/nodeGraphs/dashboards/ttl.tsx | 2 +- .../cluster/containers/nodeLogs/index.tsx | 17 +- .../cluster/containers/nodeOverview/index.tsx | 13 +- .../containers/nodesOverview/index.tsx | 9 +- .../containers/raftMessages/messages.tsx | 2 +- .../db-console/src/views/hotRanges/index.tsx | 18 +- .../reports/containers/customChart/index.tsx | 2 +- .../nodeHistory/decommissionedNodeHistory.tsx | 8 +- .../reports/containers/settings/index.tsx | 12 +- .../statementDiagnosticsHistory/index.tsx | 6 +- 75 files changed, 1018 insertions(+), 277 deletions(-) create mode 100644 pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleToString.tsx create mode 100644 pkg/ui/workspaces/cluster-ui/src/timestamp/index.ts create mode 100644 pkg/ui/workspaces/cluster-ui/src/timestamp/timestamp.tsx diff --git a/pkg/ui/workspaces/cluster-ui/src/contexts/timezoneContext.tsx b/pkg/ui/workspaces/cluster-ui/src/contexts/timezoneContext.tsx index dfe6deb91f9a..16f2353e0457 100644 --- a/pkg/ui/workspaces/cluster-ui/src/contexts/timezoneContext.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/contexts/timezoneContext.tsx @@ -14,7 +14,7 @@ import { createContext, useContext } from "react"; export const CoordinatedUniversalTime = "Etc/UTC"; export const TimezoneContext = createContext(CoordinatedUniversalTime); -interface WithTimezoneProps { +export interface WithTimezoneProps { timezone: string; } @@ -23,7 +23,7 @@ interface WithTimezoneProps { export function WithTimezone( Component: React.ComponentType, ) { - return (props: T) => { + return (props: React.PropsWithChildren) => { // This lambda is a React function component. // It is safe to call a hook here. // eslint-disable-next-line diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx index 8ce59b2cc78e..85a32a560be8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx @@ -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"; @@ -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); @@ -611,13 +612,18 @@ 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 () ), cell: table => - !table.details.statsLastUpdated - ? "No table statistics found" - : table.details.statsLastUpdated.format(DATE_FORMAT), + !table.details.statsLastUpdated ? ( + "No table statistics found" + ) : ( + + ), sort: table => table.details.statsLastUpdated, className: cx("database-table__col--table-stats"), name: "tableStatsUpdated", diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx index c4bf25c32535..32eafdc11aa7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx @@ -32,8 +32,7 @@ import { } from "src/summaryCard"; import * as format from "src/util/format"; import { - DATE_FORMAT, - DATE_FORMAT_24_UTC, + DATE_FORMAT_24_TZ, EncodeDatabaseTableUri, EncodeDatabaseUri, EncodeUriName, @@ -61,6 +60,7 @@ import LoadingError from "../sqlActivity/errorComponent"; import { Loading } from "../loading"; import { UIConfigState } from "../store"; import { QuoteIdentifier } from "../api/safesql"; +import { Timestamp } from "../timestamp"; const cx = classNames.bind(styles); const booleanSettingCx = classnames.bind(booleanSettingStyles); @@ -306,9 +306,13 @@ export class DatabaseTablePage extends React.Component< private getLastResetString() { 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: + + ); } } @@ -316,11 +320,14 @@ export class DatabaseTablePage extends React.Component< // 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}:{" "} + + + ); } private renderIndexRecommendations = ( @@ -420,7 +427,7 @@ export class DatabaseTablePage extends React.Component< }, { name: "last used", - title: "Last Used (UTC)", + title: "Last Used", hideTitleUnderline: true, className: cx("index-stats-table__col-last-used"), cell: indexStat => this.getLastUsedString(indexStat), @@ -566,9 +573,12 @@ export class DatabaseTablePage extends React.Component< {this.props.details.statsLastUpdated && ( + } /> )} {this.props.automaticStatsCollectionEnabled != diff --git a/pkg/ui/workspaces/cluster-ui/src/dateRangeMenu/dateRangeMenu.tsx b/pkg/ui/workspaces/cluster-ui/src/dateRangeMenu/dateRangeMenu.tsx index 86fe826f08b0..b0707edb9e99 100644 --- a/pkg/ui/workspaces/cluster-ui/src/dateRangeMenu/dateRangeMenu.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/dateRangeMenu/dateRangeMenu.tsx @@ -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"; @@ -21,6 +21,7 @@ import { Button } from "src/button"; import { Text, TextTypes } from "src/text"; import styles from "./dateRangeMenu.module.scss"; +import { CoordinatedUniversalTime, TimezoneContext } from "../contexts"; const cx = classNames.bind(styles); @@ -44,6 +45,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 @@ -66,9 +69,11 @@ export function DateRangeMenu({ * the parent component to re-initialize this. */ const [startMoment, setStartMoment] = useState( - startInit || moment.utc(), + startInit ? startInit.tz(timezone) : moment.tz(timezone), + ); + const [endMoment, setEndMoment] = useState( + endInit ? endInit.tz(timezone) : moment.tz(timezone), ); - const [endMoment, setEndMoment] = useState(endInit || moment.utc()); const onChangeStart = (m?: Moment) => { m && setStartMoment(m); @@ -99,9 +104,15 @@ 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()); }; + const timezoneLabel = + timezone.toLowerCase() === CoordinatedUniversalTime.toLowerCase() + ? "UTC" + : timezone; + return (
@@ -111,7 +122,7 @@ export function DateRangeMenu({
- Start (UTC) + Start ({timezoneLabel})
- End (UTC) + End ({timezoneLabel}) { const options = getBarChartOpts( userOptions, @@ -95,6 +96,7 @@ export const getStackedBarOpts = ( yAxisDomain, yyAxisUnits, colourPalette, + timezone, ); options.bands = getStackedBands(unstackedData, () => false); @@ -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); @@ -191,7 +194,7 @@ export const getBarChartOpts = ( ...s, })), ], - plugins: [barTooltipPlugin(yAxisUnits)], + plugins: [barTooltipPlugin(yAxisUnits, timezone)], }; const combinedOpts = merge(opts, providedOpts); diff --git a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/index.tsx b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/index.tsx index 12280349ef89..8f6470ccc5d2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/index.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/index.tsx @@ -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"; @@ -20,6 +20,7 @@ import { calculateYAxisDomain, } from "../utils/domain"; import { Options } from "uplot"; +import { TimezoneContext } from "../../contexts"; const cx = classNames.bind(styles); @@ -46,6 +47,7 @@ export const BarGraphTimeSeries: React.FC = ({ const graphRef = useRef(null); const samplingIntervalMillis = alignedData[0].length > 1 ? alignedData[0][1] - alignedData[0][0] : 1e3; + const timezone = useContext(TimezoneContext); useEffect(() => { if (!alignedData) return; @@ -69,6 +71,7 @@ export const BarGraphTimeSeries: React.FC = ({ yAxisDomain, yAxisUnits, colourPalette, + timezone, ); const plot = new uPlot(opts, stackedData, graphRef.current); @@ -82,6 +85,7 @@ export const BarGraphTimeSeries: React.FC = ({ uPlotOptions, yAxisUnits, samplingIntervalMillis, + timezone, ]); return ( diff --git a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/plugins.ts b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/plugins.ts index aad3690adeed..1964b703cb85 100644 --- a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/plugins.ts +++ b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/plugins.ts @@ -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"; @@ -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"), @@ -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); diff --git a/pkg/ui/workspaces/cluster-ui/src/graphs/utils/domain.ts b/pkg/ui/workspaces/cluster-ui/src/graphs/utils/domain.ts index 37fd4728aa05..53fd82c71c28 100644 --- a/pkg/ui/workspaces/cluster-ui/src/graphs/utils/domain.ts +++ b/pkg/ui/workspaces/cluster-ui/src/graphs/utils/domain.ts @@ -15,8 +15,9 @@ import { BytesFitScale, ComputeByteScale, ComputeDurationScale, - DATE_WITH_SECONDS_FORMAT_24_UTC, + DATE_WITH_SECONDS_FORMAT_24_TZ, DurationFitScale, + FormatWithTimezone, } from "src/util/format"; /** @@ -264,11 +265,7 @@ const timeIncrements: number[] = timeIncrementDurations.map(inc => inc.asMilliseconds(), ); -export function formatTimeStamp(timeMillis: number): string { - return moment.utc(timeMillis).format(DATE_WITH_SECONDS_FORMAT_24_UTC); -} - -function ComputeTimeAxisDomain(extent: Extent): AxisDomain { +function ComputeTimeAxisDomain(extent: Extent, timezone: string): AxisDomain { // Compute increment; for time scales, this is taken from a table of allowed // values. let increment = 0; @@ -288,17 +285,25 @@ function ComputeTimeAxisDomain(extent: Extent): AxisDomain { axisDomain.label = "time"; - let tickDateFormatter: (d: Date) => string; - if (increment < moment.duration(24, "hours").asMilliseconds()) { - tickDateFormatter = (d: Date) => moment.utc(d).format("H:mm"); - } else { - tickDateFormatter = (d: Date) => moment.utc(d).format("MM/DD H:mm"); - } + const tickDateFormatter = (d: Date, format: string) => + moment(d).tz(timezone).format(format); + + const format = + increment < moment.duration(24, "hours").asMilliseconds() + ? "H:mm" + : "MM/DD H:mm"; + axisDomain.tickFormat = (n: number) => { - return tickDateFormatter(new Date(n)); + return tickDateFormatter(new Date(n), format); }; - axisDomain.guideFormat = formatTimeStamp; + axisDomain.guideFormat = millis => { + return FormatWithTimezone( + moment(millis), + DATE_WITH_SECONDS_FORMAT_24_TZ, + timezone, + ); + }; return axisDomain; } @@ -327,19 +332,21 @@ export function calculateYAxisDomain( export function calculateXAxisDomain( startMillis: number, endMillis: number, + timezone = "UTC", ): AxisDomain { - return ComputeTimeAxisDomain([startMillis, endMillis] as Extent); + return ComputeTimeAxisDomain([startMillis, endMillis] as Extent, timezone); } export function calculateXAxisDomainBarChart( startMillis: number, endMillis: number, samplingIntervalMillis: number, + timezone = "UTC", ): AxisDomain { // For bar charts, we want to render past endMillis to fully render the // last bar. We should extend the x axis to the next sampling interval. - return ComputeTimeAxisDomain([ - startMillis, - endMillis + samplingIntervalMillis, - ] as Extent); + return ComputeTimeAxisDomain( + [startMillis, endMillis + samplingIntervalMillis] as Extent, + timezone, + ); } diff --git a/pkg/ui/workspaces/cluster-ui/src/index.ts b/pkg/ui/workspaces/cluster-ui/src/index.ts index eee101fdb08f..325ed2e36316 100644 --- a/pkg/ui/workspaces/cluster-ui/src/index.ts +++ b/pkg/ui/workspaces/cluster-ui/src/index.ts @@ -54,3 +54,4 @@ export * from "./recentExecutions"; export * from "./graphs"; export * from "./selectors"; export * from "./contexts"; +export * from "./timestamp"; diff --git a/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx index 228473cae837..42247044ffdc 100644 --- a/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx @@ -34,7 +34,7 @@ import { Anchor } from "../anchor"; import { calculateTotalWorkload, Count, - DATE_FORMAT_24_UTC, + DATE_FORMAT_24_TZ, EncodeDatabaseTableIndexUri, EncodeDatabaseTableUri, EncodeDatabaseUri, @@ -72,7 +72,7 @@ import { Filters, } from "../queryFilter"; import { commonStyles } from "../common"; -import { Loading } from "src"; +import { Loading, Timestamp } from "src"; import LoadingError from "../sqlActivity/errorComponent"; import { INTERNAL_APP_NAME_PREFIX } from "src/util/constants"; import { filteredStatementsData } from "../sqlActivity/util"; @@ -303,12 +303,12 @@ export class IndexDetailsPage extends React.Component< } } - private getTimestampString(timestamp: Moment): string { + private getTimestampString(timestamp: Moment) { const minDate = moment.utc("0001-01-01"); // minimum value as per UTC if (timestamp.isSame(minDate)) { - return "Never"; + return <>Never; } else { - return timestamp.format(DATE_FORMAT_24_UTC); + return ; } } diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/insightDetailsTables.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/insightDetailsTables.tsx index ba282b5b57fb..2db5462eb5f7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/insightDetailsTables.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/insightDetailsTables.tsx @@ -10,7 +10,10 @@ import React, { useState } from "react"; import { ColumnDescriptor, SortedTable, SortSetting } from "src/sortedtable"; -import { DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT, Duration } from "src/util"; +import { + DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_TZ, + Duration, +} from "src/util"; import { ContentionDetails, ContentionEvent, InsightExecEnum } from "../types"; import { insightsTableTitles, @@ -19,6 +22,7 @@ import { TransactionDetailsLink, } from "../workloadInsights/util"; import { TimeScale } from "../../timeScaleDropdown"; +import { Timestamp } from "../../timestamp"; interface InsightDetailsTableProps { data: ContentionEvent[]; @@ -70,7 +74,14 @@ export function makeInsightDetailsColumns( name: "contentionStartTime", title: insightsTableTitles.contentionStartTime(execType), cell: (item: ContentionEvent) => - item.startTime?.format(DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT), + item.startTime ? ( + + ) : ( + <>N/A + ), sort: (item: ContentionEvent) => item.startTime.unix(), }, { diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/statementInsightDetailsOverviewTab.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/statementInsightDetailsOverviewTab.tsx index 41a3c899d938..fa50ab5ef7cf 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/statementInsightDetailsOverviewTab.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsightDetails/statementInsightDetailsOverviewTab.tsx @@ -17,7 +17,7 @@ import { SummaryCard, SummaryCardItem } from "src/summaryCard"; import { capitalize, Duration } from "src/util"; import { Count, - DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_UTC, + DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_TZ, } from "src/util/format"; import { StmtInsightEvent } from "../types"; import classNames from "classnames/bind"; @@ -39,6 +39,7 @@ import { ContentionStatementDetailsTable } from "./insightDetailsTables"; import { WaitTimeInsightsLabels } from "../../detailsPanels/waitTimeInsightsPanel"; import { Heading } from "@cockroachlabs/ui-components"; import { SortSetting } from "../../sortedtable"; +import { Timestamp } from "../../timestamp"; const cx = classNames.bind(insightsDetailsStyles); const tableCx = classNames.bind(insightTableStyles); @@ -102,15 +103,21 @@ export const StatementInsightDetailsOverviewTab: React.FC< + } /> + } /> + } /> + } /> [] = [ { @@ -52,16 +53,30 @@ const stmtColumns: ColumnDescriptor[] = [ }, { name: "startTime", - title: "Start Time (UTC)", + title: "Start Time", cell: (item: StmtInsightEvent) => - item.startTime?.format(DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT), + item.startTime ? ( + + ) : ( + <>N/A + ), sort: (item: StmtInsightEvent) => item.startTime.unix(), }, { name: "endTime", - title: "End Time (UTC)", + title: "End Time", cell: (item: StmtInsightEvent) => - item.endTime?.format(DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT), + item.endTime ? ( + + ) : ( + <>N/A + ), sort: (item: StmtInsightEvent) => item.endTime.unix(), }, { diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsTable.tsx index e0b6130788e2..4728d701b7de 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsTable.tsx @@ -17,7 +17,7 @@ import { } from "src/sortedtable"; import { Count, - DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT, + DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_TZ, Duration, limitText, NO_SAMPLES_FOUND, @@ -38,6 +38,7 @@ import classNames from "classnames/bind"; import styles from "../util/workloadInsights.module.scss"; import { TimeScale } from "../../../timeScaleDropdown"; import { Badge } from "src/badge"; +import { Timestamp } from "../../../timestamp"; const cx = classNames.bind(styles); @@ -117,7 +118,14 @@ export function makeStatementInsightsColumns(): ColumnDescriptor - item.startTime?.format(DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT), + item.startTime ? ( + + ) : ( + <>N/A + ), sort: (item: StmtInsightEvent) => item.startTime.unix(), showByDefault: true, }, diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsTable.tsx index e1ee23fe335d..8b5efb50ccca 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsTable.tsx @@ -15,7 +15,10 @@ import { SortedTable, SortSetting, } from "src/sortedtable"; -import { DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT, Duration } from "src/util"; +import { + DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_TZ, + Duration, +} from "src/util"; import { InsightExecEnum, TransactionStatus, @@ -30,6 +33,7 @@ import { import { Link } from "react-router-dom"; import { TimeScale } from "../../../timeScaleDropdown"; import { Badge } from "src/badge"; +import { Timestamp } from "../../../timestamp"; function txnStatusToString(status: TransactionStatus) { switch (status) { @@ -99,8 +103,14 @@ export function makeTransactionInsightsColumns(): ColumnDescriptor - item.startTime?.format(DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT) ?? - "N/A", + item.startTime ? ( + + ) : ( + <>N/A + ), sort: item => item.startTime?.unix() || 0, }, { diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/util/insightsColumns.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/util/insightsColumns.tsx index a71f009712ee..e4e77a3d425f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/util/insightsColumns.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/util/insightsColumns.tsx @@ -23,7 +23,7 @@ export const insightsColumnLabels = { query: "Execution", status: "Status", insights: "Insights", - startTime: "Start Time (UTC)", + startTime: "Start Time", elapsedTime: "Elapsed Time", applicationName: "Application Name", username: "User Name", @@ -31,7 +31,7 @@ export const insightsColumnLabels = { numRetries: "Retries", isFullScan: "Full Scan", contention: "Contention Time", - contentionStartTime: "Contention Start Time (UTC)", + contentionStartTime: "Contention Start Time", rowsProcessed: "Rows Processed", schemaName: "Schema Name", databaseName: "Database Name", diff --git a/pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetails.tsx index 72507998bb0f..586afe7fc727 100644 --- a/pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/jobs/jobDetailsPage/jobDetails.tsx @@ -24,7 +24,7 @@ import { SummaryCard, SummaryCardItem } from "src/summaryCard"; import { TimestampToMoment, idAttr, - DATE_FORMAT_24_UTC, + DATE_FORMAT_24_TZ, getMatchParamByName, } from "src/util"; @@ -37,6 +37,7 @@ import summaryCardStyles from "src/summaryCard/summaryCard.module.scss"; import jobStyles from "src/jobs/jobs.module.scss"; import classNames from "classnames/bind"; +import { Timestamp } from "../../timestamp"; const cardCx = classNames.bind(summaryCardStyles); const jobCx = classNames.bind(jobStyles); @@ -116,7 +117,7 @@ export class JobDetails extends React.Component {

Next Planned Execution Time:

- {nextRun.format(DATE_FORMAT_24_UTC)} + )}
@@ -125,15 +126,21 @@ export class JobDetails extends React.Component { + } /> + } /> { "Status", "Job ID", "User Name", - "Creation Time (UTC)", - "Last Execution Time (UTC)", + "Creation Time", + "Last Execution Time", "Execution Count", ]; diff --git a/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsPage.tsx index 4d23b6d482b9..b016f5984c19 100644 --- a/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsPage.tsx @@ -24,7 +24,7 @@ import ColumnsSelector, { } from "src/columnsSelector/columnsSelector"; import { Pagination, ResultsPerPageLabel } from "src/pagination"; import { isSelectedColumn } from "src/columnsSelector/utils"; -import { DATE_FORMAT_24_UTC, syncHistory, TimestampToMoment } from "src/util"; +import { DATE_FORMAT_24_TZ, syncHistory, TimestampToMoment } from "src/util"; import { jobsColumnLabels, JobsTable, makeJobsColumns } from "./jobsTable"; import { showOptions, @@ -39,6 +39,7 @@ import { commonStyles } from "src/common"; import sortableTableStyles from "src/sortedtable/sortedtable.module.scss"; import styles from "../jobs.module.scss"; import classNames from "classnames/bind"; +import { Timestamp } from "../../timestamp"; const cx = classNames.bind(styles); const sortableTableCx = classNames.bind(sortableTableStyles); @@ -241,10 +242,16 @@ export class JobsPage extends React.Component { ); }; - formatJobsRetentionMessage = (earliestRetainedTime: ITimestamp): string => { - return `Since ${TimestampToMoment(earliestRetainedTime).format( - DATE_FORMAT_24_UTC, - )}`; + formatJobsRetentionMessage = (earliestRetainedTime: ITimestamp) => { + return ( + <> + Since{" "} + + + ); }; render(): React.ReactElement { diff --git a/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsTable.tsx index 5f9c72ca6109..0f34b6d774a4 100644 --- a/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/jobs/jobsPage/jobsTable.tsx @@ -26,13 +26,14 @@ import { pauseJob, resumeJob, } from "src/util/docs"; -import { DATE_FORMAT_24_UTC } from "src/util/format"; +import { DATE_FORMAT_24_TZ } from "src/util/format"; import { HighwaterTimestamp, JobStatusCell } from "../util"; import { JobDescriptionCell } from "./jobDescriptionCell"; import styles from "../jobs.module.scss"; import classNames from "classnames/bind"; +import { Timestamp } from "../../timestamp"; const cx = classNames.bind(styles); type Job = cockroach.server.serverpb.IJobResponse; @@ -52,9 +53,9 @@ export const jobsColumnLabels: any = { status: "Status", jobId: "Job ID", users: "User Name", - creationTime: "Creation Time (UTC)", - lastModifiedTime: "Last Modified Time (UTC)", - lastExecutionTime: "Last Execution Time (UTC)", + creationTime: "Creation Time", + lastModifiedTime: "Last Modified Time", + lastExecutionTime: "Last Execution Time", executionCount: "Execution Count", highWaterTimestamp: "High-water Timestamp", coordinatorID: "Coordinator Node", @@ -166,7 +167,12 @@ export function makeJobsColumns(): ColumnDescriptor[] { {jobsColumnLabels.creationTime} ), - cell: job => TimestampToMoment(job?.created).format(DATE_FORMAT_24_UTC), + cell: job => ( + + ), sort: job => TimestampToMoment(job?.created).valueOf(), showByDefault: true, }, @@ -181,7 +187,12 @@ export function makeJobsColumns(): ColumnDescriptor[] { {jobsColumnLabels.lastModifiedTime} ), - cell: job => TimestampToMoment(job?.modified).format(DATE_FORMAT_24_UTC), + cell: job => ( + + ), sort: job => TimestampToMoment(job?.modified).valueOf(), showByDefault: true, }, @@ -196,7 +207,12 @@ export function makeJobsColumns(): ColumnDescriptor[] { {jobsColumnLabels.lastExecutionTime} ), - cell: job => TimestampToMoment(job?.last_run).format(DATE_FORMAT_24_UTC), + cell: job => ( + + ), sort: job => TimestampToMoment(job?.last_run).valueOf(), showByDefault: true, }, diff --git a/pkg/ui/workspaces/cluster-ui/src/jobs/util/highwaterTimestamp.tsx b/pkg/ui/workspaces/cluster-ui/src/jobs/util/highwaterTimestamp.tsx index ab5ccd0fa34d..c0b47e1c7417 100644 --- a/pkg/ui/workspaces/cluster-ui/src/jobs/util/highwaterTimestamp.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/jobs/util/highwaterTimestamp.tsx @@ -11,7 +11,8 @@ import { google } from "@cockroachlabs/crdb-protobuf-client"; import { Tooltip } from "@cockroachlabs/ui-components"; import moment from "moment-timezone"; import React from "react"; -import { DATE_FORMAT_24_UTC } from "src/util/format"; +import { DATE_FORMAT_24_TZ } from "src/util/format"; +import { Timestamp } from "../../timestamp"; type ITimestamp = google.protobuf.ITimestamp; @@ -39,7 +40,9 @@ export class HighwaterTimestamp extends React.PureComponent { + } > {this.props.decimalString} diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx index 260c1a109812..1af3efa96ff4 100644 --- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx @@ -13,8 +13,9 @@ import { Tooltip } from "@cockroachlabs/ui-components"; import { ExecutionType, RecentExecution } from "./types"; import { ColumnDescriptor } from "src/sortedtable"; import { Link } from "react-router-dom"; -import { capitalize, DATE_FORMAT, Duration } from "src/util"; +import { capitalize, DATE_FORMAT_24_TZ, Duration } from "src/util"; import { StatusIcon } from "./statusIcon"; +import { Timestamp } from "../timestamp"; export type ExecutionsColumn = | "applicationName" @@ -51,7 +52,7 @@ export const executionsColumnLabels: Record< } }, retries: () => "Retries", - startTime: () => "Start Time (UTC)", + startTime: () => "Start Time", statementCount: () => "Statements", status: () => "Status", timeSpentBlocking: () => "Time Spent Blocking", @@ -182,7 +183,9 @@ function makeRecentExecutionColumns( startTime: { name: "startTime", title: executionsTableTitles.startTime(execType), - cell: (item: RecentExecution) => item.start.format(DATE_FORMAT), + cell: (item: RecentExecution) => ( + + ), sort: (item: RecentExecution) => item.start.unix(), }, elapsedTime: { diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsTable/execContentionTable.tsx b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsTable/execContentionTable.tsx index 6fb4f948649d..083c6341c2f2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsTable/execContentionTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsTable/execContentionTable.tsx @@ -14,8 +14,9 @@ import { ContendedExecution, ExecutionType } from "../types"; import { Link } from "react-router-dom"; import { StatusIcon } from "../statusIcon"; import { executionsTableTitles } from "../execTableCommon"; -import { DATE_FORMAT, Duration, limitText } from "../../util"; +import { DATE_FORMAT_24_TZ, Duration, limitText } from "../../util"; import { Tooltip } from "@cockroachlabs/ui-components"; +import { Timestamp } from "../../timestamp"; const getID = (item: ContendedExecution, execType: ExecutionType) => execType === "transaction" @@ -78,7 +79,7 @@ export function makeContentionColumns( { name: "startTime", title: executionsTableTitles.startTime(execType), - cell: item => item.start.format(DATE_FORMAT), + cell: item => , sort: item => item.start.unix(), }, { diff --git a/pkg/ui/workspaces/cluster-ui/src/schedules/scheduleDetailsPage/scheduleDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/schedules/scheduleDetailsPage/scheduleDetails.tsx index 253ddf5e6707..ced3bd0ffcd5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/schedules/scheduleDetailsPage/scheduleDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/schedules/scheduleDetailsPage/scheduleDetails.tsx @@ -20,13 +20,14 @@ import { Button } from "src/button"; import { Loading } from "src/loading"; import { SqlBox, SqlBoxSize } from "src/sql"; import { SummaryCard, SummaryCardItem } from "src/summaryCard"; -import { DATE_FORMAT_24_UTC, idAttr, getMatchParamByName } from "src/util"; +import { DATE_FORMAT_24_TZ, idAttr, getMatchParamByName } from "src/util"; import { commonStyles } from "src/common"; import summaryCardStyles from "src/summaryCard/summaryCard.module.scss"; import scheduleStyles from "src/schedules/schedules.module.scss"; import classNames from "classnames/bind"; +import { Timestamp } from "../../timestamp"; const cardCx = classNames.bind(summaryCardStyles); const scheduleCx = classNames.bind(scheduleStyles); @@ -75,11 +76,29 @@ export const ScheduleDetails: React.FC = props => { + ) : ( + <>N/A + ) + } /> + ) : ( + <>N/A + ) + } /> {} @@ -101,10 +102,15 @@ const schedulesTableColumns: ColumnDescriptor[] = [ style="tableTitle" content={

Date and time the schedule will next execute.

} > - {"Next Execution Time (UTC)"} + {"Next Execution Time"} ), - cell: schedule => schedule.nextRun?.format(DATE_FORMAT_24_UTC), + cell: schedule => + schedule.nextRun ? ( + + ) : ( + <>N/A + ), sort: schedule => schedule.nextRun?.valueOf(), }, { @@ -157,10 +163,15 @@ const schedulesTableColumns: ColumnDescriptor[] = [ style="tableTitle" content={

Date and time the schedule was created.

} > - {"Creation Time (UTC)"} + {"Creation Time"} ), - cell: schedule => schedule.created?.format(DATE_FORMAT_24_UTC), + cell: schedule => + schedule.created ? ( + + ) : ( + <>N/A + ), sort: schedule => schedule.created?.valueOf(), }, ]; diff --git a/pkg/ui/workspaces/cluster-ui/src/schedules/schedulesPage/schedulesPage.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/schedules/schedulesPage/schedulesPage.spec.tsx index e37f5bd533c8..a128a2aac7ac 100644 --- a/pkg/ui/workspaces/cluster-ui/src/schedules/schedulesPage/schedulesPage.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/schedules/schedulesPage/schedulesPage.spec.tsx @@ -57,8 +57,8 @@ describe("Schedules", () => { "Schedule ID", "Owner", "Recurrence", - "Creation Time (UTC)", - "Next Execution Time (UTC)", + "Creation Time", + "Next Execution Time", "Jobs Running", ]; diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx index f4e2984f1497..f85c3b08639a 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx @@ -26,7 +26,7 @@ import { SummaryCard, SummaryCardItem } from "../summaryCard"; import LoadingError from "../sqlActivity/errorComponent"; import { DurationToMomentDuration, TimestampToMoment } from "src/util/convert"; -import { Bytes, DATE_FORMAT, Count } from "src/util/format"; +import { Bytes, DATE_FORMAT_24_TZ, Count } from "src/util/format"; import { Col, Row } from "antd"; import "antd/lib/col/style"; import "antd/lib/row/style"; @@ -56,6 +56,7 @@ import { commonStyles } from "src/common"; import { CircleFilled } from "../icon"; import { createTimeScaleFromDateRange, TimeScale } from "src/timeScaleDropdown"; import moment from "moment-timezone"; +import { Timestamp } from "../timestamp"; const cx = classNames.bind(styles); const statementsPageCx = classNames.bind(statementsPageStyles); @@ -283,7 +284,7 @@ export class SessionDetails extends React.Component { } /> { + } className={cx("details-item")} /> @@ -364,12 +370,22 @@ export class SessionDetails extends React.Component { + } /> {session.end && ( + } /> )} { ); }; -function formatSessionStart(session: ISession): string { - const formatStr = "MMM DD, YYYY [at] H:mm"; +function formatSessionStart(session: ISession) { const start = moment.unix(Number(session.start.seconds)).utc(); - - return start.format(formatStr); + return ; } -function formatStatementStart(session: ISession): string { +function formatStatementStart(session: ISession) { if (session.active_queries.length == 0) { - return "N/A"; + return <>N/A; } - const formatStr = "MMM DD, YYYY [at] H:mm"; const start = moment .unix(Number(session.active_queries[0].start.seconds)) .utc(); - return start.format(formatStr); + return ; } export function getStatusString(status: Status): string { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx index 0c67216295b6..7ab311eb5b6e 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx @@ -10,7 +10,6 @@ import React from "react"; import { Link } from "react-router-dom"; -import moment from "moment-timezone"; import classnames from "classnames/bind"; import { cockroach } from "@cockroachlabs/crdb-protobuf-client"; import { Button, Icon } from "@cockroachlabs/ui-components"; @@ -31,7 +30,8 @@ import { import { EmptyTable } from "src/empty"; import styles from "./diagnosticsView.module.scss"; import { getBasePath, StatementDiagnosticsReport } from "../../api"; -import { DATE_FORMAT_24_UTC } from "../../util"; +import { DATE_FORMAT_24_TZ } from "../../util"; +import { Timestamp } from "../../timestamp"; export interface DiagnosticsViewStateProps { hasData: boolean; @@ -119,7 +119,9 @@ export class DiagnosticsView extends React.Component< sorter: sortByRequestedAtField, defaultSortOrder: "descend", render: (_text, record) => { - return moment.utc(record.requested_at).format(DATE_FORMAT_24_UTC); + return ( + + ); }, }, { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx index 6a5c16606b8e..8c21647c0349 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx @@ -33,7 +33,7 @@ import { InsightRecommendation, InsightType } from "../../insights"; import { SummaryCard, SummaryCardItem } from "../../summaryCard"; import { Count, - DATE_FORMAT_24_UTC, + DATE_FORMAT_24_TZ, Duration, formatNumberForDisplay, longToInt, @@ -41,6 +41,7 @@ import { TimestampToMoment, } from "../../util"; import { formatIndexes } from "./plansTable"; +import { Timestamp } from "../../timestamp"; const cx = classNames.bind(styles); @@ -163,9 +164,12 @@ function ExplainPlan({ + } /> - TimestampToMoment(item.stats.last_exec_timestamp).format( - DATE_FORMAT_24_UTC, - ), + cell: (item: PlanHashStats) => ( + + ), sort: (item: PlanHashStats) => TimestampToMoment(item.stats.last_exec_timestamp).unix(), }, diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/recentStatementDetailsOverviewTab.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/recentStatementDetailsOverviewTab.tsx index 0cb599e3a629..84863540352f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/recentStatementDetailsOverviewTab.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/recentStatementDetailsOverviewTab.tsx @@ -19,7 +19,7 @@ import { } from "src/recentExecutions"; import { WaitTimeInsightsPanel } from "src/detailsPanels/waitTimeInsightsPanel"; import { StatusIcon } from "src/recentExecutions/statusIcon"; -import { DATE_FORMAT_24_UTC, Duration } from "src/util"; +import { DATE_FORMAT_24_TZ, Duration } from "src/util"; import "antd/lib/col/style"; import "antd/lib/row/style"; @@ -28,6 +28,7 @@ import summaryCardStyles from "src/summaryCard/summaryCard.module.scss"; const summaryCardStylesCx = classNames.bind(summaryCardStyles); import styles from "./statementDetails.module.scss"; +import { Timestamp } from "../timestamp"; const cx = classNames.bind(styles); type Props = { @@ -48,8 +49,13 @@ export const RecentStatementDetailsOverviewTab = ({ + } /> 0; - const period = timeScaleToString(this.props.timeScale); return ( - {this.renderOverviewTabContent(hasTimeout, hasData, period)} + {this.renderOverviewTabContent(hasTimeout, hasData)} - {this.renderExplainPlanTabContent(hasTimeout, hasData, period)} + {this.renderExplainPlanTabContent(hasTimeout, hasData)} {!this.props.isTenant && !this.props.hasViewActivityRedactedRole && ( { if (!hasData) { return this.renderNoDataWithTimeScaleAndSqlBoxTabContent(hasTimeout); @@ -558,9 +557,12 @@ export class StatementDetails extends React.Component< : nodes.map(node => nodeRegions[node]).filter(r => r), // Remove undefined / unknown regions. ).sort(); - const lastExec = - stats.last_exec_timestamp && - TimestampToMoment(stats.last_exec_timestamp).format(DATE_FORMAT_24_UTC); + const lastExec = stats.last_exec_timestamp && ( + + ); const statementSampled = stats.exec_stats.count > Long.fromNumber(0); const unavailableTooltip = !statementSampled && ( @@ -677,7 +679,9 @@ export class StatementDetails extends React.Component<

Showing aggregated stats from{" "} - {period} + + +

@@ -822,7 +826,6 @@ export class StatementDetails extends React.Component< renderExplainPlanTabContent = ( hasTimeout: boolean, hasData: boolean, - period: string, ): React.ReactElement => { if (!hasData) { return this.renderNoDataWithTimeScaleAndSqlBoxTabContent(hasTimeout); @@ -842,7 +845,9 @@ export class StatementDetails extends React.Component<

Showing explain plans from{" "} - {period} + + +

diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index 88c45af1b912..3ac87c4e8afd 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -67,7 +67,6 @@ import { getValidOption, TimeScale, timeScale1hMinOptions, - timeScaleToString, toRoundedDateRange, } from "../timeScaleDropdown"; @@ -95,6 +94,7 @@ import { import { SearchCriteria } from "src/searchCriteria/searchCriteria"; import timeScaleStyles from "../timeScaleDropdown/timeScale.module.scss"; import { RequestState } from "src/api/types"; +import { TimeScaleToString } from "../timeScaleDropdown/timeScaleToString"; const cx = classNames.bind(styles); const sortableTableCx = classNames.bind(sortableTableStyles); @@ -568,7 +568,7 @@ export class StatementsPage extends React.Component< isSelectedColumn(userSelectedColumnsToShow, c), ); - const period = timeScaleToString(this.props.timeScale); + const period = ; const sortSettingLabel = getSortLabel( this.props.reqSortSetting, "Statement", diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTable.tsx index b7f4e111e006..e7f4c6c73ae5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTable.tsx @@ -22,7 +22,7 @@ import { unset, DurationCheckSample, } from "src/util"; -import { DATE_FORMAT, Duration } from "src/util/format"; +import { DATE_FORMAT_24_TZ, Duration } from "src/util/format"; import { countBarChart, bytesReadBarChart, @@ -53,6 +53,7 @@ type ICollectedStatementStatistics = cockroach.server.serverpb.StatementsResponse.ICollectedStatementStatistics; import styles from "./statementsTable.module.scss"; import { StatementDiagnosticsReport } from "../api"; +import { Timestamp } from "../timestamp"; const cx = classNames.bind(styles); export interface AggregateStatistics { @@ -318,8 +319,12 @@ export function makeStatementsColumns( { name: "lastExecTimestamp", title: statisticsTableTitles.lastExecTimestamp(statType), - cell: (stmt: AggregateStatistics) => - TimestampToMoment(stmt.stats.last_exec_timestamp).format(DATE_FORMAT), + cell: (stmt: AggregateStatistics) => ( + + ), sort: (stmt: AggregateStatistics) => TimestampToNumber(stmt.stats.last_exec_timestamp), showByDefault: false, diff --git a/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx b/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx index 0bbeae40c7c6..c50184f953e0 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx @@ -37,7 +37,7 @@ export const statisticsColumnLabels = { database: "Database", diagnostics: "Diagnostics", executionCount: "Execution Count", - lastExecTimestamp: "Last Execution Time (UTC)", + lastExecTimestamp: "Last Execution Time", latencyMax: "Max Latency", latencyMin: "Min Latency", latencyP50: "P50 Latency", @@ -56,10 +56,10 @@ export const statisticsColumnLabels = { rowsProcessed: "Rows Processed", sessionActiveDuration: "Session Active Duration", sessionDuration: "Session Duration", - sessionStart: "Session Start Time (UTC)", + sessionStart: "Session Start Time", sessionTxnCount: "Transaction Count", statementFingerprintId: "Statement Fingerprint ID", - statementStartTime: "Statement Start Time (UTC)", + statementStartTime: "Statement Start Time", statements: "Statements", statementsCount: "Statements", status: "Status", diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/rangeSelect.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/rangeSelect.tsx index 87adb4f4ca40..8494f3aafc09 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/rangeSelect.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/rangeSelect.tsx @@ -8,7 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useContext } from "react"; import { Button, Dropdown } from "antd"; import "antd/lib/button/style"; import "antd/lib/dropdown/style"; @@ -19,6 +19,7 @@ import classNames from "classnames/bind"; import styles from "./rangeSelector.module.scss"; import { TimeWindow } from "./timeScaleTypes"; +import { CoordinatedUniversalTime, TimezoneContext } from "../contexts"; const cx = classNames.bind(styles); @@ -88,6 +89,7 @@ const RangeSelect = ({ selected, }: RangeSelectProps): React.ReactElement => { const [isVisible, setIsVisible] = useState(false); + const timezone = useContext(TimezoneContext); /** * customDropdownOptionWasJustSelected holds whether the user had just clicked the "Custom time interval" option in * the dropdown menu. @@ -224,7 +226,10 @@ const RangeSelect = ({ {selected.timeEnd} {" "} - (UTC) + {timezone.toLowerCase() === + CoordinatedUniversalTime.toLowerCase() + ? "(UTC)" + : `(${timezone})`} )} diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.spec.tsx index cbcbfd93b983..fad47c4abc68 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.spec.tsx @@ -290,6 +290,7 @@ describe("TimeScaleDropdown functions", function () { const title = formatRangeSelectSelected( currentWindow, state.currentScale, + "UTC", ); assert.deepEqual(title, { key: "Past 10 Minutes", @@ -300,7 +301,11 @@ describe("TimeScaleDropdown functions", function () { it("returns custom Title with Time part only for current day", () => { const currentScale = { ...state.currentScale, key: "Custom" }; - const title = formatRangeSelectSelected(currentWindow, currentScale); + const title = formatRangeSelectSelected( + currentWindow, + currentScale, + "UTC", + ); const timeStart = moment .utc(currentWindow.start) .format(dropdownTimeFormat); @@ -335,7 +340,7 @@ describe("TimeScaleDropdown functions", function () { ), key: "Custom", }; - const title = formatRangeSelectSelected(window, currentScale); + const title = formatRangeSelectSelected(window, currentScale, "UTC"); const timeStart = moment.utc(window.start).format(dropdownTimeFormat); const timeEnd = moment.utc(window.end).format(dropdownTimeFormat); const dateStart = moment.utc(window.start).format(dropdownDateFormat); diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx index 676b26662c58..f6e0c6c98ad9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx @@ -8,7 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -import React, { useMemo } from "react"; +import React, { useContext, useMemo } from "react"; import moment from "moment-timezone"; import classNames from "classnames/bind"; import { @@ -25,6 +25,8 @@ import RangeSelect, { import { defaultTimeScaleOptions, findClosestTimeScale } from "./utils"; import styles from "./timeScale.module.scss"; +import { TimezoneContext } from "../contexts"; +import { FormatWithTimezone } from "../util"; const cx = classNames.bind(styles); @@ -74,6 +76,7 @@ export const getTimeLabel = ( export const formatRangeSelectSelected = ( currentWindow: TimeWindow, currentScale: TimeScale, + timezone: string, ): RangeSelectSelected => { const selected = { timeLabel: getTimeLabel(currentWindow), @@ -92,8 +95,8 @@ export const formatRangeSelectSelected = ( ...selected, dateStart: omitDayFormat ? "" : start.format(dateFormat), dateEnd: omitDayFormat || startEndOnSameDay ? "" : end.format(dateFormat), - timeStart: moment.utc(start).format(timeFormat), - timeEnd: moment.utc(end).format(timeFormat), + timeStart: FormatWithTimezone(start, timeFormat, timezone), + timeEnd: FormatWithTimezone(end, timeFormat, timezone), }; } else { return selected; @@ -139,6 +142,7 @@ export const TimeScaleDropdown: React.FC = ({ start: moment.utc(end).subtract(currentScale.windowSize), end, }; + const timezone = useContext(TimezoneContext); const onPresetOptionSelect = (rangeOption: RangeOption) => { let timeScale: TimeScale = { @@ -257,7 +261,11 @@ export const TimeScaleDropdown: React.FC = ({ return (
{ + const timezone = useContext(TimezoneContext); + + const [start, end] = toRoundedDateRange(props.ts); + const startTz = start.tz(timezone); + const endTz = end.tz(timezone); + const endDayIsToday = endTz.isSame(moment.tz(timezone), "day"); + const startEndOnSameDay = endTz.isSame(startTz, "day"); + const omitDayFormat = endDayIsToday && startEndOnSameDay; + const dateStart = omitDayFormat ? "" : startTz.format(dateFormat); + const dateEnd = + omitDayFormat || startEndOnSameDay ? "" : endTz.format(dateFormat); + const timeStart = startTz.format(timeFormat); + const timeEnd = endTz.format(timeFormat); + + return ( + <> + {dateStart} {timeStart} to {dateEnd} {timeEnd} ({timezone}) + + ); +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts index d193bd335940..8b60823a8b55 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts @@ -10,7 +10,6 @@ import moment from "moment-timezone"; import { TimeScale, TimeScaleOption, TimeScaleOptions } from "./timeScaleTypes"; -import { dateFormat, timeFormat } from "./timeScaleDropdown"; /** * timeScale1hMinOptions is a preconfigured set of time scales with 1h minimum that can be @@ -154,20 +153,6 @@ export const findClosestTimeScale = ( : { ...data[0], key: "Custom" }; }; -export const timeScaleToString = (ts: TimeScale): string => { - const [start, end] = toRoundedDateRange(ts); - const endDayIsToday = moment.utc(end).isSame(moment.utc(), "day"); - const startEndOnSameDay = end.isSame(start, "day"); - const omitDayFormat = endDayIsToday && startEndOnSameDay; - const dateStart = omitDayFormat ? "" : start.format(dateFormat); - const dateEnd = - omitDayFormat || startEndOnSameDay ? "" : end.format(dateFormat); - const timeStart = start.format(timeFormat); - const timeEnd = end.format(timeFormat); - - return `${dateStart} ${timeStart} to ${dateEnd} ${timeEnd} (UTC)`; -}; - /** * Create a fixed TimeScale object from a date range. * @param range date range containing start and end as moment objects diff --git a/pkg/ui/workspaces/cluster-ui/src/timestamp/index.ts b/pkg/ui/workspaces/cluster-ui/src/timestamp/index.ts new file mode 100644 index 000000000000..7aca95b466a2 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/timestamp/index.ts @@ -0,0 +1,11 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +export * from "./timestamp"; diff --git a/pkg/ui/workspaces/cluster-ui/src/timestamp/timestamp.tsx b/pkg/ui/workspaces/cluster-ui/src/timestamp/timestamp.tsx new file mode 100644 index 000000000000..9f0255054928 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/timestamp/timestamp.tsx @@ -0,0 +1,25 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +import { Moment } from "moment-timezone"; +import React, { useContext } from "react"; +import { FormatWithTimezone } from "../util"; +import { TimezoneContext } from "../contexts"; + +export function Timezone(props: any) { + const timezone = useContext(TimezoneContext); + return <>{timezone}; +} + +export function Timestamp(props: { time: Moment; format: string }) { + const timezone = useContext(TimezoneContext); + const { time, format } = props; + return <>{FormatWithTimezone(time, format, timezone)}; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/recentTransactionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/recentTransactionDetails.tsx index 162e55a27ca9..b123137bb2fa 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/recentTransactionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/recentTransactionDetails.tsx @@ -28,11 +28,12 @@ import { import { StatusIcon } from "src/recentExecutions/statusIcon"; import summaryCardStyles from "src/summaryCard/summaryCard.module.scss"; import { getMatchParamByName } from "src/util/query"; -import { executionIdAttr, DATE_FORMAT_24_UTC } from "src/util"; +import { executionIdAttr, DATE_FORMAT_24_TZ } from "src/util"; import styles from "../statementDetails/statementDetails.module.scss"; import { WaitTimeInsightsPanel } from "src/detailsPanels/waitTimeInsightsPanel"; import { capitalize, Duration } from "../util/format"; +import { Timestamp } from "../timestamp"; const cx = classNames.bind(styles); const summaryCardStylesCx = classNames.bind(summaryCardStyles); @@ -50,7 +51,7 @@ const BACK_TO_RECENT_TXNS_BUTTON_LABEL = "Recent Transactions"; const TXN_EXECUTION_ID_LABEL = "Transaction Execution ID"; export const RecentTxnInsightsLabels = { - START_TIME: "Start Time (UTC)", + START_TIME: "Start Time", ELAPSED_TIME: "Elapsed Time", STATUS: "Status", RETRY_COUNT: "Internal Retries", @@ -119,7 +120,12 @@ export const RecentTransactionDetails: React.FC< + } /> ; return (
diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx index 7ce9a56a0aee..697a24c855d1 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -66,7 +66,6 @@ import LoadingError from "../sqlActivity/errorComponent"; import { commonStyles } from "../common"; import { TimeScale, - timeScaleToString, timeScale1hMinOptions, getValidOption, toRoundedDateRange, @@ -84,6 +83,7 @@ import { } from "src/util/sqlActivityConstants"; import { SearchCriteria } from "src/searchCriteria/searchCriteria"; import timeScaleStyles from "../timeScaleDropdown/timeScale.module.scss"; +import { TimeScaleToString } from "../timeScaleDropdown/timeScaleToString"; type IStatementsResponse = protos.cockroach.server.serverpb.IStatementsResponse; @@ -524,7 +524,7 @@ export class TransactionsPage extends React.Component< isSelectedColumn(userSelectedColumnsToShow, c), ); - const period = timeScaleToString(this.props.timeScale); + const period = ; const sortSettingLabel = getSortLabel( this.props.reqSortSetting, "Transaction", diff --git a/pkg/ui/workspaces/cluster-ui/src/util/format.ts b/pkg/ui/workspaces/cluster-ui/src/util/format.ts index 526890b40b91..db663595ffd5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/format.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/format.ts @@ -185,17 +185,14 @@ export const DurationFitScale = }; export const DATE_FORMAT = "MMM DD, YYYY [at] H:mm"; -export const DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT = - "MMM DD, YYYY [at] H:mm:ss:ms"; /** - * Alternate 24 hour UTC formats + * Alternate 24 hour formats */ -export const DATE_FORMAT_24_UTC = "MMM DD, YYYY [at] H:mm UTC"; -export const DATE_WITH_SECONDS_FORMAT_24_UTC = "MMM DD, YYYY [at] H:mm:ss UTC"; +export const DATE_FORMAT_24_TZ = "MMM DD, YYYY [at] H:mm z"; export const DATE_WITH_SECONDS_FORMAT_24_TZ = "MMM DD, YYYY [at] H:mm:ss z"; -export const DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_UTC = - "MMM DD, YYYY [at] H:mm:ss:ms UTC"; +export const DATE_WITH_SECONDS_AND_MILLISECONDS_FORMAT_24_TZ = + "MMM DD, YYYY [at] H:mm:ss:ms z"; export function FormatWithTimezone( m: moment.Moment, diff --git a/pkg/ui/workspaces/cluster-ui/yarn.lock b/pkg/ui/workspaces/cluster-ui/yarn.lock index 9b86130a8dbd..a9ddb4bb1d8d 100644 --- a/pkg/ui/workspaces/cluster-ui/yarn.lock +++ b/pkg/ui/workspaces/cluster-ui/yarn.lock @@ -3583,6 +3583,23 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.2": + version "2.1.8-no-fsevents.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz#e324c0a247a5567192dd7180647709d7e2faf94b" + integrity sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^5.1.2" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://storage.googleapis.com/cockroach-npm-deps/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -5850,6 +5867,14 @@ antd@^3.25.2: shallowequal "^1.1.0" warning "~4.0.3" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + anymatch@^3.0.0: version "3.1.2" resolved "https://storage.googleapis.com/cockroach-npm-deps/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -6149,6 +6174,11 @@ astroturf@^0.10.2: postcss-nested "^4.1.1" resolve "^1.11.1" +async-each@^1.0.1: + version "1.0.6" + resolved "https://storage.googleapis.com/cockroach-npm-deps/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" + integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== + async-validator@~1.11.3: version "1.11.5" resolved "https://storage.googleapis.com/cockroach-npm-deps/async-validator/-/async-validator-1.11.5.tgz#9d43cf49ef6bb76be5442388d19fb9a6e47597ea" @@ -6512,11 +6542,23 @@ big.js@^5.2.2: resolved "https://storage.googleapis.com/cockroach-npm-deps/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://storage.googleapis.com/cockroach-npm-deps/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.5.0: + version "1.5.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bluebird@^3.3.5: version "3.7.2" resolved "https://storage.googleapis.com/cockroach-npm-deps/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -6611,6 +6653,22 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +braces@^2.3.2: + version "2.3.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + braces@^3.0.1: version "3.0.2" resolved "https://storage.googleapis.com/cockroach-npm-deps/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -7169,6 +7227,49 @@ cheerio@^1.0.0-rc.3: normalize-path "~3.0.0" readdirp "~3.6.0" +chokidar@^2.1.8: + version "2.1.8" + resolved "https://storage.googleapis.com/cockroach-npm-deps/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + +chokidar@^3.4.0: + version "3.5.3" + resolved "https://storage.googleapis.com/cockroach-npm-deps/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + +chokidar@^3.4.1: + version "3.5.3" + resolved "https://storage.googleapis.com/cockroach-npm-deps/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + chokidar@^3.4.2: version "3.5.2" resolved "https://storage.googleapis.com/cockroach-npm-deps/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" @@ -7328,6 +7429,15 @@ cli-width@^3.0.0: resolved "https://storage.googleapis.com/cockroach-npm-deps/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +clipboard@^2.0.0: + version "2.0.11" + resolved "https://storage.googleapis.com/cockroach-npm-deps/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5" + integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^5.0.0: version "5.0.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -7427,6 +7537,11 @@ colorette@^1.2.2: resolved "https://storage.googleapis.com/cockroach-npm-deps/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +colors@^1.1.2: + version "1.4.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + combined-stream@^1.0.8: version "1.0.8" resolved "https://storage.googleapis.com/cockroach-npm-deps/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -8376,6 +8491,11 @@ delayed-stream@~1.0.0: resolved "https://storage.googleapis.com/cockroach-npm-deps/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegate@^3.1.2: + version "3.2.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + delegates@^1.0.0: version "1.0.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -8933,6 +9053,13 @@ enzyme@^3.11.0: rst-selector-parser "^2.2.3" string.prototype.trim "^1.2.1" +errno@^0.1.1: + version "0.1.8" + resolved "https://storage.googleapis.com/cockroach-npm-deps/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + errno@^0.1.3: version "0.1.8" resolved "https://storage.googleapis.com/cockroach-npm-deps/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -9138,6 +9265,76 @@ es6-shim@^0.35.5: resolved "https://storage.googleapis.com/cockroach-npm-deps/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" integrity sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA== +esbuild-android-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-android-64/-/esbuild-android-64-0.14.43.tgz#59bf3edad6863c27aa92bbb5c1d83a9a5c981495" + integrity sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg== + +esbuild-android-arm64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-android-arm64/-/esbuild-android-arm64-0.14.43.tgz#0258704edf92ce2463af6d2900b844b5423bed63" + integrity sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA== + +esbuild-darwin-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-darwin-64/-/esbuild-darwin-64-0.14.43.tgz#72a47295678d4aa0656979baa8cf6d5c8c92656f" + integrity sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg== + +esbuild-darwin-arm64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.43.tgz#5f5823170b8d85b888957f0794e186caac447aca" + integrity sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw== + +esbuild-freebsd-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.43.tgz#e4a48b08181053837e6cd9bda19ae0af94d493b0" + integrity sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA== + +esbuild-freebsd-arm64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.43.tgz#386e780d36c1dedf3a1cdab79e0bbacd873274e6" + integrity sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA== + +esbuild-linux-32@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-32/-/esbuild-linux-32-0.14.43.tgz#040ed6b9ebf06d73acdf2acce7f1cd0c12fbc6a5" + integrity sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw== + +esbuild-linux-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-64/-/esbuild-linux-64-0.14.43.tgz#8abbb7594ab6a008f2aae72d95d8a4fdc59d9000" + integrity sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw== + +esbuild-linux-arm64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.43.tgz#4e8e9ce77cbf7efec65e79e512b3d2fbd2da398f" + integrity sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ== + +esbuild-linux-arm@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-arm/-/esbuild-linux-arm-0.14.43.tgz#9e41ee5e099c0ffdfd150da154330c2c0226cc96" + integrity sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg== + +esbuild-linux-mips64le@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.43.tgz#4b41f465a787f91cc4fe7dffa0dcabf655935a1a" + integrity sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA== + +esbuild-linux-ppc64le@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.43.tgz#ca15934f5b46728dd9ac05270e783e7feaca9eaf" + integrity sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA== + +esbuild-linux-riscv64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.43.tgz#70fce2b5a0605a67e58b5a357b0e00be1029836d" + integrity sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw== + +esbuild-linux-s390x@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.43.tgz#318d03b4f4ccc7fa44ac7562121cf4a4529e477a" + integrity sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ== + esbuild-loader@^2.19.0: version "2.19.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-loader/-/esbuild-loader-2.19.0.tgz#54f62d1da8262acfc3c5883c24da35af8324f116" @@ -9150,6 +9347,36 @@ esbuild-loader@^2.19.0: tapable "^2.2.0" webpack-sources "^2.2.0" +esbuild-netbsd-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.43.tgz#86130ce204ef0162a96e863b55851efecc92f423" + integrity sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ== + +esbuild-openbsd-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.43.tgz#0229dc2db2ded97b03bb93bba7646b30ffdf5d0d" + integrity sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg== + +esbuild-sunos-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-sunos-64/-/esbuild-sunos-64-0.14.43.tgz#17e316216eb9f1de25d52a9000356ae5b869e736" + integrity sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q== + +esbuild-windows-32@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-windows-32/-/esbuild-windows-32-0.14.43.tgz#a173757bc6dfd0f2656ff40b64f7f9290745778e" + integrity sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw== + +esbuild-windows-64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-windows-64/-/esbuild-windows-64-0.14.43.tgz#c447b23126aad158c4fe6a394342cafd97926ed1" + integrity sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw== + +esbuild-windows-arm64@0.14.43: + version "0.14.43" + resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.43.tgz#3caed1b430d394d7a7836407b9d36c4750246e76" + integrity sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw== + esbuild@0.14.43: version "0.14.43" resolved "https://storage.googleapis.com/cockroach-npm-deps/esbuild/-/esbuild-0.14.43.tgz#c227d585c512d3e0f23b88f50b8e16501147f647" @@ -9774,6 +10001,11 @@ file-system-cache@^1.0.5: fs-extra "^0.30.0" ramda "^0.21.0" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + filesize@6.1.0: version "6.1.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" @@ -10104,6 +10336,24 @@ fs.realpath@^1.0.0: resolved "https://storage.googleapis.com/cockroach-npm-deps/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@^1.2.7: + version "1.2.13" + resolved "https://storage.googleapis.com/cockroach-npm-deps/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@^2.3.2: + version "2.3.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fsm-iterator@^1.1.0: version "1.1.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/fsm-iterator/-/fsm-iterator-1.1.0.tgz#337de45de19eb205788cf02e3a955ec206760dec" @@ -10503,11 +10753,23 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw== + dependencies: + delegate "^3.1.2" + google-protobuf@^3.6.1: version "3.18.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/google-protobuf/-/google-protobuf-3.18.0.tgz#687449d8e858305d658dc1145852c306d8222f5a" integrity sha512-WlaQWRkUOo/lm9uTgNH6nk9IQt814RggWPzKBfnAVewOFzSzRUSmS1yUWRT6ixW1vS7er5p6tmLSmwzpPpmc8A== +graceful-fs@^4.1.11: + version "4.2.11" + resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graceful-fs@^4.1.15: version "4.2.6" resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -10518,6 +10780,16 @@ graceful-fs@^4.1.2: resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.1.6: + version "4.2.11" + resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graceful-fs@^4.1.9: + version "4.2.11" + resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graceful-fs@^4.2.0: version "4.2.6" resolved "https://storage.googleapis.com/cockroach-npm-deps/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -11029,6 +11301,11 @@ ignore@^5.2.0: resolved "https://storage.googleapis.com/cockroach-npm-deps/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +image-size@~0.5.0: + version "0.5.5" + resolved "https://storage.googleapis.com/cockroach-npm-deps/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== + immer@8.0.1: version "8.0.1" resolved "https://storage.googleapis.com/cockroach-npm-deps/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" @@ -11044,6 +11321,11 @@ immutable@^3.7.4: resolved "https://storage.googleapis.com/cockroach-npm-deps/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= +"immutable@^3.8.1 || ^4.0.0-rc.1": + version "4.3.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + immutable@~3.7.4: version "3.7.6" resolved "https://storage.googleapis.com/cockroach-npm-deps/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" @@ -11332,6 +11614,13 @@ is-bigint@^1.0.1: resolved "https://storage.googleapis.com/cockroach-npm-deps/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://storage.googleapis.com/cockroach-npm-deps/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== + dependencies: + binary-extensions "^1.0.0" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -13251,6 +13540,25 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^3.1.4: + version "3.1.10" + resolved "https://storage.googleapis.com/cockroach-npm-deps/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + micromatch@^4.0.2: version "4.0.4" resolved "https://storage.googleapis.com/cockroach-npm-deps/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -13311,6 +13619,11 @@ mime@1.6.0: resolved "https://storage.googleapis.com/cockroach-npm-deps/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^1.4.1: + version "1.6.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mime@^2.4.4: version "2.5.2" resolved "https://storage.googleapis.com/cockroach-npm-deps/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" @@ -13579,6 +13892,11 @@ mute-stream@0.0.8: resolved "https://storage.googleapis.com/cockroach-npm-deps/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nan@^2.12.1: + version "2.17.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://storage.googleapis.com/cockroach-npm-deps/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -13596,6 +13914,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-request@^1.0.5: + version "1.1.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/native-request/-/native-request-1.1.0.tgz#acdb30fe2eefa3e1bc8c54b3a6852e9c5c0d3cb0" + integrity sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw== + native-url@^0.2.6: version "0.2.6" resolved "https://storage.googleapis.com/cockroach-npm-deps/native-url/-/native-url-0.2.6.tgz#ca1258f5ace169c716ff44eccbddb674e10399ae" @@ -13781,6 +14104,13 @@ normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://storage.googleapis.com/cockroach-npm-deps/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + normalize-path@^3.0.0: version "3.0.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -16652,6 +16982,15 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readdirp@^2.2.1: + version "2.2.1" + resolved "https://storage.googleapis.com/cockroach-npm-deps/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + readdirp@~3.6.0: version "3.6.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -16917,6 +17256,11 @@ remark-squeeze-paragraphs@4.0.0: dependencies: mdast-squeeze-paragraphs "^4.0.0" +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + renderkid@^2.0.4: version "2.0.7" resolved "https://storage.googleapis.com/cockroach-npm-deps/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" @@ -17389,6 +17733,16 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +seamless-immutable@^7.1.3: + version "7.1.4" + resolved "https://storage.googleapis.com/cockroach-npm-deps/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8" + integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A== + +select@^1.1.2: + version "1.1.2" + resolved "https://storage.googleapis.com/cockroach-npm-deps/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA== + "semver@2 || 3 || 4 || 5": version "5.7.1" resolved "https://storage.googleapis.com/cockroach-npm-deps/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -18562,6 +18916,11 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tiny-invariant@^1.0.2: version "1.2.0" resolved "https://storage.googleapis.com/cockroach-npm-deps/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" @@ -19068,6 +19427,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +upath@^1.1.1: + version "1.2.0" + resolved "https://storage.googleapis.com/cockroach-npm-deps/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + uplot@^1.6.19: version "1.6.19" resolved "https://storage.googleapis.com/cockroach-npm-deps/uplot/-/uplot-1.6.19.tgz#98f461992a3f7f3bda7a62f4a028b8afa8da7942" @@ -19340,6 +19704,13 @@ warning@~4.0.3: dependencies: loose-envify "^1.0.0" +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://storage.googleapis.com/cockroach-npm-deps/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + watchpack@^1.7.4: version "1.7.5" resolved "https://storage.googleapis.com/cockroach-npm-deps/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx index c311aa65651b..4a04024690ab 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx @@ -34,6 +34,7 @@ import { TimeScale, Visualization, util, + WithTimezoneProps, } from "@cockroachlabs/cluster-ui"; import uPlot from "uplot"; import "uplot/dist/uPlot.min.css"; @@ -43,6 +44,7 @@ import { findClosestTimeScale, defaultTimeScaleOptions, TimeWindow, + WithTimezone, } from "@cockroachlabs/cluster-ui"; import _ from "lodash"; @@ -59,6 +61,8 @@ export interface LineGraphProps extends MetricsDataComponentProps { preCalcGraphSize?: boolean; } +interface InternalProps extends LineGraphProps, WithTimezoneProps {} + // touPlot formats our timeseries data into the format // uPlot expects which is a 2-dimensional array where the // first array contains the x-values (time). @@ -153,14 +157,14 @@ export function fillGaps( // and store its ref in a global variable. // Once we receive updates to props, we push new data to the // uPlot object. -export class LineGraph extends React.Component { - constructor(props: LineGraphProps) { +export class _LineGraph extends React.Component { + constructor(props: InternalProps) { super(props); this.setNewTimeRange = this.setNewTimeRange.bind(this); } - static defaultProps: Partial = { + static defaultProps: Partial = { // Marking a graph as not being KV-related is opt-in. isKvGraph: true, }; @@ -295,6 +299,7 @@ export class LineGraph extends React.Component { this.xAxisDomain = calculateXAxisDomain( util.NanoToMilli(this.props.timeInfo.start.toNumber()), util.NanoToMilli(this.props.timeInfo.end.toNumber()), + this.props.timezone, ); const prevKeys = @@ -365,3 +370,5 @@ export class LineGraph extends React.Component { ); } } + +export default WithTimezone(_LineGraph); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx index 24cd745d71f9..4d832f9e99cd 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx @@ -8,12 +8,13 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -import { shallow } from "enzyme"; +import { shallow, ShallowWrapper } from "enzyme"; import React from "react"; import uPlot from "uplot"; import _ from "lodash"; -import { fillGaps, LineGraph, LineGraphProps } from "./index"; +import LineGraph, { _LineGraph as LineGraphInternal } from "./index"; +import { fillGaps, LineGraphProps } from "./index"; import * as timewindow from "src/redux/timeScale"; import * as protos from "src/js/protos"; import { Axis } from "src/views/shared/components/metricQuery"; @@ -73,13 +74,17 @@ describe("", function () { it("should render a root component on mount", () => { const wrapper = linegraph({ ...mockProps }); - const root = wrapper.find(".linegraph"); + const root = wrapper.dive().find(".linegraph"); expect(root.length).toBe(1); }); it("should set a new chart on update", () => { - const wrapper = linegraph({ ...mockProps }); - const instance = wrapper.instance() as any as LineGraph; + const wrapper = linegraph({ ...mockProps }).dive() as ShallowWrapper< + any, + Readonly<{}>, + React.Component<{}, {}, any> + >; + const instance = wrapper.instance() as any as LineGraphInternal; wrapper.setProps({ data: { results: [ @@ -107,8 +112,12 @@ describe("", function () { const wrapper = linegraph({ ...mockProps, data: { results: [{}], toJSON: jest.fn() }, - }); - const instance = wrapper.instance() as unknown as LineGraph; + }).dive() as ShallowWrapper< + any, + Readonly<{}>, + React.Component<{}, {}, any> + >; + const instance = wrapper.instance() as unknown as LineGraphInternal; const mockFn = jest.fn(); const mockMetrics = [ { diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/events.spec.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/events.spec.tsx index 6df9320ce8a0..d1d1d8ff4be4 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/events.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/events.spec.tsx @@ -96,7 +96,7 @@ describe("getEventInfo", function () { const event: clusterUiApi.EventColumns = createEventWithEventType(eventType); const eventContent = shallow( - getEventInfo(event).content as React.ReactElement, + getEventInfo(event, "UTC").content as React.ReactElement, ); expect(eventContent.text()).not.toMatch(/Unknown event type/); }); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/index.tsx index 453223667a95..e8687da83ef1 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/events/index.tsx @@ -10,7 +10,7 @@ import _ from "lodash"; import moment from "moment-timezone"; -import React from "react"; +import React, { useContext } from "react"; import { Helmet } from "react-helmet"; import { Link, RouteComponentProps, withRouter } from "react-router-dom"; import { connect } from "react-redux"; @@ -31,6 +31,8 @@ import { SortedTable, util, api as clusterUiApi, + TimezoneContext, + WithTimezone, } from "@cockroachlabs/cluster-ui"; import { InlineAlert } from "@cockroachlabs/ui-components"; import "./events.styl"; @@ -56,10 +58,17 @@ export interface EventRowProps { event: clusterUiApi.EventColumns; } -export function getEventInfo(e: clusterUiApi.EventColumns): SimplifiedEvent { +export function getEventInfo( + e: clusterUiApi.EventColumns, + timezone: string, +): SimplifiedEvent { return { - fromNowString: moment(e.timestamp) - .format(util.DATE_FORMAT_24_UTC) + fromNowString: util + .FormatWithTimezone( + moment.utc(e.timestamp), + util.DATE_FORMAT_24_TZ, + timezone, + ) .replace("second", "sec") .replace("minute", "min"), content: {getEventDescription(e)}, @@ -67,22 +76,21 @@ export function getEventInfo(e: clusterUiApi.EventColumns): SimplifiedEvent { }; } -export class EventRow extends React.Component { - render() { - const { event } = this.props; - const e = getEventInfo(event); - return ( - - - -
{e.content}
-
-
{e.fromNowString}
- - - ); - } -} +export const EventRow = (props: EventRowProps) => { + const { event } = props; + const timezone = useContext(TimezoneContext) as string; + const e = getEventInfo(event, timezone); + return ( + + + +
{e.content}
+
+
{e.fromNowString}
+ + + ); +}; export interface EventBoxProps { events: clusterUiApi.EventsResponse; @@ -137,6 +145,7 @@ export interface EventPageProps { setSort: typeof eventsSortSetting.set; lastError: Error; maxSizeApiReached: boolean; + timezone: string; } export class EventPageUnconnected extends React.Component { @@ -152,7 +161,9 @@ export class EventPageUnconnected extends React.Component { renderContent() { const { events, sortSetting, maxSizeApiReached } = this.props; - const simplifiedEvents = _.map(events, getEventInfo); + const simplifiedEvents = _.map(events, event => { + return getEventInfo(event, this.props.timezone); + }); return ( <> @@ -243,7 +254,7 @@ const eventPageConnected = withRouter( refreshEvents, setSort: eventsSortSetting.set, }, - )(EventPageUnconnected), + )(WithTimezone(EventPageUnconnected)), ); export { eventBoxConnected as EventBox }; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/changefeeds.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/changefeeds.tsx index e79393d1727c..8ea5f861c824 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/changefeeds.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/changefeeds.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx index ec86511b5cd9..f31970aadbaa 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/distributed.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/distributed.tsx index dbd5ffb8b2f3..6a53a0b5a5a8 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/distributed.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/distributed.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/hardware.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/hardware.tsx index aabec09377cb..a2c8602e1125 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/hardware.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/hardware.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overload.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overload.tsx index 1d261991a5c3..e3d63ee61e34 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overload.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overload.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overview.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overview.tsx index b8aab499a08a..f358adb8a205 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overview.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/overview.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Axis, Metric } from "src/views/shared/components/metricQuery"; import { diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/queues.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/queues.tsx index 598454cb86a8..840bf6c38775 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/queues.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/queues.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/replication.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/replication.tsx index 09656e63eefa..ef7a4d918813 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/replication.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/replication.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Axis, Metric } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/requests.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/requests.tsx index 34f8739d1064..5ec3b79df7d7 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/requests.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/requests.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { GraphDashboardProps } from "./dashboardUtils"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/runtime.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/runtime.tsx index 6fa0380f9082..17214769b189 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/runtime.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/runtime.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/sql.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/sql.tsx index a7fcca4f4ea3..d9e7c652739f 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/sql.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/sql.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/storage.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/storage.tsx index 58d30ce66a55..6adaee8a662f 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/storage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/storage.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/ttl.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/ttl.tsx index b5820ce0ca3f..a41a18d57444 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/ttl.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/ttl.tsx @@ -11,7 +11,7 @@ import React from "react"; import _ from "lodash"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeLogs/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeLogs/index.tsx index 8746558d9fe2..fb0955508250 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeLogs/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeLogs/index.tsx @@ -23,7 +23,12 @@ import { refreshLogs, refreshNodes } from "src/redux/apiReducers"; import { currentNode } from "src/views/cluster/containers/nodeOverview"; import { CachedDataReducerState } from "src/redux/cachedDataReducer"; import { getDisplayName } from "src/redux/nodes"; -import { Loading, SortedTable, util } from "@cockroachlabs/cluster-ui"; +import { + Loading, + SortedTable, + util, + Timestamp, +} from "@cockroachlabs/cluster-ui"; import { getMatchParamByName } from "src/util/query"; import "./logs.styl"; @@ -54,10 +59,12 @@ export class Logs extends React.Component { { title: "Time", name: "time", - cell: (logEntry: LogEntries) => - util - .LongToMoment(logEntry.time) - .format(util.DATE_WITH_SECONDS_FORMAT_24_UTC), + cell: (logEntry: LogEntries) => ( + + ), }, { title: "Severity", diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeOverview/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeOverview/index.tsx index 1b42e0cf4b56..d7ad7f85f8e6 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeOverview/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeOverview/index.tsx @@ -31,7 +31,7 @@ import { SummaryLabel, SummaryValue, } from "src/views/shared/components/summaryBar"; -import { Button, util } from "@cockroachlabs/cluster-ui"; +import { Button, util, Timestamp } from "@cockroachlabs/cluster-ui"; import { ArrowLeft } from "@cockroachlabs/icons"; import "./nodeOverview.styl"; import { @@ -115,7 +115,7 @@ export class NodeOverview extends React.Component { render() { const { node, nodesSummary } = this.props; - const { Bytes, Percentage, DATE_FORMAT_24_UTC } = util; + const { Bytes, Percentage, DATE_FORMAT_24_TZ } = util; if (!node) { return (
@@ -306,9 +306,12 @@ export class NodeOverview extends React.Component { /> + } /> - record.decommissionedDate.format(util.DATE_FORMAT_24_UTC), + render: (_text: string, record: DecommissionedNodeStatusRow) => ( + + ), }, { key: "status", diff --git a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/messages.tsx b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/messages.tsx index 68f16306bbe0..9b111c167a38 100644 --- a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/messages.tsx +++ b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/messages.tsx @@ -10,7 +10,7 @@ import React from "react"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; import { AxisUnits } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/views/hotRanges/index.tsx b/pkg/ui/workspaces/db-console/src/views/hotRanges/index.tsx index 9774cd878ed0..2bfc30b94f00 100644 --- a/pkg/ui/workspaces/db-console/src/views/hotRanges/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/hotRanges/index.tsx @@ -10,12 +10,18 @@ import { cockroach } from "src/js/protos"; import { useDispatch, useSelector } from "react-redux"; -import React, { useRef, useEffect, useState } from "react"; +import React, { useRef, useEffect, useState, useContext } from "react"; import { Helmet } from "react-helmet"; import { refreshHotRanges } from "src/redux/apiReducers"; import HotRangesTable from "./hotRangesTable"; import ErrorBoundary from "../app/components/errorMessage/errorBoundary"; -import { Loading, Text, Anchor, util } from "@cockroachlabs/cluster-ui"; +import { + Loading, + Text, + Anchor, + util, + TimezoneContext, +} from "@cockroachlabs/cluster-ui"; import classNames from "classnames/bind"; import styles from "./hotRanges.module.styl"; import { @@ -41,6 +47,7 @@ const HotRangesPage = () => { const lastSetAt = useSelector(lastSetAtSelector); const isLoading = useSelector(isLoadingSelector); const nodeIdToLocalityMap = useSelector(selectNodeLocalities); + const timezone = useContext(TimezoneContext); useEffect(() => { if (!isValid) { @@ -91,7 +98,12 @@ const HotRangesPage = () => { } diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx index c184f818bda8..698d551291d8 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx @@ -18,7 +18,7 @@ import { createSelector } from "reselect"; import { refreshMetricMetadata, refreshNodes } from "src/redux/apiReducers"; import { nodesSummarySelector, NodesSummary } from "src/redux/nodes"; import { AdminUIState } from "src/redux/state"; -import { LineGraph } from "src/views/cluster/components/linegraph"; +import LineGraph from "src/views/cluster/components/linegraph"; import { DropdownOption } from "src/views/shared/components/dropdown"; import { MetricsDataProvider } from "src/views/shared/containers/metricDataProvider"; import { Metric, Axis } from "src/views/shared/components/metricQuery"; diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx index 5d27c71cbb0c..173d0c00601d 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx @@ -29,6 +29,7 @@ import { Table, SortSetting, util, + Timestamp, } from "@cockroachlabs/cluster-ui"; import { createSelector } from "reselect"; @@ -88,7 +89,12 @@ export class DecommissionedNodeHistory extends React.Component { - return record.decommissionedDate.format(util.DATE_FORMAT_24_UTC); + return ( + + ); }, }, ]; diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/settings/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/settings/index.tsx index 8a7ae1d85ff3..d0a093e3ecbc 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/settings/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/settings/index.tsx @@ -23,6 +23,7 @@ import { SortedTable, SortSetting, util, + Timestamp, } from "@cockroachlabs/cluster-ui"; import "./index.styl"; import { CachedDataReducerState } from "src/redux/cachedDataReducer"; @@ -109,9 +110,14 @@ export class Settings extends React.Component { name: "lastUpdated", title: "Last Updated", cell: (setting: IterableSetting) => - setting.last_updated - ? setting.last_updated.format(util.DATE_FORMAT_24_UTC) - : "No overrides", + setting.last_updated ? ( + + ) : ( + "No overrides" + ), sort: (setting: IterableSetting) => setting.last_updated?.valueOf(), }, { diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/statementDiagnosticsHistory/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/statementDiagnosticsHistory/index.tsx index 30b570f967fa..646535e057ea 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/statementDiagnosticsHistory/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/statementDiagnosticsHistory/index.tsx @@ -46,6 +46,7 @@ import { SortSetting, ColumnDescriptor, util, + Timestamp, } from "@cockroachlabs/cluster-ui"; import { cancelStatementDiagnosticsReportAction } from "src/redux/statements"; import { trackCancelDiagnosticsBundleAction } from "src/redux/analyticsActions"; @@ -95,8 +96,9 @@ class StatementDiagnosticsHistoryView extends React.Component< { title: "Activated on", name: "activated_on", - cell: record => - moment.utc(record.requested_at).format(util.DATE_FORMAT_24_UTC), + cell: record => ( + + ), sort: record => { return moment.utc(record.requested_at).unix(); },