feat(ui): Add hydration in status to dashboard application tiles (#24319)

Signed-off-by: Aditya Raj <adityaraj10600@gmail.com>
This commit is contained in:
Aditya Raj 2025-09-12 02:38:45 +05:30 committed by GitHub
parent 5a8b427322
commit 2229f9d6fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 63 additions and 2 deletions

View file

@ -29,6 +29,7 @@ var appFields = map[string]func(app *v1alpha1.Application) any{
"metadata.creationTimestamp": func(app *v1alpha1.Application) any { return app.CreationTimestamp },
"metadata.deletionTimestamp": func(app *v1alpha1.Application) any { return app.DeletionTimestamp },
"spec": func(app *v1alpha1.Application) any { return app.Spec },
"status.sourceHydrator": func(app *v1alpha1.Application) any { return app.Status.SourceHydrator },
"status.sync.status": func(app *v1alpha1.Application) any { return app.Status.Sync.Status },
"status.health": func(app *v1alpha1.Application) any { return app.Status.Health },
"status.summary": func(app *v1alpha1.Application) any { return app.Status.Summary },

View file

@ -39,6 +39,7 @@ const APP_FIELDS = [
'metadata.deletionTimestamp',
'spec',
'operation.sync',
'status.sourceHydrator',
'status.sync.status',
'status.sync.revision',
'status.health',

View file

@ -4,7 +4,7 @@ const PieChart = require('react-svg-piechart').default;
import {COLORS} from '../../../shared/components';
import * as models from '../../../shared/models';
import {HealthStatusCode, SyncStatusCode} from '../../../shared/models';
import {ComparisonStatusIcon, HealthStatusIcon} from '../utils';
import {ComparisonStatusIcon, HealthStatusIcon, HydrateOperationPhaseIcon} from '../utils';
const healthColors = new Map<models.HealthStatusCode, string>();
healthColors.set('Unknown', COLORS.health.unknown);
@ -19,11 +19,22 @@ syncColors.set('Unknown', COLORS.sync.unknown);
syncColors.set('Synced', COLORS.sync.synced);
syncColors.set('OutOfSync', COLORS.sync.out_of_sync);
const hydratorColors = new Map<string, string>();
hydratorColors.set('Hydrating', COLORS.operation.running);
hydratorColors.set('Hydrated', COLORS.operation.success);
hydratorColors.set('Failed', COLORS.operation.failed);
hydratorColors.set('None', COLORS.sync.unknown);
export const ApplicationsSummary = ({applications}: {applications: models.Application[]}) => {
const sync = new Map<string, number>();
applications.forEach(app => sync.set(app.status.sync.status, (sync.get(app.status.sync.status) || 0) + 1));
const health = new Map<string, number>();
applications.forEach(app => health.set(app.status.health.status, (health.get(app.status.health.status) || 0) + 1));
const hydrator = new Map<string, number>();
applications.forEach(app => {
const phase = app.status.sourceHydrator?.currentOperation?.phase || 'None';
hydrator.set(phase, (hydrator.get(phase) || 0) + 1);
});
const attributes = [
{
@ -38,6 +49,10 @@ export const ApplicationsSummary = ({applications}: {applications: models.Applic
title: 'HEALTHY',
value: applications.filter(app => app.status.health.status === 'Healthy').length
},
{
title: 'HYDRATED',
value: applications.filter(app => app.status.sourceHydrator?.currentOperation?.phase === 'Hydrated').length
},
{
title: 'CLUSTERS',
value: new Set(applications.map(app => app.spec.destination.server || app.spec.destination.name)).size
@ -58,6 +73,11 @@ export const ApplicationsSummary = ({applications}: {applications: models.Applic
title: 'Health',
data: Array.from(health.keys()).map(key => ({title: key, value: health.get(key), color: healthColors.get(key as models.HealthStatusCode)})),
legend: healthColors as Map<string, string>
},
{
title: 'Hydrator',
data: Array.from(hydrator.keys()).map(key => ({title: key, value: hydrator.get(key), color: hydratorColors.get(key)})),
legend: hydratorColors as Map<string, string>
}
];
return (
@ -97,6 +117,21 @@ export const ApplicationsSummary = ({applications}: {applications: models.Applic
<li style={{listStyle: 'none', whiteSpace: 'nowrap'}} key={key}>
{chart.title === 'Health' && <HealthStatusIcon state={{status: key as HealthStatusCode, message: ''}} noSpin={true} />}
{chart.title === 'Sync' && <ComparisonStatusIcon status={key as SyncStatusCode} noSpin={true} />}
{chart.title === 'Hydrator' && key !== 'None' && (
<HydrateOperationPhaseIcon
operationState={{
phase: key as any,
startedAt: '',
message: '',
drySHA: '',
hydratedSHA: '',
sourceHydrator: {} as any
}}
/>
)}
{chart.title === 'Hydrator' && key === 'None' && (
<i className='fa fa-minus-circle' style={{color: hydratorColors.get(key)}} />
)}
{` ${key} (${getLegendValue(key)})`}
</li>
))}

View file

@ -20,6 +20,15 @@
}
}
.applications-list__table-row.applications-table-row--with-hydrator {
.columns.small-4,
.columns.small-6 {
display: flex;
flex-direction: column;
justify-content: center;
}
}
.applications-table-source {
display: flex;
justify-content: space-between;

View file

@ -57,7 +57,9 @@ export const ApplicationsTable = (props: {
key={AppUtils.appInstanceName(app)}
className={`argo-table-list__row
applications-list__entry applications-list__entry--health-${app.status.health.status} ${selectedApp === i ? 'applications-tiles__selected' : ''}`}>
<div className={`row applications-list__table-row`} onClick={e => ctx.navigation.goto(`/${AppUtils.getAppUrl(app)}`, {}, {event: e})}>
<div
className={`row applications-list__table-row ${app.status.sourceHydrator?.currentOperation ? 'applications-table-row--with-hydrator' : ''}`}
onClick={e => ctx.navigation.goto(`/${AppUtils.getAppUrl(app)}`, {}, {event: e})}>
<div className='columns small-4'>
<div className='row'>
<div className=' columns small-2'>
@ -129,6 +131,12 @@ export const ApplicationsTable = (props: {
<div className='columns small-2'>
<AppUtils.HealthStatusIcon state={app.status.health} /> <span>{app.status.health.status}</span> <br />
{app.status.sourceHydrator?.currentOperation && (
<>
<AppUtils.HydrateOperationPhaseIcon operationState={app.status.sourceHydrator.currentOperation} />{' '}
<span>{app.status.sourceHydrator.currentOperation.phase}</span> <br />
</>
)}
<AppUtils.ComparisonStatusIcon status={app.status.sync.status} />
<span>{app.status.sync.status}</span> <OperationState app={app} quiet={true} />
<DropDownMenu

View file

@ -202,6 +202,13 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
<div className='columns small-9' qe-id='applications-tiles-health-status'>
<AppUtils.HealthStatusIcon state={app.status.health} /> {app.status.health.status}
&nbsp;
{app.status.sourceHydrator?.currentOperation && (
<>
<AppUtils.HydrateOperationPhaseIcon operationState={app.status.sourceHydrator.currentOperation} />{' '}
{app.status.sourceHydrator.currentOperation.phase}
&nbsp;
</>
)}
<AppUtils.ComparisonStatusIcon status={app.status.sync.status} /> {app.status.sync.status}
&nbsp;
<OperationState app={app} quiet={true} />