Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: event timeline persistent state #8240

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions frontend/src/component/events/EventTimeline/EventTimeline.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { styled } from '@mui/material';
import type { EventSchema, EventSchemaType } from 'openapi';
import { useState } from 'react';
import { startOfDay, sub } from 'date-fns';
import type { IEnvironment } from 'interfaces/environments';
import { useEventSearch } from 'hooks/api/getters/useEventSearch/useEventSearch';
import { EventTimelineEvent } from './EventTimelineEvent/EventTimelineEvent';
import {
EventTimelineHeader,
type TimeSpanOption,
timeSpanOptions,
} from './EventTimelineHeader/EventTimelineHeader';
import { EventTimelineHeader } from './EventTimelineHeader/EventTimelineHeader';
import { useEventTimeline } from './useEventTimeline';

export type EnrichedEvent = EventSchema & {
label: string;
Expand Down Expand Up @@ -88,10 +83,8 @@ const RELEVANT_EVENT_TYPES: EventSchemaType[] = [
const toISODateString = (date: Date) => date.toISOString().split('T')[0];

export const EventTimeline = () => {
const [timeSpan, setTimeSpan] = useState<TimeSpanOption>(
timeSpanOptions[0],
);
const [environment, setEnvironment] = useState<IEnvironment | undefined>();
const { timeSpan, environment, setTimeSpan, setEnvironment } =
useEventTimeline();

const endDate = new Date();
const startDate = sub(endDate, timeSpan.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
import type { IEnvironment } from 'interfaces/environments';
import { useEffect, useMemo } from 'react';
import { type TimeSpanOption, timeSpanOptions } from '../useEventTimeline';

const StyledCol = styled('div')(({ theme }) => ({
display: 'flex',
Expand All @@ -22,62 +23,6 @@ const StyledFilter = styled(TextField)(({ theme }) => ({
},
}));

export type TimeSpanOption = {
key: string;
label: string;
value: Duration;
markers: string[];
};

export const timeSpanOptions: TimeSpanOption[] = [
{
key: '30m',
label: 'last 30 min',
value: { minutes: 30 },
markers: ['30 min ago'],
},
{
key: '1h',
label: 'last hour',
value: { hours: 1 },
markers: ['1 hour ago', '30 min ago'],
},
{
key: '3h',
label: 'last 3 hours',
value: { hours: 3 },
markers: ['3 hours ago', '2 hours ago', '1 hour ago'],
},
{
key: '12h',
label: 'last 12 hours',
value: { hours: 12 },
markers: ['12 hours ago', '9 hours ago', '6 hours ago', '3 hours ago'],
},
{
key: '24h',
label: 'last 24 hours',
value: { hours: 24 },
markers: [
'24 hours ago',
'18 hours ago',
'12 hours ago',
'6 hours ago',
],
},
{
key: '48h',
label: 'last 48 hours',
value: { hours: 48 },
markers: [
'48 hours ago',
'36 hours ago',
'24 hours ago',
'12 hours ago',
],
},
];

interface IEventTimelineHeaderProps {
totalEvents: number;
timeSpan: TimeSpanOption;
Expand All @@ -101,7 +46,7 @@ export const EventTimelineHeader = ({
);

useEffect(() => {
if (activeEnvironments.length > 0) {
if (activeEnvironments.length > 0 && !environment) {
const defaultEnvironment =
activeEnvironments.find(({ type }) => type === 'production') ||
activeEnvironments[0];
Expand Down
92 changes: 92 additions & 0 deletions frontend/src/component/events/EventTimeline/useEventTimeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useLocalStorageState } from 'hooks/useLocalStorageState';
import type { IEnvironment } from 'interfaces/environments';

export type TimeSpanOption = {
key: string;
label: string;
value: Duration;
markers: string[];
};

export const timeSpanOptions: TimeSpanOption[] = [
{
key: '30m',
label: 'last 30 min',
value: { minutes: 30 },
markers: ['30 min ago'],
},
{
key: '1h',
label: 'last hour',
value: { hours: 1 },
markers: ['1 hour ago', '30 min ago'],
},
{
key: '3h',
label: 'last 3 hours',
value: { hours: 3 },
markers: ['3 hours ago', '2 hours ago', '1 hour ago'],
},
{
key: '12h',
label: 'last 12 hours',
value: { hours: 12 },
markers: ['12 hours ago', '9 hours ago', '6 hours ago', '3 hours ago'],
},
{
key: '24h',
label: 'last 24 hours',
value: { hours: 24 },
markers: [
'24 hours ago',
'18 hours ago',
'12 hours ago',
'6 hours ago',
],
},
{
key: '48h',
label: 'last 48 hours',
value: { hours: 48 },
markers: [
'48 hours ago',
'36 hours ago',
'24 hours ago',
'12 hours ago',
],
},
];

type EventTimelineState = {
open: boolean;
timeSpan: TimeSpanOption;
environment?: IEnvironment;
};

const defaultState: EventTimelineState = {
open: true,
timeSpan: timeSpanOptions[0],
};

export const useEventTimeline = () => {
const [state, setState] = useLocalStorageState<EventTimelineState>(
'event-timeline:v1',
defaultState,
);

const setField = <K extends keyof EventTimelineState>(
key: K,
value: EventTimelineState[K],
) => {
setState((prevState) => ({ ...prevState, [key]: value }));
};

return {
...state,
setOpen: (open: boolean) => setField('open', open),
setTimeSpan: (timeSpan: TimeSpanOption) =>
setField('timeSpan', timeSpan),
setEnvironment: (environment: IEnvironment) =>
setField('environment', environment),
};
};
46 changes: 10 additions & 36 deletions frontend/src/component/layout/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { forwardRef, useState, type ReactNode } from 'react';
import { forwardRef, type ReactNode } from 'react';
import { Box, Grid, styled, useMediaQuery, useTheme } from '@mui/material';
import Header from 'component/menu/Header/Header';
import OldHeader from 'component/menu/Header/OldHeader';
Expand All @@ -17,8 +17,8 @@ import { DraftBanner } from './DraftBanner/DraftBanner';
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
import { NavigationSidebar } from './NavigationSidebar/NavigationSidebar';
import { useUiFlag } from 'hooks/useUiFlag';
import { EventTimeline } from 'component/events/EventTimeline/EventTimeline';
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
import { useEventTimeline } from 'component/events/EventTimeline/useEventTimeline';
import { MainLayoutEventTimeline } from './MainLayoutEventTimeline';

interface IMainLayoutProps {
children: ReactNode;
Expand Down Expand Up @@ -107,29 +107,16 @@ const MainLayoutContentContainer = styled('div')(({ theme }) => ({
zIndex: 200,
}));

const timelineAnimations = {
start: {
maxHeight: 0,
overflow: 'hidden',
transition: 'max-height 0.3s ease-in-out',
},
enter: {
maxHeight: '105px',
},
leave: {
maxHeight: 0,
},
};

export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
({ children }, ref) => {
const { uiConfig } = useUiConfig();
const { uiConfig, isOss } = useUiConfig();
const projectId = useOptionalPathParam('projectId');
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(
projectId || '',
);
const eventTimeline = useUiFlag('eventTimeline');
const [showTimeline, setShowTimeline] = useState(false);
const eventTimeline = useUiFlag('eventTimeline') && !isOss();
const { open: showTimeline, setOpen: setShowTimeline } =
useEventTimeline();

const sidebarNavigationEnabled = useUiFlag('navigationSidebar');
const StyledMainLayoutContent = sidebarNavigationEnabled
Expand Down Expand Up @@ -189,22 +176,9 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
minWidth: 0,
}}
>
<AnimateOnMount
mounted={eventTimeline && showTimeline}
start={timelineAnimations.start}
enter={timelineAnimations.enter}
leave={timelineAnimations.leave}
>
<Box
sx={(theme) => ({
padding: theme.spacing(2),
backgroundColor:
theme.palette.background.paper,
})}
>
<EventTimeline />
</Box>
</AnimateOnMount>
<MainLayoutEventTimeline
open={eventTimeline && showTimeline}
/>

<StyledMainLayoutContent>
<MainLayoutContentContainer ref={ref}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Box } from '@mui/material';
import { EventTimeline } from 'component/events/EventTimeline/EventTimeline';
import { useEffect, useState } from 'react';

interface IMainLayoutEventTimelineProps {
open: boolean;
}

export const MainLayoutEventTimeline = ({
open,
}: IMainLayoutEventTimelineProps) => {
const [isInitialLoad, setIsInitialLoad] = useState(true);

useEffect(() => {
setIsInitialLoad(false);
}, []);

return (
<Box
sx={{
overflow: 'hidden',
transition: isInitialLoad
? 'none'
: 'max-height 0.3s ease-in-out',
maxHeight: open ? '105px' : '0',
}}
>
<Box
sx={(theme) => ({
padding: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
})}
>
<EventTimeline />
</Box>
</Box>
);
};
Loading