diff --git a/changes/issue-2224-query-author-avatar b/changes/issue-2224-query-author-avatar
new file mode 100644
index 0000000000..dbac0038e6
--- /dev/null
+++ b/changes/issue-2224-query-author-avatar
@@ -0,0 +1,2 @@
+* Add author avatars to manage queries page and edit query form
+* Format query last modified time in words
\ No newline at end of file
diff --git a/frontend/components/Avatar/Avatar.tsx b/frontend/components/Avatar/Avatar.tsx
index cc93699b40..ff6e94808c 100644
--- a/frontend/components/Avatar/Avatar.tsx
+++ b/frontend/components/Avatar/Avatar.tsx
@@ -1,6 +1,8 @@
import React, { useState, useCallback } from "react";
import classnames from "classnames";
+import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
+
interface IAvatarUserInterface {
gravatarURL: string;
}
@@ -24,9 +26,8 @@ const Avatar = ({ className, size, user }: IAvatarInterface): JSX.Element => {
setIsError(true);
}, []);
- const isSmall = size !== undefined && size.toLowerCase() === "small";
const avatarClasses = classnames(baseClass, className, {
- [`${baseClass}--${size}`]: isSmall,
+ [`${baseClass}--${size?.toLowerCase()}`]: !!size,
});
const { gravatarURL } = user;
@@ -35,7 +36,7 @@ const Avatar = ({ className, size, user }: IAvatarInterface): JSX.Element => {
diff --git a/frontend/components/Avatar/_styles.scss b/frontend/components/Avatar/_styles.scss
index 831ed346ed..e06fb5dcd4 100644
--- a/frontend/components/Avatar/_styles.scss
+++ b/frontend/components/Avatar/_styles.scss
@@ -8,6 +8,10 @@
@include size(32px);
}
+ &--xsmall {
+ @include size(16px);
+ }
+
img {
&.default {
display: none;
diff --git a/frontend/components/TableContainer/DataTable/DataTable.tsx b/frontend/components/TableContainer/DataTable/DataTable.tsx
index 160cf2b13e..a0c96691e5 100644
--- a/frontend/components/TableContainer/DataTable/DataTable.tsx
+++ b/frontend/components/TableContainer/DataTable/DataTable.tsx
@@ -435,7 +435,14 @@ const DataTable = ({
>
{row.cells.map((cell: any) => {
return (
-
{cell.render("Cell")} |
+
+ {cell.render("Cell")}
+ |
);
})}
diff --git a/frontend/interfaces/query.ts b/frontend/interfaces/query.ts
index dde2291637..a0626ae88a 100644
--- a/frontend/interfaces/query.ts
+++ b/frontend/interfaces/query.ts
@@ -36,6 +36,7 @@ export interface IQuery {
saved: boolean;
author_id: number;
author_name: string;
+ author_email: string;
observer_can_run: boolean;
packs: IPack[];
stats?: IScheduledQueryStats;
diff --git a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
index 973ed31503..46cd312db3 100644
--- a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
@@ -109,7 +109,7 @@ const ManageQueriesPage = (): JSX.Element => {
{
// refetchOnMount: false,
// refetchOnReconnect: false,
- // refetchOnWindowFocus: false,
+ refetchOnWindowFocus: false,
select: (data: IFleetQueriesResponse) => data.queries,
}
);
diff --git a/frontend/pages/queries/ManageQueriesPage/_styles.scss b/frontend/pages/queries/ManageQueriesPage/_styles.scss
index a96a5bd6ab..b00c27d968 100644
--- a/frontend/pages/queries/ManageQueriesPage/_styles.scss
+++ b/frontend/pages/queries/ManageQueriesPage/_styles.scss
@@ -182,4 +182,15 @@
}
}
}
+ .author_name__cell {
+ img,
+ div,
+ span {
+ display: flex;
+ align-items: center;
+ }
+ div {
+ padding-right: $pad-small;
+ }
+ }
}
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesListWrapper/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesListWrapper/QueriesTableConfig.tsx
index 487147c577..aff83910e2 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesListWrapper/QueriesTableConfig.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesListWrapper/QueriesTableConfig.tsx
@@ -3,11 +3,12 @@
// definitions for the selection row for some reason when we dont really need it.
import React from "react";
import ReactTooltip from "react-tooltip";
-import format from "date-fns/format";
+import formatDistanceToNow from "date-fns/formatDistanceToNow";
import permissionsUtils from "utilities/permissions";
// @ts-ignore
+import Avatar from "components/Avatar";
import Checkbox from "components/forms/fields/Checkbox";
import LinkCell from "components/TableContainer/DataTable/LinkCell/LinkCell";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell/HeaderCell";
@@ -19,6 +20,7 @@ import PATHS from "router/paths";
import { IQuery } from "interfaces/query";
import { IUser } from "interfaces/user";
+import { addGravatarUrlToResource } from "fleet/helpers";
interface IQueryRow {
id: string;
@@ -112,9 +114,19 @@ const generateTableHeaders = (currentUser: IUser): IDataColumn[] => {
/>
),
accessor: "author_name",
- Cell: (cellProps: ICellProps): JSX.Element => (
-
- ),
+ Cell: (cellProps: ICellProps): JSX.Element => {
+ const { author_name, author_email } = cellProps.row.original;
+ const author = author_name === currentUser.name ? "You" : author_name;
+ return (
+
+
+ {author}
+
+ );
+ },
sortType: "caseInsensitive",
},
{
@@ -127,7 +139,12 @@ const generateTableHeaders = (currentUser: IUser): IDataColumn[] => {
),
accessor: "updated_at",
Cell: (cellProps: ICellProps): JSX.Element => (
-
+
),
},
];
diff --git a/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx b/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
index a83a151f43..f7d63d9efd 100644
--- a/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
+++ b/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
@@ -4,6 +4,7 @@ import ReactTooltip from "react-tooltip";
import { size } from "lodash";
import { useDebouncedCallback } from "use-debounce/lib";
+import { addGravatarUrlToResource } from "fleet/helpers";
// @ts-ignore
import { listCompatiblePlatforms, parseSqlTables } from "utilities/sql_tools";
@@ -11,6 +12,7 @@ import { AppContext } from "context/app";
import { QueryContext } from "context/query";
import { IQuery, IQueryFormData } from "interfaces/query";
+import Avatar from "components/Avatar";
import FleetAce from "components/FleetAce"; // @ts-ignore
import validateQuery from "components/forms/validators/validate_query";
import Button from "components/buttons/Button";
@@ -169,6 +171,27 @@ const QueryForm = ({
}
};
+ const renderAuthor = (): JSX.Element | null => {
+ return storedQuery ? (
+ <>
+ Author
+
+
+
+ {storedQuery.author_name === currentUser?.name
+ ? "You"
+ : storedQuery.author_name}
+
+
+ >
+ ) : null;
+ };
+
const renderLabelComponent = (): JSX.Element | null => {
if (!showOpenSchemaActionText) {
return null;
@@ -261,12 +284,17 @@ const QueryForm = ({
const renderRunForObserver = (