diff --git a/frontend/src/component/common/Table/cells/FeatureSeenCell/LastSeenTooltip.tsx b/frontend/src/component/common/Table/cells/FeatureSeenCell/LastSeenTooltip.tsx index f5cd431b0118..18c7b6d9860d 100644 --- a/frontend/src/component/common/Table/cells/FeatureSeenCell/LastSeenTooltip.tsx +++ b/frontend/src/component/common/Table/cells/FeatureSeenCell/LastSeenTooltip.tsx @@ -1,6 +1,6 @@ import { styled, SxProps, Theme, Typography } from '@mui/material'; import TimeAgo from 'react-timeago'; -import { IEnvironments, IFeatureEnvironment } from 'interfaces/featureToggle'; +import { ILastSeenEnvironments } from 'interfaces/featureToggle'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useLastSeenColors } from 'component/feature/FeatureView/FeatureEnvironmentSeen/useLastSeenColors'; @@ -56,7 +56,7 @@ const StyledValue = styled('div', { interface ILastSeenTooltipProps { featureLastSeen: string; - environments?: IEnvironments[] | IFeatureEnvironment[]; + environments?: ILastSeenEnvironments[]; className?: string; sx?: SxProps; } diff --git a/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen.tsx b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen.tsx index 1ce06f75cf11..8e2253674ce6 100644 --- a/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen.tsx @@ -1,17 +1,18 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import TimeAgo from 'react-timeago'; import { LastSeenTooltip } from 'component/common/Table/cells/FeatureSeenCell/LastSeenTooltip'; -import React, { FC, ReactElement } from 'react'; -import { IEnvironments, IFeatureEnvironment } from 'interfaces/featureToggle'; +import { FC, ReactElement } from 'react'; +import { ILastSeenEnvironments } from 'interfaces/featureToggle'; import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver'; import { Box, styled, SxProps } from '@mui/material'; import { ReactComponent as UsageLine } from 'assets/icons/usage-line.svg'; import { ReactComponent as UsageRate } from 'assets/icons/usage-rate.svg'; import { useLastSeenColors } from './useLastSeenColors'; +import { getLatestLastSeenAt } from './getLatestLastSeenAt'; interface IFeatureEnvironmentSeenProps { featureLastSeen: string | undefined; - environments: IEnvironments[] | IFeatureEnvironment[]; + environments: ILastSeenEnvironments[]; sx?: SxProps; } @@ -75,43 +76,42 @@ export const FeatureEnvironmentSeen = ({ sx, }: IFeatureEnvironmentSeenProps) => { const getColor = useLastSeenColors(); + + const lastSeen = getLatestLastSeenAt(environments) || featureLastSeen; + return ( - { - const [color, textColor] = getColor(unit); - return ( - - } - color={color} - > - - - ); - }} - /> - ) - } - elseShow={ + <> + {lastSeen ? ( + { + const [color, textColor] = getColor(unit); + return ( + + } + color={color} + > + + + ); + }} + /> + ) : ( - } - /> + )} + ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.test.ts b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.test.ts new file mode 100644 index 000000000000..98e621e7133a --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.test.ts @@ -0,0 +1,48 @@ +import { IEnvironments } from 'interfaces/featureToggle'; + +import { getLatestLastSeenAt } from './getLatestLastSeenAt'; + +describe('getLatestLastSeenAt', () => { + test('should return the most recent lastSeenAt date', () => { + const input: IEnvironments[] = [ + { + name: 'test1', + lastSeenAt: '2023-10-22T08:48:11.869Z', + enabled: false, + variantCount: 0, + }, + { + name: 'test2', + lastSeenAt: '2023-10-23T08:48:11.869Z', + enabled: true, + variantCount: 0, + }, + { + name: 'test3', + lastSeenAt: '2023-10-24T08:48:11.869Z', + enabled: true, + variantCount: 0, + }, + ]; + const expected = '2023-10-24T08:48:11.869Z'; + expect(getLatestLastSeenAt(input)).toBe(expected); + }); + + test('should handle an empty array', () => { + const input: IEnvironments[] = []; + const expected = null; + expect(getLatestLastSeenAt(input)).toBe(expected); + }); + + test('should not fail with non-standard date formats', () => { + const input: IEnvironments[] = [ + { + name: 'test', + lastSeenAt: 'Some Invalid Date', + enabled: true, + variantCount: 0, + }, + ]; + expect(() => getLatestLastSeenAt(input)).not.toThrow(); + }); +}); diff --git a/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.ts b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.ts new file mode 100644 index 000000000000..03474e4a982d --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureEnvironmentSeen/getLatestLastSeenAt.ts @@ -0,0 +1,19 @@ +import { ILastSeenEnvironments } from 'interfaces/featureToggle'; + +export const getLatestLastSeenAt = ( + environments: ILastSeenEnvironments[], +): string | null => { + try { + if (!Array.isArray(environments) || environments.length === 0) { + return null; + } + + return environments + .filter((item) => Boolean(item.lastSeenAt)) + .map((item) => new Date(item.lastSeenAt!)) + .reduce((latest, current) => (current > latest ? current : latest)) + .toISOString(); + } catch (error) { + return null; + } +}; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx index 9b86853ad1ca..b56192828bda 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx @@ -1,4 +1,7 @@ -import { IFeatureToggle } from 'interfaces/featureToggle'; +import { + IFeatureToggle, + ILastSeenEnvironments, +} from 'interfaces/featureToggle'; import { styled } from '@mui/material'; import { useLocationSettings } from 'hooks/useLocationSettings'; import { formatDateYMD } from 'utils/formatDate'; @@ -34,6 +37,13 @@ export const FeatureOverviewSidePanelDetails = ({ uiConfig.flags.lastSeenByEnvironment, ); + const lastSeenEnvironments: ILastSeenEnvironments[] = + feature.environments?.map((env) => ({ + name: env.name, + lastSeenAt: env.lastSeenAt, + enabled: env.enabled, + })); + return ( {header} @@ -50,7 +60,7 @@ export const FeatureOverviewSidePanelDetails = ({ {showLastSeenByEnvironment && ( )} diff --git a/frontend/src/interfaces/featureToggle.ts b/frontend/src/interfaces/featureToggle.ts index 8c64b94050e1..5e258ed98469 100644 --- a/frontend/src/interfaces/featureToggle.ts +++ b/frontend/src/interfaces/featureToggle.ts @@ -22,6 +22,11 @@ export interface IEnvironments { hasEnabledStrategies?: boolean; } +export type ILastSeenEnvironments = Pick< + IEnvironments, + 'name' | 'enabled' | 'lastSeenAt' +>; + export interface IFeatureToggle { stale: boolean; archived: boolean; @@ -52,7 +57,7 @@ export interface IFeatureEnvironment { enabled: boolean; strategies: IFeatureStrategy[]; variants?: IFeatureVariant[]; - lastSeenAt?: Date; + lastSeenAt?: string; } export interface IFeatureEnvironmentWithCrEnabled extends IFeatureEnvironment {