diff --git a/packages/core/admin/admin/src/content-manager/components/AttributeFilter/AdminUsersFilter.js b/packages/core/admin/admin/src/content-manager/components/AttributeFilter/AdminUsersFilter.js
new file mode 100644
index 0000000000..674e803955
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/AttributeFilter/AdminUsersFilter.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Combobox, ComboboxOption } from '@strapi/design-system';
+import { getDisplayName } from '@strapi/helper-plugin';
+import { useIntl } from 'react-intl';
+
+import { useAdminUsers } from '../../../hooks/useAdminUsers';
+
+const AdminUsersFilter = ({ value, onChange }) => {
+ const { formatMessage } = useIntl();
+ const { users, isLoading } = useAdminUsers({}, { staleTime: 2 * (1000 * 60) });
+ const ariaLabel = formatMessage({
+ id: 'content-manager.components.Filters.usersSelect.label',
+ defaultMessage: 'Search and select an user to filter',
+ });
+
+ return (
+
+ {users.map((user) => {
+ return (
+
+ {getDisplayName(user, formatMessage)}
+
+ );
+ })}
+
+ );
+};
+
+AdminUsersFilter.propTypes = {
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.string,
+};
+
+AdminUsersFilter.defaultProps = {
+ value: '',
+};
+
+export { AdminUsersFilter };
\ No newline at end of file
diff --git a/packages/core/admin/admin/src/content-manager/components/AttributeFilter/index.js b/packages/core/admin/admin/src/content-manager/components/AttributeFilter/index.js
index b34e5a1d33..78a2291d71 100644
--- a/packages/core/admin/admin/src/content-manager/components/AttributeFilter/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/AttributeFilter/index.js
@@ -2,14 +2,77 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
-
-import Filters from './Filters';
+import { useQueryParams } from '@strapi/helper-plugin';
+import { useAdminUsers } from '../../../hooks/useAdminUsers';
import useAllowedAttributes from './hooks/useAllowedAttributes';
+import Filters from './Filters';
+import { AdminUsersFilter } from './AdminUsersFilter';
+
+const AUTHOR_ATTRIBUTES = ['createdBy', 'updatedBy'];
const AttributeFilter = ({ contentType, slug, metadatas }) => {
const { formatMessage } = useIntl();
+
+ const [{ query }] = useQueryParams();
+ // We get the users selected' ids
+ const selectedUsers =
+ query?.filters?.$and?.reduce((acc, filter) => {
+ const [key, value] = Object.entries(filter)[0];
+ const id = value.id?.$eq || value.id?.$ne;
+
+ if (AUTHOR_ATTRIBUTES.includes(key) && !acc.includes(id)) {
+ acc.push(id);
+ }
+
+ return acc;
+ }, []) ?? [];
+ const { users, isLoading } = useAdminUsers(
+ { filter: { id: { in: selectedUsers } } },
+ {
+ enabled: selectedUsers.length > 0,
+ }
+ );
+
const allowedAttributes = useAllowedAttributes(contentType, slug);
const displayedFilters = allowedAttributes.map((name) => {
+ if (AUTHOR_ATTRIBUTES.includes(name)) {
+ return {
+ name,
+ metadatas: {
+ label: formatMessage({
+ id: `content-manager.components.Filters.${name}`,
+ defaultMessage: name,
+ }),
+ customOperators: [
+ {
+ intlLabel: { id: 'components.FilterOptions.FILTER_TYPES.$eq', defaultMessage: 'is' },
+ value: '$eq',
+ },
+ {
+ intlLabel: {
+ id: 'components.FilterOptions.FILTER_TYPES.$ne',
+ defaultMessage: 'is not',
+ },
+ value: '$ne',
+ },
+ ],
+ customInput: AdminUsersFilter,
+ options: users.map((user) => ({
+ label: user.firstname,
+ customValue: user.id.toString(),
+ })),
+ },
+ fieldSchema: {
+ type: 'relation',
+ mainField: { name: 'id' },
+ trackedEvent: {
+ name: 'didFilterEntries',
+ properties: { useRelation: true },
+ },
+ },
+ };
+ }
+
const attribute = contentType.attributes[name];
const { type, enum: options } = attribute;
@@ -28,6 +91,10 @@ const AttributeFilter = ({ contentType, slug, metadatas }) => {
};
});
+ if (isLoading) {
+ return null;
+ }
+
return ;
};