Skip to content

Commit 77eb641

Browse files
committed
feat: add status to Modules card
1 parent 4f32524 commit 77eb641

File tree

4 files changed

+118
-25
lines changed

4 files changed

+118
-25
lines changed

public/i18n/en.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ cluster-overview:
4141
total-persistentvolumes: 'Total Persistent Volumes'
4242
persistent-volumes-total-capacity: 'Total Capacity'
4343
nodes: 'Nodes'
44+
modules-overview: 'Modules Overview'
45+
modules-ready: 'Ready'
46+
modules-error: 'Error'
47+
modules-warning: 'Warning'
4448
tooltips:
4549
cpu-used-percentage: 'CPU used: {{percentage}}'
4650
memory-used-percentage: 'Memory used: {{percentage}}'

src/components/Clusters/views/ClusterOverview/ClusterDetails.js

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { CountingCard } from 'shared/components/CountingCard/CountingCard';
1010
import { useKymaModulesQuery } from 'components/KymaModules/kymaModulesQueries';
1111
import { useUrl } from 'hooks/useUrl';
1212
import { useNavigate } from 'react-router-dom';
13+
import { useModulesStatuses } from 'components/KymaModules/support';
14+
import { useMemo } from 'react';
1315

1416
const GardenerProvider = () => {
1517
const { t } = useTranslation();
@@ -36,6 +38,26 @@ export default function ClusterDetails({ currentCluster }) {
3638
const { modules, error, loading: loadingModules } = useKymaModulesQuery();
3739
const { clusterUrl } = useUrl();
3840
const navigate = useNavigate();
41+
const {
42+
data: statuses,
43+
loading: loadingStatuses,
44+
error: statusesError,
45+
} = useModulesStatuses(modules);
46+
47+
const moduleCounts = useMemo(() => {
48+
if (statuses && !loadingStatuses && !statusesError) {
49+
return statuses.reduce(
50+
(acc, m) => {
51+
if (m.status === 'Ready') acc.ready++;
52+
else if (m.status === 'Error') acc.error++;
53+
else if (m.status === 'Warning') acc.warning++;
54+
return acc;
55+
},
56+
{ ready: 0, error: 0, warning: 0 },
57+
);
58+
}
59+
return { ready: 0, error: 0, warning: 0 };
60+
}, [statuses, statusesError]);
3961

4062
return (
4163
<div className="resource-details-container">
@@ -73,21 +95,34 @@ export default function ClusterDetails({ currentCluster }) {
7395
}
7496
/>
7597
{!error && !loadingModules && modules && (
76-
<div className="item-wrapper sap-margin-x-small">
77-
<CountingCard
78-
className="item"
79-
value={modules?.length}
80-
title={t('kyma-modules.installed-modules')}
81-
additionalContent={
82-
<Button
83-
design="Emphasized"
84-
onClick={() => navigate(clusterUrl('kymamodules'))}
85-
>
86-
{t('kyma-modules.modify-modules')}
87-
</Button>
88-
}
89-
/>
90-
</div>
98+
<CountingCard
99+
className="item"
100+
value={modules?.length}
101+
title={t('cluster-overview.statistics.modules-overview')}
102+
subTitle={t('kyma-modules.installed-modules')}
103+
extraInfo={[
104+
{
105+
title: t('cluster-overview.statistics.modules-ready'),
106+
value: moduleCounts.ready,
107+
},
108+
{
109+
title: t('cluster-overview.statistics.modules-error'),
110+
value: moduleCounts.error,
111+
},
112+
{
113+
title: t('cluster-overview.statistics.modules-warning'),
114+
value: moduleCounts.warning,
115+
},
116+
]}
117+
additionalContent={
118+
<Button
119+
design="Emphasized"
120+
onClick={() => navigate(clusterUrl('kymamodules'))}
121+
>
122+
{t('kyma-modules.modify-modules')}
123+
</Button>
124+
}
125+
/>
91126
)}
92127
</div>
93128
);

src/components/KymaModules/support.ts

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,9 @@ type KymaResourceType = {
2222
};
2323
};
2424

25-
export function useModuleStatus(resource: KymaResourceType) {
26-
const fetch = useFetch();
27-
const [data, setData] = useState<any>(null);
28-
const [loading, setLoading] = useState(true);
29-
const [error, setError] = useState<Error | null>(null);
30-
31-
const path = resource?.metadata?.namespace
25+
const getResourcePath = (resource: KymaResourceType) => {
26+
if (!resource) return '';
27+
return resource?.metadata?.namespace
3228
? `/apis/${resource?.apiVersion}/namespaces/${
3329
resource?.metadata?.namespace
3430
}/${pluralize(resource?.kind || '').toLowerCase()}/${
@@ -37,6 +33,15 @@ export function useModuleStatus(resource: KymaResourceType) {
3733
: `/apis/${resource?.apiVersion}/${pluralize(
3834
resource?.kind || '',
3935
).toLowerCase()}/${resource?.metadata?.name}`;
36+
};
37+
38+
export function useModuleStatus(resource: KymaResourceType) {
39+
const fetch = useFetch();
40+
const [data, setData] = useState<any>(null);
41+
const [loading, setLoading] = useState(true);
42+
const [error, setError] = useState<Error | null>(null);
43+
44+
const path = getResourcePath(resource);
4045

4146
useEffect(() => {
4247
async function fetchModule() {
@@ -61,6 +66,51 @@ export function useModuleStatus(resource: KymaResourceType) {
6166
return { data, loading, error };
6267
}
6368

69+
export function useModulesStatuses(modules: any[]) {
70+
const fetch = useFetch();
71+
const [data, setData] = useState<Record<string, any>>({});
72+
const [loading, setLoading] = useState(true);
73+
const [error, setError] = useState<Error | null>(null);
74+
75+
useEffect(() => {
76+
async function fetchModules() {
77+
if (!modules || modules.length === 0) return;
78+
setLoading(true);
79+
try {
80+
const results = await Promise.all(
81+
modules.map(async module => {
82+
const resource = module?.resource;
83+
84+
if (!resource) return null;
85+
const path = getResourcePath(resource);
86+
87+
try {
88+
const response = await fetch({ relativeUrl: path });
89+
const status = (await response.json())?.status;
90+
return { key: resource?.metadata?.name, status: status.state };
91+
} catch (e) {
92+
return { key: resource?.metadata?.name, status: null, error: e };
93+
}
94+
}),
95+
);
96+
97+
setData(results);
98+
} catch (e) {
99+
if (e instanceof Error) {
100+
setError(e);
101+
}
102+
} finally {
103+
setLoading(false);
104+
}
105+
}
106+
107+
fetchModules();
108+
// eslint-disable-next-line react-hooks/exhaustive-deps
109+
}, [JSON.stringify(modules)]);
110+
111+
return { data, loading, error };
112+
}
113+
64114
export const findStatus = (
65115
kymaResource: KymaResourceType,
66116
moduleName: string,

tests/integration/tests/kyma-cluster/test-kyma-modules.spec.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ context('Test Kyma Modules views', () => {
55
cy.loginAndSelectCluster();
66
});
77

8-
it('Test Feature card for Modules', () => {
8+
it('Test Modules Overview card', () => {
99
cy.wait(2000);
1010

1111
cy.get('ui5-card')
12-
.contains('Installed Modules')
12+
.contains('Modules Overview')
1313
.should('be.visible');
1414

1515
cy.contains('ui5-card', 'Installed Modules')
@@ -99,7 +99,7 @@ context('Test Kyma Modules views', () => {
9999
.should('be.visible');
100100
});
101101

102-
it('Test number of Modules in Feature card', () => {
102+
it('Test number of Modules in Modules Overview card', () => {
103103
cy.getLeftNav()
104104
.contains('Cluster Details')
105105
.click();
@@ -108,6 +108,10 @@ context('Test Kyma Modules views', () => {
108108
.contains('2')
109109
.should('be.visible');
110110

111+
cy.contains('ui5-card', 'Ready')
112+
.contains('2')
113+
.should('be.visible');
114+
111115
cy.get('ui5-card')
112116
.contains('Modify Modules')
113117
.click();

0 commit comments

Comments
 (0)