mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
UI – Add and update performance impact features to uitilize metrics that include live query runs (#15642)
Merging during freeze with approval from all stakeholders, including verbal approval from @sharon-fdm Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
parent
b011418b71
commit
1fa5004428
1
changes/467-live-query-stats-frontend
Normal file
1
changes/467-live-query-stats-frontend
Normal file
@ -0,0 +1 @@
|
||||
* Add UI features to incorporate new live query stats
|
@ -2,13 +2,15 @@ import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import PillCell from "./PillCell";
|
||||
import PerformanceImpactCell from "./PerformanceImpactCell";
|
||||
|
||||
const PERFORMANCE_IMPACT = { indicator: "Minimal", id: 3 };
|
||||
|
||||
describe("Pill cell", () => {
|
||||
describe("Query performance cell", () => {
|
||||
it("renders pill text and tooltip on hover", async () => {
|
||||
const { user } = renderWithSetup(<PillCell value={PERFORMANCE_IMPACT} />);
|
||||
const { user } = renderWithSetup(
|
||||
<PerformanceImpactCell value={PERFORMANCE_IMPACT} />
|
||||
);
|
||||
|
||||
await user.hover(screen.getByText("Minimal"));
|
||||
|
@ -5,21 +5,27 @@ import { uniqueId } from "lodash";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
import { COLORS } from "styles/var/colors";
|
||||
|
||||
interface IPillCellProps {
|
||||
value: { indicator: string; id: number };
|
||||
interface IPerformanceImpactCellValue {
|
||||
indicator: string;
|
||||
id?: number;
|
||||
}
|
||||
interface IPerformanceImpactCellProps {
|
||||
value: IPerformanceImpactCellValue;
|
||||
isHostSpecific?: boolean;
|
||||
customIdPrefix?: string;
|
||||
hostDetails?: boolean;
|
||||
}
|
||||
|
||||
const generateClassTag = (rawValue: string): string => {
|
||||
return rawValue.replace(" ", "-").toLowerCase();
|
||||
};
|
||||
|
||||
const PillCell = ({
|
||||
const baseClass = "performance-impact-cell";
|
||||
|
||||
const PerformanceImpactCell = ({
|
||||
value,
|
||||
isHostSpecific = false,
|
||||
customIdPrefix,
|
||||
hostDetails,
|
||||
}: IPillCellProps): JSX.Element => {
|
||||
}: IPerformanceImpactCellProps): JSX.Element => {
|
||||
const { indicator, id } = value;
|
||||
const pillClassName = classnames(
|
||||
"data-table__pill",
|
||||
@ -27,43 +33,34 @@ const PillCell = ({
|
||||
"tooltip"
|
||||
);
|
||||
|
||||
const disable = () => {
|
||||
switch (indicator) {
|
||||
case "Minimal":
|
||||
return false;
|
||||
case "Considerable":
|
||||
return false;
|
||||
case "Excessive":
|
||||
return false;
|
||||
case "Undetermined":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
const disableTooltip = ![
|
||||
"Minimal",
|
||||
"Considerable",
|
||||
"Excessive",
|
||||
"Undetermined",
|
||||
].includes(indicator);
|
||||
|
||||
const tooltipText = () => {
|
||||
switch (indicator) {
|
||||
case "Minimal":
|
||||
return (
|
||||
<>
|
||||
Running this query very <br />
|
||||
frequently has little to no <br /> impact on your device’s <br />
|
||||
performance.
|
||||
Running this query very frequently has little to no <br /> impact on
|
||||
your device's performance.
|
||||
</>
|
||||
);
|
||||
case "Considerable":
|
||||
return (
|
||||
<>
|
||||
Running this query <br /> frequently can have a <br /> noticeable
|
||||
impact on your <br /> device’s performance.
|
||||
Running this query frequently can have a noticeable <br />
|
||||
impact on your device's performance.
|
||||
</>
|
||||
);
|
||||
case "Excessive":
|
||||
return (
|
||||
<>
|
||||
Running this query, even <br /> infrequently, can have a <br />
|
||||
significant impact on your <br /> device’s performance.
|
||||
Running this query, even infrequently, can have a <br />
|
||||
significant impact on your device's performance.
|
||||
</>
|
||||
);
|
||||
case "Denylisted":
|
||||
@ -76,8 +73,9 @@ const PillCell = ({
|
||||
case "Undetermined":
|
||||
return (
|
||||
<>
|
||||
To see performance impact, this query must have run with{" "}
|
||||
<b>automations</b> on {hostDetails ? "this" : "at least one"} host.
|
||||
Performance impact will be available when{" "}
|
||||
{isHostSpecific ? "the" : "this"} <br />
|
||||
query runs{isHostSpecific && " on this host"}.
|
||||
</>
|
||||
);
|
||||
default:
|
||||
@ -87,11 +85,11 @@ const PillCell = ({
|
||||
const tooltipId = uniqueId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={`${baseClass}`}>
|
||||
<span
|
||||
data-tip
|
||||
data-for={`${customIdPrefix || "pill"}__${id?.toString() || tooltipId}`}
|
||||
data-tip-disable={disable()}
|
||||
data-tip-disable={disableTooltip}
|
||||
>
|
||||
<span className={pillClassName}>{indicator}</span>
|
||||
</span>
|
||||
@ -110,8 +108,8 @@ const PillCell = ({
|
||||
{tooltipText()}
|
||||
</span>
|
||||
</ReactTooltip>
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default PillCell;
|
||||
export default PerformanceImpactCell;
|
@ -1,3 +1,9 @@
|
||||
.performance-impact-cell {
|
||||
.__react_component_tooltip {
|
||||
@include tooltip-text;
|
||||
}
|
||||
}
|
||||
|
||||
.data-table__pill {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $bold;
|
@ -0,0 +1 @@
|
||||
export { default } from "./PerformanceImpactCell";
|
@ -1 +0,0 @@
|
||||
export { default } from "./PillCell";
|
@ -16,20 +16,6 @@
|
||||
}
|
||||
|
||||
&__tip-text {
|
||||
width: max-content;
|
||||
max-width: 360px;
|
||||
padding: 6px;
|
||||
color: $core-white;
|
||||
background-color: $tooltip-bg;
|
||||
font-weight: $regular;
|
||||
font-size: $xx-small;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
z-index: 99; // not more than the site nav
|
||||
line-height: 1.375;
|
||||
white-space: initial;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
@include tooltip-text;
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,19 @@ import React from "react";
|
||||
import FleetAce from "components/FleetAce";
|
||||
import Modal from "components/Modal";
|
||||
import Button from "components/buttons/Button";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
|
||||
const baseClass = "show-query-modal";
|
||||
|
||||
interface IShowQueryModalProps {
|
||||
onCancel: () => void;
|
||||
query?: string;
|
||||
impact?: string;
|
||||
}
|
||||
|
||||
const ShowQueryModal = ({
|
||||
query,
|
||||
impact,
|
||||
onCancel,
|
||||
}: IShowQueryModalProps): JSX.Element => {
|
||||
return (
|
||||
@ -30,6 +33,12 @@ const ShowQueryModal = ({
|
||||
wrapEnabled
|
||||
readOnly
|
||||
/>
|
||||
{impact && (
|
||||
<div className={`${baseClass}__performance-impact`}>
|
||||
Performance impact:{" "}
|
||||
<PerformanceImpactCell value={{ indicator: impact }} />
|
||||
</div>
|
||||
)}
|
||||
<div className="modal-cta-wrap">
|
||||
<Button onClick={onCancel} variant="brand">
|
||||
Done
|
||||
|
@ -1,4 +1,8 @@
|
||||
.show-query-modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-large;
|
||||
|
||||
.yaml-ace {
|
||||
min-height: 0;
|
||||
}
|
||||
@ -6,4 +10,24 @@
|
||||
.yaml-ace__label {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&__performance-impact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $pad-small;
|
||||
|
||||
.data-table__pill {
|
||||
font-size: $xxx-small;
|
||||
font-weight: $xbold;
|
||||
padding: 6px 12px;
|
||||
|
||||
.__react_component_tooltip {
|
||||
@include tooltip-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-cta-wrap {
|
||||
margin-top: initial;
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,17 @@
|
||||
import React from "react";
|
||||
import { find } from "lodash";
|
||||
|
||||
import { performanceIndicator, secondsToDhms } from "utilities/helpers";
|
||||
import {
|
||||
getPerformanceImpactDescription,
|
||||
secondsToDhms,
|
||||
} from "utilities/helpers";
|
||||
import { IScheduledQuery } from "interfaces/scheduled_query";
|
||||
import { IDropdownOption } from "interfaces/dropdownOption";
|
||||
|
||||
import Checkbox from "components/forms/fields/Checkbox";
|
||||
import DropdownCell from "components/TableContainer/DataTable/DropdownCell";
|
||||
import HeaderCell from "components/TableContainer/DataTable/HeaderCell/HeaderCell";
|
||||
import PillCell from "components/TableContainer/DataTable/PillCell";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
@ -45,7 +48,7 @@ interface ICellProps extends IRowProps {
|
||||
};
|
||||
}
|
||||
|
||||
interface IPillCellProps extends IRowProps {
|
||||
interface IPerformanceImpactCellProps extends IRowProps {
|
||||
cell: {
|
||||
value: { indicator: string; id: number };
|
||||
};
|
||||
@ -64,7 +67,7 @@ interface IDataColumn {
|
||||
accessor?: string;
|
||||
Cell:
|
||||
| ((props: ICellProps) => JSX.Element)
|
||||
| ((props: IPillCellProps) => JSX.Element)
|
||||
| ((props: IPerformanceImpactCellProps) => JSX.Element)
|
||||
| ((props: IDropdownCellProps) => JSX.Element);
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
@ -170,8 +173,8 @@ const generateTableHeaders = (
|
||||
},
|
||||
disableSortBy: true,
|
||||
accessor: "performance",
|
||||
Cell: (cellProps: IPillCellProps) => (
|
||||
<PillCell value={cellProps.cell.value} />
|
||||
Cell: (cellProps: IPerformanceImpactCellProps) => (
|
||||
<PerformanceImpactCell value={cellProps.cell.value} />
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -291,7 +294,7 @@ const enhancePackQueriesData = (
|
||||
query_name: query.query_name,
|
||||
actions: generateActionDropdownOptions(),
|
||||
performance: [
|
||||
performanceIndicator(scheduledQueryPerformance),
|
||||
getPerformanceImpactDescription(scheduledQueryPerformance),
|
||||
query.query_id,
|
||||
],
|
||||
stats: query.stats,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { IPolicy } from "./policy";
|
||||
import { IQuery } from "./query";
|
||||
import { IScheduledQueryStats } from "./scheduled_query_stats";
|
||||
import { ITeamSummary } from "./team";
|
||||
import { UserRole } from "./user";
|
||||
|
||||
@ -104,4 +105,5 @@ export interface IActivityDetails {
|
||||
script_name?: string;
|
||||
deadline_days?: number;
|
||||
grace_period_days?: number;
|
||||
stats?: IScheduledQueryStats;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import activitiesAPI, {
|
||||
} from "services/entities/activities";
|
||||
|
||||
import { ActivityType, IActivityDetails } from "interfaces/activity";
|
||||
import { getPerformanceImpactDescription } from "utilities/helpers";
|
||||
|
||||
import ShowQueryModal from "components/modals/ShowQueryModal";
|
||||
import DataError from "components/DataError";
|
||||
@ -35,6 +36,7 @@ const ActivityFeed = ({
|
||||
const [showShowQueryModal, setShowShowQueryModal] = useState(false);
|
||||
const [showScriptDetailsModal, setShowScriptDetailsModal] = useState(false);
|
||||
const queryShown = useRef("");
|
||||
const queryImpact = useRef<string | undefined>(undefined);
|
||||
const scriptExecutionId = useRef("");
|
||||
|
||||
const {
|
||||
@ -82,6 +84,9 @@ const ActivityFeed = ({
|
||||
switch (activityType) {
|
||||
case ActivityType.LiveQuery:
|
||||
queryShown.current = details.query_sql ?? "";
|
||||
queryImpact.current = details.stats
|
||||
? getPerformanceImpactDescription(details.stats)
|
||||
: undefined;
|
||||
setShowShowQueryModal(true);
|
||||
break;
|
||||
case ActivityType.RanScript:
|
||||
@ -169,6 +174,7 @@ const ActivityFeed = ({
|
||||
{showShowQueryModal && (
|
||||
<ShowQueryModal
|
||||
query={queryShown.current}
|
||||
impact={queryImpact.current}
|
||||
onCancel={() => setShowShowQueryModal(false)}
|
||||
/>
|
||||
)}
|
||||
|
@ -5,6 +5,8 @@ import createMockActivity from "__mocks__/activityMock";
|
||||
import createMockQuery from "__mocks__/queryMock";
|
||||
import { createMockTeamSummary } from "__mocks__/teamMock";
|
||||
import { ActivityType } from "interfaces/activity";
|
||||
import { createCustomRenderer } from "test/test-utils";
|
||||
import createMockConfig from "__mocks__/configMock";
|
||||
|
||||
import ActivityItem from ".";
|
||||
|
||||
@ -92,12 +94,57 @@ describe("Activity Feed", () => {
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
expect(
|
||||
screen.getByText("ran the query as a live query .")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/ran the/)).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Query")).toBeInTheDocument();
|
||||
expect(screen.getByText("Show query")).toBeInTheDocument();
|
||||
});
|
||||
it("renders a live_query type activity for a saved live query with targets and performance impact", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.LiveQuery,
|
||||
details: {
|
||||
query_name: "Test Query",
|
||||
query_sql: "SELECT * FROM users",
|
||||
targets_count: 10,
|
||||
stats: {
|
||||
system_time_p50: 0,
|
||||
system_time_p95: 50.4923,
|
||||
total_executions: 345,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
expect(screen.getByText(/ran the/)).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Query")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/with excessive performance impact on 10 hosts\./)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Show query")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a live_query type activity for a saved live query with targets and no performance impact", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.LiveQuery,
|
||||
details: {
|
||||
query_name: "Test Query",
|
||||
query_sql: "SELECT * FROM users",
|
||||
targets_count: 10,
|
||||
stats: {
|
||||
system_time_p50: 0,
|
||||
system_time_p95: 0,
|
||||
total_executions: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
expect(screen.getByText(/ran the/)).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Query")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Undetermined/)).toBeNull();
|
||||
expect(screen.getByText("Show query")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_pack type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
|
@ -5,6 +5,7 @@ import { formatDistanceToNowStrict } from "date-fns";
|
||||
import { ActivityType, IActivity, IActivityDetails } from "interfaces/activity";
|
||||
import {
|
||||
addGravatarUrlToResource,
|
||||
getPerformanceImpactDescription,
|
||||
internationalTimeFormat,
|
||||
} from "utilities/helpers";
|
||||
import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
|
||||
@ -89,27 +90,40 @@ const TAGGED_TEMPLATES = {
|
||||
activity: IActivity,
|
||||
onDetailsClick?: (type: ActivityType, details: IActivityDetails) => void
|
||||
) => {
|
||||
const count = activity.details?.targets_count;
|
||||
const queryName = activity.details?.query_name;
|
||||
const querySql = activity.details?.query_sql;
|
||||
const {
|
||||
targets_count: count,
|
||||
query_name: queryName,
|
||||
query_sql: querySql,
|
||||
stats,
|
||||
} = activity.details || {};
|
||||
|
||||
const savedQueryName = queryName ? (
|
||||
const impactDescription = stats
|
||||
? getPerformanceImpactDescription(stats)
|
||||
: undefined;
|
||||
|
||||
const queryNameCopy = queryName ? (
|
||||
<>
|
||||
the <b>{queryName}</b> query as
|
||||
the <b>{queryName}</b> query
|
||||
</>
|
||||
) : (
|
||||
<>a live query</>
|
||||
);
|
||||
|
||||
const impactCopy =
|
||||
impactDescription && impactDescription !== "Undetermined" ? (
|
||||
<>with {impactDescription.toLowerCase()} performance impact</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
const hostCount =
|
||||
const hostCountCopy =
|
||||
count !== undefined
|
||||
? ` on ${count} ${count === 1 ? "host" : "hosts"}`
|
||||
: "";
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>
|
||||
ran {savedQueryName} a live query {hostCount}.
|
||||
<span className={`${baseClass}__details-content`}>
|
||||
ran {queryNameCopy} {impactCopy} {hostCountCopy}.
|
||||
</span>
|
||||
{querySql && (
|
||||
<>
|
||||
@ -119,6 +133,7 @@ const TAGGED_TEMPLATES = {
|
||||
onClick={() =>
|
||||
onDetailsClick?.(ActivityType.LiveQuery, {
|
||||
query_sql: querySql,
|
||||
stats,
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -40,15 +40,15 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
&__details-content {
|
||||
margin-right: $pad-xsmall;
|
||||
}
|
||||
|
||||
&__details-bottomline {
|
||||
font-size: $xx-small;
|
||||
color: $ui-fleet-black-25;
|
||||
}
|
||||
|
||||
&__show-query-link {
|
||||
margin-left: $pad-xsmall;
|
||||
}
|
||||
|
||||
&__show-query-icon {
|
||||
margin-left: $pad-xsmall;
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ import { uniqueId } from "lodash";
|
||||
import { IQueryStats } from "interfaces/query_stats";
|
||||
import {
|
||||
humanQueryLastRun,
|
||||
performanceIndicator,
|
||||
getPerformanceImpactDescription,
|
||||
secondsToHms,
|
||||
} from "utilities/helpers";
|
||||
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import PillCell from "components/TableContainer/DataTable/PillCell";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
interface IHeaderProps {
|
||||
@ -31,7 +31,7 @@ interface ICellProps extends IRowProps {
|
||||
};
|
||||
}
|
||||
|
||||
interface IPillCellProps extends IRowProps {
|
||||
interface IPerformanceImpactCell extends IRowProps {
|
||||
cell: {
|
||||
value: { indicator: string; id: number };
|
||||
};
|
||||
@ -43,7 +43,7 @@ interface IDataColumn {
|
||||
accessor: string;
|
||||
Cell:
|
||||
| ((props: ICellProps) => JSX.Element)
|
||||
| ((props: IPillCellProps) => JSX.Element);
|
||||
| ((props: IPerformanceImpactCell) => JSX.Element);
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
}
|
||||
@ -116,8 +116,8 @@ const generatePackTableHeaders = (): IDataColumn[] => {
|
||||
},
|
||||
disableSortBy: true,
|
||||
accessor: "performance",
|
||||
Cell: (cellProps: IPillCellProps) => (
|
||||
<PillCell
|
||||
Cell: (cellProps: IPerformanceImpactCell) => (
|
||||
<PerformanceImpactCell
|
||||
value={cellProps.cell.value}
|
||||
customIdPrefix="query-perf-pill"
|
||||
/>
|
||||
@ -139,7 +139,7 @@ const enhancePackData = (query_stats: IQueryStats[]): IPackTable[] => {
|
||||
frequency: secondsToHms(query.interval),
|
||||
last_run: humanQueryLastRun(query.last_executed),
|
||||
performance: {
|
||||
indicator: performanceIndicator(scheduledQueryPerformance),
|
||||
indicator: getPerformanceImpactDescription(scheduledQueryPerformance),
|
||||
id: query.scheduled_query_id || parseInt(uniqueId(), 10),
|
||||
},
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from "react";
|
||||
|
||||
import { IQueryStats } from "interfaces/query_stats";
|
||||
import { performanceIndicator } from "utilities/helpers";
|
||||
import { getPerformanceImpactDescription } from "utilities/helpers";
|
||||
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import PillCell from "components/TableContainer/DataTable/PillCell";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import ReportUpdatedCell from "pages/hosts/details/cards/Queries/ReportUpdatedCell";
|
||||
import Icon from "components/Icon";
|
||||
@ -32,7 +32,7 @@ interface ICellProps extends IRowProps {
|
||||
};
|
||||
}
|
||||
|
||||
interface IPillCellProps extends IRowProps {
|
||||
interface IPerformanceImpactCell extends IRowProps {
|
||||
cell: {
|
||||
value: {
|
||||
indicator: string;
|
||||
@ -47,7 +47,7 @@ interface IDataColumn {
|
||||
accessor: string;
|
||||
Cell:
|
||||
| ((props: ICellProps) => JSX.Element)
|
||||
| ((props: IPillCellProps) => JSX.Element);
|
||||
| ((props: IPerformanceImpactCell) => JSX.Element);
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
}
|
||||
@ -84,14 +84,14 @@ const generateColumnConfigs = (
|
||||
},
|
||||
disableSortBy: true,
|
||||
accessor: "performance",
|
||||
Cell: (cellProps: IPillCellProps) => {
|
||||
Cell: (cellProps: IPerformanceImpactCell) => {
|
||||
const baseClass = "performance-cell";
|
||||
return (
|
||||
<span className={baseClass}>
|
||||
<PillCell
|
||||
<PerformanceImpactCell
|
||||
value={cellProps.cell.value}
|
||||
customIdPrefix="query-perf-pill"
|
||||
hostDetails
|
||||
isHostSpecific
|
||||
/>
|
||||
{!queryReportsDisabled &&
|
||||
cellProps.row.original.should_link_to_hqr && (
|
||||
@ -145,7 +145,7 @@ const enhanceScheduleData = (
|
||||
query_name,
|
||||
id: scheduled_query_id,
|
||||
performance: {
|
||||
indicator: performanceIndicator(scheduledQueryPerformance),
|
||||
indicator: getPerformanceImpactDescription(scheduledQueryPerformance),
|
||||
id: scheduled_query_id,
|
||||
},
|
||||
last_fetched,
|
||||
|
@ -13,7 +13,7 @@ import { AppContext } from "context/app";
|
||||
import { QueryContext } from "context/query";
|
||||
import { TableContext } from "context/table";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { performanceIndicator } from "utilities/helpers";
|
||||
import { getPerformanceImpactDescription } from "utilities/helpers";
|
||||
import { SupportedPlatform } from "interfaces/platform";
|
||||
import { API_ALL_TEAMS_ID } from "interfaces/team";
|
||||
import {
|
||||
@ -67,7 +67,7 @@ const getPlatforms = (queryString: string): SupportedPlatform[] => {
|
||||
const enhanceQuery = (q: ISchedulableQuery): IEnhancedQuery => {
|
||||
return {
|
||||
...q,
|
||||
performance: performanceIndicator(
|
||||
performance: getPerformanceImpactDescription(
|
||||
pick(q.stats, ["user_time_p50", "system_time_p50", "total_executions"])
|
||||
),
|
||||
platforms: getPlatforms(q.query),
|
||||
|
@ -7,8 +7,6 @@ import { IQuery } from "interfaces/query";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
import { ITableQueryData } from "components/TableContainer/TableContainer";
|
||||
import PATHS from "router/paths";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
import { getNextLocationPath } from "utilities/helpers";
|
||||
import Button from "components/buttons/Button";
|
||||
import TableContainer from "components/TableContainer";
|
||||
@ -16,7 +14,7 @@ import CustomLink from "components/CustomLink";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
// @ts-ignore
|
||||
import Dropdown from "components/forms/fields/Dropdown";
|
||||
import generateTableHeaders from "./QueriesTableConfig";
|
||||
import generateColumnConfigs from "./QueriesTableConfig";
|
||||
|
||||
const baseClass = "queries-table";
|
||||
|
||||
@ -278,22 +276,22 @@ const QueriesTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
const tableHeaders = useMemo(
|
||||
const columnConfigs = useMemo(
|
||||
() =>
|
||||
currentUser &&
|
||||
generateTableHeaders({ currentUser, isInherited, currentTeamId }),
|
||||
generateColumnConfigs({ currentUser, isInherited, currentTeamId }),
|
||||
[currentUser, isInherited, currentTeamId]
|
||||
);
|
||||
|
||||
const searchable =
|
||||
!(queriesList?.length === 0 && searchQuery === "") && !isInherited;
|
||||
|
||||
return tableHeaders && !isLoading ? (
|
||||
return columnConfigs && !isLoading ? (
|
||||
<div className={`${baseClass}`}>
|
||||
<TableContainer
|
||||
disableCount={isInherited}
|
||||
resultsTitle="queries"
|
||||
columnConfigs={tableHeaders}
|
||||
columnConfigs={columnConfigs}
|
||||
data={queriesList}
|
||||
filters={{ name: isInherited ? "" : searchQuery }}
|
||||
isLoading={isLoading}
|
||||
|
@ -21,7 +21,7 @@ import LinkCell from "components/TableContainer/DataTable/LinkCell/LinkCell";
|
||||
import HeaderCell from "components/TableContainer/DataTable/HeaderCell/HeaderCell";
|
||||
import PlatformCell from "components/TableContainer/DataTable/PlatformCell";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import PillCell from "components/TableContainer/DataTable/PillCell";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import { COLORS } from "styles/var/colors";
|
||||
import QueryAutomationsStatusIndicator from "../QueryAutomationsStatusIndicator";
|
||||
@ -196,14 +196,7 @@ const generateTableHeaders = ({
|
||||
Header: () => {
|
||||
return (
|
||||
<div>
|
||||
<TooltipWrapper
|
||||
tipContent={
|
||||
<>
|
||||
This is the average performance impact across <br />
|
||||
all hosts where this query was scheduled.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TooltipWrapper tipContent="The average performance impact across all hosts.">
|
||||
Performance impact
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
@ -212,7 +205,7 @@ const generateTableHeaders = ({
|
||||
disableSortBy: true,
|
||||
accessor: "performance",
|
||||
Cell: (cellProps: IStringCellProps) => (
|
||||
<PillCell
|
||||
<PerformanceImpactCell
|
||||
value={{
|
||||
indicator: cellProps.cell.value,
|
||||
id: cellProps.row.original.id,
|
||||
|
@ -186,3 +186,21 @@ $max-width: 2560px;
|
||||
box-shadow: 0px 3px 0px rgba(226, 228, 234, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin tooltip-text {
|
||||
width: max-content;
|
||||
max-width: 360px;
|
||||
padding: 6px;
|
||||
color: $core-white;
|
||||
background-color: $tooltip-bg;
|
||||
font-weight: $regular;
|
||||
font-size: $xx-small;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
z-index: 99; // not more than the site nav
|
||||
line-height: 1.375;
|
||||
white-space: initial;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@ -642,9 +642,9 @@ export const readableDate = (date: string) => {
|
||||
}).format(dateString);
|
||||
};
|
||||
|
||||
export const performanceIndicator = (
|
||||
export const getPerformanceImpactDescription = (
|
||||
scheduledQueryStats: IScheduledQueryStats
|
||||
): string => {
|
||||
) => {
|
||||
if (
|
||||
!scheduledQueryStats.total_executions ||
|
||||
scheduledQueryStats.total_executions === 0 ||
|
||||
|
Loading…
Reference in New Issue
Block a user