Skip to content

Commit 8464727

Browse files
Merge branch 'main' into bump-monaco
2 parents c4e069b + 044d5e0 commit 8464727

File tree

18 files changed

+900
-250
lines changed

18 files changed

+900
-250
lines changed

src/components/Clusters/views/ClusterOverview/ClusterModulesCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CountingCard } from 'shared/components/CountingCard/CountingCard';
44
import { useKymaModulesQuery } from 'components/KymaModules/kymaModulesQueries';
55
import { useUrl } from 'hooks/useUrl';
66
import { useNavigate } from 'react-router';
7-
import { useGetAllModulesStatuses } from 'components/KymaModules/support';
7+
import { useGetAllModulesStatuses } from 'components/KymaModules/hooks';
88
import { useMemo } from 'react';
99

1010
export default function ClusterModulesCard() {

src/components/KymaModules/KymaModulesAddModule.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { KymaModuleContext } from './providers/KymaModuleProvider';
1010

1111
import './KymaModulesAddModule.scss';
1212
import { findModuleStatus } from './support';
13+
import { ModuleTemplatesContext } from './providers/ModuleTemplatesProvider';
1314

1415
export default function KymaModulesAddModule(props) {
1516
const { t } = useTranslation();
@@ -21,10 +22,11 @@ export default function KymaModulesAddModule(props) {
2122
setKymaResourceState: setKymaResource,
2223
kymaResourceLoading: loading,
2324
selectedModules: activeKymaModules,
24-
moduleTemplates,
2525
initialUnchangedResource,
2626
} = useContext(KymaModuleContext);
2727

28+
const { moduleTemplates } = useContext(ModuleTemplatesContext);
29+
2830
const [resource, setResource] = useState(cloneDeep(kymaResource));
2931

3032
const [selectedModules, setSelectedModules] = useState([]);

src/components/KymaModules/KymaModulesList.js

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext } from 'react';
1+
import { useContext, useEffect, useState } from 'react';
22
import { useTranslation } from 'react-i18next';
33

44
import { Create, ResourceDescription } from 'components/KymaModules';
@@ -9,23 +9,49 @@ import { DynamicPageComponent } from 'shared/components/DynamicPageComponent/Dyn
99
import { ResourceCreate } from 'shared/components/ResourceCreate/ResourceCreate';
1010
import { ErrorBoundary } from 'shared/components/ErrorBoundary/ErrorBoundary';
1111
import { useProtectedResources } from 'shared/hooks/useProtectedResources';
12+
import { CommunityModulesList } from './components/CommunityModulesList';
13+
import { CommunityModuleContext } from './providers/CommunityModuleProvider';
14+
import { ModuleTemplatesContext } from './providers/ModuleTemplatesProvider';
15+
import { checkSelectedModule } from './support';
16+
import { useRecoilValue } from 'recoil';
17+
import { columnLayoutState } from 'state/columnLayoutAtom';
1218

1319
export default function KymaModulesList({ namespaced }) {
1420
const { t } = useTranslation();
21+
const layoutState = useRecoilValue(columnLayoutState);
1522

1623
const {
1724
resourceName,
1825
resourceUrl,
1926
kymaResource,
2027
kymaResourceLoading,
2128
selectedModules,
22-
moduleTemplates,
2329
moduleTemplatesLoading,
24-
setOpenedModuleIndex,
30+
setOpenedModuleIndex: setOpenedManagedModuleIndex,
2531
handleResourceDelete,
2632
} = useContext(KymaModuleContext);
33+
const { moduleTemplates, communityModuleTemplates } = useContext(
34+
ModuleTemplatesContext,
35+
);
36+
const {
37+
installedCommunityModules,
38+
communityModulesLoading,
39+
setOpenedModuleIndex: setOpenedCommunityModuleIndex,
40+
} = useContext(CommunityModuleContext);
41+
42+
const [selectedEntry, setSelectedEntry] = useState(null);
2743
const { isProtected, protectedResourceWarning } = useProtectedResources();
2844

45+
useEffect(() => {
46+
if (!communityModulesLoading && installedCommunityModules.length) {
47+
setSelectedEntry(
48+
installedCommunityModules.find(module =>
49+
checkSelectedModule(module, layoutState),
50+
)?.name,
51+
);
52+
}
53+
}, [communityModulesLoading, installedCommunityModules, layoutState]);
54+
2955
if (moduleTemplatesLoading || kymaResourceLoading) {
3056
return <Spinner />;
3157
}
@@ -49,10 +75,23 @@ export default function KymaModulesList({ namespaced }) {
4975
kymaResource={kymaResource}
5076
namespaced={namespaced}
5177
resourceUrl={resourceUrl}
52-
setOpenedModuleIndex={setOpenedModuleIndex}
78+
setOpenedModuleIndex={setOpenedManagedModuleIndex}
5379
handleResourceDelete={handleResourceDelete}
80+
customSelectedEntry={selectedEntry}
81+
setSelectedEntry={setSelectedEntry}
5482
/>
5583
)}
84+
<CommunityModulesList
85+
key="community-modules-list"
86+
moduleTemplates={communityModuleTemplates}
87+
selectedModules={installedCommunityModules}
88+
modulesLoading={communityModulesLoading}
89+
namespaced={namespaced}
90+
setOpenedModuleIndex={setOpenedCommunityModuleIndex}
91+
handleResourceDelete={handleResourceDelete}
92+
customSelectedEntry={selectedEntry}
93+
setSelectedEntry={setSelectedEntry}
94+
/>
5695
</>
5796
}
5897
inlineEditForm={() => (
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import { useSetRecoilState } from 'recoil';
4+
import { Button } from '@ui5/webcomponents-react';
5+
import pluralize from 'pluralize';
6+
import {
7+
findModuleTemplate,
8+
ModuleTemplateListType,
9+
ModuleTemplateType,
10+
} from '../support';
11+
import { useUrl } from 'hooks/useUrl';
12+
import { extractApiGroupVersion } from 'resources/Roles/helpers';
13+
import {
14+
columnLayoutState,
15+
ColumnState,
16+
ShowCreate,
17+
} from 'state/columnLayoutAtom';
18+
import { isFormOpenState } from 'state/formOpenAtom';
19+
import { GenericList } from 'shared/components/GenericList/GenericList';
20+
import { useNavigate } from 'react-router';
21+
import { useFetchModuleData } from '../hooks';
22+
import { ModulesListRows } from './ModulesListRows';
23+
24+
type ModulesListProps = {
25+
moduleTemplates: ModuleTemplateListType;
26+
selectedModules: any[];
27+
modulesLoading: boolean;
28+
namespaced: boolean;
29+
setOpenedModuleIndex: React.Dispatch<
30+
React.SetStateAction<number | undefined>
31+
>;
32+
handleResourceDelete: (resourceData: any) => void;
33+
customSelectedEntry?: string;
34+
setSelectedEntry?: React.Dispatch<React.SetStateAction<any>>;
35+
};
36+
37+
export const CommunityModulesList = ({
38+
moduleTemplates,
39+
selectedModules: installedModules,
40+
modulesLoading,
41+
namespaced,
42+
setOpenedModuleIndex,
43+
handleResourceDelete,
44+
customSelectedEntry,
45+
setSelectedEntry,
46+
}: ModulesListProps) => {
47+
const { t } = useTranslation();
48+
const navigate = useNavigate();
49+
const { clusterUrl, namespaceUrl } = useUrl();
50+
const setLayoutColumn = useSetRecoilState(columnLayoutState);
51+
const setIsFormOpen = useSetRecoilState(isFormOpenState);
52+
const { getItem: getModuleResource } = useFetchModuleData(
53+
moduleTemplates,
54+
(module: ModuleTemplateType) => module?.spec?.data ?? null,
55+
'resource',
56+
);
57+
58+
const handleShowAddModule = () => {
59+
setLayoutColumn({
60+
startColumn: {
61+
resourceType: 'kymamodules',
62+
} as ColumnState,
63+
midColumn: null,
64+
endColumn: null,
65+
layout: 'TwoColumnsMidExpanded',
66+
showCreate: {
67+
resourceType: 'kymamodules',
68+
} as ShowCreate,
69+
});
70+
71+
navigate(
72+
`${window.location.pathname}?layout=TwoColumnsMidExpanded&showCreate=true`,
73+
);
74+
setIsFormOpen(state => ({ ...state, formOpen: true }));
75+
};
76+
77+
const headerRenderer = () => [
78+
t('common.headers.name'),
79+
t('kyma-modules.namespaces'),
80+
t('kyma-modules.channel'),
81+
t('kyma-modules.version'),
82+
t('kyma-modules.module-state'),
83+
t('kyma-modules.installation-state'),
84+
t('kyma-modules.documentation'),
85+
];
86+
87+
const hasDetailsLink = (resource: {
88+
name: string;
89+
channel: string;
90+
version: string;
91+
resource: { kind: string };
92+
}) => {
93+
const moduleTemplateName = findModuleTemplate(
94+
moduleTemplates,
95+
resource.name,
96+
resource.channel,
97+
resource.version,
98+
)?.metadata?.name;
99+
const moduleResource = getModuleResource(moduleTemplateName ?? '');
100+
101+
const moduleStatus = moduleResource?.status;
102+
const isDeletionFailed = moduleStatus?.state === 'Warning';
103+
const isError = moduleStatus?.state === 'Error';
104+
105+
const hasResource = !!moduleResource;
106+
107+
return hasResource && (!isDeletionFailed || !isError);
108+
};
109+
110+
const customColumnLayout = (resource: { name: string }) => {
111+
const moduleResource = getModuleResource(resource.name);
112+
113+
return {
114+
resourceName: resource?.name,
115+
resourceType: pluralize(moduleResource?.kind || ''),
116+
namespaceId: moduleResource?.metadata?.namespace || '',
117+
};
118+
};
119+
120+
const actions = [
121+
{
122+
name: t('common.buttons.delete'),
123+
tooltip: () => t('common.buttons.delete'),
124+
icon: 'delete',
125+
disabledHandler: (resource: { name: string }) => {
126+
const index = installedModules?.findIndex(module => {
127+
return module.name === resource.name;
128+
});
129+
return index < 0;
130+
},
131+
handler: (resource: { name: string }) => {
132+
const index = installedModules?.findIndex(module => {
133+
return module.name === resource.name;
134+
});
135+
setOpenedModuleIndex(index);
136+
handleResourceDelete({});
137+
},
138+
},
139+
];
140+
141+
const handleClickResource = (
142+
moduleName: string,
143+
moduleStatus: {
144+
name: string;
145+
channel: string;
146+
version: string;
147+
resource: {
148+
kind: string;
149+
apiVersion: string;
150+
metadata: { name: string; namespace: string };
151+
};
152+
},
153+
) => {
154+
setOpenedModuleIndex(
155+
installedModules?.findIndex(entry => entry.name === moduleName),
156+
);
157+
158+
setSelectedEntry?.(moduleName);
159+
160+
const moduleTemplate = findModuleTemplate(
161+
moduleTemplates,
162+
moduleName,
163+
moduleStatus.channel,
164+
moduleStatus.version,
165+
);
166+
if (!moduleStatus.resource) {
167+
const moduleResource = moduleTemplate?.spec?.data;
168+
moduleStatus.resource = {
169+
kind: moduleResource.kind,
170+
apiVersion: moduleResource.apiVersion,
171+
metadata: {
172+
name: moduleResource.metadata.name,
173+
namespace: moduleResource.metadata.namespace,
174+
},
175+
};
176+
}
177+
178+
const skipRedirect = !hasDetailsLink(moduleStatus);
179+
180+
if (skipRedirect) {
181+
return;
182+
}
183+
184+
const pathName = `${pluralize(
185+
moduleStatus?.resource?.kind || '',
186+
).toLowerCase()}/${moduleStatus?.resource?.metadata?.name}`;
187+
188+
const partialPath = moduleStatus?.resource?.metadata?.namespace
189+
? `kymamodules/namespaces/${moduleStatus?.resource?.metadata?.namespace}/${pathName}`
190+
: `kymamodules/${pathName}`;
191+
192+
const path = namespaced
193+
? namespaceUrl(partialPath)
194+
: clusterUrl(partialPath);
195+
196+
const { group, version } = extractApiGroupVersion(
197+
moduleStatus?.resource?.apiVersion,
198+
);
199+
setLayoutColumn({
200+
startColumn: {
201+
resourceType: pluralize(
202+
moduleStatus?.resource?.kind || '',
203+
).toLowerCase(),
204+
namespaceId: moduleStatus?.resource?.metadata.namespace || '',
205+
apiGroup: group,
206+
apiVersion: version,
207+
} as ColumnState,
208+
midColumn: {
209+
resourceType: pluralize(
210+
moduleStatus?.resource?.kind || '',
211+
).toLowerCase(),
212+
resourceName: moduleStatus?.resource?.metadata?.name,
213+
namespaceId: moduleStatus?.resource?.metadata.namespace || '',
214+
apiGroup: group,
215+
apiVersion: version,
216+
} as ColumnState,
217+
layout: 'TwoColumnsMidExpanded',
218+
endColumn: null,
219+
});
220+
221+
navigate(`${path}?layout=TwoColumnsMidExpanded`);
222+
};
223+
224+
return (
225+
<React.Fragment key="commmunity-modules-list">
226+
<GenericList
227+
className={'community-modules-list'}
228+
accessibleName={undefined}
229+
actions={actions as any}
230+
customRowClick={handleClickResource}
231+
extraHeaderContent={[
232+
<Button
233+
key="add-community-module"
234+
design="Emphasized"
235+
onClick={handleShowAddModule}
236+
>
237+
{t('common.buttons.add')}
238+
</Button>,
239+
]}
240+
customColumnLayout={customColumnLayout as any}
241+
enableColumnLayout
242+
hasDetailsView
243+
entries={installedModules as any}
244+
serverDataLoading={modulesLoading}
245+
headerRenderer={headerRenderer}
246+
rowRenderer={resource =>
247+
ModulesListRows({
248+
resourceName: resource.name,
249+
resource,
250+
moduleTemplates,
251+
hasDetailsLink,
252+
})
253+
}
254+
noHideFields={['Name', '', 'Namespace']}
255+
displayArrow
256+
title={'Community Modules'}
257+
sortBy={{
258+
name: (a: { name: any }, b: { name: any }) =>
259+
a.name?.localeCompare(b.name),
260+
}}
261+
emptyListProps={
262+
{
263+
image: 'TntComponents',
264+
titleText: `${t('common.labels.no')} ${t(
265+
'kyma-modules.title',
266+
).toLocaleLowerCase()}`,
267+
subtitleText: t('kyma-modules.no-modules-description'),
268+
url:
269+
'https://help.sap.com/docs/btp/sap-business-technology-platform/kyma-s-modular-approach?locale=en-US&state=DRAFT&version=Cloud',
270+
buttonText: t('common.buttons.add'),
271+
showButton: true,
272+
onClick: handleShowAddModule,
273+
} as any
274+
}
275+
customSelectedEntry={customSelectedEntry}
276+
/>
277+
</React.Fragment>
278+
);
279+
};

src/components/KymaModules/components/ModuleStatus.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { StatusBadge } from 'shared/components/StatusBadge/StatusBadge';
2-
import { useModuleStatus } from '../support';
2+
import { useModuleStatus } from '../hooks';
33

44
export const resolveType = (status: string) => {
55
if (typeof status !== 'string') {

0 commit comments

Comments
 (0)