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/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..7f198c97f74 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -0,0 +1,64 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +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 { 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' +)({ + component: RouteComponent, +}) + +const WebSocketListenerMemo = memo(MetricsWebSocketListener) + +function RouteComponent() { + const { organizationId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + const { data: service } = useService({ environmentId, serviceId }) + const { data: environment } = useEnvironment({ environmentId }) + 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 ( + <> + : undefined} + hasNoMetrics={hasNoMetrics} + observabilityCallout={ + + + + } + /> + {environment && service?.serviceType && ( + + )} + + ) +} 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/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-helm/feature/src/lib/values-override-yaml-modal/__snapshots__/values-override-yaml-modal.spec.tsx.snap b/libs/domains/service-helm/feature/src/lib/values-override-yaml-modal/__snapshots__/values-override-yaml-modal.spec.tsx.snap index 1725533d408..cdb952c6568 100644 --- a/libs/domains/service-helm/feature/src/lib/values-override-yaml-modal/__snapshots__/values-override-yaml-modal.spec.tsx.snap +++ b/libs/domains/service-helm/feature/src/lib/values-override-yaml-modal/__snapshots__/values-override-yaml-modal.spec.tsx.snap @@ -94,7 +94,7 @@ exports[`ValuesOverrideYamlModal should match snapshot 1`] = ` >