Skip to content

Commit 6113819

Browse files
Merge branch 'main' into feat/deleting-managed-resources
2 parents cebde3f + d272e62 commit 6113819

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1973
-1191
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ RUN npm prune --omit=dev
1818

1919

2020
# PRODUCTION STAGE
21-
FROM gcr.io/distroless/nodejs24-debian12@sha256:e6af293a96996c2a6d1e8a440600518ec614571a91356a38c55f316ff1ee9924 AS production
21+
FROM gcr.io/distroless/nodejs24-debian12@sha256:b8be532fe81829b3803c832989cba7f42de62b2fbc8c7599cae30f378882ea12 AS production
2222
WORKDIR /usr/src/app
2323

2424
# Copy built files

package-lock.json

Lines changed: 556 additions & 589 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,20 @@
3434
"@fastify/static": "8.2.0",
3535
"@fastify/vite": "8.2.0",
3636
"@hookform/resolvers": "5.2.2",
37-
"@sentry/node": "10.11.0",
38-
"@sentry/react": "10.11.0",
37+
"@sentry/node": "10.14.0",
38+
"@sentry/react": "10.14.0",
3939
"@sentry/vite-plugin": "4.3.0",
4040
"@ui5/webcomponents": "2.14.0",
4141
"@ui5/webcomponents-fiori": "2.14.0",
4242
"@ui5/webcomponents-icons": "2.14.0",
4343
"@ui5/webcomponents-react": "2.14.1",
4444
"@ui5/webcomponents-react-charts": "2.14.1",
45-
"@xyflow/react": "12.8.4",
45+
"@xyflow/react": "12.8.5",
4646
"clsx": "2.1.1",
4747
"dagre": "0.8.5",
48+
"diff": "^8.0.2",
4849
"dotenv": "17.2.2",
49-
"fastify": "5.6.0",
50+
"fastify": "5.6.1",
5051
"fastify-plugin": "5.0.1",
5152
"graphql": "16.11.0",
5253
"graphql-config": "5.1.5",
@@ -56,29 +57,29 @@
5657
"react": "19.1.1",
5758
"react-dom": "19.1.1",
5859
"react-error-boundary": "6.0.0",
59-
"react-hook-form": "7.62.0",
60+
"react-hook-form": "7.63.0",
6061
"react-i18next": "15.7.3",
6162
"react-router-dom": "7.9.1",
6263
"react-syntax-highlighter": "15.6.6",
6364
"react-time-ago": "7.3.5",
6465
"swr": "2.3.6",
6566
"yaml": "2.8.1",
66-
"zod": "4.1.8"
67+
"zod": "4.1.11"
6768
},
6869
"devDependencies": {
6970
"@eslint/eslintrc": "3.3.1",
70-
"@eslint/js": "9.35.0",
71+
"@eslint/js": "9.36.0",
7172
"@graphql-codegen/cli": "6.0.0",
72-
"@graphql-codegen/client-preset": "5.0.0",
73+
"@graphql-codegen/client-preset": "5.0.1",
7374
"@types/dagre": "0.7.53",
7475
"@types/js-yaml": "4.0.9",
75-
"@types/node": "22.18.3",
76+
"@types/node": "22.18.6",
7677
"@types/react": "19.1.13",
7778
"@types/react-dom": "19.1.9",
7879
"@types/react-syntax-highlighter": "15.5.13",
7980
"@ui5/webcomponents-cypress-commands": "2.14.1",
80-
"@vitejs/plugin-react": "5.0.2",
81-
"@vitest/eslint-plugin": "1.3.9",
81+
"@vitejs/plugin-react": "5.0.3",
82+
"@vitest/eslint-plugin": "1.3.12",
8283
"cypress": "15.2.0",
8384
"eslint-config-prettier": "10.1.8",
8485
"eslint-import-resolver-typescript": "4.4.4",
@@ -94,8 +95,8 @@
9495
"prettier": "3.6.2",
9596
"tsx": "4.20.5",
9697
"typescript": "5.9.2",
97-
"typescript-eslint": "8.43.0",
98-
"vite": "7.1.5",
98+
"typescript-eslint": "8.44.1",
99+
"vite": "7.1.7",
99100
"vitest": "3.2.4"
100101
}
101102
}

