From 8f4e70f0adc56550661694322173fb89d0f3eda2 Mon Sep 17 00:00:00 2001 From: RemiBonnet Date: Wed, 18 Feb 2026 16:57:38 +0100 Subject: [PATCH 01/20] feat(service): add service navigation tabs and routes for service overview, deployment, monitoring, logs, variables, and settings --- .../service/$serviceId/index.tsx | 19 +++++++ .../service/$serviceId/overview.tsx | 16 ++++++ .../_authenticated/organization/route.tsx | 52 +++++++++++++++++++ .../src/lib/service-list/service-list.tsx | 37 +++++++------ 4 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx create mode 100644 apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx new file mode 100644 index 00000000000..1f86916861f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx new file mode 100644 index 00000000000..f367335b382 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -0,0 +1,16 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+ Hello + "/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview"! +
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/route.tsx index 9963bbbe636..f396c462ffb 100644 --- a/apps/console-v5/src/routes/_authenticated/organization/route.tsx +++ b/apps/console-v5/src/routes/_authenticated/organization/route.tsx @@ -134,6 +134,51 @@ const ENVIRONMENT_TABS: NavigationTab[] = [ }, ] +const SERVICE_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview', + }, + { + id: 'deployment', + label: 'Deployment', + iconName: 'rocket', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployment', + }, + { + id: 'monitoring', + label: 'Monitoring', + iconName: 'chart-column', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring', + }, + { + id: 'service-logs', + label: 'Service Logs', + iconName: 'scroll', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs', + }, + { + id: 'variables', + label: 'Variables', + iconName: 'key', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings', + }, +] + function createRoutePatternRegex(routeIdPattern: string): RegExp { const patternPath = routeIdPattern.replace('/_authenticated/organization', '/organization') return new RegExp('^' + patternPath.replace(/\$(\w+)/g, '[^/]+') + '(/.*)?$') @@ -156,6 +201,13 @@ const NAVIGATION_CONTEXTS: Array<{ tabs: NavigationTab[] paramNames: string[] }> = [ + { + type: 'service', + routeIdPattern: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId', + tabs: SERVICE_TABS, + paramNames: ['organizationId', 'projectId', 'environmentId', 'serviceId'], + }, { type: 'environment', routeIdPattern: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId', diff --git a/libs/domains/services/feature/src/lib/service-list/service-list.tsx b/libs/domains/services/feature/src/lib/service-list/service-list.tsx index fbf2b015936..9e8f4d47440 100644 --- a/libs/domains/services/feature/src/lib/service-list/service-list.tsx +++ b/libs/domains/services/feature/src/lib/service-list/service-list.tsx @@ -99,19 +99,6 @@ function ServiceNameCell({ const deploymentRequestsCount = Number(deploymentStatus?.deployment_requests_count) - const serviceLink = match(service) - .with( - { serviceType: ServiceTypeEnum.DATABASE }, - ({ id }) => - DATABASE_URL(environment.organization.id, environment.project.id, service.environment.id, id) + - DATABASE_GENERAL_URL - ) - .otherwise( - ({ id }) => - APPLICATION_URL(environment.organization.id, environment.project.id, service.environment.id, id) + - SERVICES_GENERAL_URL - ) - const LinkDeploymentStatus = () => { const environmentLog = ENVIRONMENT_LOGS_URL(environment.organization.id, environment.project.id, environment.id) const deploymentLog = DEPLOYMENT_LOGS_VERSION_URL(service.id, deploymentStatus?.execution_id) @@ -169,7 +156,13 @@ function ServiceNameCell({ e.stopPropagation()} > @@ -206,7 +199,13 @@ function ServiceNameCell({ e.stopPropagation()} > @@ -228,7 +227,13 @@ function ServiceNameCell({ e.stopPropagation()} > From d9ab9ec9a5f4c804145d3b15390767c90920dfbf Mon Sep 17 00:00:00 2001 From: RemiBonnet Date: Mon, 23 Feb 2026 16:51:05 +0100 Subject: [PATCH 02/20] feat(service-overview): init new view --- apps/console-v5/src/routeTree.gen.ts | 50 ++ .../service/$serviceId/overview.tsx | 43 +- libs/domains/services/feature/src/index.ts | 5 +- .../auto-deploy-badge/auto-deploy-badge.tsx | 4 +- .../dockerfile-settings.spec.tsx | 4 + .../dockerfile-settings.tsx | 4 +- .../src/lib/hooks/use-commits/use-commits.ts | 6 +- .../use-last-deployed-commit.ts | 30 +- .../last-commit-author/last-commit-author.tsx | 41 +- .../src/lib/last-commit/last-commit.tsx | 16 +- .../need-redeploy-flag.spec.tsx | 11 + .../need-redeploy-flag/need-redeploy-flag.tsx | 19 +- .../src/lib/pod-details/pod-details.spec.tsx | 7 + .../src/lib/pod-details/pod-details.tsx | 6 +- .../pod-statuses-callout.spec.tsx | 5 + .../pod-statuses-callout.tsx | 13 +- .../service-action-toolbar.spec.tsx | 8 +- .../service-clone-modal.spec.tsx | 20 +- .../service-clone-modal.tsx | 6 +- .../lib/service-details/service-details.tsx | 514 ------------------ .../__snapshots__/service-list.spec.tsx.snap | 12 +- .../lib/service-list/service-list.spec.tsx | 4 - .../observability-callout.tsx | 164 ++++++ .../service-details.spec.tsx.snap | 0 .../service-header-skeleton.tsx} | 6 +- .../service-header/service-header.spec.tsx} | 9 +- .../service-header/service-header.tsx | 463 ++++++++++++++++ .../service-last-deployment.tsx | 151 +++++ .../service-overview-feature.spec.tsx | 56 ++ .../service-overview-feature.tsx | 66 +++ .../service-overview.spec.tsx | 84 +++ .../lib/service-overview/service-overview.tsx | 139 +++++ .../service-terminal-provider.tsx | 2 +- .../deployment-action/deployment-action.tsx | 18 +- .../components/empty-state/empty-state.tsx | 11 +- 35 files changed, 1385 insertions(+), 612 deletions(-) delete mode 100644 libs/domains/services/feature/src/lib/service-details/service-details.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/observability-callout.tsx rename libs/domains/services/feature/src/lib/{service-details => service-overview/service-header}/__snapshots__/service-details.spec.tsx.snap (100%) rename libs/domains/services/feature/src/lib/{service-details/service-details-skeleton.tsx => service-overview/service-header/service-header-skeleton.tsx} (87%) rename libs/domains/services/feature/src/lib/{service-details/service-details.spec.tsx => service-overview/service-header/service-header.spec.tsx} (94%) create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-last-deployment/service-last-deployment.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview-feature.spec.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview-feature.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview.spec.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview.tsx diff --git a/apps/console-v5/src/routeTree.gen.ts b/apps/console-v5/src/routeTree.gen.ts index af23f92bf3e..72ef60d8fc0 100644 --- a/apps/console-v5/src/routeTree.gen.ts +++ b/apps/console-v5/src/routeTree.gen.ts @@ -43,6 +43,8 @@ import { Route as AuthenticatedOrganizationOrganizationIdOverviewRouteImport } f import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' @@ -522,6 +524,22 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, } as any) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any + ) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -597,6 +615,8 @@ export interface FileRoutesByFullPath { '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -664,6 +684,8 @@ export interface FileRoutesByTo { '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport @@ -741,6 +763,8 @@ export interface FileRoutesById { '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -818,6 +842,8 @@ export interface FileRouteTypes { | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -885,6 +911,8 @@ export interface FileRouteTypes { | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId' id: | '__root__' | '/' @@ -961,6 +989,8 @@ export interface FileRouteTypes { | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -1490,6 +1520,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/' + path: '/project/$projectId/environment/$environmentId/service/$serviceId' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/overview' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } } } @@ -1709,6 +1753,8 @@ interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren { AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute } const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOrganizationOrganizationIdRouteRouteChildren = @@ -1752,6 +1798,10 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute, } const AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren = diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx index f367335b382..852827b6922 100644 --- a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -1,4 +1,11 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, useParams } from '@tanstack/react-router' +import { useMemo } from 'react' +import { match } from 'ts-pattern' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { EnableObservabilityModal } from '@qovery/domains/observability/feature' +import { TerraformResourcesSection } from '@qovery/domains/service-terraform/feature' +import { ServiceOverviewFeature, useService } from '@qovery/domains/services/feature' export const Route = createFileRoute( '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' @@ -7,10 +14,36 @@ export const Route = createFileRoute( }) function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + const { data: environment } = useEnvironment({ environmentId }) + const { data: service } = useService({ environmentId, serviceId }) + const { data: cluster } = useCluster({ organizationId, clusterId: environment?.cluster_id ?? '' }) + + const hasNoMetrics = useMemo( + () => + (cluster?.cloud_provider === 'AWS' || + cluster?.cloud_provider === 'SCW' || + cluster?.cloud_provider === 'GCP' || + cluster?.cloud_provider === 'AZURE') && + !cluster?.metrics_parameters?.enabled && + match(service?.serviceType) + .with('APPLICATION', 'CONTAINER', () => true) + .otherwise(() => false), + [cluster?.metrics_parameters?.enabled, service?.serviceType, cluster?.cloud_provider] + ) + return ( -
- Hello - "/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview"! -
+ : undefined} + observabilityCalloutModalContent={} + /> ) } diff --git a/libs/domains/services/feature/src/index.ts b/libs/domains/services/feature/src/index.ts index 9e53a18022a..40e14e58167 100644 --- a/libs/domains/services/feature/src/index.ts +++ b/libs/domains/services/feature/src/index.ts @@ -46,9 +46,12 @@ export * from './lib/keda/scaled-object-status/scaled-object-status' export * from './lib/keda/components' export * from './lib/service-action-toolbar/service-action-toolbar' export * from './lib/service-deployment-status-label/service-deployment-status-label' -export * from './lib/service-details/service-details' +export * from './lib/service-overview/service-header/service-header' export * from './lib/service-links-popover/service-links-popover' export * from './lib/service-list/service-list' +export * from './lib/service-overview/observability-callout' +export * from './lib/service-overview/service-overview' +export * from './lib/service-overview/service-overview-feature' export * from './lib/service-state-chip/service-state-chip' export * from './lib/service-clone-modal/service-clone-modal' export * from './lib/service-terminal/service-terminal' diff --git a/libs/domains/services/feature/src/lib/auto-deploy-badge/auto-deploy-badge.tsx b/libs/domains/services/feature/src/lib/auto-deploy-badge/auto-deploy-badge.tsx index 509f0e49c25..881f2cf639a 100644 --- a/libs/domains/services/feature/src/lib/auto-deploy-badge/auto-deploy-badge.tsx +++ b/libs/domains/services/feature/src/lib/auto-deploy-badge/auto-deploy-badge.tsx @@ -1,6 +1,6 @@ import { type IconName } from '@fortawesome/fontawesome-common-types' +import { useParams } from '@tanstack/react-router' import { type GitWebhookStatusResponse } from 'qovery-typescript-axios' -import { useParams } from 'react-router-dom' import { APPLICATION_SETTINGS_GENERAL_URL, APPLICATION_SETTINGS_URL, APPLICATION_URL } from '@qovery/shared/routes' import { Icon, Link, LoaderSpinner, Tooltip } from '@qovery/shared/ui' import { useGitWebhookStatus } from '../hooks/use-git-webhook-status/use-git-webhook-status' @@ -41,7 +41,7 @@ const webhookStatusConfig: Record< } export function AutoDeployBadge({ serviceId }: AutoDeployBadgeProps) { - const { organizationId = '', projectId = '', environmentId = '', applicationId = '' } = useParams() + const { organizationId = '', projectId = '', environmentId = '', applicationId = '' } = useParams({ strict: false }) const { data: webhookStatus, isLoading } = useGitWebhookStatus({ serviceId }) const config = webhookStatus ? webhookStatusConfig[webhookStatus.status] : undefined diff --git a/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.spec.tsx b/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.spec.tsx index 60b6a137f56..2661fe161d7 100644 --- a/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.spec.tsx +++ b/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.spec.tsx @@ -3,6 +3,10 @@ import selectEvent from 'react-select-event' import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { DockerfileSettings, type DockerfileSettingsProps } from './dockerfile-settings' +jest.mock('@tanstack/react-router', () => ({ + useParams: () => ({ environmentId: '' }), +})) + // https://github.com/suren-atoyan/monaco-react/issues/88 // https://github.com/suren-atoyan/monaco-react/issues/88#issuecomment-887055307 jest.mock('@monaco-editor/react', () => { diff --git a/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.tsx b/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.tsx index f3dd921e46f..cf9fedfda91 100644 --- a/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.tsx +++ b/libs/domains/services/feature/src/lib/dockerfile-settings/dockerfile-settings.tsx @@ -1,7 +1,7 @@ +import { useParams } from '@tanstack/react-router' import { type JobLifecycleTypeEnum } from 'qovery-typescript-axios' import { type PropsWithChildren } from 'react' import { Controller, type ControllerRenderProps, type UseFormReturn } from 'react-hook-form' -import { useParams } from 'react-router-dom' import { match } from 'ts-pattern' import { BlockContent, @@ -44,7 +44,7 @@ export function DockerfileSettings({ defaultContent, templateType, }: DockerfileSettingsProps) { - const { environmentId = '' } = useParams() + const { environmentId = '' } = useParams({ strict: false }) const { openModal, closeModal } = useModal() const { setValue, control, watch } = methods diff --git a/libs/domains/services/feature/src/lib/hooks/use-commits/use-commits.ts b/libs/domains/services/feature/src/lib/hooks/use-commits/use-commits.ts index 64f7aa6fabc..0fa8b99229e 100644 --- a/libs/domains/services/feature/src/lib/hooks/use-commits/use-commits.ts +++ b/libs/domains/services/feature/src/lib/hooks/use-commits/use-commits.ts @@ -1,11 +1,13 @@ import { useQuery } from '@tanstack/react-query' import { queries } from '@qovery/state/util-queries' -export type UseCommitsProps = Parameters[0] +export type UseCommitsProps = Parameters[0] & { enabled?: boolean } export function useCommits(props: UseCommitsProps) { + const { enabled = true, ...queryProps } = props return useQuery({ - ...queries.services.listCommits(props), + ...queries.services.listCommits(queryProps), + enabled, staleTime: 3 * 60 * 1000, // 3 minutes - commit history doesn't change frequently retry: false, retryOnMount: false, diff --git a/libs/domains/services/feature/src/lib/hooks/use-last-deployed-commit/use-last-deployed-commit.ts b/libs/domains/services/feature/src/lib/hooks/use-last-deployed-commit/use-last-deployed-commit.ts index a745427170f..371d7422923 100644 --- a/libs/domains/services/feature/src/lib/hooks/use-last-deployed-commit/use-last-deployed-commit.ts +++ b/libs/domains/services/feature/src/lib/hooks/use-last-deployed-commit/use-last-deployed-commit.ts @@ -1,20 +1,32 @@ import { type ApplicationGitRepository, type Commit } from 'qovery-typescript-axios' +import { type ServiceType } from '@qovery/domains/services/data-access' import { type UseCommitsProps, useCommits } from '../use-commits/use-commits' export type UseLastDeployedCommitProps = { - gitRepository: ApplicationGitRepository -} & UseCommitsProps + serviceId: string + serviceType: ServiceType + gitRepository?: ApplicationGitRepository | null +} & Omit -export function useLastDeployedCommit({ gitRepository, ...useCommitsProps }: UseLastDeployedCommitProps) { - const { data: commits, ...props } = useCommits(useCommitsProps) +export function useLastDeployedCommit({ gitRepository, serviceId, serviceType, ...rest }: UseLastDeployedCommitProps) { + const enabled = Boolean(gitRepository && serviceId && serviceType) + const { data: commits, ...props } = useCommits({ + serviceId: serviceId ?? '', + serviceType: (serviceType ?? 'APPLICATION') as Extract< + ServiceType, + 'APPLICATION' | 'JOB' | 'CRON_JOB' | 'LIFECYCLE_JOB' | 'HELM' | 'TERRAFORM' + >, + ...rest, + enabled, + }) - const delta = commits?.findIndex(({ git_commit_id }) => git_commit_id === gitRepository.deployed_commit_id) ?? 0 + const delta = commits?.findIndex(({ git_commit_id }) => git_commit_id === gitRepository?.deployed_commit_id) ?? 0 const defaultCommitInfo: Commit = { - created_at: gitRepository.deployed_commit_date ?? 'unknown', - git_commit_id: gitRepository.deployed_commit_id ?? 'unknown', - tag: gitRepository.deployed_commit_tag ?? 'unknown', + created_at: gitRepository?.deployed_commit_date ?? 'unknown', + git_commit_id: gitRepository?.deployed_commit_id ?? 'unknown', + tag: gitRepository?.deployed_commit_tag ?? 'unknown', message: 'unknown', - author_name: gitRepository.deployed_commit_contributor ?? 'unknown', + author_name: gitRepository?.owner ?? gitRepository?.deployed_commit_contributor ?? 'unknown', } return { data: { diff --git a/libs/domains/services/feature/src/lib/last-commit-author/last-commit-author.tsx b/libs/domains/services/feature/src/lib/last-commit-author/last-commit-author.tsx index 5d096b6c01d..24af09dcebd 100644 --- a/libs/domains/services/feature/src/lib/last-commit-author/last-commit-author.tsx +++ b/libs/domains/services/feature/src/lib/last-commit-author/last-commit-author.tsx @@ -8,6 +8,7 @@ export interface LastCommitAuthorProps extends Omit + withFullName?: boolean } export function LastCommitAuthor({ @@ -16,28 +17,36 @@ export function LastCommitAuthor({ gitRepository, serviceId, serviceType, + withFullName = false, ...props }: LastCommitAuthorProps) { const { data: { deployedCommit }, } = useLastDeployedCommit({ gitRepository, serviceId, serviceType }) - return deployedCommit.author_name && deployedCommit.author_avatar_url ? ( - - ) : ( - + return ( + + {deployedCommit.author_name && deployedCommit.author_avatar_url ? ( + + ) : ( + + )} + {withFullName && deployedCommit.author_name && ( + {deployedCommit.author_name} + )} + ) } diff --git a/libs/domains/services/feature/src/lib/last-commit/last-commit.tsx b/libs/domains/services/feature/src/lib/last-commit/last-commit.tsx index c344e6c9639..a6b215e7e73 100644 --- a/libs/domains/services/feature/src/lib/last-commit/last-commit.tsx +++ b/libs/domains/services/feature/src/lib/last-commit/last-commit.tsx @@ -18,7 +18,7 @@ export function LastCommit({ organizationId, projectId, service, gitRepository } const { data: { deployedCommit, delta }, - } = useLastDeployedCommit({ gitRepository, serviceId: service.id, serviceType: service.serviceType }) + } = useLastDeployedCommit({ serviceId: service.id, serviceType: service.serviceType, gitRepository }) const { mutate: deployService } = useDeployService({ organizationId, projectId, @@ -56,7 +56,7 @@ export function LastCommit({ organizationId, projectId, service, gitRepository } }} >

- For {service.name} + For {service.name}

), @@ -80,18 +80,14 @@ export function LastCommit({ organizationId, projectId, service, gitRepository } @@ -100,14 +96,14 @@ export function LastCommit({ organizationId, projectId, service, gitRepository } 0 ? `You have ${delta} commits ahead` : 'Deploy from another version'}> diff --git a/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.spec.tsx b/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.spec.tsx index bb13042b81d..35a27bd280e 100644 --- a/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.spec.tsx +++ b/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.spec.tsx @@ -5,6 +5,17 @@ import * as useDeploymentStatusImport from '../hooks/use-deployment-status/use-d import * as useServiceImport from '../hooks/use-service/use-service' import NeedRedeployFlag from './need-redeploy-flag' +jest.mock('@tanstack/react-router', () => ({ + useParams: () => ({ + organizationId: '', + projectId: '', + environmentId: '', + applicationId: '', + databaseId: '', + }), + useNavigate: () => jest.fn(), +})) + const useDeployServiceSpy = jest.spyOn(useDeployServiceImport, 'useDeployService') as jest.Mock describe('NeedRedeployFlag', () => { diff --git a/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.tsx b/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.tsx index e03f6133c79..01be6da9681 100644 --- a/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.tsx +++ b/libs/domains/services/feature/src/lib/need-redeploy-flag/need-redeploy-flag.tsx @@ -1,5 +1,5 @@ +import { useNavigate, useParams } from '@tanstack/react-router' import { ServiceDeploymentStatusEnum } from 'qovery-typescript-axios' -import { useNavigate, useParams } from 'react-router-dom' import { DEPLOYMENT_LOGS_VERSION_URL, ENVIRONMENT_LOGS_URL } from '@qovery/shared/routes' import { Banner } from '@qovery/shared/ui' import { useDeployService } from '../hooks/use-deploy-service/use-deploy-service' @@ -7,7 +7,13 @@ import { useDeploymentStatus } from '../hooks/use-deployment-status/use-deployme import { useService } from '../hooks/use-service/use-service' export function NeedRedeployFlag() { - const { organizationId = '', projectId = '', environmentId = '', applicationId = '', databaseId = '' } = useParams() + const { + organizationId = '', + projectId = '', + environmentId = '', + applicationId = '', + databaseId = '', + } = useParams({ strict: false }) const navigate = useNavigate() const { data: service } = useService({ environmentId, serviceId: applicationId || databaseId }) @@ -34,10 +40,11 @@ export function NeedRedeployFlag() { const mutationDeployService = () => { if (service) { deployService({ serviceId: service.id, serviceType: service.serviceType }) - navigate( - ENVIRONMENT_LOGS_URL(organizationId, projectId, environmentId) + - DEPLOYMENT_LOGS_VERSION_URL(service.id, 'latest') - ) + navigate({ + to: + ENVIRONMENT_LOGS_URL(organizationId, projectId, environmentId) + + DEPLOYMENT_LOGS_VERSION_URL(service.id, 'latest'), + }) } } diff --git a/libs/domains/services/feature/src/lib/pod-details/pod-details.spec.tsx b/libs/domains/services/feature/src/lib/pod-details/pod-details.spec.tsx index 9479b6938bc..0dc08da1d4f 100644 --- a/libs/domains/services/feature/src/lib/pod-details/pod-details.spec.tsx +++ b/libs/domains/services/feature/src/lib/pod-details/pod-details.spec.tsx @@ -1,6 +1,13 @@ +import type { ReactNode } from 'react' import { renderWithProviders } from '@qovery/shared/util-tests' import { type Pod, PodDetails } from './pod-details' +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useParams: () => ({ organizationId: '', projectId: '', environmentId: '' }), + Link: ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) => {children}, +})) + describe('PodDetails', () => { it('should match snapshot with git based pod and without last_terminated_state', () => { const pod: Pod = { diff --git a/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx b/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx index f23ea1167b9..327b7ec8613 100644 --- a/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx +++ b/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx @@ -1,5 +1,5 @@ +import { useParams } from '@tanstack/react-router' import { type PodStatusDto, type ServiceMetricsDto } from 'qovery-ws-typescript-axios' -import { useParams } from 'react-router-dom' import { match } from 'ts-pattern' import { type ServiceType } from '@qovery/domains/services/data-access' import { ENVIRONMENT_LOGS_URL, SERVICE_LOGS_URL } from '@qovery/shared/routes' @@ -34,7 +34,7 @@ export interface PodDetailsProps { } export function PodDetails({ pod, serviceId, serviceType }: PodDetailsProps) { - const { organizationId = '', projectId = '', environmentId = '' } = useParams() + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) const QOVERY_SIDECAR_NAME = 'qovery-wait-container-output' as const const { containers = [] } = pod @@ -42,7 +42,7 @@ export function PodDetails({ pod, serviceId, serviceType }: PodDetailsProps) { const defaultContainer = filteredContainers[0]?.name return ( -
+
({ + useNavigate: () => jest.fn(), + useParams: () => ({ organizationId: '', projectId: '' }), +})) + describe('PodStatusesCallout', () => { it('should render successfully', () => { jest.spyOn(useServiceTypeImport, 'useServiceType').mockReturnValue({ diff --git a/libs/domains/services/feature/src/lib/pod-statuses-callout/pod-statuses-callout.tsx b/libs/domains/services/feature/src/lib/pod-statuses-callout/pod-statuses-callout.tsx index b7bc11825ce..89e88faa07a 100644 --- a/libs/domains/services/feature/src/lib/pod-statuses-callout/pod-statuses-callout.tsx +++ b/libs/domains/services/feature/src/lib/pod-statuses-callout/pod-statuses-callout.tsx @@ -1,6 +1,6 @@ import { type IconName } from '@fortawesome/fontawesome-common-types' +import { useNavigate, useParams } from '@tanstack/react-router' import { type ReactNode, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' import { APPLICATION_SETTINGS_DOMAIN_URL, APPLICATION_SETTINGS_URL, APPLICATION_URL } from '@qovery/shared/routes' import { Button, Callout, type CalloutRootProps, Icon } from '@qovery/shared/ui' import { upperCaseFirstLetter } from '@qovery/shared/util-js' @@ -15,7 +15,7 @@ export interface PodStatusesCalloutProps { export function PodStatusesCallout({ environmentId, serviceId }: PodStatusesCalloutProps) { const navigate = useNavigate() - const { organizationId = '', projectId = '' } = useParams() + const { organizationId = '', projectId = '' } = useParams({ strict: false }) const { data: runningStatuses, isLoading: isRunningStatusesLoading } = useRunningStatus({ environmentId, serviceId }) const { data: serviceType, isLoading: isServiceTypeLoading } = useServiceType({ environmentId, serviceId }) const { mutate: cleanFailedJobs } = useCleanFailedJobs() @@ -81,11 +81,12 @@ export function PodStatusesCallout({ environmentId, serviceId }: PodStatusesCall className="text-md gap-2" size="md" onClick={() => - navigate( - APPLICATION_URL(organizationId, projectId, environmentId, serviceId) + + navigate({ + to: + APPLICATION_URL(organizationId, projectId, environmentId, serviceId) + APPLICATION_SETTINGS_URL + - APPLICATION_SETTINGS_DOMAIN_URL - ) + APPLICATION_SETTINGS_DOMAIN_URL, + }) } > Domain settings diff --git a/libs/domains/services/feature/src/lib/service-action-toolbar/service-action-toolbar.spec.tsx b/libs/domains/services/feature/src/lib/service-action-toolbar/service-action-toolbar.spec.tsx index 450eacba1d6..91a12e86b4f 100644 --- a/libs/domains/services/feature/src/lib/service-action-toolbar/service-action-toolbar.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-action-toolbar/service-action-toolbar.spec.tsx @@ -1,3 +1,4 @@ +import type { ReactNode } from 'react' import { environmentFactoryMock, helmFactoryMock } from '@qovery/shared/factories' import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { ServiceActionToolbar } from './service-action-toolbar' @@ -5,10 +6,13 @@ import { ServiceActionToolbar } from './service-action-toolbar' const mockHelm = helmFactoryMock(1)[0] const mockEnvironment = environmentFactoryMock(1)[0] -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), useParams: () => ({ organizationId: '123', projectId: '456', environmentId: '789' }), useNavigate: () => jest.fn(), + useLocation: () => ({ pathname: '/', search: '' }), + useRouter: () => ({ buildLocation: () => ({ href: '/' }) }), + Link: ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) => {children}, })) jest.mock('../hooks/use-service/use-service', () => ({ diff --git a/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.spec.tsx b/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.spec.tsx index af96b6be49c..22b6b07b82c 100644 --- a/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.spec.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from 'react-router-dom' +import { useNavigate } from '@tanstack/react-router' import selectEvent from 'react-select-event' import { environmentFactoryMock } from '@qovery/shared/factories' import { renderWithProviders, screen } from '@qovery/shared/util-tests' @@ -34,13 +34,11 @@ jest.mock('../hooks/use-environments/use-environments', () => ({ useEnvironments: () => ({ data: mockEnvironments, isLoading: false }), })) -jest.mock('react-router-dom', () => { - const navigate = jest.fn() - return { - ...jest.requireActual('react-router-dom'), - useNavigate: () => navigate, - } -}) +const mockNavigate = jest.fn() +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useNavigate: () => mockNavigate, +})) const props: ServiceCloneModalProps = { onClose: jest.fn(), @@ -80,8 +78,8 @@ describe('ServiceCloneModal', () => { name: 'test', }, }) - expect(useNavigate()).toHaveBeenCalledWith( - '/organization/0/project/1/environment/2951580907208704/application/1/general' - ) + expect(mockNavigate).toHaveBeenCalledWith({ + to: '/organization/0/project/1/environment/2951580907208704/application/1/general', + }) }) }) diff --git a/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.tsx b/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.tsx index a70d222dd53..d6bc9aff4d5 100644 --- a/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.tsx +++ b/libs/domains/services/feature/src/lib/service-clone-modal/service-clone-modal.tsx @@ -1,6 +1,6 @@ +import { useNavigate } from '@tanstack/react-router' import { type Environment } from 'qovery-typescript-axios' import { Controller, FormProvider, useForm } from 'react-hook-form' -import { useNavigate } from 'react-router-dom' import { match } from 'ts-pattern' import { useProjects } from '@qovery/domains/projects/feature' import { APPLICATION_GENERAL_URL, APPLICATION_URL, DATABASE_GENERAL_URL, DATABASE_URL } from '@qovery/shared/routes' @@ -66,9 +66,9 @@ export function ServiceCloneModal({ onClose, organizationId, projectId, serviceI }) if (service.serviceType === 'DATABASE') { - navigate(DATABASE_URL(organizationId, projectId, environmentId, result.id) + DATABASE_GENERAL_URL) + navigate({ to: DATABASE_URL(organizationId, projectId, environmentId, result.id) + DATABASE_GENERAL_URL }) } else { - navigate(APPLICATION_URL(organizationId, projectId, environmentId, result.id) + APPLICATION_GENERAL_URL) + navigate({ to: APPLICATION_URL(organizationId, projectId, environmentId, result.id) + APPLICATION_GENERAL_URL }) } return onClose() }) diff --git a/libs/domains/services/feature/src/lib/service-details/service-details.tsx b/libs/domains/services/feature/src/lib/service-details/service-details.tsx deleted file mode 100644 index 50b1d10d77c..00000000000 --- a/libs/domains/services/feature/src/lib/service-details/service-details.tsx +++ /dev/null @@ -1,514 +0,0 @@ -import { type ApplicationGitRepository, type Credentials } from 'qovery-typescript-axios' -import { type ComponentPropsWithoutRef } from 'react' -import { useParams } from 'react-router-dom' -import { P, match } from 'ts-pattern' -import { type Application, type Helm, type Job, type Terraform } from '@qovery/domains/services/data-access' -import { - IconEnum, - ServiceTypeEnum, - isHelmGitSource, - isHelmRepositorySource, - isJobContainerSource, - isJobGitSource, -} from '@qovery/shared/enums' -import { - APPLICATION_SETTINGS_GENERAL_URL, - APPLICATION_SETTINGS_RESOURCES_URL, - APPLICATION_SETTINGS_URL, - APPLICATION_SETTINGS_VALUES_OVERRIDE_FILE_URL, -} from '@qovery/shared/routes' -import { - Badge, - Button, - DescriptionDetails as Dd, - DescriptionListRoot as Dl, - DescriptionTerm as Dt, - Icon, - Link, - Section, - ToastEnum, - Tooltip, - Truncate, - toast, -} from '@qovery/shared/ui' -import { dateUTCString, timeAgo } from '@qovery/shared/util-dates' -import { buildGitProviderUrl } from '@qovery/shared/util-git' -import { useCopyToClipboard } from '@qovery/shared/util-hooks' -import { containerRegistryKindToIcon, formatCronExpression, formatMetric, twMerge } from '@qovery/shared/util-js' -import useDeploymentStatus from '../hooks/use-deployment-status/use-deployment-status' -import { useMasterCredentials } from '../hooks/use-master-credentials/use-master-credentials' -import { useService } from '../hooks/use-service/use-service' -import { LastCommitAuthor } from '../last-commit-author/last-commit-author' -import { LastCommit } from '../last-commit/last-commit' -import { getDatabaseConnectionUri } from '../service-access-modal/service-access-modal' -import { ServiceDetailsSkeleton } from './service-details-skeleton' - -function GitRepository({ - service, - gitRepository, -}: { - service: Application | Job | Helm | Terraform - gitRepository: ApplicationGitRepository -}) { - const { organizationId = '', projectId = '' } = useParams() - - return ( - <> - {gitRepository.url && gitRepository.name && ( - <> -
Repository:
-
- - - -
- - )} - {gitRepository.branch && gitRepository.url && ( - <> -
Branch:
-
- - - -
- - )} -
Target commit:
-
-
- - -
-
- - ) -} - -export interface ServiceDetailsProps extends ComponentPropsWithoutRef<'div'> { - environmentId: string - serviceId: string -} - -export function ServiceDetails({ className, environmentId, serviceId, ...props }: ServiceDetailsProps) { - const { data: service } = useService({ - environmentId, - serviceId, - }) - const { data: deploymentStatus } = useDeploymentStatus({ - environmentId, - serviceId, - }) - - const { data: masterCredentials } = useMasterCredentials({ serviceId, serviceType: service?.serviceType }) - const [, copyToClipboard] = useCopyToClipboard() - - if (!service) { - return - } - - const containerImage = match(service) - .with({ serviceType: ServiceTypeEnum.JOB, source: P.when(isJobContainerSource) }, ({ source }) => source.image) - .with({ serviceType: ServiceTypeEnum.CONTAINER }, ({ image_name, tag, registry }) => ({ - image_name, - tag, - registry, - })) - .otherwise(() => undefined) - - const helmRepository = match(service) - .with({ serviceType: 'HELM', source: P.when(isHelmRepositorySource) }, ({ source }) => source.repository) - .otherwise(() => undefined) - - const databaseSource = match(service) - .with({ serviceType: ServiceTypeEnum.DATABASE }, ({ accessibility, mode, type, version }) => ({ - accessibility, - mode, - type, - version, - masterCredentials, - })) - .otherwise(() => undefined) - - const ResourceUnit = ({ value, description }: { value?: string | number | null; description: string }) => { - if (value === undefined || value === null) { - return - } - return ( -
- {value} - {description} -
- ) - } - - const resources = match(service) - .with({ serviceType: ServiceTypeEnum.CONTAINER }, { serviceType: ServiceTypeEnum.APPLICATION }, (s) => { - const { cpu, memory, min_running_instances, max_running_instances, gpu } = s - - // Determine autoscaling mode using the same logic as other parts of the app - let autoscalingMode = 'Fixed' - if (s.autoscaling?.mode === 'KEDA') { - autoscalingMode = 'KEDA' - } else if (min_running_instances !== max_running_instances) { - autoscalingMode = 'HPA' - } - - const isFixed = autoscalingMode === 'Fixed' - - return ( - <> - - - - - - - ) - }) - .with({ serviceType: ServiceTypeEnum.DATABASE }, ({ cpu, memory, storage, instance_type, mode }) => ( - <> - {mode !== 'MANAGED' && ( - <> - - - - )} - - {mode !== 'CONTAINER' && } - - )) - .with({ serviceType: ServiceTypeEnum.JOB }, (job) => { - const { cpu, memory, max_duration_seconds, max_nb_restart, port, gpu } = job - return ( - <> - {match(job) - .with({ job_type: 'LIFECYCLE' }, ({ schedule }) => ( - - )) - .with({ job_type: 'CRON' }, ({ schedule }) => ( - - )) - .exhaustive()} - - - - - - - - ) - }) - .otherwise(() => null) - - const valuesOverride = match(service) - .with({ serviceType: 'HELM' }, (service) => { - const { - values_override: { file, set, set_json, set_string }, - } = service - const overrideWithArguments = ( - <> -
Override with arguments:
-
{set?.length || set_json?.length || set_string?.length ? 'Yes' : 'No'}
- - ) - if (file?.git?.git_repository) { - return ( -
-
Type:
-
Git repository
- - {overrideWithArguments} -
- ) - } else if (file?.raw) { - return ( -
-
Type:
-
Raw YAML
- {overrideWithArguments} -
- ) - } else { - return
{overrideWithArguments}
- } - }) - .otherwise(() => null) - - const sectionClassName = 'text-neutral-350 gap-4 pl-8 pr-5' - - const handleCopyCredentials = (credentials: Credentials) => { - if (!databaseSource) { - return - } - const connectionURI = getDatabaseConnectionUri(databaseSource, credentials) - copyToClipboard(connectionURI) - toast(ToastEnum.SUCCESS, 'Credentials copied to clipboard') - } - - return ( -
-
-
- {/* XXX: Should be Heading, typography & design wanted */} - About - - - -
-

{service.description || 'No description provided yet'}

-
- {deploymentStatus?.last_deployment_date && ( - <> -
Last deployment:
-
- - {timeAgo(new Date(deploymentStatus.last_deployment_date))} - -
- - )} - {service.updated_at && ( - <> -
Last edit:
-
- - {timeAgo(new Date(service.updated_at))} - -
- - )} -
Created:
-
- - {timeAgo(new Date(service.created_at))} - -
-
-
-
-
-
- {/* XXX: Should be Heading, typography & design wanted */} - Source - - - -
- {match(service) - .with( - { serviceType: 'APPLICATION' }, - { - serviceType: 'JOB', - source: P.when(isJobGitSource), - }, - { - serviceType: 'TERRAFORM', - }, - { - serviceType: 'HELM', - source: P.when(isHelmGitSource), - }, - (service) => { - const gitRepository = match(service) - .with({ serviceType: 'APPLICATION' }, ({ git_repository }) => git_repository) - .with({ serviceType: 'JOB' }, ({ source }) => source.docker?.git_repository) - .with({ serviceType: 'HELM' }, ({ source }) => source.git?.git_repository) - .with( - { serviceType: 'TERRAFORM' }, - ({ terraform_files_source }) => terraform_files_source?.git?.git_repository - ) - .exhaustive() - - if (!gitRepository) { - return null - } - - return ( -
- -
- ) - } - ) - .otherwise(() => undefined)} - {containerImage && ( -
- {containerImage.registry && ( - <> -
Registry:
-
- - - - - - - - -
- - )} -
Image name:
-
{containerImage.image_name}
-
Tag:
-
- -
-
- )} - {helmRepository && ( -
- {helmRepository.repository && ( - <> -
Repository:
-
- - - - - - -
- - )} -
Chart name:
-
{helmRepository.chart_name}
-
Target version:
-
{helmRepository.chart_version}
-
- )} - {databaseSource && ( -
-
Type:
-
- - - {databaseSource.version} - -
-
Mode:
-
{databaseSource.mode.toLowerCase()}
-
Accessibility:
-
{databaseSource.accessibility?.toLowerCase()}
- {databaseSource.masterCredentials && ( - <> -
Access:
-
- -
- - )} -
- )} -
- {resources && ( - <> -
-
-
- {/* XXX: Should be Heading, typography & design wanted */} - - Resources - - - - - - - - - -
-
{resources}
-
- - )} - {valuesOverride && ( - <> -
-
-
- {/* XXX: Should be Heading, typography & design wanted */} - Values override - - - -
- {valuesOverride} -
- - )} -
- ) -} - -export default ServiceDetails 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 0829bffd23b..26f8c47998f 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 @@ -230,7 +230,8 @@ exports[`ServiceList should match snapshot 1`] = ` FRONT-END @@ -573,7 +574,8 @@ exports[`ServiceList should match snapshot 1`] = ` back-end-A @@ -1213,7 +1215,8 @@ exports[`ServiceList should match snapshot 1`] = ` CRONJOB @@ -1660,7 +1663,8 @@ exports[`ServiceList should match snapshot 1`] = ` seed_script diff --git a/libs/domains/services/feature/src/lib/service-list/service-list.spec.tsx b/libs/domains/services/feature/src/lib/service-list/service-list.spec.tsx index 3aa5aef26e0..38178b8288a 100644 --- a/libs/domains/services/feature/src/lib/service-list/service-list.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-list/service-list.spec.tsx @@ -371,10 +371,6 @@ jest.mock('../hooks/use-links/use-links', () => ({ })) const mockNavigate = jest.fn() -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useNavigate: () => mockNavigate, -})) jest.mock('@tanstack/react-router', () => ({ ...jest.requireActual('@tanstack/react-router'), useNavigate: () => mockNavigate, diff --git a/libs/domains/services/feature/src/lib/service-overview/observability-callout.tsx b/libs/domains/services/feature/src/lib/service-overview/observability-callout.tsx new file mode 100644 index 00000000000..444b63b8af0 --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/observability-callout.tsx @@ -0,0 +1,164 @@ +import { motion } from 'framer-motion' +import { type ReactNode } from 'react' +import { Button, Icon, useModal } from '@qovery/shared/ui' +import { useLocalStorage } from '@qovery/shared/util-hooks' + +export interface ObservabilityCalloutProps { + /** Modal content when user clicks "Discover feature" (e.g. EnableObservabilityModal). Injected by route to avoid circular dep. */ + discoverModalContent?: ReactNode +} + +export function ObservabilityCallout({ discoverModalContent }: ObservabilityCalloutProps = {}) { + const [isOpen, setIsOpen] = useLocalStorage('observability-callout-open', true) + const { openModal } = useModal() + + if (!isOpen) return null + + return ( +
+ + + + + + + + + + + + + + + +
+
+ {Array.from({ length: 10 }).map((_, index) => ( + + + + ))} +
+
+ {Array.from({ length: 5 }).map((_, index) => ( + + + + + + + + + + ))} +
+
+ +
+
+

+ + + + + Observabilty is here! +

+

+ Designed to make troubleshooting and observability accessible to everyone on your team! +

+
+ +
+ +
+ ) +} diff --git a/libs/domains/services/feature/src/lib/service-details/__snapshots__/service-details.spec.tsx.snap b/libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap similarity index 100% rename from libs/domains/services/feature/src/lib/service-details/__snapshots__/service-details.spec.tsx.snap rename to libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap diff --git a/libs/domains/services/feature/src/lib/service-details/service-details-skeleton.tsx b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header-skeleton.tsx similarity index 87% rename from libs/domains/services/feature/src/lib/service-details/service-details-skeleton.tsx rename to libs/domains/services/feature/src/lib/service-overview/service-header/service-header-skeleton.tsx index e86ce117856..352b01adb06 100644 --- a/libs/domains/services/feature/src/lib/service-details/service-details-skeleton.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header-skeleton.tsx @@ -2,9 +2,9 @@ import { type ComponentPropsWithoutRef } from 'react' import { DescriptionDetails as Dd, DescriptionListRoot as Dl, DescriptionTerm as Dt, Skeleton } from '@qovery/shared/ui' import { twMerge } from '@qovery/shared/util-js' -interface ServiceDetailsSkeletonProps extends ComponentPropsWithoutRef<'div'> {} +interface ServiceHeaderSkeletonProps extends ComponentPropsWithoutRef<'div'> {} -export function ServiceDetailsSkeleton({ className, ...props }: ServiceDetailsSkeletonProps) { +export function ServiceHeaderSkeleton({ className, ...props }: ServiceHeaderSkeletonProps) { return (
@@ -48,4 +48,4 @@ export function ServiceDetailsSkeleton({ className, ...props }: ServiceDetailsSk ) } -export default ServiceDetailsSkeleton +export default ServiceHeaderSkeleton diff --git a/libs/domains/services/feature/src/lib/service-details/service-details.spec.tsx b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.spec.tsx similarity index 94% rename from libs/domains/services/feature/src/lib/service-details/service-details.spec.tsx rename to libs/domains/services/feature/src/lib/service-overview/service-header/service-header.spec.tsx index 90a05a4da33..ca42516ebf9 100644 --- a/libs/domains/services/feature/src/lib/service-details/service-details.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.spec.tsx @@ -1,5 +1,12 @@ +import type { ReactNode } from 'react' import { renderWithProviders } from '@qovery/shared/util-tests' -import { ServiceDetails } from './service-details' +import { ServiceDetails } from './service-header' + +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useParams: () => ({ organizationId: '', projectId: '' }), + Link: ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) => {children}, +})) jest.mock('../hooks/use-service/use-service', () => ({ useService: ({ diff --git a/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx new file mode 100644 index 00000000000..2880b4da36c --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx @@ -0,0 +1,463 @@ +import { useParams } from '@tanstack/react-router' +import { type ApplicationGitRepository, type Credentials } from 'qovery-typescript-axios' +import { type ComponentPropsWithoutRef } from 'react' +import { P, match } from 'ts-pattern' +import { + IconEnum, + ServiceTypeEnum, + isHelmGitSource, + isHelmRepositorySource, + isJobContainerSource, + isJobGitSource, +} from '@qovery/shared/enums' +import { + APPLICATION_SETTINGS_RESOURCES_URL, + APPLICATION_SETTINGS_URL, + APPLICATION_SETTINGS_VALUES_OVERRIDE_FILE_URL, +} from '@qovery/shared/routes' +import { + Badge, + Button, + DescriptionDetails as Dd, + DescriptionListRoot as Dl, + DescriptionTerm as Dt, + Heading, + Icon, + Link, + Section, + StatusChip, + ToastEnum, + Tooltip, + Truncate, + toast, +} from '@qovery/shared/ui' +import { buildGitProviderUrl } from '@qovery/shared/util-git' +import { useCopyToClipboard } from '@qovery/shared/util-hooks' +import { + containerRegistryKindToIcon, + formatCronExpression, + formatMetric, + twMerge, + upperCaseFirstLetter, +} from '@qovery/shared/util-js' +import { useMasterCredentials } from '../../hooks/use-master-credentials/use-master-credentials' +import { useRunningStatus } from '../../hooks/use-running-status/use-running-status' +import { useService } from '../../hooks/use-service/use-service' +import { getDatabaseConnectionUri } from '../../service-access-modal/service-access-modal' +import { ServiceAvatar } from '../../service-avatar/service-avatar' +import { ServiceLinksPopover } from '../../service-links-popover/service-links-popover' +import { ServiceHeaderSkeleton } from './service-header-skeleton' + +function GitRepository({ gitRepository }: { gitRepository: ApplicationGitRepository }) { + return ( + <> + {gitRepository.provider && ( + + + {match(gitRepository.provider) + .with('GITHUB', () => 'GitHub') + .with('GITLAB', () => 'GitLab') + .with('BITBUCKET', () => 'Bitbucket') + .otherwise(() => upperCaseFirstLetter(gitRepository.provider))} + + )} + {gitRepository.url && gitRepository.name && ( + + + + )} + {gitRepository.branch && gitRepository.url && ( + + + + )} + + ) +} + +export interface ServiceHeaderProps extends ComponentPropsWithoutRef<'div'> { + environmentId: string + serviceId: string +} + +export function ServiceHeader({ className, environmentId, serviceId, ...props }: ServiceHeaderProps) { + const { organizationId = '', projectId = '' } = useParams({ strict: false }) + + const { data: service } = useService({ environmentId, serviceId }) + const { data: masterCredentials } = useMasterCredentials({ serviceId, serviceType: service?.serviceType }) + const { data: runningStatus } = useRunningStatus({ environmentId, serviceId }) + + const [, copyToClipboard] = useCopyToClipboard() + + if (!service) { + return + } + + const containerImage = match(service) + .with({ serviceType: ServiceTypeEnum.JOB, source: P.when(isJobContainerSource) }, ({ source }) => source.image) + .with({ serviceType: ServiceTypeEnum.CONTAINER }, ({ image_name, tag, registry }) => ({ + image_name, + tag, + registry, + })) + .otherwise(() => undefined) + + const helmRepository = match(service) + .with({ serviceType: 'HELM', source: P.when(isHelmRepositorySource) }, ({ source }) => source.repository) + .otherwise(() => undefined) + + const databaseSource = match(service) + .with({ serviceType: ServiceTypeEnum.DATABASE }, ({ accessibility, mode, type, version }) => ({ + accessibility, + mode, + type, + version, + masterCredentials, + })) + .otherwise(() => undefined) + + const ResourceUnit = ({ value, description }: { value?: string | number | null; description: string }) => { + if (value === undefined || value === null) { + return + } + return ( +
+ {value} + {description} +
+ ) + } + + const resources = match(service) + .with({ serviceType: ServiceTypeEnum.CONTAINER }, { serviceType: ServiceTypeEnum.APPLICATION }, (s) => { + const { cpu, memory, min_running_instances, max_running_instances, gpu } = s + + // Determine autoscaling mode using the same logic as other parts of the app + let autoscalingMode = 'Fixed' + if (s.autoscaling?.mode === 'KEDA') { + autoscalingMode = 'KEDA' + } else if (min_running_instances !== max_running_instances) { + autoscalingMode = 'HPA' + } + + const isFixed = autoscalingMode === 'Fixed' + + return ( + <> + + + + + + + ) + }) + .with({ serviceType: ServiceTypeEnum.DATABASE }, ({ cpu, memory, storage, instance_type, mode }) => ( + <> + {mode !== 'MANAGED' && ( + <> + + + + )} + + {mode !== 'CONTAINER' && } + + )) + .with({ serviceType: ServiceTypeEnum.JOB }, (job) => { + const { cpu, memory, max_duration_seconds, max_nb_restart, port, gpu } = job + return ( + <> + {match(job) + .with({ job_type: 'LIFECYCLE' }, ({ schedule }) => ( + + )) + .with({ job_type: 'CRON' }, ({ schedule }) => ( + + )) + .exhaustive()} + + + + + + + + ) + }) + .otherwise(() => null) + + const valuesOverride = match(service) + .with({ serviceType: 'HELM' }, (service) => { + const { + values_override: { file, set, set_json, set_string }, + } = service + const overrideWithArguments = ( + Override with arguments: {set?.length || set_json?.length || set_string?.length ? 'Yes' : 'No'} + ) + + if (file?.git?.git_repository) { + return ( + + Type: Git repository + + {overrideWithArguments} + + ) + } else if (file?.raw) { + return ( + + Type: Raw YAML + {overrideWithArguments} + + ) + } else { + return {overrideWithArguments} + } + }) + .otherwise(() => null) + + const sectionClassName = 'text-neutral gap-4 pl-8 pr-5' + + const handleCopyCredentials = (credentials: Credentials) => { + if (!databaseSource) { + return + } + const connectionURI = getDatabaseConnectionUri(databaseSource, credentials) + copyToClipboard(connectionURI) + toast(ToastEnum.SUCCESS, 'Credentials copied to clipboard') + } + + return ( + <> +
+
+
+ + {service.name} + +
+ {service.description &&

{service.description}

} +
+ {match(service) + .with( + { serviceType: 'APPLICATION' }, + { + serviceType: 'JOB', + source: P.when(isJobGitSource), + }, + { + serviceType: 'TERRAFORM', + }, + { + serviceType: 'HELM', + source: P.when(isHelmGitSource), + }, + (service) => { + const gitRepository = match(service) + .with({ serviceType: 'APPLICATION' }, ({ git_repository }) => git_repository) + .with({ serviceType: 'JOB' }, ({ source }) => source.docker?.git_repository) + .with({ serviceType: 'HELM' }, ({ source }) => source.git?.git_repository) + .with( + { serviceType: 'TERRAFORM' }, + ({ terraform_files_source }) => terraform_files_source?.git?.git_repository + ) + .exhaustive() + + if (!gitRepository) { + return null + } + + return + } + ) + .otherwise(() => undefined)} + {containerImage && ( + <> + {containerImage.registry && ( + + + + + + + + + )} + + {containerImage.image_name} + + + + + + )} + {helmRepository && ( +
+ {helmRepository.repository && ( + <> +
Repository:
+
+ + + + + + +
+ + )} +
Chart name:
+
{helmRepository.chart_name}
+
Target version:
+
{helmRepository.chart_version}
+
+ )} + {databaseSource && ( +
+
Type:
+
+ + + {databaseSource.version} + +
+
Mode:
+
{databaseSource.mode.toLowerCase()}
+
Accessibility:
+
{databaseSource.accessibility?.toLowerCase()}
+ {databaseSource.masterCredentials && ( + <> +
Access:
+
+ +
+ + )} +
+ )} + + + +
+
+
+
+ {resources && ( + <> +
+
+
+ {/* XXX: Should be Heading, typography & design wanted */} + + Resources + + + + + + + + + +
+
{resources}
+
+ + )} + {valuesOverride && ( + <> +
+
+
+ {/* XXX: Should be Heading, typography & design wanted */} + Values override + + + +
+ {valuesOverride} +
+ + )} + + ) +} + +export default ServiceHeader diff --git a/libs/domains/services/feature/src/lib/service-overview/service-last-deployment/service-last-deployment.tsx b/libs/domains/services/feature/src/lib/service-overview/service-last-deployment/service-last-deployment.tsx new file mode 100644 index 00000000000..d6489786289 --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-last-deployment/service-last-deployment.tsx @@ -0,0 +1,151 @@ +import { Link, useParams } from '@tanstack/react-router' +import { type ApplicationGitRepository } from 'qovery-typescript-axios' +import { P, match } from 'ts-pattern' +import { type AnyService } from '@qovery/domains/services/data-access' +import { isHelmGitSource, isJobGitSource } from '@qovery/shared/enums' +import { Button, CopyToClipboard, DeploymentAction, EmptyState, Icon, StatusChip, Tooltip } from '@qovery/shared/ui' +import { dateUTCString, timeAgo } from '@qovery/shared/util-dates' +import { useDeploymentHistory } from '../../hooks/use-deployment-history/use-deployment-history' +import { LastCommitAuthor, type LastCommitAuthorProps } from '../../last-commit-author/last-commit-author' +import { LastCommit, type LastCommitProps } from '../../last-commit/last-commit' +import { isDeploymentHistory } from '../../service-deployment-list/service-deployment-list' + +const DotSeparator = () => ( + + + +) + +function getGitRepository(service: AnyService): ApplicationGitRepository | undefined { + return match(service) + .with({ serviceType: 'APPLICATION' }, ({ git_repository }) => git_repository) + .with({ serviceType: 'JOB', source: P.when(isJobGitSource) }, ({ source }) => source.docker?.git_repository) + .with({ serviceType: 'HELM', source: P.when(isHelmGitSource) }, ({ source }) => source.git?.git_repository) + .with({ serviceType: 'TERRAFORM' }, ({ terraform_files_source }) => terraform_files_source?.git?.git_repository) + .otherwise(() => undefined) +} + +export interface ServiceLastDeploymentProps { + serviceId: string + serviceType: Parameters[0]['serviceType'] + service?: AnyService +} + +export function ServiceLastDeployment({ serviceId, serviceType, service }: ServiceLastDeploymentProps) { + const { organizationId = '', projectId = '', environmentId: routeEnvironmentId = '' } = useParams({ strict: false }) + const environmentId = service?.environment?.id ?? routeEnvironmentId + const { data: deploymentHistory = [] } = useDeploymentHistory({ serviceId, serviceType }) + + const lastDeployment = deploymentHistory[0] + const gitRepository = service ? getGitRepository(service) : undefined + const showGitCommit = + Boolean(gitRepository) && + Boolean(service?.id && service?.name && service?.serviceType && 'environment' in service && service.environment) + + if (!lastDeployment) { + return ( + + + + ) + } + + const versionPill = isDeploymentHistory(lastDeployment) + ? match(lastDeployment.details) + .with({ repository: P.select({ chart_name: P.string, chart_version: P.string }) }, ({ chart_version }) => ( + + + + )) + .with({ image_name: P.string, tag: P.string }, ({ tag }) => ( + + + + x + + + )) + .otherwise(() => null) + : null + + const gitBlock = + showGitCommit && gitRepository && service ? ( + e.stopPropagation()} className="inline-flex items-center gap-2.5"> + + + {'created_at' in (lastDeployment.auditing_data ?? {}) && lastDeployment.auditing_data.created_at && ( + <> + + + Lasted{' '} + + {timeAgo(new Date(lastDeployment.auditing_data.created_at))} + + + + )} + + + + ) : null + + return ( + +
+ + + + + {showGitCommit ? ( + gitBlock + ) : versionPill ? ( + <> + + {versionPill} + + ) : null} +
+ + ) +} diff --git a/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.spec.tsx b/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.spec.tsx new file mode 100644 index 00000000000..106ef4ce03d --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.spec.tsx @@ -0,0 +1,56 @@ +import { renderWithProviders, screen } from '@qovery/shared/util-tests' +import * as useServiceImport from '../hooks/use-service/use-service' +import { ServiceOverviewFeature } from './service-overview-feature' + +jest.mock('@qovery/shared/util-web-sockets', () => ({ + MetricsWebSocketListener: () => null, +})) + +jest.mock('./service-overview', () => ({ + ServiceOverview: () =>
ServiceOverview
, +})) + +describe('ServiceOverviewFeature', () => { + it('should render nothing when service is not loaded', () => { + jest.spyOn(useServiceImport, 'useService').mockReturnValue({ + data: undefined, + isLoading: true, + error: null, + isError: false, + }) + renderWithProviders( + + ) + expect(screen.queryByTestId('service-overview')).not.toBeInTheDocument() + }) + + it('should render ServiceOverview when service is loaded', () => { + jest.spyOn(useServiceImport, 'useService').mockReturnValue({ + data: { + id: 'svc-1', + serviceType: 'APPLICATION', + name: 'my-app', + }, + isLoading: false, + error: null, + isError: false, + }) + renderWithProviders( + + ) + expect(screen.getByTestId('service-overview')).toBeInTheDocument() + }) + + it('should render nothing when serviceId is empty', () => { + jest.spyOn(useServiceImport, 'useService').mockReturnValue({ + data: { id: 'svc-1', serviceType: 'APPLICATION', name: 'my-app' }, + isLoading: false, + error: null, + isError: false, + }) + renderWithProviders( + + ) + expect(screen.queryByTestId('service-overview')).not.toBeInTheDocument() + }) +}) diff --git a/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.tsx b/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.tsx new file mode 100644 index 00000000000..2c29a54aa48 --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-overview-feature.tsx @@ -0,0 +1,66 @@ +import { type ReactNode, memo } from 'react' +import { type AnyService } from '@qovery/domains/services/data-access' +import { MetricsWebSocketListener } from '@qovery/shared/util-web-sockets' +import { useService } from '../hooks/use-service/use-service' +import { ServiceOverview } from './service-overview' + +const WebSocketListenerMemo = memo(MetricsWebSocketListener) + +export interface ServiceOverviewFeatureProps { + organizationId?: string + projectId?: string + environmentId?: string + serviceId?: string + /** Service data (from useService). When not provided, feature uses useService(environmentId, serviceId). */ + service?: AnyService + /** Environment data (from useEnvironment). Required for WebSocket; pass from route to avoid circular dep. */ + environment?: { cluster_id: string } + /** Whether cluster has no metrics (from cluster + service). Pass from route to avoid circular dep. */ + hasNoMetrics?: boolean + /** Optional slot for Terraform "Infrastructure Resources" tab (injected by route to avoid circular dep). */ + terraformResourcesSection?: ReactNode + /** Optional modal content for Observability callout (injected by route to avoid circular dep). */ + observabilityCalloutModalContent?: ReactNode +} + +export function ServiceOverviewFeature({ + organizationId = '', + projectId = '', + environmentId = '', + serviceId = '', + service: serviceProp, + environment, + hasNoMetrics = false, + terraformResourcesSection, + observabilityCalloutModalContent, +}: ServiceOverviewFeatureProps) { + const { data: serviceFromHook } = useService({ environmentId, serviceId }) + const service = serviceProp ?? serviceFromHook + + if (!service || !serviceId || !environmentId) { + return null + } + + return ( + <> + + {environment && ( + + )} + + ) +} diff --git a/libs/domains/services/feature/src/lib/service-overview/service-overview.spec.tsx b/libs/domains/services/feature/src/lib/service-overview/service-overview.spec.tsx new file mode 100644 index 00000000000..041533d81fd --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-overview.spec.tsx @@ -0,0 +1,84 @@ +import { DatabaseModeEnum } from 'qovery-typescript-axios' +import { type ReactNode } from 'react' +import { type AnyService } from '@qovery/domains/services/data-access' +import { renderWithProviders, screen } from '@qovery/shared/util-tests' +import { ServiceOverview } from './service-overview' + +jest.mock('posthog-js/react', () => ({ + useFeatureFlagVariantKey: jest.fn(() => undefined), +})) + +jest.mock('../pod-statuses-callout/pod-statuses-callout', () => ({ + PodStatusesCallout: () =>
PodStatusesCallout
, +})) +jest.mock('../pods-metrics/pods-metrics', () => ({ + PodsMetrics: ({ children }: { children?: ReactNode }) =>
PodsMetrics{children}
, +})) +jest.mock('../service-details/service-details', () => ({ + ServiceDetails: () =>
ServiceDetails
, +})) +jest.mock('../keda/scaled-object-status/scaled-object-status', () => ({ + ScaledObjectStatus: () =>
ScaledObjectStatus
, +})) +jest.mock('@qovery/domains/variables/feature', () => ({ + OutputVariables: () =>
OutputVariables
, +})) +jest.mock('@qovery/domains/service-terraform/feature', () => ({ + TerraformResourcesSection: () =>
TerraformResourcesSection
, +})) +jest.mock('./observability-callout', () => ({ + ObservabilityCallout: () =>
ObservabilityCallout
, +})) + +const databaseManagedService: AnyService = { + id: 'db-1', + serviceType: 'DATABASE', + name: 'my-db', + mode: DatabaseModeEnum.MANAGED, +} as AnyService + +const databaseContainerService: AnyService = { + id: 'db-2', + serviceType: 'DATABASE', + name: 'my-db', + mode: DatabaseModeEnum.CONTAINER, +} as AnyService + +const applicationService: AnyService = { + id: 'app-1', + serviceType: 'APPLICATION', + name: 'my-app', +} as AnyService + +describe('ServiceOverview', () => { + it('should render managed database message when database is managed', () => { + renderWithProviders() + expect(screen.getByText(/Metrics for managed databases are not available/i)).toBeInTheDocument() + expect(screen.getByTestId('service-details')).toBeInTheDocument() + }) + + it('should render PodStatusesCallout and PodsMetrics for container database', () => { + renderWithProviders() + expect(screen.getByTestId('pod-statuses-callout')).toBeInTheDocument() + expect(screen.getByTestId('pods-metrics')).toBeInTheDocument() + expect(screen.getByTestId('service-details')).toBeInTheDocument() + }) + + it('should render application overview with ObservabilityCallout when hasNoMetrics', () => { + renderWithProviders( + + ) + expect(screen.getByTestId('observability-callout')).toBeInTheDocument() + expect(screen.getByTestId('pod-statuses-callout')).toBeInTheDocument() + expect(screen.getByTestId('pods-metrics')).toBeInTheDocument() + expect(screen.getByTestId('service-details')).toBeInTheDocument() + }) + + it('should render application overview without ObservabilityCallout when hasNoMetrics is false', () => { + renderWithProviders( + + ) + expect(screen.queryByTestId('observability-callout')).not.toBeInTheDocument() + expect(screen.getByTestId('service-details')).toBeInTheDocument() + }) +}) diff --git a/libs/domains/services/feature/src/lib/service-overview/service-overview.tsx b/libs/domains/services/feature/src/lib/service-overview/service-overview.tsx new file mode 100644 index 00000000000..a2dea862242 --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/service-overview.tsx @@ -0,0 +1,139 @@ +import { useFeatureFlagVariantKey } from 'posthog-js/react' +import { DatabaseModeEnum } from 'qovery-typescript-axios' +import { type ReactNode, useMemo, useState } from 'react' +import { type AnyService } from '@qovery/domains/services/data-access' +import { OutputVariables } from '@qovery/domains/variables/feature' +import { ExternalLink, Heading, Icon, Section, TabsPrimitives } from '@qovery/shared/ui' +import { ScaledObjectStatus } from '../keda/scaled-object-status/scaled-object-status' +import { PodStatusesCallout } from '../pod-statuses-callout/pod-statuses-callout' +import { PodsMetrics } from '../pods-metrics/pods-metrics' +import { ObservabilityCallout } from './observability-callout' +import { ServiceHeader } from './service-header/service-header' +import { ServiceLastDeployment } from './service-last-deployment/service-last-deployment' + +const { Tabs } = TabsPrimitives + +export interface ServiceOverviewProps { + serviceId: string + environmentId: string + service: AnyService + hasNoMetrics?: boolean + /** Optional slot for Terraform "Infrastructure Resources" tab content (avoids circular dep with service-terraform). */ + terraformResourcesSection?: ReactNode + /** Optional modal content for Observability callout "Discover feature" (avoids circular dep with observability). */ + observabilityCalloutModalContent?: ReactNode +} + +export function ServiceOverview({ + serviceId, + environmentId, + service, + hasNoMetrics = false, + terraformResourcesSection, + observabilityCalloutModalContent, +}: ServiceOverviewProps) { + const [activeTab, setActiveTab] = useState('variables') + const isKedaFeatureEnabled = useFeatureFlagVariantKey('keda') + + const isDatabaseManaged = useMemo( + () => + service?.serviceType === 'DATABASE' && + (service as { mode?: DatabaseModeEnum })?.mode === DatabaseModeEnum.MANAGED, + [service] + ) + const isLifecycleJob = useMemo(() => service?.serviceType === 'JOB' && service.job_type === 'LIFECYCLE', [service]) + const isTerraformService = useMemo(() => service?.serviceType === 'TERRAFORM', [service]) + const isCronJob = useMemo(() => service?.serviceType === 'JOB' && service.job_type === 'CRON', [service]) + const isKedaAutoscaling = useMemo( + () => + isKedaFeatureEnabled && + (service?.serviceType === 'APPLICATION' || service?.serviceType === 'CONTAINER') && + service.autoscaling?.mode === 'KEDA', + [service, isKedaFeatureEnabled] + ) + const showApplicationOverview = useMemo( + () => + service?.serviceType === 'APPLICATION' || + service?.serviceType === 'CONTAINER' || + service?.serviceType === 'JOB' || + service?.serviceType === 'TERRAFORM', + [service?.serviceType] + ) + + if (service?.serviceType === 'DATABASE') { + return ( +
+ + {isDatabaseManaged ? ( +
+ Metrics for managed databases are not available + Check your cloud provider console to get more information +
+ ) : ( + <> + + + + )} +
+ ) + } + + if (!showApplicationOverview) { + return null + } + + return ( +
+
+
+ +
+ Last deployment + +
+
+ + {hasNoMetrics && } + + {!isTerraformService && ( + + {isCronJob && ( +
+ +

+ The number of past Completed or Failed job execution retained in the history and their TTL can be + customized in the advanced settings. +

+ + See documentation + +
+ )} +
+ )} + {isKedaAutoscaling && } + {isLifecycleJob && } + {isTerraformService && ( + + + Output Variables + Infrastructure Resources + + + + + {terraformResourcesSection ?? null} + + )} +
+
+ ) +} diff --git a/libs/domains/services/feature/src/lib/service-terminal/service-terminal-provider.tsx b/libs/domains/services/feature/src/lib/service-terminal/service-terminal-provider.tsx index 6c25294bbd5..fbfc924cf1b 100644 --- a/libs/domains/services/feature/src/lib/service-terminal/service-terminal-provider.tsx +++ b/libs/domains/services/feature/src/lib/service-terminal/service-terminal-provider.tsx @@ -1,5 +1,5 @@ +import { useLocation } from '@tanstack/react-router' import { type PropsWithChildren, createContext, useState } from 'react' -import { useLocation } from 'react-router-dom' export const ServiceTerminalContext = createContext<{ open: boolean diff --git a/libs/shared/ui/src/lib/components/deployment-action/deployment-action.tsx b/libs/shared/ui/src/lib/components/deployment-action/deployment-action.tsx index 11d7e52ce8f..173b5f9f41e 100644 --- a/libs/shared/ui/src/lib/components/deployment-action/deployment-action.tsx +++ b/libs/shared/ui/src/lib/components/deployment-action/deployment-action.tsx @@ -1,4 +1,4 @@ -import { StateEnum } from 'qovery-typescript-axios' +import { ServiceActionStatusEnum, StateEnum } from 'qovery-typescript-axios' import { forwardRef } from 'react' import { match } from 'ts-pattern' import { twMerge } from '@qovery/shared/util-js' @@ -117,7 +117,7 @@ export const StopIcon = forwardRef(function ({ clas ) }) -export const getDeploymentAction = (status: StateEnum | undefined) => { +export const getDeploymentAction = (status: StateEnum | ServiceActionStatusEnum | undefined) => { return match(status) .with( StateEnum.QUEUED, @@ -173,15 +173,25 @@ export const getDeploymentAction = (status: StateEnum | undefined) => { icon: , }) ) + .with( + ServiceActionStatusEnum.ONGOING, + ServiceActionStatusEnum.SUCCESS, + ServiceActionStatusEnum.ERROR, + ServiceActionStatusEnum.NEVER, + () => ({ + status: 'Deploy', + icon: , + }) + ) .exhaustive() } -export const DeploymentAction = ({ status }: { status: StateEnum | undefined }) => { +export const DeploymentAction = ({ status }: { status: StateEnum | ServiceActionStatusEnum | undefined }) => { const action = getDeploymentAction(status) if (!status || !action) return null return ( -
+
{action.icon} {action.status}
diff --git a/libs/shared/ui/src/lib/components/empty-state/empty-state.tsx b/libs/shared/ui/src/lib/components/empty-state/empty-state.tsx index 3762d2cd7e2..da3be10ecc9 100644 --- a/libs/shared/ui/src/lib/components/empty-state/empty-state.tsx +++ b/libs/shared/ui/src/lib/components/empty-state/empty-state.tsx @@ -1,4 +1,4 @@ -import { type IconName } from '@fortawesome/fontawesome-common-types' +import { type IconName, type IconStyle } from '@fortawesome/fontawesome-common-types' import { type PropsWithChildren, type ReactNode } from 'react' import { twMerge } from '@qovery/shared/util-js' import { Icon } from '../icon/icon' @@ -6,11 +6,12 @@ import { Icon } from '../icon/icon' export interface EmptyStateProps extends PropsWithChildren { title: string icon?: IconName | ReactNode + iconStyle?: IconStyle description?: string className?: string } -export function EmptyState({ title, description, className, icon, children }: EmptyStateProps) { +export function EmptyState({ title, description, className, icon, iconStyle, children }: EmptyStateProps) { return (
- + Date: Tue, 24 Feb 2026 17:58:46 +0100 Subject: [PATCH 03/20] refactor(service-overview): update service overview component with observability features and improve UI consistency - Replaced ServiceOverviewFeature with ServiceOverview and added ObservabilityCallout for enhanced observability integration. - Introduced MetricsWebSocketListener for real-time metrics updates. - Updated styling for various components to ensure consistency across the UI. - Refactored error handling and loading states in TerraformResourcesSection and ResourceDetails components. - Enhanced user feedback in ResourceTreeList and AutoDeployBadge components with improved class names and visual cues. --- apps/console-v5/src/routeTree.gen.ts | 839 ++++++++++-------- .../service/$serviceId/overview.tsx | 46 +- .../enable-observability-modal.tsx | 10 +- .../lib/resource-details/resource-details.tsx | 29 +- .../resource-tree-list.spec.tsx | 10 +- .../resource-tree-list/resource-tree-list.tsx | 34 +- .../terraform-resources-section.tsx | 51 +- libs/domains/services/feature/src/index.ts | 7 +- .../application-settings-resources.tsx | 30 +- .../fixed-instances-mode.tsx | 1 - .../hpa-autoscaling-mode.tsx | 3 +- .../auto-deploy-badge/auto-deploy-badge.tsx | 14 +- .../scaled-object-status.tsx | 122 +-- .../src/lib/pod-details/pod-details.tsx | 6 +- .../service-links-popover.spec.tsx.snap | 12 +- .../service-links-popover.tsx | 44 +- .../instance-metrics.spec.tsx.snap | 268 ++++++ .../instance-metrics}/empty-state.tsx | 12 +- .../instance-metrics-skeleton.tsx} | 6 +- .../instance-metrics.spec.tsx} | 16 +- .../instance-metrics/instance-metrics.tsx} | 88 +- .../observability-callout.tsx | 19 +- .../service-details.spec.tsx.snap | 286 ------ .../service-header-skeleton.tsx | 51 -- .../service-header/service-header.spec.tsx | 132 ++- .../service-header/service-header.tsx | 553 ++++-------- .../service-instance.spec.tsx | 120 +++ .../service-instance/service-instance.tsx | 159 ++++ .../service-last-deployment.spec.tsx | 113 +++ .../service-last-deployment.tsx | 99 ++- .../service-overview-feature.spec.tsx | 56 -- .../service-overview-feature.tsx | 66 -- .../service-overview.spec.tsx | 191 ++-- .../lib/service-overview/service-overview.tsx | 194 ++-- .../output-variables.spec.tsx.snap | 208 ++++- .../lib/output-variables/output-variables.tsx | 28 +- .../src/lib/ui/page-general/page-general.tsx | 22 +- .../page-settings-resources.tsx | 3 +- .../step-resources/step-resources.tsx | 2 +- .../step-resources/step-resources.tsx | 2 +- libs/shared/console-shared/src/index.ts | 1 - .../application-settings-resources.spec.tsx | 23 - .../copy-to-clipboard-button-icon.spec.tsx | 3 +- .../copy-to-clipboard-button-icon.tsx | 5 +- .../table-primitives/table-primitives.tsx | 26 +- .../src/lib/get-service-state-colors.ts | 20 +- 46 files changed, 2248 insertions(+), 1782 deletions(-) rename libs/{shared/console-shared/src/lib/application-settings/ui => domains/services/feature/src/lib}/application-settings-resources/application-settings-resources.tsx (94%) rename libs/{shared/console-shared/src/lib/application-settings/ui => domains/services/feature/src/lib}/application-settings-resources/fixed-instances-mode.tsx (97%) rename libs/{shared/console-shared/src/lib/application-settings/ui => domains/services/feature/src/lib}/application-settings-resources/hpa-autoscaling-mode.tsx (93%) create mode 100644 libs/domains/services/feature/src/lib/service-overview/instance-metrics/__snapshots__/instance-metrics.spec.tsx.snap rename libs/domains/services/feature/src/lib/{pods-metrics => service-overview/instance-metrics}/empty-state.tsx (85%) rename libs/domains/services/feature/src/lib/{pods-metrics/pods-metrics-skeleton.tsx => service-overview/instance-metrics/instance-metrics-skeleton.tsx} (85%) rename libs/domains/services/feature/src/lib/{pods-metrics/pods-metrics.spec.tsx => service-overview/instance-metrics/instance-metrics.spec.tsx} (81%) rename libs/domains/services/feature/src/lib/{pods-metrics/pods-metrics.tsx => service-overview/instance-metrics/instance-metrics.tsx} (81%) delete mode 100644 libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap delete mode 100644 libs/domains/services/feature/src/lib/service-overview/service-header/service-header-skeleton.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-instance/service-instance.spec.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-instance/service-instance.tsx create mode 100644 libs/domains/services/feature/src/lib/service-overview/service-last-deployment/service-last-deployment.spec.tsx delete mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview-feature.spec.tsx delete mode 100644 libs/domains/services/feature/src/lib/service-overview/service-overview-feature.tsx delete mode 100644 libs/shared/console-shared/src/lib/application-settings/ui/application-settings-resources/application-settings-resources.spec.tsx diff --git a/apps/console-v5/src/routeTree.gen.ts b/apps/console-v5/src/routeTree.gen.ts index 72ef60d8fc0..8be32cf4531 100644 --- a/apps/console-v5/src/routeTree.gen.ts +++ b/apps/console-v5/src/routeTree.gen.ts @@ -1,86 +1,90 @@ /* eslint-disable */ + // @ts-nocheck + // noinspection JSUnusedGlobalSymbols + // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + import { Route as rootRouteImport } from './routes/__root' import { Route as AuthenticatedRouteImport } from './routes/_authenticated' +import { Route as IndexRouteImport } from './routes/index' +import { Route as LoginIndexRouteImport } from './routes/login/index' +import { Route as LoginAuth0CallbackRouteImport } from './routes/login/auth0-callback' +import { Route as AuthenticatedOrganizationRouteRouteImport } from './routes/_authenticated/organization/route' +import { Route as AuthenticatedOrganizationIndexRouteImport } from './routes/_authenticated/organization/index' import { Route as AuthenticatedAcceptInvitationIndexRouteImport } from './routes/_authenticated/accept-invitation/index' -import { Route as AuthenticatedOnboardingPersonalizeRouteImport } from './routes/_authenticated/onboarding/personalize' -import { Route as AuthenticatedOnboardingPlansRouteImport } from './routes/_authenticated/onboarding/plans' import { Route as AuthenticatedOnboardingProjectRouteImport } from './routes/_authenticated/onboarding/project' -import { Route as AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/$clusterId/index' -import { Route as AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/alert-rules' +import { Route as AuthenticatedOnboardingPlansRouteImport } from './routes/_authenticated/onboarding/plans' +import { Route as AuthenticatedOnboardingPersonalizeRouteImport } from './routes/_authenticated/onboarding/personalize' +import { Route as AuthenticatedOrganizationOrganizationIdRouteRouteImport } from './routes/_authenticated/organization/$organizationId/route' +import { Route as AuthenticatedOrganizationOrganizationIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/index' +import { Route as AuthenticatedOrganizationOrganizationIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdClustersRouteImport } from './routes/_authenticated/organization/$organizationId/clusters' +import { Route as AuthenticatedOrganizationOrganizationIdAuditLogsRouteImport } from './routes/_authenticated/organization/$organizationId/audit-logs' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/settings/route' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/route' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/settings/index' import { Route as AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/index' -import { Route as AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/issues' +import { Route as AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/$clusterId/index' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport } from './routes/_authenticated/organization/$organizationId/settings/webhook' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsRolesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/roles' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport } from './routes/_authenticated/organization/$organizationId/settings/members' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/labels-annotations' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/helm-repositories' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport } from './routes/_authenticated/organization/$organizationId/settings/git-repository-access' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/container-registries' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/cloud-credentials' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-summary' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-details' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport } from './routes/_authenticated/organization/$organizationId/settings/api-token' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport } from './routes/_authenticated/organization/$organizationId/settings/ai-copilot' +import { Route as AuthenticatedOrganizationOrganizationIdClusterNewRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/new' import { Route as AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/notification-channel' -import { Route as AuthenticatedOrganizationOrganizationIdAlertsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/route' -import { Route as AuthenticatedOrganizationOrganizationIdAuditLogsRouteImport } from './routes/_authenticated/organization/$organizationId/audit-logs' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/issues' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/alert-rules' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/index' import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/variables' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/overview' import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/overview' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' -import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/route' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/route' import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/route' -import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/features' -import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/index' import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/index' -import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources' -import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/route' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/summary' -import { Route as AuthenticatedOrganizationOrganizationIdClusterNewRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/new' -import { Route as AuthenticatedOrganizationOrganizationIdClustersRouteImport } from './routes/_authenticated/organization/$organizationId/clusters' -import { Route as AuthenticatedOrganizationOrganizationIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/index' -import { Route as AuthenticatedOrganizationOrganizationIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/overview' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/general' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/features' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/index' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/index' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/overview' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/general' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/index' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/route' -import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/variables' -import { Route as AuthenticatedOrganizationOrganizationIdRouteRouteImport } from './routes/_authenticated/organization/$organizationId/route' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport } from './routes/_authenticated/organization/$organizationId/settings/ai-copilot' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport } from './routes/_authenticated/organization/$organizationId/settings/api-token' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-details' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-summary' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/cloud-credentials' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/container-registries' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/settings/danger-zone' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/settings/general' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport } from './routes/_authenticated/organization/$organizationId/settings/git-repository-access' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/helm-repositories' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/settings/index' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/labels-annotations' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport } from './routes/_authenticated/organization/$organizationId/settings/members' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsRolesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/roles' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/settings/route' -import { Route as AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport } from './routes/_authenticated/organization/$organizationId/settings/webhook' -import { Route as AuthenticatedOrganizationIndexRouteImport } from './routes/_authenticated/organization/index' -import { Route as AuthenticatedOrganizationRouteRouteImport } from './routes/_authenticated/organization/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as LoginAuth0CallbackRouteImport } from './routes/login/auth0-callback' -import { Route as LoginIndexRouteImport } from './routes/login/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' const AuthenticatedRoute = AuthenticatedRouteImport.update({ id: '/_authenticated', @@ -101,36 +105,42 @@ const LoginAuth0CallbackRoute = LoginAuth0CallbackRouteImport.update({ path: '/login/auth0-callback', getParentRoute: () => rootRouteImport, } as any) -const AuthenticatedOrganizationRouteRoute = AuthenticatedOrganizationRouteRouteImport.update({ - id: '/organization', - path: '/organization', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedOrganizationIndexRoute = AuthenticatedOrganizationIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedOrganizationRouteRoute, -} as any) -const AuthenticatedAcceptInvitationIndexRoute = AuthenticatedAcceptInvitationIndexRouteImport.update({ - id: '/accept-invitation/', - path: '/accept-invitation/', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedOnboardingProjectRoute = AuthenticatedOnboardingProjectRouteImport.update({ - id: '/onboarding/project', - path: '/onboarding/project', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedOnboardingPlansRoute = AuthenticatedOnboardingPlansRouteImport.update({ - id: '/onboarding/plans', - path: '/onboarding/plans', - getParentRoute: () => AuthenticatedRoute, -} as any) -const AuthenticatedOnboardingPersonalizeRoute = AuthenticatedOnboardingPersonalizeRouteImport.update({ - id: '/onboarding/personalize', - path: '/onboarding/personalize', - getParentRoute: () => AuthenticatedRoute, -} as any) +const AuthenticatedOrganizationRouteRoute = + AuthenticatedOrganizationRouteRouteImport.update({ + id: '/organization', + path: '/organization', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOrganizationIndexRoute = + AuthenticatedOrganizationIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => AuthenticatedOrganizationRouteRoute, + } as any) +const AuthenticatedAcceptInvitationIndexRoute = + AuthenticatedAcceptInvitationIndexRouteImport.update({ + id: '/accept-invitation/', + path: '/accept-invitation/', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingProjectRoute = + AuthenticatedOnboardingProjectRouteImport.update({ + id: '/onboarding/project', + path: '/onboarding/project', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingPlansRoute = + AuthenticatedOnboardingPlansRouteImport.update({ + id: '/onboarding/plans', + path: '/onboarding/plans', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingPersonalizeRoute = + AuthenticatedOnboardingPersonalizeRouteImport.update({ + id: '/onboarding/personalize', + path: '/onboarding/personalize', + getParentRoute: () => AuthenticatedRoute, + } as any) const AuthenticatedOrganizationOrganizationIdRouteRoute = AuthenticatedOrganizationOrganizationIdRouteRouteImport.update({ id: '/$organizationId', @@ -177,13 +187,15 @@ const AuthenticatedOrganizationOrganizationIdSettingsIndexRoute = AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdAlertsIndexRoute = AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute = AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport.update({ @@ -195,85 +207,113 @@ const AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute = AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport.update({ id: '/webhook', path: '/webhook', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsRolesRoute = AuthenticatedOrganizationOrganizationIdSettingsRolesRouteImport.update({ id: '/roles', path: '/roles', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsMembersRoute = AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport.update({ id: '/members', path: '/members', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute = - AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport.update({ - id: '/labels-annotations', - path: '/labels-annotations', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport.update( + { + id: '/labels-annotations', + path: '/labels-annotations', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute = - AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport.update({ - id: '/helm-repositories', - path: '/helm-repositories', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport.update( + { + id: '/helm-repositories', + path: '/helm-repositories', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute = - AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport.update({ - id: '/git-repository-access', - path: '/git-repository-access', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport.update( + { + id: '/git-repository-access', + path: '/git-repository-access', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute = AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport.update({ id: '/general', path: '/general', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute = AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport.update({ id: '/danger-zone', path: '/danger-zone', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute = - AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport.update({ - id: '/container-registries', - path: '/container-registries', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport.update( + { + id: '/container-registries', + path: '/container-registries', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute = - AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport.update({ - id: '/cloud-credentials', - path: '/cloud-credentials', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport.update( + { + id: '/cloud-credentials', + path: '/cloud-credentials', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute = - AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport.update({ - id: '/billing-summary', - path: '/billing-summary', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport.update( + { + id: '/billing-summary', + path: '/billing-summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute = - AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport.update({ - id: '/billing-details', - path: '/billing-details', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport.update( + { + id: '/billing-details', + path: '/billing-details', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute = AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport.update({ id: '/api-token', path: '/api-token', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute = AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport.update({ id: '/ai-copilot', path: '/ai-copilot', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdClusterNewRoute = AuthenticatedOrganizationOrganizationIdClusterNewRouteImport.update({ @@ -282,216 +322,302 @@ const AuthenticatedOrganizationOrganizationIdClusterNewRoute = getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute = - AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport.update({ - id: '/notification-channel', - path: '/notification-channel', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport.update( + { + id: '/notification-channel', + path: '/notification-channel', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute = AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport.update({ id: '/issues', path: '/issues', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute = AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport.update({ id: '/alert-rules', path: '/alert-rules', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, } as any) const AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport.update({ - id: '/project/$projectId/', - path: '/project/$projectId/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport.update( + { + id: '/project/$projectId/', + path: '/project/$projectId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport.update({ - id: '/cluster/$clusterId/', - path: '/cluster/$clusterId/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport.update( + { + id: '/cluster/$clusterId/', + path: '/cluster/$clusterId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport.update({ - id: '/project/$projectId/variables', - path: '/project/$projectId/variables', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport.update( + { + id: '/project/$projectId/variables', + path: '/project/$projectId/variables', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport.update({ - id: '/project/$projectId/overview', - path: '/project/$projectId/overview', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport.update( + { + id: '/project/$projectId/overview', + path: '/project/$projectId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport.update({ - id: '/cluster/$clusterId/overview', - path: '/cluster/$clusterId/overview', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport.update( + { + id: '/cluster/$clusterId/overview', + path: '/cluster/$clusterId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport.update({ - id: '/cluster/$clusterId/cluster-logs', - path: '/cluster/$clusterId/cluster-logs', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport.update( + { + id: '/cluster/$clusterId/cluster-logs', + path: '/cluster/$clusterId/cluster-logs', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport.update({ - id: '/project/$projectId/settings', - path: '/project/$projectId/settings', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport.update( + { + id: '/project/$projectId/settings', + path: '/project/$projectId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport.update({ - id: '/cluster/create/$slug', - path: '/cluster/create/$slug', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport.update( + { + id: '/cluster/create/$slug', + path: '/cluster/create/$slug', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport.update({ - id: '/cluster/$clusterId/settings', - path: '/cluster/$clusterId/settings', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport.update( + { + id: '/cluster/$clusterId/settings', + path: '/cluster/$clusterId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport.update({ - id: '/general', - path: '/general', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport.update({ - id: '/danger-zone', - path: '/danger-zone', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport.update({ - id: '/summary', - path: '/summary', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport.update( + { + id: '/summary', + path: '/summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport.update({ - id: '/resources', - path: '/resources', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport.update({ - id: '/general', - path: '/general', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute = - AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport.update({ - id: '/features', - path: '/features', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport.update( + { + id: '/features', + path: '/features', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport.update({ - id: '/resources', - path: '/resources', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport.update({ - id: '/network', - path: '/network', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport.update( + { + id: '/network', + path: '/network', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport.update({ - id: '/image-registry', - path: '/image-registry', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport.update( + { + id: '/image-registry', + path: '/image-registry', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport.update({ - id: '/general', - path: '/general', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport.update({ - id: '/eks-anywhere', - path: '/eks-anywhere', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport.update( + { + id: '/eks-anywhere', + path: '/eks-anywhere', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport.update({ - id: '/danger-zone', - path: '/danger-zone', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport.update({ - id: '/credentials', - path: '/credentials', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport.update( + { + id: '/credentials', + path: '/credentials', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute = - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport.update({ - id: '/advanced-settings', - path: '/advanced-settings', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport.update( + { + id: '/advanced-settings', + path: '/advanced-settings', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport.update({ - id: '/project/$projectId/environment/$environmentId/', - path: '/project/$projectId/environment/$environmentId/', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/', + path: '/project/$projectId/environment/$environmentId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport.update({ - id: '/project/$projectId/environment/$environmentId/variables', - path: '/project/$projectId/environment/$environmentId/variables', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/variables', + path: '/project/$projectId/environment/$environmentId/variables', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport.update({ - id: '/project/$projectId/environment/$environmentId/overview', - path: '/project/$projectId/environment/$environmentId/overview', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/overview', + path: '/project/$projectId/environment/$environmentId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport.update({ - id: '/project/$projectId/environment/$environmentId/deployments', - path: '/project/$projectId/environment/$environmentId/deployments', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/deployments', + path: '/project/$projectId/environment/$environmentId/deployments', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport.update({ - id: '/project/$projectId/environment/$environmentId/settings', - path: '/project/$projectId/environment/$environmentId/settings', - getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/settings', + path: '/project/$projectId/environment/$environmentId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport.update( { @@ -499,15 +625,17 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm path: '/preview-environments', getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, - } as any + } as any, ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport.update({ - id: '/general', - path: '/general', - getParentRoute: () => - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport.update( { @@ -515,22 +643,24 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm path: '/deployment-rules', getParentRoute: () => AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, - } as any + } as any, ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute = - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport.update({ - id: '/danger-zone', - path: '/danger-zone', - getParentRoute: () => - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, - } as any) + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport.update( { id: '/project/$projectId/environment/$environmentId/service/$serviceId/', path: '/project/$projectId/environment/$environmentId/service/$serviceId/', getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any + } as any, ) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport.update( @@ -538,7 +668,7 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm id: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', path: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, - } as any + } as any, ) export interface FileRoutesByFullPath { @@ -1548,15 +1678,17 @@ const AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren: Authentic { AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute: AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute, - AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute: AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute, + AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute: + AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute, AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute: AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute, - AuthenticatedOrganizationOrganizationIdAlertsIndexRoute: AuthenticatedOrganizationOrganizationIdAlertsIndexRoute, + AuthenticatedOrganizationOrganizationIdAlertsIndexRoute: + AuthenticatedOrganizationOrganizationIdAlertsIndexRoute, } const AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdAlertsRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren + AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren { @@ -1613,7 +1745,7 @@ const AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren: Authent const AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdSettingsRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren + AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren { @@ -1652,7 +1784,7 @@ const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteC const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren { @@ -1679,7 +1811,7 @@ const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren { @@ -1700,7 +1832,7 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteC const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren { @@ -1727,7 +1859,7 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren, ) interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren { @@ -1763,11 +1895,16 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren, AuthenticatedOrganizationOrganizationIdSettingsRouteRoute: AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren, - AuthenticatedOrganizationOrganizationIdAuditLogsRoute: AuthenticatedOrganizationOrganizationIdAuditLogsRoute, - AuthenticatedOrganizationOrganizationIdClustersRoute: AuthenticatedOrganizationOrganizationIdClustersRoute, - AuthenticatedOrganizationOrganizationIdOverviewRoute: AuthenticatedOrganizationOrganizationIdOverviewRoute, - AuthenticatedOrganizationOrganizationIdIndexRoute: AuthenticatedOrganizationOrganizationIdIndexRoute, - AuthenticatedOrganizationOrganizationIdClusterNewRoute: AuthenticatedOrganizationOrganizationIdClusterNewRoute, + AuthenticatedOrganizationOrganizationIdAuditLogsRoute: + AuthenticatedOrganizationOrganizationIdAuditLogsRoute, + AuthenticatedOrganizationOrganizationIdClustersRoute: + AuthenticatedOrganizationOrganizationIdClustersRoute, + AuthenticatedOrganizationOrganizationIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdIndexRoute: + AuthenticatedOrganizationOrganizationIdIndexRoute, + AuthenticatedOrganizationOrganizationIdClusterNewRoute: + AuthenticatedOrganizationOrganizationIdClusterNewRoute, AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute: AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute, AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute: @@ -1806,7 +1943,7 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr const AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren = AuthenticatedOrganizationOrganizationIdRouteRoute._addFileChildren( - AuthenticatedOrganizationOrganizationIdRouteRouteChildren + AuthenticatedOrganizationOrganizationIdRouteRouteChildren, ) interface AuthenticatedOrganizationRouteRouteChildren { @@ -1814,14 +1951,17 @@ interface AuthenticatedOrganizationRouteRouteChildren { AuthenticatedOrganizationIndexRoute: typeof AuthenticatedOrganizationIndexRoute } -const AuthenticatedOrganizationRouteRouteChildren: AuthenticatedOrganizationRouteRouteChildren = { - AuthenticatedOrganizationOrganizationIdRouteRoute: AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren, - AuthenticatedOrganizationIndexRoute: AuthenticatedOrganizationIndexRoute, -} +const AuthenticatedOrganizationRouteRouteChildren: AuthenticatedOrganizationRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdRouteRoute: + AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren, + AuthenticatedOrganizationIndexRoute: AuthenticatedOrganizationIndexRoute, + } -const AuthenticatedOrganizationRouteRouteWithChildren = AuthenticatedOrganizationRouteRoute._addFileChildren( - AuthenticatedOrganizationRouteRouteChildren -) +const AuthenticatedOrganizationRouteRouteWithChildren = + AuthenticatedOrganizationRouteRoute._addFileChildren( + AuthenticatedOrganizationRouteRouteChildren, + ) interface AuthenticatedRouteChildren { AuthenticatedOrganizationRouteRoute: typeof AuthenticatedOrganizationRouteRouteWithChildren @@ -1832,14 +1972,19 @@ interface AuthenticatedRouteChildren { } const AuthenticatedRouteChildren: AuthenticatedRouteChildren = { - AuthenticatedOrganizationRouteRoute: AuthenticatedOrganizationRouteRouteWithChildren, - AuthenticatedOnboardingPersonalizeRoute: AuthenticatedOnboardingPersonalizeRoute, + AuthenticatedOrganizationRouteRoute: + AuthenticatedOrganizationRouteRouteWithChildren, + AuthenticatedOnboardingPersonalizeRoute: + AuthenticatedOnboardingPersonalizeRoute, AuthenticatedOnboardingPlansRoute: AuthenticatedOnboardingPlansRoute, AuthenticatedOnboardingProjectRoute: AuthenticatedOnboardingProjectRoute, - AuthenticatedAcceptInvitationIndexRoute: AuthenticatedAcceptInvitationIndexRoute, + AuthenticatedAcceptInvitationIndexRoute: + AuthenticatedAcceptInvitationIndexRoute, } -const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren(AuthenticatedRouteChildren) +const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren( + AuthenticatedRouteChildren, +) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, @@ -1847,4 +1992,6 @@ const rootRouteChildren: RootRouteChildren = { LoginAuth0CallbackRoute: LoginAuth0CallbackRoute, LoginIndexRoute: LoginIndexRoute, } -export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)._addFileTypes() +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx index 852827b6922..0d445b58bf7 100644 --- a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -1,11 +1,13 @@ import { createFileRoute, useParams } from '@tanstack/react-router' -import { useMemo } from 'react' +import { memo, useMemo } from 'react' import { match } from 'ts-pattern' import { useCluster } from '@qovery/domains/clusters/feature' import { useEnvironment } from '@qovery/domains/environments/feature' import { EnableObservabilityModal } from '@qovery/domains/observability/feature' import { TerraformResourcesSection } from '@qovery/domains/service-terraform/feature' -import { ServiceOverviewFeature, useService } from '@qovery/domains/services/feature' +import { ObservabilityCallout, ServiceOverview } from '@qovery/domains/services/feature' +import { useService } from '@qovery/domains/services/feature' +import { MetricsWebSocketListener } from '@qovery/shared/util-web-sockets' export const Route = createFileRoute( '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' @@ -13,11 +15,13 @@ export const Route = createFileRoute( component: RouteComponent, }) +const WebSocketListenerMemo = memo(MetricsWebSocketListener) + function RouteComponent() { - const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + const { organizationId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) - const { data: environment } = useEnvironment({ environmentId }) const { data: service } = useService({ environmentId, serviceId }) + const { data: environment } = useEnvironment({ environmentId }) const { data: cluster } = useCluster({ organizationId, clusterId: environment?.cluster_id ?? '' }) const hasNoMetrics = useMemo( @@ -34,16 +38,28 @@ function RouteComponent() { ) return ( - : undefined} - observabilityCalloutModalContent={} - /> + <> + : undefined} + hasNoMetrics={hasNoMetrics} + observabilityCallout={ + + + + } + /> + {environment && service?.serviceType && ( + + )} + ) } diff --git a/libs/domains/observability/feature/src/lib/enable-observability-modal/enable-observability-modal.tsx b/libs/domains/observability/feature/src/lib/enable-observability-modal/enable-observability-modal.tsx index 68e984a58d5..fcc8e7bab9d 100644 --- a/libs/domains/observability/feature/src/lib/enable-observability-modal/enable-observability-modal.tsx +++ b/libs/domains/observability/feature/src/lib/enable-observability-modal/enable-observability-modal.tsx @@ -7,13 +7,13 @@ import { twMerge } from '@qovery/shared/util-js' export function EnableObservabilityContent({ className }: { className?: string }) { return (
-

Observability is here!

+

Observability is here!

-

+

We've just released our brand-new Observability feature, now available for everyone.
Why is this exciting?

-
    +
    • 1-click setup: Dev + Ops friendly
    • @@ -61,7 +61,7 @@ export function EnableObservabilityVideo() { allowFullScreen className={clsx( 'absolute left-0 top-0 h-full w-full', - showIframe ? 'animate-[fadein_0.12s_ease-in-out_forwards' : 'rounded bg-neutral-100', + showIframe ? 'animate-[fadein_0.12s_ease-in-out_forwards' : 'rounded bg-surface-neutral', !showIframe && 'invisible' )} /> @@ -106,7 +106,7 @@ export function EnableObservabilityModal() {
      -
      +
      Starting from $299/month closeModal()} />
      diff --git a/libs/domains/service-terraform/feature/src/lib/resource-details/resource-details.tsx b/libs/domains/service-terraform/feature/src/lib/resource-details/resource-details.tsx index 38a2d7a28e0..1b8f7ac6cdd 100644 --- a/libs/domains/service-terraform/feature/src/lib/resource-details/resource-details.tsx +++ b/libs/domains/service-terraform/feature/src/lib/resource-details/resource-details.tsx @@ -20,9 +20,9 @@ export function ResourceDetails({ resource }: ResourceDetailsProps): ReactElemen if (!resource) { return (
      - -

      No resource selected

      -

      Select a resource from the list to view details.

      + +

      No resource selected

      +

      Select a resource from the list to view details.

      ) } @@ -45,12 +45,10 @@ export function ResourceDetails({ resource }: ResourceDetailsProps): ReactElemen return (
      - + - - Key - + Key Value @@ -61,16 +59,27 @@ export function ResourceDetails({ resource }: ResourceDetailsProps): ReactElemen onMouseEnter={() => setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(null)} > - + 0 && 'border-t border-neutral' + )} + > {row.key} - + 0 && 'border-t border-neutral', + hoveredIndex === index && 'group' + )} + > {row.value} {hoveredIndex === index && ( )} diff --git a/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.spec.tsx b/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.spec.tsx index c5814653df1..7e8408cb75a 100644 --- a/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.spec.tsx +++ b/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.spec.tsx @@ -74,7 +74,7 @@ describe('ResourceTreeList', () => { ) const selectedButton = screen.getByRole('button', { name: /app_bucket/ }) - expect(selectedButton).toHaveClass('bg-brand-50') + expect(selectedButton).toHaveClass('bg-surface-brand-subtle') }) it('should call onSelectResource when clicking a resource', async () => { @@ -109,7 +109,7 @@ describe('ResourceTreeList', () => { // Non-matching button should have dimmed styling const nonMatchingButton = screen.getByRole('button', { name: /app_bucket/ }) - expect(nonMatchingButton).toHaveClass('text-neutral-250') + expect(nonMatchingButton).toHaveClass('text-neutral-disabled') }) it('should highlight matching resources when searching by type', () => { @@ -125,7 +125,7 @@ describe('ResourceTreeList', () => { expect(screen.getByText('app_db')).toBeInTheDocument() // Non-matching resources are visible but dimmed const nonMatchingButton = screen.getByRole('button', { name: /app_bucket/ }) - expect(nonMatchingButton).toHaveClass('text-neutral-250') + expect(nonMatchingButton).toHaveClass('text-neutral-disabled') }) it('should highlight matching resources when searching by attribute keys', () => { @@ -141,7 +141,7 @@ describe('ResourceTreeList', () => { expect(screen.getByText('app_db')).toBeInTheDocument() // Non-matching resources are visible but dimmed const nonMatchingButton = screen.getByRole('button', { name: /app_bucket/ }) - expect(nonMatchingButton).toHaveClass('text-neutral-250') + expect(nonMatchingButton).toHaveClass('text-neutral-disabled') }) it('should highlight matching resources when searching by attribute values', () => { @@ -157,7 +157,7 @@ describe('ResourceTreeList', () => { expect(screen.getByText('app_db')).toBeInTheDocument() // Non-matching resources are visible but dimmed const nonMatchingButton = screen.getByRole('button', { name: /app_bucket/ }) - expect(nonMatchingButton).toHaveClass('text-neutral-250') + expect(nonMatchingButton).toHaveClass('text-neutral-disabled') }) it('should show no results message when search returns nothing', () => { diff --git a/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.tsx b/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.tsx index 2bccc1177cb..fc7e6baa1c6 100644 --- a/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.tsx +++ b/libs/domains/service-terraform/feature/src/lib/resource-tree-list/resource-tree-list.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx' import { type ReactElement, useEffect, useMemo, useState } from 'react' import { type TerraformResource } from '@qovery/domains/service-terraform/data-access' import { Icon, TreeView } from '@qovery/shared/ui' @@ -63,8 +64,8 @@ export function ResourceTreeList({ if (resources.length === 0) { return (
      - -

      No result for this search

      + +

      No result for this search

      ) } @@ -72,9 +73,9 @@ export function ResourceTreeList({ if (searchQuery && !hasMatches) { return (
      - -

      No resources match

      -

      No resources found for ${searchQuery}

      + +

      No resources match

      +

      No resources found for: {searchQuery}

      ) } @@ -87,7 +88,7 @@ export function ResourceTreeList({ {group.displayName} - ({group.resources.length}) + ({group.resources.length}) @@ -96,24 +97,19 @@ export function ResourceTreeList({ const matches = resourceMatchMap.get(resource.id) ?? true const isSelected = selectedResourceId === resource.id - function getButtonClassName(): string { - const base = - 'w-full cursor-pointer rounded h-8 px-2 gap-2 text-sm transition-colors flex items-center mb-1' - if (isSelected) { - return `${base} bg-brand-50 text-brand-500` - } - if (matches) { - return `${base} text-neutral-350 hover:bg-neutral-100` - } - return `${base} text-neutral-250 hover:bg-neutral-100` - } - return (
    • - )} -
    • +
      {/* Tree list */} diff --git a/libs/domains/services/feature/src/index.ts b/libs/domains/services/feature/src/index.ts index 40e14e58167..b5b78e7fd8c 100644 --- a/libs/domains/services/feature/src/index.ts +++ b/libs/domains/services/feature/src/index.ts @@ -41,17 +41,13 @@ export * from './lib/hooks/use-metrics/use-metrics' export * from './lib/hooks/use-recent-services/use-recent-services' export * from './lib/hooks/use-favorite-services/use-favorite-services' export * from './lib/pod-statuses-callout/pod-statuses-callout' -export * from './lib/pods-metrics/pods-metrics' -export * from './lib/keda/scaled-object-status/scaled-object-status' -export * from './lib/keda/components' export * from './lib/service-action-toolbar/service-action-toolbar' export * from './lib/service-deployment-status-label/service-deployment-status-label' export * from './lib/service-overview/service-header/service-header' export * from './lib/service-links-popover/service-links-popover' export * from './lib/service-list/service-list' -export * from './lib/service-overview/observability-callout' export * from './lib/service-overview/service-overview' -export * from './lib/service-overview/service-overview-feature' +export * from './lib/service-overview/observability-callout' export * from './lib/service-state-chip/service-state-chip' export * from './lib/service-clone-modal/service-clone-modal' export * from './lib/service-terminal/service-terminal' @@ -66,3 +62,4 @@ export * from './lib/service-access-modal/service-access-modal' export * from './lib/service-deployment-list/service-deployment-list' export * from './lib/pod-details/pod-details' export * from './lib/force-unlock-modal/force-unlock-modal' +export * from './lib/application-settings-resources/application-settings-resources' diff --git a/libs/shared/console-shared/src/lib/application-settings/ui/application-settings-resources/application-settings-resources.tsx b/libs/domains/services/feature/src/lib/application-settings-resources/application-settings-resources.tsx similarity index 94% rename from libs/shared/console-shared/src/lib/application-settings/ui/application-settings-resources/application-settings-resources.tsx rename to libs/domains/services/feature/src/lib/application-settings-resources/application-settings-resources.tsx index 9cd06c79a4e..7005aa4c0fe 100644 --- a/libs/shared/console-shared/src/lib/application-settings/ui/application-settings-resources/application-settings-resources.tsx +++ b/libs/domains/services/feature/src/lib/application-settings-resources/application-settings-resources.tsx @@ -1,3 +1,4 @@ +import { useQuery } from '@tanstack/react-query' import posthog from 'posthog-js' import { useFeatureFlagVariantKey } from 'posthog-js/react' import { useEffect, useRef } from 'react' @@ -5,9 +6,7 @@ import { Controller, useFieldArray, useFormContext } from 'react-hook-form' import { useParams } from 'react-router-dom' import { match } from 'ts-pattern' import { hasGpuInstance, useCluster } from '@qovery/domains/clusters/feature' -import { useEnvironment } from '@qovery/domains/environments/feature' import { type AnyService, type Database, type Helm } from '@qovery/domains/services/data-access' -import { KedaSettings, useRunningStatus } from '@qovery/domains/services/feature' import { CLUSTER_SETTINGS_RESOURCES_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes' import { Callout, @@ -21,6 +20,9 @@ import { inputSizeUnitRules, } from '@qovery/shared/ui' import { loadHpaSettingsFromAdvancedSettings } from '@qovery/shared/util-services' +import { queries } from '@qovery/state/util-queries' +import { useRunningStatus } from '../hooks/use-running-status/use-running-status' +import { KedaSettings } from '../keda/components/keda-settings' import { FixedInstancesMode } from './fixed-instances-mode' import { HpaAutoscalingMode } from './hpa-autoscaling-mode' @@ -33,6 +35,17 @@ export interface ApplicationSettingsResourcesProps { advancedSettings?: unknown } +interface UseEnvironmentProps { + environmentId?: string +} + +function useEnvironment({ environmentId }: UseEnvironmentProps) { + return useQuery({ + ...queries.environments.details({ environmentId: environmentId as string }), + enabled: Boolean(environmentId), + }) +} + export function ApplicationSettingsResources({ displayWarningCpu, displayInstanceLimits = true, @@ -73,11 +86,9 @@ export function ApplicationSettingsResources({ const hpaMemoryAverageUtilizationPercent = watch('hpa_memory_average_utilization_percent') ?? 60 const previousAutoscalingModeRef = useRef(autoscalingMode) - // Adjust min/max values when switching between autoscaling modes useEffect(() => { const previousMode = previousAutoscalingModeRef.current - // When switching from no autoscaling to HPA/KEDA, set min=1 and max=2 if (previousMode === 'NONE' && (autoscalingMode === 'HPA' || autoscalingMode === 'KEDA')) { if (minRunningInstances === maxRunningInstances) { setValue('min_running_instances', 1) @@ -85,14 +96,12 @@ export function ApplicationSettingsResources({ } } - // When switching to no autoscaling (NONE), ensure max equals min if ((previousMode === 'HPA' || previousMode === 'KEDA') && autoscalingMode === 'NONE') { if (minRunningInstances !== maxRunningInstances) { setValue('max_running_instances', minRunningInstances) } } - // Set default HPA settings when switching to HPA mode if (autoscalingMode === 'HPA' && !hpaMetricTypeRaw) { const hpaSettings = loadHpaSettingsFromAdvancedSettings(advancedSettings) setValue('hpa_metric_type', hpaSettings.hpa_metric_type) @@ -103,16 +112,11 @@ export function ApplicationSettingsResources({ previousAutoscalingModeRef.current = autoscalingMode }, [autoscalingMode, minRunningInstances, maxRunningInstances, hpaMetricTypeRaw, advancedSettings, setValue]) - // Determine the current saved autoscaling mode (not the form value) const currentAutoscalingMode = match(service) .with({ serviceType: 'APPLICATION' }, { serviceType: 'CONTAINER' }, (s) => { - // If KEDA autoscaling policy exists, it's KEDA if (s.autoscaling?.mode === 'KEDA') return 'KEDA' - // If min === max, it's fixed instances (NONE mode) if (s.min_running_instances === s.max_running_instances) return 'NONE' - // If min !== max and no KEDA, backend considers it as HPA if (s.min_running_instances !== s.max_running_instances) return 'HPA' - // Default to NONE return 'NONE' }) .otherwise(() => 'NONE') @@ -205,7 +209,6 @@ export function ApplicationSettingsResources({ ) - // KEDA allows 0 instances (scale to zero), other modes require at least 1 const effectiveMinInstances = autoscalingMode === 'KEDA' ? 0 : minInstances return ( @@ -370,7 +373,6 @@ export function ApplicationSettingsResources({ )} - {/* Mode NONE: Fixed instances */} {autoscalingMode === 'NONE' && ( )} - {/* Mode HPA: Horizontal Pod Autoscaler */} {autoscalingMode === 'HPA' && ( )} - {/* Mode KEDA: Event-driven autoscaling */} {autoscalingMode === 'KEDA' && ( = { ACTIVE: { icon: 'circle-check', - iconClassName: 'text-green-500', + iconClassName: 'text-positive', tooltip: 'Webhook is correctly configured. Auto-deploy will trigger on git events.', }, NOT_CONFIGURED: { icon: 'circle-question', - iconClassName: 'text-red-500', + iconClassName: 'text-negative', tooltip: 'No webhook found for auto-deployment. Click to configure it in settings.', }, MISCONFIGURED: { icon: 'triangle-exclamation', - iconClassName: 'text-yellow-500', + iconClassName: 'text-warning', tooltip: 'Webhook is missing required events. Click to fix it in settings.', }, UNABLE_TO_VERIFY: { icon: 'circle-exclamation', - iconClassName: 'text-neutral-350', + iconClassName: 'text-neutral-subtle', tooltip: "Couldn't verify webhook status. This could be due to expired credentials, insufficient permissions, or git provider API unavailability.", }, @@ -54,11 +54,11 @@ export function AutoDeployBadge({ serviceId }: AutoDeployBadgeProps) { return ( - - + + Auto-deploy {isLoading ? ( - + ) : ( config && )} diff --git a/libs/domains/services/feature/src/lib/keda/scaled-object-status/scaled-object-status.tsx b/libs/domains/services/feature/src/lib/keda/scaled-object-status/scaled-object-status.tsx index 8b588b92bdf..0d0601b28ca 100644 --- a/libs/domains/services/feature/src/lib/keda/scaled-object-status/scaled-object-status.tsx +++ b/libs/domains/services/feature/src/lib/keda/scaled-object-status/scaled-object-status.tsx @@ -1,6 +1,5 @@ import { match } from 'ts-pattern' -import { Badge, Heading, Icon, Section, Skeleton, StatusChip } from '@qovery/shared/ui' -import { useRunningStatus } from '../../hooks/use-running-status/use-running-status' +import { Badge, BlockContent, StatusChip } from '@qovery/shared/ui' interface ScaledObjectCondition { type: string @@ -9,90 +8,61 @@ interface ScaledObjectCondition { reason?: string } -interface ScaledObjectStatusDto { +export interface ScaledObjectStatusDto { name: string conditions?: ScaledObjectCondition[] } export interface ScaledObjectStatusProps { - environmentId: string - serviceId: string + scaledObject: ScaledObjectStatusDto } -export function ScaledObjectStatus({ environmentId, serviceId }: ScaledObjectStatusProps) { - const { data: runningStatus, isLoading } = useRunningStatus({ environmentId, serviceId }) - - // Only show for APPLICATION and CONTAINER with scaled_object - if (!runningStatus || !('scaled_object' in runningStatus) || !runningStatus.scaled_object) { - return null - } - - const scaledObject = runningStatus.scaled_object as ScaledObjectStatusDto - - if (isLoading) { - return ( -
      - Scaled Object - -
      - ) - } - +export function ScaledObjectStatus({ scaledObject }: ScaledObjectStatusProps) { return ( -
      - Scaled Object (KEDA) -
      -
      -
      - -
      - {scaledObject.name} -
      -
      -
      - - {scaledObject.conditions && scaledObject.conditions.length > 0 && ( -
      - {scaledObject.conditions - .filter((condition) => { - // Don't show Fallback at all - if (condition.type === 'Fallback') return false - // Only show Paused if it's True (paused) - if (condition.type === 'Paused' && condition.status !== 'True') return false - // Only show Active if it's True (if False, all scalers have correct values, not an issue) - return !(condition.type === 'Active' && condition.status !== 'True') - }) - .map((condition, index) => { - const chipStatus = match({ type: condition.type, status: condition.status }) - // Positive conditions: True = good, False = bad/warning - .with({ type: 'Ready', status: 'True' }, () => 'RUNNING' as const) - .with({ type: 'Ready', status: 'False' }, () => 'ERROR' as const) - .with({ type: 'Active', status: 'True' }, () => 'RUNNING' as const) - .with({ type: 'Active', status: 'False' }, () => 'WARNING' as const) - // Paused: True = bad (paused) - .with({ type: 'Paused', status: 'True' }, () => 'ERROR' as const) - // Default - .otherwise(() => (condition.status === 'True' ? ('RUNNING' as const) : ('WARNING' as const))) + + {scaledObject.conditions && scaledObject.conditions.length > 0 && ( +
      + {scaledObject.conditions + .filter((condition) => { + // Don't show Fallback at all + if (condition.type === 'Fallback') return false + // Only show Paused if it's True (paused) + if (condition.type === 'Paused' && condition.status !== 'True') return false + // Only show Active if it's True (if False, all scalers have correct values, not an issue) + return !(condition.type === 'Active' && condition.status !== 'True') + }) + .map((condition, index) => { + const chipStatus = match({ type: condition.type, status: condition.status }) + // Positive conditions: True = good, False = bad/warning + .with({ type: 'Ready', status: 'True' }, () => 'RUNNING' as const) + .with({ type: 'Ready', status: 'False' }, () => 'ERROR' as const) + .with({ type: 'Active', status: 'True' }, () => 'RUNNING' as const) + .with({ type: 'Active', status: 'False' }, () => 'WARNING' as const) + // Paused: True = bad (paused) + .with({ type: 'Paused', status: 'True' }, () => 'ERROR' as const) + // Default + .otherwise(() => (condition.status === 'True' ? ('RUNNING' as const) : ('WARNING' as const))) - return ( -
      -
      -
      - {condition.type} - - - {condition.status} - -
      - {condition.reason && Reason: {condition.reason}} - {condition.message && {condition.message}} + return ( +
      +
      +
      + {condition.type} + + + {condition.status} + +
      +
      + {condition.reason && Reason: {condition.reason}} + {condition.message && {condition.message}}
      - ) - })} -
      - )} -
      -
      +
      + ) + })} +
+ )} + ) } diff --git a/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx b/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx index 327b7ec8613..50a821bbd47 100644 --- a/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx +++ b/libs/domains/services/feature/src/lib/pod-details/pod-details.tsx @@ -23,7 +23,7 @@ export interface Pod extends Partial, Partial { function TimelineCircle() { return ( - + ) } @@ -42,8 +42,8 @@ export function PodDetails({ pod, serviceId, serviceType }: PodDetailsProps) { const defaultContainer = filteredContainers[0]?.name return ( -
-
+
+

1 @@ -68,7 +68,7 @@ exports[`ServiceLinksPopover should match snapshot 1`] = ` class="flex p-2" > @@ -78,18 +78,18 @@ exports[`ServiceLinksPopover should match snapshot 1`] = ` />

8080
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..b39d9f763a2 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 @@ -63,13 +63,9 @@ export function ServiceLinksPopover({ return ( {children} - +
-

+

{filteredLinks?.length ?? 0} {pluralize(filteredLinks?.length ?? 0, 'link')} attached

{serviceType !== 'HELM' && ( @@ -84,27 +80,24 @@ export function ServiceLinksPopover({
    {nginxLinks.map((link: LinkProps) => (
  • - + -
  • ))} {nginxLinks.length > 0 && gatewayApiLinks.length > 0 && ( <> -
  • -
  • +
    +
  • Gateway API / Envoy stack more info @@ -124,33 +117,24 @@ export function ServiceLinksPopover({ side="right" classNameContent="max-w-xs" > - - - +
  • )} {gatewayApiLinks.map((link: LinkProps) => (
  • - + -
  • ))} diff --git a/libs/domains/services/feature/src/lib/service-overview/instance-metrics/__snapshots__/instance-metrics.spec.tsx.snap b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/__snapshots__/instance-metrics.spec.tsx.snap new file mode 100644 index 00000000000..33ebc04ebc6 --- /dev/null +++ b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/__snapshots__/instance-metrics.spec.tsx.snap @@ -0,0 +1,268 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`InstanceMetrics should match snapshot with data 1`] = ` +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + +
    + + +
    + + + + + + + + + + +
    + + Unknown + +
    +
    + + 30 MiB (20%) + + 12 mCPU (5%) + + - + + - + +
    +
    +
    +`; + +exports[`InstanceMetrics should match snapshot with empty data 1`] = ` +
    +
    + + + Application is not running + + + Deploy the application first + +
    +
    +`; + +exports[`InstanceMetrics should match snapshot with null data 1`] = ` +
    +
    + + + Application is not running + + + Deploy the application first + +
    +
    +`; diff --git a/libs/domains/services/feature/src/lib/pods-metrics/empty-state.tsx b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/empty-state.tsx similarity index 85% rename from libs/domains/services/feature/src/lib/pods-metrics/empty-state.tsx rename to libs/domains/services/feature/src/lib/service-overview/instance-metrics/empty-state.tsx index 4f79f82017a..9c1b240df33 100644 --- a/libs/domains/services/feature/src/lib/pods-metrics/empty-state.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/empty-state.tsx @@ -1,9 +1,9 @@ import { P, match } from 'ts-pattern' import { Icon, IconAwesomeEnum } from '@qovery/shared/ui' import { upperCaseFirstLetter } from '@qovery/shared/util-js' -import { useDeploymentStatus } from '../hooks/use-deployment-status/use-deployment-status' -import { useRunningStatus } from '../hooks/use-running-status/use-running-status' -import { useService } from '../hooks/use-service/use-service' +import { useDeploymentStatus } from '../../hooks/use-deployment-status/use-deployment-status' +import { useRunningStatus } from '../../hooks/use-running-status/use-running-status' +import { useService } from '../../hooks/use-service/use-service' function Box({ title, @@ -15,10 +15,10 @@ function Box({ icon?: IconAwesomeEnum }) { return ( -
    - +
    + {title} - {description} + {description}
    ) } diff --git a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics-skeleton.tsx b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics-skeleton.tsx similarity index 85% rename from libs/domains/services/feature/src/lib/pods-metrics/pods-metrics-skeleton.tsx rename to libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics-skeleton.tsx index e5d321f7ed2..cea6dbb92ed 100644 --- a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics-skeleton.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics-skeleton.tsx @@ -2,9 +2,9 @@ import { Skeleton, TablePrimitives } from '@qovery/shared/ui' const { Table } = TablePrimitives -export function PodsMetricsSkeleton() { +export function InstanceMetricsSkeleton() { return ( - + {[...Array(3)].map((_, index) => ( @@ -29,4 +29,4 @@ export function PodsMetricsSkeleton() { ) } -export default PodsMetricsSkeleton +export default InstanceMetricsSkeleton diff --git a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.spec.tsx b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.spec.tsx similarity index 81% rename from libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.spec.tsx rename to libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.spec.tsx index 6a78516be76..53c7779c1b3 100644 --- a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.spec.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.spec.tsx @@ -1,12 +1,12 @@ import { ResourceStatusDto, UnitDto } from 'qovery-ws-typescript-axios' import { ServiceTypeEnum } from '@qovery/shared/enums' import { renderWithProviders } from '@qovery/shared/util-tests' -import * as useDeploymentStatusImport from '../hooks/use-deployment-status/use-deployment-status' -import * as useMetricsImport from '../hooks/use-metrics/use-metrics' -import * as useServiceImport from '../hooks/use-service/use-service' -import PodsMetrics from './pods-metrics' +import * as useDeploymentStatusImport from '../../hooks/use-deployment-status/use-deployment-status' +import * as useMetricsImport from '../../hooks/use-metrics/use-metrics' +import * as useServiceImport from '../../hooks/use-service/use-service' +import { InstanceMetrics } from './instance-metrics' -describe('PodsMetrics', () => { +describe('InstanceMetrics', () => { it('should match snapshot with data', () => { jest.spyOn(useMetricsImport, 'useMetrics').mockReturnValue({ data: [ @@ -44,7 +44,7 @@ describe('PodsMetrics', () => { error: {}, isError: false, }) - const { container } = renderWithProviders() + const { container } = renderWithProviders() expect(container).toMatchSnapshot() }) it('should match snapshot with empty data', () => { @@ -75,7 +75,7 @@ describe('PodsMetrics', () => { error: {}, isError: false, }) - const { container } = renderWithProviders() + const { container } = renderWithProviders() expect(container).toMatchSnapshot() }) it('should match snapshot with null data', () => { @@ -106,7 +106,7 @@ describe('PodsMetrics', () => { error: {}, isError: false, }) - const { container } = renderWithProviders() + const { container } = renderWithProviders() expect(container).toMatchSnapshot() }) }) diff --git a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.tsx b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.tsx similarity index 81% rename from libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.tsx rename to libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.tsx index 4e7dfc865cd..939f67e63f8 100644 --- a/libs/domains/services/feature/src/lib/pods-metrics/pods-metrics.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/instance-metrics/instance-metrics.tsx @@ -7,6 +7,7 @@ import { getSortedRowModel, useReactTable, } from '@tanstack/react-table' +import clsx from 'clsx' import { type ServiceStateDto } from 'qovery-ws-typescript-axios' import { Fragment, type PropsWithChildren, memo, useEffect, useMemo, useState } from 'react' import { P, match } from 'ts-pattern' @@ -16,8 +17,8 @@ import { Badge, Button, CopyToClipboard, + EmptyState, Icon, - IconAwesomeEnum, StatusChip, TablePrimitives, Tooltip, @@ -25,19 +26,19 @@ import { } from '@qovery/shared/ui' import { dateFullFormat, dateUTCString, timeAgo } from '@qovery/shared/util-dates' import { formatMetric, pluralize, twMerge } from '@qovery/shared/util-js' -import { useMetrics } from '../hooks/use-metrics/use-metrics' -import { useRunningStatus } from '../hooks/use-running-status/use-running-status' -import { useService } from '../hooks/use-service/use-service' -import { type Pod, PodDetails } from '../pod-details/pod-details' -import EmptyState from './empty-state' -import { PodsMetricsSkeleton } from './pods-metrics-skeleton' +import { useMetrics } from '../../hooks/use-metrics/use-metrics' +import { useRunningStatus } from '../../hooks/use-running-status/use-running-status' +import { useService } from '../../hooks/use-service/use-service' +import { type Pod, PodDetails } from '../../pod-details/pod-details' +import { EmptyState as EmptyStatePodsMetrics } from './empty-state' +import { InstanceMetricsSkeleton } from './instance-metrics-skeleton' const { Table } = TablePrimitives const columnHelper = createColumnHelper() const placeholder = -export interface PodsMetricsMemoizedProps extends PropsWithChildren { +export interface InstanceMetricsMemoizedProps extends PropsWithChildren { environmentId: string serviceId: string pods: Pod[] @@ -52,9 +53,9 @@ export interface PodsMetricsMemoizedProps extends PropsWithChildren { // NOTE: Memoized component to avoid loop with re-rendering, because of the useReactTable hook and the WS subscription // https://qovery.atlassian.net/browse/FRT-1391 -const PodsMetricsMemoized = memo(PodsMetricsTable) +const InstanceMetricsMemoized = memo(InstanceMetricsTable) -function PodsMetricsTable({ +function InstanceMetricsTable({ environmentId, serviceId, children, @@ -66,7 +67,7 @@ function PodsMetricsTable({ isRunningStatusesLoading, isServiceError, isServiceLoading, -}: PodsMetricsMemoizedProps) { +}: InstanceMetricsMemoizedProps) { const [sorting, setSorting] = useState([]) const containerImage = match(service) @@ -83,7 +84,7 @@ function PodsMetricsTable({
    - @@ -91,7 +92,7 @@ function PodsMetricsTable({ ) : ( - @@ -103,7 +104,7 @@ function PodsMetricsTable({ cell: (info) => ( - {info.getValue()?.toLowerCase() ?? 'Unknown'} + {info.getValue()?.toLowerCase() ?? 'Unknown'} ), sortingFn: (rowA, rowB, columnId) => { @@ -150,6 +151,7 @@ function PodsMetricsTable({ const memoryColumn = columnHelper.accessor('memory.current', { header: 'Memory', + minSize: 120, cell: (info) => match(info.row.original) .with({ serviceType: ServiceTypeEnum.JOB, state: 'COMPLETED' }, () => null) @@ -159,6 +161,7 @@ function PodsMetricsTable({ const cpuColumn = columnHelper.accessor('cpu.current', { header: 'vCPU', + minSize: 120, cell: (info) => match(info.row.original) .with({ serviceType: ServiceTypeEnum.JOB, state: 'COMPLETED' }, () => null) @@ -192,7 +195,7 @@ function PodsMetricsTable({ const value = info.getValue() return value ? ( - + {dateFormat === 'relative' ? timeAgo(new Date(value)) : dateFullFormat(value)} @@ -274,7 +277,7 @@ function PodsMetricsTable({ if (pods.length === 0 && !isMetricsLoading && isRunningStatusesLoading) { // NOTE: runningStatuses may never resolve if service not started - return + return } else if ( (pods.length === 0 && !isMetricsLoading && !isRunningStatusesLoading) || isMetricsError || @@ -282,25 +285,30 @@ function PodsMetricsTable({ isServiceError ) { return ( -
    - - Metrics for instances are not available, try again - There is a technical issue on retrieving the instance metrics. -
    + ) } else if (isServiceLoading || pods.length === 0) { - return + return } return ( <> -
    - +
    + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( - + @@ -348,7 +366,7 @@ function PodsMetricsTable({ ))} {row.getIsExpanded() && row.original.containers && service?.serviceType && ( - + {/* 2nd row is a custom 1 cell row */} @@ -365,12 +383,12 @@ function PodsMetricsTable({ ) } -export interface PodsMetricsProps extends PropsWithChildren { +export interface InstanceMetricsProps extends PropsWithChildren { environmentId: string serviceId: string } -export function PodsMetrics(props: PodsMetricsProps) { +export function InstanceMetrics(props: InstanceMetricsProps) { const { environmentId, serviceId } = props const { @@ -405,7 +423,7 @@ export function PodsMetrics(props: PodsMetricsProps) { }, [metrics, runningStatuses]) return ( - +
    - discoverModalContent && + children && openModal({ - content: discoverModalContent, + content: children, options: { width: 680, }, @@ -155,7 +150,7 @@ export function ObservabilityCallout({ discoverModalContent }: ObservabilityCall diff --git a/libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap b/libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap deleted file mode 100644 index 47ae3044e99..00000000000 --- a/libs/domains/services/feature/src/lib/service-overview/service-header/__snapshots__/service-details.spec.tsx.snap +++ /dev/null @@ -1,286 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`ServiceDetails should match application snapshot 1`] = ` - -
    -
    -
    -
    - - About - - -