diff --git a/web/src/components/Incidents/AlertsChart/AlertsChart.tsx b/web/src/components/Incidents/AlertsChart/AlertsChart.tsx index 43dd8758..9c113894 100644 --- a/web/src/components/Incidents/AlertsChart/AlertsChart.tsx +++ b/web/src/components/Incidents/AlertsChart/AlertsChart.tsx @@ -32,6 +32,7 @@ import { generateDateArray, generateAlertsDateArray, getCurrentTime, + isInTimeWindow, } from '../utils'; import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime'; import { useTranslation } from 'react-i18next'; @@ -41,7 +42,7 @@ import { MonitoringState } from '../../../store/store'; import { isEmpty } from 'lodash-es'; import { DataTestIDs } from '../../data-test'; -const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => { +const AlertsChart = ({ theme, daysSpan }: { theme: 'light' | 'dark'; daysSpan: number }) => { const dispatch = useDispatch(); const [chartContainerHeight, setChartContainerHeight] = useState(); const [chartHeight, setChartHeight] = useState(); @@ -72,8 +73,13 @@ const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => { const chartData: AlertsChartBar[][] = useMemo(() => { if (!Array.isArray(alertsData) || alertsData.length === 0) return []; - return alertsData.map((alert) => createAlertsChartBars(alert)); - }, [alertsData]); + return alertsData + .filter((alert) => { + const lastTimestamp = alert.values[alert.values.length - 1][0]; + return isInTimeWindow(lastTimestamp, daysSpan, currentTime); + }) + .map((alert) => createAlertsChartBars(alert)); + }, [alertsData, currentTime, daysSpan]); useEffect(() => { setChartContainerHeight(chartData?.length < 5 ? 300 : chartData?.length * 55); diff --git a/web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx b/web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx index b5d27ad1..7fde46f0 100644 --- a/web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx +++ b/web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx @@ -33,6 +33,7 @@ import { calculateIncidentsChartDomain, createIncidentsChartBars, generateDateArray, + isInTimeWindow, } from '../utils'; import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime'; import { useTranslation } from 'react-i18next'; @@ -84,10 +85,15 @@ const IncidentsChart = ({ const chartData = useMemo(() => { if (!Array.isArray(incidentsData) || incidentsData.length === 0) return []; - const filteredIncidents = selectedGroupId + const groupFilteredIncidents = selectedGroupId ? incidentsData.filter((incident) => incident.group_id === selectedGroupId) : incidentsData; + const filteredIncidents = groupFilteredIncidents.filter((incident) => { + const lastTimestamp = incident.values[incident.values.length - 1][0]; + return isInTimeWindow(lastTimestamp, chartDays * (60 * 60 * 24 * 1000), currentTime); + }); + // Create chart bars and sort by original x values to maintain proper order const chartBars = filteredIncidents.map((incident) => createIncidentsChartBars(incident, dateValues), @@ -96,7 +102,7 @@ const IncidentsChart = ({ // Reassign consecutive x values to eliminate gaps between bars return chartBars.map((bars, index) => bars.map((bar) => ({ ...bar, x: index + 1 }))); - }, [incidentsData, dateValues, selectedGroupId]); + }, [incidentsData, dateValues, selectedGroupId, currentTime, chartDays]); useEffect(() => { setIsLoading(false); @@ -175,7 +181,9 @@ const IncidentsChart = ({ if (datum.nodata) { return ''; } - const startDate = dateTimeFormatter(i18n.language).format(new Date(datum.y0)); + const startDate = dateTimeFormatter(i18n.language).format( + new Date(datum.originalStartDate), + ); const endDate = datum.firing ? '---' : dateTimeFormatter(i18n.language).format(new Date(datum.y)); diff --git a/web/src/components/Incidents/IncidentsPage.tsx b/web/src/components/Incidents/IncidentsPage.tsx index c8557b24..8142c10d 100644 --- a/web/src/components/Incidents/IncidentsPage.tsx +++ b/web/src/components/Incidents/IncidentsPage.tsx @@ -265,7 +265,12 @@ const IncidentsPage = () => { if (rules && alertsData) { dispatch( setAlertsTableData({ - alertsTableData: groupAlertsForTable(alertsData, rules), + alertsTableData: groupAlertsForTable( + alertsData, + rules, + incidentsLastRefreshTime, + daysSpan, + ), }), ); } @@ -604,7 +609,7 @@ const IncidentsPage = () => { { /> - + )} diff --git a/web/src/components/Incidents/processAlerts.ts b/web/src/components/Incidents/processAlerts.ts index b216aa2a..4bc8a416 100644 --- a/web/src/components/Incidents/processAlerts.ts +++ b/web/src/components/Incidents/processAlerts.ts @@ -291,8 +291,19 @@ export function convertToAlerts( export const groupAlertsForTable = ( alerts: Array, alertingRulesData: Array, + currentTime: number, + daysSpan: number, ): Array => { - const groupedAlerts = alerts.reduce((acc: Array, alert) => { + const filteredAlerts = alerts.filter((alert) => { + const endTime = alert.values[alert.values.length - 1][0]; + const delta = (currentTime - daysSpan) / 1000; + if (endTime < delta) { + return false; + } + return true; + }); + + const groupedAlerts = filteredAlerts.reduce((acc: Array, alert) => { const { component, alertstate, severity, layer, alertname, silenced } = alert; const existingGroup = acc.find((group) => group.component === component); const rule = alertingRulesData?.find((rule) => alertname === rule.name); diff --git a/web/src/components/Incidents/processIncidents.spec.ts b/web/src/components/Incidents/processIncidents.spec.ts index ab6ccd63..1c46b7d0 100644 --- a/web/src/components/Incidents/processIncidents.spec.ts +++ b/web/src/components/Incidents/processIncidents.spec.ts @@ -607,14 +607,6 @@ describe('getIncidentsTimeRanges', () => { const now = getCurrentTime(); describe('basic functionality', () => { - it('should return single range for timespan less than one day', () => { - const timespan = 12 * 60 * 60 * 1000; // 12 hours - const result = getIncidentsTimeRanges(timespan, now); - - expect(result).toHaveLength(1); - expect(result[0].duration).toBe(ONE_DAY); - }); - it('should split longer timespans into daily chunks', () => { const timespan = 3 * ONE_DAY; // 3 days const result = getIncidentsTimeRanges(timespan, now); diff --git a/web/src/components/Incidents/processIncidents.ts b/web/src/components/Incidents/processIncidents.ts index 803123f1..6f5fd7c6 100644 --- a/web/src/components/Incidents/processIncidents.ts +++ b/web/src/components/Incidents/processIncidents.ts @@ -167,7 +167,8 @@ export const getIncidentsTimeRanges = ( currentTime: number, ): Array<{ endTime: number; duration: number }> => { const ONE_DAY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds - const startTime = currentTime - timespan; + const FIFTEEN_DAYS = 15 * ONE_DAY; + const startTime = currentTime - FIFTEEN_DAYS; const timeRanges = [{ endTime: startTime + ONE_DAY, duration: ONE_DAY }]; while (timeRanges[timeRanges.length - 1].endTime < currentTime) { diff --git a/web/src/components/Incidents/utils.ts b/web/src/components/Incidents/utils.ts index a778c0ff..4ab16608 100644 --- a/web/src/components/Incidents/utils.ts +++ b/web/src/components/Incidents/utils.ts @@ -72,6 +72,24 @@ export const isResolved = (lastTimestamp: number, currentTime: number): boolean return delta >= threshold; }; +/** + * Checks if the last timestamp is in the time window. + * @param lastTimestamp - The last timestamp in the incident/alert (in seconds) + * @param daysSpan - The number of days in the time window (in milliseconds). + * @param currentTime - The current time in milliseconds. + * @returns true if the last timestamp is in the time window, false otherwise. + */ +export const isInTimeWindow = ( + lastTimestamp: number, + daysSpan: number, + currentTime: number, +): boolean => { + // convert chartDays to ms and then convert the result to seconds + const timeWindow = (currentTime - daysSpan) / 1000; + // if endTime is lower than currentTime-chartDays, return false else true + return lastTimestamp >= timeWindow; +}; + /** * Inserts padding data points to ensure the chart renders correctly. * This allows the chart to properly render events, especially single data points. @@ -273,6 +291,7 @@ export const createIncidentsChartBars = (incident: Incident, dateArray: SpanDate const groupedData = consolidateAndMergeIntervals(incident, dateArray); const data: { + originalStartDate: Date; y0: Date; y: Date; x: number; @@ -296,8 +315,17 @@ export const createIncidentsChartBars = (incident: Incident, dateArray: SpanDate const severity = getSeverityName(groupedData[i][2]); const isLastElement = i === groupedData.length - 1; + let startDateTimestamp = groupedData[i][0]; + // if startDateTimestamp is more than 24 hours before the dateArray[0], set startDateTimestamp to 24 hours before the dateArray[0] + // in a way to avoid the chart to be too far in the past + // 86400 is 24 hours in seconds + if (startDateTimestamp < dateArray[0] - 86400) { + startDateTimestamp = dateArray[0] - 86400; + } + data.push({ - y0: new Date(groupedData[i][0] * 1000), + originalStartDate: new Date(groupedData[i][0] * 1000), + y0: new Date(startDateTimestamp * 1000), y: new Date(groupedData[i][1] * 1000), x: incident.x, name: severity,