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
5 changes: 4 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"createButton": "Create",
"cancelButton": "Cancel",
"chargingTargetLabel": "Charging Target",
"chargingTargetTypeLabel": "Charging Target Type",
"displayNameLabel": "Display Name",
"nameLabel": "Name",
"metadataHeader": "Metadata",
Expand Down Expand Up @@ -282,7 +283,9 @@
"name": "Name",
"componentSelection": "Component Selection",
"search": "Search",
"components": "Components"
"components": "Components",
"notSelected": "Not selected",
"btp": "BTP"
},
"buttons": {
"viewResource": "View resource",
Expand Down
1 change: 1 addition & 0 deletions src/components/Dialogs/CreateProjectDialog.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const CreateProjectWorkspaceDialogWrapper: React.FC<{
};
return (
<CreateProjectWorkspaceDialog
type={'workspace'}
isOpen={isOpen}
setIsOpen={setIsOpen}
titleText="Create Workspace"
Expand Down
6 changes: 5 additions & 1 deletion src/components/Dialogs/CreateProjectDialogContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@ export function CreateProjectDialogContainer({
name: '',
displayName: '',
chargingTarget: '',
chargingTargetType: 'btp',
members: [],
},
});
const { t } = useTranslation();
const { user } = useAuthOnboarding();

const username = user?.email;

const clearForm = useCallback(() => {
resetField('name');
resetField('chargingTarget');
resetField('displayName');
resetField('chargingTargetType');
}, [resetField]);

useEffect(() => {
Expand All @@ -79,6 +80,7 @@ export function CreateProjectDialogContainer({
name,
chargingTarget,
displayName,
chargingTargetType,
members,
}: OnCreatePayload): Promise<boolean> => {
try {
Expand All @@ -87,6 +89,7 @@ export function CreateProjectDialogContainer({
displayName: displayName,
chargingTarget: chargingTarget,
members: members,
chargingTargetType: chargingTargetType,
}),
);
setIsOpen(false);
Expand Down Expand Up @@ -115,6 +118,7 @@ export function CreateProjectDialogContainer({
register={register}
errors={errors}
setValue={setValue}
type={'project'}
onCreate={handleSubmit(handleProjectCreate)}
/>
);
Expand Down
5 changes: 5 additions & 0 deletions src/components/Dialogs/CreateProjectWorkspaceDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type OnCreatePayload = {
name: string;
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members: Member[];
};

Expand All @@ -34,6 +35,7 @@ export interface CreateProjectWorkspaceDialogProps {
errors: FieldErrors<CreateDialogProps>;
setValue: UseFormSetValue<CreateDialogProps>;
projectName?: string;
type: 'workspace' | 'project';
}

export function CreateProjectWorkspaceDialog({
Expand All @@ -47,6 +49,7 @@ export function CreateProjectWorkspaceDialog({
errors,
setValue,
projectName,
type,
}: CreateProjectWorkspaceDialogProps) {
const { t } = useTranslation();
const [isKubectlDialogOpen, setIsKubectlDialogOpen] = useState(false);
Expand Down Expand Up @@ -87,6 +90,8 @@ export function CreateProjectWorkspaceDialog({
<MetadataForm
register={register}
errors={errors}
setValue={setValue}
requireChargingTarget={type === 'project'}
sideFormContent={
<FormGroup
headerText={t('CreateProjectWorkspaceDialog.membersHeader')}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Dialogs/CreateWorkspaceDialogContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type CreateDialogProps = {
name: string;
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members: Member[];
};

Expand All @@ -54,17 +55,18 @@ export function CreateWorkspaceDialogContainer({
displayName: '',
chargingTarget: '',
members: [],
chargingTargetType: '',
},
});
const { t } = useTranslation();
const { user } = useAuthOnboarding();

const username = user?.email;

const clearForm = useCallback(() => {
resetField('name');
resetField('chargingTarget');
resetField('displayName');
resetField('chargingTargetType');
}, [resetField]);

useEffect(() => {
Expand Down Expand Up @@ -127,6 +129,7 @@ export function CreateWorkspaceDialogContainer({
register={register}
errors={errors}
setValue={setValue}
type={'workspace'}
projectName={project}
onCreate={handleSubmit(handleWorkspaceCreate)}
/>
Expand Down
57 changes: 49 additions & 8 deletions src/components/Dialogs/MetadataForm.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,52 @@
import { FieldErrors, UseFormRegister } from 'react-hook-form';
import { FieldErrors, UseFormRegister, UseFormSetValue } from 'react-hook-form';
import { CreateDialogProps } from './CreateWorkspaceDialogContainer.tsx';
import { useTranslation } from 'react-i18next';
import { Form, FormGroup, Input, Label } from '@ui5/webcomponents-react';
import {
Form,
FormGroup,
Input,
Label,
Option,
Select,
SelectDomRef,
Ui5CustomEvent,
} from '@ui5/webcomponents-react';
import styles from './CreateProjectWorkspaceDialog.module.css';
import React from 'react';

export interface MetadataFormProps {
register: UseFormRegister<CreateDialogProps>;
errors: FieldErrors<CreateDialogProps>;

setValue: UseFormSetValue<CreateDialogProps>;
sideFormContent?: React.ReactNode;
requireChargingTarget?: boolean;
}

interface SelectOption {
label: string;
value: string;
}

export function MetadataForm({
register,
errors,

setValue,
sideFormContent,
requireChargingTarget = false,
}: MetadataFormProps) {
const { t } = useTranslation();

const handleChargingTargetTypeChange = (
event: Ui5CustomEvent<SelectDomRef, { selectedOption: HTMLElement }>,
) => {
const selectedOption = event.detail.selectedOption as HTMLElement;
setValue('chargingTargetType', selectedOption.dataset.value);
};
const chargingTypes: SelectOption[] = [
...(!requireChargingTarget
? [{ label: t('common.notSelected'), value: '' }]
: []),
{ label: t('common.btp'), value: 'btp' },
];
return (
<Form>
<FormGroup
Expand All @@ -36,7 +64,6 @@ export function MetadataForm({
valueStateMessage={<span>{errors.name?.message}</span>}
required
/>

<Label for={'displayName'}>
{t('CreateProjectWorkspaceDialog.displayNameLabel')}
</Label>
Expand All @@ -45,8 +72,21 @@ export function MetadataForm({
{...register('displayName')}
className={styles.input}
/>

<Label for={'chargingTarget'}>
<Label for={'chargingTargetType'} required={requireChargingTarget}>
{t('CreateProjectWorkspaceDialog.chargingTargetTypeLabel')}
</Label>
<Select
id={'chargingTargetType'}
className={styles.input}
onChange={handleChargingTargetTypeChange}
>
{chargingTypes.map((option) => (
<Option key={option.value} data-value={option.value}>
{option.label}
</Option>
))}
</Select>
<Label for={'chargingTarget'} required={requireChargingTarget}>
{t('CreateProjectWorkspaceDialog.chargingTargetLabel')}
</Label>
<Input
Expand All @@ -55,6 +95,7 @@ export function MetadataForm({
className={styles.input}
/>
</FormGroup>

{sideFormContent ? sideFormContent : null}
</Form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type CreateDialogProps = {
name: string;
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members: Member[];
};

Expand Down Expand Up @@ -96,6 +97,7 @@ export const CreateManagedControlPlaneWizardContainer: FC<
name: '',
displayName: '',
chargingTarget: '',
chargingTargetType: '',
members: [],
},
mode: 'onChange',
Expand All @@ -121,6 +123,7 @@ export const CreateManagedControlPlaneWizardContainer: FC<
const clearFormFields = useCallback(() => {
resetField('name');
resetField('chargingTarget');
resetField('chargingTargetType');
resetField('displayName');
}, [resetField]);

Expand Down Expand Up @@ -294,7 +297,11 @@ export const CreateManagedControlPlaneWizardContainer: FC<
selected={selectedStep === 'metadata'}
data-step="metadata"
>
<MetadataForm register={register} errors={errors} />
<MetadataForm
setValue={setValue}
register={register}
errors={errors}
/>
</WizardStep>
<WizardStep
icon={'user-edit'}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/api/types/crate/createManagedControlPlane.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Resource } from '../resource';
import {
CHARGING_TARGET_LABEL,
CHARGING_TARGET_TYPE_LABEL,
DISPLAY_NAME_ANNOTATION,
} from '../shared/keyNames';
import { Member } from '../shared/members';
Expand Down Expand Up @@ -59,6 +60,7 @@ export const CreateManagedControlPlane = (
optional?: {
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members?: Member[];
selectedComponents?: ComponentSelectionItem[];
},
Expand All @@ -82,6 +84,7 @@ export const CreateManagedControlPlane = (
[DISPLAY_NAME_ANNOTATION]: optional?.displayName ?? '',
},
labels: {
[CHARGING_TARGET_TYPE_LABEL]: optional?.chargingTargetType ?? '',
[CHARGING_TARGET_LABEL]: optional?.chargingTarget ?? '',
},
},
Expand Down
3 changes: 3 additions & 0 deletions src/lib/api/types/crate/createProject.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Resource } from '../resource';
import {
CHARGING_TARGET_LABEL,
CHARGING_TARGET_TYPE_LABEL,
DISPLAY_NAME_ANNOTATION,
} from '../shared/keyNames';
import { Member } from '../shared/members';
Expand Down Expand Up @@ -29,6 +30,7 @@ export const CreateProject = (
optional?: {
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members?: Member[];
},
): CreateProjectType => {
Expand All @@ -41,6 +43,7 @@ export const CreateProject = (
[DISPLAY_NAME_ANNOTATION]: optional?.displayName ?? '',
},
labels: {
[CHARGING_TARGET_TYPE_LABEL]: optional?.chargingTargetType ?? '',
[CHARGING_TARGET_LABEL]: optional?.chargingTarget ?? '',
},
},
Expand Down
3 changes: 3 additions & 0 deletions src/lib/api/types/crate/createWorkspace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Resource } from '../resource';
import {
CHARGING_TARGET_LABEL,
CHARGING_TARGET_TYPE_LABEL,
DISPLAY_NAME_ANNOTATION,
} from '../shared/keyNames';
import { Member } from '../shared/members';
Expand Down Expand Up @@ -31,6 +32,7 @@ export const CreateWorkspace = (
optional?: {
displayName?: string;
chargingTarget?: string;
chargingTargetType?: string;
members?: Member[];
},
): CreateWorkspaceType => {
Expand All @@ -44,6 +46,7 @@ export const CreateWorkspace = (
[DISPLAY_NAME_ANNOTATION]: optional?.displayName ?? '',
},
labels: {
[CHARGING_TARGET_TYPE_LABEL]: optional?.chargingTargetType ?? '',
[CHARGING_TARGET_LABEL]: optional?.chargingTarget ?? '',
},
},
Expand Down
2 changes: 2 additions & 0 deletions src/lib/api/types/shared/keyNames.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const DISPLAY_NAME_ANNOTATION: string = 'openmcp.cloud/display-name';
export const CHARGING_TARGET_LABEL: string =
'openmcp.cloud.sap/charging-target';
export const CHARGING_TARGET_TYPE_LABEL: string =
'openmcp.cloud.sap/charging-target-type';
3 changes: 3 additions & 0 deletions src/lib/api/validations/regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export const projectWorkspaceNameRegex =
// Matches managed control plane names: 1-63 chars per segment, lowercase alphanum/dash, dot-separated, no leading/trailing dash.
export const managedControlPlaneNameRegex =
/^(?!-)[a-z0-9-]{1,63}(?<!-)(?:\.(?!-)[a-z0-9-]{1,63}(?<!-))*$/;

export const btpChargingTargetRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
2 changes: 2 additions & 0 deletions src/lib/api/validations/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const validationSchemaProjectWorkspace = z.object({
.max(25, t('validationErrors.max25chars')),
displayName: z.string().optional(),
chargingTarget: z.string().optional(),
chargingTargetType: z.string().optional(),
members: z.array(member).refine((members) => members?.length > 0),
});
export const validationSchemaCreateManagedControlPlane = z.object({
Expand All @@ -31,5 +32,6 @@ export const validationSchemaCreateManagedControlPlane = z.object({
.max(25, t('validationErrors.max25chars')),
displayName: z.string().optional(),
chargingTarget: z.string().optional(),
chargingTargetType: z.string().optional(),
members: z.array(member),
});
Loading