Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 11 additions & 8 deletions cypress/support/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { mount } from 'cypress/react';
import './commands';
import { FrontendConfigContext } from '../../src/context/FrontendConfigContext';
import { mockedFrontendConfig } from '../../src/utils/testing';

import { ToastProvider } from '../../src/context/ToastContext.tsx';
// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
Expand All @@ -36,12 +36,15 @@ declare global {
}
}


Cypress.Commands.add('mount', (component, options) => {
return mount(<ThemeProvider>
<FrontendConfigContext value={mockedFrontendConfig}>
{component}
</FrontendConfigContext>
</ThemeProvider>, options);
return mount(
<ThemeProvider>
<ToastProvider>
<FrontendConfigContext value={mockedFrontendConfig}>
{component}
</FrontendConfigContext>
</ToastProvider>
</ThemeProvider>,
options,
);
});

80 changes: 76 additions & 4 deletions public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@
"messageHeader": "Message",
"reasonHeader": "Reason",
"transitionHeader": "Last transition time"
},
},
"CopyKubeconfigButton": {
"copiedMessage": "Copied to Clipboard",
"copiedMessage": "Copied to Clipboard",
"failedMessage": "Failed to copy, Error:",
"menuDownload": "Download",
"menuCopy": "Copy to clipboard"
Expand Down Expand Up @@ -113,7 +113,6 @@
"errorMessage": "useCopyButton must be used within a CopyButtonProvider"
},
"CreateProjectWorkspaceDialog": {
"learnButton": "Learn how to do this in code",
"createButton": "Create",
"cancelButton": "Cancel",
"chargingTargetLabel": "Charging Target",
Expand Down Expand Up @@ -146,6 +145,79 @@
"copiedMessage": "Copied To Clipboard",
"failedMessage": "Failed to copy"
},
"DeleteWorkspaceDialog": {
"title": "Delete a Workspace",
"introSection1": "The below instructions will help you delete the workspace \"{{workspaceName}}\" from project namespace \"{{projectNamespace}}\" using kubectl.",
"introSection2": "Remember that this action is <bold1>irreversible</bold1> and all resources within the workspace will be <bold2>permanently deleted</bold2>.",
"mainCommandDescription": "Run this command to delete the workspace:",
"verificationCommandDescription": "To verify the workspace has been deleted, run:"
},
"KubectlDeleteMcpDialog": {
"title": "Delete a Managed Control Plane",
"introSection1": "The below will help you delete the Managed Control Plane \"{{mcpName}}\" from workspace \"{{workspaceNamespace}}\" using kubectl.",
"introSection2": "Remember that this action is <bold1>irreversible</bold1> and all resources managed by this control plane will be <bold2>permanently deleted</bold2>.",
"annotateCommand": "First, annotate the ManagedControlPlane to confirm deletion:",
"deleteCommand": "Run this command to delete the Managed Control Plane:",
"verificationCommandDescription": "To verify the MCP has been deleted, run:"
},
"KubectlCreateProjectDialog": {
"title": "Create a Project",
"introSection": "A Project is the starting point to our ManagedControlPlane offering. Projects are usually created for each Organization/Team or similar root setups.",
"formFields": {
"projectName": {
"label": "Project Name",
"placeholder": "Enter project name"
},
"chargingTargetId": {
"label": "BTP Global Account ID",
"placeholder": "Enter your Global Account ID",
"defaultValue": "<your-ga-id>"
},
"userEmail": {
"label": "User Email",
"placeholder": "Enter your email address",
"defaultValue": "<[email protected]>"
}
},
"mainCommandDescription": "Run this command to create a new project:",
"resultCommandDescription": "To see the result of the project creation, run the below command:",
"namespaceCommandDescription": "A namespace is automatically generated for your project:"
},
"KubectlCreateWorkspaceDialog": {
"title": "Create a Workspace",
"introSection": "Let's add a Workspace to our Project. We use workspaces to separate productive and non-productive ManagedControlPlanes.",
"formFields": {
"workspaceName": {
"label": "Workspace Name",
"placeholder": "Enter workspace name"
},
"chargingTargetId": {
"label": "BTP Global Account ID",
"placeholder": "Enter your Global Account ID",
"defaultValue": "<your-ga-id>"
},
"userEmail": {
"label": "User Email",
"placeholder": "Enter your email address",
"defaultValue": "<[email protected]>"
}
},
"mainCommandDescription": "Run this command to create a new workspace in your project:",
"resultCommandDescription": "To see the result of the workspace creation, run the below command:"
},
"KubectlBaseDialog": {
"prerequisites": "Prerequisites",
"prerequisitesText": "Make sure you have installed <bold1>kubectl</bold1> and the <bold2>kubelogin</bold2> plugin. We recommend using <bold3>krew</bold3> which is a plugin manager for kubectl.",
"formFieldsNote": "<bold1>Important</bold1>: Before executing, modify the commands below:",
"onboardingGuide": "You can also use our <link1>Onboarding Guide</link1> for more information."
},
"CommonKubectl": {
"emphasis": {
"irreversible": "irreversible",
"permanentlyDeleted": "permanently deleted"
},
"learnButton": "Learn how to do this in code"
},
"App": {
"loading": "Loading..."
},
Expand All @@ -164,4 +236,4 @@
"userExists": "User with this email already exists!",
"atLeastOneUser": "You need to have at least one member assigned."
}
}
}
8 changes: 8 additions & 0 deletions src/components/ControlPlanes/ControlPlaneCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../lib/api/types/crate/deleteMCP.ts';
import { DeleteConfirmationDialog } from '../Dialogs/DeleteConfirmationDialog.tsx';
import MCPHealthPopoverButton from '../ControlPlane/MCPHealthPopoverButton.tsx';
import { KubectlDeleteMcp } from '../Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteMcp.tsx';

