integrate with API to show query name and sql query for live query activity (#8864)
* integrate with API to show query name and sql query for live query activity * move ShowQueryModal to common components
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
@@ -6,8 +6,9 @@ import activitiesAPI, {
|
||||
IActivitiesResponse,
|
||||
} from "services/entities/activities";
|
||||
|
||||
import { IActivity } from "interfaces/activity";
|
||||
import { IActivity, IActivityDetails } from "interfaces/activity";
|
||||
|
||||
import ShowQueryModal from "components/modals/ShowQueryModal";
|
||||
import DataError from "components/DataError";
|
||||
import Button from "components/buttons/Button";
|
||||
import Spinner from "components/Spinner";
|
||||
@@ -16,7 +17,6 @@ import FleetIcon from "components/icons/FleetIcon";
|
||||
import ActivityItem from "./ActivityItem";
|
||||
|
||||
const baseClass = "activity-feed";
|
||||
|
||||
interface IActvityCardProps {
|
||||
setShowActivityFeedTitle: (showActivityFeedTitle: boolean) => void;
|
||||
}
|
||||
@@ -28,6 +28,8 @@ const ActivityFeed = ({
|
||||
}: IActvityCardProps): JSX.Element => {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [showMore, setShowMore] = useState(true);
|
||||
const [showShowQueryModal, setShowShowQueryModal] = useState(false);
|
||||
const queryShown = useRef("");
|
||||
|
||||
const {
|
||||
data: activities,
|
||||
@@ -74,6 +76,11 @@ const ActivityFeed = ({
|
||||
setPageIndex(pageIndex + 1);
|
||||
};
|
||||
|
||||
const handleDetailsClick = (details: IActivityDetails) => {
|
||||
queryShown.current = details.query_sql ?? "";
|
||||
setShowShowQueryModal(true);
|
||||
};
|
||||
|
||||
const renderError = () => {
|
||||
return <DataError card />;
|
||||
};
|
||||
@@ -91,12 +98,6 @@ const ActivityFeed = ({
|
||||
);
|
||||
};
|
||||
|
||||
const renderActivityBlock = (activity: IActivity) => {
|
||||
const { id } = activity;
|
||||
|
||||
return <ActivityItem activity={activity} key={id} />;
|
||||
};
|
||||
|
||||
// Renders opaque information as activity feed is loading
|
||||
const opacity = isFetchingActivities ? { opacity: 0.4 } : { opacity: 1 };
|
||||
|
||||
@@ -113,7 +114,13 @@ const ActivityFeed = ({
|
||||
</div>
|
||||
)}
|
||||
<div style={opacity}>
|
||||
{activities?.map((activity) => renderActivityBlock(activity))}
|
||||
{activities?.map((activity) => (
|
||||
<ActivityItem
|
||||
activity={activity}
|
||||
onDetailsClick={handleDetailsClick}
|
||||
key={activity.id}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -142,6 +149,12 @@ const ActivityFeed = ({
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{showShowQueryModal && (
|
||||
<ShowQueryModal
|
||||
query={queryShown.current}
|
||||
onCancel={() => setShowShowQueryModal(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -82,6 +82,7 @@ describe("Activity Feed", () => {
|
||||
screen.getByText("ran the query as a live query .")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Query")).toBeInTheDocument();
|
||||
expect(screen.getByText("Show query")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_pack type activity", () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import { find, lowerCase, noop } from "lodash";
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
|
||||
import { ActivityType, IActivity } from "interfaces/activity";
|
||||
import { ActivityType, IActivity, IActivityDetails } from "interfaces/activity";
|
||||
import { addGravatarUrlToResource } from "utilities/helpers";
|
||||
import Avatar from "components/Avatar";
|
||||
import Button from "components/buttons/Button";
|
||||
@@ -14,7 +14,10 @@ const DEFAULT_GRAVATAR_URL =
|
||||
"https://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&size=200";
|
||||
|
||||
const TAGGED_TEMPLATES = {
|
||||
liveQueryActivityTemplate: (activity: IActivity) => {
|
||||
liveQueryActivityTemplate: (
|
||||
activity: IActivity,
|
||||
onDetailsClick?: (details: IActivityDetails) => void
|
||||
) => {
|
||||
const count = activity.details?.targets_count;
|
||||
const queryName = activity.details?.query_name;
|
||||
const querySql = activity.details?.query_sql;
|
||||
@@ -37,15 +40,12 @@ const TAGGED_TEMPLATES = {
|
||||
<span>
|
||||
ran {savedQueryName} a live query {hostCount}.
|
||||
</span>
|
||||
|
||||
{/* TODO: the API does not yet send back querySql yet so will implement
|
||||
the onClick handler when we get it. We dont show this for now. */}
|
||||
{false && (
|
||||
{querySql && (
|
||||
<>
|
||||
<Button
|
||||
className={`${baseClass}__show-query-link`}
|
||||
variant="text-link"
|
||||
onClick={noop}
|
||||
onClick={() => onDetailsClick?.({ query_sql: querySql })}
|
||||
>
|
||||
Show query{" "}
|
||||
<Icon className={`${baseClass}__show-query-icon`} name="eye" />
|
||||
@@ -107,10 +107,16 @@ const TAGGED_TEMPLATES = {
|
||||
},
|
||||
};
|
||||
|
||||
const getDetail = (activity: IActivity) => {
|
||||
const getDetail = (
|
||||
activity: IActivity,
|
||||
onDetailsClick?: (details: IActivityDetails) => void
|
||||
) => {
|
||||
switch (activity.type) {
|
||||
case ActivityType.LiveQuery: {
|
||||
return TAGGED_TEMPLATES.liveQueryActivityTemplate(activity);
|
||||
return TAGGED_TEMPLATES.liveQueryActivityTemplate(
|
||||
activity,
|
||||
onDetailsClick
|
||||
);
|
||||
}
|
||||
case ActivityType.AppliedSpecPack: {
|
||||
return TAGGED_TEMPLATES.editPackCtlActivityTemplate();
|
||||
@@ -138,9 +144,18 @@ const getDetail = (activity: IActivity) => {
|
||||
|
||||
interface IActivityItemProps {
|
||||
activity: IActivity;
|
||||
|
||||
/** A handler for handling clicking on the details of an activity. Not all
|
||||
* activites have more details so this is optional. An example of additonal
|
||||
* details is showing the query for a live query action.
|
||||
*/
|
||||
onDetailsClick?: (details: IActivityDetails) => void;
|
||||
}
|
||||
|
||||
const ActivityItem = ({ activity }: IActivityItemProps) => {
|
||||
const ActivityItem = ({
|
||||
activity,
|
||||
onDetailsClick = noop,
|
||||
}: IActivityItemProps) => {
|
||||
const { actor_email } = activity;
|
||||
const { gravatarURL } = actor_email
|
||||
? addGravatarUrlToResource({ email: actor_email })
|
||||
@@ -156,7 +171,8 @@ const ActivityItem = ({ activity }: IActivityItemProps) => {
|
||||
<div className={`${baseClass}__details`}>
|
||||
<p>
|
||||
<span className={`${baseClass}__details-topline`}>
|
||||
<b>{activity.actor_full_name}</b> {getDetail(activity)}
|
||||
<b>{activity.actor_full_name}</b>{" "}
|
||||
{getDetail(activity, onDetailsClick)}
|
||||
</span>
|
||||
<br />
|
||||
<span className={`${baseClass}__details-bottomline`}>
|
||||
|
||||
@@ -29,10 +29,11 @@ import Spinner from "components/Spinner";
|
||||
import TeamsDropdown from "components/TeamsDropdown";
|
||||
import TableDataError from "components/DataError";
|
||||
import MainContent from "components/MainContent";
|
||||
import ShowQueryModal from "components/modals/ShowQueryModal";
|
||||
|
||||
import ScheduleTable from "./components/ScheduleTable";
|
||||
import ScheduleEditorModal from "./components/ScheduleEditorModal";
|
||||
import RemoveScheduledQueryModal from "./components/RemoveScheduledQueryModal";
|
||||
import ShowQueryModal from "./components/ShowQueryModal";
|
||||
|
||||
const baseClass = "manage-schedule-page";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user