From f73e637c666deb22ba27e02a5658ff7f379cd322 Mon Sep 17 00:00:00 2001 From: Killian Colla Date: Fri, 25 Sep 2026 14:47:52 +0200 Subject: [PATCH 1/5] feat(build-settings): move the build settings into a dedicated page --- apps/console/src/routeTree.gen.ts | 26 ++ .../$serviceId/settings/build-settings.tsx | 8 + .../service-settings/feature/src/index.ts | 1 + .../service-build-settings.spec.tsx | 203 +++++++++++++ .../service-build-settings.tsx | 275 ++++++++++++++++++ .../service-settings-layout.tsx | 4 + .../service-advanced-settings.spec.tsx | 26 ++ .../service-advanced-settings.tsx | 4 +- 8 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings.tsx create mode 100644 libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.spec.tsx create mode 100644 libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx diff --git a/apps/console/src/routeTree.gen.ts b/apps/console/src/routeTree.gen.ts index 4327c6663da..ea67da7cbf4 100644 --- a/apps/console/src/routeTree.gen.ts +++ b/apps/console/src/routeTree.gen.ts @@ -193,6 +193,7 @@ import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnviron import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration' @@ -1725,6 +1726,15 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, } as any, ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRouteImport.update( + { + id: '/build-settings', + path: '/build-settings', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRouteImport.update( { @@ -2013,6 +2023,7 @@ export interface FileRoutesByFullPath { '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute @@ -2194,6 +2205,7 @@ export interface FileRoutesByTo { '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute @@ -2398,6 +2410,7 @@ export interface FileRoutesById { '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute @@ -2603,6 +2616,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' @@ -2784,6 +2798,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections' | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' @@ -2987,6 +3002,7 @@ export interface FileRouteTypes { | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/ai-configuration' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/automations' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/connections' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' @@ -4360,6 +4376,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRouteImport parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' + path: '/build-settings' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration': { id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/blueprint-configuration' path: '/blueprint-configuration' @@ -4821,6 +4844,7 @@ interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvi AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAiConfigurationRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute @@ -4852,6 +4876,8 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAutomationsRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBlueprintConfigurationRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsBuildSettingsRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConnectionsRoute: diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings.tsx new file mode 100644 index 00000000000..f8074dd13c8 --- /dev/null +++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings.tsx @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ServiceBuildSettings } from '@qovery/domains/service-settings/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/build-settings' +)({ + component: ServiceBuildSettings, +}) diff --git a/libs/domains/service-settings/feature/src/index.ts b/libs/domains/service-settings/feature/src/index.ts index 80f1cbff7a5..0ce58513827 100644 --- a/libs/domains/service-settings/feature/src/index.ts +++ b/libs/domains/service-settings/feature/src/index.ts @@ -17,3 +17,4 @@ export * from './lib/terraform-variables-settings/terraform-variables-settings' export * from './lib/job-dockerfile-settings/job-dockerfile-settings' export * from './lib/database-settings-resources/database-settings-resources' export * from './lib/agentic-workflow-settings/agentic-workflow-settings' +export * from './lib/service-build-settings/service-build-settings' diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.spec.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.spec.tsx new file mode 100644 index 00000000000..43079fea695 --- /dev/null +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.spec.tsx @@ -0,0 +1,203 @@ +import { type Application, type Job, type Terraform } from '@qovery/domains/services/data-access' +import { applicationFactoryMock, cronjobFactoryMock, terraformFactoryMock } from '@qovery/shared/factories' +import { renderWithProviders, screen } from '@qovery/shared/util-tests' +import { ServiceBuildSettings } from './service-build-settings' + +const mockEditService = jest.fn() +const mockService = { data: undefined as Application | Job | Terraform | undefined } + +const mockAdvancedSettings = { + data: undefined as Record | undefined, +} + +const mockDefaultAdvancedSettings = { + data: undefined as Record | undefined, +} + +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useParams: () => ({ organizationId: 'org-1', projectId: 'proj-1', environmentId: 'env-1', serviceId: 'svc-1' }), +})) + +jest.mock('@qovery/shared/util-hooks', () => ({ + ...jest.requireActual('@qovery/shared/util-hooks'), + useSupportChat: () => ({ showChat: jest.fn() }), +})) + +jest.mock('@qovery/domains/services/feature', () => ({ + useService: () => mockService, + useAdvancedSettings: () => mockAdvancedSettings, + useDefaultAdvancedSettings: () => mockDefaultAdvancedSettings, + useEditService: () => ({ mutate: mockEditService, isLoading: false }), +})) + +const applicationAdvancedSettings: Record = { + 'build.timeout_max_sec': 1800, + 'build.cpu_max_in_milli': 4000, + 'build.ram_max_in_gib': 8, + 'build.ephemeral_storage_in_gib': 10, + 'build.disable_buildkit_cache': false, + 'build.skip_git_submodules': false, + 'network.ingress.enable_cors': true, +} + +const applicationDefaultAdvancedSettings: Record = { + 'build.timeout_max_sec': 1800, + 'build.cpu_max_in_milli': 4000, + 'build.ram_max_in_gib': 8, + 'build.ephemeral_storage_in_gib': 10, + 'build.disable_buildkit_cache': false, + 'build.skip_git_submodules': false, + 'network.ingress.enable_cors': true, +} + +const terraformAdvancedSettings: Record = { + 'build.timeout_max_sec': 1800, + 'build.cpu_max_in_milli': 4000, + 'build.ram_max_in_gib': 8, + 'build.ephemeral_storage_in_gib': 10, + 'build.skip_git_submodules': false, + 'network.ingress.enable_cors': true, +} + +const mockApplication = { ...applicationFactoryMock(1)[0], build_settings_editable: true } as Application +const mockJob = { ...cronjobFactoryMock(1)[0], build_settings_editable: true } as Job +const mockTerraform = { ...terraformFactoryMock(1)[0], build_settings_editable: true } as Terraform +const mockApplicationDisabled = { ...applicationFactoryMock(1)[0], build_settings_editable: false } as Application + +function setup(service: Application | Job | Terraform, advancedSettings?: Record) { + mockService.data = service + mockAdvancedSettings.data = advancedSettings ?? applicationAdvancedSettings + mockDefaultAdvancedSettings.data = advancedSettings ?? applicationDefaultAdvancedSettings + mockEditService.mockClear() + return renderWithProviders() +} + +describe('ServiceBuildSettings', () => { + it('should render number inputs for timeout, CPU, RAM, and ephemeral storage', () => { + setup(mockApplication) + expect(screen.getByLabelText(/timeout/i)).toBeInTheDocument() + expect(screen.getByLabelText(/cpu/i)).toBeInTheDocument() + expect(screen.getByLabelText(/ram/i)).toBeInTheDocument() + expect(screen.getByLabelText(/ephemeral storage/i)).toBeInTheDocument() + }) + + it('should render toggle switches for BuildKit cache and Git submodules for Application', () => { + setup(mockApplication) + expect(screen.getByRole('switch', { name: /buildkit cache/i })).toBeInTheDocument() + expect(screen.getByRole('switch', { name: /git submodules/i })).toBeInTheDocument() + }) + + it('should render toggle switches for BuildKit cache and Git submodules for Job', () => { + setup(mockJob) + expect(screen.getByRole('switch', { name: /buildkit cache/i })).toBeInTheDocument() + expect(screen.getByRole('switch', { name: /git submodules/i })).toBeInTheDocument() + }) + + it('should NOT render BuildKit cache toggle for Terraform', () => { + setup(mockTerraform, terraformAdvancedSettings) + expect(screen.queryByRole('switch', { name: /buildkit cache/i })).not.toBeInTheDocument() + expect(screen.getByRole('switch', { name: /git submodules/i })).toBeInTheDocument() + }) + + it('should pre-populate form fields with values from advanced settings', () => { + setup(mockApplication) + expect(screen.getByLabelText(/timeout/i)).toHaveValue(1800) + expect(screen.getByLabelText(/cpu/i)).toHaveValue(4000) + expect(screen.getByLabelText(/ram/i)).toHaveValue(8) + expect(screen.getByLabelText(/ephemeral storage/i)).toHaveValue(10) + }) + + it('should call editService with build_settings in the payload on submit', async () => { + const { userEvent } = setup(mockApplication) + + const timeoutInput = screen.getByLabelText(/timeout/i) + await userEvent.clear(timeoutInput) + await userEvent.type(timeoutInput, '3600') + + await userEvent.click(screen.getByRole('button', { name: 'Save' })) + + expect(mockEditService).toHaveBeenCalledWith( + expect.objectContaining({ + serviceId: 'svc-1', + payload: expect.objectContaining({ + build_settings: expect.objectContaining({ + timeout_max_sec: 3600, + cpu_max_in_milli: 4000, + ram_max_in_gib: 8, + ephemeral_storage_in_gib: 10, + disable_buildkit_cache: false, + skip_git_submodules: false, + }), + }), + }), + expect.anything() + ) + }) + + it('should not include disable_buildkit_cache in build_settings for Terraform', async () => { + const { userEvent } = setup(mockTerraform, terraformAdvancedSettings) + + const timeoutInput = screen.getByLabelText(/timeout/i) + await userEvent.clear(timeoutInput) + await userEvent.type(timeoutInput, '3600') + + await userEvent.click(screen.getByRole('button', { name: 'Save' })) + + expect(mockEditService).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.objectContaining({ + build_settings: expect.not.objectContaining({ + disable_buildkit_cache: expect.anything(), + }), + }), + }), + expect.anything() + ) + }) + + it('should send all build_settings fields even when only one is modified', async () => { + const { userEvent } = setup(mockApplication) + + const cpuInput = screen.getByLabelText(/cpu/i) + await userEvent.clear(cpuInput) + await userEvent.type(cpuInput, '8000') + + await userEvent.click(screen.getByRole('button', { name: 'Save' })) + + const payload = mockEditService.mock.calls[0][0].payload + expect(payload.build_settings).toEqual( + expect.objectContaining({ + timeout_max_sec: 1800, + cpu_max_in_milli: 8000, + ram_max_in_gib: 8, + ephemeral_storage_in_gib: 10, + disable_buildkit_cache: false, + skip_git_submodules: false, + }) + ) + }) + + it('should show Save button when build_settings_editable is true', () => { + setup(mockApplication) + expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument() + }) + + it('should disable all inputs when build_settings_editable is false', () => { + setup(mockApplicationDisabled) + expect(screen.getByLabelText(/timeout/i)).toBeDisabled() + expect(screen.getByLabelText(/cpu/i)).toBeDisabled() + expect(screen.getByLabelText(/ram/i)).toBeDisabled() + expect(screen.getByLabelText(/ephemeral storage/i)).toBeDisabled() + }) + + it('should show a callout message when build_settings_editable is false', () => { + setup(mockApplicationDisabled) + expect(screen.getByText(/not available for your organization/i)).toBeInTheDocument() + }) + + it('should not show Save button when build_settings_editable is false', () => { + setup(mockApplicationDisabled) + expect(screen.queryByRole('button', { name: 'Save' })).not.toBeInTheDocument() + }) +}) diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx new file mode 100644 index 00000000000..5105040e520 --- /dev/null +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx @@ -0,0 +1,275 @@ +import { useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form' +import { type Application, type Job, type Terraform } from '@qovery/domains/services/data-access' +import { + useAdvancedSettings, + useDefaultAdvancedSettings, + useEditService, + useService, +} from '@qovery/domains/services/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Button, Callout, Icon, InputText, InputToggle, LoaderSpinner, Section } from '@qovery/shared/ui' +import { useDocumentTitle, useSupportChat } from '@qovery/shared/util-hooks' +import { buildEditServicePayload } from '@qovery/shared/util-services' + +type BuildSettingsService = Application | Job | Terraform + +interface BuildSettingsFormData { + timeout_max_sec: number | '' + cpu_max_in_milli: number | '' + ram_max_in_gib: number | '' + ephemeral_storage_in_gib: number | '' + disable_buildkit_cache: boolean + skip_git_submodules: boolean +} + +function getDefaultValues( + advancedSettings: Record | undefined, + defaultAdvancedSettings: Record | undefined +): BuildSettingsFormData { + const get = (key: string) => advancedSettings?.[key] ?? defaultAdvancedSettings?.[key] + return { + timeout_max_sec: (get('build.timeout_max_sec') as number) ?? '', + cpu_max_in_milli: (get('build.cpu_max_in_milli') as number) ?? '', + ram_max_in_gib: (get('build.ram_max_in_gib') as number) ?? '', + ephemeral_storage_in_gib: (get('build.ephemeral_storage_in_gib') as number) ?? '', + disable_buildkit_cache: (get('build.disable_buildkit_cache') as boolean) ?? false, + skip_git_submodules: (get('build.skip_git_submodules') as boolean) ?? false, + } +} + +function toNumberOrNull(value: number | ''): number | null { + if (value === '') return null + return Number(value) +} + +interface BuildSettingsFormProps { + service: BuildSettingsService + onSubmit: () => void + loading: boolean + disabled: boolean + defaultAdvancedSettings: Record | undefined +} + +function BuildSettingsForm({ service, onSubmit, loading, disabled, defaultAdvancedSettings }: BuildSettingsFormProps) { + const { formState, control } = useFormContext() + const { showChat } = useSupportChat() + + const isTerraform = service.serviceType === 'TERRAFORM' + + return ( +
+
+ +
+ {disabled && ( + + + + + + Build settings customization is not available for your organization. + + + + )} +
+
+ ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + {!isTerraform && ( + ( + + )} + /> + )} + ( + + )} + /> +
+ {!disabled && ( +
+ +
+ )} +
+
+
+
+ ) +} + +function ServiceBuildSettingsContent() { + useDocumentTitle('Build settings - Service settings') + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + const { data: service } = useService({ environmentId, serviceId, suspense: true }) + + const { data: advancedSettings } = useAdvancedSettings({ + serviceId, + serviceType: service?.serviceType as BuildSettingsService['serviceType'], + suspense: true, + }) + + const { data: defaultAdvancedSettings } = useDefaultAdvancedSettings({ + serviceType: service?.serviceType as BuildSettingsService['serviceType'], + suspense: true, + }) + + const { mutate: editService, isLoading } = useEditService({ + organizationId, + projectId, + environmentId, + }) + + const methods = useForm({ + mode: 'onChange', + defaultValues: getDefaultValues( + advancedSettings as Record | undefined, + defaultAdvancedSettings as Record | undefined + ), + }) + + if (!service) return null + + const buildService = service as BuildSettingsService + const isTerraform = buildService.serviceType === 'TERRAFORM' + // TODO: remove cast once SDK is regenerated with build_settings_editable field + const buildSettingsEditable = (buildService as BuildSettingsService & { build_settings_editable?: boolean }) + .build_settings_editable ?? false + + const onSubmit = methods.handleSubmit((data) => { + const buildSettings: Record = { + timeout_max_sec: Number(data.timeout_max_sec), + cpu_max_in_milli: Number(data.cpu_max_in_milli), + ram_max_in_gib: Number(data.ram_max_in_gib), + ephemeral_storage_in_gib: toNumberOrNull(data.ephemeral_storage_in_gib), + skip_git_submodules: data.skip_git_submodules, + } + + if (!isTerraform) { + buildSettings.disable_buildkit_cache = data.disable_buildkit_cache + } + + const payload = buildEditServicePayload({ + service: buildService, + request: { build_settings: buildSettings }, + } as Parameters[0]) + + editService( + { serviceId, payload }, + { onSuccess: () => methods.reset(data) } + ) + }) + + return ( + + | undefined} + /> + + ) +} + +const BuildSettingsFallback = () => ( +
+ +
+) + +export function ServiceBuildSettings() { + return ( + }> + + + ) +} diff --git a/libs/domains/service-settings/feature/src/lib/service-settings-layout/service-settings-layout.tsx b/libs/domains/service-settings/feature/src/lib/service-settings-layout/service-settings-layout.tsx index 552f3c602ac..43df4232005 100644 --- a/libs/domains/service-settings/feature/src/lib/service-settings-layout/service-settings-layout.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-settings-layout/service-settings-layout.tsx @@ -98,6 +98,7 @@ export function ServiceSettingsLayout({ children }: ServiceSettingsLayoutProps) toSettingsPath(pathSettings, '/deployment-restrictions'), 'cart-flatbed' ) + const buildSettingsLink = linkItem('Build settings', toSettingsPath(pathSettings, '/build-settings'), 'wrench') const advancedSettingsLink = linkItem( 'Advanced settings', toSettingsPath(pathSettings, '/advanced-settings'), @@ -150,6 +151,7 @@ export function ServiceSettingsLayout({ children }: ServiceSettingsLayoutProps) portLink, healthchecksLink, deploymentRestrictionsLink, + buildSettingsLink, advancedSettingsLink, dangerZoneLink, ]) @@ -185,6 +187,7 @@ export function ServiceSettingsLayout({ children }: ServiceSettingsLayoutProps) terraformArgumentsLink, resourcesLink, deploymentRestrictionsLink, + buildSettingsLink, advancedSettingsLink, dangerZoneLink, ] @@ -195,6 +198,7 @@ export function ServiceSettingsLayout({ children }: ServiceSettingsLayoutProps) configureJobLink, resourcesLink, deploymentRestrictionsLink, + buildSettingsLink, advancedSettingsLink, dangerZoneLink, ]) diff --git a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.spec.tsx b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.spec.tsx index 9d77e840db1..8e3651322fc 100644 --- a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.spec.tsx @@ -154,6 +154,32 @@ describe('AdvancedSettings', () => { ) }) + it('should not display build.* keys in the table', () => { + const advancedSettingsWithBuild = { + ...advancedSettings, + 'build.timeout_max_sec': 1800, + 'build.cpu_max_in_milli': 4000, + 'build.ram_max_in_gib': 8, + } + const defaultAdvancedSettingsWithBuild = { + ...defaultAdvancedSettings, + 'build.timeout_max_sec': 1800, + 'build.cpu_max_in_milli': 4000, + 'build.ram_max_in_gib': 8, + } + renderWithProviders( + + ) + expect(screen.queryByText('build.timeout_max_sec')).not.toBeInTheDocument() + expect(screen.queryByText('build.cpu_max_in_milli')).not.toBeInTheDocument() + expect(screen.queryByText('build.ram_max_in_gib')).not.toBeInTheDocument() + expect(screen.getByText('deployment.custom_domain_check_enabled')).toBeInTheDocument() + }) + it('should hide the sticky toaster after a successful submit', async () => { mockMutateEdit.mockImplementation((_variables, options) => { options?.onSuccess?.() diff --git a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx index 8973325da19..3f74dd7b4c5 100644 --- a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx +++ b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx @@ -114,7 +114,9 @@ export function AdvancedSettings({ } } } - return entries.sort(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB)) + return entries + .filter((entry) => !entry.name.startsWith('build.')) + .sort(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB)) }, [advancedSettings, defaultAdvancedSettings]) const onSubmit = handleSubmit((data: Record) => { From 178d36bd6c3929694e5f217a8580153fc859b83c Mon Sep 17 00:00:00 2001 From: Killian Colla Date: Fri, 25 Sep 2026 14:54:22 +0200 Subject: [PATCH 2/5] callout change --- .../src/lib/service-build-settings/service-build-settings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx index 5105040e520..497854dad97 100644 --- a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx @@ -71,9 +71,9 @@ function BuildSettingsForm({ service, onSubmit, loading, disabled, defaultAdvanc - + Build settings customization is not available for your organization. - From 60b370d271a3dcfbb96412a0272442382d783876 Mon Sep 17 00:00:00 2001 From: Killian Colla Date: Fri, 25 Sep 2026 15:17:01 +0200 Subject: [PATCH 3/5] fix tests and cubic comments --- .../service-build-settings.tsx | 88 ++++++++++++++----- .../service-advanced-settings.spec.tsx | 24 ++++- .../service-advanced-settings.tsx | 11 ++- 3 files changed, 96 insertions(+), 27 deletions(-) diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx index 497854dad97..43975ee06e4 100644 --- a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx @@ -1,5 +1,5 @@ import { useParams } from '@tanstack/react-router' -import { Suspense } from 'react' +import { Suspense, useEffect } from 'react' import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form' import { type Application, type Job, type Terraform } from '@qovery/domains/services/data-access' import { @@ -15,6 +15,12 @@ import { buildEditServicePayload } from '@qovery/shared/util-services' type BuildSettingsService = Application | Job | Terraform +const BUILD_SETTINGS_SERVICE_TYPES = ['APPLICATION', 'JOB', 'TERRAFORM'] as const + +function isBuildSettingsServiceType(serviceType: string): serviceType is BuildSettingsService['serviceType'] { + return (BUILD_SETTINGS_SERVICE_TYPES as readonly string[]).includes(serviceType) +} + interface BuildSettingsFormData { timeout_max_sec: number | '' cpu_max_in_milli: number | '' @@ -61,10 +67,7 @@ function BuildSettingsForm({ service, onSubmit, loading, disabled, defaultAdvanc return (
- +
{disabled && ( @@ -73,7 +76,14 @@ function BuildSettingsForm({ service, onSubmit, loading, disabled, defaultAdvanc Build settings customization is not available for your organization. - @@ -190,14 +200,44 @@ function ServiceBuildSettingsContent() { const { data: service } = useService({ environmentId, serviceId, suspense: true }) + if (!service || !isBuildSettingsServiceType(service.serviceType)) { + return

Build settings are not available for this service type.

+ } + + return ( + + ) +} + +interface ServiceBuildSettingsFormProps { + service: BuildSettingsService + organizationId: string + projectId: string + environmentId: string + serviceId: string +} + +function ServiceBuildSettingsForm({ + service, + organizationId, + projectId, + environmentId, + serviceId, +}: ServiceBuildSettingsFormProps) { const { data: advancedSettings } = useAdvancedSettings({ serviceId, - serviceType: service?.serviceType as BuildSettingsService['serviceType'], + serviceType: service.serviceType, suspense: true, }) const { data: defaultAdvancedSettings } = useDefaultAdvancedSettings({ - serviceType: service?.serviceType as BuildSettingsService['serviceType'], + serviceType: service.serviceType, suspense: true, }) @@ -215,19 +255,26 @@ function ServiceBuildSettingsContent() { ), }) - if (!service) return null + // Reset form when navigating between services + useEffect(() => { + methods.reset( + getDefaultValues( + advancedSettings as Record | undefined, + defaultAdvancedSettings as Record | undefined + ) + ) + }, [serviceId, advancedSettings, defaultAdvancedSettings, methods]) - const buildService = service as BuildSettingsService - const isTerraform = buildService.serviceType === 'TERRAFORM' + const isTerraform = service.serviceType === 'TERRAFORM' // TODO: remove cast once SDK is regenerated with build_settings_editable field - const buildSettingsEditable = (buildService as BuildSettingsService & { build_settings_editable?: boolean }) - .build_settings_editable ?? false + const buildSettingsEditable = + (service as BuildSettingsService & { build_settings_editable?: boolean }).build_settings_editable ?? false const onSubmit = methods.handleSubmit((data) => { const buildSettings: Record = { - timeout_max_sec: Number(data.timeout_max_sec), - cpu_max_in_milli: Number(data.cpu_max_in_milli), - ram_max_in_gib: Number(data.ram_max_in_gib), + timeout_max_sec: toNumberOrNull(data.timeout_max_sec), + cpu_max_in_milli: toNumberOrNull(data.cpu_max_in_milli), + ram_max_in_gib: toNumberOrNull(data.ram_max_in_gib), ephemeral_storage_in_gib: toNumberOrNull(data.ephemeral_storage_in_gib), skip_git_submodules: data.skip_git_submodules, } @@ -237,20 +284,17 @@ function ServiceBuildSettingsContent() { } const payload = buildEditServicePayload({ - service: buildService, + service, request: { build_settings: buildSettings }, } as Parameters[0]) - editService( - { serviceId, payload }, - { onSuccess: () => methods.reset(data) } - ) + editService({ serviceId, payload }, { onSuccess: () => methods.reset(data) }) }) return ( { expect(screen.getByText('deployment.custom_domain_check_enabled')).toBeInTheDocument() }) + it('should still display build.* keys for Container services', () => { + const advancedSettingsWithBuild = { + ...advancedSettings, + 'build.timeout_max_sec': 1800, + } + const defaultAdvancedSettingsWithBuild = { + ...defaultAdvancedSettings, + 'build.timeout_max_sec': 1800, + } + renderWithProviders( + + ) + expect(screen.getByText('build.timeout_max_sec')).toBeInTheDocument() + }) + it('should hide the sticky toaster after a successful submit', async () => { mockMutateEdit.mockImplementation((_variables, options) => { options?.onSuccess?.() diff --git a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx index 3f74dd7b4c5..768226b6b58 100644 --- a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx +++ b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx @@ -84,13 +84,18 @@ export function AdvancedSettings({ const advancedSettings = advancedSettingsProp ?? advancedSettingsFetched const defaultAdvancedSettings = defaultAdvancedSettingsProp ?? defaultAdvancedSettingsFetched + const hasBuildSettingsPage = ['APPLICATION', 'JOB', 'TERRAFORM'].includes(serviceType) + const shouldHideKey = (key: string) => hasBuildSettingsPage && key.startsWith('build.') + const [overriddenOnly, setOverriddenOnly] = useState(false) const { control, handleSubmit, formState, reset } = useForm>({ mode: 'onChange', defaultValues: { ...(advancedSettings ? Object.entries(advancedSettings).reduce>((acc, [key, value]) => { - acc[key] = formatValue(value) + if (!shouldHideKey(key)) { + acc[key] = formatValue(value) + } return acc }, {}) : {}), @@ -115,14 +120,14 @@ export function AdvancedSettings({ } } return entries - .filter((entry) => !entry.name.startsWith('build.')) + .filter((entry) => !shouldHideKey(entry.name)) .sort(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB)) }, [advancedSettings, defaultAdvancedSettings]) const onSubmit = handleSubmit((data: Record) => { let dataFormatted = { ...data } Object.keys(dataFormatted).forEach((key) => { - if (key.includes('.')) delete dataFormatted[key] + if (key.includes('.') || shouldHideKey(key)) delete dataFormatted[key] }) dataFormatted = objectFlattener(dataFormatted) Object.keys(dataFormatted).forEach((key) => { From 8abbf0a32671059277425e8029f1a84815d3c791 Mon Sep 17 00:00:00 2001 From: Killian Colla Date: Fri, 25 Sep 2026 15:38:12 +0200 Subject: [PATCH 4/5] extract shared constant and fix useMemo deps --- .../service-build-settings/service-build-settings.tsx | 11 ++++++----- .../src/lib/domains-services-data-access.ts | 2 ++ .../service-advanced-settings.tsx | 5 +++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx index 43975ee06e4..9588e113cbc 100644 --- a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx @@ -1,7 +1,12 @@ import { useParams } from '@tanstack/react-router' import { Suspense, useEffect } from 'react' import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form' -import { type Application, type Job, type Terraform } from '@qovery/domains/services/data-access' +import { + type Application, + BUILD_SETTINGS_SERVICE_TYPES, + type Job, + type Terraform, +} from '@qovery/domains/services/data-access' import { useAdvancedSettings, useDefaultAdvancedSettings, @@ -15,8 +20,6 @@ import { buildEditServicePayload } from '@qovery/shared/util-services' type BuildSettingsService = Application | Job | Terraform -const BUILD_SETTINGS_SERVICE_TYPES = ['APPLICATION', 'JOB', 'TERRAFORM'] as const - function isBuildSettingsServiceType(serviceType: string): serviceType is BuildSettingsService['serviceType'] { return (BUILD_SETTINGS_SERVICE_TYPES as readonly string[]).includes(serviceType) } @@ -255,7 +258,6 @@ function ServiceBuildSettingsForm({ ), }) - // Reset form when navigating between services useEffect(() => { methods.reset( getDefaultValues( @@ -266,7 +268,6 @@ function ServiceBuildSettingsForm({ }, [serviceId, advancedSettings, defaultAdvancedSettings, methods]) const isTerraform = service.serviceType === 'TERRAFORM' - // TODO: remove cast once SDK is regenerated with build_settings_editable field const buildSettingsEditable = (service as BuildSettingsService & { build_settings_editable?: boolean }).build_settings_editable ?? false diff --git a/libs/domains/services/data-access/src/lib/domains-services-data-access.ts b/libs/domains/services/data-access/src/lib/domains-services-data-access.ts index 0486c7843c8..f360081241d 100644 --- a/libs/domains/services/data-access/src/lib/domains-services-data-access.ts +++ b/libs/domains/services/data-access/src/lib/domains-services-data-access.ts @@ -168,6 +168,8 @@ export type TerraformType = Extract export type ArgoCdType = Extract export type AgenticWorkflowType = Extract +export const BUILD_SETTINGS_SERVICE_TYPES: readonly ServiceType[] = ['APPLICATION', 'JOB', 'TERRAFORM'] as const + // XXX: Need to remove `serviceType` and use only `service_type` since the the API now supports it. // Waiting to have this implementation available in the edition interfaces. export type Application = _Application & { diff --git a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx index 768226b6b58..ba76ba8b474 100644 --- a/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx +++ b/libs/domains/services/feature/src/lib/service-advanced-settings/service-advanced-settings.tsx @@ -11,6 +11,7 @@ import { import { useMemo, useState } from 'react' import { Controller, useForm } from 'react-hook-form' import { + BUILD_SETTINGS_SERVICE_TYPES, type Database, type EditableService, type AdvancedSettings as _AdvancedSettings, @@ -84,7 +85,7 @@ export function AdvancedSettings({ const advancedSettings = advancedSettingsProp ?? advancedSettingsFetched const defaultAdvancedSettings = defaultAdvancedSettingsProp ?? defaultAdvancedSettingsFetched - const hasBuildSettingsPage = ['APPLICATION', 'JOB', 'TERRAFORM'].includes(serviceType) + const hasBuildSettingsPage = BUILD_SETTINGS_SERVICE_TYPES.includes(serviceType) const shouldHideKey = (key: string) => hasBuildSettingsPage && key.startsWith('build.') const [overriddenOnly, setOverriddenOnly] = useState(false) @@ -122,7 +123,7 @@ export function AdvancedSettings({ return entries .filter((entry) => !shouldHideKey(entry.name)) .sort(({ name: nameA }, { name: nameB }) => nameA.localeCompare(nameB)) - }, [advancedSettings, defaultAdvancedSettings]) + }, [advancedSettings, defaultAdvancedSettings, serviceType]) const onSubmit = handleSubmit((data: Record) => { let dataFormatted = { ...data } From e8b3b6b82460b3767ca4f7e095249b46f7deee8d Mon Sep 17 00:00:00 2001 From: Killian Colla Date: Fri, 25 Sep 2026 16:26:30 +0200 Subject: [PATCH 5/5] added small props on the toggles --- .../src/lib/service-build-settings/service-build-settings.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx index 9588e113cbc..c42c91c3abb 100644 --- a/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-build-settings/service-build-settings.tsx @@ -160,6 +160,7 @@ function BuildSettingsForm({ service, onSubmit, loading, disabled, defaultAdvanc control={control} render={({ field }) => ( (