interface Props {
controlPlane: ListControlPlanesType;
Expand Down Expand Up @@ -98,6 +99,13 @@ export function ControlPlaneCard({
</Card>
<DeleteConfirmationDialog
resourceName={controlPlane.metadata.name}
kubectl={
<KubectlDeleteMcp
projectName={projectName}
workspaceName={workspace.metadata.name}
resourceName={controlPlane.metadata.name}
/>
}
isOpen={dialogDeleteMcpIsOpen}
setIsOpen={setDialogDeleteMcpIsOpen}
onDeletionConfirmed={async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import useApiResource, {
} from '../../../lib/api/useApiResource.ts';
import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts';
import { DeleteConfirmationDialog } from '../../Dialogs/DeleteConfirmationDialog.tsx';
import { KubectlDeleteWorkspace } from '../../Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteWorkspace.tsx';
import { useToast } from '../../../context/ToastContext.tsx';
import { ListControlPlanes } from '../../../lib/api/types/crate/controlPlanes.ts';
import IllustratedError from '../../Shared/IllustratedError.tsx';
Expand Down Expand Up @@ -154,6 +155,12 @@ export function ControlPlaneListWorkspaceGridTile({
</ObjectPageSection>
<DeleteConfirmationDialog
resourceName={workspaceName}
kubectl={
<KubectlDeleteWorkspace
projectName={projectName}
resourceName={workspaceName}
/>
}
isOpen={dialogDeleteWsIsOpen}
setIsOpen={setDialogDeleteWsIsOpen}
onDeletionConfirmed={async () => {
Expand Down
42 changes: 27 additions & 15 deletions src/components/Dialogs/CreateProjectWorkspaceDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import {
import { Member } from '../../lib/api/types/shared/members';
import { ErrorDialog, ErrorDialogHandle } from '../Shared/ErrorMessageBox.tsx';

import { FormEvent, useState } from 'react';
import { KubectlInfoButton } from './KubectlCommandInfo/KubectlInfoButton.tsx';
import { KubectlCreateWorkspaceDialog } from './KubectlCommandInfo/KubectlCreateWorkspaceDialog.tsx';
import { KubectlCreateProjectDialog } from './KubectlCommandInfo/KubectlCreateProjectDialog.tsx';

import { EditMembers } from '../Members/EditMembers.tsx';
import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js';
import { useFrontendConfig } from '../../context/FrontendConfigContext.tsx';
// import { useFrontendConfig } from '../../context/FrontendConfigContext.tsx';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete unused code

Suggested change
// import { useFrontendConfig } from '../../context/FrontendConfigContext.tsx';

(Also further down)

import { useTranslation } from 'react-i18next';

import { CreateDialogProps } from './CreateWorkspaceDialogContainer.tsx';
import { FormEvent } from 'react';
import { FieldErrors, UseFormRegister, UseFormSetValue } from 'react-hook-form';

export type OnCreatePayload = {
Expand All @@ -38,6 +41,7 @@ export interface CreateProjectWorkspaceDialogProps {
register: UseFormRegister<CreateDialogProps>;
errors: FieldErrors<CreateDialogProps>;
setValue: UseFormSetValue<CreateDialogProps>;
projectName?: string;
}

export function CreateProjectWorkspaceDialog({
Expand All @@ -50,9 +54,14 @@ export function CreateProjectWorkspaceDialog({
register,
errors,
setValue,
projectName,
}: CreateProjectWorkspaceDialogProps) {
const { links } = useFrontendConfig();
// const { links } = useFrontendConfig();
const { t } = useTranslation();
const [isKubectlDialogOpen, setIsKubectlDialogOpen] = useState(false);

const openKubectlDialog = () => setIsKubectlDialogOpen(true);
const closeKubectlDialog = () => setIsKubectlDialogOpen(false);

return (
<>
Expand All @@ -65,24 +74,18 @@ export function CreateProjectWorkspaceDialog({
<Bar
design="Footer"
endContent={
<>
<Button
design={ButtonDesign.Transparent}
icon="sap-icon://question-mark"
onClick={() => {
window.open(links.COM_PAGE_GETTING_STARTED, '_blank');
}}
>
{t('CreateProjectWorkspaceDialog.learnButton')}
</Button>
<div
style={{ display: 'flex', alignItems: 'center', gap: '8px' }}
>
<KubectlInfoButton onClick={openKubectlDialog} />
<Button onClick={() => setIsOpen(false)}>
{' '}
{t('CreateProjectWorkspaceDialog.cancelButton')}
</Button>
<Button design="Emphasized" onClick={() => onCreate()}>
{t('CreateProjectWorkspaceDialog.createButton')}
</Button>
</>
</div>
}
/>
}
Expand All @@ -95,6 +98,15 @@ export function CreateProjectWorkspaceDialog({
setValue={setValue}
/>
</Dialog>
<KubectlCreateWorkspaceDialog
project={projectName}
isOpen={isKubectlDialogOpen && !!projectName}
onClose={closeKubectlDialog}
/>
<KubectlCreateProjectDialog
isOpen={isKubectlDialogOpen && !projectName}
onClose={closeKubectlDialog}
/>
<ErrorDialog ref={errorDialogRef} />
</>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/Dialogs/CreateWorkspaceDialogContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export function CreateWorkspaceDialogContainer({
register={register}
errors={errors}
setValue={setValue}
projectName={project}
onCreate={handleSubmit(handleWorkspaceCreate)}
/>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/Dialogs/DeleteConfirmationDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('DeleteConfirmationDialog', () => {
isOpen={true}
setIsOpen={cy.stub().as('setIsOpen')}
resourceName="test-resource"
kubectl={<div>Resource Component</div>}
onDeletionConfirmed={cy.stub().as('onDeletionConfirmed')}
onCanceled={cy.stub().as('onCanceled')}
{...props}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Dialogs/DeleteConfirmationDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { ReactNode, useEffect, useRef, useState } from 'react';
import {
Bar,
Button,
Expand All @@ -17,6 +17,7 @@ interface DeleteConfirmationDialogProps {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
resourceName: string;
kubectl: ReactNode;
onDeletionConfirmed?: () => void;
onCanceled?: () => void;
}
Expand All @@ -27,6 +28,7 @@ export function DeleteConfirmationDialog({
resourceName,
onDeletionConfirmed,
onCanceled,
kubectl,
}: DeleteConfirmationDialogProps) {
const [confirmed, setConfirmed] = useState(false);
const confirmationInput = useRef<InputDomRef>(null);
Expand Down Expand Up @@ -113,6 +115,7 @@ export function DeleteConfirmationDialog({
onInput={onConfirmationInputChange}
/>
</FormItem>
<FormItem>{kubectl}</FormItem>
</FormGroup>
</Form>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useState } from 'react';
import { KubectlDeleteMcpDialog } from '../KubectlDeleteMcpDialog';
import { KubectlInfoButton } from '../KubectlInfoButton';

interface KubectlDeleteMcpProps {
projectName: string;
workspaceName: string;
resourceName: string;
}

export const KubectlDeleteMcp = ({
projectName,
workspaceName,
resourceName,
}: KubectlDeleteMcpProps) => {
const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false);

const openInfoDialog = () => setIsInfoDialogOpen(true);
const closeInfoDialog = () => setIsInfoDialogOpen(false);

return (
<>
<KubectlInfoButton onClick={openInfoDialog} />
<KubectlDeleteMcpDialog
projectName={projectName}
workspaceName={workspaceName}
resourceName={resourceName}
isOpen={isInfoDialogOpen}
onClose={closeInfoDialog}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useState } from 'react';
import { DeleteWorkspaceDialog } from '../KubectlDeleteWorkspaceDialog';
import { KubectlInfoButton } from '../KubectlInfoButton';

interface KubectlDeleteWorkspaceProps {
projectName?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here: Can’t we just assume that there’s always a projectName?

resourceName: string;
}

export const KubectlDeleteWorkspace = ({
projectName,
resourceName,
}: KubectlDeleteWorkspaceProps) => {
const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false);

const openInfoDialog = () => setIsInfoDialogOpen(true);
const closeInfoDialog = () => setIsInfoDialogOpen(false);

return (
<>
<KubectlInfoButton onClick={openInfoDialog} />
<DeleteWorkspaceDialog
projectName={projectName}
resourceName={resourceName}
isOpen={isInfoDialogOpen}
onClose={closeInfoDialog}
/>
</>
);
};
Loading
Loading