Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5fb8659
Update controlPlanes.ts
lucasgoral Nov 18, 2025
0b62d18
init
lucasgoral Nov 18, 2025
cb189cc
Create useGetMcpUserRights.ts
lucasgoral Nov 18, 2025
a37efd7
fix
lucasgoral Nov 18, 2025
32b7f19
refactor
lucasgoral Nov 18, 2025
2c331ab
Update ManagedControlPlaneAuthorization.tsx
lucasgoral Nov 18, 2025
167641a
fix
lucasgoral Nov 18, 2025
712a596
fix
lucasgoral Nov 18, 2025
dc19d5a
fix
lucasgoral Nov 18, 2025
61e524b
fixes
lucasgoral Nov 18, 2025
ec47667
Update createManagedControlPlane.ts
lucasgoral Nov 19, 2025
7ea2c60
fix
lucasgoral Nov 19, 2025
31f683d
fix
lucasgoral Nov 19, 2025
298e433
fix
lucasgoral Nov 20, 2025
9fef1f3
fixes
lucasgoral Nov 20, 2025
0ae6a20
fix
lucasgoral Nov 20, 2025
eff0e91
Update ProjectsList.tsx
lucasgoral Nov 20, 2025
7961d81
fix
lucasgoral Nov 20, 2025
c2b3815
fix
lucasgoral Nov 20, 2025
2dffee6
fix
lucasgoral Nov 20, 2025
b4a41e4
Merge branch 'main' into mcp-where-user-is-not-member-showing-generic…
lucasgoral Nov 21, 2025
6ea7ff8
Merge branch 'main' into mcp-where-user-is-not-member-showing-generic…
lucasgoral Nov 26, 2025
197e54f
Merge branch 'main' into mcp-where-user-is-not-member-showing-generic…
lucasgoral Nov 28, 2025
2bb19bc
Update McpPage.tsx
lucasgoral Nov 28, 2025
5fdc05c
Update src/spaces/mcp/pages/McpPage.tsx
lucasgoral Nov 28, 2025
4e146e4
Update src/spaces/mcp/authorization/ManagedControlPlaneAuthorization.tsx
lucasgoral Nov 28, 2025
ca798bb
Update ManagedControlPlaneAuthorization.tsx
lucasgoral Nov 28, 2025
886df06
Update McpPage.tsx
lucasgoral Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -523,5 +523,14 @@
"branchTitle": "Branch",
"gitRepositoryCreated": "Git Repository created successfully",
"gitRepositoryCreationFailed": "Failed to create Git Repository: {{error}}"
},
"mcp": {
"authorization": {
"accessDenied": {
"title": "Access Denied",
"details": "You are not authorized to see this Managed Control Plane."
},
"backToWorkspaces": "Back to Workspaces"
}
}
}
23 changes: 12 additions & 11 deletions src/components/ControlPlanes/ControlPlaneCard/ControlPlaneCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,21 @@ export const ControlPlaneCard = ({
resourceName={controlPlane.metadata.name}
resourceType={'managedcontrolplanes'}
/>
{showWarningBecauseOfDisabledSystemIdentityProvider && (
<Infobox size="sm" variant="warning">
{showWarningBecauseOfDisabledSystemIdentityProvider ? (
<Infobox size="sm" variant="warning" noMargin>
{t('ConnectButton.unsupportedIdP')}
</Infobox>
) : (
<ConnectButton
disabled={!isConnectButtonEnabled}
controlPlaneName={name}
projectName={projectName}
workspaceName={workspace.metadata.name ?? ''}
namespace={controlPlane.status?.access?.namespace ?? ''}
secretName={controlPlane.status?.access?.name ?? ''}
secretKey={controlPlane.status?.access?.key ?? ''}
/>
)}
<ConnectButton
disabled={!isConnectButtonEnabled}
controlPlaneName={name}
projectName={projectName}
workspaceName={workspace.metadata.name ?? ''}
namespace={controlPlane.status?.access?.namespace ?? ''}
secretName={controlPlane.status?.access?.name ?? ''}
secretKey={controlPlane.status?.access?.key ?? ''}
/>
</FlexBox>
</FlexBox>
</FlexBox>
Expand Down
12 changes: 12 additions & 0 deletions src/components/Ui/Center/Center.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.wrapper {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}

.textAlignCenter {
text-align: center;
}
19 changes: 19 additions & 0 deletions src/components/Ui/Center/Center.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { PropsWithChildren, ReactNode, CSSProperties } from 'react';
import cx from 'clsx';
import styles from './Center.module.css';

export type CenterProps = PropsWithChildren<{
className?: string;
style?: CSSProperties;
textAlignCenter?: boolean;
}>;

export const Center = ({ children, className, style, textAlignCenter = true }: CenterProps): ReactNode => {
const classes = cx(styles.wrapper, { [styles.textAlignCenter]: textAlignCenter }, className);

return (
<div className={classes} style={style}>
{children}
</div>
);
};
14 changes: 12 additions & 2 deletions src/components/Ui/Infobox/Infobox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
margin-right: 1rem;
}

.icon-sm {
width: 1.25rem;
height: 1.25rem;
}

.content {
flex-grow: 1;
padding-right: 0.5rem;
Expand All @@ -29,8 +34,9 @@
}

.size-sm {
padding: 0.75rem 1rem;
font-size: 0.875rem;
padding: 0.5rem 0.875rem;
font-size: 0.75rem;
border-radius: 0.5rem;
}

.size-md {
Expand Down Expand Up @@ -67,3 +73,7 @@
color: var(--sapBackgroundColor);
line-height: 1.2rem;
}

.no-margin {
margin-bottom: 0;
}
5 changes: 4 additions & 1 deletion src/components/Ui/Infobox/Infobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface LabelProps {
fullWidth?: boolean;
className?: string;
icon?: string;
noMargin?: boolean;
}

const variantIcons = {
Expand All @@ -29,6 +30,7 @@ export const Infobox: React.FC<LabelProps> = ({
fullWidth = false,
className,
icon,
noMargin = false,
}) => {
const infoboxClasses = cx(
styles.infobox,
Expand All @@ -41,6 +43,7 @@ export const Infobox: React.FC<LabelProps> = ({
[styles['variant-warning']]: variant === 'warning',
[styles['variant-danger']]: variant === 'danger',
[styles['full-width']]: fullWidth,
[styles['no-margin']]: noMargin,
},
className,
);
Expand All @@ -49,7 +52,7 @@ export const Infobox: React.FC<LabelProps> = ({

return (
<div className={infoboxClasses} id={id}>
{iconName && <Icon name={iconName} className={styles.icon} />}
{iconName && <Icon name={iconName} className={cx(styles.icon, { [styles['icon-sm']]: size === 'sm' })} />}
<div className={styles.content}>{children}</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Ui/NotFoundBanner/NotFoundBanner.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
.button {
margin-inline: auto;
margin-block: 2rem;
}
}
31 changes: 17 additions & 14 deletions src/components/Ui/NotFoundBanner/NotFoundBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Trans, useTranslation } from 'react-i18next';
import styles from './NotFoundBanner.module.css';
import { Button } from '@ui5/webcomponents-react';
import { useNavigate } from 'react-router-dom';
import { Center } from '../Center/Center.tsx';

export interface NotFoundBannerProps {
entityType: string;
Expand All @@ -14,19 +15,21 @@ export function NotFoundBanner({ entityType }: NotFoundBannerProps) {
const navigate = useNavigate();

return (
<IllustratedBanner
illustrationName={IllustrationMessageType.PageNotFound}
title={t('NotFoundBanner.titleMessage', { entityType })}
subtitle={
<div className={styles.subtitleContainer}>
<span>
<Trans i18nKey="NotFoundBanner.subtitleMessage" values={{ entityType }} />
</span>
<Button className={styles.button} onClick={() => navigate('/')}>
{t('NotFoundBanner.navigateHome')}
</Button>
</div>
}
/>
<Center>
<IllustratedBanner
illustrationName={IllustrationMessageType.PageNotFound}
title={t('NotFoundBanner.titleMessage', { entityType })}
subtitle={
<div className={styles.subtitleContainer}>
<span>
<Trans i18nKey="NotFoundBanner.subtitleMessage" values={{ entityType }} />
</span>
<Button className={styles.button} onClick={() => navigate('/')}>
{t('NotFoundBanner.navigateHome')}
</Button>
</div>
}
/>
</Center>
);
}
5 changes: 5 additions & 0 deletions src/lib/api/types/crossplane/CRDList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ export type CRDResponse = {
export const CRDRequest: Resource<CRDResponse> = {
path: '/apis/apiextensions.k8s.io/v1/customresourcedefinitions',
};

export const CRDRequestAuthCheck: Resource<CRDResponse> = {
path: '/apis/apiextensions.k8s.io/v1/customresourcedefinitions',
jq: '{kind: .kind}',
};
1 change: 0 additions & 1 deletion src/lib/shared/McpContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ interface Mcp {
project: string;
workspace: string;
name: string;

secretNamespace?: string;
secretName?: string;
secretKey?: string;
Expand Down
56 changes: 56 additions & 0 deletions src/spaces/mcp/authorization/ManagedControlPlaneAuthorization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';

import IllustratedError from '../../../components/Shared/IllustratedError.tsx';
import { BusyIndicator, Button } from '@ui5/webcomponents-react';
import { generatePath, useNavigate, useParams } from 'react-router-dom';
import { Routes } from '../../../Routes.ts';

import { Center } from '../../../components/Ui/Center/Center.tsx';
import { CRDRequestAuthCheck } from '../../../lib/api/types/crossplane/CRDList.ts';
import { useApiResource } from '../../../lib/api/useApiResource.ts';

export interface ManagedControlPlaneAuthorizationProps {
children: ReactNode;
}
export const ManagedControlPlaneAuthorization = ({ children }: ManagedControlPlaneAuthorizationProps) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { projectName, workspaceName } = useParams();
const onBack = () => {
if (workspaceName) {
navigate(
generatePath(Routes.Project, {
projectName: projectName ?? '',
}),
);
} else {
navigate(Routes.Home);
}
};

// Check if user has access to CRDs in the MCP's cluster
const { error, isLoading } = useApiResource(CRDRequestAuthCheck);
if (isLoading) {
return (
<Center>
<BusyIndicator active />
</Center>
);
}
const isUserNotAuthorized = error?.status === 403 || error?.status === 401;
if (isUserNotAuthorized)
return (
<Center>
<IllustratedError
title={t('mcp.authorization.accessDenied.title')}
details={t('mcp.authorization.accessDenied.details')}
/>
<Button design={'Default'} icon={'navigation-left-arrow'} onClick={onBack}>
{t('mcp.authorization.backToWorkspaces')}
</Button>
</Center>
);

return <>{children}</>;
};
Loading
Loading