Skip to content

Commit

Permalink
management-service + client: Updated dashboard data (#144)
Browse files Browse the repository at this point in the history
* management-service: Created new endpoint to fetch the newest report without sources
client: Updated fetching dashboard report to align with backend changes

* client: Unified icons across interface

* client: Report incident refactored - removed sourced as they are in different endpoint with pagination
management-service: Updated return type of report incidents

* client: refactor after review

* client: refactor after review

* client: refactor after review

* client: refactor after review

* client: refactor after review
  • Loading branch information
MDybek authored Dec 8, 2024
1 parent 5f364c1 commit ecda78b
Show file tree
Hide file tree
Showing 25 changed files with 349 additions and 125 deletions.
24 changes: 19 additions & 5 deletions client/src/api/managment-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface ReportAwaitingGeneration {
reportType: ReportType;
sinceMs: number;
toMs: number;
requestedAtMs: number;
[key: string]: string | number;
}

Expand All @@ -70,13 +71,14 @@ export interface ReportDetails {
id: string;
clusterId: string;
title: string;
urgency: UrgencyLevel;
urgency: UrgencyLevel | null;
requestedAtMs: number;
sinceMs: number;
toMs: number;
totalApplicationEntries: number;
totalNodeEntries: number;
analyzedApplications: number;
analyzedNodes: number;
sinceMs: number;
toMs: number;
}

export interface ClusterSummary {
Expand Down Expand Up @@ -162,7 +164,8 @@ export interface ApplicationIncident {
urgency: UrgencyLevel;
accuracy: AccuracyLevel;
recommendation: string;
sources: ApplicationIncidentSource[];
sinceMs: number;
toMs: number;
}

export interface ApplicationIncidentSource {
Expand All @@ -184,7 +187,8 @@ export interface NodeIncident {
accuracy: AccuracyLevel;
summary: string;
recommendation: string;
sources: NodeIncidentSource[];
sinceMs: number;
toMs: number;
}

export interface AllIncidentsFromReport {
Expand Down Expand Up @@ -508,6 +512,16 @@ class ManagmentServiceApi {
return response.data;
}

public async getLatestReport(): Promise<
ReportDetails
> {
await this.refreshTokenIfExpired();
const response = await this.axiosInstance.get(
'/api/v1/reports/latest',
);
return response.data;
}

public async getApplicationIncident(
id: string,
): Promise<ApplicationIncident> {
Expand Down
1 change: 1 addition & 0 deletions client/src/assets/cycle-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions client/src/assets/hive-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions client/src/components/SVGIcon/SVGIcon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@
mask: url('/src/assets/clusters-icon.svg') no-repeat center;
}

.cycle-icon {
@extend .svg-icon;
mask: url('/src/assets/cycle-icon.svg') no-repeat center;
}

.hive-icon {
@extend .svg-icon;
mask: url('/src/assets/hive-icon.svg') no-repeat center;
}

.clusters-icon--white {
@extend .svg-icon;
mask: url('/src/assets/clusters-icon.svg') no-repeat center;
Expand Down
34 changes: 7 additions & 27 deletions client/src/hooks/useReportStats.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
UrgencyLevel,
AllIncidentsFromReport,
ManagmentServiceApiInstance,
ReportDetails,
UrgencyLevel,
} from 'api/managment-service';
import { groupBy } from 'lib/arrays';
import { useEffect, useState } from 'react';
Expand Down Expand Up @@ -76,43 +76,25 @@ const groupIncidentsByNode = <T extends { nodeName: string }>(
return groupBy(incidents, (incident) => incident.nodeName);
};

const useReportDetails = (reportId: string | null) => {
const [report, setReport] = useState<ReportDetails | null>(null);
export const useReportDetails = (report: ReportDetails | null) => {
const [incidents, setIncidents] = useState<AllIncidentsFromReport | null>(
null,
);
const [isReportLoading, setIsReportLoading] = useState(true);
const [areIncidentsLoading, setAreIncidentsLoading] = useState(true);
const [incidentStats, setIncidentStats] = useState<IncidentStats | null>(
null,
);

useEffect(() => {
if (!reportId) return;

const fetchReport = async (id: string) => {
try {
const reportData = await ManagmentServiceApiInstance.getReport(id);
setReport(reportData);
setIsReportLoading(false);
} catch (e: unknown) {
console.error('Failed to fetch report by id', id);
}
};

fetchReport(reportId);
}, [reportId]);

useEffect(() => {
if (!report || !reportId) return;
if (!report) return;
const fetchIncidents = async () => {
try {
const incidentsData =
await ManagmentServiceApiInstance.getIncidentsFromReport(reportId);
await ManagmentServiceApiInstance.getIncidentsFromReport(report.id);
setIncidents(incidentsData);
setAreIncidentsLoading(false);
} catch (e: unknown) {
console.error('Failed to fetch report by id', reportId);
console.error('Failed to fetch report by id', report.id);
}
};

Expand All @@ -121,7 +103,7 @@ const useReportDetails = (reportId: string | null) => {
}

fetchIncidents();
}, [reportId, report]);
}, [report]);

useEffect(() => {
if (!incidents) return;
Expand Down Expand Up @@ -179,11 +161,9 @@ const useReportDetails = (reportId: string | null) => {

return {
incidents,
report: report,
incidentStats,
areIncidentsLoading,
isReportLoading,
};
};

export default useReportDetails;
export default useReportDetails;
27 changes: 15 additions & 12 deletions client/src/pages/Clusters/Clusters.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import SectionComponent from 'components/SectionComponent/SectionComponent';
import PageTemplate from 'components/PageTemplate/PageTemplate';
import HeaderWithIcon from 'components/PageTemplate/components/HeaderWithIcon/HeaderWithIcon';
import Table, { TableColumn } from 'components/Table/Table';
import Table, {TableColumn} from 'components/Table/Table';
import './Clusters.scss';
import Channels from './components/NotificationChannelsColumn/NotificationChannelsColumn';
import { useEffect, useState } from 'react';
import {useEffect, useState} from 'react';
import {
ClusterSummary,
ManagmentServiceApiInstance,
Expand All @@ -15,7 +15,7 @@ import SVGIcon from 'components/SVGIcon/SVGIcon';
import LinkComponent from 'components/LinkComponent/LinkComponent.tsx';
import ReportActionsCell from './ReportActionsCell';
import AccuracyBadge from 'components/AccuracyBadge/AccuracyBadge.tsx';
import { dateTimeFromTimestampMs } from 'lib/date.ts';
import {dateTimeFromTimestampMs} from 'lib/date.ts';
import CenteredSpinner from 'components/CenteredSpinner/CenteredSpinner';

interface ClusterDataRow {
Expand Down Expand Up @@ -79,18 +79,18 @@ const columns: Array<TableColumn<ClusterDataRow>> = [
header: 'Notification',
columnKey: 'notificationChannels',
customComponent: ({
notificationChannels,
}: {
notificationChannels,
}: {
notificationChannels: NotificationChannelColumn[];
}) => {
return <Channels channels={notificationChannels} />;
return <Channels channels={notificationChannels}/>;
},
},
{
header: 'Accuracy',
columnKey: 'accuracy',
customComponent: (row: ClusterDataRow) => {
return <AccuracyBadge label={row.accuracy} />;
return <AccuracyBadge label={row.accuracy}/>;
},
},
{
Expand All @@ -101,7 +101,7 @@ const columns: Array<TableColumn<ClusterDataRow>> = [
header: 'Reports',
columnKey: 'actions',
customComponent: (row: ClusterDataRow) => {
return <ReportActionsCell clusterId={row.name} />;
return <ReportActionsCell clusterId={row.name}/>;
},
},
];
Expand Down Expand Up @@ -135,17 +135,20 @@ const Clusters = () => {
fetchClusters();
}, []);

const header = <HeaderWithIcon title={'Clusters'} />;
const header = <HeaderWithIcon
title={'Clusters'}
icon={<SVGIcon iconName="clusters-icon"/>}
/>;

return (
<PageTemplate header={header}>
<SectionComponent
title={'Clusters'}
icon={<SVGIcon iconName="clusters-icon" />}
icon={<SVGIcon iconName="hive-icon"/>}
>
{isLoading && <CenteredSpinner />}
{isLoading && <CenteredSpinner/>}
{!isLoading && clusters.length > 0 && (
<Table columns={columns} rows={clusters} />
<Table columns={columns} rows={clusters}/>
)}
{!isLoading && clusters.length === 0 && (
<div>No registered clusters yet</div>
Expand Down
32 changes: 13 additions & 19 deletions client/src/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
import { ManagmentServiceApiInstance } from 'api/managment-service';
import {ManagmentServiceApiInstance, ReportDetails} from 'api/managment-service';
import PageTemplate from 'components/PageTemplate/PageTemplate';
import HeaderWithIcon from 'components/PageTemplate/components/HeaderWithIcon/HeaderWithIcon';
import useReportDetails from 'hooks/useReportStats';

import { useEffect, useState } from 'react';
import ReportDetailsSection from './components/ReportDetailsSection/ReportDetailsSection';
import useReportDetails from 'hooks/useReportStats';

const Home = () => {
const [lastReportId, setLastReportId] = useState<string | null>(null);
const [lastReport, setLastReport] = useState<ReportDetails | null>(null);
const [isReportLoading, setIsReportLoading] = useState(true);

useEffect(() => {
const fetchReports = async () => {
const getLatestReport = async () => {
try {
const [onDemandReports, scheduledReports] = await Promise.all([
ManagmentServiceApiInstance.getReports('ON_DEMAND'),
ManagmentServiceApiInstance.getReports('SCHEDULED'),
]);
const reports = [...onDemandReports, ...scheduledReports];
if (reports.length > 0) {
reports.sort((a, b) => b.requestedAtMs - a.requestedAtMs);
setLastReportId(reports[0].id);
const latestReport = await ManagmentServiceApiInstance.getLatestReport();
if (latestReport) {
setLastReport(latestReport);
}
} catch (e: unknown) {
console.error('Failed to fetch reports');
console.error('Failed to fetch the latest report', e);
}
};

fetchReports();
getLatestReport();
setIsReportLoading(false);
}, []);

const {
incidents,
report,
incidentStats,
areIncidentsLoading,
isReportLoading,
} = useReportDetails(lastReportId);
} = useReportDetails(lastReport);

return (
<PageTemplate header={<HeaderWithIcon title={'Dashboard'} />}>
<ReportDetailsSection
report={report}
report={lastReport}
incidents={incidents}
incidentStats={incidentStats}
areIncidentsLoading={areIncidentsLoading}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import SectionComponent from 'components/SectionComponent/SectionComponent';
import SVGIcon from 'components/SVGIcon/SVGIcon';
import ReportTitle from 'pages/Home/components/ReportTitle/ReportTitle';
import { IncidentStats, ReportStats } from 'hooks/useReportStats';
import { ReportDetails } from 'api/managment-service';
import {IncidentStats, ReportStats} from 'hooks/useReportStats';
import StatisticsDisplay, {
StatItemData,
} from 'components/StatisticsDisplay/StatisticsDisplay';
Expand All @@ -16,8 +15,9 @@ import {
urgencyIncidentCount,
} from 'types/incident';
import './ReportDetailsSection.scss';
import { useNavigate } from 'react-router-dom';
import {useNavigate} from 'react-router-dom';
import CenteredSpinner from 'components/CenteredSpinner/CenteredSpinner';
import {ReportDetails} from 'api/managment-service.ts';

const statItems = (
report: ReportDetails,
Expand Down Expand Up @@ -73,14 +73,14 @@ const statItems = (
title: 'Node with highest number of incidents',
value: stats.nodeWithMostIncidents.nodeName,
unit: '',
valueColor: colors.urgency.low,
valueColor: colors.urgency.high,
});

defaultStats.push({
title: `Incidents from ${stats.nodeWithMostIncidents.nodeName}`,
value: stats.nodeWithMostIncidents.numberOfIncidents,
unit: 'incidents',
valueColor: colors.urgency.low,
valueColor: colors.urgency.high,
});
}

Expand All @@ -89,14 +89,14 @@ const statItems = (
title: 'Application with highest number of incidents',
value: stats.applicationWithMostIncidents.applicationName,
unit: '',
valueColor: colors.urgency.low,
valueColor: colors.urgency.high,
});

defaultStats.push({
title: `Incidents from ${stats.applicationWithMostIncidents.applicationName}`,
value: stats.applicationWithMostIncidents.numberOfIncidents,
unit: 'incidents',
valueColor: colors.urgency.low,
valueColor: colors.urgency.high,
});
}

Expand All @@ -123,9 +123,10 @@ const ReportDetailsSection = ({
if (isReportLoading || !report) {
return <CenteredSpinner />;
}

return (
<SectionComponent
icon={<SVGIcon iconName="chart-icon" />}
icon={<SVGIcon iconName="chart-icon"/>}
title={
<ReportTitle
source={report.clusterId}
Expand All @@ -135,7 +136,7 @@ const ReportDetailsSection = ({
}
>
<div className="dashboard-report-details-section">
{areIncidentsLoading && <CenteredSpinner />}
{areIncidentsLoading && <CenteredSpinner/>}
{incidents && incidentStats && (
<div className="dashboard-report-details-section__incidents">
<ReportDetailsSubsection title={'Statistics'}>
Expand Down Expand Up @@ -170,4 +171,4 @@ const ReportDetailsSection = ({
</SectionComponent>
);
};
export default ReportDetailsSection;
export default ReportDetailsSection;
Loading

0 comments on commit ecda78b

Please sign in to comment.