diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/schemaInsights/schemaInsightsView.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/schemaInsights/schemaInsightsView.tsx index fd3c07d84ac9..4b49858e46e0 100644 --- a/pkg/ui/workspaces/cluster-ui/src/insights/schemaInsights/schemaInsightsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/insights/schemaInsights/schemaInsightsView.tsx @@ -136,7 +136,7 @@ export const SchemaInsightsView: React.FC = ({ // redux changes and syncs the URL params with redux. syncHistory( { - ascending: sortSetting.ascending.toString(), + ascending: sortSetting.ascending?.toString(), columnTitle: sortSetting.columnTitle, ...getFullFiltersAsStringRecord(filters), [SCHEMA_INSIGHT_SEARCH_PARAM]: search, diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts index 1710bafc6c51..c63da702fd4f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts @@ -621,6 +621,7 @@ const statementsPagePropsFixture: StatementsPageProps = { onFilterChange: noop, onChangeLimit: noop, onChangeReqSort: noop, + onApplySearchCriteria: noop, }; export const statementsPagePropsWithRequestError: StatementsPageProps = { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss index b9bb3991d0c8..01f93c8ee3a6 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss @@ -56,6 +56,10 @@ cl-table-container { margin-bottom: 0px; } +.margin-bottom { + margin-bottom: 10px; +} + .root { flex-grow: 0; width: 100%; diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index 91f4ce6ab278..f4904b19d3b7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -21,7 +21,7 @@ import { updateSortSettingQueryParamsOnTab, } from "src/sortedtable"; import { Search } from "src/search"; -import { Pagination } from "src/pagination"; +import { Pagination, ResultsPerPageLabel } from "src/pagination"; import { calculateActiveFilters, defaultFilters, @@ -59,6 +59,7 @@ import { SqlStatsSortType, StatementsRequest, createCombinedStmtsRequest, + SqlStatsSortOptions, } from "src/api/statementsApi"; import ClearStats from "../sqlActivity/clearStats"; import LoadingError from "../sqlActivity/errorComponent"; @@ -83,8 +84,9 @@ import { STATS_LONG_LOADING_DURATION, stmtRequestSortOptions, getSortLabel, + getSortColumn, + getSubsetWarning, } from "src/util/sqlActivityConstants"; -import { Button } from "src/button"; import { SearchCriteria } from "src/searchCriteria/searchCriteria"; import timeScaleStyles from "../timeScaleDropdown/timeScale.module.scss"; @@ -125,6 +127,7 @@ export interface StatementsPageDispatchProps { onTimeScaleChange: (ts: TimeScale) => void; onChangeLimit: (limit: number) => void; onChangeReqSort: (sort: SqlStatsSortType) => void; + onApplySearchCriteria: (ts: TimeScale, limit: number, sort: string) => void; } export interface StatementsPageStateProps { statements: AggregateStatistics[]; @@ -275,6 +278,13 @@ export class StatementsPage extends React.Component< } }; + isSortSettingSameAsReqSort = (): boolean => { + return ( + getSortColumn(this.state.reqSortSetting) == + this.props.sortSetting.columnTitle + ); + }; + changeTimeScale = (ts: TimeScale): void => { this.setState(prevState => ({ ...prevState, @@ -283,10 +293,30 @@ export class StatementsPage extends React.Component< }; updateRequestParams = (): void => { - this.props.onChangeLimit(this.state.limit); - this.props.onChangeReqSort(this.state.reqSortSetting); - this.props.onTimeScaleChange(this.state.timeScale); + if (this.props.limit !== this.state.limit) { + this.props.onChangeLimit(this.state.limit); + } + + if (this.props.reqSortSetting !== this.state.reqSortSetting) { + this.props.onChangeReqSort(this.state.reqSortSetting); + } + + if (this.props.timeScale !== this.state.timeScale) { + this.props.onTimeScaleChange(this.state.timeScale); + } + if (this.props.onApplySearchCriteria) { + this.props.onApplySearchCriteria( + this.state.timeScale, + this.state.limit, + getSortLabel(this.state.reqSortSetting), + ); + } this.refreshStatements(); + const ss: SortSetting = { + ascending: false, + columnTitle: getSortColumn(this.state.reqSortSetting), + }; + this.changeSortSetting(ss); }; resetPagination = (): void => { @@ -478,6 +508,16 @@ export class StatementsPage extends React.Component< this.setState(prevState => ({ ...prevState, reqSortSetting: newSort })); }; + hasReqSortOption = (): boolean => { + let found = false; + Object.values(SqlStatsSortOptions).forEach((option: SqlStatsSortType) => { + if (getSortColumn(option) == this.props.sortSetting.columnTitle) { + found = true; + } + }); + return found; + }; + renderStatements = (): React.ReactElement => { const { pagination, filters, activeFilters } = this.state; const { @@ -598,6 +638,12 @@ export class StatementsPage extends React.Component<

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

{hasAdminRole && ( @@ -620,6 +666,19 @@ export class StatementsPage extends React.Component< onRemoveFilter={this.onSubmitFilters} onClearFilters={this.onClearFilters} /> + {!this.isSortSettingSameAsReqSort() && ( + + )} dispatch(updateStmsPageReqSortAction(sort)), + onApplySearchCriteria: (ts: TimeScale, limit: number, sort: string) => + dispatch( + analyticsActions.track({ + name: "Apply Search Criteria", + page: "Statements", + tsValue: ts.key, + limitValue: limit, + sortValue: sort, + }), + ), }, activePageProps: mapDispatchToRecentStatementsPageProps(dispatch), }), diff --git a/pkg/ui/workspaces/cluster-ui/src/store/analytics/analytics.reducer.ts b/pkg/ui/workspaces/cluster-ui/src/store/analytics/analytics.reducer.ts index 3f938e9e2ad0..74d299c67d51 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/analytics/analytics.reducer.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/analytics/analytics.reducer.ts @@ -26,6 +26,14 @@ type Page = | "Workload Insights - Statement" | "Workload Insights - Transaction"; +type ApplySearchCriteriaEvent = { + name: "Apply Search Criteria"; + page: Page; + tsValue: string; + limitValue: number; + sortValue: string; +}; + type BackButtonClick = { name: "Back Clicked"; page: Page; @@ -102,6 +110,7 @@ type TimeScaleChangeEvent = { }; type AnalyticsEvent = + | ApplySearchCriteriaEvent | BackButtonClick | ColumnsChangeEvent | FilterEvent diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx index 683cf90321a8..070c3c6038b9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -24,7 +24,7 @@ import { SortSetting, updateSortSettingQueryParamsOnTab, } from "../sortedtable"; -import { Pagination } from "../pagination"; +import { Pagination, ResultsPerPageLabel } from "../pagination"; import { statisticsClasses } from "./transactionsPageClasses"; import { aggregateAcrossNodeIDs, @@ -54,6 +54,7 @@ import { SqlStatsSortType, createCombinedStmtsRequest, StatementsRequest, + SqlStatsSortOptions, } from "src/api/statementsApi"; import ColumnsSelector from "../columnsSelector/columnsSelector"; import { SelectOption } from "../multiSelectCheckbox/multiSelectCheckbox"; @@ -79,8 +80,9 @@ import { STATS_LONG_LOADING_DURATION, txnRequestSortOptions, getSortLabel, + getSortColumn, + getSubsetWarning, } from "src/util/sqlActivityConstants"; -import { Button } from "src/button"; import { SearchCriteria } from "src/searchCriteria/searchCriteria"; import timeScaleStyles from "../timeScaleDropdown/timeScale.module.scss"; @@ -131,6 +133,7 @@ export interface TransactionsPageDispatchProps { columnTitle: string, ascending: boolean, ) => void; + onApplySearchCriteria: (ts: TimeScale, limit: number, sort: string) => void; } export type TransactionsPageProps = TransactionsPageStateProps & @@ -289,6 +292,13 @@ export class TransactionsPage extends React.Component< } }; + isSortSettingSameAsReqSort = (): boolean => { + return ( + getSortColumn(this.state.reqSortSetting) == + this.props.sortSetting.columnTitle + ); + }; + onChangePage = (current: number): void => { const { pagination } = this.state; this.setState({ pagination: { ...pagination, current } }); @@ -391,10 +401,41 @@ export class TransactionsPage extends React.Component< }; updateRequestParams = (): void => { - this.props.onChangeLimit(this.state.limit); - this.props.onChangeReqSort(this.state.reqSortSetting); - this.props.onTimeScaleChange(this.state.timeScale); + if (this.props.limit !== this.state.limit) { + this.props.onChangeLimit(this.state.limit); + } + + if (this.props.reqSortSetting !== this.state.reqSortSetting) { + this.props.onChangeReqSort(this.state.reqSortSetting); + } + + if (this.props.timeScale !== this.state.timeScale) { + this.props.onTimeScaleChange(this.state.timeScale); + } + + if (this.props.onApplySearchCriteria) { + this.props.onApplySearchCriteria( + this.state.timeScale, + this.state.limit, + getSortLabel(this.state.reqSortSetting), + ); + } this.refreshData(); + const ss: SortSetting = { + ascending: false, + columnTitle: getSortColumn(this.state.reqSortSetting), + }; + this.onChangeSortSetting(ss); + }; + + hasReqSortOption = (): boolean => { + let found = false; + Object.values(SqlStatsSortOptions).forEach((option: SqlStatsSortType) => { + if (getSortColumn(option) == this.props.sortSetting.columnTitle) { + found = true; + } + }); + return found; }; renderTransactions(): React.ReactElement { @@ -488,6 +529,7 @@ export class TransactionsPage extends React.Component< const period = timeScaleToString(this.props.timeScale); const sortSettingLabel = getSortLabel(this.props.reqSortSetting); + return ( <>
@@ -527,6 +569,15 @@ export class TransactionsPage extends React.Component<

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

{hasAdminRole && ( @@ -549,6 +600,19 @@ export class TransactionsPage extends React.Component< onRemoveFilter={this.onSubmitFilters} onClearFilters={this.onClearFilters} /> + {!this.isSortSettingSameAsReqSort() && ( + + )} dispatch(updateTxnsPageReqSortAction(sort)), + onApplySearchCriteria: (ts: TimeScale, limit: number, sort: string) => + dispatch( + analyticsActions.track({ + name: "Apply Search Criteria", + page: "Transactions", + tsValue: ts.key, + limitValue: limit, + sortValue: sort, + }), + ), }, activePageProps: mapDispatchToRecentTransactionsPageProps(dispatch), }), diff --git a/pkg/ui/workspaces/cluster-ui/src/util/sqlActivityConstants.ts b/pkg/ui/workspaces/cluster-ui/src/util/sqlActivityConstants.ts index 65b7fbe66069..1352039eaab5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/sqlActivityConstants.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/sqlActivityConstants.ts @@ -10,6 +10,10 @@ import { duration } from "moment"; import { SqlStatsSortOptions, SqlStatsSortType } from "src/api/statementsApi"; +import { + getLabel, + StatisticTableColumnKeys, +} from "../statsTableUtil/statsTableUtil"; export const limitOptions = [ { value: 25, label: "25" }, @@ -37,6 +41,25 @@ export function getSortLabel(sort: SqlStatsSortType): string { } } +export function getSortColumn(sort: SqlStatsSortType): string { + switch (sort) { + case SqlStatsSortOptions.SERVICE_LAT: + return "time"; + case SqlStatsSortOptions.EXECUTION_COUNT: + return "executionCount"; + case SqlStatsSortOptions.CPU_TIME: + return "cpu"; + case SqlStatsSortOptions.P99_STMTS_ONLY: + return "latencyP99"; + case SqlStatsSortOptions.CONTENTION_TIME: + return "contention"; + case SqlStatsSortOptions.PCT_RUNTIME: + return "workloadPct"; + default: + return ""; + } +} + export const stmtRequestSortOptions = Object.values(SqlStatsSortOptions) .map(sortVal => ({ value: sortVal as SqlStatsSortType, @@ -55,3 +78,18 @@ export const txnRequestSortOptions = stmtRequestSortOptions.filter( ); export const STATS_LONG_LOADING_DURATION = duration(2, "s"); + +export function getSubsetWarning( + type: "statement" | "transaction", + limit: number, + sortLabel: string, + showSuggestion: boolean, + columnTitle: StatisticTableColumnKeys, +): string { + const warningSuggestion = showSuggestion + ? `Update the search criteria to see the ${type} fingerprints + sorted on ${getLabel(columnTitle, type)}.` + : ""; + return `You are viewing a subset (Top ${limit}) of fingerprints by ${sortLabel}. + ${warningSuggestion}`; +} diff --git a/pkg/ui/workspaces/db-console/src/redux/analyticsActions.ts b/pkg/ui/workspaces/db-console/src/redux/analyticsActions.ts index 39c8e380e776..7363c1762a1d 100644 --- a/pkg/ui/workspaces/db-console/src/redux/analyticsActions.ts +++ b/pkg/ui/workspaces/db-console/src/redux/analyticsActions.ts @@ -9,6 +9,7 @@ // licenses/APL.txt. import { PayloadAction } from "src/interfaces/action"; +import { TimeScale } from "@cockroachlabs/cluster-ui"; export const TRACK_STATEMENTS_SEARCH = "cockroachui/analytics/TRACK_STATEMENTS_SEARCH"; @@ -21,6 +22,8 @@ export const TRACK_CANCEL_DIAGNOSTIC_BUNDLE = "cockroachui/analytics/TRACK_CANCEL_DIAGNOSTIC_BUNDLE"; export const TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION = "cockroachui/analytics/TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION"; +export const TRACK_APPLY_SEARCH_CRITERIA = + "cockroachui/analytics/TRACK_APPLY_SEARCH_CRITERIA"; export interface TableSortActionPayload { tableName: string; @@ -28,6 +31,12 @@ export interface TableSortActionPayload { ascending?: boolean; } +export interface ApplySearchCriteriaPayload { + ts: TimeScale; + limit: number; + sort: string; +} + export function trackStatementsSearchAction( searchResults: number, ): PayloadAction { @@ -87,3 +96,18 @@ export function trackStatementDetailsSubnavSelectionAction( payload: tabName, }; } + +export function trackApplySearchCriteriaAction( + ts: TimeScale, + limit: number, + sort: string, +): PayloadAction { + return { + type: TRACK_APPLY_SEARCH_CRITERIA, + payload: { + ts, + limit, + sort, + }, + }; +} diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx index 04adb3a1855e..44c0347d480b 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx @@ -56,6 +56,7 @@ import { setGlobalTimeScaleAction, } from "src/redux/statements"; import { + trackApplySearchCriteriaAction, trackCancelDiagnosticsBundleAction, trackDownloadDiagnosticsBundleAction, trackStatementsPaginationAction, @@ -363,6 +364,7 @@ const fingerprintsPageActions = { ), onChangeLimit: (newLimit: number) => limitSetting.set(newLimit), onChangeReqSort: (sort: api.SqlStatsSortType) => reqSortSetting.set(sort), + onApplySearchCriteria: trackApplySearchCriteriaAction, }; type StateProps = { diff --git a/pkg/ui/workspaces/db-console/src/views/transactions/transactionsPage.tsx b/pkg/ui/workspaces/db-console/src/views/transactions/transactionsPage.tsx index 232d05007f6f..219428954c48 100644 --- a/pkg/ui/workspaces/db-console/src/views/transactions/transactionsPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/transactions/transactionsPage.tsx @@ -50,6 +50,7 @@ import { selectTxnsDataValid, selectTxnsDataInFlight, } from "src/selectors/executionFingerprintsSelectors"; +import { trackApplySearchCriteriaAction } from "src/redux/analyticsActions"; // selectData returns the array of AggregateStatistics to show on the // TransactionsPage, based on if the appAttr route parameter is set. @@ -142,6 +143,7 @@ const fingerprintsPageActions = { onSearchComplete: (query: string) => searchLocalSetting.set(query), onChangeLimit: (newLimit: number) => limitSetting.set(newLimit), onChangeReqSort: (sort: api.SqlStatsSortType) => reqSortSetting.set(sort), + onApplySearchCriteria: trackApplySearchCriteriaAction, }; type StateProps = {