From 6fdc1c2934a483228aedc57a82c2060efc0492eb Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Wed, 15 Jul 2026 15:37:15 +0200 Subject: [PATCH 1/3] feat(blueprints): add incentives to update blueprints in the service settings --- .../terraform-general-settings.spec.tsx | 102 ++++++++++++++++++ .../terraform-general-settings.tsx | 43 +++++++- package.json | 2 +- yarn.lock | 10 +- 4 files changed, 149 insertions(+), 8 deletions(-) diff --git a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.spec.tsx b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.spec.tsx index 6ef648d0f13..6a261ead416 100644 --- a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.spec.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.spec.tsx @@ -4,6 +4,20 @@ import { organizationFactoryMock, terraformFactoryMock } from '@qovery/shared/fa import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { TerraformGeneralSettings } from './terraform-general-settings' +const mockNavigate = jest.fn() +const mockUseBlueprintUpdate = jest.fn() + +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useNavigate: () => mockNavigate, + useParams: () => ({ + organizationId: 'organization-id', + projectId: 'project-id', + environmentId: 'environment-id', + serviceId: 'service-id', + }), +})) + jest.mock('@qovery/domains/organizations/feature', () => ({ EditGitRepositorySettings: () => null, })) @@ -11,12 +25,17 @@ jest.mock('@qovery/domains/organizations/feature', () => ({ jest.mock('@qovery/domains/services/feature', () => ({ AutoDeploySection: () => null, GeneralSetting: () => null, + useBlueprintUpdate: (props: unknown) => mockUseBlueprintUpdate(props), })) describe('TerraformGeneralSettings', () => { const service = terraformFactoryMock(1)[0] const organization = organizationFactoryMock(1)[0] + beforeEach(() => { + jest.clearAllMocks() + }) + it('should render main sections', () => { renderWithProviders( wrapWithReactHookForm(, { @@ -31,4 +50,87 @@ describe('TerraformGeneralSettings', () => { expect(screen.getByText('Source')).toBeInTheDocument() expect(screen.getByText('Build and deploy')).toBeInTheDocument() }) + + it('shows the major version signal and blueprint update callout for blueprint services', async () => { + mockUseBlueprintUpdate.mockReturnValue({ + data: { + is_up_to_date: false, + new_major_versions: [{ service_version: '17', latest_tag: 'aws/postgres/17/1.0.0' }], + }, + }) + + const { userEvent } = renderWithProviders( + wrapWithReactHookForm( + , + { + defaultValues: { + name: service.name, + terraform_action: TerraformAutoDeployConfigTerraformActionEnum.DEFAULT, + }, + } + ) + ) + + expect(screen.getByRole('button', { name: 'New major version available' })).toBeInTheDocument() + expect(screen.getByText('New blueprint version available')).toBeInTheDocument() + + await userEvent.click(screen.getByRole('button', { name: 'Update' })) + + expect(mockNavigate).toHaveBeenCalledWith({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/update/blueprint', + params: { + organizationId: 'organization-id', + projectId: 'project-id', + environmentId: 'environment-id', + serviceId: 'service-id', + }, + }) + }) + + it('hides blueprint update signals when the blueprint is up to date', () => { + mockUseBlueprintUpdate.mockReturnValue({ + data: { + is_up_to_date: true, + new_major_versions: [], + }, + }) + + renderWithProviders( + wrapWithReactHookForm( + , + { + defaultValues: { + name: service.name, + terraform_action: TerraformAutoDeployConfigTerraformActionEnum.DEFAULT, + }, + } + ) + ) + + expect(screen.queryByText('New major version available')).not.toBeInTheDocument() + expect(screen.queryByText('New blueprint version available')).not.toBeInTheDocument() + }) + + it('still renders the blueprint update callout when an older API response omits major versions', () => { + mockUseBlueprintUpdate.mockReturnValue({ + data: { + is_up_to_date: false, + }, + }) + + renderWithProviders( + wrapWithReactHookForm( + , + { + defaultValues: { + name: service.name, + terraform_action: TerraformAutoDeployConfigTerraformActionEnum.DEFAULT, + }, + } + ) + ) + + expect(screen.queryByText('New major version available')).not.toBeInTheDocument() + expect(screen.getByText('New blueprint version available')).toBeInTheDocument() + }) }) diff --git a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx index 4a48089813b..45cb5c8b41b 100644 --- a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx @@ -1,9 +1,10 @@ +import { useNavigate, useParams } from '@tanstack/react-router' import { type Organization, TerraformAutoDeployConfigTerraformActionEnum } from 'qovery-typescript-axios' import { Controller, useFormContext } from 'react-hook-form' import { EditGitRepositorySettings } from '@qovery/domains/organizations/feature' import { type Terraform } from '@qovery/domains/services/data-access' -import { AutoDeploySection, GeneralSetting } from '@qovery/domains/services/feature' -import { Heading, InputSelect, Section } from '@qovery/shared/ui' +import { AutoDeploySection, GeneralSetting, useBlueprintUpdate } from '@qovery/domains/services/feature' +import { Button, Callout, Heading, Icon, InputSelect, Section } from '@qovery/shared/ui' const triggeredActionItems = [ { label: 'Plan & apply', value: TerraformAutoDeployConfigTerraformActionEnum.DEFAULT }, @@ -34,6 +35,7 @@ export function TerraformGeneralSettings({ service, organization }: TerraformGen rootPathLabel="Terraform root folder path" rootPathHint="Provide the folder path where the Terraform code is located in the repository." /> + {service.blueprint_id && }
@@ -59,4 +61,41 @@ export function TerraformGeneralSettings({ service, organization }: TerraformGen ) } +function BlueprintUpdateSettings({ blueprintId }: { blueprintId: string }) { + const { data: blueprintUpdate } = useBlueprintUpdate({ blueprintId, suspense: true }) + console.log('blueprintUpdate', blueprintUpdate) + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + const navigate = useNavigate() + + if (!blueprintUpdate) return null + + const openBlueprintUpdate = () => { + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/update/blueprint', + params: { organizationId, projectId, environmentId, serviceId }, + }) + } + + return ( + <> + {blueprintUpdate.new_major_versions?.length > 0 && ( + + )} + {!blueprintUpdate.is_up_to_date && ( + + + + + New blueprint version available + + + )} + + ) +} + export default TerraformGeneralSettings diff --git a/package.json b/package.json index de3d8048b56..82e843a9525 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "mermaid": "11.6.0", "monaco-editor": "0.53.0", "posthog-js": "1.345.1", - "qovery-typescript-axios": "1.1.920", + "qovery-typescript-axios": "1.1.925", "react": "18.3.1", "react-country-flag": "3.0.2", "react-datepicker": "4.12.0", diff --git a/yarn.lock b/yarn.lock index 1a6101f9168..1629f25e935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6343,7 +6343,7 @@ __metadata: prettier: 3.2.5 prettier-plugin-tailwindcss: 0.5.14 pretty-quick: 4.0.0 - qovery-typescript-axios: 1.1.920 + qovery-typescript-axios: 1.1.925 qovery-ws-typescript-axios: 0.1.621 react: 18.3.1 react-country-flag: 3.0.2 @@ -25862,12 +25862,12 @@ __metadata: languageName: node linkType: hard -"qovery-typescript-axios@npm:1.1.920": - version: 1.1.920 - resolution: "qovery-typescript-axios@npm:1.1.920" +"qovery-typescript-axios@npm:1.1.925": + version: 1.1.925 + resolution: "qovery-typescript-axios@npm:1.1.925" dependencies: axios: 1.15.2 - checksum: ea8634a4c39cffb4127fd69ff1e9f59948db6ab35ddce30afd8a32312ae5a2394e88196b6ab25f47bf5a8d1801c839d5b63e8d7ca708da1da77eb9bdf6fe0e68 + checksum: 8661bdc95d6333250ca73cc697f5c4f33dcd357504af2f7242f55054ce6b2530778519494078b0df6a3c9df1f20b388c178ec4e63b89efca6a5dc1b44c09199e languageName: node linkType: hard From b1c52e561a9ff5c85964dfced6e7b703c3da670d Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Fri, 17 Jul 2026 16:20:17 +0200 Subject: [PATCH 2/3] UI tweaks --- .../terraform-general-settings.tsx | 9 +++++---- .../blueprint-configuration-view.tsx | 4 ++-- .../blueprint-step-summary/blueprint-step-summary.tsx | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx index 45cb5c8b41b..1c15cb1f46d 100644 --- a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx @@ -63,7 +63,6 @@ export function TerraformGeneralSettings({ service, organization }: TerraformGen function BlueprintUpdateSettings({ blueprintId }: { blueprintId: string }) { const { data: blueprintUpdate } = useBlueprintUpdate({ blueprintId, suspense: true }) - console.log('blueprintUpdate', blueprintUpdate) const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) const navigate = useNavigate() @@ -79,9 +78,11 @@ function BlueprintUpdateSettings({ blueprintId }: { blueprintId: string }) { return ( <> {blueprintUpdate.new_major_versions?.length > 0 && ( - +
+ +
)} {!blueprintUpdate.is_up_to_date && ( diff --git a/libs/domains/services/feature/src/lib/service-creation-flow/blueprint/blueprint-configuration-view/blueprint-configuration-view.tsx b/libs/domains/services/feature/src/lib/service-creation-flow/blueprint/blueprint-configuration-view/blueprint-configuration-view.tsx index 191bc8c69c4..8dc3628a5bd 100644 --- a/libs/domains/services/feature/src/lib/service-creation-flow/blueprint/blueprint-configuration-view/blueprint-configuration-view.tsx +++ b/libs/domains/services/feature/src/lib/service-creation-flow/blueprint/blueprint-configuration-view/blueprint-configuration-view.tsx @@ -144,7 +144,7 @@ function ServiceInformationSectionContent({ onContinue }: ServiceInformationSect /> {blueprintVersionOptions.length > 1 ? ( @@ -153,7 +153,7 @@ function ServiceInformationSectionContent({ onContinue }: ServiceInformationSect isSearchable={blueprintVersionOptions.length > 6} /> ) : ( - + )}
From b241ea5a94f7411c1d0696719a9c3f0816bb01b1 Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Fri, 17 Jul 2026 17:15:10 +0200 Subject: [PATCH 3/3] Hide "Edit" button for service's Source setting when service is blueprint-based --- .../edit-git-repository-settings.tsx | 4 +++- .../terraform-general-settings.spec.tsx | 22 ++++++++++++++++++- .../terraform-general-settings.tsx | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/libs/domains/organizations/feature/src/lib/git-repository-service-settings/edit-git-repository-settings/edit-git-repository-settings.tsx b/libs/domains/organizations/feature/src/lib/git-repository-service-settings/edit-git-repository-settings/edit-git-repository-settings.tsx index 5caf135b8c2..2224c38c51f 100644 --- a/libs/domains/organizations/feature/src/lib/git-repository-service-settings/edit-git-repository-settings/edit-git-repository-settings.tsx +++ b/libs/domains/organizations/feature/src/lib/git-repository-service-settings/edit-git-repository-settings/edit-git-repository-settings.tsx @@ -8,6 +8,7 @@ interface EditGitRepositorySettingsProps { gitRepository?: ApplicationGitRepository rootPathLabel?: string rootPathHint?: string + showEditAction?: boolean } export function EditGitRepositorySettings({ @@ -15,6 +16,7 @@ export function EditGitRepositorySettings({ gitRepository, rootPathHint, rootPathLabel, + showEditAction = true, }: EditGitRepositorySettingsProps) { const { setValue } = useFormContext<{ provider: GitProviderEnum | undefined @@ -52,7 +54,7 @@ export function EditGitRepositorySettings({ return ( ({ ...jest.requireActual('@tanstack/react-router'), @@ -19,7 +20,10 @@ jest.mock('@tanstack/react-router', () => ({ })) jest.mock('@qovery/domains/organizations/feature', () => ({ - EditGitRepositorySettings: () => null, + EditGitRepositorySettings: (props: unknown) => { + mockEditGitRepositorySettings(props) + return null + }, })) jest.mock('@qovery/domains/services/feature', () => ({ @@ -73,6 +77,9 @@ describe('TerraformGeneralSettings', () => { expect(screen.getByRole('button', { name: 'New major version available' })).toBeInTheDocument() expect(screen.getByText('New blueprint version available')).toBeInTheDocument() + expect(mockEditGitRepositorySettings).toHaveBeenCalledWith( + expect.objectContaining({ showEditAction: false }) + ) await userEvent.click(screen.getByRole('button', { name: 'Update' })) @@ -87,6 +94,19 @@ describe('TerraformGeneralSettings', () => { }) }) + it('allows editing the Git repository for non-blueprint services', () => { + renderWithProviders( + wrapWithReactHookForm(, { + defaultValues: { + name: service.name, + terraform_action: TerraformAutoDeployConfigTerraformActionEnum.DEFAULT, + }, + }) + ) + + expect(mockEditGitRepositorySettings).toHaveBeenCalledWith(expect.objectContaining({ showEditAction: true })) + }) + it('hides blueprint update signals when the blueprint is up to date', () => { mockUseBlueprintUpdate.mockReturnValue({ data: { diff --git a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx index 1c15cb1f46d..a61dee900a8 100644 --- a/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx +++ b/libs/domains/service-settings/feature/src/lib/service-general-settings/terraform-general-settings/terraform-general-settings.tsx @@ -34,6 +34,7 @@ export function TerraformGeneralSettings({ service, organization }: TerraformGen gitRepository={service.terraform_files_source?.git?.git_repository} rootPathLabel="Terraform root folder path" rootPathHint="Provide the folder path where the Terraform code is located in the repository." + showEditAction={!service.blueprint_id} /> {service.blueprint_id && }