public/locales/en.json

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"signInButton": "Sign in"
77
},
88
"Entities": {
9-
"ManagedControlPlane": "Managed Control Plane",
9+
"ManagedControlPlane": "ManagedControlPlane",
1010
"Project": "Project",
1111
"Workspace": "Workspace",
1212
"Users": "Users",
@@ -64,7 +64,11 @@
6464
"loadingErrorMessage": "Failed to list mcps in workspace"
6565
},
6666
"ControlPlaneCard": {
67-
"deleteConfirmationDialog": "MCP deletion triggered. The list will refresh automatically once completed."
67+
"deleteConfirmationDialog": "MCP deletion triggered. The list will refresh automatically once completed.",
68+
"editMCP": "Edit ManagedControlPlane",
69+
"duplicateMCP": "Duplicate ManagedControlPlane",
70+
"deleteMCP": "Delete ManagedControlPlane"
71+
6872
},
6973
"ControlPlaneListAllWorkspaces": {
7074
"emptyListTitleMessage": "No Workspaces created yet",
@@ -91,8 +95,6 @@
9195
},
9296
"CopyKubeconfigButton": {
9397
"kubeconfigButton": "Kubeconfig",
94-
"copiedMessage": "Copied to Clipboard",
95-
"failedMessage": "Failed to copy, Error:",
9698
"menuDownload": "Download",
9799
"menuCopy": "Copy to clipboard"
98100
},
@@ -109,11 +111,8 @@
109111
"subtitleMessage": "Sorry, we couldn’t find what you are looking for.<br />The link may be incorrect or the {{entityType}} might have been removed.",
110112
"navigateHome": "Back to Homepage"
111113
},
112-
"IntelligentBreadcrumbs": {
113-
"homeLabel": "Home",
114-
"projects": "Projects",
115-
"workspaces": "Workspaces",
116-
"mcps": "MCPs"
114+
"PathAwareBreadcrumbs": {
115+
"projectsLabel": "Projects"
117116
},
118117
"MCPContext": {
119118
"errorMessage": "An unknown error occurred"
@@ -223,10 +222,6 @@
223222
"treeExecution": "Execution",
224223
"noExecutionFound": "No Executions found"
225224
},
226-
"CopyButton": {
227-
"copiedMessage": "Copied To Clipboard",
228-
"failedMessage": "Failed to copy"
229-
},
230225
"DeleteWorkspaceDialog": {
231226
"title": "Delete a Workspace",
232227
"introSection1": "The below instructions will help you delete the workspace \"{{workspaceName}}\" from project namespace \"{{projectNamespace}}\" using kubectl.",
@@ -360,7 +355,9 @@
360355
"unhealthy": "Unhealthy",
361356
"progress": "Managed",
362357
"remaining": "Remaining",
363-
"active": "Active"
358+
"active": "Active",
359+
"copyToClipboardSuccessToast": "Copied to clipboard",
360+
"copyToClipboardFailedToast": "Failed to copy to clipboard"
364361
},
365362
"errors": {
366363
"installError": "Install error",
@@ -376,22 +373,32 @@
376373
"create": "Create",
377374
"close": "Close",
378375
"back": "Back",
379-
"cancel": "Cancel"
376+
"cancel": "Cancel",
377+
"update": "Update"
380378
},
381379
"yaml": {
382-
"copiedToClipboard": "YAML copied to clipboard!",
383380
"YAML": "File"
384381
},
385382
"createMCP": {
386383
"dialogTitle": "Create Managed Control Plane",
387384
"titleText": "Managed Control Plane Created Successfully!",
388-
"subtitleText": "Your Managed Control Plane is being set up. It will be ready to use in just a few minutes. You can safely close this window."
385+
"subtitleText": "Your Managed Control Plane is being set up. It will be ready to use in just a few minutes. You can safely close this window.",
386+
"copySuffix": "-copy"
387+
},
388+
"editMCP": {
389+
"dialogTitle": "Edit Managed Control Plane",
390+
"titleText": "Managed Control Plane Updated Successfully!",
391+
"subtitleText": "Your Managed Control Plane is being updated. It will be ready to use in just a few minutes. You can safely close this window.",
392+
"editComponents": "Edit components",
393+
"duplicatingMCPInfo1": "Duplicating a <span>ManagedControlPlane</span> will only create a <span>ManagedControlPlane</span> with the same configuration. ",
394+
"duplicatingMCPInfo2": "<b>It will NOT copy the managed resources inside</b>"
389395
},
390396
"componentsSelection": {
391397
"selectComponents": "Select Components",
392398
"selectedComponents": "Selected Components",
393399
"pleaseSelectComponents": "Choose the components you want to add to your Managed Control Plane.",
394-
"cannotLoad": "Cannot load components list"
400+
"cannotLoad": "Cannot load components list",
401+
"noComponentsFound": "No components found matching your search."
395402
},
396403
"Hints": {
397404
"CrossplaneHint": {

src/Routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const Routes = {
2+
Home: '/',
3+
Project: '/mcp/projects/:projectName',
4+
Mcp: '/mcp/projects/:projectName/workspaces/:workspaceName/mcps/:controlPlaneName',
5+
} as const;

src/components/ComponentsSelection/ComponentsSelection.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export const ComponentsSelection: React.FC<ComponentsSelectionProps> = ({
101101
/>
102102

103103
<Grid>
104-
<div data-layout-span="XL8 L8 M8 S8">
104+
<div data-layout-span="XL7 L7 M7 S7">
105105
{searchResults.length > 0 ? (
106106
searchResults.map((component) => {
107107
const providerDisabled = isProviderDisabled(component);
@@ -167,13 +167,13 @@ export const ComponentsSelection: React.FC<ComponentsSelectionProps> = ({
167167
);
168168
})
169169
) : (
170-
<Infobox fullWidth variant="success">
171-
<Text>{t('componentsSelection.pleaseSelectComponents')}</Text>
170+
<Infobox fullWidth variant="normal" icon="search">
171+
<Text>{t('componentsSelection.noComponentsFound')}</Text>
172172
</Infobox>
173173
)}
174174
</div>
175175

176-
<div data-layout-span="XL4 L4 M4 S4">
176+
<div data-layout-span="XL5 L5 M5 S5">
177177
{templateDefaultsError ? (
178178
<div style={{ marginBottom: 8 }}>
179179
<IllustratedError title={templateDefaultsError} compact />
@@ -191,7 +191,7 @@ export const ComponentsSelection: React.FC<ComponentsSelectionProps> = ({
191191
))}
192192
</List>
193193
) : (
194-
<Infobox fullWidth variant="success">
194+
<Infobox variant="success" icon="add">
195195
<Text>{t('componentsSelection.pleaseSelectComponents')}</Text>
196196
</Infobox>
197197
)}

src/components/ComponentsSelection/ComponentsSelectionContainer.tsx

Lines changed: 9 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
import React, { useEffect, useMemo, useRef, useState } from 'react';
1+
import React from 'react';
22
import { ComponentsSelection } from './ComponentsSelection.tsx';
3-
43
import IllustratedError from '../Shared/IllustratedError.tsx';
5-
import { sortVersions } from '../../utils/componentsVersions.ts';
6-
7-
import { ListManagedComponents } from '../../lib/api/types/crate/listManagedComponents.ts';
8-
import { useApiResource } from '../../lib/api/useApiResource.ts';
94
import Loading from '../Shared/Loading.tsx';
10-
import { ComponentsListItem, removeComponents } from '../../lib/api/types/crate/createManagedControlPlane.ts';
5+
import { ComponentsListItem } from '../../lib/api/types/crate/createManagedControlPlane.ts';
116
import { useTranslation } from 'react-i18next';
12-
import { ManagedControlPlaneTemplate } from '../../lib/api/types/templates/mcpTemplate.ts';
137

148
export interface ComponentsSelectionProps {
159
componentsList: ComponentsListItem[];
1610
setComponentsList: (components: ComponentsListItem[]) => void;
17-
managedControlPlaneTemplate?: ManagedControlPlaneTemplate;
11+
isLoading: boolean;
12+
error: unknown;
13+
templateDefaultsError?: string;
1814
}
1915

2016
/**
@@ -25,89 +21,19 @@ export const getSelectedComponents = (components: ComponentsListItem[]) => {
2521
const isCrossplaneSelected = components.some(({ name, isSelected }) => name === 'crossplane' && isSelected);
2622
return components.filter((component) => {
2723
if (!component.isSelected) return false;
28-
if (component.name?.includes('provider') && !isCrossplaneSelected) {
29-
return false;
30-
}
24+
if (component.name?.includes('provider') && !isCrossplaneSelected) return false;
3125
return true;
3226
});
3327
};
3428

35-
type TemplateDefaultComponent = {
36-
name: string;
37-
version: string;
38-
removable?: boolean;
39-
versionChangeable?: boolean;
40-
};
41-
4229
export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> = ({
4330
setComponentsList,
4431
componentsList,
45-
managedControlPlaneTemplate,
32+
isLoading,
33+
error,
34+
templateDefaultsError,
4635
}) => {
47-
const { data: availableManagedComponentsListData, error, isLoading } = useApiResource(ListManagedComponents());
4836
const { t } = useTranslation();
49-
const initialized = useRef(false);
50-
const [templateDefaultsError, setTemplateDefaultsError] = useState<string | null>(null);
51-
const defaultComponents = useMemo<TemplateDefaultComponent[]>(
52-
() => managedControlPlaneTemplate?.spec?.spec?.components?.defaultComponents ?? [],
53-
[managedControlPlaneTemplate],
54-
);
55-
56-
useEffect(() => {
57-
const items = availableManagedComponentsListData?.items ?? [];
58-
59-
if (!items.length) {
60-
if (!initialized.current) return;
61-
setTemplateDefaultsError(null);
62-
return;
63-
}
64-
65-
if (!initialized.current) {
66-
const newComponentsList = items
67-
.map((item) => {
68-
const versions = sortVersions(item.status.versions);
69-
const template = defaultComponents.find((dc) => dc.name === item.metadata.name);
70-
const templateVersion = template?.version;
71-
const selectedVersion = template
72-
? templateVersion && versions.includes(templateVersion)
73-
? templateVersion
74-
: ''
75-
: (versions[0] ?? '');
76-
return {
77-
name: item.metadata.name,
78-
versions,
79-
selectedVersion,
80-
isSelected: !!template,
81-
documentationUrl: '',
82-
};
83-
})
84-
.filter((component) => !removeComponents.find((item) => item === component.name));
85-
86-
setComponentsList(newComponentsList);
87-
initialized.current = true;
88-
}
89-
90-
if (!defaultComponents.length) {
91-
setTemplateDefaultsError(null);
92-
return;
93-
}
94-
95-
const errors: string[] = [];
96-
defaultComponents.forEach((dc: TemplateDefaultComponent) => {
97-
if (!dc?.name) return;
98-
const item = items.find((it) => it.metadata.name === dc.name);
99-
if (!item) {
100-
errors.push(`Component "${dc.name}" from template is not available.`);
101-
return;
102-
}
103-
const versions: string[] = Array.isArray(item.status?.versions) ? item.status.versions : [];
104-
if (dc.version && !versions.includes(dc.version)) {
105-
errors.push(`Component "${dc.name}" version "${dc.version}" from template is not available.`);
106-
}
107-
});
108-
109-
setTemplateDefaultsError(errors.length ? errors.join('\n') : null);
110-
}, [availableManagedComponentsListData, defaultComponents, setComponentsList]);
11137

11238
if (isLoading) {
11339
return <Loading />;
@@ -117,7 +43,6 @@ export const ComponentsSelectionContainer: React.FC<ComponentsSelectionProps> =
11743
return <IllustratedError compact={true} />;
11844
}
11945

120-
// Defensive: If the API returned no items, show error
12146
if (!componentsList || componentsList.length === 0) {
12247
return <IllustratedError title={t('componentsSelection.cannotLoad')} compact={true} />;
12348
}

0 commit comments

Comments
 (0)