From 9c04b5777b886e505787b04cc79f886f44f6bf43 Mon Sep 17 00:00:00 2001 From: Andreas Kienle Date: Tue, 22 Jul 2025 12:09:30 +0200 Subject: [PATCH 1/4] Add 404 pages --- public/locales/en.json | 13 ++- src/AppRouter.tsx | 9 +- .../List/ControlPlaneListAllWorkspaces.tsx | 26 +---- .../IllustratedBanner.cy.tsx | 18 ++- .../IllustratedBanner/IllustratedBanner.tsx | 23 ++-- .../Ui/NotFoundBanner/NotFoundBanner.cy.tsx | 18 +++ .../NotFoundBanner/NotFoundBanner.module.css | 9 ++ .../Ui/NotFoundBanner/NotFoundBanner.tsx | 32 +++++ src/lib/api/error.spec.ts | 34 ++++++ src/lib/api/error.ts | 4 + src/lib/api/types/crate/controlPlanes.ts | 34 +++--- src/lib/api/types/crate/listWorkspaces.ts | 4 +- .../mcp/pages/McpPage.tsx} | 109 +++++++----------- src/spaces/onboarding/pages/ProjectPage.tsx | 65 +++++++++++ .../ControlPlanes/ControlPlaneListView.tsx | 53 --------- 15 files changed, 271 insertions(+), 180 deletions(-) create mode 100644 src/components/Ui/NotFoundBanner/NotFoundBanner.cy.tsx create mode 100644 src/components/Ui/NotFoundBanner/NotFoundBanner.module.css create mode 100644 src/components/Ui/NotFoundBanner/NotFoundBanner.tsx create mode 100644 src/lib/api/error.spec.ts rename src/{views/ControlPlanes/ControlPlaneView.tsx => spaces/mcp/pages/McpPage.tsx} (58%) create mode 100644 src/spaces/onboarding/pages/ProjectPage.tsx delete mode 100644 src/views/ControlPlanes/ControlPlaneListView.tsx diff --git a/public/locales/en.json b/public/locales/en.json index c9f3294b..aa053a98 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -5,6 +5,10 @@ "learnMore": "Learn more in our documentation", "signInButton": "Sign in" }, + "Entities": { + "ManagedControlPlane": "Managed Control Plane", + "Project": "Project" + }, "ComponentList": { "tableComponentHeader": "Name", "tableVersionHeader": "Version" @@ -87,6 +91,11 @@ "subtitleMessage": "Get started by creating your first Managed Control Plane.", "helpButton": "Help" }, + "NotFoundBanner": { + "titleMessage": "{{entityType}} not found", + "subtitleMessage": "Sorry, we couldn’t find what you are looking for.
The link may be incorrect or the {{entityType}} might have been removed.", + "navigateHome": "Back to Homepage" + }, "IntelligentBreadcrumbs": { "homeLabel": "Home" }, @@ -151,7 +160,7 @@ "EditMembers": { "addButton": "Add" }, - "ControlPlaneListView": { + "ProjectsPage": { "header": "Your instances of ManagedControlPlane", "projectHeader": "Project:" }, @@ -161,7 +170,7 @@ "deleteProject": "Delete project", "deleteConfirmationDialog": "Project deleted" }, - "ControlPlaneView": { + "McpPage": { "accessError": "Managed Control Plane does not have access information yet", "componentsTitle": "Components", "crossplaneTitle": "Crossplane", diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index b65c4ff4..b621a018 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -1,10 +1,10 @@ import { HashRouter as Router, Navigate, Route } from 'react-router-dom'; -import ControlPlaneView from './views/ControlPlanes/ControlPlaneView.tsx'; import ProjectListView from './views/ProjectList'; -import ControlPlaneListView from './views/ControlPlanes/ControlPlaneListView.tsx'; import GlobalProviderOutlet from './components/Core/ApiConfigWrapper.tsx'; import { ShellBarComponent } from './components/Core/ShellBar.tsx'; import { SentryRoutes } from './mount.ts'; +import ProjectPage from './spaces/onboarding/pages/ProjectPage.tsx'; +import McpPage from './spaces/mcp/pages/McpPage.tsx'; function AppRouter() { return ( @@ -14,13 +14,14 @@ function AppRouter() { }> } /> - } /> + } /> } + element={} /> } /> + } /> diff --git a/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx b/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx index 92a2b0ca..8e764abf 100644 --- a/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx +++ b/src/components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx @@ -1,45 +1,31 @@ import { Button, FlexBox, IllustratedMessage } from '@ui5/webcomponents-react'; -import IllustratedError from '../../Shared/IllustratedError.tsx'; import '@ui5/webcomponents-fiori/dist/illustrations/NoData.js'; import '@ui5/webcomponents-fiori/dist/illustrations/EmptyList.js'; import '@ui5/webcomponents-icons/dist/delete'; -import Loading from '../../Shared/Loading.tsx'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; import { ControlPlaneListWorkspaceGridTile } from './ControlPlaneListWorkspaceGridTile.tsx'; -import { useApiResource } from '../../../lib/api/useApiResource.ts'; -import { ListWorkspaces } from '../../../lib/api/types/crate/listWorkspaces.ts'; +import { ListWorkspacesType } from '../../../lib/api/types/crate/listWorkspaces.ts'; import { useLink } from '../../../lib/shared/useLink.ts'; import { useTranslation } from 'react-i18next'; interface Props { projectName: string; + workspaces: ListWorkspacesType[]; } -export default function ControlPlaneListAllWorkspaces({ projectName }: Props) { +export default function ControlPlaneListAllWorkspaces({ projectName, workspaces }: Props) { const { workspaceCreationGuide } = useLink(); - const { data: allWorkspaces, error } = useApiResource( - ListWorkspaces(projectName), - ); const { t } = useTranslation(); - if (!allWorkspaces) { - return ; - } - if (error) { - return ; - } - return ( <> - {allWorkspaces.length === 0 ? ( + {workspaces.length === 0 ? ( } + illustrationName={IllustrationMessageType.NoData} + />, + ); + + cy.get('button').contains('Button as subtitle').should('be.visible'); + }); + it('renders help button with correct text and icon', () => { cy.mount( ', () => { ); cy.get('ui5-button').contains('Need Help?').should('be.visible'); - cy.get('ui5-button').should( - 'have.attr', - 'icon', - 'sap-icon://question-mark', - ); + cy.get('ui5-button').should('have.attr', 'icon', 'sap-icon://question-mark'); }); it('renders a link with correct attributes', () => { diff --git a/src/components/Ui/IllustratedBanner/IllustratedBanner.tsx b/src/components/Ui/IllustratedBanner/IllustratedBanner.tsx index b203cb33..d89b45bd 100644 --- a/src/components/Ui/IllustratedBanner/IllustratedBanner.tsx +++ b/src/components/Ui/IllustratedBanner/IllustratedBanner.tsx @@ -1,43 +1,50 @@ import IllustrationMessageDesign from '@ui5/webcomponents-fiori/dist/types/IllustrationMessageDesign.js'; import IllustrationMessageType from '@ui5/webcomponents-fiori/dist/types/IllustrationMessageType.js'; -import { FlexBox, IllustratedMessage, Button } from '@ui5/webcomponents-react'; +import { FlexBox, IllustratedMessage, Button, UI5WCSlotsNode } from '@ui5/webcomponents-react'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; import '@ui5/webcomponents-fiori/dist/illustrations/AllIllustrations.js'; +import { ReactElement } from 'react'; type InfoBannerProps = { title: string; - subtitle: string; + subtitle: string | UI5WCSlotsNode; illustrationName: IllustrationMessageType; // e.g. 'NoData', 'SimpleError', etc. help?: { link: string; buttonText: string; buttonIcon?: string; }; - button?: React.ReactElement; + button?: ReactElement; }; export const IllustratedBanner = ({ title, - subtitle, + subtitle: subtitleProp, illustrationName, help, button, }: InfoBannerProps) => { + let subtitleText, subtitleNode; + if (typeof subtitleProp === 'string') { + subtitleText = subtitleProp; + } else { + subtitleNode = subtitleProp; + } + return ( {help && ( diff --git a/src/components/Ui/NotFoundBanner/NotFoundBanner.cy.tsx b/src/components/Ui/NotFoundBanner/NotFoundBanner.cy.tsx new file mode 100644 index 00000000..771ea4df --- /dev/null +++ b/src/components/Ui/NotFoundBanner/NotFoundBanner.cy.tsx @@ -0,0 +1,18 @@ +import '@ui5/webcomponents-fiori/dist/illustrations/AllIllustrations.js'; +import { NotFoundBanner } from './NotFoundBanner.tsx'; +import { MemoryRouter } from 'react-router-dom'; + +describe('', () => { + it('renders title and subtitle interpolating the entityType', () => { + cy.mount( + + + , + ); + + cy.contains('%entityType% not found').should('be.visible'); + cy.contains('Sorry, we couldn’t find what you are looking for').should('be.visible'); + + cy.get('ui5-button').contains('Back to Homepage'); + }); +}); diff --git a/src/components/Ui/NotFoundBanner/NotFoundBanner.module.css b/src/components/Ui/NotFoundBanner/NotFoundBanner.module.css new file mode 100644 index 00000000..f017a659 --- /dev/null +++ b/src/components/Ui/NotFoundBanner/NotFoundBanner.module.css @@ -0,0 +1,9 @@ +.subtitleContainer { + display: flex; + flex-direction: column; +} + +.button { + margin-inline: auto; + margin-block: 2rem; +} \ No newline at end of file diff --git a/src/components/Ui/NotFoundBanner/NotFoundBanner.tsx b/src/components/Ui/NotFoundBanner/NotFoundBanner.tsx new file mode 100644 index 00000000..8e2e7741 --- /dev/null +++ b/src/components/Ui/NotFoundBanner/NotFoundBanner.tsx @@ -0,0 +1,32 @@ +import { IllustratedBanner } from '../IllustratedBanner/IllustratedBanner.tsx'; +import IllustrationMessageType from '@ui5/webcomponents-fiori/dist/types/IllustrationMessageType.js'; +import { Trans, useTranslation } from 'react-i18next'; + +import styles from './NotFoundBanner.module.css'; +import { Button } from '@ui5/webcomponents-react'; +import { useNavigate } from 'react-router-dom'; + +export interface NotFoundBannerProps { + entityType: string; +} +export function NotFoundBanner({ entityType }: NotFoundBannerProps) { + const { t } = useTranslation(); + const navigate = useNavigate(); + + return ( + + + + + + + } + /> + ); +} diff --git a/src/lib/api/error.spec.ts b/src/lib/api/error.spec.ts new file mode 100644 index 00000000..53338c82 --- /dev/null +++ b/src/lib/api/error.spec.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest'; +import { isNotFoundError, APIError } from './error'; + +describe('error', () => { + describe('isNotFoundError', () => { + it('should return true if error.status is 404', () => { + expect(isNotFoundError(new APIError('', 404))).toBe(true); + expect(isNotFoundError(new APIError('not found', 404))).toBe(true); + }); + + it('should return true if error.status is 403', () => { + expect(isNotFoundError(new APIError('', 403))).toBe(true); + expect(isNotFoundError(new APIError('not found', 403))).toBe(true); + }); + + it('should return false if error is undefined', () => { + expect(isNotFoundError(undefined)).toBe(false); + }); + + it('should return false if error is null', () => { + expect(isNotFoundError(null)).toBe(false); + }); + + it('should return false if error has no status field', () => { + expect(isNotFoundError({} as APIError)).toBe(false); + }); + + it('should return false if error.status is not 404 or 403', () => { + expect(isNotFoundError(new APIError('', 500))).toBe(false); + expect(isNotFoundError(new APIError('', 400))).toBe(false); + expect(isNotFoundError(new APIError('', 401))).toBe(false); + }); + }); +}); diff --git a/src/lib/api/error.ts b/src/lib/api/error.ts index 97788900..3b35a067 100644 --- a/src/lib/api/error.ts +++ b/src/lib/api/error.ts @@ -21,3 +21,7 @@ export class ValidationError extends Error { Object.setPrototypeOf(this, ValidationError.prototype); } } + +export function isNotFoundError(error?: APIError | null): boolean { + return !!error && (error.status === 404 || error.status === 403); +} diff --git a/src/lib/api/types/crate/controlPlanes.ts b/src/lib/api/types/crate/controlPlanes.ts index 33ea8498..0df87bcf 100644 --- a/src/lib/api/types/crate/controlPlanes.ts +++ b/src/lib/api/types/crate/controlPlanes.ts @@ -10,13 +10,13 @@ export interface Metadata { export interface ControlPlaneType { metadata: Metadata; spec: - | { - authentication: { - enableSystemIdentityProvider?: boolean; - }; - components: ControlPlaneComponentsType; - } - | undefined; + | { + authentication: { + enableSystemIdentityProvider?: boolean; + }; + components: ControlPlaneComponentsType; + } + | undefined; status: ControlPlaneStatusType | undefined; } @@ -36,13 +36,13 @@ export interface ControlPlaneStatusType { status: ReadyStatus; conditions: ControlPlaneStatusCondition[]; access: - | { - key: string | undefined; - name: string | undefined; - namespace: string | undefined; - kubeconfig: string | undefined; - } - | undefined; + | { + key: string | undefined; + name: string | undefined; + namespace: string | undefined; + kubeconfig: string | undefined; + } + | undefined; } export interface ControlPlaneStatusCondition { @@ -73,9 +73,9 @@ export const ListControlPlanes = ( }; export const ControlPlane = ( - projectName: string, - workspaceName: string, - controlPlaneName: string, + projectName?: string, + workspaceName?: string, + controlPlaneName?: string, ): Resource => { return { path: `/apis/core.openmcp.cloud/v1alpha1/namespaces/project-${projectName}--ws-${workspaceName}/managedcontrolplanes/${controlPlaneName}`, diff --git a/src/lib/api/types/crate/listWorkspaces.ts b/src/lib/api/types/crate/listWorkspaces.ts index 116c281c..ffbbe59c 100644 --- a/src/lib/api/types/crate/listWorkspaces.ts +++ b/src/lib/api/types/crate/listWorkspaces.ts @@ -19,9 +19,7 @@ export function isWorkspaceReady(workspace: ListWorkspacesType): boolean { return workspace.status != null && workspace.status.namespace != null; } -export const ListWorkspaces = ( - projectName: string, -): Resource => { +export const ListWorkspaces = (projectName?: string): Resource => { return { path: `/apis/core.openmcp.cloud/v1alpha1/namespaces/project-${projectName}/workspaces`, jq: '[.items[] | {metadata: .metadata | {name, namespace, annotations, deletionTimestamp}, status: .status, spec: .spec | {members: [.members[] | {name, roles}]}}]', diff --git a/src/views/ControlPlanes/ControlPlaneView.tsx b/src/spaces/mcp/pages/McpPage.tsx similarity index 58% rename from src/views/ControlPlanes/ControlPlaneView.tsx rename to src/spaces/mcp/pages/McpPage.tsx index e4e0a99e..7c49b983 100644 --- a/src/views/ControlPlanes/ControlPlaneView.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -1,63 +1,50 @@ -import { - ObjectPage, - ObjectPageSection, - ObjectPageTitle, - Panel, - Title, -} from '@ui5/webcomponents-react'; +import { BusyIndicator, ObjectPage, ObjectPageSection, ObjectPageTitle, Panel, Title } from '@ui5/webcomponents-react'; import { useParams } from 'react-router-dom'; -import CopyKubeconfigButton from '../../components/ControlPlanes/CopyKubeconfigButton.tsx'; +import CopyKubeconfigButton from '../../../components/ControlPlanes/CopyKubeconfigButton.tsx'; import '@ui5/webcomponents-fiori/dist/illustrations/SimpleBalloon'; import '@ui5/webcomponents-fiori/dist/illustrations/SimpleError'; // thorws error sometimes if not imported import '@ui5/webcomponents-fiori/dist/illustrations/BeforeSearch'; -import IllustratedError from '../../components/Shared/IllustratedError.tsx'; -import IntelligentBreadcrumbs from '../../components/Core/IntelligentBreadcrumbs.tsx'; +import IllustratedError from '../../../components/Shared/IllustratedError.tsx'; +import IntelligentBreadcrumbs from '../../../components/Core/IntelligentBreadcrumbs.tsx'; -import FluxList from '../../components/ControlPlane/FluxList.tsx'; -import { ControlPlane as ControlPlaneResource } from '../../lib/api/types/crate/controlPlanes.ts'; +import FluxList from '../../../components/ControlPlane/FluxList.tsx'; +import { ControlPlane as ControlPlaneResource } from '../../../lib/api/types/crate/controlPlanes.ts'; import { useTranslation } from 'react-i18next'; -import { - McpContextProvider, - WithinManagedControlPlane, -} from '../../lib/shared/McpContext.tsx'; -import { ManagedResources } from '../../components/ControlPlane/ManagedResources.tsx'; -import { ProvidersConfig } from '../../components/ControlPlane/ProvidersConfig.tsx'; -import { Providers } from '../../components/ControlPlane/Providers.tsx'; -import ComponentList from '../../components/ControlPlane/ComponentList.tsx'; -import MCPHealthPopoverButton from '../../components/ControlPlane/MCPHealthPopoverButton.tsx'; -import useResource from '../../lib/api/useApiResource'; +import { McpContextProvider, WithinManagedControlPlane } from '../../../lib/shared/McpContext.tsx'; +import { ManagedResources } from '../../../components/ControlPlane/ManagedResources.tsx'; +import { ProvidersConfig } from '../../../components/ControlPlane/ProvidersConfig.tsx'; +import { Providers } from '../../../components/ControlPlane/Providers.tsx'; +import ComponentList from '../../../components/ControlPlane/ComponentList.tsx'; +import MCPHealthPopoverButton from '../../../components/ControlPlane/MCPHealthPopoverButton.tsx'; +import useResource from '../../../lib/api/useApiResource.ts'; -import { YamlViewButtonWithLoader } from '../../components/Yaml/YamlViewButtonWithLoader.tsx'; -import { Landscapers } from '../../components/ControlPlane/Landscapers.tsx'; -import { AuthProviderMcp } from '../../spaces/mcp/auth/AuthContextMcp.tsx'; +import { YamlViewButtonWithLoader } from '../../../components/Yaml/YamlViewButtonWithLoader.tsx'; +import { Landscapers } from '../../../components/ControlPlane/Landscapers.tsx'; +import { AuthProviderMcp } from '../auth/AuthContextMcp.tsx'; +import { isNotFoundError } from '../../../lib/api/error.ts'; +import { NotFoundBanner } from '../../../components/Ui/NotFoundBanner/NotFoundBanner.tsx'; -export default function ControlPlaneView() { - const { projectName, workspaceName, controlPlaneName, contextName } = - useParams(); +export default function McpPage() { + const { projectName, workspaceName, controlPlaneName, contextName } = useParams(); const { t } = useTranslation(); - const { data: mcp, error } = useResource( - ControlPlaneResource( - projectName ?? '', - workspaceName ?? '', - controlPlaneName ?? '', - ), - ); + const { + data: mcp, + error, + isLoading, + } = useResource(ControlPlaneResource(projectName, workspaceName, controlPlaneName)); - if (!projectName || !workspaceName || !controlPlaneName) { - return <>; + if (isLoading) { + return ; } - if (error) { - return ; + if (!projectName || !workspaceName || !controlPlaneName || !contextName || isNotFoundError(error)) { + return ; } - if ( - !mcp?.status?.access?.key || - !mcp?.status?.access?.name || - !mcp?.status?.access?.namespace - ) { - return ; + + if (error || !mcp) { + return ; } return ( @@ -66,7 +53,7 @@ export default function ControlPlaneView() { project: projectName, workspace: workspaceName, name: controlPlaneName, - context: contextName!, + context: contextName, }} > @@ -107,17 +94,13 @@ export default function ControlPlaneView() { - {t('ControlPlaneView.componentsTitle')} - - } + header={{t('McpPage.componentsTitle')}} noAnimation > @@ -126,17 +109,13 @@ export default function ControlPlaneView() { - {t('ControlPlaneView.crossplaneTitle')} - - } + header={{t('McpPage.crossplaneTitle')}} noAnimation >
@@ -153,17 +132,13 @@ export default function ControlPlaneView() { - {t('ControlPlaneView.landscapersTitle')} - - } + header={{t('McpPage.landscapersTitle')}} noAnimation > @@ -172,15 +147,13 @@ export default function ControlPlaneView() { {t('ControlPlaneView.gitOpsTitle')} - } + header={{t('McpPage.gitOpsTitle')}} noAnimation > diff --git a/src/spaces/onboarding/pages/ProjectPage.tsx b/src/spaces/onboarding/pages/ProjectPage.tsx new file mode 100644 index 00000000..3f94c4de --- /dev/null +++ b/src/spaces/onboarding/pages/ProjectPage.tsx @@ -0,0 +1,65 @@ +import { ObjectPage, ObjectPageTitle, Title } from '@ui5/webcomponents-react'; +import ProjectChooser from '../../../components/Projects/ProjectChooser.tsx'; +import { useParams } from 'react-router-dom'; +import ControlPlaneListAllWorkspaces from '../../../components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx'; +import IntelligentBreadcrumbs from '../../../components/Core/IntelligentBreadcrumbs.tsx'; +import { ControlPlaneListToolbar } from '../../../components/ControlPlanes/List/ControlPlaneListToolbar.tsx'; +import { Trans, useTranslation } from 'react-i18next'; +import useApiResource from '../../../lib/api/useApiResource.ts'; +import { ListWorkspaces } from '../../../lib/api/types/crate/listWorkspaces.ts'; +import Loading from '../../../components/Shared/Loading.tsx'; +import { isNotFoundError } from '../../../lib/api/error.ts'; +import { NotFoundBanner } from '../../../components/Ui/NotFoundBanner/NotFoundBanner.tsx'; +import IllustratedError from '../../../components/Shared/IllustratedError.tsx'; + +export default function ProjectPage() { + const { projectName } = useParams(); + const { data: workspaces, error, isLoading } = useApiResource(ListWorkspaces(projectName)); + const { t } = useTranslation(); + + if (isLoading) { + return ; + } + + if (isNotFoundError(error)) { + return ; + } + + if (error || !workspaces || !projectName) { + return ; + } + + return ( + <> + + }} /> + + } + subHeader={ +
+

{t('ProjectsPage.projectHeader')}

+ +
+ } + breadcrumbs={} + actionsBar={} + /> + } + //TODO: project chooser should be part of the breadcrumb section if possible? + > + +
+ + ); +} diff --git a/src/views/ControlPlanes/ControlPlaneListView.tsx b/src/views/ControlPlanes/ControlPlaneListView.tsx deleted file mode 100644 index 4fe4ea43..00000000 --- a/src/views/ControlPlanes/ControlPlaneListView.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ObjectPage, ObjectPageTitle, Title } from '@ui5/webcomponents-react'; -import ProjectChooser from '../../components/Projects/ProjectChooser.tsx'; -import { useParams } from 'react-router-dom'; -import ControlPlaneListAllWorkspaces from '../../components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx'; -import IntelligentBreadcrumbs from '../../components/Core/IntelligentBreadcrumbs.tsx'; -import { ControlPlaneListToolbar } from '../../components/ControlPlanes/List/ControlPlaneListToolbar.tsx'; -import { Trans, useTranslation } from 'react-i18next'; - -export default function ControlPlaneListView() { - const { projectName } = useParams(); - const { t } = useTranslation(); - - return ( - <> - - }} - /> - - } - subHeader={ -
-

- {t('ControlPlaneListView.projectHeader')} -

- -
- } - breadcrumbs={} - actionsBar={ - - } - /> - } - //TODO: project chooser should be part of the breadcrumb section if possible? - > - -
- - ); -} From 630f5775fb59fa60cc52a93508491828c5580a36 Mon Sep 17 00:00:00 2001 From: Andreas Kienle Date: Tue, 22 Jul 2025 12:36:42 +0200 Subject: [PATCH 2/4] Fix type --- src/spaces/mcp/pages/McpPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spaces/mcp/pages/McpPage.tsx b/src/spaces/mcp/pages/McpPage.tsx index 7c49b983..1d5152c8 100644 --- a/src/spaces/mcp/pages/McpPage.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -44,7 +44,7 @@ export default function McpPage() { } if (error || !mcp) { - return ; + return ; } return ( From 547a52a104eeba634e420bd8d536e9e5ba3af6ca Mon Sep 17 00:00:00 2001 From: Andreas Kienle Date: Tue, 22 Jul 2025 12:40:38 +0200 Subject: [PATCH 3/4] Fix type --- src/spaces/onboarding/pages/ProjectPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spaces/onboarding/pages/ProjectPage.tsx b/src/spaces/onboarding/pages/ProjectPage.tsx index 3f94c4de..22b7cac6 100644 --- a/src/spaces/onboarding/pages/ProjectPage.tsx +++ b/src/spaces/onboarding/pages/ProjectPage.tsx @@ -26,7 +26,7 @@ export default function ProjectPage() { } if (error || !workspaces || !projectName) { - return ; + return ; } return ( From 7bebddb145fd43b4ac8f854ce80bb7cb0c0b0433 Mon Sep 17 00:00:00 2001 From: Andreas Kienle Date: Thu, 24 Jul 2025 13:42:04 +0200 Subject: [PATCH 4/4] Fix imports --- src/spaces/mcp/pages/McpPage.tsx | 4 ++-- src/spaces/onboarding/pages/ProjectPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spaces/mcp/pages/McpPage.tsx b/src/spaces/mcp/pages/McpPage.tsx index 1d5152c8..ca2e4b5d 100644 --- a/src/spaces/mcp/pages/McpPage.tsx +++ b/src/spaces/mcp/pages/McpPage.tsx @@ -17,7 +17,7 @@ import { ProvidersConfig } from '../../../components/ControlPlane/ProvidersConfi import { Providers } from '../../../components/ControlPlane/Providers.tsx'; import ComponentList from '../../../components/ControlPlane/ComponentList.tsx'; import MCPHealthPopoverButton from '../../../components/ControlPlane/MCPHealthPopoverButton.tsx'; -import useResource from '../../../lib/api/useApiResource.ts'; +import { useApiResource } from '../../../lib/api/useApiResource.ts'; import { YamlViewButtonWithLoader } from '../../../components/Yaml/YamlViewButtonWithLoader.tsx'; import { Landscapers } from '../../../components/ControlPlane/Landscapers.tsx'; @@ -33,7 +33,7 @@ export default function McpPage() { data: mcp, error, isLoading, - } = useResource(ControlPlaneResource(projectName, workspaceName, controlPlaneName)); + } = useApiResource(ControlPlaneResource(projectName, workspaceName, controlPlaneName)); if (isLoading) { return ; diff --git a/src/spaces/onboarding/pages/ProjectPage.tsx b/src/spaces/onboarding/pages/ProjectPage.tsx index 22b7cac6..aa5288a8 100644 --- a/src/spaces/onboarding/pages/ProjectPage.tsx +++ b/src/spaces/onboarding/pages/ProjectPage.tsx @@ -5,7 +5,7 @@ import ControlPlaneListAllWorkspaces from '../../../components/ControlPlanes/Lis import IntelligentBreadcrumbs from '../../../components/Core/IntelligentBreadcrumbs.tsx'; import { ControlPlaneListToolbar } from '../../../components/ControlPlanes/List/ControlPlaneListToolbar.tsx'; import { Trans, useTranslation } from 'react-i18next'; -import useApiResource from '../../../lib/api/useApiResource.ts'; +import { useApiResource } from '../../../lib/api/useApiResource.ts'; import { ListWorkspaces } from '../../../lib/api/types/crate/listWorkspaces.ts'; import Loading from '../../../components/Shared/Loading.tsx'; import { isNotFoundError } from '../../../lib/api/error.ts';