mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
refactor activity items and add query name to live_query activity type (#8740)
This commit is contained in:
parent
267aaf0dbe
commit
e7616dd422
14 changed files with 471 additions and 230 deletions
|
|
@ -0,0 +1 @@
|
|||
- add the query name and and query modal for live query actions.
|
||||
|
|
@ -8,11 +8,6 @@ const DEFAULT_ACTIVITY_MOCK: IActivity = {
|
|||
actor_gravatar: "",
|
||||
actor_email: "rachel@fleetdm.com",
|
||||
type: ActivityType.EditedAgentOptions,
|
||||
details: {
|
||||
global: false,
|
||||
team_id: 1,
|
||||
team_name: "Apples",
|
||||
},
|
||||
};
|
||||
|
||||
const createMockActivity = (overrides?: Partial<IActivity>): IActivity => {
|
||||
22
frontend/__mocks__/queryMock.ts
Normal file
22
frontend/__mocks__/queryMock.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { IQuery } from "interfaces/query";
|
||||
|
||||
const DEFAULT_QUERY_MOCK: IQuery = {
|
||||
created_at: "2022-11-03T17:22:14Z",
|
||||
updated_at: "2022-11-03T17:22:14Z",
|
||||
id: 1,
|
||||
name: "Test Query",
|
||||
description: "A test query",
|
||||
query: "SELECT * FROM users",
|
||||
saved: true,
|
||||
author_id: 1,
|
||||
author_name: "Rachel",
|
||||
author_email: "rachel@fleetdm.com",
|
||||
observer_can_run: false,
|
||||
packs: [],
|
||||
};
|
||||
|
||||
const createMockQuery = (overrides?: Partial<IQuery>): IQuery => {
|
||||
return { ...DEFAULT_QUERY_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
export default createMockQuery;
|
||||
21
frontend/__mocks__/teamMock.ts
Normal file
21
frontend/__mocks__/teamMock.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { ITeam, ITeamSummary } from "interfaces/team";
|
||||
|
||||
const DEFAULT_MOCK_TEAM_SUMMARY: ITeamSummary = {
|
||||
id: 1,
|
||||
name: "Team 1",
|
||||
};
|
||||
|
||||
const DEFAUT_TEAM_MOCK: ITeam = {
|
||||
...DEFAULT_MOCK_TEAM_SUMMARY,
|
||||
};
|
||||
|
||||
export const createMockTeamSummary = (
|
||||
overrides?: Partial<ITeamSummary>
|
||||
): ITeamSummary => {
|
||||
return { ...DEFAULT_MOCK_TEAM_SUMMARY, ...overrides };
|
||||
};
|
||||
|
||||
const createMockTeam = (overrides?: Partial<ITeam>): ITeam => {
|
||||
return { ...DEFAUT_TEAM_MOCK, ...overrides };
|
||||
};
|
||||
export default createMockTeam;
|
||||
|
|
@ -25,7 +25,7 @@ export type ButtonVariant =
|
|||
|
||||
export interface IButtonProps {
|
||||
autofocus?: boolean;
|
||||
children: React.ReactChild;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
size?: string;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export interface IActivityDetails {
|
|||
policy_name?: string;
|
||||
query_id?: number;
|
||||
query_name?: string;
|
||||
query_sql?: string;
|
||||
team_id?: number;
|
||||
team_name?: string;
|
||||
teams?: ITeamSummary[];
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ import { noop } from "lodash";
|
|||
|
||||
import { createCustomRenderer } from "test/test-utils";
|
||||
import mockServer from "test/mock-server";
|
||||
import {
|
||||
activityHandler2DaysAgo,
|
||||
activityHandler9Activities,
|
||||
} from "test/handlers/activity-handlers";
|
||||
import { activityHandler9Activities } from "test/handlers/activity-handlers";
|
||||
|
||||
import ActivityFeed from "./ActivityFeed";
|
||||
|
||||
|
|
@ -84,23 +81,4 @@ describe("Activity Feed", () => {
|
|||
|
||||
expect(screen.getByRole("button", { name: "Previous" })).toBeEnabled();
|
||||
});
|
||||
|
||||
it("renders avatar, actor name, timestamp", async () => {
|
||||
mockServer.use(activityHandler2DaysAgo);
|
||||
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
});
|
||||
|
||||
render(<ActivityFeed setShowActivityFeedTitle={noop} />);
|
||||
|
||||
// waiting for the activity data to render
|
||||
await screen.findByText("Rachel");
|
||||
|
||||
expect(screen.getByRole("img")).toHaveAttribute("alt", "User avatar");
|
||||
expect(screen.getByText("Rachel")).toBeInTheDocument();
|
||||
expect(screen.getByText("2 days ago")).toBeInTheDocument();
|
||||
});
|
||||
// TODO: Create unit size component for individual activities and
|
||||
// test each activity type with different details at the unit level
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { find, isEmpty, lowerCase } from "lodash";
|
||||
import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
import activitiesAPI, {
|
||||
IActivitiesResponse,
|
||||
} from "services/entities/activities";
|
||||
import { addGravatarUrlToResource } from "utilities/helpers";
|
||||
|
||||
import { IActivity, ActivityType } from "interfaces/activity";
|
||||
import { IActivity } from "interfaces/activity";
|
||||
|
||||
import DataError from "components/DataError";
|
||||
import Avatar from "components/Avatar";
|
||||
import Button from "components/buttons/Button";
|
||||
import Spinner from "components/Spinner";
|
||||
// @ts-ignore
|
||||
import FleetIcon from "components/icons/FleetIcon";
|
||||
import ActivityItem from "./ActivityItem";
|
||||
|
||||
const baseClass = "activity-feed";
|
||||
|
||||
|
|
@ -23,74 +21,8 @@ interface IActvityCardProps {
|
|||
setShowActivityFeedTitle: (showActivityFeedTitle: boolean) => void;
|
||||
}
|
||||
|
||||
interface IActivityDisplay extends IActivity {
|
||||
key?: string;
|
||||
}
|
||||
|
||||
const DEFAULT_GRAVATAR_URL =
|
||||
"https://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&size=200";
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 8;
|
||||
|
||||
const TAGGED_TEMPLATES = {
|
||||
liveQueryActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.targets_count;
|
||||
return typeof count === "undefined" || typeof count !== "number"
|
||||
? "ran a live query"
|
||||
: `ran a live query on ${count} ${count === 1 ? "host" : "hosts"}`;
|
||||
},
|
||||
editPackCtlActivityTemplate: () => {
|
||||
return "edited a pack using fleetctl";
|
||||
},
|
||||
editPolicyCtlActivityTemplate: () => {
|
||||
return "edited policies using fleetctl";
|
||||
},
|
||||
editQueryCtlActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.specs?.length;
|
||||
return typeof count === "undefined" || typeof count !== "number"
|
||||
? "edited a query using fleetctl"
|
||||
: `edited ${count === 1 ? "a query" : "queries"} using fleetctl`;
|
||||
},
|
||||
editTeamCtlActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.teams?.length;
|
||||
return count === 1 && activity.details?.teams ? (
|
||||
<>
|
||||
edited <b>{activity.details?.teams[0].name}</b> team using fleetctl
|
||||
</>
|
||||
) : (
|
||||
"edited multiple teams using fleetctl"
|
||||
);
|
||||
},
|
||||
userAddedBySSOTempalte: () => {
|
||||
return `was added to Fleet by SSO`;
|
||||
},
|
||||
editAgentOptions: (activity: IActivity) => {
|
||||
return activity.details?.global ? (
|
||||
"edited agent options"
|
||||
) : (
|
||||
<>
|
||||
edited agent options on <b>{activity.details?.team_name}</b> team
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
defaultActivityTemplate: (activity: IActivity) => {
|
||||
const entityName = find(activity.details, (_, key) =>
|
||||
key.includes("_name")
|
||||
);
|
||||
|
||||
const activityType = lowerCase(activity.type).replace(" saved", "");
|
||||
|
||||
return !entityName ? (
|
||||
`${activityType}`
|
||||
) : (
|
||||
<span>
|
||||
{activityType} <b>{entityName}</b>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const ActivityFeed = ({
|
||||
setShowActivityFeedTitle,
|
||||
}: IActvityCardProps): JSX.Element => {
|
||||
|
|
@ -142,35 +74,6 @@ const ActivityFeed = ({
|
|||
setPageIndex(pageIndex + 1);
|
||||
};
|
||||
|
||||
const getDetail = (activity: IActivity) => {
|
||||
switch (activity.type) {
|
||||
case ActivityType.LiveQuery: {
|
||||
return TAGGED_TEMPLATES.liveQueryActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.AppliedSpecPack: {
|
||||
return TAGGED_TEMPLATES.editPackCtlActivityTemplate();
|
||||
}
|
||||
case ActivityType.AppliedSpecPolicy: {
|
||||
return TAGGED_TEMPLATES.editPolicyCtlActivityTemplate();
|
||||
}
|
||||
case ActivityType.AppliedSpecSavedQuery: {
|
||||
return TAGGED_TEMPLATES.editQueryCtlActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.AppliedSpecTeam: {
|
||||
return TAGGED_TEMPLATES.editTeamCtlActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.UserAddedBySSO: {
|
||||
return TAGGED_TEMPLATES.userAddedBySSOTempalte();
|
||||
}
|
||||
case ActivityType.EditedAgentOptions: {
|
||||
return TAGGED_TEMPLATES.editAgentOptions(activity);
|
||||
}
|
||||
default: {
|
||||
return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderError = () => {
|
||||
return <DataError card />;
|
||||
};
|
||||
|
|
@ -188,36 +91,10 @@ const ActivityFeed = ({
|
|||
);
|
||||
};
|
||||
|
||||
const renderActivityBlock = (activity: IActivityDisplay) => {
|
||||
const { actor_email, id, key } = activity;
|
||||
const { gravatarURL } = actor_email
|
||||
? addGravatarUrlToResource({ email: actor_email })
|
||||
: { gravatarURL: DEFAULT_GRAVATAR_URL };
|
||||
const renderActivityBlock = (activity: IActivity) => {
|
||||
const { id } = activity;
|
||||
|
||||
return (
|
||||
<div className={`${baseClass}__block`} key={key || id}>
|
||||
<Avatar
|
||||
className={`${baseClass}__avatar-image`}
|
||||
user={{
|
||||
gravatarURL,
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
<div className={`${baseClass}__details`}>
|
||||
<p>
|
||||
<span className={`${baseClass}__details-topline`}>
|
||||
<b>{activity.actor_full_name}</b> {getDetail(activity)}.
|
||||
</span>
|
||||
<br />
|
||||
<span className={`${baseClass}__details-bottomline`}>
|
||||
{formatDistanceToNowStrict(new Date(activity.created_at), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <ActivityItem activity={activity} key={id} />;
|
||||
};
|
||||
|
||||
// Renders opaque information as activity feed is loading
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import createMockActivity from "__mocks__/activityMock";
|
||||
import createMockQuery from "__mocks__/queryMock";
|
||||
import { createMockTeamSummary } from "__mocks__/teamMock";
|
||||
import { ActivityType } from "interfaces/activity";
|
||||
|
||||
import ActivityItem from ".";
|
||||
|
||||
describe("Activity Feed", () => {
|
||||
it("renders avatar, actor name, timestamp", async () => {
|
||||
const currentDate = new Date();
|
||||
currentDate.setDate(currentDate.getDate() - 2);
|
||||
|
||||
const activity = createMockActivity({
|
||||
created_at: currentDate.toISOString(),
|
||||
});
|
||||
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
// waiting for the activity data to render
|
||||
await screen.findByText("Rachel");
|
||||
|
||||
expect(screen.getByRole("img")).toHaveAttribute("alt", "User avatar");
|
||||
expect(screen.getByText("Rachel")).toBeInTheDocument();
|
||||
expect(screen.getByText("2 days ago")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a default activity for activities without a specific message", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.CreatedPack,
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("created pack.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a default activity for activities with a named property", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.CreatedPack,
|
||||
details: { pack_name: "Test pack" },
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("created pack .")).toBeInTheDocument();
|
||||
expect(screen.getByText("Test pack")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a live_query type activity", () => {
|
||||
const activity = createMockActivity({ type: ActivityType.LiveQuery });
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("ran a live query .")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a live_query type activity with host count details", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.LiveQuery,
|
||||
details: {
|
||||
targets_count: 10,
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("ran a live query on 10 hosts.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a live_query type activity for a saved live query with targets", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.LiveQuery,
|
||||
details: {
|
||||
query_name: "Test Query",
|
||||
query_sql: "SELECT * FROM users",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("ran the query as a live query .")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Query")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_pack type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecPack,
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited a pack using fleetctl.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_policy type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecPolicy,
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited policies using fleetctl.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_saved_query type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecSavedQuery,
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited a query using fleetctl.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_saved_query type activity when run on multiple queries", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecSavedQuery,
|
||||
details: { specs: [createMockQuery(), createMockQuery()] },
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited queries using fleetctl.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_team type activity for a single team", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecTeam,
|
||||
details: { teams: [createMockTeamSummary()] },
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("edited team using fleetctl.")).toBeInTheDocument();
|
||||
expect(screen.getByText("Team 1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an applied_spec_team type activity for multiple team", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.AppliedSpecTeam,
|
||||
details: {
|
||||
teams: [createMockTeamSummary(), createMockTeamSummary()],
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited multiple teams using fleetctl.")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an user_added_by_sso type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserAddedBySSO,
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("was added to Fleet by SSO.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an edited_agent_options type activity for a team", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.EditedAgentOptions,
|
||||
details: { team_name: "Test Team 1" },
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("edited agent options on team.")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Team 1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an edited_agent_options type activity globally", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.EditedAgentOptions,
|
||||
details: { global: true },
|
||||
});
|
||||
render(<ActivityItem activity={activity} />);
|
||||
|
||||
expect(screen.getByText("edited agent options.")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
import React from "react";
|
||||
import { find, lowerCase, noop } from "lodash";
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
|
||||
import { ActivityType, IActivity } from "interfaces/activity";
|
||||
import { addGravatarUrlToResource } from "utilities/helpers";
|
||||
import Avatar from "components/Avatar";
|
||||
import Button from "components/buttons/Button";
|
||||
import Icon from "components/Icon";
|
||||
|
||||
const baseClass = "activity-item";
|
||||
|
||||
const DEFAULT_GRAVATAR_URL =
|
||||
"https://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&size=200";
|
||||
|
||||
const TAGGED_TEMPLATES = {
|
||||
liveQueryActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.targets_count;
|
||||
const queryName = activity.details?.query_name;
|
||||
const querySql = activity.details?.query_sql;
|
||||
|
||||
const savedQueryName = queryName ? (
|
||||
<>
|
||||
the <b>{queryName}</b> query as
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
const hostCount =
|
||||
count !== undefined
|
||||
? ` on ${count} ${count === 1 ? "host" : "hosts"}`
|
||||
: "";
|
||||
|
||||
return (
|
||||
<>
|
||||
<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 && (
|
||||
<>
|
||||
<Button
|
||||
className={`${baseClass}__show-query-link`}
|
||||
variant="text-link"
|
||||
onClick={noop}
|
||||
>
|
||||
Show query{" "}
|
||||
<Icon className={`${baseClass}__show-query-icon`} name="eye" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
editPackCtlActivityTemplate: () => {
|
||||
return "edited a pack using fleetctl.";
|
||||
},
|
||||
editPolicyCtlActivityTemplate: () => {
|
||||
return "edited policies using fleetctl.";
|
||||
},
|
||||
editQueryCtlActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.specs?.length;
|
||||
return typeof count === "undefined" || count === 1
|
||||
? "edited a query using fleetctl."
|
||||
: "edited queries using fleetctl.";
|
||||
},
|
||||
editTeamCtlActivityTemplate: (activity: IActivity) => {
|
||||
const count = activity.details?.teams?.length;
|
||||
return count === 1 && activity.details?.teams ? (
|
||||
<>
|
||||
edited <b>{activity.details?.teams[0].name}</b> team using fleetctl.
|
||||
</>
|
||||
) : (
|
||||
"edited multiple teams using fleetctl."
|
||||
);
|
||||
},
|
||||
userAddedBySSOTempalte: () => {
|
||||
return "was added to Fleet by SSO.";
|
||||
},
|
||||
editAgentOptions: (activity: IActivity) => {
|
||||
return activity.details?.global ? (
|
||||
"edited agent options."
|
||||
) : (
|
||||
<>
|
||||
edited agent options on <b>{activity.details?.team_name}</b> team.
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
defaultActivityTemplate: (activity: IActivity) => {
|
||||
const entityName = find(activity.details, (_, key) =>
|
||||
key.includes("_name")
|
||||
);
|
||||
|
||||
const activityType = lowerCase(activity.type).replace(" saved", "");
|
||||
|
||||
return !entityName ? (
|
||||
`${activityType}.`
|
||||
) : (
|
||||
<span>
|
||||
{activityType} <b>{entityName}</b>.
|
||||
</span>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const getDetail = (activity: IActivity) => {
|
||||
switch (activity.type) {
|
||||
case ActivityType.LiveQuery: {
|
||||
return TAGGED_TEMPLATES.liveQueryActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.AppliedSpecPack: {
|
||||
return TAGGED_TEMPLATES.editPackCtlActivityTemplate();
|
||||
}
|
||||
case ActivityType.AppliedSpecPolicy: {
|
||||
return TAGGED_TEMPLATES.editPolicyCtlActivityTemplate();
|
||||
}
|
||||
case ActivityType.AppliedSpecSavedQuery: {
|
||||
return TAGGED_TEMPLATES.editQueryCtlActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.AppliedSpecTeam: {
|
||||
return TAGGED_TEMPLATES.editTeamCtlActivityTemplate(activity);
|
||||
}
|
||||
case ActivityType.UserAddedBySSO: {
|
||||
return TAGGED_TEMPLATES.userAddedBySSOTempalte();
|
||||
}
|
||||
case ActivityType.EditedAgentOptions: {
|
||||
return TAGGED_TEMPLATES.editAgentOptions(activity);
|
||||
}
|
||||
default: {
|
||||
return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface IActivityItemProps {
|
||||
activity: IActivity;
|
||||
}
|
||||
|
||||
const ActivityItem = ({ activity }: IActivityItemProps) => {
|
||||
const { actor_email } = activity;
|
||||
const { gravatarURL } = actor_email
|
||||
? addGravatarUrlToResource({ email: actor_email })
|
||||
: { gravatarURL: DEFAULT_GRAVATAR_URL };
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Avatar
|
||||
className={`${baseClass}__avatar-image`}
|
||||
user={{ gravatarURL }}
|
||||
size="small"
|
||||
/>
|
||||
<div className={`${baseClass}__details`}>
|
||||
<p>
|
||||
<span className={`${baseClass}__details-topline`}>
|
||||
<b>{activity.actor_full_name}</b> {getDetail(activity)}
|
||||
</span>
|
||||
<br />
|
||||
<span className={`${baseClass}__details-bottomline`}>
|
||||
{formatDistanceToNowStrict(new Date(activity.created_at), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActivityItem;
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
.activity-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-bottom: $pad-large;
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: relative;
|
||||
height: 36px !important;
|
||||
z-index: 0;
|
||||
top: 35px;
|
||||
left: 17px;
|
||||
border-left: 1px dashed $ui-fleet-blue-15;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:last-child:before {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
&__avatar-image {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__details {
|
||||
padding-left: $pad-large;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__details-topline {
|
||||
font-size: $x-small;
|
||||
}
|
||||
|
||||
&__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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./ActivityItem";
|
||||
|
|
@ -11,59 +11,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-bottom: $pad-large;
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
&__block:before {
|
||||
content: "";
|
||||
position: relative;
|
||||
height: 36px !important;
|
||||
z-index: 0;
|
||||
top: 35px;
|
||||
left: 17px;
|
||||
border-left: 1px dashed $ui-fleet-blue-15;
|
||||
height: 100%;
|
||||
}
|
||||
&__block:last-child:before {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
&__avatar-frame {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&__avatar-image {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__details {
|
||||
padding-left: $pad-large;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__details-topline {
|
||||
font-size: $x-small;
|
||||
}
|
||||
|
||||
&__details-bottomline {
|
||||
font-size: $xx-small;
|
||||
color: $ui-fleet-black-25;
|
||||
}
|
||||
|
||||
&__pagination {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { rest } from "msw";
|
||||
|
||||
import createMockActivity from "__mocks__/activityFeedMock";
|
||||
import createMockActivity from "__mocks__/activityMock";
|
||||
import { baseUrl } from "test/test-utils";
|
||||
|
||||
export const defaultActivityHandler = rest.get(
|
||||
|
|
@ -38,21 +38,3 @@ export const activityHandler9Activities = rest.get(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const activityHandler2DaysAgo = rest.get(
|
||||
baseUrl("/activities"),
|
||||
(req, res, context) => {
|
||||
const currentDate = new Date();
|
||||
currentDate.setDate(currentDate.getDate() - 2);
|
||||
|
||||
return res(
|
||||
context.json({
|
||||
activities: [
|
||||
createMockActivity({
|
||||
created_at: currentDate.toISOString(),
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue