diff --git a/apps/console/src/routeTree.gen.ts b/apps/console/src/routeTree.gen.ts
index 7cdf6d22e3c..b804a3eecfc 100644
--- a/apps/console/src/routeTree.gen.ts
+++ b/apps/console/src/routeTree.gen.ts
@@ -87,13 +87,16 @@ import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSetting
import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings'
import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAddonsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/addons'
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 AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/index'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments'
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'
@@ -103,7 +106,6 @@ import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnviron
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/index'
-import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest'
@@ -115,6 +117,7 @@ import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnviron
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/route'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/route'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateTerraformIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/terraform/index'
@@ -123,6 +126,7 @@ import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnviron
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/index'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/index'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/index'
@@ -155,6 +159,8 @@ import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnviron
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
+import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments'
import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables'
@@ -775,14 +781,6 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
} as any,
)
-const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute =
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport.update(
- {
- id: '/project/$projectId/environment/$environmentId/variables',
- path: '/project/$projectId/environment/$environmentId/variables',
- getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
- } as any,
- )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport.update(
{
@@ -799,6 +797,14 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEdit
getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteImport.update(
+ {
+ id: '/project/$projectId/environment/$environmentId/variables',
+ path: '/project/$projectId/environment/$environmentId/variables',
+ getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport.update(
{
@@ -815,6 +821,15 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRouteImport.update(
+ {
+ id: '/',
+ path: '/',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport.update(
{
@@ -833,6 +848,24 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRouteImport.update(
+ {
+ id: '/external-secrets',
+ path: '/external-secrets',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute,
+ } as any,
+ )
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRouteImport.update(
+ {
+ id: '/built-in',
+ path: '/built-in',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport.update(
{
@@ -910,14 +943,6 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
} as any,
)
-const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute =
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteImport.update(
- {
- id: '/project/$projectId/environment/$environmentId/service/$serviceId/variables',
- path: '/project/$projectId/environment/$environmentId/service/$serviceId/variables',
- getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
- } as any,
- )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRouteImport.update(
{
@@ -1006,6 +1031,14 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteImport.update(
+ {
+ id: '/project/$projectId/environment/$environmentId/service/$serviceId/variables',
+ path: '/project/$projectId/environment/$environmentId/service/$serviceId/variables',
+ getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteImport.update(
{
@@ -1076,6 +1109,15 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRouteImport.update(
+ {
+ id: '/',
+ path: '/',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRouteImport.update(
{
@@ -1363,6 +1405,24 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute,
} as any,
)
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRouteImport.update(
+ {
+ id: '/external-secrets',
+ path: '/external-secrets',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute,
+ } as any,
+ )
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRouteImport.update(
+ {
+ id: '/built-in',
+ path: '/built-in',
+ getParentRoute: () =>
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute,
+ } as any,
+ )
const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute =
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRouteImport.update(
{
@@ -1640,9 +1700,9 @@ export interface FileRoutesByFullPath {
'/organization/$organizationId/project/$projectId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute
- '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/new': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute
@@ -1650,10 +1710,14 @@ export interface FileRoutesByFullPath {
'/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute
'/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/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/overview/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRouteWithChildren
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren
@@ -1665,7 +1729,6 @@ export interface FileRoutesByFullPath {
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute
- '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute
@@ -1688,6 +1751,8 @@ export interface FileRoutesByFullPath {
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute
@@ -1720,6 +1785,7 @@ export interface FileRoutesByFullPath {
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute
@@ -1802,7 +1868,6 @@ export interface FileRoutesByTo {
'/organization/$organizationId/project/$projectId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute
'/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute
- '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/new': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute
@@ -1810,14 +1875,16 @@ export interface FileRoutesByTo {
'/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute
'/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/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute
- '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute
@@ -1840,6 +1907,8 @@ export interface FileRoutesByTo {
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute
@@ -1872,6 +1941,7 @@ export interface FileRoutesByTo {
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobIndexRoute
'/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute
@@ -1963,9 +2033,9 @@ export interface FileRoutesById {
'/_authenticated/organization/$organizationId/project/$projectId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute
@@ -1973,10 +2043,14 @@ export interface FileRoutesById {
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute
'/_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/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRouteWithChildren
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren
@@ -1988,7 +2062,6 @@ export interface FileRoutesById {
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute
@@ -2011,6 +2084,8 @@ export interface FileRoutesById {
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute
@@ -2043,6 +2118,7 @@ export interface FileRoutesById {
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobIndexRoute
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute
@@ -2134,9 +2210,9 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/settings/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/overview'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments'
- | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/new'
@@ -2144,10 +2220,14 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database'
@@ -2159,7 +2239,6 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
- | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create'
@@ -2182,6 +2261,8 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports'
@@ -2214,6 +2295,7 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/'
@@ -2296,7 +2378,6 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/settings'
| '/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments'
- | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/new'
@@ -2304,14 +2385,16 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/overview'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/settings'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
- | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create'
@@ -2334,6 +2417,8 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports'
@@ -2366,6 +2451,7 @@ export interface FileRouteTypes {
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings'
+ | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job'
| '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database'
@@ -2456,9 +2542,9 @@ export interface FileRouteTypes {
| '/_authenticated/organization/$organizationId/project/$projectId/settings/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments'
- | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new'
@@ -2466,10 +2552,14 @@ export interface FileRouteTypes {
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules'
| '/_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/variables/built-in'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database'
@@ -2481,7 +2571,6 @@ export interface FileRouteTypes {
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/manifest'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
- | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/'
@@ -2504,6 +2593,8 @@ export interface FileRouteTypes {
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports'
@@ -2536,6 +2627,7 @@ export interface FileRouteTypes {
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/'
+ | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/cron-job/'
| '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/'
@@ -3102,13 +3194,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
}
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables': {
- id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
- path: '/project/$projectId/environment/$environmentId/variables'
- fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
- preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport
- parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
- }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments'
path: '/project/$projectId/environment/$environmentId/deployments'
@@ -3123,6 +3208,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
+ path: '/project/$projectId/environment/$environmentId/variables'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings'
path: '/project/$projectId/environment/$environmentId/settings'
@@ -3137,6 +3229,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/'
+ path: '/'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/'
path: '/'
@@ -3151,6 +3250,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
+ path: '/external-secrets'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute
+ }
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
+ path: '/built-in'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments'
path: '/preview-environments'
@@ -3214,13 +3327,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
}
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': {
- id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
- path: '/project/$projectId/environment/$environmentId/service/$serviceId/variables'
- fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
- preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteImport
- parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
- }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
path: '/project/$projectId/environment/$environmentId/service/$serviceId/service-logs'
@@ -3298,6 +3404,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
+ path: '/project/$projectId/environment/$environmentId/service/$serviceId/variables'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings'
path: '/project/$projectId/environment/$environmentId/service/$serviceId/settings'
@@ -3354,6 +3467,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/'
+ path: '/'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/'
path: '/'
@@ -3578,6 +3698,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRouteImport
parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute
}
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
+ path: '/external-secrets'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute
+ }
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in': {
+ id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+ path: '/built-in'
+ fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+ preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRouteImport
+ parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute
+ }
'/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file': {
id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file'
path: '/values-override-file'
@@ -3972,6 +4106,27 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren,
)
+interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteChildren {
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute
+}
+
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteChildren =
+ {
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesBuiltInRoute,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesExternalSecretsRoute,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesIndexRoute,
+ }
+
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteWithChildren =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute._addFileChildren(
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteChildren,
+ )
+
interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteChildren {
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute
@@ -4077,6 +4232,27 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteChildren,
)
+interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteChildren {
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute
+}
+
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteChildren =
+ {
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesBuiltInRoute,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesExternalSecretsRoute,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesIndexRoute,
+ }
+
+const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteWithChildren =
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute._addFileChildren(
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteChildren,
+ )
+
interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteChildren {
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute
@@ -4277,13 +4453,14 @@ interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren {
AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRouteWithChildren
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren
@@ -4295,7 +4472,6 @@ interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren {
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdManifestRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute
@@ -4349,12 +4525,12 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteRouteWithChildren,
AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute,
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute:
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute:
@@ -4363,6 +4539,8 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren,
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRoute:
+ AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRouteRouteWithChildren,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateCronJobRouteRoute:
@@ -4385,8 +4563,6 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute,
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute:
- AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute:
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute,
AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute:
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/addons.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/addons.tsx
index c81fe757f3c..f455889b667 100644
--- a/apps/console/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/addons.tsx
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/addons.tsx
@@ -5,6 +5,7 @@ import { useMemo, useState } from 'react'
import {
AddonToggleCard,
SECRET_MANAGER_OPTIONS,
+ SecretManagerAssociatedExternalSecretsModal,
SecretManagerIntegrationModal,
SecretManagerList,
getSecretManagerOption,
@@ -109,6 +110,21 @@ function RouteComponent() {
})
}
+ const openSecretManagerAssociatedExternalSecretsModal = (integration: SecretManagerAccess) => {
+ openModal({
+ content: (
+
+ ),
+ options: {
+ fakeModal: true,
+ },
+ })
+ }
+
const handleSave = async () => {
if (!cluster) return
@@ -193,6 +209,7 @@ function RouteComponent() {
openSecretManagerModal(getSecretManagerOption(manager.endpoint.mode), manager)
}
onDelete={handleDeleteSecretManager}
+ onViewAssociatedExternalSecrets={openSecretManagerAssociatedExternalSecretsModal}
/>
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx
deleted file mode 100644
index fe5a52c8a81..00000000000
--- a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { createFileRoute, useParams } from '@tanstack/react-router'
-import { Suspense } from 'react'
-import { match } from 'ts-pattern'
-import { isEditableService } from '@qovery/domains/services/data-access'
-import { useDeployService, useService } from '@qovery/domains/services/feature'
-import {
- ImportEnvironmentVariableModalFeature,
- VariableList,
- VariablesActionToolbar,
-} from '@qovery/domains/variables/feature'
-import { Heading, LoaderSpinner, Section, toast, useModal } from '@qovery/shared/ui'
-import { useDocumentTitle } from '@qovery/shared/util-hooks'
-
-export const Route = createFileRoute(
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
-)({
- component: RouteComponent,
-})
-
-function RouteComponent() {
- const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false })
- useDocumentTitle('Service - Variables')
-
- const { data: service } = useService({
- environmentId,
- serviceId,
- suspense: true,
- })
-
- const scope = match(service?.serviceType)
- .with('APPLICATION', () => 'APPLICATION' as const)
- .with('CONTAINER', () => 'CONTAINER' as const)
- .with('JOB', () => 'JOB' as const)
- .with('HELM', () => 'HELM' as const)
- .with('TERRAFORM', () => 'TERRAFORM' as const)
- .otherwise(() => undefined)
-
- const { mutate: deployService } = useDeployService({
- organizationId,
- projectId,
- environmentId,
- })
- const { openModal, closeModal } = useModal()
-
- const toasterCallback = () => {
- if (!service || !isEditableService(service)) {
- return
- }
- deployService({
- serviceId,
- serviceType: service.serviceType,
- })
- }
-
- return (
-
-
-
- }
- >
-
-
-
-
- Service variables
- {scope && (
-
- openModal({
- content: (
-
- ),
- options: {
- width: 750,
- },
- })
- }
- onCreateVariable={() =>
- toast(
- 'success',
- 'Creation success',
- 'You need to redeploy your service for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }
- />
- )}
-
-
-
- {scope && (
-
- {
- toast(
- 'success',
- 'Creation success',
- 'You need to redeploy your service for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }}
- onEditVariable={() => {
- toast(
- 'success',
- 'Edition success',
- 'You need to redeploy your service for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }}
- onDeleteVariable={(variable) => {
- let name = variable.key
- if (name && name.length > 30) {
- name = name.substring(0, 30) + '...'
- }
- toast(
- 'success',
- 'Deletion success',
- `${name} has been deleted. You need to redeploy your service for your changes to be applied.`,
- toasterCallback,
- 'Redeploy'
- )
- }}
- />
-
- )}
-
-
-
- )
-}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in.tsx
new file mode 100644
index 00000000000..51479897e65
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in.tsx
@@ -0,0 +1,12 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { BuiltInTab } from '@qovery/domains/services/feature'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets.tsx
new file mode 100644
index 00000000000..ac6f8440b72
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets.tsx
@@ -0,0 +1,38 @@
+import { Navigate, createFileRoute } from '@tanstack/react-router'
+import { useFeatureFlagEnabled } from 'posthog-js/react'
+import { getServiceVariableScope, useService } from '@qovery/domains/services/feature'
+import { ExternalSecretsTab } from '@qovery/domains/variables/feature'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const { organizationId, projectId, environmentId, serviceId } = Route.useParams()
+ const secretManagerEnabled = useFeatureFlagEnabled('secret-manager') === true
+
+ if (!secretManagerEnabled) {
+ return (
+
+ )
+ }
+
+ return
+}
+
+function ExternalSecretsRouteContent({ environmentId, serviceId }: { environmentId: string; serviceId: string }) {
+ const { data: service } = useService({ environmentId, serviceId, suspense: true })
+ const scope = getServiceVariableScope(service?.serviceType)
+
+ if (!scope) {
+ return null
+ }
+
+ return
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/index.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/index.tsx
new file mode 100644
index 00000000000..740dbfcc397
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/index.tsx
@@ -0,0 +1,12 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { CustomTab } from '@qovery/domains/services/feature'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ return
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/route.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/route.tsx
new file mode 100644
index 00000000000..6c23b89d2f5
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/route.tsx
@@ -0,0 +1,87 @@
+import { type IconName } from '@fortawesome/fontawesome-common-types'
+import { Outlet, createFileRoute, useMatchRoute } from '@tanstack/react-router'
+import { useFeatureFlagEnabled } from 'posthog-js/react'
+import { Suspense } from 'react'
+import { Heading, Icon, LoaderSpinner, Navbar, Section } from '@qovery/shared/ui'
+import { useDocumentTitle } from '@qovery/shared/util-hooks'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables'
+)({
+ component: RouteComponent,
+})
+
+const tabs = [
+ {
+ id: 'custom',
+ label: 'Custom',
+ iconName: 'sliders' as IconName,
+ routeId:
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/',
+ },
+ {
+ id: 'external-secrets',
+ label: 'External secrets',
+ iconName: 'lock-keyhole' as IconName,
+ routeId:
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets',
+ },
+ {
+ id: 'built-in',
+ label: 'Built-in',
+ iconName: 'cube' as IconName,
+ routeId:
+ '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/built-in',
+ },
+]
+
+const OutletLoader = () => (
+
+
+
+)
+
+function RouteComponent() {
+ const matchRoute = useMatchRoute()
+ const secretManagerEnabled = useFeatureFlagEnabled('secret-manager') === true
+ const visibleTabs = secretManagerEnabled ? tabs : tabs.filter((tab) => tab.id !== 'external-secrets')
+ useDocumentTitle('Service - Variables')
+
+ const activeTabId = visibleTabs.find((tab) => matchRoute({ to: tab.routeId }))?.id
+
+ return (
+
+
+
+
+ Service variables
+
+
+
+
+
+
+
+
+ {visibleTabs.map((tab) => (
+
+
+ {tab.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx
deleted file mode 100644
index 6f0edd321c6..00000000000
--- a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { createFileRoute } from '@tanstack/react-router'
-import { useParams } from '@tanstack/react-router'
-import { Suspense } from 'react'
-import { useDeployEnvironment } from '@qovery/domains/environments/feature'
-import { VariableList, VariablesActionToolbar } from '@qovery/domains/variables/feature'
-import { ENVIRONMENT_LOGS_URL, ENVIRONMENT_STAGES_URL } from '@qovery/shared/routes'
-import { Heading, LoaderSpinner, Section, toast } from '@qovery/shared/ui'
-import { useDocumentTitle } from '@qovery/shared/util-hooks'
-
-export const Route = createFileRoute(
- '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
-)({
- component: RouteComponent,
-})
-
-function RouteComponent() {
- const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false })
-
- useDocumentTitle('Services - Variables')
-
- const { mutate: deployEnvironment } = useDeployEnvironment({
- projectId,
- logsLink: ENVIRONMENT_LOGS_URL(organizationId, projectId, environmentId) + ENVIRONMENT_STAGES_URL(),
- })
-
- const toasterCallback = () => {
- deployEnvironment({ environmentId })
- }
-
- return (
-
-
-
- }
- >
-
-
-
-
- Environment variables
-
- toast(
- 'success',
- 'Creation success',
- 'You need to redeploy your environment for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }
- />
-
-
-
-
- {
- toast(
- 'success',
- 'Creation success',
- 'You need to redeploy your environment for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }}
- onEditVariable={() => {
- toast(
- 'success',
- 'Edition success',
- 'You need to redeploy your environment for your changes to be applied.',
- toasterCallback,
- 'Redeploy'
- )
- }}
- onDeleteVariable={(variable) => {
- let name = variable.key
- if (name && name.length > 30) {
- name = name.substring(0, 30) + '...'
- }
- toast(
- 'success',
- 'Deletion success',
- `${name} has been deleted. You need to redeploy your environment for your changes to be applied.`,
- toasterCallback,
- 'Redeploy'
- )
- }}
- />
-
-
-
-
- )
-}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in.tsx
new file mode 100644
index 00000000000..7b4d57ef8d8
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in.tsx
@@ -0,0 +1,67 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { useDeployEnvironment } from '@qovery/domains/environments/feature'
+import { VariableList } from '@qovery/domains/variables/feature'
+import { ENVIRONMENT_LOGS_URL, ENVIRONMENT_STAGES_URL } from '@qovery/shared/routes'
+import { toast } from '@qovery/shared/ui'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const { organizationId, projectId, environmentId } = Route.useParams()
+
+ const { mutate: deployEnvironment } = useDeployEnvironment({
+ projectId,
+ logsLink: ENVIRONMENT_LOGS_URL(organizationId, projectId, environmentId) + ENVIRONMENT_STAGES_URL(),
+ })
+
+ const toasterCallback = () => {
+ deployEnvironment({ environmentId })
+ }
+
+ const onCreateVariableToast = () =>
+ toast(
+ 'success',
+ 'Creation success',
+ 'You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+
+ const onEditVariableToast = () =>
+ toast(
+ 'success',
+ 'Edition success',
+ 'You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+
+ const onDeleteVariableToast = () => {
+ toast(
+ 'success',
+ 'Deletion success',
+ 'Your variable has been deleted. You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+ }
+
+ return (
+ }
+ scope="ENVIRONMENT"
+ organizationId={organizationId}
+ projectId={projectId}
+ environmentId={environmentId}
+ onCreateVariable={onCreateVariableToast}
+ onEditVariable={onEditVariableToast}
+ onDeleteVariable={onDeleteVariableToast}
+ />
+ )
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets.tsx
new file mode 100644
index 00000000000..0fc81b2b406
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets.tsx
@@ -0,0 +1,26 @@
+import { Navigate, createFileRoute } from '@tanstack/react-router'
+import { useFeatureFlagEnabled } from 'posthog-js/react'
+import { ExternalSecretsTab } from '@qovery/domains/variables/feature'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const { organizationId, projectId, environmentId } = Route.useParams()
+ const secretManagerEnabled = useFeatureFlagEnabled('secret-manager') === true
+
+ if (!secretManagerEnabled) {
+ return (
+
+ )
+ }
+
+ return
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/index.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/index.tsx
new file mode 100644
index 00000000000..10ad4f720c7
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/index.tsx
@@ -0,0 +1,75 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { useDeployEnvironment } from '@qovery/domains/environments/feature'
+import { VariableList, VariablesActionToolbar } from '@qovery/domains/variables/feature'
+import { ENVIRONMENT_LOGS_URL, ENVIRONMENT_STAGES_URL } from '@qovery/shared/routes'
+import { toast } from '@qovery/shared/ui'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/'
+)({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const { organizationId, projectId, environmentId } = Route.useParams()
+
+ const { mutate: deployEnvironment } = useDeployEnvironment({
+ projectId,
+ logsLink: ENVIRONMENT_LOGS_URL(organizationId, projectId, environmentId) + ENVIRONMENT_STAGES_URL(),
+ })
+
+ const toasterCallback = () => {
+ deployEnvironment({ environmentId })
+ }
+
+ const onCreateVariableToast = () =>
+ toast(
+ 'success',
+ 'Creation success',
+ 'You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+
+ const onEditVariableToast = () =>
+ toast(
+ 'success',
+ 'Edition success',
+ 'You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+
+ const onDeleteVariableToast = () => {
+ toast(
+ 'success',
+ 'Deletion success',
+ 'Your variable has been deleted. You need to redeploy your environment for your changes to be applied.',
+ toasterCallback,
+ 'Redeploy'
+ )
+ }
+
+ return (
+
+ }
+ scope="ENVIRONMENT"
+ organizationId={organizationId}
+ projectId={projectId}
+ environmentId={environmentId}
+ onCreateVariable={onCreateVariableToast}
+ onEditVariable={onEditVariableToast}
+ onDeleteVariable={onDeleteVariableToast}
+ />
+ )
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/route.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/route.tsx
new file mode 100644
index 00000000000..88aa02173fc
--- /dev/null
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables/route.tsx
@@ -0,0 +1,84 @@
+import { type IconName } from '@fortawesome/fontawesome-common-types'
+import { Outlet, createFileRoute, useMatchRoute } from '@tanstack/react-router'
+import { useFeatureFlagEnabled } from 'posthog-js/react'
+import { Suspense } from 'react'
+import { Heading, Icon, LoaderSpinner, Navbar, Section } from '@qovery/shared/ui'
+import { useDocumentTitle } from '@qovery/shared/util-hooks'
+
+export const Route = createFileRoute(
+ '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables'
+)({
+ component: RouteComponent,
+})
+
+const tabs = [
+ {
+ id: 'custom',
+ label: 'Custom',
+ iconName: 'sliders' as IconName,
+ routeId: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/',
+ },
+ {
+ id: 'external-secrets',
+ label: 'External secrets',
+ iconName: 'lock-keyhole' as IconName,
+ routeId: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets',
+ },
+ {
+ id: 'built-in',
+ label: 'Built-in',
+ iconName: 'cube' as IconName,
+ routeId: '/organization/$organizationId/project/$projectId/environment/$environmentId/variables/built-in',
+ },
+]
+
+const OutletLoader = () => (
+
+
+
+)
+
+function RouteComponent() {
+ const matchRoute = useMatchRoute()
+ const secretManagerEnabled = useFeatureFlagEnabled('secret-manager') === true
+ const visibleTabs = secretManagerEnabled ? tabs : tabs.filter((tab) => tab.id !== 'external-secrets')
+ useDocumentTitle('Environment - Variables')
+
+ const activeTabId = visibleTabs.find((tab) => matchRoute({ to: tab.routeId }))?.id
+
+ return (
+
+
+
+
+ Environment variables
+
+
+
+
+
+
+
+
+ {visibleTabs.map((tab) => (
+
+
+ {tab.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx
index 3bdef0f648f..c1a798efb5f 100644
--- a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx
+++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx
@@ -1,7 +1,9 @@
import { createFileRoute, useParams } from '@tanstack/react-router'
-import { Suspense } from 'react'
+import { Suspense, useState } from 'react'
import { VariableList, VariablesActionToolbar } from '@qovery/domains/variables/feature'
-import { Heading, LoaderSpinner, Section, toast } from '@qovery/shared/ui'
+import { Heading, Icon, LoaderSpinner, Navbar, Section, toast } from '@qovery/shared/ui'
+
+type VariableTab = 'custom' | 'built-in'
export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/variables')({
component: RouteComponent,
@@ -9,6 +11,7 @@ export const Route = createFileRoute('/_authenticated/organization/$organization
function RouteComponent() {
const { projectId = '' } = useParams({ strict: false })
+ const [activeTab, setActiveTab] = useState('custom')
return (
Project variables
- toast('success', 'Creation success')}
- />
-
-
{
- toast('success', 'Creation success')
- }}
- onEditVariable={() => {
- toast('success', 'Edition success')
- }}
- onDeleteVariable={() => {
- toast('success', 'Deletion success')
- }}
- />
+
+
+
+
+
+ setActiveTab('custom')}>
+
+ Custom
+
+ setActiveTab('built-in')}>
+
+ Built-in
+
+
+
+
+
+
+
+ {activeTab === 'custom' && (
+ toast('success', 'Creation success')}
+ />
+ }
+ scope="PROJECT"
+ projectId={projectId}
+ onCreateVariable={() => {
+ toast('success', 'Creation success')
+ }}
+ onEditVariable={() => {
+ toast('success', 'Edition success')
+ }}
+ onDeleteVariable={() => {
+ toast('success', 'Deletion success')
+ }}
+ />
+ )}
+ {activeTab === 'built-in' && (
+ }
+ scope="PROJECT"
+ projectId={projectId}
+ onCreateVariable={() => {
+ toast('success', 'Creation success')
+ }}
+ onEditVariable={() => {
+ toast('success', 'Edition success')
+ }}
+ onDeleteVariable={() => {
+ toast('success', 'Deletion success')
+ }}
+ />
+ )}
+
+
diff --git a/libs/domains/clusters/feature/src/index.ts b/libs/domains/clusters/feature/src/index.ts
index 52224bdcd5f..5620205fa58 100644
--- a/libs/domains/clusters/feature/src/index.ts
+++ b/libs/domains/clusters/feature/src/index.ts
@@ -36,6 +36,7 @@ export * from './lib/cluster-creation-flow/step-addons/step-addons'
export * from './lib/cluster-creation-flow/step-summary/step-summary'
export * from './lib/cluster-creation-flow/cluster-creation-flow'
export * from './lib/cluster-addons'
+export * from './lib/secret-manager-modals/secret-manager-associated-external-secrets-modal'
export * from './lib/secret-manager-modals/secret-manager-integration-modal'
export * from './lib/cluster-general-settings/cluster-general-settings'
export * from './lib/cluster-credentials-settings/cluster-credentials-settings'
diff --git a/libs/domains/clusters/feature/src/lib/cluster-credentials-modal/cluster-credentials-modal.tsx b/libs/domains/clusters/feature/src/lib/cluster-credentials-modal/cluster-credentials-modal.tsx
index e6af204788d..90e0599de98 100644
--- a/libs/domains/clusters/feature/src/lib/cluster-credentials-modal/cluster-credentials-modal.tsx
+++ b/libs/domains/clusters/feature/src/lib/cluster-credentials-modal/cluster-credentials-modal.tsx
@@ -9,7 +9,6 @@ import {
useDeleteCloudProviderCredential,
useEditCloudProviderCredential,
} from '@qovery/domains/cloud-providers/feature'
-import { CLUSTER_SETTINGS_IMAGE_REGISTRY_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes'
import {
Button,
Callout,
@@ -172,7 +171,7 @@ function CalloutEdit({
{clusterId && (
Go to mirroring registry section
diff --git a/libs/domains/clusters/feature/src/lib/cluster-credentials-settings/cluster-credentials-settings.tsx b/libs/domains/clusters/feature/src/lib/cluster-credentials-settings/cluster-credentials-settings.tsx
index 95603ab63ab..af134d2ed28 100644
--- a/libs/domains/clusters/feature/src/lib/cluster-credentials-settings/cluster-credentials-settings.tsx
+++ b/libs/domains/clusters/feature/src/lib/cluster-credentials-settings/cluster-credentials-settings.tsx
@@ -3,7 +3,6 @@ import { type ClusterCredentials } from 'qovery-typescript-axios'
import { useCallback } from 'react'
import { Controller, useFormContext } from 'react-hook-form'
import { useCloudProviderCredentials } from '@qovery/domains/cloud-providers/feature'
-import { CLUSTER_SETTINGS_IMAGE_REGISTRY_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes'
import { Callout, ExternalLink, Icon, InputSelect, Link, LoaderSpinner, useModal } from '@qovery/shared/ui'
import {
ClusterCredentialsModal,
@@ -127,11 +126,7 @@ export function ClusterCredentialsSettings({ cloudProvider, isSetting }: Cluster
the credentials properly in this cluster's mirroring registry section.
Go to mirroring registry section
diff --git a/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.spec.tsx b/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.spec.tsx
new file mode 100644
index 00000000000..35a774573e6
--- /dev/null
+++ b/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.spec.tsx
@@ -0,0 +1,134 @@
+import { APIVariableScopeEnum, type ExternalSecretAssociatedServiceResponse } from 'qovery-typescript-axios'
+import { renderWithProviders, screen } from '@qovery/shared/util-tests'
+import * as useSecretManagerAssociatedServices from '../hooks/use-secret-manager-associated-services/use-secret-manager-associated-services'
+import SecretManagerAssociatedExternalSecretsModal, {
+ type SecretManagerAssociatedExternalSecretsModalProps,
+ groupExternalSecretsByProjectEnvironment,
+} from './secret-manager-associated-external-secrets-modal'
+
+const useSecretManagerAssociatedServicesMockSpy = jest.spyOn(
+ useSecretManagerAssociatedServices,
+ 'useSecretManagerAssociatedServices'
+) as jest.Mock
+
+const props: SecretManagerAssociatedExternalSecretsModalProps = {
+ organizationId: '0000-0000-0000',
+ secretManagerAccessId: 'secret-manager-access-id',
+ onClose: jest.fn(),
+}
+
+const data: ExternalSecretAssociatedServiceResponse[] = [
+ {
+ project_id: 'project-1',
+ project_name: 'Project 1',
+ environment_id: 'environment-1',
+ environment_name: 'Development',
+ variable_name: 'ENV_EXT_DATABASE',
+ external_secret_name: 'staging/application/database',
+ },
+ {
+ project_id: 'project-1',
+ project_name: 'Project 1',
+ environment_id: 'environment-1',
+ environment_name: 'Development',
+ service_id: 'service-1',
+ service_name: 'Backend API',
+ service_type: APIVariableScopeEnum.APPLICATION,
+ variable_name: 'CREDENTIALS',
+ external_secret_name: 'staging/database/credentials',
+ },
+ {
+ project_id: 'project-1',
+ project_name: 'Project 1',
+ environment_id: 'environment-1',
+ environment_name: 'Development',
+ service_id: 'service-1',
+ service_name: 'Backend API',
+ service_type: APIVariableScopeEnum.APPLICATION,
+ variable_name: 'DATABASE',
+ external_secret_name: 'staging/application/database',
+ },
+]
+
+describe('SecretManagerAssociatedExternalSecretsModal', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ useSecretManagerAssociatedServicesMockSpy.mockReturnValue({
+ data,
+ isLoading: false,
+ })
+ })
+
+ it('should group external secrets by project, environment, and service', () => {
+ const result = groupExternalSecretsByProjectEnvironment(data)
+
+ expect(result).toHaveLength(1)
+ expect(result[0].environments[0].externalSecrets).toHaveLength(1)
+ expect(result[0].environments[0].services).toHaveLength(1)
+ expect(result[0].environments[0].services[0]).toMatchObject({
+ service_id: 'service-1',
+ service_name: 'Backend API',
+ externalSecrets: [
+ {
+ variable_name: 'CREDENTIALS',
+ external_secret_name: 'staging/database/credentials',
+ },
+ {
+ variable_name: 'DATABASE',
+ external_secret_name: 'staging/application/database',
+ },
+ ],
+ })
+ })
+
+ it('should render environment and service sections with service depth', async () => {
+ const { userEvent } = renderWithProviders( )
+
+ expect(screen.getByRole('heading', { name: 'Associated external secrets' })).toBeInTheDocument()
+ expect(screen.getByRole('heading', { name: 'Environment external secret' })).toBeInTheDocument()
+ expect(screen.getByRole('heading', { name: 'Service external secrets' })).toBeInTheDocument()
+
+ await userEvent.click(screen.getAllByRole('button')[0])
+ await userEvent.click(screen.getAllByRole('button')[1])
+
+ expect(screen.getByText('ENV_EXT_DATABASE')).toBeInTheDocument()
+ expect(screen.getByText('staging/application/database')).toBeInTheDocument()
+
+ await userEvent.click(screen.getAllByRole('button')[2])
+ await userEvent.click(screen.getAllByRole('button')[3])
+ await userEvent.click(screen.getAllByRole('button')[4])
+
+ expect(screen.getByText('Backend API')).toBeInTheDocument()
+ expect(screen.getByText('CREDENTIALS')).toBeInTheDocument()
+ expect(screen.getByText('DATABASE')).toBeInTheDocument()
+ })
+
+ it('should search by service name', () => {
+ const result = groupExternalSecretsByProjectEnvironment(data, 'backend')
+
+ expect(result).toHaveLength(1)
+ expect(result[0].environments[0].externalSecrets).toHaveLength(0)
+ expect(result[0].environments[0].services[0]).toMatchObject({
+ service_name: 'Backend API',
+ externalSecrets: [
+ {
+ variable_name: 'CREDENTIALS',
+ },
+ {
+ variable_name: 'DATABASE',
+ },
+ ],
+ })
+ })
+
+ it('should show loading spinner when loading', () => {
+ useSecretManagerAssociatedServicesMockSpy.mockReturnValue({
+ data: [],
+ isLoading: true,
+ })
+
+ renderWithProviders( )
+
+ expect(screen.queryByPlaceholderText(/search/i)).not.toBeInTheDocument()
+ })
+})
diff --git a/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.tsx b/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.tsx
new file mode 100644
index 00000000000..76a6bb24214
--- /dev/null
+++ b/libs/domains/clusters/feature/src/lib/secret-manager-modals/secret-manager-associated-external-secrets-modal.tsx
@@ -0,0 +1,426 @@
+import { type APIVariableScopeEnum, type ExternalSecretAssociatedServiceResponse } from 'qovery-typescript-axios'
+import { useMemo, useState } from 'react'
+import { match } from 'ts-pattern'
+import { IconEnum } from '@qovery/shared/enums'
+import { Heading, Icon, InputSearch, Link, LoaderSpinner, Section, TreeView } from '@qovery/shared/ui'
+import { pluralize } from '@qovery/shared/util-js'
+import { useSecretManagerAssociatedServices } from '../hooks/use-secret-manager-associated-services/use-secret-manager-associated-services'
+
+export interface SecretManagerAssociatedExternalSecretsModalProps {
+ secretManagerAccessId: string
+ organizationId: string
+ onClose: () => void
+}
+
+interface ExternalSecret {
+ id: string
+ variable_name: string
+ external_secret_name: string
+}
+
+interface Service {
+ service_id: string
+ service_name: string
+ service_type?: APIVariableScopeEnum
+ externalSecrets: ExternalSecret[]
+}
+
+interface Environment {
+ environment_id: string
+ environment_name: string
+ externalSecrets: ExternalSecret[]
+ services: Service[]
+}
+
+interface Project {
+ project_id: string
+ project_name: string
+ environments: Environment[]
+}
+
+function buildExternalSecretId({
+ environment_id,
+ service_id,
+ variable_name,
+ external_secret_name,
+}: ExternalSecretAssociatedServiceResponse): string {
+ return [service_id ?? environment_id, variable_name, external_secret_name].filter(Boolean).join('-')
+}
+
+function matchesSearch({
+ item,
+ searchValue,
+}: {
+ item: ExternalSecretAssociatedServiceResponse
+ searchValue?: string
+}): boolean {
+ if (!searchValue) {
+ return true
+ }
+
+ const search = searchValue.toLowerCase()
+
+ return [
+ item.project_name,
+ item.environment_name,
+ item.service_name,
+ item.variable_name,
+ item.external_secret_name,
+ ].some((value) => value?.toLowerCase().includes(search))
+}
+
+function getServiceIconName(serviceType?: APIVariableScopeEnum): IconEnum {
+ return match(serviceType)
+ .with('APPLICATION', () => IconEnum.APPLICATION)
+ .with('CONTAINER', () => IconEnum.CONTAINER)
+ .with('HELM', () => IconEnum.HELM)
+ .with('TERRAFORM', () => IconEnum.TERRAFORM)
+ .with('JOB', () => IconEnum.CRON_JOB)
+ .otherwise(() => IconEnum.SERVICES)
+}
+
+export function groupExternalSecretsByProjectEnvironment(
+ data: ExternalSecretAssociatedServiceResponse[],
+ searchValue?: string
+): Project[] {
+ const projects: Project[] = []
+
+ data.forEach((item) => {
+ if (!matchesSearch({ item, searchValue })) {
+ return
+ }
+
+ let project = projects.find((proj) => proj.project_id === item.project_id)
+ if (!project) {
+ project = {
+ project_id: item.project_id,
+ project_name: item.project_name,
+ environments: [],
+ }
+ projects.push(project)
+ }
+
+ let environment = project.environments.find((env) => env.environment_id === item.environment_id)
+ if (!environment) {
+ environment = {
+ environment_id: item.environment_id,
+ environment_name: item.environment_name,
+ externalSecrets: [],
+ services: [],
+ }
+ project.environments.push(environment)
+ }
+
+ const externalSecret = {
+ id: buildExternalSecretId(item),
+ variable_name: item.variable_name,
+ external_secret_name: item.external_secret_name,
+ }
+
+ if (!item.service_id) {
+ environment.externalSecrets.push(externalSecret)
+ return
+ }
+
+ let service = environment.services.find((service) => service.service_id === item.service_id)
+ if (!service) {
+ service = {
+ service_id: item.service_id,
+ service_name: item.service_name ?? item.service_id,
+ service_type: item.service_type,
+ externalSecrets: [],
+ }
+ environment.services.push(service)
+ }
+
+ service.externalSecrets.push(externalSecret)
+ })
+
+ return projects
+}
+
+function getProjectsWithEnvironmentExternalSecrets(projects: Project[]): Project[] {
+ return projects
+ .map((project) => ({
+ ...project,
+ environments: project.environments
+ .filter((environment) => environment.externalSecrets.length > 0)
+ .map((environment) => ({ ...environment, services: [] })),
+ }))
+ .filter((project) => project.environments.length > 0)
+}
+
+function getProjectsWithServiceExternalSecrets(projects: Project[]): Project[] {
+ return projects
+ .map((project) => ({
+ ...project,
+ environments: project.environments
+ .filter((environment) => environment.services.length > 0)
+ .map((environment) => ({ ...environment, externalSecrets: [] })),
+ }))
+ .filter((project) => project.environments.length > 0)
+}
+
+function EmptyState() {
+ return (
+
+ )
+}
+
+export function SecretManagerAssociatedExternalSecretsModal({
+ secretManagerAccessId,
+ organizationId,
+ onClose,
+}: SecretManagerAssociatedExternalSecretsModalProps) {
+ const { data: associatedExternalSecrets = [], isLoading } = useSecretManagerAssociatedServices({
+ secretManagerAccessId,
+ })
+ const [searchValue, setSearchValue] = useState()
+ const normalizedSearch = searchValue?.trim() || undefined
+
+ const groupedExternalSecrets = useMemo(
+ () => groupExternalSecretsByProjectEnvironment(associatedExternalSecrets, normalizedSearch),
+ [associatedExternalSecrets, normalizedSearch]
+ )
+
+ const environmentExternalSecrets = useMemo(
+ () => associatedExternalSecrets.filter((externalSecret) => !externalSecret.service_id),
+ [associatedExternalSecrets]
+ )
+ const serviceExternalSecrets = useMemo(
+ () => associatedExternalSecrets.filter((externalSecret) => externalSecret.service_id),
+ [associatedExternalSecrets]
+ )
+
+ const environmentTreeData = useMemo(
+ () => getProjectsWithEnvironmentExternalSecrets(groupedExternalSecrets),
+ [groupedExternalSecrets]
+ )
+ const serviceTreeData = useMemo(
+ () => getProjectsWithServiceExternalSecrets(groupedExternalSecrets),
+ [groupedExternalSecrets]
+ )
+
+ const hasEnvironmentExternalSecrets = environmentExternalSecrets.length > 0
+ const hasServiceExternalSecrets = serviceExternalSecrets.length > 0
+ const hasAnyExternalSecrets = associatedExternalSecrets.length > 0
+ const noSearchResults =
+ Boolean(normalizedSearch) &&
+ hasAnyExternalSecrets &&
+ environmentTreeData.length === 0 &&
+ serviceTreeData.length === 0
+
+ return (
+
+
+ Associated {pluralize(associatedExternalSecrets.length, 'external secret')}
+
+ {isLoading ? (
+
+
+
+ ) : (
+ <>
+ setSearchValue(value)}
+ />
+ {noSearchResults || !hasAnyExternalSecrets ? (
+
+ ) : (
+
+ {hasEnvironmentExternalSecrets && (
+
+
+ {pluralize(environmentExternalSecrets.length, 'Environment external secret')}
+
+ {environmentTreeData.length > 0 ? (
+
+ {environmentTreeData.map((project) => (
+
+ {project.project_name}
+
+ {project.environments.map((environment) => (
+
+
+
+ onClose()}
+ to="/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets"
+ params={{
+ organizationId,
+ projectId: project.project_id,
+ environmentId: environment.environment_id,
+ }}
+ className="text-sm"
+ >
+ {environment.environment_name}
+
+
+
+
+ {environment.externalSecrets.map((externalSecret) => (
+
+ onClose()}
+ to="/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets"
+ params={{
+ organizationId,
+ projectId: project.project_id,
+ environmentId: environment.environment_id,
+ }}
+ className="flex items-center py-1.5 pl-5 text-sm"
+ >
+
+
+ {externalSecret.variable_name}
+
+ {externalSecret.external_secret_name}
+
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+ ))}
+
+ ) : (
+
+ No matching {pluralize(environmentExternalSecrets.length, 'environment external secret')}.
+
+ )}
+
+ )}
+ {hasServiceExternalSecrets && (
+
+
+ {pluralize(serviceExternalSecrets.length, 'Service external secret')}
+
+ {serviceTreeData.length > 0 ? (
+
+ {serviceTreeData.map((project) => (
+
+ {project.project_name}
+
+ {project.environments.map((environment) => (
+
+
+
+ onClose()}
+ to="/organization/$organizationId/project/$projectId/environment/$environmentId/variables/external-secrets"
+ params={{
+ organizationId,
+ projectId: project.project_id,
+ environmentId: environment.environment_id,
+ }}
+ className="text-sm"
+ >
+ {environment.environment_name}
+
+
+
+ {environment.services.map((service) => (
+
+
+
+ onClose()}
+ to="/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets"
+ params={{
+ organizationId,
+ projectId: project.project_id,
+ environmentId: environment.environment_id,
+ serviceId: service.service_id,
+ }}
+ className="flex items-center text-sm"
+ >
+
+ {service.service_name}
+
+
+
+
+ {service.externalSecrets.map((externalSecret) => (
+
+ onClose()}
+ to="/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables/external-secrets"
+ params={{
+ organizationId,
+ projectId: project.project_id,
+ environmentId: environment.environment_id,
+ serviceId: service.service_id,
+ }}
+ className="flex items-center py-1.5 pl-5 text-sm"
+ >
+
+
+ {externalSecret.variable_name}
+
+ {externalSecret.external_secret_name}
+
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ ))}
+
+
+ ))}
+
+ ) : (
+
+ No matching {pluralize(serviceExternalSecrets.length, 'service external secret')}.
+
+ )}
+
+ )}
+
+ )}
+ >
+ )}
+
+ )
+}
+
+export default SecretManagerAssociatedExternalSecretsModal
diff --git a/libs/domains/organizations/feature/src/index.ts b/libs/domains/organizations/feature/src/index.ts
index bacd8532db6..3f537da6808 100644
--- a/libs/domains/organizations/feature/src/index.ts
+++ b/libs/domains/organizations/feature/src/index.ts
@@ -1,7 +1,7 @@
export * from './lib/annotation-setting/annotation-setting'
export * from './lib/annotation-create-edit-modal/annotation-create-edit-modal'
export * from './lib/add-credit-card-modal/add-credit-card-modal'
-export * from './lib/label-annotation-items-list-modal/label-annotation-items-list-modal'
+export * from './lib/associated-items-modal/associated-items-modal'
export * from './lib/git-branch-settings/git-branch-settings'
export * from './lib/git-provider-setting/git-provider-setting'
export * from './lib/git-repository-setting/git-repository-setting'
diff --git a/libs/domains/organizations/feature/src/lib/associated-items-modal/__snapshots__/associated-items-modal.spec.tsx.snap b/libs/domains/organizations/feature/src/lib/associated-items-modal/__snapshots__/associated-items-modal.spec.tsx.snap
new file mode 100644
index 00000000000..938979a3388
--- /dev/null
+++ b/libs/domains/organizations/feature/src/lib/associated-items-modal/__snapshots__/associated-items-modal.spec.tsx.snap
@@ -0,0 +1,166 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`AssociatedItemsModal should match snapshot with services 1`] = `
+
+
+
+
+ Associated items (3)
+
+
+
+
+
+
+
+
+ Services
+
+
+
+
+
+
+
+ Project 1
+
+
+
+
+
+
+
+
+ Project 2
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.spec.tsx b/libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.spec.tsx
similarity index 54%
rename from libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.spec.tsx
rename to libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.spec.tsx
index 5e840b78711..1b6d10e9c55 100644
--- a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.spec.tsx
+++ b/libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.spec.tsx
@@ -1,34 +1,14 @@
-import { type OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner } from 'qovery-typescript-axios'
import { renderWithProviders, screen } from '@qovery/shared/util-tests'
-import * as useAnnotationsGroupAssociatedItems from '../hooks/use-annotations-group-associated-items/use-annotations-group-associated-items'
-import * as useLabelsGroupAssociatedItems from '../hooks/use-labels-group-associated-items/use-labels-group-associated-items'
import {
- LabelAnnotationItemsListModal,
- type LabelAnnotationItemsListModalProps,
+ type AssociatedItem,
+ AssociatedItemsModal,
+ type AssociatedItemsModalProps,
filterClustersForAssociatedItemsModal,
getServiceAssociatedItems,
groupByProjectEnvironmentsServices,
-} from './label-annotation-items-list-modal'
+} from './associated-items-modal'
-const useAnnotationsGroupAssociatedItemsSpy = jest.spyOn(
- useAnnotationsGroupAssociatedItems,
- 'useAnnotationsGroupAssociatedItems'
-) as jest.Mock
-
-const useLabelsGroupAssociatedItemsSpy = jest.spyOn(
- useLabelsGroupAssociatedItems,
- 'useLabelsGroupAssociatedItems'
-) as jest.Mock
-
-const props: LabelAnnotationItemsListModalProps = {
- type: 'annotation',
- organizationId: '0000-0000-0000',
- groupId: '0000-0000-0000',
- onClose: jest.fn(),
- associatedItemsCount: 3,
-}
-
-const data: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[] = [
+const data: AssociatedItem[] = [
{
project_id: '1',
project_name: 'Project 1',
@@ -58,7 +38,7 @@ const data: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[
},
]
-const clusterOnlyData: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[] = [
+const clusterOnlyData: AssociatedItem[] = [
{
cluster_id: null,
cluster_name: null,
@@ -72,17 +52,29 @@ const clusterOnlyData: OrganizationAnnotationsGroupAssociatedItemsResponseListRe
},
]
-describe('LabelAnnotationItemsListModal', () => {
- beforeEach(() => {
- props.type = 'annotation'
- useAnnotationsGroupAssociatedItemsSpy.mockReturnValue({
- data,
- })
- useLabelsGroupAssociatedItemsSpy.mockReturnValue({
- data,
- })
- })
+const externalSecretData: AssociatedItem[] = [
+ {
+ project_id: '1',
+ project_name: 'Project 1',
+ environment_id: '1',
+ environment_name: 'Development',
+ item_id: 'service-1-API_TOKEN-prod/api-token',
+ item_name: 'API_TOKEN',
+ item_type: 'APPLICATION',
+ item_link_id: 'service-1',
+ item_subtitle: 'prod/api-token',
+ },
+]
+const baseProps: AssociatedItemsModalProps = {
+ title: 'Associated items (3)',
+ organizationId: '0000-0000-0000',
+ items: data,
+ isLoading: false,
+ onClose: jest.fn(),
+}
+
+describe('AssociatedItemsModal', () => {
it('should group data by projects, environments, and services correctly', () => {
const result = groupByProjectEnvironmentsServices(data)
@@ -103,21 +95,8 @@ describe('LabelAnnotationItemsListModal', () => {
expect(result[1].environments[0].services).toHaveLength(1)
})
- it('should match snapshots with annotation', async () => {
- const { baseElement, userEvent } = renderWithProviders( )
-
- const triggers = screen.getAllByRole('button')
- screen.getByText(/project 1/i)
- await userEvent.click(triggers[0])
-
- const triggerEnvironment = screen.getByText(/development/i).parentElement
- await userEvent.click(triggerEnvironment!)
-
- expect(baseElement).toMatchSnapshot()
- })
-
- it('should match snapshots with label', async () => {
- const { baseElement, userEvent } = renderWithProviders( )
+ it('should match snapshot with services', async () => {
+ const { baseElement, userEvent } = renderWithProviders( )
const triggers = screen.getAllByRole('button')
screen.getByText(/project 1/i)
@@ -137,24 +116,60 @@ describe('LabelAnnotationItemsListModal', () => {
expect(result[0].environments[0].services).toHaveLength(2)
})
+ it('should preserve service links and subtitles when grouping custom items', () => {
+ const result = groupByProjectEnvironmentsServices(externalSecretData)
+
+ expect(result[0].environments[0].services[0]).toMatchObject({
+ service_id: 'service-1-API_TOKEN-prod/api-token',
+ service_name: 'API_TOKEN',
+ service_link_id: 'service-1',
+ service_subtitle: 'prod/api-token',
+ })
+ })
+
+ it('should render custom item label, placeholder, and subtitle', async () => {
+ const { userEvent } = renderWithProviders(
+
+ )
+
+ expect(screen.getByPlaceholderText('Search by project, environment, or external secret name')).toBeInTheDocument()
+ expect(screen.getByRole('heading', { name: 'External secret' })).toBeInTheDocument()
+
+ await userEvent.click(screen.getAllByRole('button')[0])
+ await userEvent.click(screen.getAllByRole('button')[1])
+
+ expect(screen.getByText('API_TOKEN')).toBeInTheDocument()
+ expect(screen.getByText('prod/api-token')).toBeInTheDocument()
+ })
+
it('should filter clusters by item_name for search', () => {
expect(filterClustersForAssociatedItemsModal(clusterOnlyData, 'obs')).toHaveLength(1)
expect(filterClustersForAssociatedItemsModal(clusterOnlyData, 'nomatch')).toHaveLength(0)
})
it('should render only clusters section and cluster name for CLUSTER-only payload', () => {
- useAnnotationsGroupAssociatedItemsSpy.mockReturnValue({
- data: clusterOnlyData,
- })
- useLabelsGroupAssociatedItemsSpy.mockReturnValue({
- data: clusterOnlyData,
- })
-
- renderWithProviders( )
+ renderWithProviders( )
expect(screen.getByRole('heading', { name: /associated item \(1\)/i })).toBeInTheDocument()
- expect(screen.getByRole('heading', { name: /^clusters$/i })).toBeInTheDocument()
expect(screen.getByText('cluster-for-obs-team')).toBeInTheDocument()
expect(screen.queryByRole('heading', { name: /^services$/i })).not.toBeInTheDocument()
})
+
+ it('should show loading spinner when isLoading is true', () => {
+ renderWithProviders( )
+
+ expect(screen.queryByPlaceholderText(/search/i)).not.toBeInTheDocument()
+ })
+
+ it('should show empty state when items is empty and not loading', () => {
+ renderWithProviders( )
+
+ expect(screen.getByText('No value found')).toBeInTheDocument()
+ })
})
diff --git a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.tsx b/libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.tsx
similarity index 65%
rename from libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.tsx
rename to libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.tsx
index 2a5a2305aec..c190da59e7f 100644
--- a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/label-annotation-items-list-modal.tsx
+++ b/libs/domains/organizations/feature/src/lib/associated-items-modal/associated-items-modal.tsx
@@ -1,19 +1,29 @@
-import {
- type AnnotationsGroupAssociatedItemType,
- type OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner,
-} from 'qovery-typescript-axios'
import { useMemo, useState } from 'react'
import { match } from 'ts-pattern'
import { IconEnum } from '@qovery/shared/enums'
import { Heading, Icon, InputSearch, Link, LoaderSpinner, Section, TreeView } from '@qovery/shared/ui'
import { pluralize } from '@qovery/shared/util-js'
-import { useAnnotationsGroupAssociatedItems } from '../hooks/use-annotations-group-associated-items/use-annotations-group-associated-items'
-import { useLabelsGroupAssociatedItems } from '../hooks/use-labels-group-associated-items/use-labels-group-associated-items'
+
+export interface AssociatedItem {
+ project_id?: string | null
+ project_name?: string | null
+ environment_id?: string | null
+ environment_name?: string | null
+ item_id: string
+ item_name: string
+ item_type: string
+ item_link_id?: string | null
+ item_subtitle?: string | null
+ cluster_id?: string | null
+ cluster_name?: string | null
+}
interface Service {
service_id: string
service_name: string
- service_type: AnnotationsGroupAssociatedItemType
+ service_type: string
+ service_link_id?: string | null
+ service_subtitle?: string | null
}
interface Environment {
@@ -28,71 +38,64 @@ interface Project {
environments: Environment[]
}
-export function groupByProjectEnvironmentsServices(
- data: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[],
- searchValue?: string
-) {
+export function groupByProjectEnvironmentsServices(data: AssociatedItem[], searchValue?: string) {
const projects: Project[] = []
- data.forEach(
- ({
- project_id = '',
- project_name = '',
- environment_id = '',
- environment_name = '',
- item_id,
- item_name = '',
- item_type,
- }) => {
- // Check if the search value is present in the project, environment or service name
- if (
- searchValue === undefined ||
- project_name.toLowerCase().includes(searchValue.toLowerCase()) ||
- environment_name.toLowerCase().includes(searchValue.toLowerCase()) ||
- item_name.toLowerCase().includes(searchValue.toLowerCase())
- ) {
- let project = projects.find((proj) => proj.project_id === project_id)
- if (!project) {
- project = {
- project_id,
- project_name,
- environments: [],
- }
- projects.push(project)
- }
+ data.forEach((item) => {
+ const projectId = item.project_id ?? ''
+ const projectName = item.project_name ?? ''
+ const environmentId = item.environment_id ?? ''
+ const environmentName = item.environment_name ?? ''
+ const itemName = item.item_name ?? ''
- let environment = project.environments.find((env) => env.environment_id === environment_id)
- if (!environment) {
- environment = {
- environment_id,
- environment_name,
- services: [],
- }
- project.environments.push(environment)
+ if (
+ searchValue === undefined ||
+ projectName.toLowerCase().includes(searchValue.toLowerCase()) ||
+ environmentName.toLowerCase().includes(searchValue.toLowerCase()) ||
+ itemName.toLowerCase().includes(searchValue.toLowerCase())
+ ) {
+ let project = projects.find((proj) => proj.project_id === projectId)
+ if (!project) {
+ project = {
+ project_id: projectId,
+ project_name: projectName,
+ environments: [],
}
+ projects.push(project)
+ }
- environment.services.push({ service_id: item_id, service_name: item_name, service_type: item_type })
+ let environment = project.environments.find((env) => env.environment_id === environmentId)
+ if (!environment) {
+ environment = {
+ environment_id: environmentId,
+ environment_name: environmentName,
+ services: [],
+ }
+ project.environments.push(environment)
}
+
+ environment.services.push({
+ service_id: item.item_id,
+ service_name: itemName,
+ service_type: item.item_type,
+ service_link_id: item.item_link_id,
+ service_subtitle: item.item_subtitle,
+ })
}
- )
+ })
return projects
}
-function isClusterAssociatedItem(item: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner): boolean {
+function isClusterAssociatedItem(item: AssociatedItem): boolean {
return item.item_type === 'CLUSTER'
}
-export function getServiceAssociatedItems(
- data: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[]
-): OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[] {
+export function getServiceAssociatedItems(data: AssociatedItem[]): AssociatedItem[] {
return data.filter((item) => !isClusterAssociatedItem(item))
}
-export function filterClustersForAssociatedItemsModal(
- data: OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[],
- searchValue?: string
-): OrganizationAnnotationsGroupAssociatedItemsResponseListResultsInner[] {
+export function filterClustersForAssociatedItemsModal(data: AssociatedItem[], searchValue?: string): AssociatedItem[] {
return data.filter((item) => {
if (!isClusterAssociatedItem(item)) {
return false
@@ -108,51 +111,38 @@ export function filterClustersForAssociatedItemsModal(
})
}
-export interface LabelAnnotationItemsListModalProps {
- type: 'label' | 'annotation'
+export interface AssociatedItemsModalProps {
+ title: string
organizationId: string
- groupId: string
- associatedItemsCount: number
+ items: AssociatedItem[]
+ isLoading: boolean
onClose: () => void
+ searchPlaceholder?: string
+ itemLabel?: string
}
-export function LabelAnnotationItemsListModal({
- type,
+export function AssociatedItemsModal({
+ title,
organizationId,
- groupId,
- associatedItemsCount,
+ items,
+ isLoading,
onClose,
-}: LabelAnnotationItemsListModalProps) {
- const { data: labelsGroupAssociatedItems = [], isLoading: labelsGroupIsLoading } = useLabelsGroupAssociatedItems({
- organizationId,
- labelsGroupId: groupId,
- enabled: type === 'label',
- })
- const { data: annotationsGroupAssociatedItems = [], isLoading: annotationsGroupIsLoading } =
- useAnnotationsGroupAssociatedItems({
- organizationId,
- annotationsGroupId: groupId,
- enabled: type === 'annotation',
- })
-
+ searchPlaceholder = 'Search by project, environment, service, or cluster name',
+ itemLabel = 'Service',
+}: AssociatedItemsModalProps) {
const [searchValue, setSearchValue] = useState()
const normalizedSearch = searchValue?.trim() || undefined
- const rawItems = useMemo(
- () => (type === 'label' ? labelsGroupAssociatedItems : annotationsGroupAssociatedItems),
- [type, labelsGroupAssociatedItems, annotationsGroupAssociatedItems]
- )
-
- const serviceSourceItems = useMemo(() => getServiceAssociatedItems(rawItems), [rawItems])
- const clusterSourceItems = useMemo(() => filterClustersForAssociatedItemsModal(rawItems), [rawItems])
+ const serviceSourceItems = useMemo(() => getServiceAssociatedItems(items), [items])
+ const clusterSourceItems = useMemo(() => filterClustersForAssociatedItemsModal(items), [items])
const serviceTreeData = useMemo(
() => groupByProjectEnvironmentsServices(serviceSourceItems, normalizedSearch),
[serviceSourceItems, normalizedSearch]
)
const clusterRows = useMemo(
- () => filterClustersForAssociatedItemsModal(rawItems, normalizedSearch),
- [rawItems, normalizedSearch]
+ () => filterClustersForAssociatedItemsModal(items, normalizedSearch),
+ [items, normalizedSearch]
)
const hasServicesInSource = serviceSourceItems.length > 0
@@ -160,24 +150,17 @@ export function LabelAnnotationItemsListModal({
const hasAnySource = hasServicesInSource || hasClustersInSource
const noSearchResults =
Boolean(normalizedSearch) && hasAnySource && serviceTreeData.length === 0 && clusterRows.length === 0
- const isLoading = type === 'label' ? labelsGroupIsLoading : annotationsGroupIsLoading
return (
-
- Associated {pluralize(associatedItemsCount, 'item')} ({associatedItemsCount})
-
+ {title}
{isLoading ? (
) : (
<>
- setSearchValue(value)}
- />
+ setSearchValue(value)} />
{noSearchResults ? (
@@ -190,10 +173,10 @@ export function LabelAnnotationItemsListModal({
) : (
- {hasClustersInSource ? (
+ {hasClustersInSource && (
- Clusters
+ {pluralize(clusterSourceItems.length, 'Cluster')}
{clusterRows.length > 0 ? (
@@ -220,11 +203,11 @@ export function LabelAnnotationItemsListModal({
No matching clusters.
)}
- ) : null}
- {hasServicesInSource ? (
+ )}
+ {hasServicesInSource && (
- Services
+ {pluralize(serviceSourceItems.length, itemLabel)}
{serviceTreeData.length > 0 ? (
{environment.services.map((service) => (
-
+
onClose()}
@@ -265,7 +248,7 @@ export function LabelAnnotationItemsListModal({
params={{
organizationId,
environmentId: environment.environment_id,
- serviceId: service.service_id,
+ serviceId: service.service_link_id ?? service.service_id,
projectId: project.project_id,
}}
className="flex items-center py-1.5 pl-5 text-sm"
@@ -277,7 +260,14 @@ export function LabelAnnotationItemsListModal({
width={20}
className="mr-2"
/>
- {service.service_name}
+
+ {service.service_name}
+ {service.service_subtitle && (
+
+ {service.service_subtitle}
+
+ )}
+
))}
@@ -291,10 +281,12 @@ export function LabelAnnotationItemsListModal({
))}
) : (
-
No matching services.
+
+ No matching {pluralize(serviceSourceItems.length, itemLabel.toLowerCase())}.
+
)}
- ) : null}
+ )}
)}
>
@@ -303,4 +295,4 @@ export function LabelAnnotationItemsListModal({
)
}
-export default LabelAnnotationItemsListModal
+export default AssociatedItemsModal
diff --git a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/__snapshots__/label-annotation-items-list-modal.spec.tsx.snap b/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/__snapshots__/label-annotation-items-list-modal.spec.tsx.snap
deleted file mode 100644
index 0f598286743..00000000000
--- a/libs/domains/organizations/feature/src/lib/label-annotation-items-list-modal/__snapshots__/label-annotation-items-list-modal.spec.tsx.snap
+++ /dev/null
@@ -1,339 +0,0 @@
-// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
-
-exports[`LabelAnnotationItemsListModal should match snapshots with annotation 1`] = `
-
-
-
-
- Associated
- items
- (
- 3
- )
-
-
-
-
-
-
-
-
- Services
-
-
-
-
-
-
-
- Project 1
-
-
-
-
-
-
-
-
- Project 2
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`LabelAnnotationItemsListModal should match snapshots with label 1`] = `
-
-
-
-
- Associated
- items
- (
- 3
- )
-
-
-
-
-
-
-
-
- Services
-
-
-
-
-
-
-
- Project 1
-
-
-
-
-
-
-
-
- Project 2
-
-
-
-
-
-
-
-
-
-`;
diff --git a/libs/domains/organizations/feature/src/lib/settings-labels-annotations/settings-labels-annotations.tsx b/libs/domains/organizations/feature/src/lib/settings-labels-annotations/settings-labels-annotations.tsx
index 2412332d493..e0cb786af3f 100644
--- a/libs/domains/organizations/feature/src/lib/settings-labels-annotations/settings-labels-annotations.tsx
+++ b/libs/domains/organizations/feature/src/lib/settings-labels-annotations/settings-labels-annotations.tsx
@@ -19,12 +19,15 @@ import {
} from '@qovery/shared/ui'
import { dateMediumLocalFormat, timeAgo } from '@qovery/shared/util-dates'
import { useDocumentTitle } from '@qovery/shared/util-hooks'
+import { pluralize } from '@qovery/shared/util-js'
import { AnnotationCreateEditModal } from '../annotation-create-edit-modal/annotation-create-edit-modal'
+import { type AssociatedItem, AssociatedItemsModal } from '../associated-items-modal/associated-items-modal'
+import { useAnnotationsGroupAssociatedItems } from '../hooks/use-annotations-group-associated-items/use-annotations-group-associated-items'
import { useAnnotationsGroups } from '../hooks/use-annotations-groups/use-annotations-groups'
import { useDeleteAnnotationsGroup } from '../hooks/use-delete-annotations-group/use-delete-annotations-group'
import { useDeleteLabelsGroup } from '../hooks/use-delete-labels-group/use-delete-labels-group'
+import { useLabelsGroupAssociatedItems } from '../hooks/use-labels-group-associated-items/use-labels-group-associated-items'
import { useLabelsGroups } from '../hooks/use-labels-groups/use-labels-groups'
-import { LabelAnnotationItemsListModal } from '../label-annotation-items-list-modal/label-annotation-items-list-modal'
import { LabelCreateEditModal } from '../label-create-edit-modal/label-create-edit-modal'
const LabelsAnnotationsRowsSkeleton = () => (
@@ -45,6 +48,57 @@ const LabelsAnnotationsRowsSkeleton = () => (
)
+function LabelAnnotationAssociatedItemsContent({
+ type,
+ organizationId,
+ groupId,
+ associatedItemsCount,
+ onClose,
+}: {
+ type: 'label' | 'annotation'
+ organizationId: string
+ groupId: string
+ associatedItemsCount: number
+ onClose: () => void
+}) {
+ const { data: labelsGroupAssociatedItems = [], isLoading: labelsGroupIsLoading } = useLabelsGroupAssociatedItems({
+ organizationId,
+ labelsGroupId: groupId,
+ enabled: type === 'label',
+ })
+ const { data: annotationsGroupAssociatedItems = [], isLoading: annotationsGroupIsLoading } =
+ useAnnotationsGroupAssociatedItems({
+ organizationId,
+ annotationsGroupId: groupId,
+ enabled: type === 'annotation',
+ })
+
+ const rawItems = type === 'label' ? labelsGroupAssociatedItems : annotationsGroupAssociatedItems
+ const isLoading = type === 'label' ? labelsGroupIsLoading : annotationsGroupIsLoading
+
+ const items: AssociatedItem[] = rawItems.map((item) => ({
+ project_id: item.project_id,
+ project_name: item.project_name,
+ environment_id: item.environment_id,
+ environment_name: item.environment_name,
+ item_id: item.item_id,
+ item_name: item.item_name ?? '',
+ item_type: item.item_type,
+ cluster_id: item.cluster_id,
+ cluster_name: item.cluster_name,
+ }))
+
+ return (
+
+ )
+}
+
interface LabelsGroupsListProps {
organizationId: string
onOpenItems: (labelsGroup: OrganizationLabelsGroupEnrichedResponse) => void
@@ -290,7 +344,7 @@ export function SettingsLabelsAnnotations() {
onOpenItems={(labelsGroup) => {
openModal({
content: (
- {
openModal({
content: (
-
+ toast(
+ 'success',
+ 'Creation success',
+ 'You need to redeploy your service for your changes to be applied.',
+ redeployServiceAction,
+ 'Redeploy'
+ )
+
+ if (!scope) {
+ return null
+ }
+
+ return (
+ }
+ scope={scope}
+ serviceId={serviceId}
+ organizationId={organizationId}
+ projectId={projectId}
+ environmentId={environmentId}
+ onCreateVariable={onCreateVariableToast}
+ onEditVariable={() => {
+ toast(
+ 'success',
+ 'Edition success',
+ 'You need to redeploy your service for your changes to be applied.',
+ redeployServiceAction,
+ 'Redeploy'
+ )
+ }}
+ />
+ )
+}
diff --git a/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.spec.tsx b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.spec.tsx
new file mode 100644
index 00000000000..fbdff2c2dc8
--- /dev/null
+++ b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.spec.tsx
@@ -0,0 +1,92 @@
+import { useParams } from '@tanstack/react-router'
+import { type ReactNode } from 'react'
+import { VariableList, useVariables } from '@qovery/domains/variables/feature'
+import { useModal } from '@qovery/shared/ui'
+import { renderWithProviders, screen } from '@qovery/shared/util-tests'
+import { useService } from '../hooks/use-service/use-service'
+import { CustomTab } from './service-variables-custom-tab'
+import { useServiceVariablesTab } from './use-service-variables-tab'
+
+jest.mock('@qovery/domains/variables/feature', () => ({
+ ...jest.requireActual('@qovery/domains/variables/feature'),
+ useVariables: jest.fn(),
+ VariableList: jest.fn(() =>
),
+ CreateUpdateVariableModal: () =>
,
+ ImportEnvironmentVariableModalFeature: () =>
,
+}))
+
+jest.mock('@qovery/shared/ui', () => ({
+ ...jest.requireActual('@qovery/shared/ui'),
+ Tooltip: ({ children }: { children: ReactNode }) => <>{children}>,
+ useModal: jest.fn(),
+}))
+
+jest.mock('@tanstack/react-router', () => ({
+ ...jest.requireActual('@tanstack/react-router'),
+ useParams: jest.fn(),
+}))
+
+jest.mock('../hooks/use-service/use-service', () => ({
+ useService: jest.fn(),
+}))
+
+jest.mock('./use-service-variables-tab', () => ({
+ useServiceVariablesTab: jest.fn(),
+}))
+
+describe('CustomTab', () => {
+ const useVariablesMock = useVariables as jest.MockedFunction
+ const useModalMock = useModal as jest.MockedFunction
+ const variableListMock = VariableList as jest.MockedFunction
+ const useServiceVariablesTabMock = useServiceVariablesTab as jest.MockedFunction
+ const useParamsMock = useParams as jest.MockedFunction
+ const useServiceMock = useService as jest.MockedFunction
+
+ beforeEach(() => {
+ jest.clearAllMocks()
+ useParamsMock.mockReturnValue({
+ organizationId: 'org-id',
+ projectId: 'project-id',
+ environmentId: 'environment-id',
+ serviceId: 'service-id',
+ })
+ useServiceMock.mockReturnValue({
+ data: { serviceType: 'APPLICATION' },
+ } as ReturnType)
+ useServiceVariablesTabMock.mockReturnValue({
+ secretManagers: [],
+ hasClusterSecretManagerConfigured: false,
+ redeployServiceAction: jest.fn(),
+ })
+ useModalMock.mockReturnValue({
+ openModal: jest.fn(),
+ closeModal: jest.fn(),
+ })
+ })
+
+ it('should render custom empty state when there are no custom variables', () => {
+ useVariablesMock.mockReturnValue({
+ data: [],
+ isLoading: false,
+ } as ReturnType)
+
+ renderWithProviders( )
+
+ expect(screen.getByText('No custom variables added yet')).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: /add variable/i })).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: /import variables/i })).toBeInTheDocument()
+ expect(variableListMock).not.toHaveBeenCalled()
+ })
+
+ it('should render variable list when at least one custom variable exists', () => {
+ useVariablesMock.mockReturnValue({
+ data: [{ id: 'var-1', scope: 'APPLICATION' }],
+ isLoading: false,
+ } as ReturnType)
+
+ renderWithProviders( )
+
+ expect(variableListMock).toHaveBeenCalled()
+ expect(screen.queryByText('No custom variables added yet')).not.toBeInTheDocument()
+ })
+})
diff --git a/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.tsx b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.tsx
new file mode 100644
index 00000000000..89409fcb02c
--- /dev/null
+++ b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-custom-tab.tsx
@@ -0,0 +1,181 @@
+import { useParams } from '@tanstack/react-router'
+import {
+ CreateUpdateVariableModal,
+ ImportEnvironmentVariableModalFeature,
+ VariableList,
+ VariablesActionToolbar,
+ useVariables,
+} from '@qovery/domains/variables/feature'
+import { isExternalSecretVariable } from '@qovery/domains/variables/util'
+import { Button, DropdownMenu, EmptyState, Icon, toast, useModal } from '@qovery/shared/ui'
+import { useService } from '../hooks/use-service/use-service'
+import { getServiceVariableScope } from './service-variables-utils'
+import { useServiceVariablesTab } from './use-service-variables-tab'
+
+export function CustomTab() {
+ const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false })
+ const { data: service } = useService({ environmentId, serviceId, suspense: true })
+ const { redeployServiceAction, hasClusterSecretManagerConfigured } = useServiceVariablesTab()
+ const scope = getServiceVariableScope(service?.serviceType)
+ const { openModal, closeModal } = useModal()
+ const { data: variables = [], isLoading: isVariablesLoading } = useVariables({
+ parentId: serviceId,
+ scope,
+ })
+ const customVariables = variables.filter(
+ (variable) => variable.scope !== 'BUILT_IN' && !isExternalSecretVariable(variable)
+ )
+
+ const onCreateVariableToast = () =>
+ toast(
+ 'success',
+ 'Creation success',
+ 'You need to redeploy your service for your changes to be applied.',
+ redeployServiceAction,
+ 'Redeploy'
+ )
+
+ if (!scope) {
+ return null
+ }
+
+ const handleOpenCreateVariableModal = (isFile = false) =>
+ openModal({
+ content: (
+
+ ),
+ options: {
+ fakeModal: true,
+ },
+ })
+
+ const handleOpenImportVariablesModal = () =>
+ openModal({
+ content: (
+
+ ),
+ options: {
+ width: 750,
+ },
+ })
+
+ if (!isVariablesLoading && customVariables.length === 0) {
+ return (
+
+
+
+
+
+
+
+ Add variable
+
+
+
+
+ handleOpenCreateVariableModal()} icon={ }>
+ Variable
+
+ handleOpenCreateVariableModal(true)}
+ icon={ }
+ >
+ Variable as file
+
+
+
+
+
+
+
+
+ Import variables
+
+
+
+
+ }>
+ Import from .env file
+
+ window.open('https://dashboard.doppler.com', '_blank')}
+ icon={ }
+ >
+ Import from Doppler
+
+
+
+
+
+
+ )
+ }
+
+ return (
+
+ }
+ scope={scope}
+ serviceId={serviceId}
+ organizationId={organizationId}
+ projectId={projectId}
+ environmentId={environmentId}
+ onCreateVariable={onCreateVariableToast}
+ onEditVariable={() => {
+ toast(
+ 'success',
+ 'Edition success',
+ 'You need to redeploy your service for your changes to be applied.',
+ redeployServiceAction,
+ 'Redeploy'
+ )
+ }}
+ onDeleteVariable={(variable) => {
+ let name = variable.key
+ if (name && name.length > 30) {
+ name = name.substring(0, 30) + '...'
+ }
+ toast(
+ 'success',
+ 'Deletion success',
+ `${name} has been deleted. You need to redeploy your service for your changes to be applied.`,
+ redeployServiceAction,
+ 'Redeploy'
+ )
+ }}
+ />
+ )
+}
diff --git a/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-utils.ts b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-utils.ts
new file mode 100644
index 00000000000..5668fdd9def
--- /dev/null
+++ b/libs/domains/services/feature/src/lib/service-variables-tabs/service-variables-utils.ts
@@ -0,0 +1,22 @@
+import { match } from 'ts-pattern'
+import { type ServiceType } from '@qovery/domains/services/data-access'
+
+export type ServiceVariableScope = 'APPLICATION' | 'CONTAINER' | 'JOB' | 'HELM' | 'TERRAFORM'
+
+export function getServiceVariableScope(serviceType?: ServiceType): ServiceVariableScope | undefined
+export function getServiceVariableScope(
+ serviceType: ServiceType | undefined,
+ fallbackScope: ServiceVariableScope
+): ServiceVariableScope
+export function getServiceVariableScope(
+ serviceType?: ServiceType,
+ fallbackScope?: ServiceVariableScope
+): ServiceVariableScope | undefined {
+ return match(serviceType)
+ .with('APPLICATION', () => 'APPLICATION' as const)
+ .with('CONTAINER', () => 'CONTAINER' as const)
+ .with('JOB', () => 'JOB' as const)
+ .with('HELM', () => 'HELM' as const)
+ .with('TERRAFORM', () => 'TERRAFORM' as const)
+ .otherwise(() => fallbackScope)
+}
diff --git a/libs/domains/services/feature/src/lib/service-variables-tabs/use-service-variables-tab.ts b/libs/domains/services/feature/src/lib/service-variables-tabs/use-service-variables-tab.ts
new file mode 100644
index 00000000000..51966a32e76
--- /dev/null
+++ b/libs/domains/services/feature/src/lib/service-variables-tabs/use-service-variables-tab.ts
@@ -0,0 +1,44 @@
+import { useParams } from '@tanstack/react-router'
+import { useFeatureFlagEnabled } from 'posthog-js/react'
+import { isEditableServiceType } from '@qovery/domains/services/data-access'
+import { useVariablesSecretManagers } from '@qovery/domains/variables/feature'
+import { useDeployService } from '../hooks/use-deploy-service/use-deploy-service'
+import { useService } from '../hooks/use-service/use-service'
+
+export function useServiceVariablesTab() {
+ const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false })
+ const secretManagerEnabled = useFeatureFlagEnabled('secret-manager') === true
+
+ const { secretManagers, hasClusterSecretManagerConfigured, clusterId } = useVariablesSecretManagers({
+ enabled: secretManagerEnabled,
+ })
+ const { data: service } = useService({
+ environmentId,
+ serviceId,
+ suspense: true,
+ })
+
+ const { mutate: deployService } = useDeployService({
+ organizationId,
+ projectId,
+ environmentId,
+ })
+
+ const redeployServiceAction = () => {
+ if (!service?.serviceType || !isEditableServiceType(service.serviceType)) {
+ return
+ }
+
+ deployService({
+ serviceId,
+ serviceType: service.serviceType,
+ })
+ }
+
+ return {
+ secretManagers,
+ hasClusterSecretManagerConfigured: secretManagerEnabled && hasClusterSecretManagerConfigured,
+ redeployServiceAction,
+ clusterId,
+ }
+}
diff --git a/libs/domains/variables/feature/src/index.ts b/libs/domains/variables/feature/src/index.ts
index b1d0191929b..2db76182a2d 100644
--- a/libs/domains/variables/feature/src/index.ts
+++ b/libs/domains/variables/feature/src/index.ts
@@ -14,5 +14,6 @@ export * from './lib/flow-create-variable/flow-create-variable'
export * from './lib/field-variable-suggestion/field-variable-suggestion'
export * from './lib/dropdown-variable/dropdown-variable'
export * from './lib/import-environment-variable-modal/import-environment-variable-modal'
+export * from './lib/external-secrets/external-secrets-tab'
export * from './lib/external-secrets/add-secret-modal/add-secret-modal'
export * from './lib/external-secrets/use-variables-secret-managers'
diff --git a/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.spec.tsx b/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.spec.tsx
new file mode 100644
index 00000000000..c26d2122dc1
--- /dev/null
+++ b/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.spec.tsx
@@ -0,0 +1,309 @@
+import { useParams } from '@tanstack/react-router'
+import { type ReactElement, type ReactNode } from 'react'
+import { useModal, useModalConfirmation } from '@qovery/shared/ui'
+import { renderWithProviders, screen } from '@qovery/shared/util-tests'
+import { useCreateVariable } from '../hooks/use-create-variable/use-create-variable'
+import { useDeleteVariable } from '../hooks/use-delete-variable/use-delete-variable'
+import { useEditVariable } from '../hooks/use-edit-variable/use-edit-variable'
+import { useVariables } from '../hooks/use-variables/use-variables'
+import { type AddSecretModalSubmitData } from './add-secret-modal/add-secret-modal'
+import { ExternalSecretsTab } from './external-secrets-tab'
+import { useVariablesSecretManagers } from './use-variables-secret-managers'
+
+jest.mock('../hooks/use-variables/use-variables', () => ({
+ useVariables: jest.fn(),
+}))
+
+jest.mock('../hooks/use-create-variable/use-create-variable', () => ({
+ useCreateVariable: jest.fn(),
+}))
+
+jest.mock('../hooks/use-edit-variable/use-edit-variable', () => ({
+ useEditVariable: jest.fn(),
+}))
+
+jest.mock('../hooks/use-delete-variable/use-delete-variable', () => ({
+ useDeleteVariable: jest.fn(),
+}))
+
+jest.mock('@qovery/shared/ui', () => ({
+ ...jest.requireActual('@qovery/shared/ui'),
+ Tooltip: ({ children }: { children: ReactNode }) => children,
+ useModal: jest.fn(),
+ useModalConfirmation: jest.fn(),
+}))
+
+jest.mock('@tanstack/react-router', () => ({
+ ...jest.requireActual('@tanstack/react-router'),
+ useParams: jest.fn(),
+ Link: ({
+ to,
+ params,
+ children,
+ }: {
+ to: string
+ params?: { organizationId?: string; clusterId?: string }
+ children: ReactNode
+ }) => {
+ const href = to
+ .replace('$organizationId', params?.organizationId ?? '')
+ .replace('$clusterId', params?.clusterId ?? '')
+ return {children}
+ },
+}))
+
+jest.mock('./use-variables-secret-managers', () => ({
+ useVariablesSecretManagers: jest.fn(),
+}))
+
+describe('ExternalSecretsTab', () => {
+ const useVariablesMock = useVariables as jest.MockedFunction
+ const useCreateVariableMock = useCreateVariable as jest.MockedFunction
+ const useEditVariableMock = useEditVariable as jest.MockedFunction
+ const useDeleteVariableMock = useDeleteVariable as jest.MockedFunction
+ const useModalMock = useModal as jest.MockedFunction
+ const useModalConfirmationMock = useModalConfirmation as jest.MockedFunction
+ const useVariablesSecretManagersMock = useVariablesSecretManagers as jest.MockedFunction<
+ typeof useVariablesSecretManagers
+ >
+ const useParamsMock = useParams as jest.MockedFunction
+
+ beforeEach(() => {
+ jest.clearAllMocks()
+ useParamsMock.mockReturnValue({
+ organizationId: 'organization-id',
+ environmentId: 'environment-id',
+ serviceId: 'service-id',
+ })
+ useVariablesSecretManagersMock.mockReturnValue({
+ secretManagers: [],
+ hasClusterSecretManagerConfigured: false,
+ clusterId: 'cluster-id',
+ })
+ useCreateVariableMock.mockReturnValue({
+ mutateAsync: jest.fn(),
+ } as ReturnType)
+ useEditVariableMock.mockReturnValue({
+ mutateAsync: jest.fn(),
+ } as ReturnType)
+ useDeleteVariableMock.mockReturnValue({
+ mutateAsync: jest.fn(),
+ } as ReturnType)
+ useModalMock.mockReturnValue({
+ openModal: jest.fn(),
+ closeModal: jest.fn(),
+ })
+ useModalConfirmationMock.mockReturnValue({
+ openModalConfirmation: jest.fn(),
+ })
+ })
+
+ it('should render the external secrets table layout from the worktree', () => {
+ useVariablesMock.mockReturnValue({
+ data: [
+ {
+ id: 'secret-1',
+ key: 'MY_EXTERNAL_SECRET',
+ value: 'prod/database/credentials',
+ scope: 'APPLICATION',
+ variable_type: 'EXTERNAL_SECRET',
+ secret_manager_access_id: 'sm-1',
+ created_at: '2026-01-01T00:00:00.000Z',
+ },
+ ],
+ isLoading: false,
+ } as ReturnType)
+ useVariablesSecretManagersMock.mockReturnValue({
+ secretManagers: [
+ {
+ id: 'sm-1',
+ name: 'Prod secret manager',
+ created_at: '2026-01-01T00:00:00.000Z',
+ updated_at: '2026-01-01T00:00:00.000Z',
+ endpoint: { mode: 'AWS_SECRET_MANAGER' },
+ authentication: { mode: 'STS' },
+ },
+ ],
+ hasClusterSecretManagerConfigured: true,
+ clusterId: 'cluster-id',
+ })
+
+ renderWithProviders( )
+
+ expect(screen.getByText('1 external secret')).toBeInTheDocument()
+ expect(screen.getByText('MY_EXTERNAL_SECRET')).toBeInTheDocument()
+ expect(screen.getByText('prod/database/credentials')).toBeInTheDocument()
+ expect(screen.getByText('Scope')).toBeInTheDocument()
+ expect(screen.getByText('Actions')).toBeInTheDocument()
+ expect(screen.getByText('Prod secret manager')).toBeInTheDocument()
+ expect(screen.queryByText('Status')).not.toBeInTheDocument()
+ })
+
+ it('should render file external secrets in the external secrets table', () => {
+ useVariablesMock.mockReturnValue({
+ data: [
+ {
+ id: 'secret-file-1',
+ key: 'MY_EXTERNAL_SECRET_FILE',
+ value: 'prod/database/credentials-file',
+ scope: 'APPLICATION',
+ variable_type: 'EXTERNAL_SECRET',
+ mount_path: '/vault/secrets/credentials',
+ secret_manager_access_id: 'sm-1',
+ created_at: '2026-01-01T00:00:00.000Z',
+ },
+ ],
+ isLoading: false,
+ } as ReturnType)
+ useVariablesSecretManagersMock.mockReturnValue({
+ secretManagers: [
+ {
+ id: 'sm-1',
+ name: 'Prod secret manager',
+ created_at: '2026-01-01T00:00:00.000Z',
+ updated_at: '2026-01-01T00:00:00.000Z',
+ endpoint: { mode: 'AWS_SECRET_MANAGER' },
+ authentication: { mode: 'STS' },
+ },
+ ],
+ hasClusterSecretManagerConfigured: true,
+ clusterId: 'cluster-id',
+ })
+
+ renderWithProviders( )
+
+ expect(screen.getByText('MY_EXTERNAL_SECRET_FILE')).toBeInTheDocument()
+ expect(screen.getByText('prod/database/credentials-file')).toBeInTheDocument()
+ })
+
+ it('should keep mount path when editing an external secret file', async () => {
+ const editVariable = jest.fn()
+ const openModal = jest.fn()
+ useEditVariableMock.mockReturnValue({
+ mutateAsync: editVariable,
+ } as unknown as ReturnType)
+ useModalMock.mockReturnValue({
+ openModal,
+ closeModal: jest.fn(),
+ })
+ useVariablesMock.mockReturnValue({
+ data: [
+ {
+ id: 'secret-file-1',
+ key: 'MY_EXTERNAL_SECRET_FILE',
+ value: 'prod/database/credentials-file',
+ scope: 'APPLICATION',
+ variable_type: 'EXTERNAL_SECRET',
+ mount_path: '/vault/secrets/credentials',
+ secret_manager_access_id: 'sm-1',
+ created_at: '2026-01-01T00:00:00.000Z',
+ },
+ ],
+ isLoading: false,
+ } as ReturnType)
+ useVariablesSecretManagersMock.mockReturnValue({
+ secretManagers: [
+ {
+ id: 'sm-1',
+ name: 'Prod secret manager',
+ created_at: '2026-01-01T00:00:00.000Z',
+ updated_at: '2026-01-01T00:00:00.000Z',
+ endpoint: { mode: 'AWS_SECRET_MANAGER' },
+ authentication: { mode: 'STS' },
+ },
+ ],
+ hasClusterSecretManagerConfigured: true,
+ clusterId: 'cluster-id',
+ })
+
+ const { userEvent } = renderWithProviders( )
+
+ await userEvent.click(screen.getByRole('button', { name: 'Edit' }))
+
+ const modalContent = openModal.mock.calls[0][0].content as ReactElement<{
+ initialSecret: { filePath?: string }
+ isFile?: boolean
+ onSubmit: (secret: AddSecretModalSubmitData) => Promise
+ }>
+
+ expect(modalContent.props.isFile).toBe(true)
+ expect(modalContent.props.initialSecret.filePath).toBe('/vault/secrets/credentials')
+
+ await modalContent.props.onSubmit({
+ name: 'MY_EXTERNAL_SECRET_FILE',
+ reference: 'prod/database/credentials-file',
+ filePath: '/vault/secrets/updated-credentials',
+ isFile: true,
+ secretManagerAccessId: 'sm-1',
+ })
+
+ expect(editVariable).toHaveBeenCalledWith({
+ variableId: 'secret-file-1',
+ variableEditRequest: {
+ key: 'MY_EXTERNAL_SECRET_FILE',
+ value: 'prod/database/credentials-file',
+ mount_path: '/vault/secrets/updated-credentials',
+ description: null,
+ secret_manager_access_id: 'sm-1',
+ },
+ })
+ })
+
+ it('should fetch environment-scoped external secrets when scope is ENVIRONMENT', () => {
+ useVariablesMock.mockReturnValue({
+ data: [
+ {
+ id: 'env-secret-1',
+ key: 'ENV_EXTERNAL_SECRET',
+ value: 'staging/shared/credentials',
+ scope: 'ENVIRONMENT',
+ variable_type: 'EXTERNAL_SECRET',
+ secret_manager_access_id: 'sm-1',
+ created_at: '2026-01-01T00:00:00.000Z',
+ },
+ ],
+ isLoading: false,
+ } as ReturnType)
+ useVariablesSecretManagersMock.mockReturnValue({
+ secretManagers: [
+ {
+ id: 'sm-1',
+ name: 'Prod secret manager',
+ created_at: '2026-01-01T00:00:00.000Z',
+ updated_at: '2026-01-01T00:00:00.000Z',
+ endpoint: { mode: 'AWS_SECRET_MANAGER' },
+ authentication: { mode: 'STS' },
+ },
+ ],
+ hasClusterSecretManagerConfigured: true,
+ clusterId: 'cluster-id',
+ })
+
+ renderWithProviders( )
+
+ expect(useVariablesMock).toHaveBeenCalledWith(
+ expect.objectContaining({
+ parentId: 'environment-id',
+ scope: 'ENVIRONMENT',
+ })
+ )
+ expect(screen.getByText('ENV_EXTERNAL_SECRET')).toBeInTheDocument()
+ })
+
+ it('should render empty state when no secret manager is configured', () => {
+ useVariablesMock.mockReturnValue({
+ data: [],
+ isLoading: false,
+ } as ReturnType)
+
+ renderWithProviders( )
+
+ expect(screen.getByText('No secret manager linked on your cluster')).toBeInTheDocument()
+
+ const clusterSettingsLink = screen.getByRole('link', { name: /cluster settings/i })
+ expect(clusterSettingsLink).toHaveAttribute(
+ 'href',
+ '/organization/organization-id/cluster/cluster-id/settings/addons'
+ )
+ })
+})
diff --git a/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.tsx b/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.tsx
new file mode 100644
index 00000000000..690651aa05c
--- /dev/null
+++ b/libs/domains/variables/feature/src/lib/external-secrets/external-secrets-tab.tsx
@@ -0,0 +1,634 @@
+import { Link, useParams } from '@tanstack/react-router'
+import {
+ type ColumnFiltersState,
+ type RowSelectionState,
+ type SortingState,
+ createColumnHelper,
+ flexRender,
+ getCoreRowModel,
+ getFacetedRowModel,
+ getFacetedUniqueValues,
+ getFilteredRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from '@tanstack/react-table'
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import { match } from 'ts-pattern'
+import { type VariableScope } from '@qovery/domains/variables/data-access'
+import { isExternalSecretVariable } from '@qovery/domains/variables/util'
+import {
+ Button,
+ Checkbox,
+ DropdownMenu,
+ EmptyState,
+ Icon,
+ TableFilter,
+ TableFilterSearch,
+ TablePrimitives,
+ Tooltip,
+ useModal,
+ useModalConfirmation,
+} from '@qovery/shared/ui'
+import { getSecretManagerProvider } from '@qovery/shared/util-clusters'
+import { generateScopeLabel, pluralize, twMerge } from '@qovery/shared/util-js'
+import { useCreateVariable } from '../hooks/use-create-variable/use-create-variable'
+import { useDeleteVariable } from '../hooks/use-delete-variable/use-delete-variable'
+import { useEditVariable } from '../hooks/use-edit-variable/use-edit-variable'
+import { useVariables } from '../hooks/use-variables/use-variables'
+import {
+ AddSecretModal,
+ type AddSecretModalSubmitData,
+ type SecretSourceOption,
+ mapSecretManagersToSources,
+} from './add-secret-modal/add-secret-modal'
+import { useVariablesSecretManagers } from './use-variables-secret-managers'
+
+const { Table } = TablePrimitives
+
+const ADD_SECRET_OPTIONS = [
+ {
+ value: 'variable',
+ label: 'Add secret as variable',
+ icon: 'key' as const,
+ },
+ {
+ value: 'file',
+ label: 'Add secret as file',
+ icon: 'file-lines' as const,
+ },
+]
+
+const gridLayoutClassName = 'grid w-full grid-cols-[32px_minmax(0,1fr)_240px_220px_140px_104px]'
+
+type ExternalSecretRow = {
+ id: string
+ name: string
+ description?: string
+ filePath?: string
+ isFile?: boolean
+ reference: string
+ source: string | null
+ sourceIcon?: 'AWS' | 'GCP'
+ scope: string
+}
+
+const columnHelper = createColumnHelper()
+
+export type ExternalSecretsTabProps = {
+ scope: VariableScope
+ parentId: string
+}
+
+export function ExternalSecretsTab({ scope, parentId }: ExternalSecretsTabProps) {
+ const { organizationId = '' } = useParams({ strict: false })
+ const { secretManagers, hasClusterSecretManagerConfigured, clusterId } = useVariablesSecretManagers()
+
+ const { data: variables = [] } = useVariables({
+ parentId,
+ scope,
+ suspense: true,
+ })
+
+ const secrets = useMemo(
+ () =>
+ variables.filter(isExternalSecretVariable).map((variable) => {
+ const secretManager = secretManagers.find((manager) => manager.id === variable.secret_manager_access_id)
+
+ return {
+ id: variable.id,
+ name: variable.key,
+ description: variable.description,
+ filePath: variable.mount_path ?? undefined,
+ isFile: Boolean(variable.mount_path),
+ reference: variable.value ?? '',
+ source: secretManager?.name ?? null,
+ sourceIcon: getSecretManagerProvider(secretManager),
+ scope: generateScopeLabel(variable.scope),
+ }
+ }),
+ [secretManagers, variables]
+ )
+ const [search, setSearch] = useState('')
+ const [sorting, setSorting] = useState([])
+ const [columnFilters, setColumnFilters] = useState([])
+ const [rowSelection, setRowSelection] = useState({})
+ const { openModal, closeModal } = useModal()
+ const { openModalConfirmation } = useModalConfirmation()
+ const { mutateAsync: createVariable } = useCreateVariable()
+ const { mutateAsync: editVariable } = useEditVariable()
+ const { mutateAsync: deleteVariable } = useDeleteVariable()
+
+ const secretSources = useMemo(() => mapSecretManagersToSources(secretManagers), [secretManagers])
+
+ const getSourceOption = useCallback(
+ (secret?: ExternalSecretRow): SecretSourceOption | undefined => {
+ if (!secret?.source) {
+ return secretSources[0]
+ }
+ return secretSources.find((option) => option.tableLabel === secret.source) ?? secretSources[0]
+ },
+ [secretSources]
+ )
+
+ const handleCreateSecret = useCallback(
+ async ({
+ name,
+ description,
+ filePath,
+ isFile,
+ reference,
+ secretManagerAccessId,
+ }: {
+ name: string
+ description?: string
+ filePath?: string
+ isFile: boolean
+ reference: string
+ secretManagerAccessId: string
+ }) => {
+ await createVariable({
+ variableRequest: {
+ key: name,
+ value: reference,
+ mount_path: isFile ? filePath ?? null : null,
+ is_secret: false,
+ variable_scope: scope,
+ variable_parent_id: parentId,
+ description: description ?? null,
+ secret_manager_access_id: secretManagerAccessId,
+ },
+ })
+ },
+ [createVariable, parentId, scope]
+ )
+
+ const handleEditSecret = useCallback(
+ async (
+ secretId: string,
+ { name, description, filePath, isFile, reference, secretManagerAccessId }: AddSecretModalSubmitData
+ ) => {
+ const variableEditRequest = {
+ key: name,
+ value: reference,
+ mount_path: isFile ? filePath ?? null : null,
+ description: description ?? null,
+ secret_manager_access_id: secretManagerAccessId,
+ }
+
+ await editVariable({
+ variableId: secretId,
+ variableEditRequest,
+ })
+ },
+ [editVariable]
+ )
+
+ const handleOpenAddSecret = useCallback(
+ (isFile: boolean) => {
+ openModal({
+ content: (
+
+ ),
+ options: {
+ width: 520,
+ fakeModal: true,
+ },
+ })
+ },
+ [closeModal, handleCreateSecret, openModal, secretSources]
+ )
+
+ const handleOpenEditSecret = useCallback(
+ (secret: ExternalSecretRow) => {
+ openModal({
+ content: (
+ handleEditSecret(secret.id, updated)}
+ />
+ ),
+ options: {
+ width: 520,
+ fakeModal: true,
+ },
+ })
+ },
+ [closeModal, getSourceOption, handleEditSecret, openModal, secretSources]
+ )
+
+ useEffect(() => {
+ setSearch('')
+ setSorting([])
+ setColumnFilters([])
+ setRowSelection({})
+ }, [secrets])
+
+ const handleDeleteSecrets = useCallback(
+ async (secretIds: string[]) => {
+ const idsToDelete = new Set(secretIds)
+ await Promise.all([...idsToDelete].map((variableId) => deleteVariable({ variableId })))
+ setRowSelection((prev) => {
+ const next = { ...prev }
+ for (const secretId of secretIds) {
+ delete next[secretId]
+ }
+ return next
+ })
+ },
+ [deleteVariable]
+ )
+
+ const handleConfirmDeleteSecrets = useCallback(
+ (secretIds: string[]) => {
+ if (secretIds.length === 0) return
+
+ const deletionTargetLabel =
+ secretIds.length === 1
+ ? secrets.find((secret) => secret.id === secretIds[0])?.name ?? 'this secret'
+ : 'these secrets'
+
+ openModalConfirmation({
+ title: `Delete ${secretIds.length} ${pluralize(secretIds.length, 'secret')}`,
+ name: deletionTargetLabel,
+ confirmationMethod: 'action',
+ action: () => handleDeleteSecrets(secretIds),
+ })
+ },
+ [handleDeleteSecrets, openModalConfirmation, secrets]
+ )
+
+ const emptyStateConfig = useMemo(() => {
+ if (secrets.length > 0) {
+ return null
+ }
+
+ if (!hasClusterSecretManagerConfigured) {
+ return {
+ title: 'No secret manager linked on your cluster',
+ description: 'Secret add-on has been activated on your cluster but no secret manager are linked to it.',
+ icon: 'lock-keyhole' as const,
+ actions: (
+
+
+ Cluster settings
+
+
+
+ ),
+ }
+ }
+
+ return {
+ title: 'No external secrets yet',
+ description: 'Add a secret or connect a secret manager to sync external secrets.',
+ icon: 'lock-keyhole' as const,
+ actions: (
+ handleOpenAddSecret(false)}
+ >
+
+ Add secret
+
+ ),
+ }
+ }, [clusterId, handleOpenAddSecret, hasClusterSecretManagerConfigured, organizationId, secrets.length])
+
+ const columns = useMemo(
+ () => [
+ columnHelper.display({
+ id: 'select',
+ enableColumnFilter: false,
+ enableSorting: false,
+ header: ({ table }) => (
+
+ {
+ if (checked === 'indeterminate') return
+ table.toggleAllRowsSelected(checked)
+ }}
+ />
+
+ ),
+ cell: ({ row }) => (
+ e.stopPropagation()}>
+ {
+ if (checked === 'indeterminate') return
+ row.toggleSelected(checked)
+ }}
+ />
+
+ ),
+ }),
+ columnHelper.accessor('name', {
+ header: 'Name',
+ enableSorting: true,
+ cell: (info) => {
+ const secret = info.row.original
+ const showFilePath = secret.isFile && secret.filePath
+ return (
+
+
+
+ {secret.name}
+
+ {secret.description && (
+
+
+
+
+
+ )}
+
+ {showFilePath && (
+
+
+ {secret.filePath}
+
+ )}
+
+ )
+ },
+ }),
+ columnHelper.accessor('reference', {
+ header: 'Reference',
+ enableSorting: true,
+ cell: (info) => {info.getValue()} ,
+ }),
+ columnHelper.accessor((row) => row.source ?? 'No sources detected', {
+ id: 'source',
+ header: 'Source',
+ enableSorting: false,
+ enableColumnFilter: true,
+ filterFn: (row, columnId, filterValue) => {
+ if (!Array.isArray(filterValue) || filterValue.length === 0) return true
+ return filterValue.includes(row.getValue(columnId))
+ },
+ cell: (info) => {
+ const secret = info.row.original
+ if (!secret.source) {
+ return No sources detected
+ }
+ return (
+
+ {secret.sourceIcon && }
+
+ {secret.source}
+
+
+ )
+ },
+ }),
+ columnHelper.accessor('scope', {
+ header: 'Scope',
+ enableSorting: false,
+ enableColumnFilter: true,
+ filterFn: (row, columnId, filterValue) => {
+ if (!Array.isArray(filterValue) || filterValue.length === 0) return true
+ return filterValue.includes(row.getValue(columnId))
+ },
+ cell: (info) => {info.getValue()} ,
+ }),
+ columnHelper.display({
+ id: 'actions',
+ header: 'Actions',
+ cell: (info) => {
+ const secret = info.row.original
+ return (
+
+
handleOpenEditSecret(secret)}
+ >
+
+
+
+
+
+
+
handleConfirmDeleteSecrets([secret.id])}
+ >
+
+
+
+
+
+
+
+ )
+ },
+ }),
+ ],
+ [handleConfirmDeleteSecrets, handleOpenEditSecret]
+ )
+
+ const table = useReactTable({
+ data: secrets,
+ columns,
+ state: { sorting, rowSelection, globalFilter: search, columnFilters },
+ onSortingChange: setSorting,
+ onRowSelectionChange: setRowSelection,
+ onGlobalFilterChange: setSearch,
+ onColumnFiltersChange: setColumnFilters,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ getFacetedRowModel: getFacetedRowModel(),
+ getFacetedUniqueValues: getFacetedUniqueValues(),
+ getRowId: (row) => row.id,
+ })
+
+ const shouldShowEmptyState = secrets.length === 0 && Boolean(emptyStateConfig)
+ const totalRows = table.getPreFilteredRowModel().rows.length
+ const isSearching = table.getRowCount() !== totalRows
+ const countText = isSearching
+ ? `${table.getRowCount()}/${totalRows} external ${pluralize(table.getRowCount(), 'secret')}`
+ : `${totalRows} external ${pluralize(totalRows, 'secret')}`
+ const selectedRows = table.getSelectedRowModel().rows.map(({ original }) => original)
+ const selectedIds = selectedRows.map((secret) => secret.id)
+ const hasSelection = selectedIds.length > 0
+
+ return (
+
+ {!shouldShowEmptyState && (
+
+
+ {countText}
+
+
+
+
+
+
+
+
setSearch(e.target.value)}
+ />
+
+
+
+
+ Add secret
+
+
+
+
+ {ADD_SECRET_OPTIONS.map((option) => (
+ }
+ onSelect={() => handleOpenAddSecret(option.value === 'file')}
+ >
+ {option.label}
+
+ ))}
+
+
+
+
+ )}
+
+ {shouldShowEmptyState && emptyStateConfig ? (
+
+
+ {emptyStateConfig.actions}
+
+
+ ) : (
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {['source', 'scope'].includes(header.column.id) ? (
+
+ ) : header.column.getCanSort() ? (
+
+ {flexRender(header.column.columnDef.header, header.getContext())}
+ {match(header.column.getIsSorted())
+ .with('asc', () => )
+ .with('desc', () => )
+ .with(false, () => (
+
+ ))
+ .exhaustive()}
+
+ ) : (
+ flexRender(header.column.columnDef.header, header.getContext())
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))}
+
+
+ )}
+
+
+
+
+ {selectedIds.length} selected {pluralize(selectedIds.length, 'secret')}
+
+
+ setRowSelection({})}>
+ Unselect
+
+ handleConfirmDeleteSecrets(selectedIds)}
+ >
+
+
+
+
+
+
+ )
+}
diff --git a/libs/domains/variables/feature/src/lib/hooks/use-delete-variable/use-delete-variable.ts b/libs/domains/variables/feature/src/lib/hooks/use-delete-variable/use-delete-variable.ts
index b7b79aa1971..f84e4a36fe7 100644
--- a/libs/domains/variables/feature/src/lib/hooks/use-delete-variable/use-delete-variable.ts
+++ b/libs/domains/variables/feature/src/lib/hooks/use-delete-variable/use-delete-variable.ts
@@ -9,6 +9,9 @@ export function useDeleteVariable() {
queryClient.invalidateQueries({
queryKey: queries.variables.list._def,
})
+ queryClient.invalidateQueries({
+ queryKey: queries.clusters.listSecretManagerAssociatedServices._def,
+ })
},
meta: {
notifyOnError: true,
diff --git a/libs/domains/variables/feature/src/lib/hooks/use-variables/use-variables.ts b/libs/domains/variables/feature/src/lib/hooks/use-variables/use-variables.ts
index ceae995938d..a46bc5d93b2 100644
--- a/libs/domains/variables/feature/src/lib/hooks/use-variables/use-variables.ts
+++ b/libs/domains/variables/feature/src/lib/hooks/use-variables/use-variables.ts
@@ -7,9 +7,10 @@ export interface UseVariablesProps {
parentId: string
scope?: VariableScope
isSecret?: boolean
+ suspense?: boolean
}
-export function useVariables({ parentId, scope, isSecret }: UseVariablesProps) {
+export function useVariables({ parentId, scope, isSecret, suspense = false }: UseVariablesProps) {
return useQuery({
...queries.variables.list({
parentId,
@@ -35,6 +36,7 @@ export function useVariables({ parentId, scope, isSecret }: UseVariablesProps) {
})
},
enabled: Boolean(scope),
+ suspense,
})
}
diff --git a/libs/domains/variables/feature/src/lib/variable-list/__snapshots__/variable-list.spec.tsx.snap b/libs/domains/variables/feature/src/lib/variable-list/__snapshots__/variable-list.spec.tsx.snap
index e5a724b17e0..f1ca6fd7959 100644
--- a/libs/domains/variables/feature/src/lib/variable-list/__snapshots__/variable-list.spec.tsx.snap
+++ b/libs/domains/variables/feature/src/lib/variable-list/__snapshots__/variable-list.spec.tsx.snap
@@ -3,28 +3,28 @@
exports[`VariableList should match snapshot 1`] = `
Custom variables
Built-in variables
['data']
resetRowSelection: () => void
}
-export function VariableListActionBar({ selectedRows = [], resetRowSelection }: VariableListActionBarProps) {
+export function VariableListActionBar({
+ className,
+ fixed = false,
+ selectedRows = [],
+ resetRowSelection,
+}: VariableListActionBarProps) {
const selectedCount = selectedRows.length
const hasSelection = Boolean(selectedCount)
@@ -42,6 +49,8 @@ export function VariableListActionBar({ selectedRows = [], resetRowSelection }:
return (
void
onEditVariable?: (variable: VariableResponse | void) => void
onDeleteVariable?: (variable: VariableResponse) => void
@@ -105,6 +107,9 @@ export type VariableListProps = {
export function VariableList({
className,
+ hideSectionLabel = false,
+ showOnly,
+ headerActions,
onCreateVariable,
onEditVariable,
onDeleteVariable,
@@ -127,7 +132,19 @@ export function VariableList({
const { mutateAsync: deleteVariable } = useDeleteVariable()
const [globalFilter, setGlobalFilter] = useState('')
const [builtInGlobalFilter, setBuiltInGlobalFilter] = useState('')
- const nonBuiltInVariables = useMemo(() => variables.filter((variable) => variable.scope !== 'BUILT_IN'), [variables])
+ const nonBuiltInVariables = useMemo(
+ () =>
+ variables.filter((variable) => {
+ if (variable.scope === 'BUILT_IN') {
+ return false
+ }
+ if (showOnly === 'custom' && isExternalSecretVariable(variable)) {
+ return false
+ }
+ return true
+ }),
+ [showOnly, variables]
+ )
const builtInVariables = useMemo(() => variables.filter((variable) => variable.scope === 'BUILT_IN'), [variables])
const variablesById = useMemo(() => new Map(variables.map((variable) => [variable.id, variable])), [variables])
const scrollToVariable = useCallback((variableId: string) => {
@@ -155,6 +172,23 @@ export function VariableList({
})
}
+ const _onCreateStandaloneVariable = (isFile = false) =>
+ openModal({
+ content: (
+
+ ),
+ options: {
+ fakeModal: true,
+ },
+ })
+
const _onEditVariable: (variable: VariableResponse) => void = (variable) => {
openModal({
content: (
@@ -202,15 +236,17 @@ export function VariableList({
? 'grid w-full grid-cols-[32px_minmax(0,40%)_50px_minmax(0%,40%)_minmax(0,12%)]'
: isEnvironmentScope
? 'grid w-full grid-cols-[32px_minmax(0,40%)_50px_minmax(0,30%)_minmax(0,15%)_minmax(0,12%)]'
- : 'grid w-full grid-cols-[32px_minmax(0,40%)_50px_minmax(0,20%)_minmax(0,15%)_minmax(0,10%)_minmax(0,12%)]'
+ : 'grid w-full grid-cols-[32px_minmax(0,40%)_minmax(0,1fr)_minmax(0,15%)_minmax(0,12%)_88px]'
const builtInGridLayoutClassName =
props.scope === 'PROJECT'
? 'grid w-full grid-cols-[minmax(0,calc(40%_+_32px))_50px_minmax(0,40%)_minmax(0,12%)]'
- : 'grid w-full grid-cols-[minmax(0,calc(40%_+_32px))_50px_minmax(0,30%)_minmax(0,15%)_minmax(0,12%)]'
+ : isEnvironmentScope
+ ? 'grid w-full grid-cols-[minmax(0,calc(40%_+_32px))_50px_minmax(0,30%)_minmax(0,15%)_minmax(0,12%)]'
+ : 'grid w-full grid-cols-[minmax(0,calc(40%_+_32px))_minmax(0,1fr)_minmax(0,15%)_minmax(0,12%)_88px]'
const columnHelper = createColumnHelper<(typeof variables)[number]>()
- const columns = useMemo(
- () => [
+ const columns = useMemo(() => {
+ const baseColumns = [
columnHelper.display({
id: 'select',
enableColumnFilter: false,
@@ -252,6 +288,7 @@ export function VariableList({
columnHelper.accessor('key', {
id: 'key',
header: ({ table }) => {
+ if (headerActions) return 'Name'
const totalRows = table.getPreFilteredRowModel().rows.length
const isSearching = table.getRowCount() !== totalRows
return isSearching
@@ -262,6 +299,8 @@ export function VariableList({
size: showServiceLinkColumn ? 40 : 45,
cell: (info) => {
const variable = info.row.original
+ const isFileVariable = environmentVariableFile(variable)
+ const showFilePathUnderName = isServiceScope && showOnly === 'custom' && isFileVariable
const aliasedVariable = variable.aliased_variable
const overriddenVariable = variable.overridden_variable
@@ -287,7 +326,7 @@ export function VariableList({
OVERRIDE
)}
- {variable.mount_path && (
+ {variable.mount_path && !showFilePathUnderName && (
FILE
@@ -311,6 +350,12 @@ export function VariableList({
)}
+ {showFilePathUnderName && (
+
+
+ {getEnvironmentVariableFileMountPath(variable)}
+
+ )}
{(aliasedVariable || overriddenVariable) && (
@@ -333,79 +378,202 @@ export function VariableList({
}),
columnHelper.display({
id: 'actions',
+ header: 'Actions',
cell: (info) => {
const variable = info.row.original
const alreadyOverridden = variables.some(({ overridden_variable }) => variable.id === overridden_variable?.id)
const disableOverride = match(variable.scope)
.with('APPLICATION', 'CONTAINER', 'JOB', 'HELM', () => true)
.otherwise(() => alreadyOverridden)
+ const isDoppler = variable.owned_by === ExternalServiceEnum.DOPPLER
+ const canEdit = isDoppler || variable.scope !== 'BUILT_IN'
+ const isBuiltIn = variable.scope === 'BUILT_IN'
+ const showAliasOnly = isServiceScope && showOnly === 'built-in' && isBuiltIn
- return (
-
-
-
-
-
-
-
- {variable.owned_by === ExternalServiceEnum.DOPPLER ? (
- }
- onSelect={() => window.open('https://dashboard.doppler.com', '_blank')}
- >
- Edit in Doppler
-
- ) : (
- <>
- {variable.scope !== 'BUILT_IN' && (
- } onSelect={() => _onEditVariable(variable)}>
- Edit
-
- )}
- {!variable.overridden_variable && !variable.aliased_variable && (
- <>
- }
- onSelect={() => _onCreateVariable(variable, 'ALIAS')}
- >
- Create alias
+ if (!isServiceScope) {
+ return (
+
+
+
+
+
+
+
+ {isDoppler ? (
+ }
+ onSelect={() => window.open('https://dashboard.doppler.com', '_blank')}
+ >
+ Edit in Doppler
+
+ ) : (
+ <>
+ {variable.scope !== 'BUILT_IN' && (
+ } onSelect={() => _onEditVariable(variable)}>
+ Edit
- {variable.scope !== 'BUILT_IN' && variable.scope !== props.scope && (
+ )}
+ {!variable.overridden_variable && !variable.aliased_variable && (
+ <>
}
- disabled={disableOverride}
- onSelect={() => _onCreateVariable(variable, 'OVERRIDE')}
+ icon={ }
+ onSelect={() => _onCreateVariable(variable, 'ALIAS')}
>
-
+ {variable.scope !== 'BUILT_IN' && variable.scope !== props.scope && (
+ }
+ disabled={disableOverride}
+ onSelect={() => _onCreateVariable(variable, 'OVERRIDE')}
>
- Create override
-
+
+ Create override
+
+
+ )}
+ >
+ )}
+ {variable.owned_by === 'QOVERY' && variable.scope !== 'BUILT_IN' && (
+ <>
+
+ }
+ onSelect={() => _onDeleteVariable(variable)}
+ color="red"
+ >
+ Delete
- )}
- >
- )}
- {variable.owned_by === 'QOVERY' && variable.scope !== 'BUILT_IN' && (
- <>
-
- }
- onSelect={() => _onDeleteVariable(variable)}
- color="red"
- >
- Delete
-
- >
- )}
- >
- )}
-
-
+ >
+ )}
+ >
+ )}
+
+
+ )
+ }
+
+ if (showAliasOnly) {
+ if (variable.overridden_variable || variable.aliased_variable) {
+ return
+ }
+
+ return (
+
_onCreateVariable(variable, 'ALIAS')}
+ >
+
+
+
+
+
+
+ )
+ }
+
+ return (
+
+
{
+ if (!canEdit) return
+ if (isDoppler) {
+ window.open('https://dashboard.doppler.com', '_blank')
+ return
+ }
+ _onEditVariable(variable)
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isDoppler ? (
+ }
+ onSelect={() => window.open('https://dashboard.doppler.com', '_blank')}
+ >
+ Edit in Doppler
+
+ ) : (
+ <>
+ {!variable.overridden_variable && !variable.aliased_variable && (
+ <>
+ }
+ onSelect={() => _onCreateVariable(variable, 'ALIAS')}
+ >
+ Create alias
+
+ {variable.scope !== 'BUILT_IN' && variable.scope !== props.scope && (
+ }
+ disabled={disableOverride}
+ onSelect={() => _onCreateVariable(variable, 'OVERRIDE')}
+ >
+
+ Create override
+
+
+ )}
+ >
+ )}
+ {variable.owned_by === 'QOVERY' && variable.scope !== 'BUILT_IN' && (
+ <>
+
+ }
+ onSelect={() => _onDeleteVariable(variable)}
+ color="red"
+ >
+ Delete
+
+ >
+ )}
+ >
+ )}
+
+
+
)
},
}),
@@ -416,7 +584,9 @@ export function VariableList({
filterFn: 'arrIncludesSome',
cell: (info) => {
const variable = info.row.original
- if (environmentVariableFile(variable)) {
+ const shouldRenderFilePathInName = isServiceScope && showOnly === 'custom'
+
+ if (environmentVariableFile(variable) && !shouldRenderFilePathInName) {
return (
_onEditVariable(variable)}>
{variable.value !== null ? (
@@ -450,7 +620,7 @@ export function VariableList({
.otherwise(({ organizationId, projectId, environmentId }) => [
columnHelper.accessor('service_name', {
header: 'Service link',
- enableColumnFilter: true,
+ enableColumnFilter: !isServiceScope,
filterFn: 'arrIncludesSome',
size: 15,
meta: {
@@ -481,7 +651,9 @@ export function VariableList({
)}
{variable.service_name}
- ) : null
+ ) : (
+
-
+ )
},
}),
]),
@@ -531,14 +703,40 @@ export function VariableList({
)
},
}),
- ],
- // Keep the pre-existing modal handlers local so this alias change does not rewrite modal behavior.
+ ]
+
+ if (!isServiceScope) {
+ return baseColumns
+ }
+
+ const actionsColumn = baseColumns.find((column) => (column as { id?: string }).id === 'actions')
+ const orderedColumns = baseColumns.filter((column) => (column as { id?: string }).id !== 'actions')
+
+ if (actionsColumn) {
+ orderedColumns.push(actionsColumn)
+ }
+
+ return orderedColumns
// eslint-disable-next-line react-hooks/exhaustive-deps
- [variables, variablesById, props, scrollToVariable, showServiceLinkColumn]
- )
+ }, [
+ variables,
+ variablesById,
+ props,
+ scrollToVariable,
+ showServiceLinkColumn,
+ _onCreateVariable,
+ _onEditVariable,
+ showOnly,
+ headerActions,
+ isServiceScope,
+ ])
const nonBuiltInColumns = useMemo(() => {
if (!isEnvironmentScope && props.scope !== 'PROJECT') {
- return columns
+ return columns.filter((column) => {
+ const id = (column as { id?: string }).id
+ const accessorKey = (column as { accessorKey?: string }).accessorKey
+ return id !== 'service_name' && accessorKey !== 'service_name'
+ })
}
return columns.filter((column) => {
const id = (column as { id?: string }).id
@@ -651,12 +849,59 @@ export function VariableList({
return
}
+ if (showOnly === 'custom' && nonBuiltInVariables.length === 0) {
+ return (
+
+
+
+
+
+
+
+ Add variable
+
+
+
+
+ _onCreateStandaloneVariable()} icon={ }>
+ Variable
+
+ _onCreateStandaloneVariable(true)}
+ icon={ }
+ >
+ Variable as file
+
+
+
+
+ window.open('https://dashboard.doppler.com', '_blank')}
+ >
+
+ Import from Doppler
+
+
+
+
+ )
+ }
+
if (variables.length === 0) {
return (
)
}
@@ -668,19 +913,52 @@ export function VariableList({
rowGridClassName: string,
isBuiltInTable: boolean
) => {
- const hideServiceLinkColumn = isServiceScope && !isBuiltInTable
+ const totalRows = tableInstance.getPreFilteredRowModel().rows.length
+ const isSearching = tableInstance.getRowCount() !== totalRows
+ const countText = isSearching
+ ? `${tableInstance.getRowCount()}/${totalRows} ${pluralize(tableInstance.getRowCount(), 'variable')}`
+ : `${totalRows} ${pluralize(totalRows, 'variable')}`
+ const countTooltipContent = isBuiltInTable
+ ? 'Qovery automatically injects built-in variables for service interconnection and system information.'
+ : 'Custom variables are values you define to configure your service behavior at build and runtime.'
+ const isTabLayout = Boolean(showOnly)
+
return (
-
-
+
+ {headerActions && (
+
+
+ {countText}
+
+
+
+
+
+
+
+
setFilterValue(event.target.value)}
+ />
+ {headerActions}
+
+
+ )}
+
{tableInstance.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => (
- // Keep this column hidden (not removed) in Service scope (custom vars only) to preserve visual column alignment
@@ -710,9 +988,10 @@ export function VariableList({
) : (
flexRender(header.column.columnDef.header, header.getContext())
)}
- {header.column.id === 'key' && (
+ {header.column.id === 'key' && !headerActions && (
setFilterValue(event.target.value)}
/>
@@ -731,12 +1010,13 @@ export function VariableList({
className={twMerge('h-16 items-center hover:bg-surface-neutral-subtle', rowGridClassName)}
>
{row.getVisibleCells().map((cell) => (
- // Keep this cell hidden (not removed) in service scope (custom vars only) to preserve visual column alignment
{flexRender(cell.column.columnDef.cell, cell.getContext())}
@@ -753,21 +1033,29 @@ export function VariableList({
const selectedRows = table.getSelectedRowModel().rows.map(({ original }) => original)
+ const isTabLayout = Boolean(showOnly)
+
return (
-
- {nonBuiltInVariables.length > 0 && (
-
- Custom variables
+
+ {showOnly !== 'built-in' && nonBuiltInVariables.length > 0 && (
+
+ {!hideSectionLabel && Custom variables }
{renderTable(table, globalFilter, setGlobalFilter, gridLayoutClassName, false)}
-
+
)}
- {builtInVariables.length > 0 && (
-
- Built-in variables
+ {showOnly !== 'custom' && builtInVariables.length > 0 && (
+
+ {!hideSectionLabel && Built-in variables }
{renderTable(builtInTable, builtInGlobalFilter, setBuiltInGlobalFilter, builtInGridLayoutClassName, true)}
-
+
+ )}
+ {showOnly !== 'built-in' && (
+ table.resetRowSelection()}
+ />
)}
- table.resetRowSelection()} />
)
}
diff --git a/libs/domains/variables/feature/src/lib/variables-action-toolbar/variables-action-toolbar.tsx b/libs/domains/variables/feature/src/lib/variables-action-toolbar/variables-action-toolbar.tsx
index 3328e893741..4ab6dfd583a 100644
--- a/libs/domains/variables/feature/src/lib/variables-action-toolbar/variables-action-toolbar.tsx
+++ b/libs/domains/variables/feature/src/lib/variables-action-toolbar/variables-action-toolbar.tsx
@@ -8,6 +8,8 @@ export type VariablesActionToolbarProps = {
onCreateVariable?: (variable: VariableResponse | void) => void
onImportEnvFile?: () => void
importEnvFileAccess?: 'button' | 'dropdown'
+ showDopplerButton?: boolean
+ hasClusterSecretManagerConfigured?: boolean
} & (
| {
scope: Extract
@@ -30,6 +32,8 @@ export function VariablesActionToolbar({
onCreateVariable,
onImportEnvFile,
importEnvFileAccess = 'button',
+ showDopplerButton = false,
+ hasClusterSecretManagerConfigured = false,
...props
}: VariablesActionToolbarProps) {
const { openModal, closeModal } = useModal()
@@ -45,6 +49,7 @@ export function VariablesActionToolbar({
mode="CREATE"
onSubmit={onCreateVariable}
isFile={isFile}
+ hasClusterSecretManagerConfigured={hasClusterSecretManagerConfigured}
{...props}
/>
),
@@ -54,7 +59,7 @@ export function VariablesActionToolbar({
})
return (
-
+
{showImportButton ? (
@@ -100,6 +105,7 @@ export function VariablesActionToolbar({
+
diff --git a/libs/domains/variables/util/src/index.ts b/libs/domains/variables/util/src/index.ts
index 551cf38ff1f..c12ad5524f9 100644
--- a/libs/domains/variables/util/src/index.ts
+++ b/libs/domains/variables/util/src/index.ts
@@ -1 +1,2 @@
+export * from './lib/is-external-secret-variable/is-external-secret-variable'
export * from './lib/sort-variables/sort-variables'
diff --git a/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.spec.ts b/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.spec.ts
new file mode 100644
index 00000000000..dee63748f35
--- /dev/null
+++ b/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.spec.ts
@@ -0,0 +1,29 @@
+import { type VariableResponse } from 'qovery-typescript-axios'
+import { isExternalSecretVariable } from './is-external-secret-variable'
+
+const baseVariable = {
+ id: 'var-1',
+ key: 'MY_VAR',
+ value: 'value',
+ scope: 'APPLICATION',
+} as VariableResponse
+
+describe('isExternalSecretVariable', () => {
+ it('should return true for EXTERNAL_SECRET variables', () => {
+ expect(isExternalSecretVariable({ ...baseVariable, variable_type: 'EXTERNAL_SECRET' })).toBe(true)
+ })
+
+ it('should return true for EXTERNAL_SECRET variables mounted as files', () => {
+ expect(
+ isExternalSecretVariable({
+ ...baseVariable,
+ variable_type: 'EXTERNAL_SECRET',
+ mount_path: '/vault/secrets/credentials',
+ })
+ ).toBe(true)
+ })
+
+ it.each(['VALUE', 'FILE', 'ALIAS', 'OVERRIDE'] as const)('should return false for %s variables', (variable_type) => {
+ expect(isExternalSecretVariable({ ...baseVariable, variable_type })).toBe(false)
+ })
+})
diff --git a/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.ts b/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.ts
new file mode 100644
index 00000000000..6103f340bd2
--- /dev/null
+++ b/libs/domains/variables/util/src/lib/is-external-secret-variable/is-external-secret-variable.ts
@@ -0,0 +1,5 @@
+import { type VariableResponse } from 'qovery-typescript-axios'
+
+export function isExternalSecretVariable(variable: VariableResponse): boolean {
+ return variable.variable_type === 'EXTERNAL_SECRET'
+}
diff --git a/libs/shared/routes/src/lib/router.ts b/libs/shared/routes/src/lib/router.ts
index bd0a4fb7191..024a1d3a6c7 100644
--- a/libs/shared/routes/src/lib/router.ts
+++ b/libs/shared/routes/src/lib/router.ts
@@ -9,7 +9,6 @@ export * from './sub-router/login.router'
export * from './sub-router/settings.router'
export * from './sub-router/logs.router'
export * from './sub-router/clusters.router'
-export * from './sub-router/cluster.router'
export * from './sub-router/job.router'
export * from './sub-router/helm.router'
export * from './sub-router/audit-logs.router'
diff --git a/libs/shared/routes/src/lib/sub-router/cluster.router.ts b/libs/shared/routes/src/lib/sub-router/cluster.router.ts
deleted file mode 100644
index 53f901b58d6..00000000000
--- a/libs/shared/routes/src/lib/sub-router/cluster.router.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-export const CLUSTER_URL = (organizationId = ':organizationId', clusterId = ':clusterId') =>
- `/organization/${organizationId}/cluster/${clusterId}`
-
-export const CLUSTER_OVERVIEW_URL = '/overview'
-
-// settings
-export const CLUSTER_SETTINGS_URL = '/settings'
-export const CLUSTER_SETTINGS_GENERAL_URL = '/general'
-export const CLUSTER_SETTINGS_EKS_ANYWHERE_URL = '/eks-anywhere'
-export const CLUSTER_SETTINGS_CREDENTIALS_URL = '/credentials'
-export const CLUSTER_SETTINGS_RESOURCES_URL = '/resources'
-export const CLUSTER_SETTINGS_IMAGE_REGISTRY_URL = '/image-registry'
-export const CLUSTER_SETTINGS_NETWORK_URL = '/network'
-export const CLUSTER_SETTINGS_ADVANCED_SETTINGS_URL = '/advanced-settings'
-export const CLUSTER_SETTINGS_DANGER_ZONE_URL = '/danger-zone'
diff --git a/libs/shared/ui/src/lib/components/action-toolbar/action-toolbar.tsx b/libs/shared/ui/src/lib/components/action-toolbar/action-toolbar.tsx
index 134eecd8f47..961e5bc0603 100644
--- a/libs/shared/ui/src/lib/components/action-toolbar/action-toolbar.tsx
+++ b/libs/shared/ui/src/lib/components/action-toolbar/action-toolbar.tsx
@@ -15,8 +15,10 @@ const toolbarButtonVariants = cva(['active:scale-100'], {
color: {
brand: [],
neutral: [],
+ neutralInverted: [],
green: [],
red: [],
+ redInverted: [],
yellow: [],
current: [],
},
diff --git a/libs/shared/ui/src/lib/components/button-primitive/button-primitive.tsx b/libs/shared/ui/src/lib/components/button-primitive/button-primitive.tsx
index 84d1f015313..51bd5917f3f 100644
--- a/libs/shared/ui/src/lib/components/button-primitive/button-primitive.tsx
+++ b/libs/shared/ui/src/lib/components/button-primitive/button-primitive.tsx
@@ -31,8 +31,10 @@ const _buttonVariants = cva(
color: {
brand: ['outline-brand-strong'],
neutral: ['outline-neutral-strong'],
+ neutralInverted: ['outline-neutral-strong'],
green: ['outline-positive-strong'],
red: ['outline-negative-strong'],
+ redInverted: ['outline-negative-strong'],
yellow: ['outline-warning-strong'],
current: [''],
},
@@ -158,6 +160,19 @@ const _buttonVariants = cva(
'text-neutral',
],
},
+ {
+ variant: ['outline'],
+ color: 'neutralInverted',
+ className: [
+ 'bg-surface-neutralInvert',
+ 'border',
+ 'border-neutralInvert',
+ 'hover:border-neutralInvert',
+ 'hover:bg-surface-neutralInvert-component',
+ 'data-[state=open]:bg-surface-neutralInvert-component',
+ 'text-neutralInvert',
+ ],
+ },
{
variant: ['outline'],
color: 'red',
@@ -170,6 +185,19 @@ const _buttonVariants = cva(
'data-[state=open]:bg-surface-negative-subtle',
],
},
+ {
+ variant: ['outline'],
+ color: 'redInverted',
+ className: [
+ 'border',
+ 'border-negativeInvert-subtle',
+ 'bg-surface-negativeInvert-component',
+ 'text-negativeInvert',
+ 'hover:border-negativeInvert-subtle',
+ 'hover:bg-surface-negativeInvert-component',
+ 'data-[state=open]:bg-surface-negativeInvert-component',
+ ],
+ },
{
variant: ['outline'],
color: 'yellow',
diff --git a/libs/shared/ui/src/lib/components/navbar/navbar.tsx b/libs/shared/ui/src/lib/components/navbar/navbar.tsx
index 5c0dbcc432a..86fd3c7abbc 100644
--- a/libs/shared/ui/src/lib/components/navbar/navbar.tsx
+++ b/libs/shared/ui/src/lib/components/navbar/navbar.tsx
@@ -21,6 +21,7 @@ const navbarItemVariants = cva(
'relative',
'inline-block',
'select-none',
+ 'cursor-pointer',
'px-3',
'py-3',
'text-sm',
diff --git a/libs/shared/ui/src/lib/components/sticky-action-form-toaster/sticky-action-form-toaster.tsx b/libs/shared/ui/src/lib/components/sticky-action-form-toaster/sticky-action-form-toaster.tsx
index 75f9f866a1a..c17a61be98f 100644
--- a/libs/shared/ui/src/lib/components/sticky-action-form-toaster/sticky-action-form-toaster.tsx
+++ b/libs/shared/ui/src/lib/components/sticky-action-form-toaster/sticky-action-form-toaster.tsx
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react'
+import { twMerge } from '@qovery/shared/util-js'
import Button, { type ButtonProps } from '../button/button'
export interface StickyActionFormToasterProps {
@@ -12,6 +13,7 @@ export interface StickyActionFormToasterProps {
className?: string
disabledValidation?: boolean
loading?: boolean
+ fixed?: boolean
}
export function StickyActionFormToaster(props: StickyActionFormToasterProps) {
@@ -24,6 +26,7 @@ export function StickyActionFormToaster(props: StickyActionFormToasterProps) {
className = '',
visible = false,
submitButtonColor,
+ fixed = false,
} = props
const [visibleState, setVisibleState] = useState(visible)
@@ -42,7 +45,13 @@ export function StickyActionFormToaster(props: StickyActionFormToasterProps) {
const submitButtonColorValue = submitButtonColor ?? 'green'
return (
-
+
-
+
{sortedUniqueValues.map(
([value, count]) =>
value != null && (
column.setFilterValue((arr: [] = []) => [...new Set([...arr, value])])}
>
{column.columnDef.meta?.customFacetEntry ? (
@@ -91,8 +94,8 @@ export function TableFilter({ column }: { column: Column }) {
})
) : (
<>
- {value}
- {hideCount ? null : count}
+ {value}
+ {hideCount ? null : count}
>
)}
diff --git a/libs/shared/ui/src/lib/styles/base/themes.scss b/libs/shared/ui/src/lib/styles/base/themes.scss
index 3c3f0cef869..75667ba78f8 100644
--- a/libs/shared/ui/src/lib/styles/base/themes.scss
+++ b/libs/shared/ui/src/lib/styles/base/themes.scss
@@ -7,6 +7,7 @@
--background-1: hsla(0, 0%, 100%, 1);
--background-2: hsla(270, 20%, 98%, 1);
--background-overlay: hsla(0, 0%, 0%, 0.8);
+ --background-invert-1: hsla(270, 6%, 3%, 1);
/* Neutral */
--neutral-1: hsla(300, 100%, 100%, 1);
@@ -101,6 +102,9 @@
--negative-12: hsla(8, 50%, 24%, 1);
/* Negative invert */
+ --negative-invert-2: hsla(10, 24%, 10%, 1);
+ --negative-invert-3: hsla(5, 48%, 15%, 1);
+ --negative-invert-6: hsla(7, 55%, 28%, 1);
--negative-invert-11: hsla(12, 100%, 75%, 1);
/* Info */
@@ -142,6 +146,7 @@
--background-1: hsla(270, 6%, 3%, 1);
--background-2: hsla(270, 6%, 7%, 1);
--background-overlay: hsla(0, 0%, 0%, 0.8);
+ --background-invert-1: hsla(0, 0%, 100%, 1);
/* Neutral */
--neutral-1: hsla(270, 6%, 7%, 1);
@@ -235,6 +240,9 @@
--negative-12: hsla(10, 86%, 89%, 1);
/* Negative invert */
+ --negative-invert-2: hsla(8, 100%, 98%, 1);
+ --negative-invert-3: hsla(10, 92%, 95%, 1);
+ --negative-invert-6: hsla(11, 95%, 84%, 1);
--negative-invert-11: hsla(10, 82%, 45%, 1);
/* Info */
diff --git a/tailwind-workspace-preset.js b/tailwind-workspace-preset.js
index 05c9f7efa62..1f83a6ca6d2 100644
--- a/tailwind-workspace-preset.js
+++ b/tailwind-workspace-preset.js
@@ -280,6 +280,10 @@ module.exports = {
component: 'var(--negative-3)',
subtle: 'var(--negative-2)',
},
+ negativeInvert: {
+ component: 'var(--negative-invert-3)',
+ subtle: 'var(--negative-invert-2)',
+ },
positive: {
solid: 'var(--positive-9)',
solidHover: 'var(--positive-10)',
@@ -384,6 +388,10 @@ module.exports = {
component: 'var(--negative-alpha-7)',
subtle: 'var(--negative-6)',
},
+ negativeInvert: {
+ DEFAULT: 'var(--negative-invert-6)',
+ subtle: 'var(--negative-invert-6)',
+ },
warning: {
strong: 'var(--warning-9)',
component: 'var(--warning-alpha-7)',