From 1415f66a013f81b8e1c2cc46246be23d34b14ec7 Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Mon, 23 Feb 2026 11:10:40 +0100 Subject: [PATCH 1/3] fix: reduce amount of "links" requests by calling the env-scoped endpoint --- .../src/lib/domains-environments-data-access.ts | 7 +++++++ libs/domains/services/feature/src/index.ts | 1 + .../src/lib/hooks/use-env-links/use-env-links.ts | 16 ++++++++++++++++ .../service-links-popover.tsx | 4 ++-- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts diff --git a/libs/domains/environments/data-access/src/lib/domains-environments-data-access.ts b/libs/domains/environments/data-access/src/lib/domains-environments-data-access.ts index 590ab14afba..688968b06c8 100644 --- a/libs/domains/environments/data-access/src/lib/domains-environments-data-access.ts +++ b/libs/domains/environments/data-access/src/lib/domains-environments-data-access.ts @@ -84,6 +84,13 @@ export const environments = createQueryKeys('environments', { return result.data.results ?? [] }, }), + listLinks: ({ environmentId }: { environmentId: string }) => ({ + queryKey: [environmentId], + async queryFn() { + const result = await environmentApi.listEnvironmentServicesLinks(environmentId) + return result.data.results ?? [] + }, + }), list: ({ projectId }: { projectId: string }) => ({ queryKey: [projectId], async queryFn() { diff --git a/libs/domains/services/feature/src/index.ts b/libs/domains/services/feature/src/index.ts index 33ad72f33c3..5e6cc6f1fae 100644 --- a/libs/domains/services/feature/src/index.ts +++ b/libs/domains/services/feature/src/index.ts @@ -17,6 +17,7 @@ export * from './lib/hooks/use-deployment-restrictions/use-deployment-restrictio export * from './lib/hooks/use-deployment-status/use-deployment-status' export * from './lib/hooks/use-edit-deployment-restriction/use-edit-deployment-restriction' export * from './lib/hooks/use-links/use-links' +export * from './lib/hooks/use-env-links/use-env-links' export * from './lib/hooks/use-list-statuses/use-list-statuses' export * from './lib/hooks/use-running-status/use-running-status' export * from './lib/hooks/use-service-statuses/use-service-statuses' diff --git a/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts b/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts new file mode 100644 index 00000000000..bf451925e8e --- /dev/null +++ b/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query' +import { queries } from '@qovery/state/util-queries' + +export interface UseEnvLinksProps { + environmentId: string + enabled?: boolean +} + +export function useEnvLinks({ environmentId, enabled = true }: UseEnvLinksProps) { + return useQuery({ + ...queries.environments.listLinks({ + environmentId, + }), + enabled, + }) +} diff --git a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx index abf254cebe4..18e4e7d657f 100644 --- a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx +++ b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx @@ -12,7 +12,7 @@ import { Truncate, } from '@qovery/shared/ui' import { pluralize } from '@qovery/shared/util-js' -import { useLinks } from '../hooks/use-links/use-links' +import { useEnvLinks } from '../hooks/use-env-links/use-env-links' import { useServiceType } from '../hooks/use-service-type/use-service-type' export interface ServiceLinksPopoverProps extends Pick { @@ -33,7 +33,7 @@ export function ServiceLinksPopover({ side = 'bottom', }: ServiceLinksPopoverProps) { const { data: serviceType } = useServiceType({ environmentId, serviceId }) - const { data: links = [] } = useLinks({ serviceId, serviceType }) + const { data: links = [] } = useEnvLinks({ environmentId }) // Remove default Qovery links const filteredLinks = useMemo( From 64f901a76603c7fd297daf7dce1c573975d5fcd3 Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Mon, 23 Feb 2026 11:28:49 +0100 Subject: [PATCH 2/3] Update outdated unit tests and snapshots --- .../service-links-popover.spec.tsx | 6 +++--- .../__snapshots__/service-list.spec.tsx.snap | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx index 3930e7fdf16..058fa54ea3f 100644 --- a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx @@ -12,10 +12,10 @@ jest.mock('../hooks/use-service-type/use-service-type', () => { const mockUseLinks = jest.fn() -jest.mock('../hooks/use-links/use-links', () => { +jest.mock('../hooks/use-env-links/use-env-links', () => { return { - ...jest.requireActual('../hooks/use-links/use-links'), - useLinks: () => mockUseLinks(), + ...jest.requireActual('../hooks/use-env-links/use-env-links'), + useEnvLinks: () => mockUseLinks(), } }) diff --git a/libs/domains/services/feature/src/lib/service-list/__snapshots__/service-list.spec.tsx.snap b/libs/domains/services/feature/src/lib/service-list/__snapshots__/service-list.spec.tsx.snap index b7dc56c71ff..5ecc84cd255 100644 --- a/libs/domains/services/feature/src/lib/service-list/__snapshots__/service-list.spec.tsx.snap +++ b/libs/domains/services/feature/src/lib/service-list/__snapshots__/service-list.spec.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`ServiceList should match snapshot 1`] = `
@@ -244,7 +244,7 @@ exports[`ServiceList should match snapshot 1`] = ` aria-controls="radix-:r25:" aria-expanded="false" aria-haspopup="dialog" - class="inline-flex items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 ml-3" + class="items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 hidden ml-3" data-state="closed" type="button" > @@ -596,7 +596,7 @@ exports[`ServiceList should match snapshot 1`] = ` aria-controls="radix-:r2f:" aria-expanded="false" aria-haspopup="dialog" - class="inline-flex items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 ml-3" + class="items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 hidden ml-3" data-state="closed" type="button" > @@ -1246,7 +1246,7 @@ exports[`ServiceList should match snapshot 1`] = ` aria-controls="radix-:r2q:" aria-expanded="false" aria-haspopup="dialog" - class="inline-flex items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 ml-3" + class="items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 hidden ml-3" data-state="closed" type="button" > @@ -1693,7 +1693,7 @@ exports[`ServiceList should match snapshot 1`] = ` aria-controls="radix-:r35:" aria-expanded="false" aria-haspopup="dialog" - class="inline-flex items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 ml-3" + class="items-center shrink-0 font-medium transition-[background-color,transform] active:scale-[0.97] disabled:scale-100 disabled:pointer-events-none focus-visible:[&:not(:active)]:outline-2 outline-0 shadow-sm disabled:shadow-none active:shadow-none outline-brand-500 text-xs h-7 px-2 rounded-full border border-neutral-250 dark:border-neutral-400 text-neutral-400 dark:text-white hover:[&:not(:active)]:border-neutral-300 dark:hover:[&:not(:active):not(:disabled)]:border-neutral-350 active:bg-neutral-150 disabled:text-neutral-300 disabled:bg-neutral-100 disabled:border-none bg-neutral-100 dark:bg-neutral-500 hover:[&:not(:active):not(:disabled)]:bg-white dark:hover:[&:not(:active):not(:disabled)]:bg-neutral-400 hidden ml-3" data-state="closed" type="button" > From adb0b8bca6339371a9bb8371326ceddbb2705877 Mon Sep 17 00:00:00 2001 From: Romain Billard Date: Mon, 23 Feb 2026 12:44:06 +0100 Subject: [PATCH 3/3] Renaming useEnvLinks to useEnvironmentLinks --- .../feature/src/lib/hooks/use-env-links/use-env-links.ts | 4 ++-- .../lib/service-links-popover/service-links-popover.spec.tsx | 2 +- .../src/lib/service-links-popover/service-links-popover.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts b/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts index bf451925e8e..50d3fcc9230 100644 --- a/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts +++ b/libs/domains/services/feature/src/lib/hooks/use-env-links/use-env-links.ts @@ -1,12 +1,12 @@ import { useQuery } from '@tanstack/react-query' import { queries } from '@qovery/state/util-queries' -export interface UseEnvLinksProps { +export interface useEnvironmentLinksProps { environmentId: string enabled?: boolean } -export function useEnvLinks({ environmentId, enabled = true }: UseEnvLinksProps) { +export function useEnvironmentLinks({ environmentId, enabled = true }: useEnvironmentLinksProps) { return useQuery({ ...queries.environments.listLinks({ environmentId, diff --git a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx index 058fa54ea3f..f6577a12ed3 100644 --- a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.spec.tsx @@ -15,7 +15,7 @@ const mockUseLinks = jest.fn() jest.mock('../hooks/use-env-links/use-env-links', () => { return { ...jest.requireActual('../hooks/use-env-links/use-env-links'), - useEnvLinks: () => mockUseLinks(), + useEnvironmentLinks: () => mockUseLinks(), } }) diff --git a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx index 18e4e7d657f..efeeca23bf7 100644 --- a/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx +++ b/libs/domains/services/feature/src/lib/service-links-popover/service-links-popover.tsx @@ -12,7 +12,7 @@ import { Truncate, } from '@qovery/shared/ui' import { pluralize } from '@qovery/shared/util-js' -import { useEnvLinks } from '../hooks/use-env-links/use-env-links' +import { useEnvironmentLinks } from '../hooks/use-env-links/use-env-links' import { useServiceType } from '../hooks/use-service-type/use-service-type' export interface ServiceLinksPopoverProps extends Pick { @@ -33,7 +33,7 @@ export function ServiceLinksPopover({ side = 'bottom', }: ServiceLinksPopoverProps) { const { data: serviceType } = useServiceType({ environmentId, serviceId }) - const { data: links = [] } = useEnvLinks({ environmentId }) + const { data: links = [] } = useEnvironmentLinks({ environmentId }) // Remove default Qovery links const filteredLinks = useMemo(