2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Added staff filtering to AdminX history view (#18104)

refs https://github.com/TryGhost/Product/issues/3832

---

This pull request adds a new feature to filter and view the history of
actions performed by staff users in the admin settings. It introduces a
new route and a MultiSelect component for the history modal, and updates
the UserDetailModal component to link to the history modal with the user
parameter.
This commit is contained in:
Jono M 2023-09-13 08:42:58 +01:00 committed by GitHub
parent 46897bdb3a
commit 9bf40795d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 22 deletions

View file

@ -72,6 +72,7 @@ const modalPaths: {[key: string]: () => Promise<{default: React.FC<NiceModalHocP
'newsletters/add': AddNewsletterModal,
'newsletters/show/:id': NewsletterDetailModal,
'history/view': HistoryModal,
'history/view/:user': HistoryModal,
'integrations/zapier': ZapierModal,
'integrations/slack': SlackModal,
'integrations/amp': AmpModal,

View file

@ -5,12 +5,15 @@ import InfiniteScrollListener from '../../../admin-x-ds/global/InfiniteScrollLis
import List from '../../../admin-x-ds/global/List';
import ListItem from '../../../admin-x-ds/global/ListItem';
import Modal from '../../../admin-x-ds/global/modal/Modal';
import MultiSelect from '../../../admin-x-ds/global/form/MultiSelect';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import Popover from '../../../admin-x-ds/global/Popover';
import Toggle from '../../../admin-x-ds/global/form/Toggle';
import ToggleGroup from '../../../admin-x-ds/global/form/ToggleGroup';
import useRouting from '../../../hooks/useRouting';
import useStaffUsers from '../../../hooks/useStaffUsers';
import {Action, getActionTitle, getContextResource, getLinkTarget, isBulkAction, useBrowseActions} from '../../../api/actions';
import {RoutingModalProps} from '../../providers/RoutingProvider';
import {generateAvatarColor, getInitials} from '../../../utils/helpers';
import {useCallback, useState} from 'react';
@ -62,28 +65,45 @@ const HistoryFilterToggle: React.FC<{
};
const HistoryFilter: React.FC<{
userId?: string;
excludedEvents: string[];
excludedResources: string[];
toggleEventType: (event: string, included: boolean) => void;
toggleResourceType: (resource: string, included: boolean) => void;
}> = ({excludedEvents, excludedResources, toggleEventType, toggleResourceType}) => {
}> = ({userId, excludedEvents, excludedResources, toggleEventType, toggleResourceType}) => {
const {updateRoute} = useRouting();
const {users} = useStaffUsers();
return (
<Popover position='right' trigger={<Button label='Filter' link />}>
<div className='flex w-[220px] flex-col gap-8 p-5'>
<ToggleGroup>
<HistoryFilterToggle excludedItems={excludedEvents} item='added' label='Added' toggleItem={toggleEventType} />
<HistoryFilterToggle excludedItems={excludedEvents} item='edited' label='Edited' toggleItem={toggleEventType} />
<HistoryFilterToggle excludedItems={excludedEvents} item='deleted' label='Deleted' toggleItem={toggleEventType} />
</ToggleGroup>
<ToggleGroup>
<HistoryFilterToggle excludedItems={excludedResources} item='post' label='Posts' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='page' label='Pages' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='tag' label='Tags' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='offer,product' label='Tiers & offers' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='api_key,integration,setting,user,webhook' label='Settings & staff' toggleItem={toggleResourceType} />
</ToggleGroup>
</div>
</Popover>
<div className='flex items-center gap-4'>
<Popover position='right' trigger={<Button label='Filter' link />}>
<div className='flex w-[220px] flex-col gap-8 p-5'>
<ToggleGroup>
<HistoryFilterToggle excludedItems={excludedEvents} item='added' label='Added' toggleItem={toggleEventType} />
<HistoryFilterToggle excludedItems={excludedEvents} item='edited' label='Edited' toggleItem={toggleEventType} />
<HistoryFilterToggle excludedItems={excludedEvents} item='deleted' label='Deleted' toggleItem={toggleEventType} />
</ToggleGroup>
<ToggleGroup>
<HistoryFilterToggle excludedItems={excludedResources} item='post' label='Posts' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='page' label='Pages' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='tag' label='Tags' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='offer,product' label='Tiers & offers' toggleItem={toggleResourceType} />
<HistoryFilterToggle excludedItems={excludedResources} item='api_key,integration,setting,user,webhook' label='Settings & staff' toggleItem={toggleResourceType} />
</ToggleGroup>
</div>
</Popover>
{userId ?
<Button label='Clear search' link onClick={() => updateRoute('history/view')} /> :
<div className='w-[200px]'>
<MultiSelect
options={users.map(user => ({label: user.name, value: user.id}))}
placeholder='Search staff'
values={[]}
onChange={([option]) => updateRoute(`history/view/${option.value}`)}
/>
</div>
}
</div>
);
};
@ -131,13 +151,12 @@ const formatDateForFilter = (date: Date) => {
const PAGE_SIZE = 200;
const HistoryModal = NiceModal.create(() => {
const HistoryModal = NiceModal.create<RoutingModalProps>(({params}) => {
const modal = useModal();
const {updateRoute} = useRouting();
const [excludedEvents, setExcludedEvents] = useState<string[]>([]);
const [excludedResources, setExcludedResources] = useState<string[]>(['label']);
const [user] = useState<string>();
const {data, fetchNextPage} = useBrowseActions({
searchParams: {
@ -146,12 +165,12 @@ const HistoryModal = NiceModal.create(() => {
filter: [
excludedEvents.length && `event:-[${excludedEvents.join(',')}]`,
excludedResources.length && `resource_type:-[${excludedResources.join(',')}]`,
user && `actor_id:${user}`
params?.user && `actor_id:${params.user}`
].filter(Boolean).join('+')
},
getNextPageParams: (lastPage, otherParams) => ({
...otherParams,
filter: [otherParams.filter, `created_at:<'${formatDateForFilter(new Date(lastPage.actions[lastPage.actions.length - 1].created_at))}'`].join('+')
filter: [otherParams.filter, lastPage.actions.length && `created_at:<'${formatDateForFilter(new Date(lastPage.actions[lastPage.actions.length - 1].created_at))}'`].join('+')
}),
keepPreviousData: true
});
@ -183,6 +202,7 @@ const HistoryModal = NiceModal.create(() => {
excludedResources={excludedResources}
toggleEventType={(event, included) => toggleValue(setExcludedEvents, event, !included)}
toggleResourceType={(resource, included) => toggleValue(setExcludedResources, resource, !included)}
userId={params?.user}
/>}
onOk={() => {
modal.remove();

View file

@ -623,7 +623,8 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
id: 'view-user-activity',
label: 'View user activity',
onClick: () => {
// TODO: show user activity
mainModal.remove();
updateRoute(`history/view/${userData.id}`);
}
}
]);