mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
chore: begin front end cleanup (#7865)
Begins cleaning up the front end. Removes the "legacy" event log component in favor of only using the new one. What we do is simply not to show the filters if you're not on enterprise. This means that we'll get pagination (and maybe exports?) for everyone. It also means that you can reverse-engineer the filters and use them even on non-enterprise, as long as you're happy editing URLs manually. However, putting it behind a flag on the front end always exposed that kind of risk, so I don't think this is a bad move.
This commit is contained in:
parent
4738d4a61f
commit
764d03767b
@ -4,17 +4,13 @@ import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import EventCard from 'component/events/EventCard/EventCard';
|
||||
import { useEventSettings } from 'hooks/useEventSettings';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Search } from 'component/common/Search/Search';
|
||||
import theme from 'themes/theme';
|
||||
import { useLegacyEventSearch } from 'hooks/api/getters/useEventSearch/useLegacyEventSearch';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useOnVisible } from 'hooks/useOnVisible';
|
||||
import { styled } from '@mui/system';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { EventLogFilters } from './EventLogFilters';
|
||||
import type { EventSchema } from 'openapi';
|
||||
import { useEventLogSearch } from './useEventLogSearch';
|
||||
import { StickyPaginationBar } from 'component/common/Table/StickyPaginationBar/StickyPaginationBar';
|
||||
import { EventActions } from './EventActions';
|
||||
@ -38,11 +34,13 @@ const StyledFilters = styled(EventLogFilters)({
|
||||
padding: 0,
|
||||
});
|
||||
|
||||
const EventResultWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(2, 4, 4, 4),
|
||||
const EventResultWrapper = styled('div', {
|
||||
shouldForwardProp: (prop) => prop !== 'withFilters',
|
||||
})<{ withFilters: boolean }>(({ theme, withFilters }) => ({
|
||||
padding: theme.spacing(withFilters ? 2 : 4, 4, 4, 4),
|
||||
display: 'flex',
|
||||
flexFlow: 'column',
|
||||
gap: theme.spacing(1),
|
||||
gap: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const Placeholder = styled('li')({
|
||||
@ -51,7 +49,9 @@ const Placeholder = styled('li')({
|
||||
'&[data-loading-events=true]': { zIndex: '1' }, // .skeleton has z-index: 9990
|
||||
});
|
||||
|
||||
const NewEventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
export const EventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const showFilters = useUiFlag('newEventSearch') && isEnterprise();
|
||||
const {
|
||||
events,
|
||||
total,
|
||||
@ -148,11 +148,22 @@ const NewEventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
</PageHeader>
|
||||
}
|
||||
>
|
||||
<EventResultWrapper ref={ref}>
|
||||
<StyledFilters
|
||||
logType={project ? 'project' : feature ? 'flag' : 'global'}
|
||||
state={filterState}
|
||||
onChange={setTableState}
|
||||
<EventResultWrapper ref={ref} withFilters={showFilters}>
|
||||
<ConditionallyRender
|
||||
condition={showFilters}
|
||||
show={
|
||||
<StyledFilters
|
||||
logType={
|
||||
project
|
||||
? 'project'
|
||||
: feature
|
||||
? 'flag'
|
||||
: 'global'
|
||||
}
|
||||
state={filterState}
|
||||
onChange={setTableState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{resultComponent()}
|
||||
</EventResultWrapper>
|
||||
@ -172,92 +183,3 @@ const NewEventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
</PageContent>
|
||||
);
|
||||
};
|
||||
|
||||
export const LegacyEventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
const [query, setQuery] = useState('');
|
||||
const { events, totalEvents, fetchNextPage } = useLegacyEventSearch(
|
||||
project,
|
||||
feature,
|
||||
query,
|
||||
);
|
||||
const fetchNextPageRef = useOnVisible<HTMLDivElement>(fetchNextPage);
|
||||
const { eventSettings, setEventSettings } = useEventSettings();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
// Cache the previous search results so that we can show those while
|
||||
// fetching new results for a new search query in the background.
|
||||
const [cache, setCache] = useState<EventSchema[]>();
|
||||
useEffect(() => events && setCache(events), [events]);
|
||||
|
||||
const onShowData = () => {
|
||||
setEventSettings((prev) => ({ showData: !prev.showData }));
|
||||
};
|
||||
|
||||
const searchInputField = <Search onChange={setQuery} debounceTime={500} />;
|
||||
|
||||
const showDataSwitch = (
|
||||
<FormControlLabel
|
||||
label='Full events'
|
||||
control={
|
||||
<Switch
|
||||
checked={eventSettings.showData}
|
||||
onChange={onShowData}
|
||||
color='primary'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const count = events?.length || 0;
|
||||
const totalCount = totalEvents || 0;
|
||||
const countText = `${count} of ${totalCount}`;
|
||||
|
||||
return (
|
||||
<PageContent
|
||||
header={
|
||||
<PageHeader
|
||||
title={`${title} (${countText})`}
|
||||
actions={
|
||||
<>
|
||||
{showDataSwitch}
|
||||
{!isSmallScreen && searchInputField}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{isSmallScreen && searchInputField}
|
||||
</PageHeader>
|
||||
}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(cache && cache.length === 0)}
|
||||
show={<p>No events found.</p>}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(cache && cache.length > 0)}
|
||||
show={
|
||||
<StyledEventsList>
|
||||
{cache?.map((entry) => (
|
||||
<ConditionallyRender
|
||||
key={entry.id}
|
||||
condition={eventSettings.showData}
|
||||
show={() => <EventJson entry={entry} />}
|
||||
elseShow={() => <EventCard entry={entry} />}
|
||||
/>
|
||||
))}
|
||||
</StyledEventsList>
|
||||
}
|
||||
/>
|
||||
<div ref={fetchNextPageRef} />
|
||||
</PageContent>
|
||||
);
|
||||
};
|
||||
|
||||
export const EventLog = (props: IEventLogProps) => {
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const showFilters = useUiFlag('newEventSearch') && isEnterprise();
|
||||
if (showFilters) {
|
||||
return <NewEventLog {...props} />;
|
||||
} else {
|
||||
return <LegacyEventLog {...props} />;
|
||||
}
|
||||
};
|
||||
|
@ -1,92 +0,0 @@
|
||||
import useSWR from 'swr';
|
||||
import { useCallback, useState, useEffect, useMemo } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import type { EventSchema } from 'openapi';
|
||||
|
||||
const PATH = formatApiPath('api/admin/events/search');
|
||||
|
||||
export interface IUseEventSearchOutput {
|
||||
events?: EventSchema[];
|
||||
fetchNextPage: () => void;
|
||||
loading: boolean;
|
||||
totalEvents?: number;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
interface IEventSearch {
|
||||
type?: string;
|
||||
project?: string;
|
||||
feature?: string;
|
||||
query?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use useEventSearch instead. Remove with flag: newEventSearch
|
||||
*/
|
||||
export const useLegacyEventSearch = (
|
||||
project?: string,
|
||||
feature?: string,
|
||||
query?: string,
|
||||
): IUseEventSearchOutput => {
|
||||
const [events, setEvents] = useState<EventSchema[]>();
|
||||
const [totalEvents, setTotalEvents] = useState<number>(0);
|
||||
const [offset, setOffset] = useState(0);
|
||||
|
||||
const search: IEventSearch = useMemo(
|
||||
() => ({ project, feature, query, limit: 50 }),
|
||||
[project, feature, query],
|
||||
);
|
||||
|
||||
const { data, error, isValidating } = useSWR<{
|
||||
events: EventSchema[];
|
||||
totalEvents?: number;
|
||||
}>([PATH, search, offset], () => searchEvents(PATH, { ...search, offset }));
|
||||
|
||||
// Reset the page when there are new search conditions.
|
||||
useEffect(() => {
|
||||
setOffset(0);
|
||||
setTotalEvents(0);
|
||||
setEvents(undefined);
|
||||
}, [search]);
|
||||
|
||||
// Append results to the page when more data has been fetched.
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setEvents((prev) => [
|
||||
...(prev?.slice(0, offset) || []),
|
||||
...data.events,
|
||||
]);
|
||||
if (data.totalEvents) {
|
||||
setTotalEvents(data.totalEvents);
|
||||
}
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
// Update the offset to fetch more results at the end of the page.
|
||||
const fetchNextPage = useCallback(() => {
|
||||
if (events && !isValidating) {
|
||||
setOffset(events.length);
|
||||
}
|
||||
}, [events, isValidating]);
|
||||
|
||||
return {
|
||||
events,
|
||||
loading: !error && !data,
|
||||
fetchNextPage,
|
||||
totalEvents,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
const searchEvents = (path: string, search: IEventSearch) => {
|
||||
return fetch(path, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(search),
|
||||
})
|
||||
.then(handleErrorResponses('Event history'))
|
||||
.then((res) => res.json());
|
||||
};
|
Loading…
Reference in New Issue
Block a user