1
0
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:
Thomas Heartman 2024-08-14 10:03:28 +02:00 committed by GitHub
parent 4738d4a61f
commit 764d03767b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 24 additions and 194 deletions

View File

@ -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} />;
}
};

View File

@ -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());
};