Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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