diff --git a/frontend/src/component/events/EventTimeline/EventTimeline.tsx b/frontend/src/component/events/EventTimeline/EventTimeline.tsx index 915ed492e453..956f588bd073 100644 --- a/frontend/src/component/events/EventTimeline/EventTimeline.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimeline.tsx @@ -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; @@ -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( - timeSpanOptions[0], - ); - const [environment, setEnvironment] = useState(); + const { timeSpan, environment, setTimeSpan, setEnvironment } = + useEventTimeline(); const endDate = new Date(); const startDate = sub(endDate, timeSpan.value); diff --git a/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx b/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx index 0cb1d0ef86ed..d7c29ea76d4b 100644 --- a/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx @@ -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', @@ -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; @@ -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]; diff --git a/frontend/src/component/events/EventTimeline/useEventTimeline.ts b/frontend/src/component/events/EventTimeline/useEventTimeline.ts new file mode 100644 index 000000000000..31467d988689 --- /dev/null +++ b/frontend/src/component/events/EventTimeline/useEventTimeline.ts @@ -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( + 'event-timeline:v1', + defaultState, + ); + + const setField = ( + 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), + }; +}; diff --git a/frontend/src/component/layout/MainLayout/MainLayout.tsx b/frontend/src/component/layout/MainLayout/MainLayout.tsx index 13d4ec961191..43383c050f07 100644 --- a/frontend/src/component/layout/MainLayout/MainLayout.tsx +++ b/frontend/src/component/layout/MainLayout/MainLayout.tsx @@ -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'; @@ -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; @@ -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( ({ 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 @@ -189,22 +176,9 @@ export const MainLayout = forwardRef( minWidth: 0, }} > - - ({ - padding: theme.spacing(2), - backgroundColor: - theme.palette.background.paper, - })} - > - - - + diff --git a/frontend/src/component/layout/MainLayout/MainLayoutEventTimeline.tsx b/frontend/src/component/layout/MainLayout/MainLayoutEventTimeline.tsx new file mode 100644 index 000000000000..f3e0da38e9a2 --- /dev/null +++ b/frontend/src/component/layout/MainLayout/MainLayoutEventTimeline.tsx @@ -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 ( + + ({ + padding: theme.spacing(2), + backgroundColor: theme.palette.background.paper, + })} + > + + + + ); +};