diff --git a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-array-item-modal.spec.tsx b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-array-item-modal.spec.tsx
new file mode 100644
index 00000000000..3b2cca80fb2
--- /dev/null
+++ b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-array-item-modal.spec.tsx
@@ -0,0 +1,37 @@
+import { renderWithProviders, screen, waitFor } from '@qovery/shared/util-tests'
+import { type PlatformFieldDescriptor } from '../platform-configuration/platform-configuration-utils'
+import { ProfileArrayItemModal } from './profile-array-item-modal'
+
+const cidrField = {
+ key: 'cidr',
+ label: 'CIDR',
+ type: 'string',
+ required: false,
+ sensitive: false,
+ constraints: {},
+} as PlatformFieldDescriptor
+
+describe('ProfileArrayItemModal', () => {
+ it.each([
+ ['optional fields', cidrField, {}],
+ ['prefilled required fields', { ...cidrField, required: true, defaultValue: '10.0.0.0/8' }, { cidr: '10.0.0.0/8' }],
+ ])('lets an item made of %s be added without editing it', async (_, field, submittedValues) => {
+ const onSubmit = jest.fn()
+ const { userEvent } = renderWithProviders(
+
+ )
+
+ const addButton = screen.getByRole('button', { name: 'Add' })
+ await waitFor(() => expect(addButton).toBeEnabled())
+ await userEvent.click(addButton)
+
+ expect(onSubmit).toHaveBeenCalledWith(submittedValues)
+ })
+})
diff --git a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-changes-bar.tsx b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-changes-bar.tsx
index e0f4b53b8a3..985e3645645 100644
--- a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-changes-bar.tsx
+++ b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-changes-bar.tsx
@@ -58,7 +58,7 @@ export function ProfileChangesBar({
size="md"
className="gap-1.5"
loading={isSaving}
- disabled={isDeploying || !canSubmit}
+ disabled={isPending || !canSubmit}
onClick={onSave}
>
@@ -71,7 +71,7 @@ export function ProfileChangesBar({
size="md"
className="gap-1.5"
loading={isDeploying}
- disabled={isSaving || !canSubmit}
+ disabled={isPending || !canSubmit}
onClick={onSaveAndDeploy}
>
diff --git a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.spec.tsx b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.spec.tsx
new file mode 100644
index 00000000000..2340f196c7b
--- /dev/null
+++ b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.spec.tsx
@@ -0,0 +1,68 @@
+import { type FieldSchemaResponse } from 'qovery-typescript-axios'
+import { renderWithProviders, screen } from '@qovery/shared/util-tests'
+import { ProfileConfigurationField } from './profile-configuration-field'
+
+const scalarItemField = {
+ key: 'name',
+ label: 'Name',
+ type: 'string',
+ required: true,
+ sensitive: false,
+ constraints: {},
+}
+
+function createArrayField(itemFields: unknown[]) {
+ return {
+ key: 'listeners',
+ label: 'Listeners',
+ type: 'array',
+ required: false,
+ sensitive: false,
+ constraints: {},
+ items: { type: 'object', fields: itemFields },
+ itemFields: [],
+ } as unknown as FieldSchemaResponse
+}
+
+describe('ProfileConfigurationField', () => {
+ it('lets object items with scalar fields be added', () => {
+ renderWithProviders(
+
+ )
+
+ expect(screen.getByRole('button', { name: 'Add item to Listeners' })).toBeEnabled()
+ })
+
+ it('does not add object items whose nested settings the modal cannot edit', () => {
+ renderWithProviders(
+
+ )
+
+ expect(screen.getByRole('button', { name: 'Add item to Listeners' })).toBeDisabled()
+ expect(screen.getByText('Items with nested settings cannot be added here yet.')).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: 'Edit https' })).toBeEnabled()
+ })
+})
diff --git a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.tsx b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.tsx
index 5d853943a46..14eaae7358d 100644
--- a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.tsx
+++ b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-configuration-field.tsx
@@ -158,7 +158,10 @@ function ProfileArrayField({
const isItemModalOpenRef = useRef(false)
const items: unknown[] = Array.isArray(value) ? value : []
const { minItems, maxItems } = field.constraints
- const canAdd = typeof maxItems !== 'number' || items.length < maxItems
+ const isUnderLimit = typeof maxItems !== 'number' || items.length < maxItems
+ const hasNestedItemFields =
+ field.items.type === 'object' && field.items.fields.some((itemField) => !isPlatformScalarField(itemField))
+ const canAdd = isUnderLimit && !hasNestedItemFields
const canRemove = typeof minItems !== 'number' || items.length > minItems
const error = getFieldViolation(violations, path)
@@ -239,7 +242,9 @@ function ProfileArrayField({
Add item
- {typeof maxItems === 'number' && !canAdd ? (
+ {hasNestedItemFields ? (
+
Items with nested settings cannot be added here yet.
+ ) : typeof maxItems === 'number' && !isUnderLimit ? (
Limit of {maxItems} reached.
) : null}
diff --git a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-operator-footer.spec.tsx b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-operator-footer.spec.tsx
index a95a193aae0..9981644752a 100644
--- a/libs/domains/clusters/feature/src/lib/cluster-profile/profile-operator-footer.spec.tsx
+++ b/libs/domains/clusters/feature/src/lib/cluster-profile/profile-operator-footer.spec.tsx
@@ -38,7 +38,7 @@ describe('ProfileOperatorFooter', () => {
expect(container).toBeEmptyDOMElement()
})
- it('refreshes the Operator status while displayed', () => {
+ it('configures polling for the Operator status', () => {
mockUseClusterOperatorStatus.mockReturnValue({ data: operatorStatus, isLoading: false })
renderWithProviders(