diff --git a/.eslintignore b/.eslintignore index baa5366301c..5cbf16fc003 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,5 @@ node_modules !.storybook + +# Auto-generated TanStack Router file +apps/console-v5/src/routeTree.gen.ts diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d1035d1c6ae..fd3839c63bb 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -3,13 +3,13 @@ name: Pull request on: pull_request: branches: - - staging + - '**' jobs: test-build-and-deploy: uses: ./.github/workflows/test-build-and-deploy.yml with: flow: pull-request - branch: staging + branch: ${{ github.base_ref }} configuration: staging project-name: Console V3 environment-name: staging diff --git a/.github/workflows/test-build-and-deploy.yml b/.github/workflows/test-build-and-deploy.yml index 07fb8619481..9a296ccfe4e 100644 --- a/.github/workflows/test-build-and-deploy.yml +++ b/.github/workflows/test-build-and-deploy.yml @@ -98,10 +98,9 @@ jobs: # easier troubleshooting. See more here: https://nx.dev/nx-cloud/set-up/record-commands#recording-non-nx-commands parallel-commands: | npx nx-cloud record -- npx nx format:check + # Single line required: nrwl/ci splits by newline and runs each line as a separate parallel command parallel-commands-on-agents: | - npx nx affected --target=lint --parallel=3 - npx nx affected --target=test --parallel=3 --configuration=${{ inputs.configuration }} --ci --coverage --coverageReporters=lcov --silent - npx nx affected --target=build --parallel=3 + EXCLUDED_PAGES="$(node -e "const p=JSON.parse(require('child_process').execSync('npx nx show projects --json',{encoding:'utf8'})); process.stdout.write(p.filter(x=>x.startsWith('pages-')).join(','));")"; if [ -n "$EXCLUDED_PAGES" ]; then EXCLUDE_ARG="--exclude=$EXCLUDED_PAGES"; EXCLUDE_TEST_ARG="--exclude=$EXCLUDED_PAGES,console"; echo "Skipping pages projects: $EXCLUDED_PAGES"; else EXCLUDE_ARG=""; EXCLUDE_TEST_ARG="--exclude=console"; fi; if [ "${{ inputs.flow }}" = "pull-request" ]; then npx nx run console-v5:lint && npx nx affected --target=test --parallel=3 --configuration=${{ inputs.configuration }} --ci --coverage --coverageReporters=lcov --silent $EXCLUDE_TEST_ARG && yarn sync-changelog && npx nx run console-v5:build --configuration=development; else npx nx affected --target=lint --parallel=3 $EXCLUDE_ARG && npx nx affected --target=test --parallel=3 --configuration=${{ inputs.configuration }} --ci --coverage --coverageReporters=lcov --silent $EXCLUDE_TEST_ARG && yarn sync-changelog && npx nx affected --target=build --parallel=3 $EXCLUDE_ARG; fi artifacts-path: | dist/ coverage/ diff --git a/.gitignore b/.gitignore index 9a266c1ebcc..2898e7c588b 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,6 @@ Thumbs.db .nx/workspace-data .cursor/rules/nx-rules.mdc .github/instructions/nx.instructions.md + +vite.config.*.timestamp* +vitest.config.*.timestamp* \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 349c327ed2e..84fb8e6314c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,4 +10,7 @@ # Symlinked skill directories (cause EISDIR errors in prettier) .cursor/skills -.claude/skills \ No newline at end of file +.claude/skills + +# Auto-generated TanStack Router file +apps/console-v5/src/routeTree.gen.ts \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2c35670abca..a5ad1dc7532 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,14 +44,14 @@ RUN --mount=type=cache,target=/root/.yarn \ # Copy source files (use .dockerignore to exclude unnecessary files) COPY . . -# Build with NX cache mount for faster rebuilds +# Build console-v5 with NX cache mount for faster rebuilds RUN --mount=type=cache,target=/app/node_modules/.cache/nx \ - yarn build + yarn nx build console-v5 --configuration=production # Bundle static assets with nginx FROM nginx:1.25-alpine # Copy built assets from builder -COPY --from=builder /app/dist/apps/* /usr/share/nginx/html +COPY --from=builder /app/dist/apps/console-v5 /usr/share/nginx/html # Add your nginx.conf COPY nginx.conf /etc/nginx/conf.d/default.conf # Expose port diff --git a/__tests__/mocks.ts b/__tests__/mocks.ts index b4d35c0db33..60481643626 100644 --- a/__tests__/mocks.ts +++ b/__tests__/mocks.ts @@ -1,5 +1,6 @@ -import { Auth0ProviderOptions } from '@auth0/auth0-react' -import { ComponentType } from 'react' +import type { Auth0ProviderOptions } from '@auth0/auth0-react' +import type { ComponentType } from 'react' +import * as React from 'react' jest.mock('@auth0/auth0-react', () => ({ Auth0Provider: ({ children }: Auth0ProviderOptions) => children, @@ -16,6 +17,42 @@ jest.mock('@auth0/auth0-react', () => ({ }, })) +jest.mock('@tanstack/react-router', () => { + const React = jest.requireActual('react') + const navigateMock = jest.fn() + return { + ...jest.requireActual('@tanstack/react-router'), + useParams: jest.fn(() => ({ + organizationId: '', + projectId: '', + environmentId: '', + serviceId: '', + clusterId: '', + applicationId: '', + databaseId: '', + })), + useNavigate: jest.fn(() => navigateMock), + useLocation: jest.fn(() => ({ + pathname: '/', + search: '', + })), + useRouter: jest.fn(() => ({ + buildLocation: jest.fn(() => ({ + href: '/', + })), + })), + useMatches: jest.fn(() => []), + useSearch: jest.fn(() => ({})), + useMatchRoute: jest.fn(() => () => false), + Link: React.forwardRef( + ( + { children, ...props }: { children?: React.ReactNode; [key: string]: unknown }, + ref: React.Ref + ) => React.createElement('a', { ref, ...props }, children) + ), + } +}) + jest.mock('@uidotdev/usehooks', () => ({ useDocumentTitle: jest.fn(), useClickAway: jest.fn(), diff --git a/apps/console-v5/.eslintrc.json b/apps/console-v5/.eslintrc.json new file mode 100644 index 00000000000..89145d4e1bc --- /dev/null +++ b/apps/console-v5/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*", "**/vite.config.*.timestamp*", "**/vitest.config.*.timestamp*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/console-v5/index.html b/apps/console-v5/index.html new file mode 100644 index 00000000000..c9c289d35e0 --- /dev/null +++ b/apps/console-v5/index.html @@ -0,0 +1,15 @@ + + + + + Console + + + + + + +
+ + + diff --git a/apps/console-v5/postcss.config.js b/apps/console-v5/postcss.config.js new file mode 100644 index 00000000000..a8f1ac998bf --- /dev/null +++ b/apps/console-v5/postcss.config.js @@ -0,0 +1,15 @@ +const { join } = require('path') + +// Note: If you use library-specific PostCSS/Tailwind configuration then you should remove the `postcssConfig` build +// option from your application's configuration (i.e. project.json). +// +// See: https://nx.dev/guides/using-tailwind-css-in-react#step-4:-applying-configuration-to-libraries + +module.exports = { + plugins: { + tailwindcss: { + config: join(__dirname, 'tailwind.config.js'), + }, + autoprefixer: {}, + }, +} diff --git a/apps/console-v5/project.json b/apps/console-v5/project.json new file mode 100644 index 00000000000..32f5343e65b --- /dev/null +++ b/apps/console-v5/project.json @@ -0,0 +1,66 @@ +{ + "name": "console-v5", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "/apps/console-v5/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/apps/console-v5" + }, + "configurations": { + "development": { + "mode": "development" + }, + "production": { + "mode": "production" + } + } + }, + "serve": { + "executor": "@nx/vite:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "console-v5:build" + }, + "configurations": { + "development": { + "buildTarget": "console-v5:build:development", + "hmr": true + }, + "production": { + "buildTarget": "console-v5:build:production", + "hmr": false + } + } + }, + "preview": { + "dependsOn": ["build"], + "executor": "@nx/vite:preview-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "console-v5:build" + }, + "configurations": { + "development": { + "buildTarget": "console-v5:build:development" + }, + "production": { + "buildTarget": "console-v5:build:production" + } + } + }, + "serve-static": { + "executor": "@nx/web:file-server", + "dependsOn": ["build"], + "options": { + "buildTarget": "console-v5:build", + "spa": true + } + } + } +} diff --git a/apps/console-v5/public/changelog/latest.json b/apps/console-v5/public/changelog/latest.json new file mode 100644 index 00000000000..94a6046dab6 --- /dev/null +++ b/apps/console-v5/public/changelog/latest.json @@ -0,0 +1,8 @@ +[ + { + "name": "Qovery Changelogs", + "summary": "We're super excited to announce new features and improvements: NGINX Ingress migration update, Road to Kubernetes 1.34 and 1.35, Terraform improvements", + "url": "https://www.qovery.com/changelog/2026-03-11", + "firstPublishedAt": "2026-03-11T00:00:00.000Z" + } +] diff --git a/apps/console-v5/public/favicon.ico b/apps/console-v5/public/favicon.ico new file mode 100644 index 00000000000..20f5df0ffaa Binary files /dev/null and b/apps/console-v5/public/favicon.ico differ diff --git a/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumb-item.tsx b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumb-item.tsx new file mode 100644 index 00000000000..2a36f8f5cc0 --- /dev/null +++ b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumb-item.tsx @@ -0,0 +1,205 @@ +import { Link, useNavigate } from '@tanstack/react-router' +import clsx from 'clsx' +import { Command } from 'cmdk' +import { type ReactNode, useCallback, useMemo, useRef, useState } from 'react' +import { Button, Command as CommandMenu, Icon, Popover, Truncate } from '@qovery/shared/ui' +import { twMerge } from '@qovery/shared/util-js' + +export interface BreadcrumbItemData { + id: string + label: string + path: string + prefix?: ReactNode + suffix?: ReactNode + logo_url?: string +} + +interface BreadcrumbItemProps { + item: BreadcrumbItemData + items?: BreadcrumbItemData[] + isCurrentScope?: boolean +} + +export function BreadcrumbItem({ item, items, isCurrentScope = false }: BreadcrumbItemProps) { + const navigate = useNavigate() + const [searchQuery, setSearchQuery] = useState('') + const [open, setOpen] = useState(false) + const inputRef = useRef(null) + const listRef = useRef(null) + + const filteredItems = useMemo( + () => items?.filter((i) => i.label.toLowerCase().includes(searchQuery.toLowerCase())) || [], + [items, searchQuery] + ) + + const focusSearch = useCallback(() => { + requestAnimationFrame(() => { + inputRef.current?.focus() + }) + }, []) + + const scrollToCurrentItem = useCallback(() => { + requestAnimationFrame(() => { + const container = listRef.current + const currentItem = container?.querySelector('[data-current-item="true"]') + + if (!(container instanceof HTMLElement) || !(currentItem instanceof HTMLElement)) { + return + } + + const containerRect = container.getBoundingClientRect() + const itemRect = currentItem.getBoundingClientRect() + const isOutsideViewport = itemRect.top < containerRect.top || itemRect.bottom > containerRect.bottom + + if (isOutsideViewport) { + currentItem.scrollIntoView({ block: 'nearest' }) + } + }) + }, []) + + const handleOpenChange = useCallback( + (nextOpen: boolean) => { + setOpen(nextOpen) + + if (nextOpen) { + focusSearch() + scrollToCurrentItem() + return + } + + setSearchQuery('') + }, + [focusSearch, scrollToCurrentItem] + ) + + const handleSelect = useCallback( + (path: string) => { + setOpen(false) + setSearchQuery('') + navigate({ to: path }) + }, + [navigate] + ) + + if (!items || items.length <= 1) { + return ( + + {item.prefix} + + {item.suffix} + + ) + } + + return ( +
+ + {item.prefix} + + {item.suffix} + + + + + + { + event.preventDefault() + focusSearch() + }} + > + +
+
+ + +
+
+ + +
+ +

No result for this search

+
+
+ {filteredItems.map((listItem) => ( + handleSelect(listItem.path)} + className="mb-1 justify-between truncate rounded px-2 py-2 text-sm font-medium text-neutral last:mb-0 data-[selected=true]:bg-surface-brand-subtle data-[selected=true]:text-brand" + > +
+ + {listItem.label} +
+
+ ))} +
+
+
+
+
+ ) +} + +export default BreadcrumbItem diff --git a/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx new file mode 100644 index 00000000000..457cc4dcc07 --- /dev/null +++ b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx @@ -0,0 +1,183 @@ +import { useParams, useRouter } from '@tanstack/react-router' +import { useMemo } from 'react' +import { ClusterAvatar, useClusters } from '@qovery/domains/clusters/feature' +import { EnvironmentMode, useEnvironments } from '@qovery/domains/environments/feature' +import { useOrganization, useOrganizations } from '@qovery/domains/organizations/feature' +import { useProjects } from '@qovery/domains/projects/feature' +import { ServiceAvatar, ServiceStateChip, useServices } from '@qovery/domains/services/feature' +import { Avatar } from '@qovery/shared/ui' +import { Separator } from '../header' +import { BreadcrumbItem, type BreadcrumbItemData } from './breadcrumb-item' + +export function Breadcrumbs() { + const { buildLocation } = useRouter() + const { + organizationId = '', + clusterId = '', + projectId = '', + environmentId = '', + serviceId = '', + } = useParams({ strict: false }) + + const { data: organizations = [] } = useOrganizations({ + enabled: true, + suspense: true, + }) + const { data: organization } = useOrganization({ organizationId, enabled: !!organizationId, suspense: true }) + const { data: clusters = [] } = useClusters({ organizationId, suspense: true }) + const { data: projects = [] } = useProjects({ organizationId, suspense: true }) + const { data: environments = [] } = useEnvironments({ projectId, suspense: true }) + const { data: services = [] } = useServices({ environmentId, suspense: true }) + + // Necessary to keep the organization from client by Qovery team + const allOrganizations = + organizations.find((org) => org.id !== organizationId) && organization + ? [...organizations.filter((org) => org.id !== organizationId), organization] + : organizations + + const orgItems: BreadcrumbItemData[] = allOrganizations + .sort((a, b) => a.name.trim().localeCompare(b.name.trim())) + .map((organization) => ({ + id: organization.id, + label: organization.name, + path: buildLocation({ to: '/organization/$organizationId/overview', params: { organizationId: organization.id } }) + .href, + logo_url: organization.logo_url ?? undefined, + })) + + const currentOrg = useMemo( + () => orgItems.find((organization) => organization.id === organizationId), + [organizationId, orgItems] + ) + + const clusterItems: BreadcrumbItemData[] = clusters.map((cluster) => ({ + id: cluster.id, + label: cluster.name, + path: buildLocation({ + to: '/organization/$organizationId/cluster/$clusterId/overview', + params: { organizationId, clusterId: cluster.id }, + }).href, + })) + + const projectItems: BreadcrumbItemData[] = projects + .sort((a, b) => a.name.trim().localeCompare(b.name.trim())) + .map((project) => ({ + id: project.id, + label: project.name, + path: buildLocation({ + to: '/organization/$organizationId/project/$projectId/overview', + params: { organizationId, projectId: project.id }, + }).href, + })) + + const environmentItems: BreadcrumbItemData[] = environments + .sort((a, b) => a.name.trim().localeCompare(b.name.trim())) + .map((environment) => ({ + id: environment.id, + label: environment.name, + prefix: , + path: buildLocation({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview', + params: { organizationId, projectId: environment.project.id, environmentId: environment.id }, + }).href, + })) + + const serviceItems: BreadcrumbItemData[] = services + .sort((a, b) => a.name.trim().localeCompare(b.name.trim())) + .map((service) => ({ + id: service.id, + label: service.name, + path: buildLocation({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview', + params: { organizationId, projectId, environmentId, serviceId: service.id }, + }).href, + prefix: ( + + ), + suffix: , + })) + + const currentCluster = useMemo( + () => clusterItems.find((cluster) => cluster.id === clusterId), + [clusterId, clusterItems] + ) + + const currentProject = useMemo( + () => projectItems.find((project) => project.id === projectId), + [projectId, projectItems] + ) + + const currentEnvironment = useMemo( + () => environmentItems.find((environment) => environment.id === environmentId), + [environmentId, environmentItems] + ) + + const currentService = useMemo( + () => serviceItems.find((service) => service.id === serviceId), + [serviceId, serviceItems] + ) + + const breadcrumbData: Array<{ item: BreadcrumbItemData; items: BreadcrumbItemData[] }> = [] + + if (currentOrg) { + breadcrumbData.push({ + item: { + ...currentOrg, + prefix: ( + + ), + }, + items: orgItems, + }) + } + + if (currentCluster) { + breadcrumbData.push({ + item: { + ...currentCluster, + prefix: cluster.id === clusterId)} size="sm" />, + }, + items: clusterItems, + }) + } + + if (currentProject) { + breadcrumbData.push({ + item: currentProject, + items: projectItems, + }) + } + + if (currentEnvironment) { + breadcrumbData.push({ + item: currentEnvironment, + items: environmentItems, + }) + } + + if (currentService) { + breadcrumbData.push({ + item: currentService, + items: serviceItems, + }) + } + + return ( +
+ {breadcrumbData.map((data, index) => ( +
+ + {index < breadcrumbData.length - 1 && } +
+ ))} +
+ ) +} + +export default Breadcrumbs diff --git a/apps/console-v5/src/app/components/header/header.tsx b/apps/console-v5/src/app/components/header/header.tsx new file mode 100644 index 00000000000..a983c6f23bd --- /dev/null +++ b/apps/console-v5/src/app/components/header/header.tsx @@ -0,0 +1,45 @@ +import { useFeatureFlagVariantKey } from 'posthog-js/react' +import { Suspense } from 'react' +import { SpotlightTrigger } from '@qovery/pages/layout' +import { DevopsCopilotButton } from '@qovery/shared/devops-copilot/feature' +import { LogoIcon } from '@qovery/shared/ui' +import { Breadcrumbs } from './breadcrumbs/breadcrumbs' +import { UserMenu } from './user-menu/user-menu' + +export function Separator() { + return ( +
+ + + +
+ ) +} + +export function Header() { + const isDevopsCopilotEnabled = useFeatureFlagVariantKey('devops-copilot') + + return ( +
+
+ + + }> + <> + +
+ + {isDevopsCopilotEnabled && } + +
+ +
+
+
+ ) +} + +export default Header diff --git a/apps/console-v5/src/app/components/header/user-menu/user-menu.tsx b/apps/console-v5/src/app/components/header/user-menu/user-menu.tsx new file mode 100644 index 00000000000..658a9c6d5fd --- /dev/null +++ b/apps/console-v5/src/app/components/header/user-menu/user-menu.tsx @@ -0,0 +1,108 @@ +import { useAuth0 } from '@auth0/auth0-react' +import * as ToggleGroup from '@radix-ui/react-toggle-group' +import { useState } from 'react' +import { UserSettingsModal, useUserAccount } from '@qovery/shared/iam/feature' +import { Avatar, DropdownMenu, Icon } from '@qovery/shared/ui' +import { useModal } from '@qovery/shared/ui' +import { type Theme, useTheme } from '../../theme-provider/theme-provider' + +const THEMES = [ + { value: 'system', icon: 'desktop' }, + { value: 'light', icon: 'sun-bright' }, + { value: 'dark', icon: 'moon' }, +] as const + +export function UserMenu() { + const { theme, setTheme } = useTheme() + const { logout, user: userToken } = useAuth0() + const { data: user } = useUserAccount() + const [hoveredIndex, setHoveredIndex] = useState(null) + const { openModal } = useModal() + + const onLogout = async () => { + await logout() + } + + const displayName = `${user?.first_name} ${user?.last_name}` + const initials = displayName + .split(' ') + .map((part) => part.charAt(0).toUpperCase()) + .slice(0, 2) + .join('') + + const activeIndex = THEMES.findIndex((t) => t.value === theme) + const indicatorIndex = hoveredIndex ?? activeIndex + + return ( + + + + + +
+

{displayName}

+

{user?.communication_email ?? userToken?.email}

+
+ + + + openModal({ content: })}> + Profile settings + + +
+ Theme + val && setTheme(val as Theme)} + className="relative flex h-6 items-center gap-0.5 rounded-md border border-neutral bg-surface-neutral p-[1px]" + onMouseLeave={() => setHoveredIndex(null)} + > + + {THEMES.map((t, index) => ( + setHoveredIndex(index)} + > + + + ))} + +
+ + + + Give feedback + + + + Home page + + + + Sign out +
+
+ ) +} + +export default UserMenu diff --git a/apps/console-v5/src/app/components/theme-provider/theme-provider.tsx b/apps/console-v5/src/app/components/theme-provider/theme-provider.tsx new file mode 100644 index 00000000000..e21979aea14 --- /dev/null +++ b/apps/console-v5/src/app/components/theme-provider/theme-provider.tsx @@ -0,0 +1,99 @@ +import { createContext, useContext, useEffect, useState } from 'react' + +export type Theme = 'dark' | 'light' | 'system' + +type ThemeProviderProps = { + children: React.ReactNode + defaultTheme?: Theme + storageKey?: string +} + +type ThemeProviderState = { + theme: Theme + setTheme: (theme: Theme) => void +} + +const initialState: ThemeProviderState = { + theme: 'system', + setTheme: () => null, +} + +const ThemeProviderContext = createContext(initialState) + +// Inspired by https://ui.shadcn.com/docs/dark-mode/vite +export function ThemeProvider({ + children, + defaultTheme = 'system', + storageKey = 'ui-theme', + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme) + + useEffect(() => { + const root = window.document.documentElement + + // Create stylesheet to disable transitions during theme switch + // Inspired by https://paco.me/writing/disable-theme-transitions + const css = document.createElement('style') + css.appendChild( + document.createTextNode( + `* { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + -ms-transition: none !important; + transition: none !important; + }` + ) + ) + document.head.appendChild(css) + + const systemThemeQuery = window.matchMedia('(prefers-color-scheme: dark)') + if (theme === 'system') { + const systemTheme = systemThemeQuery.matches ? 'dark' : 'light' + root.setAttribute('data-theme', systemTheme) + } else { + root.setAttribute('data-theme', theme) + } + + // Force browser repaint + void window.getComputedStyle(css).opacity + // Remove the stylesheet to re-enable transitions + document.head.removeChild(css) + + const themeChangeHandler = (event: MediaQueryListEvent) => { + if (theme === 'system') { + const newColorScheme = event.matches ? 'dark' : 'light' + root.setAttribute('data-theme', newColorScheme) + } + } + + systemThemeQuery.addEventListener('change', themeChangeHandler) + + return () => { + systemThemeQuery.removeEventListener('change', themeChangeHandler) + } + }, [theme]) + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme) + setTheme(theme) + }, + } + + return ( + + {children} + + ) +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext) + + if (context === undefined) throw new Error('useTheme must be used within a ThemeProvider') + + return context +} diff --git a/apps/console-v5/src/assets/.gitkeep b/apps/console-v5/src/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/console-v5/src/auth/auth0.tsx b/apps/console-v5/src/auth/auth0.tsx new file mode 100644 index 00000000000..c2c73fec003 --- /dev/null +++ b/apps/console-v5/src/auth/auth0.tsx @@ -0,0 +1,66 @@ +import { Auth0Provider, type User, useAuth0 } from '@auth0/auth0-react' +import { createContext, useContext } from 'react' +import { OAUTH_AUDIENCE, OAUTH_DOMAIN, OAUTH_KEY } from '@qovery/shared/util-node-env' + +export interface Auth0ContextType { + isAuthenticated: boolean + user: User | undefined + login: (returnTo?: string) => void + logout: () => void + isLoading: boolean +} + +export const Auth0Context = createContext(undefined) + +// Holds the returnTo path from Auth0's appState between onRedirectCallback and the callback component +let _pendingReturnTo: string | undefined + +export function consumePendingReturnTo(): string | undefined { + const val = _pendingReturnTo + _pendingReturnTo = undefined + return val +} + +export function Auth0Wrapper({ children }: { children: React.ReactNode }) { + return ( + { + _pendingReturnTo = appState?.returnTo + }} + > + {children} + + ) +} + +function Auth0ContextProvider({ children }: { children: React.ReactNode }) { + const { isAuthenticated, user, loginWithRedirect, logout, isLoading } = useAuth0() + + const contextValue = { + isAuthenticated, + user, + login: (returnTo?: string) => loginWithRedirect({ appState: { returnTo } }), + logout: () => logout({ logoutParams: { returnTo: window.location.origin } }), + isLoading, + } + + return {children} +} + +export function useAuth0Context() { + const context = useContext(Auth0Context) + if (context === undefined) { + throw new Error('useAuth0Context must be used within Auth0Wrapper') + } + return context +} diff --git a/apps/console-v5/src/main.tsx b/apps/console-v5/src/main.tsx new file mode 100644 index 00000000000..2b0e29f656d --- /dev/null +++ b/apps/console-v5/src/main.tsx @@ -0,0 +1,191 @@ +import { type IconName } from '@fortawesome/fontawesome-common-types' +import { Provider as TooltipProvider } from '@radix-ui/react-tooltip' +import { + type Mutation, + MutationCache, + type Query, + QueryCache, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' +import { RouterProvider, createRouter } from '@tanstack/react-router' +import axios from 'axios' +import posthog from 'posthog-js' +import { StrictMode, useEffect } from 'react' +import * as ReactDOM from 'react-dom/client' +import { FlatProviders, makeProvider } from 'react-flat-providers' +import { IntercomProvider } from 'react-use-intercom' +import { devopsCopilotAxios } from '@qovery/shared/devops-copilot/data-access' +import { LoaderSpinner, ToastEnum, toast, toastError } from '@qovery/shared/ui' +import { + DEVOPS_COPILOT_API_BASE_URL, + INTERCOM, + POSTHOG, + POSTHOG_APIHOST, + QOVERY_API, +} from '@qovery/shared/util-node-env' +import { useAuthInterceptor } from '@qovery/shared/utils' +// TODO: Improve this import to use the shared/ui package +// eslint-disable-next-line @nx/enforce-module-boundaries +import '../../../libs/shared/ui/src/lib/styles/main.scss' +import { ThemeProvider } from './app/components/theme-provider/theme-provider' +import { Auth0Wrapper, useAuth0Context } from './auth/auth0' +// Import the generated route tree +import { routeTree } from './routeTree.gen' + +type ToastArgs = { + status?: ToastEnum + title: string + description?: string + callback?: () => void + iconAction?: IconName + labelAction?: string + externalLink?: string +} + +interface _QueryMeta { + notifyOnSuccess?: boolean | ((data: unknown, query: Query) => ToastArgs) | ToastArgs + notifyOnError?: boolean | { title: string; description?: string } +} + +interface _MutationMeta { + notifyOnSuccess?: + | boolean + | (( + data: unknown, + variables: unknown, + context: unknown, + mutation: Mutation + ) => ToastArgs) + | ToastArgs + notifyOnError?: boolean | { title: string; description?: string } +} + +declare module '@tanstack/react-query' { + interface MutationMeta extends _MutationMeta {} + interface QueryMeta extends _QueryMeta {} +} + +// posthog init +posthog.init(POSTHOG, { + api_host: POSTHOG_APIHOST, + capture_pageview: 'history_change', +}) + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60_000, + }, + }, + mutationCache: new MutationCache({ + onSuccess(data, variables, context, mutation) { + if (mutation.meta?.notifyOnSuccess) { + if (mutation.meta.notifyOnSuccess === true) { + toast(ToastEnum.SUCCESS, JSON.stringify(data)) + } else { + const { + status = ToastEnum.SUCCESS, + title, + description, + callback, + iconAction, + labelAction, + externalLink, + } = typeof mutation.meta.notifyOnSuccess === 'function' + ? mutation.meta.notifyOnSuccess(data, variables, context, mutation) + : mutation.meta.notifyOnSuccess + toast(status, title, description, callback, iconAction, labelAction, externalLink) + } + } + }, + onError(error, _variables, _context, mutation) { + if (mutation.meta?.notifyOnError) { + if (mutation.meta.notifyOnError === true) { + toastError(error as Error) + } else { + toastError(error as Error, mutation.meta.notifyOnError.title, mutation.meta.notifyOnError.description) + } + } + }, + }), + queryCache: new QueryCache({ + onSuccess(data, query) { + if (query.meta?.notifyOnSuccess) { + if (query.meta.notifyOnSuccess === true) { + toast(ToastEnum.SUCCESS, JSON.stringify(data)) + } else { + const { + status = ToastEnum.SUCCESS, + title, + description, + callback, + iconAction, + labelAction, + externalLink, + } = typeof query.meta.notifyOnSuccess === 'function' + ? query.meta.notifyOnSuccess(data, query) + : query.meta.notifyOnSuccess + toast(status, title, description, callback, iconAction, labelAction, externalLink) + } + } + }, + onError(error, query) { + if (query.meta?.notifyOnError) { + if (query.meta.notifyOnError === true) { + toastError(error as Error) + } else { + toastError(error as Error, query.meta.notifyOnError.title, query.meta.notifyOnError.description) + } + } + }, + }), +}) + +function App() { + const auth = useAuth0Context() + + // Keep PostHog's identified user in sync once Auth0 resolves the session + useEffect(() => { + if (!auth.user?.sub) { + return + } + + posthog.identify(auth.user.sub, { + ...auth.user, + }) + }, [auth.user]) + + // Create a new router instance + const router = createRouter({ routeTree, context: { auth, queryClient } }) + + useAuthInterceptor(axios, QOVERY_API) + useAuthInterceptor(devopsCopilotAxios, DEVOPS_COPILOT_API_BASE_URL) + + if (auth.isLoading) { + return ( +
+ +
+ ) + } + + return +} + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) +root.render( + + + + + +) diff --git a/apps/console-v5/src/routeTree.gen.ts b/apps/console-v5/src/routeTree.gen.ts new file mode 100644 index 00000000000..e65e7d13fd6 --- /dev/null +++ b/apps/console-v5/src/routeTree.gen.ts @@ -0,0 +1,3498 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as AuthenticatedRouteImport } from './routes/_authenticated' +import { Route as IndexRouteImport } from './routes/index' +import { Route as LoginIndexRouteImport } from './routes/login/index' +import { Route as LoginAuth0CallbackRouteImport } from './routes/login/auth0-callback' +import { Route as AuthenticatedOrganizationRouteRouteImport } from './routes/_authenticated/organization/route' +import { Route as AuthenticatedOrganizationIndexRouteImport } from './routes/_authenticated/organization/index' +import { Route as AuthenticatedAcceptInvitationIndexRouteImport } from './routes/_authenticated/accept-invitation/index' +import { Route as AuthenticatedOnboardingProjectRouteImport } from './routes/_authenticated/onboarding/project' +import { Route as AuthenticatedOnboardingPlansRouteImport } from './routes/_authenticated/onboarding/plans' +import { Route as AuthenticatedOnboardingPersonalizeRouteImport } from './routes/_authenticated/onboarding/personalize' +import { Route as AuthenticatedOrganizationOrganizationIdRouteRouteImport } from './routes/_authenticated/organization/$organizationId/route' +import { Route as AuthenticatedOrganizationOrganizationIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/index' +import { Route as AuthenticatedOrganizationOrganizationIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdClustersRouteImport } from './routes/_authenticated/organization/$organizationId/clusters' +import { Route as AuthenticatedOrganizationOrganizationIdAuditLogsRouteImport } from './routes/_authenticated/organization/$organizationId/audit-logs' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/settings/route' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/route' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/settings/index' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/index' +import { Route as AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/$clusterId/index' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport } from './routes/_authenticated/organization/$organizationId/settings/webhook' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport } from './routes/_authenticated/organization/$organizationId/settings/members' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/labels-annotations' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/helm-repositories' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport } from './routes/_authenticated/organization/$organizationId/settings/git-repository-access' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/container-registries' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/cloud-credentials' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-summary' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/billing-details' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport } from './routes/_authenticated/organization/$organizationId/settings/api-token' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport } from './routes/_authenticated/organization/$organizationId/settings/ai-copilot' +import { Route as AuthenticatedOrganizationOrganizationIdClusterNewRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/new' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/notification-channel' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/issues' +import { Route as AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport } from './routes/_authenticated/organization/$organizationId/alerts/alert-rules' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRouteImport } from './routes/_authenticated/organization/$organizationId/settings/roles/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/index' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/variables' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/route' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/route' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/route' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/index' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/index' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRouteImport } from './routes/_authenticated/organization/$organizationId/settings/roles/edit/$roleId' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/summary' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/general' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/features' +import { Route as AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/create/$slug/eks' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' +import { Route as AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables' +import { Route as 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 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 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 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' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline' +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 AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/route' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/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 AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/index' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/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' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' +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 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' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.$alertId.edit' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.create.metric.$metric' + +const AuthenticatedRoute = AuthenticatedRouteImport.update({ + id: '/_authenticated', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const LoginIndexRoute = LoginIndexRouteImport.update({ + id: '/login/', + path: '/login/', + getParentRoute: () => rootRouteImport, +} as any) +const LoginAuth0CallbackRoute = LoginAuth0CallbackRouteImport.update({ + id: '/login/auth0-callback', + path: '/login/auth0-callback', + getParentRoute: () => rootRouteImport, +} as any) +const AuthenticatedOrganizationRouteRoute = + AuthenticatedOrganizationRouteRouteImport.update({ + id: '/organization', + path: '/organization', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOrganizationIndexRoute = + AuthenticatedOrganizationIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => AuthenticatedOrganizationRouteRoute, + } as any) +const AuthenticatedAcceptInvitationIndexRoute = + AuthenticatedAcceptInvitationIndexRouteImport.update({ + id: '/accept-invitation/', + path: '/accept-invitation/', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingProjectRoute = + AuthenticatedOnboardingProjectRouteImport.update({ + id: '/onboarding/project', + path: '/onboarding/project', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingPlansRoute = + AuthenticatedOnboardingPlansRouteImport.update({ + id: '/onboarding/plans', + path: '/onboarding/plans', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOnboardingPersonalizeRoute = + AuthenticatedOnboardingPersonalizeRouteImport.update({ + id: '/onboarding/personalize', + path: '/onboarding/personalize', + getParentRoute: () => AuthenticatedRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdRouteRoute = + AuthenticatedOrganizationOrganizationIdRouteRouteImport.update({ + id: '/$organizationId', + path: '/$organizationId', + getParentRoute: () => AuthenticatedOrganizationRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdIndexRoute = + AuthenticatedOrganizationOrganizationIdIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdOverviewRoute = + AuthenticatedOrganizationOrganizationIdOverviewRouteImport.update({ + id: '/overview', + path: '/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdClustersRoute = + AuthenticatedOrganizationOrganizationIdClustersRouteImport.update({ + id: '/clusters', + path: '/clusters', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdAuditLogsRoute = + AuthenticatedOrganizationOrganizationIdAuditLogsRouteImport.update({ + id: '/audit-logs', + path: '/audit-logs', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsRouteRoute = + AuthenticatedOrganizationOrganizationIdSettingsRouteRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdAlertsRouteRoute = + AuthenticatedOrganizationOrganizationIdAlertsRouteRouteImport.update({ + id: '/alerts', + path: '/alerts', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsIndexRoute = + AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdAlertsIndexRoute = + AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute = + AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport.update({ + id: '/$clusterId/', + path: '/$clusterId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute = + AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport.update({ + id: '/webhook', + path: '/webhook', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsMembersRoute = + AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport.update({ + id: '/members', + path: '/members', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute = + AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport.update( + { + id: '/labels-annotations', + path: '/labels-annotations', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute = + AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport.update( + { + id: '/helm-repositories', + path: '/helm-repositories', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute = + AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport.update( + { + id: '/git-repository-access', + path: '/git-repository-access', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute = + AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport.update({ + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute = + AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport.update({ + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute = + AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport.update( + { + id: '/container-registries', + path: '/container-registries', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute = + AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport.update( + { + id: '/cloud-credentials', + path: '/cloud-credentials', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute = + AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport.update( + { + id: '/billing-summary', + path: '/billing-summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute = + AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport.update( + { + id: '/billing-details', + path: '/billing-details', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute = + AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport.update({ + id: '/api-token', + path: '/api-token', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute = + AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport.update({ + id: '/ai-copilot', + path: '/ai-copilot', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdClusterNewRoute = + AuthenticatedOrganizationOrganizationIdClusterNewRouteImport.update({ + id: '/cluster/new', + path: '/cluster/new', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute = + AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport.update( + { + id: '/notification-channel', + path: '/notification-channel', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute = + AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport.update({ + id: '/issues', + path: '/issues', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute = + AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport.update({ + id: '/alert-rules', + path: '/alert-rules', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute = + AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRouteImport.update({ + id: '/roles/', + path: '/roles/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport.update( + { + id: '/project/$projectId/', + path: '/project/$projectId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport.update( + { + id: '/cluster/$clusterId/', + path: '/cluster/$clusterId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport.update( + { + id: '/project/$projectId/variables', + path: '/project/$projectId/variables', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport.update( + { + id: '/project/$projectId/overview', + path: '/project/$projectId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport.update( + { + id: '/cluster/$clusterId/overview', + path: '/cluster/$clusterId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport.update( + { + id: '/cluster/$clusterId/cluster-logs', + path: '/cluster/$clusterId/cluster-logs', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRouteImport.update( + { + id: '/cluster/$clusterId/cloud-shell', + path: '/cluster/$clusterId/cloud-shell', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport.update( + { + id: '/project/$projectId/settings', + path: '/project/$projectId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport.update( + { + id: '/cluster/create/$slug', + path: '/cluster/create/$slug', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport.update( + { + id: '/cluster/$clusterId/settings', + path: '/cluster/$clusterId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRouteImport.update( + { + id: '/project/$projectId/deployment-rules/', + path: '/project/$projectId/deployment-rules/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute = + AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRouteImport.update( + { + id: '/roles/edit/$roleId', + path: '/roles/edit/$roleId', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRouteImport.update( + { + id: '/project/$projectId/deployment-rules/create', + path: '/project/$projectId/deployment-rules/create', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport.update( + { + id: '/summary', + path: '/summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRouteImport.update( + { + id: '/kubeconfig', + path: '/kubeconfig', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport.update( + { + id: '/features', + path: '/features', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRouteImport.update( + { + id: '/eks', + path: '/eks', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport.update( + { + id: '/network', + path: '/network', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport.update( + { + id: '/image-registry', + path: '/image-registry', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport.update( + { + id: '/eks-anywhere', + path: '/eks-anywhere', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport.update( + { + id: '/credentials', + path: '/credentials', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport.update( + { + id: '/advanced-settings', + path: '/advanced-settings', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/', + path: '/project/$projectId/environment/$environmentId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/variables', + path: '/project/$projectId/environment/$environmentId/variables', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/deployments', + path: '/project/$projectId/environment/$environmentId/deployments', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRouteImport.update( + { + id: '/project/$projectId/deployment-rules/edit/$deploymentRuleId', + path: '/project/$projectId/deployment-rules/edit/$deploymentRuleId', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/settings', + path: '/project/$projectId/environment/$environmentId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/overview', + path: '/project/$projectId/environment/$environmentId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport.update( + { + id: '/preview-environments', + path: '/preview-environments', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport.update( + { + id: '/deployment-rules', + path: '/deployment-rules', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/new', + path: '/project/$projectId/environment/$environmentId/service/new', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRouteImport.update( + { + id: '/pipeline', + path: '/pipeline', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/create/', + path: '/project/$projectId/environment/$environmentId/service/create/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/deployment/$deploymentId/', + path: '/project/$projectId/environment/$environmentId/deployment/$deploymentId/', + 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( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/service-logs', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/service-logs', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/overview', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs', + path: '/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/create/database', + path: '/project/$projectId/environment/$environmentId/service/create/database', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/create/$slug', + path: '/project/$projectId/environment/$environmentId/service/create/$slug', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/settings', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/settings', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/monitoring', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/monitoring', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRouteImport.update( + { + id: '/', + path: '/', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments/', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments/', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRouteImport.update( + { + id: '/summary', + path: '/summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRouteImport.update( + { + id: '/variables', + path: '/variables', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRouteImport.update( + { + id: '/summary', + path: '/summary', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRouteImport.update( + { + id: '/ports', + path: '/ports', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRouteImport.update( + { + id: '/health-checks', + path: '/health-checks', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRouteImport.update( + { + id: '/values-override-file', + path: '/values-override-file', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRouteImport.update( + { + id: '/values-override-arguments', + path: '/values-override-arguments', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRouteImport.update( + { + id: '/terraform-variables', + path: '/terraform-variables', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRouteImport.update( + { + id: '/terraform-configuration', + path: '/terraform-configuration', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRouteImport.update( + { + id: '/terraform-arguments', + path: '/terraform-arguments', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRouteImport.update( + { + id: '/storage', + path: '/storage', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRouteImport.update( + { + id: '/resources', + path: '/resources', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRouteImport.update( + { + id: '/port', + path: '/port', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRouteImport.update( + { + id: '/networking', + path: '/networking', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRouteImport.update( + { + id: '/health-checks', + path: '/health-checks', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRouteImport.update( + { + id: '/general', + path: '/general', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRouteImport.update( + { + id: '/domain', + path: '/domain', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRouteImport.update( + { + id: '/dockerfile', + path: '/dockerfile', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRouteImport.update( + { + id: '/deployment-restrictions', + path: '/deployment-restrictions', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRouteImport.update( + { + id: '/danger-zone', + path: '/danger-zone', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRouteImport.update( + { + id: '/configure', + path: '/configure', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRouteImport.update( + { + id: '/advanced-settings', + path: '/advanced-settings', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRouteImport.update( + { + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteImport.update( + { + id: '/alerts', + path: '/alerts', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRouteImport.update( + { + id: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId', + path: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRouteImport.update( + { + id: '/$alertId/edit', + path: '/$alertId/edit', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute, + } as any, + ) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRouteImport.update( + { + id: '/create/metric/$metric', + path: '/create/metric/$metric', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute, + } as any, + ) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/organization': typeof AuthenticatedOrganizationRouteRouteWithChildren + '/login/auth0-callback': typeof LoginAuth0CallbackRoute + '/login': typeof LoginIndexRoute + '/organization/$organizationId': typeof AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren + '/onboarding/personalize': typeof AuthenticatedOnboardingPersonalizeRoute + '/onboarding/plans': typeof AuthenticatedOnboardingPlansRoute + '/onboarding/project': typeof AuthenticatedOnboardingProjectRoute + '/accept-invitation': typeof AuthenticatedAcceptInvitationIndexRoute + '/organization/': typeof AuthenticatedOrganizationIndexRoute + '/organization/$organizationId/alerts': typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren + '/organization/$organizationId/settings': typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren + '/organization/$organizationId/audit-logs': typeof AuthenticatedOrganizationOrganizationIdAuditLogsRoute + '/organization/$organizationId/clusters': typeof AuthenticatedOrganizationOrganizationIdClustersRoute + '/organization/$organizationId/overview': typeof AuthenticatedOrganizationOrganizationIdOverviewRoute + '/organization/$organizationId/': typeof AuthenticatedOrganizationOrganizationIdIndexRoute + '/organization/$organizationId/alerts/alert-rules': typeof AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute + '/organization/$organizationId/alerts/issues': typeof AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute + '/organization/$organizationId/alerts/notification-channel': typeof AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute + '/organization/$organizationId/cluster/new': typeof AuthenticatedOrganizationOrganizationIdClusterNewRoute + '/organization/$organizationId/settings/ai-copilot': typeof AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute + '/organization/$organizationId/settings/api-token': typeof AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute + '/organization/$organizationId/settings/billing-details': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute + '/organization/$organizationId/settings/billing-summary': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute + '/organization/$organizationId/settings/cloud-credentials': typeof AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute + '/organization/$organizationId/settings/container-registries': typeof AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute + '/organization/$organizationId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute + '/organization/$organizationId/settings/general': typeof AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute + '/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute + '/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute + '/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute + '/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute + '/organization/$organizationId/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute + '/organization/$organizationId/alerts/': typeof AuthenticatedOrganizationOrganizationIdAlertsIndexRoute + '/organization/$organizationId/settings/': typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRoute + '/organization/$organizationId/cluster/$clusterId/settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren + '/organization/$organizationId/cluster/create/$slug': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren + '/organization/$organizationId/project/$projectId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren + '/organization/$organizationId/cluster/$clusterId/cloud-shell': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute + '/organization/$organizationId/cluster/$clusterId/cluster-logs': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute + '/organization/$organizationId/cluster/$clusterId/overview': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute + '/organization/$organizationId/project/$projectId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute + '/organization/$organizationId/project/$projectId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute + '/organization/$organizationId/cluster/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute + '/organization/$organizationId/project/$projectId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute + '/organization/$organizationId/settings/roles': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute + '/organization/$organizationId/cluster/$clusterId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute + '/organization/$organizationId/cluster/$clusterId/settings/credentials': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute + '/organization/$organizationId/cluster/$clusterId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute + '/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute + '/organization/$organizationId/cluster/$clusterId/settings/general': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute + '/organization/$organizationId/cluster/$clusterId/settings/image-registry': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute + '/organization/$organizationId/cluster/$clusterId/settings/network': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute + '/organization/$organizationId/cluster/$clusterId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute + '/organization/$organizationId/cluster/create/$slug/eks': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute + '/organization/$organizationId/cluster/create/$slug/features': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute + '/organization/$organizationId/cluster/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute + '/organization/$organizationId/cluster/create/$slug/kubeconfig': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute + '/organization/$organizationId/cluster/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute + '/organization/$organizationId/cluster/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute + '/organization/$organizationId/project/$projectId/deployment-rules/create': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute + '/organization/$organizationId/project/$projectId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute + '/organization/$organizationId/project/$projectId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute + '/organization/$organizationId/settings/roles/edit/$roleId': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute + '/organization/$organizationId/cluster/$clusterId/settings/': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute + '/organization/$organizationId/cluster/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute + '/organization/$organizationId/project/$projectId/deployment-rules': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute + '/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/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute + '/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/overview/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/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/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren + '/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/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute + '/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/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute + '/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/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/login/auth0-callback': typeof LoginAuth0CallbackRoute + '/login': typeof LoginIndexRoute + '/onboarding/personalize': typeof AuthenticatedOnboardingPersonalizeRoute + '/onboarding/plans': typeof AuthenticatedOnboardingPlansRoute + '/onboarding/project': typeof AuthenticatedOnboardingProjectRoute + '/accept-invitation': typeof AuthenticatedAcceptInvitationIndexRoute + '/organization': typeof AuthenticatedOrganizationIndexRoute + '/organization/$organizationId/audit-logs': typeof AuthenticatedOrganizationOrganizationIdAuditLogsRoute + '/organization/$organizationId/clusters': typeof AuthenticatedOrganizationOrganizationIdClustersRoute + '/organization/$organizationId/overview': typeof AuthenticatedOrganizationOrganizationIdOverviewRoute + '/organization/$organizationId': typeof AuthenticatedOrganizationOrganizationIdIndexRoute + '/organization/$organizationId/alerts/alert-rules': typeof AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute + '/organization/$organizationId/alerts/issues': typeof AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute + '/organization/$organizationId/alerts/notification-channel': typeof AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute + '/organization/$organizationId/cluster/new': typeof AuthenticatedOrganizationOrganizationIdClusterNewRoute + '/organization/$organizationId/settings/ai-copilot': typeof AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute + '/organization/$organizationId/settings/api-token': typeof AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute + '/organization/$organizationId/settings/billing-details': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute + '/organization/$organizationId/settings/billing-summary': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute + '/organization/$organizationId/settings/cloud-credentials': typeof AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute + '/organization/$organizationId/settings/container-registries': typeof AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute + '/organization/$organizationId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute + '/organization/$organizationId/settings/general': typeof AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute + '/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute + '/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute + '/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute + '/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute + '/organization/$organizationId/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute + '/organization/$organizationId/alerts': typeof AuthenticatedOrganizationOrganizationIdAlertsIndexRoute + '/organization/$organizationId/settings': typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRoute + '/organization/$organizationId/cluster/$clusterId/cloud-shell': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute + '/organization/$organizationId/cluster/$clusterId/cluster-logs': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute + '/organization/$organizationId/cluster/$clusterId/overview': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute + '/organization/$organizationId/project/$projectId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute + '/organization/$organizationId/project/$projectId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute + '/organization/$organizationId/cluster/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute + '/organization/$organizationId/project/$projectId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute + '/organization/$organizationId/settings/roles': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute + '/organization/$organizationId/cluster/$clusterId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute + '/organization/$organizationId/cluster/$clusterId/settings/credentials': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute + '/organization/$organizationId/cluster/$clusterId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute + '/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute + '/organization/$organizationId/cluster/$clusterId/settings/general': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute + '/organization/$organizationId/cluster/$clusterId/settings/image-registry': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute + '/organization/$organizationId/cluster/$clusterId/settings/network': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute + '/organization/$organizationId/cluster/$clusterId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute + '/organization/$organizationId/cluster/create/$slug/eks': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute + '/organization/$organizationId/cluster/create/$slug/features': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute + '/organization/$organizationId/cluster/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute + '/organization/$organizationId/cluster/create/$slug/kubeconfig': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute + '/organization/$organizationId/cluster/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute + '/organization/$organizationId/cluster/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute + '/organization/$organizationId/project/$projectId/deployment-rules/create': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute + '/organization/$organizationId/project/$projectId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute + '/organization/$organizationId/project/$projectId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute + '/organization/$organizationId/settings/roles/edit/$roleId': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute + '/organization/$organizationId/cluster/$clusterId/settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute + '/organization/$organizationId/cluster/create/$slug': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute + '/organization/$organizationId/project/$projectId/deployment-rules': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute + '/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute + '/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/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/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/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute + '/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/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 + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute + '/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/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/_authenticated': typeof AuthenticatedRouteWithChildren + '/_authenticated/organization': typeof AuthenticatedOrganizationRouteRouteWithChildren + '/login/auth0-callback': typeof LoginAuth0CallbackRoute + '/login/': typeof LoginIndexRoute + '/_authenticated/organization/$organizationId': typeof AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren + '/_authenticated/onboarding/personalize': typeof AuthenticatedOnboardingPersonalizeRoute + '/_authenticated/onboarding/plans': typeof AuthenticatedOnboardingPlansRoute + '/_authenticated/onboarding/project': typeof AuthenticatedOnboardingProjectRoute + '/_authenticated/accept-invitation/': typeof AuthenticatedAcceptInvitationIndexRoute + '/_authenticated/organization/': typeof AuthenticatedOrganizationIndexRoute + '/_authenticated/organization/$organizationId/alerts': typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren + '/_authenticated/organization/$organizationId/settings': typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren + '/_authenticated/organization/$organizationId/audit-logs': typeof AuthenticatedOrganizationOrganizationIdAuditLogsRoute + '/_authenticated/organization/$organizationId/clusters': typeof AuthenticatedOrganizationOrganizationIdClustersRoute + '/_authenticated/organization/$organizationId/overview': typeof AuthenticatedOrganizationOrganizationIdOverviewRoute + '/_authenticated/organization/$organizationId/': typeof AuthenticatedOrganizationOrganizationIdIndexRoute + '/_authenticated/organization/$organizationId/alerts/alert-rules': typeof AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute + '/_authenticated/organization/$organizationId/alerts/issues': typeof AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute + '/_authenticated/organization/$organizationId/alerts/notification-channel': typeof AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute + '/_authenticated/organization/$organizationId/cluster/new': typeof AuthenticatedOrganizationOrganizationIdClusterNewRoute + '/_authenticated/organization/$organizationId/settings/ai-copilot': typeof AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute + '/_authenticated/organization/$organizationId/settings/api-token': typeof AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute + '/_authenticated/organization/$organizationId/settings/billing-details': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute + '/_authenticated/organization/$organizationId/settings/billing-summary': typeof AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute + '/_authenticated/organization/$organizationId/settings/cloud-credentials': typeof AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute + '/_authenticated/organization/$organizationId/settings/container-registries': typeof AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute + '/_authenticated/organization/$organizationId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute + '/_authenticated/organization/$organizationId/settings/general': typeof AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute + '/_authenticated/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute + '/_authenticated/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute + '/_authenticated/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/_authenticated/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute + '/_authenticated/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute + '/_authenticated/organization/$organizationId/$clusterId/': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute + '/_authenticated/organization/$organizationId/alerts/': typeof AuthenticatedOrganizationOrganizationIdAlertsIndexRoute + '/_authenticated/organization/$organizationId/settings/': typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren + '/_authenticated/organization/$organizationId/cluster/create/$slug': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren + '/_authenticated/organization/$organizationId/project/$projectId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren + '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/overview': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute + '/_authenticated/organization/$organizationId/project/$projectId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute + '/_authenticated/organization/$organizationId/project/$projectId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute + '/_authenticated/organization/$organizationId/settings/roles/': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/eks': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/features': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/general': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute + '/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute + '/_authenticated/organization/$organizationId/project/$projectId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute + '/_authenticated/organization/$organizationId/settings/roles/edit/$roleId': typeof AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute + '/_authenticated/organization/$organizationId/cluster/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute + '/_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/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 + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute + '/_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/overview/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute + '/_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/create/$slug': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute + '/_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 + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute + '/_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/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 + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute + '/_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/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/organization' + | '/login/auth0-callback' + | '/login' + | '/organization/$organizationId' + | '/onboarding/personalize' + | '/onboarding/plans' + | '/onboarding/project' + | '/accept-invitation' + | '/organization/' + | '/organization/$organizationId/alerts' + | '/organization/$organizationId/settings' + | '/organization/$organizationId/audit-logs' + | '/organization/$organizationId/clusters' + | '/organization/$organizationId/overview' + | '/organization/$organizationId/' + | '/organization/$organizationId/alerts/alert-rules' + | '/organization/$organizationId/alerts/issues' + | '/organization/$organizationId/alerts/notification-channel' + | '/organization/$organizationId/cluster/new' + | '/organization/$organizationId/settings/ai-copilot' + | '/organization/$organizationId/settings/api-token' + | '/organization/$organizationId/settings/billing-details' + | '/organization/$organizationId/settings/billing-summary' + | '/organization/$organizationId/settings/cloud-credentials' + | '/organization/$organizationId/settings/container-registries' + | '/organization/$organizationId/settings/danger-zone' + | '/organization/$organizationId/settings/general' + | '/organization/$organizationId/settings/git-repository-access' + | '/organization/$organizationId/settings/helm-repositories' + | '/organization/$organizationId/settings/labels-annotations' + | '/organization/$organizationId/settings/members' + | '/organization/$organizationId/settings/webhook' + | '/organization/$organizationId/$clusterId' + | '/organization/$organizationId/alerts/' + | '/organization/$organizationId/settings/' + | '/organization/$organizationId/cluster/$clusterId/settings' + | '/organization/$organizationId/cluster/create/$slug' + | '/organization/$organizationId/project/$projectId/settings' + | '/organization/$organizationId/cluster/$clusterId/cloud-shell' + | '/organization/$organizationId/cluster/$clusterId/cluster-logs' + | '/organization/$organizationId/cluster/$clusterId/overview' + | '/organization/$organizationId/project/$projectId/overview' + | '/organization/$organizationId/project/$projectId/variables' + | '/organization/$organizationId/cluster/$clusterId' + | '/organization/$organizationId/project/$projectId' + | '/organization/$organizationId/settings/roles' + | '/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' + | '/organization/$organizationId/cluster/$clusterId/settings/credentials' + | '/organization/$organizationId/cluster/$clusterId/settings/danger-zone' + | '/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' + | '/organization/$organizationId/cluster/$clusterId/settings/general' + | '/organization/$organizationId/cluster/$clusterId/settings/image-registry' + | '/organization/$organizationId/cluster/$clusterId/settings/network' + | '/organization/$organizationId/cluster/$clusterId/settings/resources' + | '/organization/$organizationId/cluster/create/$slug/eks' + | '/organization/$organizationId/cluster/create/$slug/features' + | '/organization/$organizationId/cluster/create/$slug/general' + | '/organization/$organizationId/cluster/create/$slug/kubeconfig' + | '/organization/$organizationId/cluster/create/$slug/resources' + | '/organization/$organizationId/cluster/create/$slug/summary' + | '/organization/$organizationId/project/$projectId/deployment-rules/create' + | '/organization/$organizationId/project/$projectId/settings/danger-zone' + | '/organization/$organizationId/project/$projectId/settings/general' + | '/organization/$organizationId/settings/roles/edit/$roleId' + | '/organization/$organizationId/cluster/$clusterId/settings/' + | '/organization/$organizationId/cluster/create/$slug/' + | '/organization/$organizationId/project/$projectId/deployment-rules' + | '/organization/$organizationId/project/$projectId/settings/' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/overview' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/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' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' + | '/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/overview/' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + | '/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/create/$slug' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' + | '/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/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' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' + | '/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/create/$slug/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' + | '/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/create/$slug/' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/login/auth0-callback' + | '/login' + | '/onboarding/personalize' + | '/onboarding/plans' + | '/onboarding/project' + | '/accept-invitation' + | '/organization' + | '/organization/$organizationId/audit-logs' + | '/organization/$organizationId/clusters' + | '/organization/$organizationId/overview' + | '/organization/$organizationId' + | '/organization/$organizationId/alerts/alert-rules' + | '/organization/$organizationId/alerts/issues' + | '/organization/$organizationId/alerts/notification-channel' + | '/organization/$organizationId/cluster/new' + | '/organization/$organizationId/settings/ai-copilot' + | '/organization/$organizationId/settings/api-token' + | '/organization/$organizationId/settings/billing-details' + | '/organization/$organizationId/settings/billing-summary' + | '/organization/$organizationId/settings/cloud-credentials' + | '/organization/$organizationId/settings/container-registries' + | '/organization/$organizationId/settings/danger-zone' + | '/organization/$organizationId/settings/general' + | '/organization/$organizationId/settings/git-repository-access' + | '/organization/$organizationId/settings/helm-repositories' + | '/organization/$organizationId/settings/labels-annotations' + | '/organization/$organizationId/settings/members' + | '/organization/$organizationId/settings/webhook' + | '/organization/$organizationId/$clusterId' + | '/organization/$organizationId/alerts' + | '/organization/$organizationId/settings' + | '/organization/$organizationId/cluster/$clusterId/cloud-shell' + | '/organization/$organizationId/cluster/$clusterId/cluster-logs' + | '/organization/$organizationId/cluster/$clusterId/overview' + | '/organization/$organizationId/project/$projectId/overview' + | '/organization/$organizationId/project/$projectId/variables' + | '/organization/$organizationId/cluster/$clusterId' + | '/organization/$organizationId/project/$projectId' + | '/organization/$organizationId/settings/roles' + | '/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' + | '/organization/$organizationId/cluster/$clusterId/settings/credentials' + | '/organization/$organizationId/cluster/$clusterId/settings/danger-zone' + | '/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' + | '/organization/$organizationId/cluster/$clusterId/settings/general' + | '/organization/$organizationId/cluster/$clusterId/settings/image-registry' + | '/organization/$organizationId/cluster/$clusterId/settings/network' + | '/organization/$organizationId/cluster/$clusterId/settings/resources' + | '/organization/$organizationId/cluster/create/$slug/eks' + | '/organization/$organizationId/cluster/create/$slug/features' + | '/organization/$organizationId/cluster/create/$slug/general' + | '/organization/$organizationId/cluster/create/$slug/kubeconfig' + | '/organization/$organizationId/cluster/create/$slug/resources' + | '/organization/$organizationId/cluster/create/$slug/summary' + | '/organization/$organizationId/project/$projectId/deployment-rules/create' + | '/organization/$organizationId/project/$projectId/settings/danger-zone' + | '/organization/$organizationId/project/$projectId/settings/general' + | '/organization/$organizationId/settings/roles/edit/$roleId' + | '/organization/$organizationId/cluster/$clusterId/settings' + | '/organization/$organizationId/cluster/create/$slug' + | '/organization/$organizationId/project/$projectId/deployment-rules' + | '/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' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' + | '/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/overview' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings' + | '/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/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' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' + | '/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/create/$slug/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' + | '/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/create/$slug' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' + id: + | '__root__' + | '/' + | '/_authenticated' + | '/_authenticated/organization' + | '/login/auth0-callback' + | '/login/' + | '/_authenticated/organization/$organizationId' + | '/_authenticated/onboarding/personalize' + | '/_authenticated/onboarding/plans' + | '/_authenticated/onboarding/project' + | '/_authenticated/accept-invitation/' + | '/_authenticated/organization/' + | '/_authenticated/organization/$organizationId/alerts' + | '/_authenticated/organization/$organizationId/settings' + | '/_authenticated/organization/$organizationId/audit-logs' + | '/_authenticated/organization/$organizationId/clusters' + | '/_authenticated/organization/$organizationId/overview' + | '/_authenticated/organization/$organizationId/' + | '/_authenticated/organization/$organizationId/alerts/alert-rules' + | '/_authenticated/organization/$organizationId/alerts/issues' + | '/_authenticated/organization/$organizationId/alerts/notification-channel' + | '/_authenticated/organization/$organizationId/cluster/new' + | '/_authenticated/organization/$organizationId/settings/ai-copilot' + | '/_authenticated/organization/$organizationId/settings/api-token' + | '/_authenticated/organization/$organizationId/settings/billing-details' + | '/_authenticated/organization/$organizationId/settings/billing-summary' + | '/_authenticated/organization/$organizationId/settings/cloud-credentials' + | '/_authenticated/organization/$organizationId/settings/container-registries' + | '/_authenticated/organization/$organizationId/settings/danger-zone' + | '/_authenticated/organization/$organizationId/settings/general' + | '/_authenticated/organization/$organizationId/settings/git-repository-access' + | '/_authenticated/organization/$organizationId/settings/helm-repositories' + | '/_authenticated/organization/$organizationId/settings/labels-annotations' + | '/_authenticated/organization/$organizationId/settings/members' + | '/_authenticated/organization/$organizationId/settings/webhook' + | '/_authenticated/organization/$organizationId/$clusterId/' + | '/_authenticated/organization/$organizationId/alerts/' + | '/_authenticated/organization/$organizationId/settings/' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings' + | '/_authenticated/organization/$organizationId/cluster/create/$slug' + | '/_authenticated/organization/$organizationId/project/$projectId/settings' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/overview' + | '/_authenticated/organization/$organizationId/project/$projectId/overview' + | '/_authenticated/organization/$organizationId/project/$projectId/variables' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/' + | '/_authenticated/organization/$organizationId/project/$projectId/' + | '/_authenticated/organization/$organizationId/settings/roles/' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/eks' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/features' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/general' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/resources' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/summary' + | '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create' + | '/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' + | '/_authenticated/organization/$organizationId/project/$projectId/settings/general' + | '/_authenticated/organization/$organizationId/settings/roles/edit/$roleId' + | '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/' + | '/_authenticated/organization/$organizationId/cluster/create/$slug/' + | '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/' + | '/_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/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' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' + | '/_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/overview/' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + | '/_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/create/$slug' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' + | '/_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/' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' + | '/_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/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' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' + | '/_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/create/$slug/' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AuthenticatedRoute: typeof AuthenticatedRouteWithChildren + LoginAuth0CallbackRoute: typeof LoginAuth0CallbackRoute + LoginIndexRoute: typeof LoginIndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/_authenticated': { + id: '/_authenticated' + path: '' + fullPath: '' + preLoaderRoute: typeof AuthenticatedRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/login/': { + id: '/login/' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/login/auth0-callback': { + id: '/login/auth0-callback' + path: '/login/auth0-callback' + fullPath: '/login/auth0-callback' + preLoaderRoute: typeof LoginAuth0CallbackRouteImport + parentRoute: typeof rootRouteImport + } + '/_authenticated/organization': { + id: '/_authenticated/organization' + path: '/organization' + fullPath: '/organization' + preLoaderRoute: typeof AuthenticatedOrganizationRouteRouteImport + parentRoute: typeof AuthenticatedRoute + } + '/_authenticated/organization/': { + id: '/_authenticated/organization/' + path: '/' + fullPath: '/organization/' + preLoaderRoute: typeof AuthenticatedOrganizationIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationRouteRoute + } + '/_authenticated/accept-invitation/': { + id: '/_authenticated/accept-invitation/' + path: '/accept-invitation' + fullPath: '/accept-invitation' + preLoaderRoute: typeof AuthenticatedAcceptInvitationIndexRouteImport + parentRoute: typeof AuthenticatedRoute + } + '/_authenticated/onboarding/project': { + id: '/_authenticated/onboarding/project' + path: '/onboarding/project' + fullPath: '/onboarding/project' + preLoaderRoute: typeof AuthenticatedOnboardingProjectRouteImport + parentRoute: typeof AuthenticatedRoute + } + '/_authenticated/onboarding/plans': { + id: '/_authenticated/onboarding/plans' + path: '/onboarding/plans' + fullPath: '/onboarding/plans' + preLoaderRoute: typeof AuthenticatedOnboardingPlansRouteImport + parentRoute: typeof AuthenticatedRoute + } + '/_authenticated/onboarding/personalize': { + id: '/_authenticated/onboarding/personalize' + path: '/onboarding/personalize' + fullPath: '/onboarding/personalize' + preLoaderRoute: typeof AuthenticatedOnboardingPersonalizeRouteImport + parentRoute: typeof AuthenticatedRoute + } + '/_authenticated/organization/$organizationId': { + id: '/_authenticated/organization/$organizationId' + path: '/$organizationId' + fullPath: '/organization/$organizationId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationRouteRoute + } + '/_authenticated/organization/$organizationId/': { + id: '/_authenticated/organization/$organizationId/' + path: '/' + fullPath: '/organization/$organizationId/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/overview': { + id: '/_authenticated/organization/$organizationId/overview' + path: '/overview' + fullPath: '/organization/$organizationId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdOverviewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/clusters': { + id: '/_authenticated/organization/$organizationId/clusters' + path: '/clusters' + fullPath: '/organization/$organizationId/clusters' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClustersRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/audit-logs': { + id: '/_authenticated/organization/$organizationId/audit-logs' + path: '/audit-logs' + fullPath: '/organization/$organizationId/audit-logs' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAuditLogsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/settings': { + id: '/_authenticated/organization/$organizationId/settings' + path: '/settings' + fullPath: '/organization/$organizationId/settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/alerts': { + id: '/_authenticated/organization/$organizationId/alerts' + path: '/alerts' + fullPath: '/organization/$organizationId/alerts' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/settings/': { + id: '/_authenticated/organization/$organizationId/settings/' + path: '/' + fullPath: '/organization/$organizationId/settings/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/alerts/': { + id: '/_authenticated/organization/$organizationId/alerts/' + path: '/' + fullPath: '/organization/$organizationId/alerts/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRoute + } + '/_authenticated/organization/$organizationId/$clusterId/': { + id: '/_authenticated/organization/$organizationId/$clusterId/' + path: '/$clusterId' + fullPath: '/organization/$organizationId/$clusterId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/settings/webhook': { + id: '/_authenticated/organization/$organizationId/settings/webhook' + path: '/webhook' + fullPath: '/organization/$organizationId/settings/webhook' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/members': { + id: '/_authenticated/organization/$organizationId/settings/members' + path: '/members' + fullPath: '/organization/$organizationId/settings/members' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/labels-annotations': { + id: '/_authenticated/organization/$organizationId/settings/labels-annotations' + path: '/labels-annotations' + fullPath: '/organization/$organizationId/settings/labels-annotations' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/helm-repositories': { + id: '/_authenticated/organization/$organizationId/settings/helm-repositories' + path: '/helm-repositories' + fullPath: '/organization/$organizationId/settings/helm-repositories' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/git-repository-access': { + id: '/_authenticated/organization/$organizationId/settings/git-repository-access' + path: '/git-repository-access' + fullPath: '/organization/$organizationId/settings/git-repository-access' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/general': { + id: '/_authenticated/organization/$organizationId/settings/general' + path: '/general' + fullPath: '/organization/$organizationId/settings/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/danger-zone': { + id: '/_authenticated/organization/$organizationId/settings/danger-zone' + path: '/danger-zone' + fullPath: '/organization/$organizationId/settings/danger-zone' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/container-registries': { + id: '/_authenticated/organization/$organizationId/settings/container-registries' + path: '/container-registries' + fullPath: '/organization/$organizationId/settings/container-registries' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/cloud-credentials': { + id: '/_authenticated/organization/$organizationId/settings/cloud-credentials' + path: '/cloud-credentials' + fullPath: '/organization/$organizationId/settings/cloud-credentials' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/billing-summary': { + id: '/_authenticated/organization/$organizationId/settings/billing-summary' + path: '/billing-summary' + fullPath: '/organization/$organizationId/settings/billing-summary' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/billing-details': { + id: '/_authenticated/organization/$organizationId/settings/billing-details' + path: '/billing-details' + fullPath: '/organization/$organizationId/settings/billing-details' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/api-token': { + id: '/_authenticated/organization/$organizationId/settings/api-token' + path: '/api-token' + fullPath: '/organization/$organizationId/settings/api-token' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsApiTokenRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/ai-copilot': { + id: '/_authenticated/organization/$organizationId/settings/ai-copilot' + path: '/ai-copilot' + fullPath: '/organization/$organizationId/settings/ai-copilot' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/new': { + id: '/_authenticated/organization/$organizationId/cluster/new' + path: '/cluster/new' + fullPath: '/organization/$organizationId/cluster/new' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterNewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/alerts/notification-channel': { + id: '/_authenticated/organization/$organizationId/alerts/notification-channel' + path: '/notification-channel' + fullPath: '/organization/$organizationId/alerts/notification-channel' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRoute + } + '/_authenticated/organization/$organizationId/alerts/issues': { + id: '/_authenticated/organization/$organizationId/alerts/issues' + path: '/issues' + fullPath: '/organization/$organizationId/alerts/issues' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsIssuesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRoute + } + '/_authenticated/organization/$organizationId/alerts/alert-rules': { + id: '/_authenticated/organization/$organizationId/alerts/alert-rules' + path: '/alert-rules' + fullPath: '/organization/$organizationId/alerts/alert-rules' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/roles/': { + id: '/_authenticated/organization/$organizationId/settings/roles/' + path: '/roles' + fullPath: '/organization/$organizationId/settings/roles' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/' + path: '/project/$projectId' + fullPath: '/organization/$organizationId/project/$projectId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/' + path: '/cluster/$clusterId' + fullPath: '/organization/$organizationId/cluster/$clusterId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/variables': { + id: '/_authenticated/organization/$organizationId/project/$projectId/variables' + path: '/project/$projectId/variables' + fullPath: '/organization/$organizationId/project/$projectId/variables' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/overview': { + id: '/_authenticated/organization/$organizationId/project/$projectId/overview' + path: '/project/$projectId/overview' + fullPath: '/organization/$organizationId/project/$projectId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/overview': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/overview' + path: '/cluster/$clusterId/overview' + fullPath: '/organization/$organizationId/cluster/$clusterId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs' + path: '/cluster/$clusterId/cluster-logs' + fullPath: '/organization/$organizationId/cluster/$clusterId/cluster-logs' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell' + path: '/cluster/$clusterId/cloud-shell' + fullPath: '/organization/$organizationId/cluster/$clusterId/cloud-shell' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/settings': { + id: '/_authenticated/organization/$organizationId/project/$projectId/settings' + path: '/project/$projectId/settings' + fullPath: '/organization/$organizationId/project/$projectId/settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug' + path: '/cluster/create/$slug' + fullPath: '/organization/$organizationId/cluster/create/$slug' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings' + path: '/cluster/$clusterId/settings' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/settings/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/settings/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/settings/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/' + path: '/project/$projectId/deployment-rules' + fullPath: '/organization/$organizationId/project/$projectId/deployment-rules' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/' + path: '/' + fullPath: '/organization/$organizationId/cluster/create/$slug/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/' + path: '/' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/settings/roles/edit/$roleId': { + id: '/_authenticated/organization/$organizationId/settings/roles/edit/$roleId' + path: '/roles/edit/$roleId' + fullPath: '/organization/$organizationId/settings/roles/edit/$roleId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/settings/general': { + id: '/_authenticated/organization/$organizationId/project/$projectId/settings/general' + path: '/general' + fullPath: '/organization/$organizationId/project/$projectId/settings/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone': { + id: '/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' + path: '/danger-zone' + fullPath: '/organization/$organizationId/project/$projectId/settings/danger-zone' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create': { + id: '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create' + path: '/project/$projectId/deployment-rules/create' + fullPath: '/organization/$organizationId/project/$projectId/deployment-rules/create' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/summary': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/summary' + path: '/summary' + fullPath: '/organization/$organizationId/cluster/create/$slug/summary' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/resources': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/resources' + path: '/resources' + fullPath: '/organization/$organizationId/cluster/create/$slug/resources' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig' + path: '/kubeconfig' + fullPath: '/organization/$organizationId/cluster/create/$slug/kubeconfig' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/general': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/general' + path: '/general' + fullPath: '/organization/$organizationId/cluster/create/$slug/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/features': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/features' + path: '/features' + fullPath: '/organization/$organizationId/cluster/create/$slug/features' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/create/$slug/eks': { + id: '/_authenticated/organization/$organizationId/cluster/create/$slug/eks' + path: '/eks' + fullPath: '/organization/$organizationId/cluster/create/$slug/eks' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' + path: '/resources' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/resources' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' + path: '/network' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/network' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' + path: '/image-registry' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/image-registry' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' + path: '/general' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' + path: '/eks-anywhere' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' + path: '/danger-zone' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/danger-zone' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' + path: '/credentials' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/credentials' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings': { + id: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' + path: '/advanced-settings' + fullPath: '/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/' + path: '/project/$projectId/environment/$environmentId' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId' + 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' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId': { + id: '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId' + path: '/project/$projectId/deployment-rules/edit/$deploymentRuleId' + fullPath: '/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRouteImport + 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' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' + path: '/project/$projectId/environment/$environmentId/overview' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' + path: '/preview-environments' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' + path: '/general' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' + path: '/deployment-rules' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' + path: '/danger-zone' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new' + path: '/project/$projectId/environment/$environmentId/service/new' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/new' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline' + path: '/pipeline' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/' + path: '/project/$projectId/environment/$environmentId/service/create' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/' + path: '/project/$projectId/environment/$environmentId/service/$serviceId' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/' + path: '/project/$projectId/environment/$environmentId/deployment/$deploymentId' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId' + 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' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/overview' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' + path: '/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' + path: '/project/$projectId/environment/$environmentId/service/create/database' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug' + path: '/project/$projectId/environment/$environmentId/service/create/$slug' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteImport + 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' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/monitoring' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/' + path: '/' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' + path: '/summary' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' + path: '/resources' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' + path: '/general' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' + path: '/variables' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' + path: '/summary' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' + path: '/resources' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports' + path: '/ports' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks' + path: '/health-checks' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general' + path: '/general' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute + } + '/_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' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments' + path: '/values-override-arguments' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables' + path: '/terraform-variables' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' + path: '/terraform-configuration' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' + path: '/terraform-arguments' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' + path: '/storage' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' + path: '/resources' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' + path: '/port' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' + path: '/networking' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' + path: '/health-checks' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' + path: '/general' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' + path: '/domain' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' + path: '/dockerfile' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' + path: '/deployment-restrictions' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' + path: '/danger-zone' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' + path: '/configure' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' + path: '/advanced-settings' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' + path: '/dashboard' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' + path: '/alerts' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + path: '/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' + path: '/$alertId/edit' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute + } + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric': { + id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' + path: '/create/metric/$metric' + fullPath: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute + } + } +} + +interface AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute + AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute + AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute + AuthenticatedOrganizationOrganizationIdAlertsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute: + AuthenticatedOrganizationOrganizationIdAlertsAlertRulesRoute, + AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute: + AuthenticatedOrganizationOrganizationIdAlertsIssuesRoute, + AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute: + AuthenticatedOrganizationOrganizationIdAlertsNotificationChannelRoute, + AuthenticatedOrganizationOrganizationIdAlertsIndexRoute: + AuthenticatedOrganizationOrganizationIdAlertsIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdAlertsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute + AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute + AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute + AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute + AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute + AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute + AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute + AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute + AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute + AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute + AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + AuthenticatedOrganizationOrganizationIdSettingsMembersRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute + AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute + AuthenticatedOrganizationOrganizationIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRoute + AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute + AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute +} + +const AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute: + AuthenticatedOrganizationOrganizationIdSettingsAiCopilotRoute, + AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute: + AuthenticatedOrganizationOrganizationIdSettingsApiTokenRoute, + AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute: + AuthenticatedOrganizationOrganizationIdSettingsBillingDetailsRoute, + AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute: + AuthenticatedOrganizationOrganizationIdSettingsBillingSummaryRoute, + AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute: + AuthenticatedOrganizationOrganizationIdSettingsCloudCredentialsRoute, + AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute: + AuthenticatedOrganizationOrganizationIdSettingsContainerRegistriesRoute, + AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute: + AuthenticatedOrganizationOrganizationIdSettingsDangerZoneRoute, + AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute: + AuthenticatedOrganizationOrganizationIdSettingsGeneralRoute, + AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute: + AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute, + AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute: + AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute, + AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute: + AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute, + AuthenticatedOrganizationOrganizationIdSettingsMembersRoute: + AuthenticatedOrganizationOrganizationIdSettingsMembersRoute, + AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute: + AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute, + AuthenticatedOrganizationOrganizationIdSettingsIndexRoute: + AuthenticatedOrganizationOrganizationIdSettingsIndexRoute, + AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute: + AuthenticatedOrganizationOrganizationIdSettingsRolesIndexRoute, + AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute: + AuthenticatedOrganizationOrganizationIdSettingsRolesEditRoleIdRoute, + } + +const AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsAdvancedSettingsRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsCredentialsRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsDangerZoneRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsEksAnywhereRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsGeneralRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsImageRegistryRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsNetworkRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsResourcesRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute + AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren: AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugEksRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugFeaturesRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugGeneralRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugKubeconfigRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugResourcesRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugSummaryRoute, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsDangerZoneRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsGeneralRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewPipelineRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDeploymentRulesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsGeneralRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsPreviewEnvironmentsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsAlertIdEditRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsCreateMetricMetricRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringAlertsRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringDashboardRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsAdvancedSettingsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsConfigureRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDangerZoneRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDeploymentRestrictionsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDockerfileRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsDomainRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsGeneralRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsHealthChecksRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsNetworkingRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsPortRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsResourcesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsStorageRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformArgumentsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformConfigurationRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsTerraformVariablesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideArgumentsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsValuesOverrideFileRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugGeneralRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugHealthChecksRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugPortsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugResourcesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugSummaryRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugVariablesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute +} + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteChildren: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseGeneralRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseResourcesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseSummaryRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseIndexRoute, + } + +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteChildren, + ) + +interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdAuditLogsRoute: typeof AuthenticatedOrganizationOrganizationIdAuditLogsRoute + AuthenticatedOrganizationOrganizationIdClustersRoute: typeof AuthenticatedOrganizationOrganizationIdClustersRoute + AuthenticatedOrganizationOrganizationIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdOverviewRoute + AuthenticatedOrganizationOrganizationIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdIndexRoute + AuthenticatedOrganizationOrganizationIdClusterNewRoute: typeof AuthenticatedOrganizationOrganizationIdClusterNewRoute + AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute: typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute + AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute +} + +const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOrganizationOrganizationIdRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdAlertsRouteRoute: + AuthenticatedOrganizationOrganizationIdAlertsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute: + AuthenticatedOrganizationOrganizationIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdAuditLogsRoute: + AuthenticatedOrganizationOrganizationIdAuditLogsRoute, + AuthenticatedOrganizationOrganizationIdClustersRoute: + AuthenticatedOrganizationOrganizationIdClustersRoute, + AuthenticatedOrganizationOrganizationIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdIndexRoute: + AuthenticatedOrganizationOrganizationIdIndexRoute, + AuthenticatedOrganizationOrganizationIdClusterNewRoute: + AuthenticatedOrganizationOrganizationIdClusterNewRoute, + AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute: + AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRoute: + AuthenticatedOrganizationOrganizationIdClusterCreateSlugRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdCloudShellRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdClusterLogsRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdVariablesRoute, + AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute: + AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesCreateRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdDeploymentRulesEditDeploymentRuleIdRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceNewRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdMonitoringRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateSlugRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateDatabaseRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdPreCheckLogsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdCloudShellRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdOverviewRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdServiceLogsRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdVariablesRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentDeploymentIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceCreateIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsIndexRoute, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdServiceServiceIdDeploymentsLogsExecutionIdRoute, + } + +const AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren = + AuthenticatedOrganizationOrganizationIdRouteRoute._addFileChildren( + AuthenticatedOrganizationOrganizationIdRouteRouteChildren, + ) + +interface AuthenticatedOrganizationRouteRouteChildren { + AuthenticatedOrganizationOrganizationIdRouteRoute: typeof AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren + AuthenticatedOrganizationIndexRoute: typeof AuthenticatedOrganizationIndexRoute +} + +const AuthenticatedOrganizationRouteRouteChildren: AuthenticatedOrganizationRouteRouteChildren = + { + AuthenticatedOrganizationOrganizationIdRouteRoute: + AuthenticatedOrganizationOrganizationIdRouteRouteWithChildren, + AuthenticatedOrganizationIndexRoute: AuthenticatedOrganizationIndexRoute, + } + +const AuthenticatedOrganizationRouteRouteWithChildren = + AuthenticatedOrganizationRouteRoute._addFileChildren( + AuthenticatedOrganizationRouteRouteChildren, + ) + +interface AuthenticatedRouteChildren { + AuthenticatedOrganizationRouteRoute: typeof AuthenticatedOrganizationRouteRouteWithChildren + AuthenticatedOnboardingPersonalizeRoute: typeof AuthenticatedOnboardingPersonalizeRoute + AuthenticatedOnboardingPlansRoute: typeof AuthenticatedOnboardingPlansRoute + AuthenticatedOnboardingProjectRoute: typeof AuthenticatedOnboardingProjectRoute + AuthenticatedAcceptInvitationIndexRoute: typeof AuthenticatedAcceptInvitationIndexRoute +} + +const AuthenticatedRouteChildren: AuthenticatedRouteChildren = { + AuthenticatedOrganizationRouteRoute: + AuthenticatedOrganizationRouteRouteWithChildren, + AuthenticatedOnboardingPersonalizeRoute: + AuthenticatedOnboardingPersonalizeRoute, + AuthenticatedOnboardingPlansRoute: AuthenticatedOnboardingPlansRoute, + AuthenticatedOnboardingProjectRoute: AuthenticatedOnboardingProjectRoute, + AuthenticatedAcceptInvitationIndexRoute: + AuthenticatedAcceptInvitationIndexRoute, +} + +const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren( + AuthenticatedRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AuthenticatedRoute: AuthenticatedRouteWithChildren, + LoginAuth0CallbackRoute: LoginAuth0CallbackRoute, + LoginIndexRoute: LoginIndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/apps/console-v5/src/router-types.d.ts b/apps/console-v5/src/router-types.d.ts new file mode 100644 index 00000000000..67aa5570e0a --- /dev/null +++ b/apps/console-v5/src/router-types.d.ts @@ -0,0 +1,8 @@ +import { type createRouter } from '@tanstack/react-router' +import { type routeTree } from './routeTree.gen' + +declare module '@tanstack/react-router' { + interface Register { + router: ReturnType> + } +} diff --git a/apps/console-v5/src/routes/__root.tsx b/apps/console-v5/src/routes/__root.tsx new file mode 100644 index 00000000000..a03381a75a4 --- /dev/null +++ b/apps/console-v5/src/routes/__root.tsx @@ -0,0 +1,20 @@ +import { type QueryClient } from '@tanstack/react-query' +import { Outlet, createRootRouteWithContext } from '@tanstack/react-router' +import { ModalProvider, ToastBehavior } from '@qovery/shared/ui' +import { type Auth0ContextType } from '../auth/auth0' + +interface RouterContext { + auth: Auth0ContextType + queryClient: QueryClient +} + +const RootLayout = () => { + return ( + + + + + ) +} + +export const Route = createRootRouteWithContext()({ component: RootLayout }) diff --git a/apps/console-v5/src/routes/_authenticated.tsx b/apps/console-v5/src/routes/_authenticated.tsx new file mode 100644 index 00000000000..0eee2970924 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated.tsx @@ -0,0 +1,12 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated')({ + beforeLoad: ({ context, location }) => { + if (!context.auth.isAuthenticated && !context.auth.isLoading) { + // Pass current pathname as returnTo so Auth0 can restore it after login + context.auth.login(location.href) + return + } + }, + component: () => , +}) diff --git a/apps/console-v5/src/routes/_authenticated/accept-invitation/index.tsx b/apps/console-v5/src/routes/_authenticated/accept-invitation/index.tsx new file mode 100644 index 00000000000..f788fea0254 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/accept-invitation/index.tsx @@ -0,0 +1,28 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { useEffect } from 'react' +import { AcceptInvitation, useInviteMember } from '@qovery/domains/onboarding/feature' + +export const Route = createFileRoute('/_authenticated/accept-invitation/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { acceptInvitation, displayInvitation, checkTokenInStorage } = useInviteMember() + const navigate = useNavigate() + + useEffect(() => { + checkTokenInStorage() + }, [checkTokenInStorage]) + + const onSubmit = async () => { + await acceptInvitation() + } + + useEffect(() => { + if (displayInvitation === false) { + navigate({ to: '/login' }) + } + }, [displayInvitation, navigate]) + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/onboarding/personalize.tsx b/apps/console-v5/src/routes/_authenticated/onboarding/personalize.tsx new file mode 100644 index 00000000000..519b048c1c2 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/onboarding/personalize.tsx @@ -0,0 +1,160 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { Navigate, createFileRoute, useNavigate } from '@tanstack/react-router' +import posthog from 'posthog-js' +import { TypeOfUseEnum } from 'qovery-typescript-axios' +import { FormProvider, useForm } from 'react-hook-form' +import { Container, StepPersonalize } from '@qovery/domains/onboarding/feature' +import { useCreateUserSignUp, useUserSignUp } from '@qovery/domains/users-sign-up/feature' +import { useAuth } from '@qovery/shared/auth' +import { IconEnum } from '@qovery/shared/enums' +import { Icon } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/onboarding/personalize')({ + component: Personalize, +}) + +const dataCloudProviders = [ + { + label: 'Amazon Web Service (AWS)', + value: 'AWS', + icon: , + }, + { + label: 'Google Cloud Patform (GCP)', + value: 'GCP', + icon: , + }, + { + label: 'Scaleway', + value: 'SCW', + icon: , + }, + { + label: 'Azure', + value: 'AZURE', + icon: , + }, + { + label: 'Other', + value: 'OTHER', + icon: , + }, +] + +const dataQoveryUsage = [ + { + label: 'Spin up testing/dev/QA environments', + value: 'i-want-to-easily-spin-up-testing-dev-qa-environments', + }, + { + label: 'Simplify my deployment pipeline', + value: 'i-want-to-simplify-my-deployment-pipeline', + }, + { + label: 'Automate my deployment pipeline', + value: 'i-want-to-automate-my-deployment-pipeline', + }, + { + label: 'Deploy my new project', + value: 'i-want-to-easily-deploy-my-new-project', + }, + { + label: 'Migrate my apps from Heroku', + value: 'i-want-to-easily-migrate-my-apps-from-heroku', + }, + { + label: 'Find a better alternative to Heroku', + value: 'i-want-to-find-a-better-alternative-to-heroku', + }, + { + label: 'Spin up and manage my Kubernetes cluster', + value: 'i-want-to-easily-spin-up-and-manage-my-kubernetes-cluster', + }, + { + label: 'Deploy my apps on my Kubernetes cluster', + value: 'i-want-to-easily-deploy-my-apps-on-my-kubernetes-cluster', + }, + { + label: 'Other', + value: 'other', + }, +] + +function Personalize() { + useDocumentTitle('Onboarding Personalize - Qovery') + const { isAuthenticated } = useAuth0() + const navigate = useNavigate() + const { user } = useAuth0() + const { authLogout } = useAuth() + const { data: userSignUp } = useUserSignUp() + const { mutateAsync: createUserSignUp } = useCreateUserSignUp() + + const methods = useForm<{ + first_name: string + last_name: string + user_email: string + company_name?: string + qovery_usage: string + qovery_usage_other?: string + type_of_use: TypeOfUseEnum + infrastructure_hosting: string + phone: string + }>({ + mode: 'onChange', + defaultValues: { + first_name: userSignUp?.first_name ? userSignUp.first_name : user?.name?.split(' ')[0], + last_name: userSignUp?.last_name ? userSignUp.last_name : user?.name?.split(' ')[1], + user_email: userSignUp?.user_email ? userSignUp.user_email : user?.email, + company_name: userSignUp?.company_name ?? '', + qovery_usage: userSignUp?.qovery_usage ?? undefined, + qovery_usage_other: userSignUp?.qovery_usage_other ?? undefined, + type_of_use: userSignUp?.type_of_use ?? TypeOfUseEnum.WORK, + infrastructure_hosting: userSignUp?.infrastructure_hosting ?? 'AWS', + phone: '', + }, + }) + + const onSubmit = methods.handleSubmit(async (data) => { + if (!data) return + + const normalizedData = { ...data } + + if (normalizedData.qovery_usage !== 'other') { + delete normalizedData.qovery_usage_other + } + + try { + await createUserSignUp({ + ...userSignUp, + ...normalizedData, + }) + + posthog.capture('onboarding-tailor-experience-completed', { + qovery_usage: normalizedData.qovery_usage, + infrastructure_hosting: normalizedData.infrastructure_hosting, + }) + + navigate({ to: `/onboarding/project` }) + } catch (error) { + console.error(error) + } + }) + + if (!isAuthenticated) { + return + } + + return ( + + + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/onboarding/plans.tsx b/apps/console-v5/src/routes/_authenticated/onboarding/plans.tsx new file mode 100644 index 00000000000..59a588e0604 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/onboarding/plans.tsx @@ -0,0 +1,21 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { Navigate, createFileRoute } from '@tanstack/react-router' +import { Container, OnboardingPlans } from '@qovery/domains/onboarding/feature' + +export const Route = createFileRoute('/_authenticated/onboarding/plans')({ + component: Plans, +}) + +function Plans() { + const { isAuthenticated } = useAuth0() + + if (!isAuthenticated) { + return + } + + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/onboarding/project.tsx b/apps/console-v5/src/routes/_authenticated/onboarding/project.tsx new file mode 100644 index 00000000000..22b1719cf20 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/onboarding/project.tsx @@ -0,0 +1,21 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { Navigate, createFileRoute } from '@tanstack/react-router' +import { Container, OnboardingProject } from '@qovery/domains/onboarding/feature' + +export const Route = createFileRoute('/_authenticated/onboarding/project')({ + component: Project, +}) + +function Project() { + const { isAuthenticated } = useAuth0() + + if (!isAuthenticated) { + return + } + + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/$clusterId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/$clusterId/index.tsx new file mode 100644 index 00000000000..fa6ce968b9f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/$clusterId/index.tsx @@ -0,0 +1,17 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/$clusterId/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, clusterId } = useParams({ strict: false }) + + if (!organizationId || !clusterId) { + return null + } + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/alert-rules.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/alert-rules.tsx new file mode 100644 index 00000000000..4240efaac42 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/alert-rules.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { OrganizationAlerting } from '@qovery/domains/observability/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/alerts/alert-rules')({ + component: AlertRulesComponent, +}) + +function AlertRulesComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/index.tsx new file mode 100644 index 00000000000..506dfcf0026 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/index.tsx @@ -0,0 +1,15 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/alerts/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId } = useParams({ strict: false }) + + if (!organizationId) { + return null + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/issues.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/issues.tsx new file mode 100644 index 00000000000..058d6fcea1e --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/issues.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { IssueOverview } from '@qovery/domains/observability/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/alerts/issues')({ + component: IssuesComponent, +}) + +function IssuesComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/notification-channel.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/notification-channel.tsx new file mode 100644 index 00000000000..4cac251da40 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/notification-channel.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { NotificationChannelOverview } from '@qovery/domains/observability/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/alerts/notification-channel')({ + component: NotificationChannelComponent, +}) + +function NotificationChannelComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/route.tsx new file mode 100644 index 00000000000..776d0e11e6e --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/alerts/route.tsx @@ -0,0 +1,49 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { useAlerts } from '@qovery/domains/observability/feature' +import { ErrorBoundary, Sidebar } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/alerts')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '' } = useParams({ strict: false }) + useDocumentTitle('Alerting') + + const { data: alerts = [] } = useAlerts({ organizationId }) + const issuesCount = alerts.length + const pathAlerts = `/organization/${organizationId}/alerts` + + return ( +
+ +
+
+ + + +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/audit-logs.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/audit-logs.tsx new file mode 100644 index 00000000000..ce9c6f2194b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/audit-logs.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { AuditLogsView } from '@qovery/domains/audit-logs/feature' +import { auditLogsParamsSchema } from '@qovery/shared/router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/audit-logs')({ + component: RouteComponent, + validateSearch: auditLogsParamsSchema, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell.tsx new file mode 100644 index 00000000000..a61d1037342 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell.tsx @@ -0,0 +1,30 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ClusterTerminal, useCluster } from '@qovery/domains/clusters/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = Route.useParams() + useDocumentTitle('Cluster - Cloud shell') + + const { data: cluster } = useCluster({ + organizationId, + clusterId, + suspense: true, + }) + + if (!cluster) { + return null + } + + return ( +
+
+ +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs.tsx new file mode 100644 index 00000000000..610995abc27 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs.tsx @@ -0,0 +1,72 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { useMemo, useRef } from 'react' +import { + ClusterHeaderLogs, + ClusterLogsList, + useCluster, + useClusterLogs, + useClusterStatus, +} from '@qovery/domains/clusters/feature' +import { EmptyState, LoaderDots } from '@qovery/shared/ui' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + + const { + data: logs = [], + isLoading: isLogsLoading, + isFetched: isLogsFetched, + } = useClusterLogs({ + organizationId, + clusterId, + refetchInterval: 3000, + }) + const { data: cluster } = useCluster({ organizationId, clusterId }) + const { data: clusterStatus } = useClusterStatus({ organizationId, clusterId }) + + const refScrollSection = useRef(null) + const firstLogTimestamp = logs[0]?.timestamp + const firstDate = useMemo(() => (firstLogTimestamp ? new Date(firstLogTimestamp) : undefined), [firstLogTimestamp]) + + if (!cluster || !clusterStatus) { + return null + } + + return ( +
+ {isLogsLoading && !isLogsFetched ? ( +
+
+ +

Cluster logs are loading…

+
+
+ ) : isLogsFetched && logs.length > 0 ? ( + <> +
+ +
+ + + ) : ( +
+ +
+ )} +
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/index.tsx new file mode 100644 index 00000000000..e8b029defbd --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/index.tsx @@ -0,0 +1,21 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, clusterId } = useParams({ strict: false }) + + if (!organizationId || !clusterId) { + return null + } + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/overview.tsx new file mode 100644 index 00000000000..e78c75be6f5 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/overview.tsx @@ -0,0 +1,255 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { ClusterDeploymentStatusEnum } from 'qovery-typescript-axios' +import { + ClusterCardNodeUsage, + ClusterTableNode, + ClusterTableNodepool, + useClusterMetrics, + useClusterMetricsSocket, +} from '@qovery/domains/cluster-metrics/feature' +import { ClusterCardResources } from '@qovery/domains/cluster-metrics/feature' +import { ClusterCardSetup } from '@qovery/domains/cluster-metrics/feature' +import { + ClusterActions, + ClusterAvatar, + ClusterNeedRedeployFlag, + ClusterType, + hasGpuInstance, + useCluster, + useClusterRunningStatus, + useClusterRunningStatusSocket, + useClusterStatus, + useDeployCluster, +} from '@qovery/domains/clusters/feature' +import { IconEnum } from '@qovery/shared/enums' +import { Badge, ErrorBoundary, Heading, Icon, Section, Skeleton, Tooltip } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/overview')({ + component: RouteComponent, +}) + +function TableSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+ +
+ {[1, 2, 3, 4, 5, 6].map((index) => ( +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ ))} +
+
+ ) +} + +function TableLegend() { + return ( +
+ + + Reserved + + + + + + +
+ ) +} + +function ClusterOverview({ organizationId, clusterId }: { organizationId: string; clusterId: string }) { + const { data: cluster, isLoading: isClusterLoading } = useCluster({ organizationId, clusterId }) + const { mutate: deployCluster } = useDeployCluster() + const { data: runningStatus } = useClusterRunningStatus({ organizationId, clusterId }) + const { data: clusterMetrics } = useClusterMetrics({ organizationId, clusterId }) + const { data: clusterStatus, isLoading: isClusterStatusLoading } = useClusterStatus({ + organizationId, + clusterId, + refetchInterval: 4_000, + enabled: Boolean(organizationId) && Boolean(clusterId), + }) + + const isLoading = isClusterLoading || isClusterStatusLoading || !runningStatus || !clusterMetrics + + const isKarpenter = cluster?.features?.find((feature) => feature.id === 'KARPENTER') + + return ( + <> + {cluster && cluster.deployment_status !== ClusterDeploymentStatusEnum.UP_TO_DATE && ( + + deployCluster({ + organizationId, + clusterId, + }) + } + /> + )} +
+
+
+
+ + + + + {cluster?.name} + +
+
+
+ {cluster?.production && ( + + Production + + )} + {cluster?.is_default && Default} + {cluster ? ( + cluster.kubernetes === 'SELF_MANAGED' ? ( + + + Self managed + + ) : ( + <> + + + Qovery managed + + + + ) + ) : ( + + )} + {cluster?.region !== 'on-premise' && cluster?.kubernetes !== 'PARTIALLY_MANAGED' && ( + + + {cluster?.region} + + + )} + {cluster?.kubernetes !== 'SELF_MANAGED' && ( + <> + + {cluster?.version && ( + + {cluster?.version} + + )} + + {cluster?.instance_type && + cluster.kubernetes !== 'PARTIALLY_MANAGED' && + cluster?.instance_type !== 'KARPENTER' && ( + + + {cluster?.instance_type?.toLowerCase().replace('_', '.')} + + + )} + + )} + {hasGpuInstance(cluster) && ( + + GPU pool + + )} +
+
+ + {cluster && clusterStatus ? ( + + ) : ( +
+ )} + +
+
+
+
+ {typeof runningStatus === 'string' ? ( +
+
+ + No metrics available because the running status is unavailable. +
+
+ ) : ( + <> +
+ + + +
+ {isLoading ? ( + + ) : isKarpenter ? ( +
+ + +
+ ) : ( +
+ +
+ +
+
+ )} + + )} +
+ + ) +} + +function RouteComponent() { + useDocumentTitle('Cluster - Overview') + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + + useClusterRunningStatusSocket({ organizationId, clusterId }) + useClusterMetricsSocket({ organizationId, clusterId }) + + if (!organizationId || !clusterId) { + return null + } + + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings.tsx new file mode 100644 index 00000000000..0dcdf9837ad --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings.tsx @@ -0,0 +1,27 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ClusterAdvancedSettingsFeature } from '@qovery/domains/clusters/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/advanced-settings' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Advanced settings - Cluster settings') + + return ( +
+
+ + +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials.tsx new file mode 100644 index 00000000000..6c597b688ed --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials.tsx @@ -0,0 +1,112 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { + type ClusterCloudProviderInfo, + type ClusterCloudProviderInfoRequest, + type ClusterCredentials, +} from 'qovery-typescript-axios' +import { useEffect } from 'react' +import { type FieldValues, FormProvider, useForm } from 'react-hook-form' +import { useCloudProviderCredentials } from '@qovery/domains/cloud-providers/feature' +import { + ClusterCredentialsSettings, + useClusterCloudProviderInfo, + useEditCloudProviderInfo, +} from '@qovery/domains/clusters/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { ToastEnum, toast } from '@qovery/shared/toast' +import { BlockContent, Button, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/credentials' +)({ + component: RouteComponent, +}) + +const handleSubmit = ( + data: FieldValues, + credentials: ClusterCredentials[], + cluster: ClusterCloudProviderInfo +): ClusterCloudProviderInfoRequest => { + const currentCredentials = credentials.filter((item) => item.id === data['credentials'])[0] + + return { + cloud_provider: cluster.cloud_provider, + credentials: { + id: currentCredentials.id, + name: currentCredentials.name, + }, + region: cluster.region, + } +} + +function ClusterCredentialsSettingsForm() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + + const methods = useForm({ + mode: 'onChange', + }) + + const { data: clusterCloudProviderInfo } = useClusterCloudProviderInfo({ + organizationId, + clusterId, + }) + const { data: credentials = [] } = useCloudProviderCredentials({ + organizationId, + cloudProvider: clusterCloudProviderInfo?.cloud_provider, + }) + const { mutateAsync: editCloudProviderInfo, isLoading: isEditCloudProviderInfoLoading } = useEditCloudProviderInfo() + + const onSubmit = methods.handleSubmit((data) => { + const findCredentials = credentials.find((credential) => credential.id === data['credentials']) + + if (data && clusterCloudProviderInfo && findCredentials) { + const clusterCloudProviderInfoRequest = handleSubmit(data, credentials, clusterCloudProviderInfo) + + editCloudProviderInfo({ + organizationId, + clusterId, + cloudProviderInfoRequest: clusterCloudProviderInfoRequest, + }) + } else { + toast(ToastEnum.ERROR, 'Please select a credential') + } + }) + + useEffect(() => { + if (clusterCloudProviderInfo) { + methods.setValue('credentials', clusterCloudProviderInfo.credentials?.id) + } + }, [methods, clusterCloudProviderInfo]) + + return ( + +
+
+ +
+
+ + + +
+ +
+
+
+
+
+
+ ) +} + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone.tsx new file mode 100644 index 00000000000..1807140c39e --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone.tsx @@ -0,0 +1,39 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { type Cluster } from 'qovery-typescript-axios' +import { ClusterDeleteModal, useCluster } from '@qovery/domains/clusters/feature' +import { BlockContentDelete, useModal } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/danger-zone' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + + const { data: cluster } = useCluster({ organizationId, clusterId }) + const { openModal } = useModal() + + const deleteCluster = (cluster: Cluster) => { + openModal({ + content: , + }) + } + + if (!cluster) { + return null + } + + return ( +
+
+ deleteCluster(cluster)} + /> +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere.tsx new file mode 100644 index 00000000000..72a0a5666ff --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere.tsx @@ -0,0 +1,124 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { useEffect, useState } from 'react' +import { FormProvider, useForm } from 'react-hook-form' +import { + ClusterEksSettings, + type ClusterEksSettingsFormData, + getEksAnywhereGitFormValues, + getInfrastructureChartsParametersWithEksAnywhereGit, + stripEksAnywhereGitFormFields, + useCluster, + useEditCluster, +} from '@qovery/domains/clusters/feature' +import { GitRepositorySettings } from '@qovery/domains/organizations/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Button, LoaderSpinner, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/eks-anywhere' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + const { data: cluster, isLoading: isClusterLoading } = useCluster({ organizationId, clusterId }) + const { mutateAsync: editCluster, isLoading: isEditClusterLoading } = useEditCluster() + const [gitDisabled, setGitDisabled] = useState(true) + const currentGitRepository = cluster?.infrastructure_charts_parameters?.eks_anywhere_parameters?.git_repository + const currentRepository = getEksAnywhereGitFormValues(cluster).repository + + const methods = useForm({ + mode: 'onChange', + defaultValues: { + ...cluster, + ...getEksAnywhereGitFormValues(cluster), + }, + }) + + const onSubmit = methods.handleSubmit(async (data) => { + if (data && cluster) { + try { + await editCluster({ + organizationId, + clusterId: cluster.id, + clusterRequest: { + ...cluster, + ...stripEksAnywhereGitFormFields(data), + infrastructure_charts_parameters: getInfrastructureChartsParametersWithEksAnywhereGit(data), + }, + }) + } catch (error) { + console.error(error) + } + } + }) + + const editGitSettings = () => { + setGitDisabled(false) + methods.setValue('provider', currentGitRepository?.provider) + methods.setValue('repository', undefined) + } + + useEffect(() => { + if (cluster && !isClusterLoading) { + methods.reset({ + ...cluster, + ...getEksAnywhereGitFormValues(cluster), + }) + setGitDisabled(true) + } + }, [cluster, isClusterLoading, methods, currentGitRepository?.provider]) + + if (isClusterLoading) { + return ( +
+
+
+ +
+
+
+ ) + } + + return ( + +
+
+ +
+
+ + } + /> +
+ +
+
+
+
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general.tsx new file mode 100644 index 00000000000..72be01bd3cf --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general.tsx @@ -0,0 +1,140 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { type Cluster } from 'qovery-typescript-axios' +import { type FieldValues, FormProvider, useForm } from 'react-hook-form' +import { ClusterGeneralSettings, useCluster, useEditCluster } from '@qovery/domains/clusters/feature' +import { LabelSetting } from '@qovery/domains/organizations/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { useUserRole } from '@qovery/shared/iam/feature' +import { BlockContent, Button, Callout, ExternalLink, Heading, Icon, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/general' +)({ + component: RouteComponent, +}) + +const handleSubmit = (data: FieldValues, cluster: Cluster) => { + return { + ...cluster, + name: data['name'], + description: data['description'] || '', + production: data['production'], + } +} + +function ClusterGeneralSettingsForm({ cluster }: { cluster: Cluster }) { + const { organizationId = '' } = useParams({ strict: false }) + const { mutateAsync: editCluster, isLoading: isEditClusterLoading } = useEditCluster() + const { isQoveryAdminUser } = useUserRole() + + const methods = useForm({ + mode: 'onChange', + defaultValues: cluster, + }) + + const onSubmit = methods.handleSubmit((data) => { + if (data) { + const cloneCluster = handleSubmit(data, cluster) + + if (isQoveryAdminUser) { + if (data.metrics_parameters?.enabled) { + cloneCluster.metrics_parameters = { + enabled: data.metrics_parameters?.enabled ?? false, + configuration: { + kind: 'MANAGED_BY_QOVERY', + resource_profile: cloneCluster.metrics_parameters?.configuration?.resource_profile, + cloud_watch_export_config: { + ...cloneCluster.metrics_parameters?.configuration?.cloud_watch_export_config, + enabled: data.metrics_parameters?.configuration?.cloud_watch_export_config?.enabled ?? false, + }, + high_availability: cloneCluster.metrics_parameters?.configuration?.high_availability, + internal_network_monitoring: cloneCluster.metrics_parameters?.configuration?.internal_network_monitoring, + alerting: { + ...cloneCluster.metrics_parameters?.configuration?.alerting, + enabled: data.metrics_parameters?.configuration?.alerting?.enabled ?? false, + }, + }, + } + } else { + cloneCluster.metrics_parameters = { + enabled: false, + } + } + } + + editCluster({ + organizationId, + clusterId: cluster.id, + clusterRequest: cloneCluster, + }) + } + }) + + return ( + +
+
+ +
+ {cluster.cloud_provider !== 'ON_PREMISE' && ( + + + + + + Qovery manages this resource for you + + Use exclusively the Qovery console to update the resources managed by Qovery on your cloud account. +
Do not manually update or upgrade them on the cloud provider console, otherwise you will risk + a drift in the configuration. +
+ + Click here for more details + +
+
+
+ )} +
+ + + + {cluster.cloud_provider === 'AWS' && ( +
+ Extra tags + +
+ )} +
+ +
+
+
+
+
+
+ ) +} + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + const { data: cluster } = useCluster({ organizationId, clusterId }) + + if (!cluster) { + return null + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry.tsx new file mode 100644 index 00000000000..b4d884e0ba4 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry.tsx @@ -0,0 +1,98 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { type ContainerRegistryRequest } from 'qovery-typescript-axios' +import { FormProvider, useForm } from 'react-hook-form' +import { useCluster } from '@qovery/domains/clusters/feature' +import { + ContainerRegistryForm, + useContainerRegistries, + useEditContainerRegistry, +} from '@qovery/domains/organizations/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Button, Callout, Icon, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/image-registry' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + const { data: containerRegistries } = useContainerRegistries({ + organizationId, + }) + const containerRegistry = containerRegistries?.find((registry) => registry.cluster?.id === clusterId) + const { mutate: editContainerRegistry, isLoading: isLoadingEditContainerRegistry } = useEditContainerRegistry() + const { data: cluster } = useCluster({ organizationId, clusterId }) + + const methods = useForm({ + mode: 'onChange', + defaultValues: { + type: containerRegistry?.config?.role_arn ? 'STS' : 'STATIC', + ...containerRegistry, + }, + }) + + if (!containerRegistry) { + return null + } + + const onSubmit = methods.handleSubmit((containerRegistryRequest) => { + const { type, ...rest } = containerRegistryRequest + editContainerRegistry({ + organizationId: organizationId, + containerRegistryId: containerRegistry.id, + containerRegistryRequest: { + ...rest, + config: + type === 'STS' + ? { + role_arn: containerRegistryRequest.config?.role_arn, + region: containerRegistryRequest.config?.region, + } + : { + role_arn: undefined, + ...containerRegistryRequest.config, + }, + }, + }) + }) + + return ( +
+
+ +
+ +
+ + {(methods.formState.dirtyFields.kind || methods.formState.dirtyFields.url) && ( + + + + + + You will have to delete any image stored in the previous container registry manually + + + )} +
+ +
+ +
+
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index.tsx new file mode 100644 index 00000000000..a77bb86962f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/index.tsx @@ -0,0 +1,21 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/settings/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, clusterId } = useParams({ strict: false }) + + if (!organizationId || !clusterId) { + return null + } + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network.tsx new file mode 100644 index 00000000000..7526c65db30 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network.tsx @@ -0,0 +1,28 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { ClusterNetworkSettings } from '@qovery/domains/clusters/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/network' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + + return ( +
+
+ +
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources.tsx new file mode 100644 index 00000000000..7e546b8f9af --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources.tsx @@ -0,0 +1,210 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { + type Cluster, + type ClusterFeatureKarpenterParametersResponse, + type ClusterFeatureStringResponse, + type ClusterRequestFeaturesInner, +} from 'qovery-typescript-axios' +import { type FieldValues, FormProvider, useForm } from 'react-hook-form' +import { SCW_CONTROL_PLANE_FEATURE_ID } from '@qovery/domains/cloud-providers/feature' +import { + ClusterMigrationModal, + ClusterResourcesSettings, + useCluster, + useEditCluster, + useUpdateKarpenterPrivateFargate, +} from '@qovery/domains/clusters/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { type ClusterResourcesEdit, type SCWControlPlaneFeatureType } from '@qovery/shared/interfaces' +import { Button, Section, useModal } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings/resources' +)({ + component: RouteComponent, +}) + +function getValueByKey(key: string, data: { [key: string]: string }[] = []): string[] { + return data.filter((obj) => key in obj).map((obj) => obj[key]) +} + +const handleSubmit = (data: FieldValues, cluster: Cluster): Cluster => { + const payload = { + ...cluster, + max_running_nodes: data['nodes'][1], + min_running_nodes: data['nodes'][0], + disk_size: data['disk_size'], + instance_type: data['instance_type'], + } + + const hasKarpenterFeature = cluster.features?.some((f) => f.id === 'KARPENTER') + + if (data['karpenter']?.enabled && !hasKarpenterFeature) { + payload.features = [ + ...(cluster.features || []), + { + id: 'KARPENTER', + value: { + spot_enabled: data['karpenter'].spot_enabled ?? false, + disk_size_in_gib: data['karpenter'].disk_size_in_gib, + default_service_architecture: data['karpenter'].default_service_architecture, + qovery_node_pools: data['karpenter'].qovery_node_pools, + }, + } as ClusterRequestFeaturesInner, + ] + } else { + payload.features = cluster.features?.map((feature) => { + if (feature.id === 'KARPENTER') { + return { + ...feature, + value: { + spot_enabled: data['karpenter'].spot_enabled ?? false, + disk_size_in_gib: data['karpenter'].disk_size_in_gib, + default_service_architecture: data['karpenter'].default_service_architecture, + qovery_node_pools: data['karpenter'].qovery_node_pools, + }, + } + } + + return feature + }) + } + + if (cluster.cloud_provider === 'SCW') { + payload.features = cluster.features?.map((feature) => { + if (feature.id === SCW_CONTROL_PLANE_FEATURE_ID) { + return { + ...feature, + value: data['scw_control_plane'], + } + } + + return feature + }) + } + + return payload +} + +function ClusterResourcesSettingsForm({ cluster }: { cluster: Cluster }) { + const { organizationId, clusterId } = useParams({ strict: false }) + const karpenterFeature = cluster.features?.find( + (feature) => feature.id === 'KARPENTER' + ) as ClusterFeatureKarpenterParametersResponse + const scwFeature = cluster.features?.find( + (feature) => feature.id === SCW_CONTROL_PLANE_FEATURE_ID + ) as ClusterFeatureStringResponse + + const { openModal, closeModal } = useModal() + + const methods = useForm({ + mode: 'onChange', + defaultValues: { + cluster_type: cluster.kubernetes, + instance_type: cluster.instance_type, + nodes: [cluster.min_running_nodes || 1, cluster.max_running_nodes || 1], + disk_size: cluster.disk_size || 0, + karpenter: karpenterFeature + ? { + enabled: true, + spot_enabled: karpenterFeature.value.spot_enabled, + disk_size_in_gib: karpenterFeature.value.disk_size_in_gib, + default_service_architecture: karpenterFeature.value.default_service_architecture, + qovery_node_pools: karpenterFeature.value.qovery_node_pools, + } + : { + enabled: false, + }, + scw_control_plane: scwFeature ? (scwFeature.value as SCWControlPlaneFeatureType) : undefined, + }, + }) + const { mutate: editCluster, isLoading: isEditClusterLoading } = useEditCluster() + const { mutateAsync: updateKarpenterPrivateFargate } = useUpdateKarpenterPrivateFargate() + + const onSubmit = methods.handleSubmit(async (data) => { + const updateCluster = async () => { + const cloneCluster = handleSubmit(data, cluster) + editCluster({ + clusterId: cluster.id, + organizationId: organizationId || '', + clusterRequest: cloneCluster, + }) + } + + const updateClusterKarpenterSubnets = async () => { + if (data?.aws_existing_vpc?.eks_subnets) { + try { + await updateKarpenterPrivateFargate({ + organizationId: organizationId || '', + clusterId: clusterId || '', + clusterKarpenterPrivateSubnetIdsPutRequest: { + eks_karpenter_fargate_subnets_zone_a_ids: getValueByKey('A', data.aws_existing_vpc.eks_subnets), + eks_karpenter_fargate_subnets_zone_b_ids: getValueByKey('B', data.aws_existing_vpc.eks_subnets), + eks_karpenter_fargate_subnets_zone_c_ids: getValueByKey('C', data.aws_existing_vpc.eks_subnets), + }, + }) + await updateCluster() + } catch (error) { + console.error(error) + } + } else { + await updateCluster() + } + } + + if (data && cluster) { + const hasKarpenterFeature = cluster.features?.some((f) => f.id === 'KARPENTER') + if (data.karpenter?.enabled === !hasKarpenterFeature) { + openModal({ + content: , + }) + } else { + await updateClusterKarpenterSubnets() + } + } + }) + + const hasAlreadyKarpenter = cluster.features?.some((f) => f.id === 'KARPENTER') + + return ( + +
+
+ +
+ +
+ +
+ +
+
+
+ ) +} + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + const { data: cluster } = useCluster({ organizationId, clusterId }) + + if (!cluster) { + return null + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/route.tsx new file mode 100644 index 00000000000..5f1971e5b7f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/$clusterId/settings/route.tsx @@ -0,0 +1,142 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import clsx from 'clsx' +import { useFeatureFlagEnabled } from 'posthog-js/react' +import { match } from 'ts-pattern' +import { useCluster } from '@qovery/domains/clusters/feature' +import { Sidebar } from '@qovery/shared/ui' +import { twMerge } from '@qovery/shared/util-js' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/$clusterId/settings')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', clusterId = '' } = useParams({ strict: false }) + const { data: cluster } = useCluster({ organizationId, clusterId }) + const isEksAnywhereEnabled = useFeatureFlagEnabled('eks-anywhere') + + const pathSettings = `/organization/${organizationId}/cluster/${clusterId}/settings` + + const generalLink = { + title: 'General', + to: `${pathSettings}/general`, + icon: 'gear' as const, + } + + const eksLink = { + title: 'EKS Anywhere configuration', + to: `${pathSettings}/eks-anywhere`, + icon: 'cloud' as const, + } + + const credentialsLink = { + title: 'Credentials', + to: `${pathSettings}/credentials`, + icon: 'key' as const, + } + + const resourcesLink = { + title: 'Resources', + to: `${pathSettings}/resources`, + icon: 'chart-bullet' as const, + } + + const imageRegistryLink = { + title: 'Mirroring registry', + to: `${pathSettings}/image-registry`, + icon: 'box' as const, + } + + const networkLink = { + title: 'Network', + to: `${pathSettings}/network`, + icon: 'plug' as const, + } + + const advancedSettingsLink = { + title: 'Advanced settings', + to: `${pathSettings}/advanced-settings`, + icon: 'gears' as const, + } + + const dangerZoneLink = { + title: 'Danger zone', + to: `${pathSettings}/danger-zone`, + icon: 'skull' as const, + } + + const eksAnywhereCluster = isEksAnywhereEnabled && cluster?.kubernetes === 'PARTIALLY_MANAGED' + + const LINKS_SETTINGS = match(cluster) + .with({ kubernetes: 'SELF_MANAGED' }, () => [generalLink, imageRegistryLink, advancedSettingsLink, dangerZoneLink]) + .with( + { cloud_provider: 'AWS', kubernetes: 'MANAGED' }, + { cloud_provider: 'AWS', kubernetes: 'PARTIALLY_MANAGED' }, + () => { + return [ + generalLink, + ...(eksAnywhereCluster ? [eksLink] : []), + credentialsLink, + ...(eksAnywhereCluster ? [] : [resourcesLink]), + imageRegistryLink, + ...(eksAnywhereCluster ? [] : [networkLink]), + advancedSettingsLink, + dangerZoneLink, + ] + } + ) + .with({ cloud_provider: 'SCW' }, () => [ + generalLink, + credentialsLink, + resourcesLink, + imageRegistryLink, + networkLink, + advancedSettingsLink, + dangerZoneLink, + ]) + .with({ cloud_provider: 'GCP' }, () => [ + generalLink, + credentialsLink, + imageRegistryLink, + networkLink, + advancedSettingsLink, + dangerZoneLink, + ]) + .with({ cloud_provider: 'AZURE' }, () => [ + generalLink, + credentialsLink, + resourcesLink, + imageRegistryLink, + networkLink, + advancedSettingsLink, + dangerZoneLink, + ]) + .otherwise(() => []) + + return ( +
+ +
+
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/eks.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/eks.tsx new file mode 100644 index 00000000000..cc45fe93964 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/eks.tsx @@ -0,0 +1,31 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { StepEks } from '@qovery/domains/clusters/feature' +import { GitRepositorySettings } from '@qovery/domains/organizations/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/eks')({ + component: Eks, +}) + +function Eks() { + useDocumentTitle('EKS configuration - Create Cluster') + const { organizationId = '', slug } = useParams({ strict: false }) + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/cluster/create/${slug}` + + return ( + + } + onSubmit={() => navigate({ to: `${creationFlowUrl}/summary` })} + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/features.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/features.tsx new file mode 100644 index 00000000000..29a9b3619cb --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/features.tsx @@ -0,0 +1,21 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { StepFeatures } from '@qovery/domains/clusters/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/features')({ + component: Features, +}) + +function Features() { + useDocumentTitle('Features - Create Cluster') + const { organizationId = '', slug } = useParams({ strict: false }) + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/cluster/create/${slug}` + + const handleSubmit = () => { + navigate({ to: `${creationFlowUrl}/summary` }) + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/general.tsx new file mode 100644 index 00000000000..902e91be53f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/general.tsx @@ -0,0 +1,36 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { match } from 'ts-pattern' +import { StepGeneral } from '@qovery/domains/clusters/feature' +import { LabelSetting } from '@qovery/domains/organizations/feature' +import { type ClusterGeneralData } from '@qovery/shared/interfaces' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/general')({ + component: General, +}) + +function General() { + useDocumentTitle('General - Create Cluster') + const { organizationId = '', slug } = useParams({ strict: false }) + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/cluster/create/${slug}` + + const handleSubmit = (data: ClusterGeneralData) => { + match(data) + .with({ installation_type: 'SELF_MANAGED' }, () => navigate({ to: `${creationFlowUrl}/kubeconfig` })) + .with({ installation_type: 'MANAGED', cloud_provider: 'GCP' }, () => + navigate({ to: `${creationFlowUrl}/features` }) + ) + .with({ installation_type: 'PARTIALLY_MANAGED' }, () => navigate({ to: `${creationFlowUrl}/kubeconfig` })) + .otherwise(() => navigate({ to: `${creationFlowUrl}/resources` })) + } + + return ( + } + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/index.tsx new file mode 100644 index 00000000000..17d67c93a47 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/index.tsx @@ -0,0 +1,17 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', slug = 'new' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig.tsx new file mode 100644 index 00000000000..a79af92c1cc --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig.tsx @@ -0,0 +1,27 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { StepKubeconfig } from '@qovery/domains/clusters/feature' +import { type ClusterKubeconfigData } from '@qovery/shared/interfaces' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/kubeconfig')({ + component: Kubeconfig, +}) + +function Kubeconfig() { + useDocumentTitle('Kubeconfig - Create Cluster') + const { organizationId = '', slug } = useParams({ strict: false }) + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/cluster/create/${slug}` + + const handleSubmit = (_data: ClusterKubeconfigData) => { + if (slug === 'aws-eks-anywhere') { + navigate({ to: `${creationFlowUrl}/eks` }) + return + } + + navigate({ to: `${creationFlowUrl}/summary` }) + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources.tsx new file mode 100644 index 00000000000..47ebe45b52d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/resources.tsx @@ -0,0 +1,48 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { match } from 'ts-pattern' +import { SCW_CONTROL_PLANE_FEATURE_ID } from '@qovery/domains/cloud-providers/feature' +import { StepResources, useClusterContainerCreateContext } from '@qovery/domains/clusters/feature' +import { type ClusterResourcesData } from '@qovery/shared/interfaces' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/resources')({ + component: Resources, +}) + +function Resources() { + useDocumentTitle('Resources - Create Cluster') + const { organizationId = '', slug } = useParams({ strict: false }) + const navigate = useNavigate() + const { generalData, setFeaturesData } = useClusterContainerCreateContext() + + const creationFlowUrl = `/organization/${organizationId}/cluster/create/${slug}` + + const handleSubmit = (data: ClusterResourcesData) => { + match(generalData?.cloud_provider) + .with('AWS', () => { + navigate({ to: `${creationFlowUrl}/features` }) + }) + .with('SCW', () => { + // Set control plane feature data + setFeaturesData({ + vpc_mode: 'DEFAULT', + features: { + [SCW_CONTROL_PLANE_FEATURE_ID]: { + id: SCW_CONTROL_PLANE_FEATURE_ID, + title: 'Control Plane', + value: true, + extendedValue: data.scw_control_plane, + }, + }, + }) + // Navigate to features step for network configuration + navigate({ to: `${creationFlowUrl}/features` }) + }) + .otherwise(() => { + navigate({ to: `${creationFlowUrl}/summary` }) + setFeaturesData(undefined) + }) + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/route.tsx new file mode 100644 index 00000000000..67fa0d93031 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/route.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Outlet } from '@tanstack/react-router' +import { ClusterCreationFlow } from '@qovery/domains/clusters/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/summary.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/summary.tsx new file mode 100644 index 00000000000..ff42e5a9a5a --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/create/$slug/summary.tsx @@ -0,0 +1,13 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { StepSummary } from '@qovery/domains/clusters/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/create/$slug/summary')({ + component: Summary, +}) + +function Summary() { + useDocumentTitle('Summary - Create Cluster') + const { organizationId = '' } = useParams({ strict: false }) + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/new.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/new.tsx new file mode 100644 index 00000000000..e955a38d1ce --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/cluster/new.tsx @@ -0,0 +1,31 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { ClusterNew } from '@qovery/domains/clusters/feature' +import { Heading, Icon, Link, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/cluster/new')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '' } = useParams({ strict: false }) + useDocumentTitle('Create new cluster') + + return ( +
+ + + Back to clusters + +
+ Install cluster +
+ +
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/clusters.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/clusters.tsx new file mode 100644 index 00000000000..e7bf2bcc9cf --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/clusters.tsx @@ -0,0 +1,77 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { ClusterCard, useClusterStatuses, useClusters } from '@qovery/domains/clusters/feature' +import { EmptyState, Heading, Icon, Link, LoaderSpinner, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/clusters')({ + component: RouteComponent, +}) + +const Clusters = () => { + const { organizationId = '' } = useParams({ strict: false }) + const { data: clusters = [] } = useClusters({ organizationId, suspense: true }) + const { data: clusterStatuses = [] } = useClusterStatuses({ organizationId, suspense: true }) + + if (clusters.length === 0) { + return ( + + + + New Cluster + + + ) + } + + return ( +
+ {clusters.map((cluster) => ( + c.cluster_id === cluster.id)} + /> + ))} +
+ ) +} + +function RouteComponent() { + useDocumentTitle('Clusters - Manage your clusters') + const { organizationId = '' } = useParams({ strict: false }) + + return ( +
+
+
+
+ Clusters + + + Add cluster + +
+
+
+
+ + +
+ } + > + + +
+ + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/index.tsx new file mode 100644 index 00000000000..d130b1e3e55 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/index.tsx @@ -0,0 +1,16 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId } = useParams({ strict: false }) + + if (!organizationId) { + return null + } + + // Redirect to overview + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/overview.tsx new file mode 100644 index 00000000000..d66f4c3cc41 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/overview.tsx @@ -0,0 +1,22 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SectionProductionHealth } from '@qovery/domains/clusters/feature' +import { OrganizationOverview } from '@qovery/domains/organizations/feature' +import { ProjectList } from '@qovery/domains/projects/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/overview')({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Organization - Overview') + + return ( +
+ + + + +
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create.tsx new file mode 100644 index 00000000000..681364de92b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { CreateDeploymentRule } from '@qovery/domains/projects/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/create' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId.tsx new file mode 100644 index 00000000000..415bfb737e9 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { EditDeploymentRule } from '@qovery/domains/projects/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/edit/$deploymentRuleId' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/index.tsx new file mode 100644 index 00000000000..d6f533f677c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/index.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { DeploymentRules } from '@qovery/domains/projects/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/index.tsx new file mode 100644 index 00000000000..7e4da9ad8cb --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/index.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' +import { EnvironmentPipeline } from '@qovery/domains/environment-logs/feature' +import { Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Deployment details') + + return ( +
+
+
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs.tsx new file mode 100644 index 00000000000..ef0b929dcd9 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs.tsx @@ -0,0 +1,49 @@ +import { type QueryClient } from '@tanstack/react-query' +import { createFileRoute } from '@tanstack/react-router' +import { type EnvironmentStatusesWithStagesPreCheckStage } from 'qovery-typescript-axios' +import { useCallback, useState } from 'react' +import { ListPreCheckLogs } from '@qovery/domains/environment-logs/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { QOVERY_WS } from '@qovery/shared/util-node-env' +import { useReactQueryWsSubscription } from '@qovery/state/util-queries' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs' +)({ + component: RouteComponent, +}) + +function PreCheckLogs() { + const { environmentId, organizationId, projectId, deploymentId } = Route.useParams() + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const [preCheckStage, setPreCheckStage] = useState() + + const messageHandler = useCallback( + (_: QueryClient, { pre_check_stage }: { pre_check_stage: EnvironmentStatusesWithStagesPreCheckStage }) => { + setPreCheckStage(pre_check_stage) + }, + [setPreCheckStage] + ) + // XXX: If we don't have a version, it works like WS otherwise, it works like a REST API + useReactQueryWsSubscription({ + url: QOVERY_WS + '/deployment/status', + urlSearchParams: { + organization: organizationId, + cluster: environment?.cluster_id, + project: projectId, + environment: environmentId, + version: deploymentId, + }, + enabled: + Boolean(organizationId) && Boolean(environment?.cluster_id) && Boolean(projectId) && Boolean(environmentId), + onMessage: messageHandler, + }) + + if (!environment) return null + + return +} + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments.tsx new file mode 100644 index 00000000000..3a6a0c1289d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments.tsx @@ -0,0 +1,34 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { EnvironmentDeploymentListSkeleton } from '@qovery/domains/environments/feature' +import { EnvironmentDeploymentList } from '@qovery/domains/environments/feature' +import { Heading, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Deployment history') + + return ( +
+
+
+
+ Deployments +
+
+
+
+ }> + + +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index.tsx new file mode 100644 index 00000000000..6dd5e253255 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/index.tsx new file mode 100644 index 00000000000..1fc714d186c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/index.tsx @@ -0,0 +1,93 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ServiceList } from '@qovery/domains/services/feature' +import { Section, Skeleton, TablePrimitives } from '@qovery/shared/ui' + +const { Table } = TablePrimitives + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/' +)({ + component: RouteComponent, +}) + +function Services() { + const { environmentId = '' } = Route.useParams() + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + + if (!environment) { + return null + } + + return +} + +function ServiceListSkeleton() { + const columnSizes = ['40%', '15%', '15%', '20%', '10%'] + + return ( +
+
+
+
+
+ + +
+
+
+
+
+
+
+ + +
+ + + + {[...Array(5)].map((_, index) => ( + + + + ))} + + + + {[...Array(3)].map((_, index) => ( + + {[...Array(5)].map((_, index) => ( + + {index === 0 ? ( +
+ + +
+ ) : ( + + )} +
+ ))} +
+ ))} +
+
+
+
+
+
+ ) +} + +function RouteComponent() { + return ( + }> + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline.tsx new file mode 100644 index 00000000000..03fa7b01fce --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ServicesDeploymentPipeline } from '@qovery/domains/services/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Deployment Pipeline') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx new file mode 100644 index 00000000000..e26222889ea --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx @@ -0,0 +1,107 @@ +import { type IconName } from '@fortawesome/fontawesome-common-types' +import { Outlet, createFileRoute, useMatchRoute } from '@tanstack/react-router' +import { + EnvironmentLastDeploymentSection, + EnvironmentMode, + MenuManageDeployment, + MenuOtherActions, + useDeploymentStatus, + useEnvironment, +} from '@qovery/domains/environments/feature' +import { Heading, Icon, Link, Navbar, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { environmentId, projectId, organizationId } = Route.useParams() + const matchRoute = useMatchRoute() + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: deploymentStatus } = useDeploymentStatus({ environmentId }) + + const tabs = [ + { + id: 'services', + label: 'List', + iconName: 'list-ul' as IconName, + routeId: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/', + }, + { + id: 'pipeline', + label: 'Pipeline', + iconName: 'timeline' as IconName, + routeId: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview/pipeline', + }, + ] + const activeTabId = tabs.find((tab) => matchRoute({ to: tab.routeId }))?.id + + if (!environment || !deploymentStatus) { + return null + } + + return ( +
+
+
+
+
+ + {environment?.name} +
+ +
+ + +
+
+
+
+
+ +
+
+ Services + + + New service + +
+
+
+
+ + {tabs.map((tab) => ( + + + {tab.label} + + ))} + +
+
+
+
+ +
+
+
+
+
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell.tsx new file mode 100644 index 00000000000..6d979ca209b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell.tsx @@ -0,0 +1,59 @@ +import { createFileRoute } from '@tanstack/react-router' +import { DatabaseModeEnum } from 'qovery-typescript-axios' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ServiceTerminal, useService } from '@qovery/domains/services/feature' +import { Icon } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { environmentId = '', serviceId = '' } = Route.useParams() + useDocumentTitle('Service - Cloud shell') + + const { data: environment } = useEnvironment({ + environmentId, + suspense: true, + }) + + const { data: service } = useService({ + environmentId, + serviceId, + suspense: true, + }) + + const isManagedDatabase = service?.serviceType === 'DATABASE' && service.mode === DatabaseModeEnum.MANAGED + + if (!environment || !service) { + return null + } + + if (isManagedDatabase) { + return ( +
+
+ + Cloud shell is not available for managed databases. +
+
+ ) + } + + return ( +
+
+ +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/index.tsx new file mode 100644 index 00000000000..d33669ef1ad --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/index.tsx @@ -0,0 +1,34 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ServiceDeploymentList, ServiceDeploymentListSkeleton } from '@qovery/domains/services/feature' +import { Heading, Section } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { environmentId = '', serviceId = '' } = useParams({ strict: false }) + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + + return ( +
+
+
+
+ Deployments +
+
+
+
+ }> + + +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId.tsx new file mode 100644 index 00000000000..c819976df8a --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { DeploymentLogs } from '@qovery/domains/service-logs/feature' +import { LoaderPlaceholder } from '@qovery/domains/service-logs/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( + + + + } + > + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx new file mode 100644 index 00000000000..1f86916861f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.$alertId.edit.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.$alertId.edit.tsx new file mode 100644 index 00000000000..e912c4964ba --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.$alertId.edit.tsx @@ -0,0 +1,107 @@ +import { Navigate, createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { AlertRuleConditionOperator } from 'qovery-typescript-axios' +import { useMemo } from 'react' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { + type AlertConfiguration, + AlertingCreationFlow, + type MetricCategory, + useAlertRules, +} from '@qovery/domains/observability/feature' +import { useService } from '@qovery/domains/services/feature' +import { LoaderSpinner } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { + organizationId = '', + projectId = '', + environmentId = '', + serviceId = '', + alertId = '', + } = useParams({ strict: false }) + const navigate = useNavigate() + + const { data: environment, isFetched: isEnvironmentFetched } = useEnvironment({ environmentId }) + const { data: service, isFetched: isServiceFetched } = useService({ environmentId, serviceId }) + const { data: alertRules = [], isFetched: isAlertRulesFetched } = useAlertRules({ + organizationId, + serviceId, + }) + const alertRule = useMemo(() => alertRules.find((rule) => rule.id === alertId), [alertId, alertRules]) + + const goToAlertsList = () => { + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts', + params: { organizationId, projectId, environmentId, serviceId }, + }) + } + + const initialAlert = useMemo(() => { + if (!alertRule) { + return undefined + } + + const rawThreshold = alertRule.condition.threshold ?? 0 + const threshold = + alertRule.tag === 'http_latency' ? rawThreshold : alertRule.condition.threshold != null ? rawThreshold * 100 : 80 + const isMissingInstance = alertRule.tag === 'missing_instance' + + return [ + { + id: alertRule.id, + tag: alertRule.tag, + for_duration: alertRule.for_duration || 'PT5M', + condition: { + kind: alertRule.condition.kind || 'BUILT', + function: alertRule.condition.function || 'AVG', + operator: isMissingInstance ? AlertRuleConditionOperator.BELOW : alertRule.condition.operator || 'ABOVE', + threshold: isMissingInstance ? 1 : threshold, + promql: alertRule.condition.promql || '', + }, + name: alertRule.name, + severity: alertRule.severity, + alert_receiver_ids: alertRule.alert_receiver_ids || [], + skipped: false, + presentation: { summary: alertRule.presentation.summary }, + }, + ] + }, [alertRule]) + + if (!isEnvironmentFetched || !isServiceFetched || !isAlertRulesFetched) { + return ( +
+ +
+ ) + } + + if (!environment || !service || !alertRule || !initialAlert) { + return ( + + ) + } + + return ( + goToAlertsList()} + onClose={() => goToAlertsList()} + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.create.metric.$metric.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.create.metric.$metric.tsx new file mode 100644 index 00000000000..debe5e2d85e --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.create.metric.$metric.tsx @@ -0,0 +1,100 @@ +import { Navigate, createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { useMemo } from 'react' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { AlertingCreationFlow, type MetricCategory } from '@qovery/domains/observability/feature' +import { useService } from '@qovery/domains/services/feature' +import { LoaderSpinner } from '@qovery/shared/ui' + +interface AlertsCreateSearch { + templates?: string +} + +const METRIC_CATEGORIES: MetricCategory[] = [ + 'cpu', + 'memory', + 'http_error', + 'http_latency', + 'missing_instance', + 'instance_restart', + 'hpa_limit', +] + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric' +)({ + component: RouteComponent, + validateSearch: (search: Record): AlertsCreateSearch => ({ + templates: typeof search.templates === 'string' ? search.templates : undefined, + }), +}) + +function RouteComponent() { + const { + organizationId = '', + projectId = '', + environmentId = '', + serviceId = '', + metric = '', + } = useParams({ strict: false }) + const search = Route.useSearch() + const navigate = useNavigate() + + const { data: environment, isFetched: isEnvironmentFetched } = useEnvironment({ environmentId }) + const { data: service, isFetched: isServiceFetched } = useService({ environmentId, serviceId }) + + const selectedMetrics = useMemo(() => { + const fromTemplates = (search.templates ?? '') + .split(',') + .map((item) => item.trim()) + .filter((item): item is MetricCategory => METRIC_CATEGORIES.includes(item as MetricCategory)) + + if (fromTemplates.length > 0) { + return fromTemplates + } + + if (METRIC_CATEGORIES.includes(metric as MetricCategory)) { + return [metric as MetricCategory] + } + + return ['cpu' as MetricCategory] + }, [metric, search.templates]) + + if (!isEnvironmentFetched || !isServiceFetched) { + return ( +
+ +
+ ) + } + + if (!environment || !service || selectedMetrics.length === 0) { + return ( + + ) + } + + return ( + { + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts', + params: { organizationId, projectId, environmentId, serviceId }, + }) + }} + onClose={() => { + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts', + params: { organizationId, projectId, environmentId, serviceId }, + }) + }} + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.tsx new file mode 100644 index 00000000000..f92b85fe5b3 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts.tsx @@ -0,0 +1,90 @@ +import { Outlet, createFileRoute, useMatchRoute, useParams } from '@tanstack/react-router' +import { match } from 'ts-pattern' +import { useClusterStatus, useDeployCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ServiceAlerting } from '@qovery/domains/observability/feature' +import { useService } from '@qovery/domains/services/feature' +import { Button, Callout, Icon } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Alerts - Qovery') + const { organizationId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + const matchRoute = useMatchRoute() + const isCreateRoute = Boolean( + matchRoute({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric', + }) + ) + const isEditRoute = Boolean( + matchRoute({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit', + }) + ) + const isAlertSubRoute = isCreateRoute || isEditRoute + + const { data: environment } = useEnvironment({ environmentId, suspense: !isAlertSubRoute }) + const { data: service } = useService({ environmentId, serviceId, suspense: !isAlertSubRoute }) + const { data: deploymentStatus } = useClusterStatus({ + organizationId, + clusterId: environment?.cluster_id ?? '', + enabled: !isAlertSubRoute && Boolean(environment?.cluster_id), + refetchInterval: 5000, + }) + const { mutate: redeployCluster } = useDeployCluster() + + if (isAlertSubRoute) { + return + } + + if (!environment || !service || !deploymentStatus) return null + + const isClusterDeploying = match(deploymentStatus.status) + .with('BUILDING', 'DEPLOYING', () => true) + .otherwise(() => false) + + return ( +
+ + {!isClusterDeploying ? ( + + + + + + Alert rule is not deployed + To apply this change redeploy your cluster + + + + ) : ( + + + + + + Cluster is deploying... + + Last alert rule created or updated should be applied automatically when the cluster is deployed. + + + + )} + +
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard.tsx new file mode 100644 index 00000000000..338182da458 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard.tsx @@ -0,0 +1,226 @@ +import { type IconName } from '@fortawesome/fontawesome-common-types' +import { createFileRoute, useParams } from '@tanstack/react-router' +import posthog from 'posthog-js' +import { useCallback, useEffect, useMemo } from 'react' +import { match } from 'ts-pattern' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { + EnableObservabilityButtonContactUs, + EnableObservabilityContent, + EnableObservabilityVideo, + ServiceDashboard, +} from '@qovery/domains/observability/feature' +import { useDeploymentStatus, useService } from '@qovery/domains/services/feature' +import { monitoringDashboardSearchParamsSchema } from '@qovery/shared/router' +import { Badge, Button, EmptyState, Heading, Icon, Section, Tooltip } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/dashboard' +)({ + component: RouteComponent, + validateSearch: monitoringDashboardSearchParamsSchema, +}) + +function RouteComponent() { + useDocumentTitle('Monitoring - Qovery') + const { environmentId = '', serviceId = '' } = useParams({ strict: false }) + const search = Route.useSearch() + const navigate = Route.useNavigate() + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: serviceStatus } = useDeploymentStatus({ environmentId, serviceId }) + const { data: service } = useService({ environmentId, serviceId, suspense: true }) + const { data: cluster } = useCluster({ + organizationId: environment?.organization.id ?? '', + clusterId: environment?.cluster_id ?? '', + suspense: true, + }) + + const hasMetrics = useMemo( + () => + ((cluster?.cloud_provider === 'AWS' || + cluster?.cloud_provider === 'SCW' || + cluster?.cloud_provider === 'GCP' || + cluster?.cloud_provider === 'AZURE') && + cluster?.metrics_parameters?.enabled && + match(service?.serviceType) + .with('APPLICATION', 'CONTAINER', () => true) + .otherwise(() => false)) || + false, + [cluster?.metrics_parameters?.enabled, service?.serviceType, cluster?.cloud_provider] + ) + + const noMetricsAvailable = useMemo( + () => serviceStatus?.state === 'STOPPED' || serviceStatus?.state === 'READY', + [serviceStatus?.state] + ) + + const setDashboardQueryParams = useCallback( + (updates: Partial) => { + navigate({ + replace: true, + search: (previousSearch) => ({ + ...previousSearch, + ...updates, + }), + }) + }, + [navigate] + ) + + // Keep analytics in an effect to avoid firing on every render + useEffect(() => { + posthog.capture('service-monitoring', { + metrics_enabled: hasMetrics, + service: { + organization_id: environment?.organization.id ?? '', + project_id: environment?.project.id ?? '', + environment_id: environmentId, + service_id: serviceId, + service_name: service?.name ?? '', + }, + }) + }, [environment?.organization.id, environment?.project.id, environmentId, hasMetrics, service?.name, serviceId]) + + if (!hasMetrics) + return ( +
+
+ + Service health check + + +
+
+
+
+ +
+ + Starting from $299/month +
+
+
+ +
+
+
+ ) + + return noMetricsAvailable ? ( +
+ +
+ ) : ( + + ) +} + +function PlaceholderCard({ + title, + description, + status, + icon, +}: { + title: string + description: string + status?: 'GREEN' | 'RED' + icon?: IconName +}) { + return ( +
+
+
+
+ {title} + {status && ( + + + + {status === 'GREEN' ? 'Healthy' : 'Unhealthy'} + + + )} +
+ + + +
+

{description}

+
+
+ ) +} + +function PlaceholderInstanceChart() { + return ( +
+
+
+
+ Instances status + +
+
+ Auto-scaling limit reached + Instance errors + + + +
+
+
+ +
+ {Array.from({ length: 5 }).map((_, index) => ( +
+
+ {100 - index * 25} +
+ ))} +
+
+ 03:00 + 05:00 + 07:00 + 09:00 + 11:00 + 13:00 + 15:00 +
+
+ ) +} + +function PlaceholderMonitoring() { + return ( +
+ +
+ + + + +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/index.tsx new file mode 100644 index 00000000000..a97bb9de2c8 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/route.tsx new file mode 100644 index 00000000000..3e98cf0dff8 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/route.tsx @@ -0,0 +1,84 @@ +import { Outlet } from '@tanstack/react-router' +import { createFileRoute, useMatchRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ErrorBoundary, LoaderSpinner, Sidebar } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring' +)({ + component: RouteComponent, +}) + +const OutletLoader = () => ( +
+ +
+) + +function RouteComponent() { + const { organizationId = '', projectId, environmentId, serviceId } = useParams({ strict: false }) + const matchRoute = useMatchRoute() + const pathMonitoring = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/${serviceId}/monitoring` + const isAlertCreationFlow = Boolean( + matchRoute({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric', + }) + ) + const isAlertEditFlow = Boolean( + matchRoute({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit', + }) + ) + const isAlertSubRoute = isAlertCreationFlow || isAlertEditFlow + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: cluster } = useCluster({ + organizationId, + clusterId: environment?.cluster_id ?? '', + suspense: true, + }) + const hasAlerting = cluster?.metrics_parameters?.configuration?.alerting?.enabled ?? false + + const dashboardLink = { + title: 'Dashboard', + to: `${pathMonitoring}/dashboard`, + icon: 'table-cells-large' as const, + } + + const alertsLink = { + title: 'Alerts', + to: `${pathMonitoring}/alerts`, + icon: 'light-emergency' as const, + } + + const LINKS_MONITORING = hasAlerting ? [dashboardLink, alertsLink] : [dashboardLink] + + return ( +
+ {!isAlertSubRoute && ( + + )} +
+
+ + }> + + + +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx new file mode 100644 index 00000000000..18b49b32bbc --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -0,0 +1,63 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { memo, useMemo } from 'react' +import { match } from 'ts-pattern' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { EnableObservabilityModal } from '@qovery/domains/observability/feature' +import { TerraformResourcesSection } from '@qovery/domains/service-terraform/feature' +import { ObservabilityCallout, ServiceOverview, useService } from '@qovery/domains/services/feature' +import { MetricsWebSocketListener } from '@qovery/shared/util-web-sockets' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview' +)({ + component: RouteComponent, +}) + +const WebSocketListenerMemo = memo(MetricsWebSocketListener) + +function RouteComponent() { + const { organizationId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: cluster } = useCluster({ organizationId, clusterId: environment?.cluster_id ?? '', suspense: true }) + const { data: service } = useService({ environmentId, serviceId, suspense: true }) + + const hasNoMetrics = useMemo( + () => + (cluster?.cloud_provider === 'AWS' || + cluster?.cloud_provider === 'SCW' || + cluster?.cloud_provider === 'GCP' || + cluster?.cloud_provider === 'AZURE') && + !cluster?.metrics_parameters?.enabled && + match(service?.serviceType) + .with('APPLICATION', 'CONTAINER', () => true) + .otherwise(() => false), + [cluster?.metrics_parameters?.enabled, service?.serviceType, cluster?.cloud_provider] + ) + + return ( + <> + : undefined} + hasNoMetrics={hasNoMetrics} + observabilityCallout={ + + + + } + /> + {environment && service?.serviceType && ( + + )} + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs.tsx new file mode 100644 index 00000000000..bc440b1a3dd --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs.tsx @@ -0,0 +1,78 @@ +import { type QueryClient } from '@tanstack/react-query' +import { createFileRoute } from '@tanstack/react-router' +import { + type DeploymentStageWithServicesStatuses, + type EnvironmentStatus, + type EnvironmentStatusesWithStagesPreCheckStage, +} from 'qovery-typescript-axios' +import { useCallback, useState } from 'react' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { ServiceStageIdsProvider } from '@qovery/domains/service-logs/feature' +import { PodLogsFeature } from '@qovery/pages/logs/environment' +import { serviceLogsParamsSchema } from '@qovery/shared/router' +import { QOVERY_WS } from '@qovery/shared/util-node-env' +import { useReactQueryWsSubscription } from '@qovery/state/util-queries' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs' +)({ + component: RouteComponent, + validateSearch: serviceLogsParamsSchema, +}) + +function ServiceLogs() { + const { environmentId = '', organizationId = '', projectId = '' } = Route.useParams() + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + + const [deploymentStages, setDeploymentStages] = useState() + const [environmentStatus, setEnvironmentStatus] = useState() + + const messageHandler = useCallback( + ( + _: QueryClient, + { + stages, + environment, + }: { + stages: DeploymentStageWithServicesStatuses[] + environment: EnvironmentStatus + pre_check_stage: EnvironmentStatusesWithStagesPreCheckStage + } + ) => { + setDeploymentStages(stages) + setEnvironmentStatus(environment) + }, + [setDeploymentStages] + ) + // XXX: If we don't have a version, it works like WS otherwise, it works like a REST API + useReactQueryWsSubscription({ + url: QOVERY_WS + '/deployment/status', + urlSearchParams: { + organization: organizationId, + cluster: environment?.cluster_id, + project: projectId, + environment: environmentId, + }, + enabled: + Boolean(organizationId) && Boolean(environment?.cluster_id) && Boolean(projectId) && Boolean(environmentId), + onMessage: messageHandler, + }) + + if (!environment) return null + + return ( + + ) +} + +function RouteComponent() { + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings.tsx new file mode 100644 index 00000000000..2a8284f63e3 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/advanced-settings.tsx @@ -0,0 +1,30 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { ServiceAdvancedSettings, ServiceAdvancedSettingsLoader } from '@qovery/domains/services/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { 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/settings/advanced-settings' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Advanced settings - Service settings') + + return ( +
+
+ + }> + + +
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure.tsx new file mode 100644 index 00000000000..35dd0eb6349 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/configure' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Configuration
+} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone.tsx new file mode 100644 index 00000000000..4b1ab6afd40 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone.tsx @@ -0,0 +1,28 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ServiceDangerZoneSettings } from '@qovery/domains/service-settings/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/danger-zone' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, projectId, environmentId } = Route.useParams() + const navigate = Route.useNavigate() + + return ( + + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/overview', + params: { + organizationId, + projectId, + environmentId, + }, + }) + } + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions.tsx new file mode 100644 index 00000000000..3354ced4b7b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ServiceDeploymentRestrictionsSettings } from '@qovery/domains/service-settings/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/deployment-restrictions' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Deployment restrictions - Service settings') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile.tsx new file mode 100644 index 00000000000..884dbe05cf1 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/dockerfile' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Dockerfile
+} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain.tsx new file mode 100644 index 00000000000..d6b68850d0d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ServiceDomainSettings } from '@qovery/domains/service-settings/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/domain' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Domain - Service settings') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general.tsx new file mode 100644 index 00000000000..c79bbffd811 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general.tsx @@ -0,0 +1,18 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useOrganization } from '@qovery/domains/organizations/feature' +import { ServiceGeneralSettings } from '@qovery/domains/service-settings/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/general' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId } = Route.useParams() + const { data: organization } = useOrganization({ organizationId, suspense: true }) + + if (!organization) return null + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks.tsx new file mode 100644 index 00000000000..b1f3f2c4168 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ApplicationContainerHealthchecksSettings } from '@qovery/domains/service-settings/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/health-checks' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Health checks - Service settings') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/index.tsx new file mode 100644 index 00000000000..aebb2c655ab --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking.tsx new file mode 100644 index 00000000000..0e00380103c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/networking' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Networking
+} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port.tsx new file mode 100644 index 00000000000..96d0f2aae70 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ApplicationContainerPortSettings } from '@qovery/domains/service-settings/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/port' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Port - Service settings') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources.tsx new file mode 100644 index 00000000000..ec672110628 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { ServiceResourcesSettings } from '@qovery/domains/service-settings/feature' +import { LoaderSpinner } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/resources' +)({ + component: RouteComponent, +}) + +const ResourcesLoader = () => ( +
+ +
+) + +function RouteComponent() { + return ( + }> + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/route.tsx new file mode 100644 index 00000000000..d84fb8def96 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/route.tsx @@ -0,0 +1,34 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { ServiceSettingsLayout } from '@qovery/domains/service-settings/feature' +import { ErrorBoundary, LoaderSpinner } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings' +)({ + component: RouteComponent, +}) + +const OutletLoader = () => ( +
+ +
+) + +function RouteComponent() { + return ( + }> + + + + + ) +} + +function RouteContent() { + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage.tsx new file mode 100644 index 00000000000..11c74a9f13f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { ApplicationContainerStorageSettings } from '@qovery/domains/service-settings/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/storage' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + useDocumentTitle('Storage - Service settings') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments.tsx new file mode 100644 index 00000000000..5869d3592a9 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { TerraformArgumentsSettings } from '@qovery/domains/service-settings/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-arguments' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration.tsx new file mode 100644 index 00000000000..8a58b7017ea --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration.tsx @@ -0,0 +1,96 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Suspense } from 'react' +import { FormProvider, useForm } from 'react-hook-form' +import { match } from 'ts-pattern' +import { + TerraformConfigurationSettings, + type TerraformGeneralData, + buildDockerfileFragment, + extractDockerfileFragmentFields, +} from '@qovery/domains/service-settings/feature' +import { useEditService, useService } from '@qovery/domains/services/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Button, LoaderSpinner, Section } from '@qovery/shared/ui' +import { buildEditServicePayload } from '@qovery/shared/util-services' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-configuration' +)({ + component: RouteComponent, +}) + +const TerraformConfigurationLoader = () => ( +
+ +
+) + +const TerraformConfigurationSettingsWrapper = () => { + const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = Route.useParams() + const { data: service } = useService({ serviceId, suspense: true }) + const { mutate: editService, isLoading: isLoadingEditService } = useEditService({ + organizationId, + projectId, + environmentId, + }) + + const methods = useForm({ + mode: 'onChange', + defaultValues: match(service) + .with({ serviceType: 'TERRAFORM' }, (s) => ({ + ...s, + ...extractDockerfileFragmentFields(s.dockerfile_fragment), + })) + .otherwise(() => ({})), + }) + + const onSubmit = methods.handleSubmit((data) => { + if (!service || !data) return + + if (service.serviceType === 'TERRAFORM') { + const payload = { + ...data, + timeout_sec: Number(data.timeout_sec ?? service.timeout_sec), + dockerfile_fragment: buildDockerfileFragment(data), + } + + editService({ + serviceId: service.id, + payload: buildEditServicePayload({ service, request: payload }), + }) + } + }) + + return ( + +
+ +
+ +
+ +
+
+
+
+ ) +} + +function RouteComponent() { + return ( + }> + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables.tsx new file mode 100644 index 00000000000..359bb183890 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables.tsx @@ -0,0 +1,112 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { FormProvider, useForm, useFormContext } from 'react-hook-form' +import { match } from 'ts-pattern' +import { TerraformVariablesTable } from '@qovery/domains/service-settings/feature' +import { TerraformVariablesProvider, useTerraformVariablesContext } from '@qovery/domains/service-terraform/feature' +import { type Terraform } from '@qovery/domains/services/data-access' +import { type TerraformGeneralData, useEditService, useService } from '@qovery/domains/services/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { Button, LoaderSpinner, Section } from '@qovery/shared/ui' +import { buildEditServicePayload } from '@qovery/shared/util-services' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/terraform-variables' +)({ + component: RouteComponent, +}) + +const TerraformVariablesLoader = () => ( +
+ +
+) + +const TerraformVariablesSettingsForm = ({ service }: { service: Terraform }) => { + const { organizationId = '', projectId = '', environmentId = '' } = Route.useParams() + const { handleSubmit } = useFormContext() + const { serializeForApi, tfVarFiles, errors } = useTerraformVariablesContext() + + const { mutate: editService, isLoading: isLoadingEditService } = useEditService({ + organizationId, + projectId, + environmentId, + }) + + if (service?.serviceType !== 'TERRAFORM') { + return null + } + + const onSubmit = handleSubmit(() => { + // Edit the service with the updated variables and the updated order of tfvars files + const payload = buildEditServicePayload({ + service, + request: { + terraform_variables_source: { + ...service.terraform_variables_source, + tf_vars: serializeForApi(), + tf_var_file_paths: [...tfVarFiles.filter((file) => file.enabled)].reverse().map((file) => file.source), + }, + }, + }) + editService({ + serviceId: service.id, + payload, + }) + }) + + return ( + <> + +
+ +
+ + ) +} + +const TerraformVariablesContent = ({ service }: { service: Terraform }) => { + const methods = useForm({ + mode: 'onChange', + defaultValues: match(service) + .with({ serviceType: 'TERRAFORM' }, (s) => s) + .otherwise(() => ({})), + }) + + return ( +
+ +
+ + + + + +
+
+ ) +} + +const TerraformVariablesWrapper = () => { + const { serviceId } = useParams({ strict: false }) + const { data: service } = useService({ serviceId }) + + if (service?.serviceType !== 'TERRAFORM') { + return null + } + + return +} + +function RouteComponent() { + return ( + }> + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments.tsx new file mode 100644 index 00000000000..573ab52f991 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-arguments' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Values override arguments
+} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file.tsx new file mode 100644 index 00000000000..0796bcd5e24 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings/values-override-file' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Values override file
+} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx new file mode 100644 index 00000000000..85aa5d266a4 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables.tsx @@ -0,0 +1,155 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { match } from 'ts-pattern' +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?.serviceType) { + 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, + undefined, + 'Redeploy' + ) + } + /> + )} +
+
+
+ {scope && ( +
+ { + toast( + 'SUCCESS', + 'Creation success', + 'You need to redeploy your service for your changes to be applied.', + toasterCallback, + undefined, + 'Redeploy' + ) + }} + onEditVariable={() => { + toast( + 'SUCCESS', + 'Edition success', + 'You need to redeploy your service for your changes to be applied.', + toasterCallback, + undefined, + '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, + undefined, + 'Redeploy' + ) + }} + /> +
+ )} +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general.tsx new file mode 100644 index 00000000000..229f42d05f5 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general.tsx @@ -0,0 +1,62 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { + AnnotationSetting, + ContainerRegistryCreateEditModal, + GitRepositorySettings, + LabelSetting, +} from '@qovery/domains/organizations/feature' +import { ApplicationContainerStepGeneral, GeneralContainerSettings } from '@qovery/domains/services/feature' +import { EntrypointCmdInputs } from '@qovery/shared/console-shared' +import { type ApplicationGeneralData } from '@qovery/shared/interfaces' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useModal } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/general' +)({ + component: General, + validateSearch: serviceCreateParamsSchema, +}) + +function General() { + const { organizationId = '', projectId = '', environmentId = '', slug } = Route.useParams() + const navigate = useNavigate() + const search = Route.useSearch() + + const { openModal, closeModal } = useModal() + + const openContainerRegistryCreateEditModal = () => { + return openModal({ + content: , + options: { + fakeModal: true, + width: 680, + }, + }) + } + + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + useDocumentTitle('General - Create Service') + + const handleSubmit = (_data: ApplicationGeneralData) => { + navigate({ to: `${creationFlowUrl}/resources`, search }) + } + + return ( + } + generalContainerSettings={ + + } + entrypointCmdInputs={} + labelSetting={} + annotationSetting={} + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks.tsx new file mode 100644 index 00000000000..93a75de110a --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks.tsx @@ -0,0 +1,43 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { Suspense } from 'react' +import { ApplicationContainerStepHealthchecks } from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { LoaderSpinner } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/health-checks' +)({ + component: Healthchecks, + validateSearch: serviceCreateParamsSchema, +}) + +function Healthchecks() { + const { organizationId = '', projectId = '', environmentId = '', slug } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + useDocumentTitle('Health checks - Create Service') + + const handleBack = () => { + navigate({ to: `${creationFlowUrl}/ports`, search }) + } + + const handleSubmit = async () => { + navigate({ to: `${creationFlowUrl}/variables`, search }) + } + + return ( + + + + } + > + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/index.tsx new file mode 100644 index 00000000000..69a16c76205 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/index.tsx @@ -0,0 +1,23 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' +import { serviceCreateParamsSchema } from '@qovery/shared/router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/' +)({ + component: RouteComponent, + validateSearch: serviceCreateParamsSchema, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', slug = '' } = useParams({ strict: false }) + const search = Route.useSearch() + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports.tsx new file mode 100644 index 00000000000..222a96c0203 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports.tsx @@ -0,0 +1,50 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { Suspense } from 'react' +import { + ApplicationContainerStepPort, + type ApplicationContainerStepPortSubmitData, +} from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { LoaderSpinner } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/ports' +)({ + component: Ports, + validateSearch: serviceCreateParamsSchema, +}) + +function Ports() { + const { organizationId = '', projectId = '', environmentId = '', slug } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + useDocumentTitle('Ports - Create Service') + + const handleBack = () => { + navigate({ to: `${creationFlowUrl}/resources`, search }) + } + + const handleSubmit = async ({ portData }: ApplicationContainerStepPortSubmitData) => { + if ((portData.ports?.length ?? 0) > 0) { + navigate({ to: `${creationFlowUrl}/health-checks`, search }) + return + } + navigate({ to: `${creationFlowUrl}/variables`, search }) + } + + return ( + + + + } + > + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources.tsx new file mode 100644 index 00000000000..ec29ac45945 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources.tsx @@ -0,0 +1,27 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { ApplicationContainerStepResources } from '@qovery/domains/services/feature' +import { type ApplicationResourcesData } from '@qovery/shared/interfaces' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/resources' +)({ + component: Resources, + validateSearch: serviceCreateParamsSchema, +}) + +function Resources() { + const { organizationId, projectId, environmentId, slug } = Route.useParams() + const navigate = useNavigate() + const search = Route.useSearch() + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + useDocumentTitle('Resources - Create Service') + + const handleSubmit = (_data: ApplicationResourcesData) => { + navigate({ to: `${creationFlowUrl}/ports`, search }) + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/route.tsx new file mode 100644 index 00000000000..0f5eb9e60b9 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/route.tsx @@ -0,0 +1,22 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { ApplicationContainerCreationFlow } from '@qovery/domains/services/feature' +import { ServiceTypeEnum } from '@qovery/shared/enums' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '', slug } = useParams({ strict: false }) + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + const defaultServiceType = slug === 'container' ? ServiceTypeEnum.CONTAINER : ServiceTypeEnum.APPLICATION + + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary.tsx new file mode 100644 index 00000000000..d508826e665 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary.tsx @@ -0,0 +1,35 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useAnnotationsGroups, useContainerRegistry, useLabelsGroups } from '@qovery/domains/organizations/feature' +import { ApplicationContainerStepSummary, useApplicationContainerCreateContext } from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/summary' +)({ + component: Summary, + validateSearch: serviceCreateParamsSchema, +}) + +function Summary() { + const { organizationId = '' } = Route.useParams() + const { generalForm } = useApplicationContainerCreateContext() + const registryId = generalForm.watch('registry') + + const { data: containerRegistry } = useContainerRegistry({ + organizationId, + containerRegistryId: registryId, + }) + const { data: annotationsGroup = [] } = useAnnotationsGroups({ organizationId }) + const { data: labelsGroup = [] } = useLabelsGroups({ organizationId }) + + useDocumentTitle('Summary - Create Service') + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables.tsx new file mode 100644 index 00000000000..79cfa2c9932 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables.tsx @@ -0,0 +1,35 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { + ApplicationContainerStepVariables, + useApplicationContainerCreateContext, +} from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug/variables' +)({ + component: Variables, + validateSearch: serviceCreateParamsSchema, +}) + +function Variables() { + const { organizationId = '', projectId = '', environmentId = '', slug } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + const { portForm } = useApplicationContainerCreateContext() + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/${slug}` + + useDocumentTitle('Environment variables - Create Service') + + const handleBack = () => { + const hasPorts = Boolean(portForm.getValues('ports')?.length) + navigate({ to: `${creationFlowUrl}/${hasPorts ? 'health-checks' : 'ports'}`, search }) + } + + const handleSubmit = () => { + navigate({ to: `${creationFlowUrl}/summary`, search }) + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general.tsx new file mode 100644 index 00000000000..5dbf0825585 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general.tsx @@ -0,0 +1,72 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { type ClusterFeatureAwsExistingVpc } from 'qovery-typescript-axios' +import { Suspense } from 'react' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment, useListDatabaseConfigurations } from '@qovery/domains/environments/feature' +import { AnnotationSetting, LabelSetting } from '@qovery/domains/organizations/feature' +import { type DatabaseCreateGeneralData, DatabaseStepGeneral } from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { LoaderSpinner } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/general' +)({ + component: General, + validateSearch: serviceCreateParamsSchema, +}) + +function General() { + return ( + + + + } + > + + + ) +} + +function GeneralContent() { + useDocumentTitle('General - Create Database') + + const { organizationId = '', projectId = '', environmentId = '' } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/database` + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: cluster } = useCluster({ + organizationId, + clusterId: environment?.cluster_id ?? '', + enabled: Boolean(environment?.cluster_id), + suspense: true, + }) + const { data: databaseConfigurations } = useListDatabaseConfigurations({ + environmentId, + enabled: Boolean(environmentId), + suspense: true, + }) + const clusterVpc = cluster?.features?.find((feature) => feature.id === 'EXISTING_VPC')?.value_object?.value as + | ClusterFeatureAwsExistingVpc + | undefined + + const handleSubmit = (_data: DatabaseCreateGeneralData) => { + navigate({ to: `${creationFlowUrl}/resources`, search }) + } + + return ( + } + annotationSetting={} + cloudProvider={environment?.cloud_provider.provider} + cluster={cluster} + clusterVpc={clusterVpc} + databaseConfigurations={databaseConfigurations} + /> + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/index.tsx new file mode 100644 index 00000000000..45e7184fc7c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/index.tsx @@ -0,0 +1,23 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' +import { serviceCreateParamsSchema } from '@qovery/shared/router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/' +)({ + component: RouteComponent, + validateSearch: serviceCreateParamsSchema, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + const search = Route.useSearch() + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources.tsx new file mode 100644 index 00000000000..7f899f75459 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources.tsx @@ -0,0 +1,100 @@ +import { Navigate, createFileRoute, useNavigate } from '@tanstack/react-router' +import { CloudProviderEnum, type ManagedDatabaseInstanceTypeResponse } from 'qovery-typescript-axios' +import { match } from 'ts-pattern' +import { useCloudProviderDatabaseInstanceTypes } from '@qovery/domains/cloud-providers/feature' +import { useCluster } from '@qovery/domains/clusters/feature' +import { useEnvironment } from '@qovery/domains/environments/feature' +import { + type DatabaseCreateResourcesData, + DatabaseStepResources, + useDatabaseCreateContext, +} from '@qovery/domains/services/feature' +import { type Value } from '@qovery/shared/interfaces' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/resources' +)({ + component: Resources, + validateSearch: serviceCreateParamsSchema, +}) + +function Resources() { + const { organizationId = '', projectId = '', environmentId = '' } = Route.useParams() + const search = Route.useSearch() + const navigate = useNavigate() + const { generalForm } = useDatabaseCreateContext() + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/database` + const generalValues = generalForm.getValues() + const databaseType = generalValues.type ?? 'POSTGRESQL' + + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const cloudProvider = environment?.cloud_provider.provider as CloudProviderEnum | undefined + const { data: cluster } = useCluster({ + organizationId, + clusterId: environment?.cluster_id ?? '', + enabled: Boolean(environment?.cluster_id), + suspense: true, + }) + const { data: databaseInstanceTypes } = useCloudProviderDatabaseInstanceTypes( + match(cloudProvider ?? CloudProviderEnum.AWS) + .with(CloudProviderEnum.AWS, (provider) => ({ + cloudProvider: provider, + databaseType, + region: cluster?.region || '', + })) + .with(CloudProviderEnum.SCW, (provider) => ({ + cloudProvider: provider, + databaseType, + })) + .with(CloudProviderEnum.GCP, (provider) => ({ + cloudProvider: provider, + databaseType, + })) + .otherwise(() => ({ + cloudProvider: CloudProviderEnum.ON_PREMISE, + databaseType, + })) + ) + + const instanceTypeOptions: Value[] = + databaseInstanceTypes?.map((instanceType: ManagedDatabaseInstanceTypeResponse) => ({ + label: instanceType.name, + value: instanceType.name, + })) ?? [] + + useDocumentTitle('Resources - Create Database') + + const handleSubmit = (_data: DatabaseCreateResourcesData) => { + navigate({ to: `${creationFlowUrl}/summary`, search }) + } + + const handleBack = () => { + navigate({ to: `${creationFlowUrl}/general`, search }) + } + + if (!generalValues.name || !generalValues.type || !generalValues.version || !generalValues.mode) { + return ( + + ) + } + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/route.tsx new file mode 100644 index 00000000000..fbaa7be023d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/route.tsx @@ -0,0 +1,19 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { DatabaseCreationFlow } from '@qovery/domains/services/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + const creationFlowUrl = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/service/create/database` + + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary.tsx new file mode 100644 index 00000000000..77009be279b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary.tsx @@ -0,0 +1,22 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useAnnotationsGroups, useLabelsGroups } from '@qovery/domains/organizations/feature' +import { DatabaseStepSummary } from '@qovery/domains/services/feature' +import { serviceCreateParamsSchema } from '@qovery/shared/router' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database/summary' +)({ + component: Summary, + validateSearch: serviceCreateParamsSchema, +}) + +function Summary() { + const { organizationId = '' } = Route.useParams() + const { data: labelsGroup = [] } = useLabelsGroups({ organizationId }) + const { data: annotationsGroup = [] } = useAnnotationsGroups({ organizationId }) + + useDocumentTitle('Summary - Create Database') + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/index.tsx new file mode 100644 index 00000000000..ca4c3efd02e --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new.tsx new file mode 100644 index 00000000000..c169a10d938 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new.tsx @@ -0,0 +1,62 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { useEnvironment, useLifecycleTemplates } from '@qovery/domains/environments/feature' +import { ServiceNew } from '@qovery/domains/services/feature' +import { Heading, Icon, Link, LoaderSpinner, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/new' +)({ + component: RouteComponent, +}) + +function ServiceNewContent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + const { data: environment } = useEnvironment({ environmentId, suspense: true }) + const { data: availableTemplates = [] } = useLifecycleTemplates({ environmentId, suspense: true }) + const cloudProvider = environment?.cloud_provider?.provider + + useDocumentTitle('Create new service - Qovery') + + return ( +
+ + + Back to services list + +
+ Create new service +

+ Step into the Qovery service and embrace the power of collaboration to kickstart your next project. +

+
+ +
+ ) +} + +function RouteComponent() { + return ( + + + + } + > + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone.tsx new file mode 100644 index 00000000000..21463d1b892 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { PageSettingsDangerZoneFeature } from '@qovery/domains/environments/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules.tsx new file mode 100644 index 00000000000..e3813ca0962 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsDeploymentRules } from '@qovery/domains/environments/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/deployment-rules' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general.tsx new file mode 100644 index 00000000000..793bb96070a --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { PageEnvironmentGeneralSettingsForm } from '@qovery/domains/environments/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/general' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/index.tsx new file mode 100644 index 00000000000..5536c3b5a98 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/index.tsx @@ -0,0 +1,19 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments.tsx new file mode 100644 index 00000000000..c397bfdc102 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +import { PageSettingsPreviewEnvironmentsFeature } from '@qovery/domains/environments/feature' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/preview-environments' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route.tsx new file mode 100644 index 00000000000..6993de56ca7 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/route.tsx @@ -0,0 +1,60 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { Sidebar } from '@qovery/shared/ui' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, projectId, environmentId } = useParams({ strict: false }) + const pathSettings = `/organization/${organizationId}/project/${projectId}/environment/${environmentId}/settings` + + const generalLink = { + title: 'General', + to: `${pathSettings}/general`, + icon: 'gear' as const, + } + + const deploymentRulesLink = { + title: 'Deployment rules', + to: `${pathSettings}/deployment-rules`, + icon: 'browsers' as const, + } + + const previewEnvironmentsLink = { + title: 'Preview environments', + to: `${pathSettings}/preview-environments`, + icon: 'eye' as const, + } + + const dangerZoneLink = { + title: 'Danger zone', + to: `${pathSettings}/danger-zone`, + icon: 'skull' as const, + } + + const LINKS_SETTINGS = [generalLink, deploymentRulesLink, previewEnvironmentsLink, dangerZoneLink] + + return ( +
+ +
+
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx new file mode 100644 index 00000000000..f467b92b86d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables.tsx @@ -0,0 +1,107 @@ +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, + undefined, + 'Redeploy' + ) + } + /> +
+
+
+
+ { + toast( + 'SUCCESS', + 'Creation success', + 'You need to redeploy your environment for your changes to be applied.', + toasterCallback, + undefined, + 'Redeploy' + ) + }} + onEditVariable={() => { + toast( + 'SUCCESS', + 'Edition success', + 'You need to redeploy your environment for your changes to be applied.', + toasterCallback, + undefined, + '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, + undefined, + 'Redeploy' + ) + }} + /> +
+
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/index.tsx new file mode 100644 index 00000000000..b7ffe7c340f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/index.tsx @@ -0,0 +1,17 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '', projectId = '' } = useParams({ strict: false }) + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/overview.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/overview.tsx new file mode 100644 index 00000000000..f5de16de240 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/overview.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { EnvironmentsTable } from '@qovery/domains/environments/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/overview')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone.tsx new file mode 100644 index 00000000000..d0c240b1cb2 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone.tsx @@ -0,0 +1,97 @@ +import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' +import { EnvironmentModeEnum } from 'qovery-typescript-axios' +import { Suspense, useState } from 'react' +import { useDeleteProject, useProject } from '@qovery/domains/projects/feature' +import { BlockContentDelete, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/settings/danger-zone' +)({ + component: RouteComponent, +}) + +export interface BlockContentDeleteProps { + title: string + modalConfirmation?: { + title: string + name?: string + mode?: string + } + description?: string + className?: string + list?: { + text: string + icon?: string + }[] + ctaLabel?: string + ctaLoading?: boolean + callback?: () => void + customWidth?: string + customModalConfirmation?: () => void +} + +function ProjectDangerZone() { + useDocumentTitle('Danger zone - Project settings') + const navigate = useNavigate() + const { organizationId = '', projectId = '' } = useParams({ strict: false }) + const { data: project } = useProject({ organizationId, projectId, suspense: true }) + const { mutateAsync } = useDeleteProject() + const [loading, setLoading] = useState(false) + + const deleteProject = async () => { + setLoading(true) + + try { + await mutateAsync({ + organizationId, + projectId, + }) + setLoading(false) + navigate({ to: '/organization/$organizationId/overview', params: { organizationId } }) + } catch (error) { + console.error(error) + } + } + + return ( +
+ +
+ ) +} + +function RouteComponent() { + return ( + + + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/general.tsx new file mode 100644 index 00000000000..93c4ea5ecaa --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/general.tsx @@ -0,0 +1,117 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { type FormEventHandler, useEffect, useState } from 'react' +import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form' +import { useEditProject, useProject } from '@qovery/domains/projects/feature' +import { SettingsHeading } from '@qovery/shared/console-shared' +import { BlockContent, Button, InputText, InputTextArea, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/settings/general' +)({ + component: RouteComponent, +}) + +export interface PageProjectGeneralProps { + onSubmit: FormEventHandler + loading: boolean +} + +function PageProjectGeneral(props: PageProjectGeneralProps) { + const { onSubmit, loading } = props + const { control, formState, getValues } = useFormContext() + + return ( +
+
+ +
+ + ( + + )} + /> + ( + + )} + /> + +
+ +
+
+
+
+ ) +} + +function ProjectGeneralSettingsForm() { + useDocumentTitle('General - Project settings') + const { organizationId = '', projectId = '' } = useParams({ strict: false }) + const { data: project } = useProject({ organizationId, projectId, suspense: true }) + const { mutateAsync: editProject } = useEditProject() + const [loading, setLoading] = useState(false) + + const methods = useForm({ + mode: 'onChange', + }) + + useEffect(() => { + methods.reset({ + name: project?.name || '', + description: project?.description || '', + }) + }, [methods, project?.name, project?.description]) + + const onSubmit = methods.handleSubmit(async (data) => { + if (data && project) { + setLoading(true) + + try { + await editProject({ + projectId, + projectRequest: { + name: data['name'], + description: data['description'], + }, + }) + } catch (error) { + console.error(error) + } + setLoading(false) + } + }) + + return ( + + + + ) +} + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/index.tsx new file mode 100644 index 00000000000..880391c9f0b --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/index.tsx @@ -0,0 +1,21 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/settings/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { projectId = '', organizationId = '' } = useParams({ strict: false }) + + if (!projectId) { + return null + } + + return ( + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/route.tsx new file mode 100644 index 00000000000..4ef2e852c9d --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/settings/route.tsx @@ -0,0 +1,47 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { Sidebar } from '@qovery/shared/ui' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/settings')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId, projectId } = useParams({ strict: false }) + + const pathSettings = `/organization/${organizationId}/project/${projectId}/settings` + + const generalLink = { + title: 'General', + to: `${pathSettings}/general`, + icon: 'gear' as const, + } + + const dangerZoneLink = { + title: 'Danger zone', + to: `${pathSettings}/danger-zone`, + icon: 'skull' as const, + } + + const LINKS_SETTINGS = [generalLink, dangerZoneLink] + + return ( +
+ +
+
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx new file mode 100644 index 00000000000..bf9fe3ca39f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/variables.tsx @@ -0,0 +1,53 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { Suspense } from 'react' +import { VariableList, VariablesActionToolbar } from '@qovery/domains/variables/feature' +import { Heading, LoaderSpinner, Section, toast } from '@qovery/shared/ui' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/project/$projectId/variables')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { projectId = '' } = useParams({ strict: false }) + + return ( + + + + } + > +
+
+
+
+ Project variables + toast('SUCCESS', 'Creation success')} + /> +
+
+
+
+ { + toast('SUCCESS', 'Creation success') + }} + onEditVariable={() => { + toast('SUCCESS', 'Edition success') + }} + onDeleteVariable={() => { + toast('SUCCESS', 'Deletion success') + }} + /> +
+
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/route.tsx new file mode 100644 index 00000000000..07f7fb82443 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/route.tsx @@ -0,0 +1,82 @@ +import { Outlet, createFileRoute, useParams } from '@tanstack/react-router' +import { ClusterStateEnum as ClusterState, type ClusterStateEnum } from 'qovery-typescript-axios' +import { Suspense, useMemo } from 'react' +import { memo } from 'react' +import { ClusterDeploymentProgressCard, useClusterStatuses, useClusters } from '@qovery/domains/clusters/feature' +import { LoaderSpinner } from '@qovery/shared/ui' +import { StatusWebSocketListener } from '@qovery/shared/util-web-sockets' +import { queries } from '@qovery/state/util-queries' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId')({ + component: RouteComponent, + loader: async ({ context, params }) => { + const { organizationId } = params + // Preload data (organization, clusters and projects) without waiting for the queries to complete + context.queryClient.prefetchQuery({ + ...queries.organizations.details({ organizationId }), + }) + context.queryClient.prefetchQuery({ + ...queries.clusters.list({ organizationId }), + }) + context.queryClient.prefetchQuery({ + ...queries.projects.list({ organizationId }), + }) + }, +}) + +const Loader = () => { + return ( +
+ +
+ ) +} + +const StatusWebSocketListenerMemo = memo(StatusWebSocketListener) + +const isDeployingStatus = (status?: ClusterStateEnum): boolean => + status === ClusterState.DEPLOYMENT_QUEUED || status === ClusterState.DEPLOYING + +function RouteComponent() { + // @ts-expect-error-next-line Because we do not have versionId param for now + const { organizationId = '', projectId = '', environmentId = '', versionId = '' } = useParams({ strict: false }) + const { data: clusters } = useClusters({ organizationId }) + const { data: clusterStatuses } = useClusterStatuses({ organizationId, enabled: !!organizationId }) + + const deployingClusters = useMemo(() => { + if (!clusters || !clusterStatuses) return [] + return clusters.filter((cluster) => { + const status = clusterStatuses.find(({ cluster_id }) => cluster_id === cluster.id)?.status + return isDeployingStatus(status) + }) + }, [clusters, clusterStatuses]) + + return ( + <> + }> + + + + {/** + * Here we are limited by the websocket API which requires a clusterId + * We need to instantiate one hook per clusterId to get the complete environment statuses of the page + */ + clusters?.map( + ({ id }) => + organizationId && ( + + ) + )} + {deployingClusters && deployingClusters.length > 0 && ( + + )} + + ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/ai-copilot.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/ai-copilot.tsx new file mode 100644 index 00000000000..404a137f1a1 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/ai-copilot.tsx @@ -0,0 +1,20 @@ +import { createFileRoute, useParams } from '@tanstack/react-router' +import { useOrganization } from '@qovery/domains/organizations/feature' +import { AICopilotSettings } from '@qovery/shared/devops-copilot/feature' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/ai-copilot')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '' } = useParams({ strict: false }) + useDocumentTitle('AI Copilot - Organization settings') + const { data: organization } = useOrganization({ organizationId }) + + if (!organization) { + return null + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/api-token.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/api-token.tsx new file mode 100644 index 00000000000..65f87075bbd --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/api-token.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsApiToken } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/api-token')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-details.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-details.tsx new file mode 100644 index 00000000000..59d07e52ea3 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-details.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsBillingDetails } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/billing-details')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-summary.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-summary.tsx new file mode 100644 index 00000000000..1018fc01b24 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/billing-summary.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsBillingSummary } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/billing-summary')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/cloud-credentials.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/cloud-credentials.tsx new file mode 100644 index 00000000000..f177678a8f0 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/cloud-credentials.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsCloudCredentials } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/cloud-credentials')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/container-registries.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/container-registries.tsx new file mode 100644 index 00000000000..4040e3e596a --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/container-registries.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsContainerRegistries } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/container-registries')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/danger-zone.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/danger-zone.tsx new file mode 100644 index 00000000000..3fec6c384aa --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/danger-zone.tsx @@ -0,0 +1,24 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' +import { SettingsDangerZone } from '@qovery/domains/organizations/feature' +import { useUserRole } from '@qovery/shared/iam/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/danger-zone')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '' } = useParams({ strict: false }) + const { roles, loading } = useUserRole() + + const isOrganizationAdmin = roles.some((role) => role.includes(`organization:${organizationId}:admin`)) + + if (loading) { + return null + } + + if (!isOrganizationAdmin) { + return + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/general.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/general.tsx new file mode 100644 index 00000000000..13fef9b5c2f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/general.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsGeneral } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/general')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/git-repository-access.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/git-repository-access.tsx new file mode 100644 index 00000000000..ad999ca1db8 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/git-repository-access.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsGitRepositoryAccess } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/git-repository-access')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/helm-repositories.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/helm-repositories.tsx new file mode 100644 index 00000000000..7713bcf3e7f --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/helm-repositories.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsHelmRepositories } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/helm-repositories')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/index.tsx new file mode 100644 index 00000000000..20166bd0ffb --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/index.tsx @@ -0,0 +1,15 @@ +import { Navigate, createFileRoute, useParams } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId } = useParams({ strict: false }) + + if (!organizationId) { + return null + } + + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/labels-annotations.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/labels-annotations.tsx new file mode 100644 index 00000000000..3dcd4de991c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/labels-annotations.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsLabelsAnnotations } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/labels-annotations')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/members.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/members.tsx new file mode 100644 index 00000000000..2fdebeef19c --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/members.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsMembers } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/members')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/edit/$roleId.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/edit/$roleId.tsx new file mode 100644 index 00000000000..80a0d1f0ca6 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/edit/$roleId.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsRolesEdit } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/roles/edit/$roleId')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/index.tsx new file mode 100644 index 00000000000..d389f5cd9ff --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/roles/index.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsRoles } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/roles/')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx new file mode 100644 index 00000000000..be2b95252e3 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx @@ -0,0 +1,167 @@ +import { Outlet, createFileRoute, useParams, useRouterState } from '@tanstack/react-router' +import { useUserRole } from '@qovery/shared/iam/feature' +import { Sidebar } from '@qovery/shared/ui' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { organizationId = '' } = useParams({ strict: false }) + const { + location: { pathname }, + } = useRouterState() + const { roles } = useUserRole() + + const isOrganizationAdmin = roles.some((role) => role.includes(`organization:${organizationId}:admin`)) + + const pathSettings = `/organization/${organizationId}/settings` + + const generalLink = { + title: 'General', + to: `${pathSettings}/general`, + icon: 'gear' as const, + } + + const labelsAnnotationsLink = { + title: 'Labels & annotations', + to: `${pathSettings}/labels-annotations`, + icon: 'tags' as const, + } + + const teamLink = { + type: 'group', + title: 'Team', + icon: 'users' as const, + children: [ + { title: 'Members', to: `${pathSettings}/members` }, + { title: 'Roles & permissions', to: `${pathSettings}/roles` }, + ], + } + + const billingPlansLink = { + type: 'group', + title: 'Billing & plans', + icon: 'credit-card' as const, + children: [ + { title: 'Billing summary', to: `${pathSettings}/billing-summary` }, + { title: 'Billing details', to: `${pathSettings}/billing-details` }, + ], + } + + const containerRegistriesLink = { + title: 'Container registries', + to: `${pathSettings}/container-registries`, + icon: 'box' as const, + } + + const helmRepositoriesLink = { + title: 'Helm repositories', + to: `${pathSettings}/helm-repositories`, + icon: 'plug' as const, + } + + const cloudCredentialsLink = { + title: 'Cloud credentials', + to: `${pathSettings}/cloud-credentials`, + icon: 'key' as const, + } + + const gitRepositoriesAccessLink = { + title: 'Git repositories access', + to: `${pathSettings}/git-repository-access`, + icon: 'git-alt' as const, + iconStyle: 'brands' as const, + } + + const webhookLink = { + title: 'Webhook', + to: `${pathSettings}/webhook`, + icon: 'webhook' as const, + } + + const apiTokenLink = { + title: 'API token', + to: `${pathSettings}/api-token`, + icon: 'rectangle-api' as const, + } + + const aiCopilotLink = { + title: 'AI Copilot', + to: `${pathSettings}/ai-copilot`, + icon: 'sparkles' as const, + } + + const dangerZoneLink = { + title: 'Danger zone', + to: `${pathSettings}/danger-zone`, + icon: 'skull' as const, + } + + const LINKS_SETTINGS = [ + generalLink, + teamLink, + billingPlansLink, + labelsAnnotationsLink, + containerRegistriesLink, + helmRepositoriesLink, + cloudCredentialsLink, + gitRepositoriesAccessLink, + webhookLink, + apiTokenLink, + aiCopilotLink, + ...(isOrganizationAdmin ? [dangerZoneLink] : []), + ] + + return ( +
+ +
+
+ +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/webhook.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/webhook.tsx new file mode 100644 index 00000000000..bce220469a0 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/webhook.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsWebhook } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/webhook')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/index.tsx b/apps/console-v5/src/routes/_authenticated/organization/index.tsx new file mode 100644 index 00000000000..c596297db62 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_authenticated/organization/')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+
+
+ {/* EMPTY FOR NOW */} + {/* TODO: Add organization list or something */} +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/route.tsx new file mode 100644 index 00000000000..89aa1572e87 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/route.tsx @@ -0,0 +1,517 @@ +import { type IconName } from '@fortawesome/fontawesome-common-types' +import { Outlet, createFileRoute, useLocation, useMatches, useParams } from '@tanstack/react-router' +import posthog from 'posthog-js' +import { Suspense, useEffect, useLayoutEffect, useRef, useState } from 'react' +import { useServiceSummary } from '@qovery/domains/services/feature' +import { DevopsCopilotContext } from '@qovery/shared/devops-copilot/context' +import { DevopsCopilotTrigger } from '@qovery/shared/devops-copilot/feature' +import { ErrorBoundary, Icon, LoaderSpinner, Navbar } from '@qovery/shared/ui' +import { queries } from '@qovery/state/util-queries' +import Header from '../../../app/components/header/header' +import { type FileRouteTypes } from '../../../routeTree.gen' + +export const Route = createFileRoute('/_authenticated/organization')({ + component: OrganizationRoute, + loader: async ({ context }) => { + // Preload data (organizations) without waiting for the queries to complete + context.queryClient.prefetchQuery({ + ...queries.organizations.list, + }) + context.queryClient.prefetchQuery({ + ...queries.user.account, + }) + }, +}) + +type NavigationContext = { + type: 'organization' | 'cluster' | 'environment' | 'service' | 'project' + params: Record + tabs: NavigationTab[] +} + +type NavigationTab = { + id: string + label: string + iconName: IconName + routeId: string +} + +const ORGANIZATION_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: '/_authenticated/organization/$organizationId/overview', + }, + { + id: 'audit-logs', + label: 'Audit Logs', + iconName: 'lock-keyhole', + routeId: '/_authenticated/organization/$organizationId/audit-logs', + }, + { + id: 'alerts', + label: 'Alerts', + iconName: 'light-emergency', + routeId: '/_authenticated/organization/$organizationId/alerts', + }, + { + id: 'clusters', + label: 'Clusters', + iconName: 'cube', + routeId: '/_authenticated/organization/$organizationId/clusters', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: '/_authenticated/organization/$organizationId/settings', + }, +] + +const CLUSTER_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: '/_authenticated/organization/$organizationId/cluster/$clusterId/overview', + }, + { + id: 'cluster-logs', + label: 'Cluster Logs', + iconName: 'scroll', + routeId: '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs', + }, + { + id: 'cloud-shell', + label: 'Cloud shell', + iconName: 'terminal', + routeId: '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: '/_authenticated/organization/$organizationId/cluster/$clusterId/settings', + }, +] + +const PROJECT_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/overview', + }, + { + id: 'deployment-rules', + label: 'Deployment rules', + iconName: 'ruler', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/deployment-rules', + }, + { + id: 'variables', + label: 'Variables', + iconName: 'key', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/variables', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/settings', + }, +] + +const ENVIRONMENT_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview', + }, + { + id: 'deployments', + label: 'Deployments', + iconName: 'rocket', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments', + }, + { + id: 'variables', + label: 'Variables', + iconName: 'key', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings', + }, +] + +const SERVICE_TABS: NavigationTab[] = [ + { + id: 'overview', + label: 'Overview', + iconName: 'table-layout', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview', + }, + { + id: 'deployments', + label: 'Deployments', + iconName: 'rocket', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments', + }, + { + id: 'service-logs', + label: 'Service Logs', + iconName: 'scroll', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/service-logs', + }, + { + id: 'cloud-shell', + label: 'Cloud shell', + iconName: 'terminal', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell', + }, + { + id: 'monitoring', + label: 'Monitoring', + iconName: 'chart-column', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring', + }, + { + id: 'variables', + label: 'Variables', + iconName: 'key', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/variables', + }, + { + id: 'settings', + label: 'Settings', + iconName: 'gear-complex', + routeId: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/settings', + }, +] + +function createRoutePatternRegex(routeIdPattern: string): RegExp { + const patternPath = routeIdPattern.replace('/_authenticated/organization', '/organization') + return new RegExp('^' + patternPath.replace(/\$(\w+)/g, '[^/]+') + '(/.*)?$') +} + +/** + * To add a new navigation context: + * 1. Create a new tabs array (example: ENVIRONMENT_TABS) with routeId using the full route ID pattern + * 2. Add a new entry to NAVIGATION_CONTEXTS with: + * - type: the context type (must match NavigationContext['type']) + * - routeIdPattern: the route ID pattern to match (example: '/_authenticated/organization/$organizationId/environment/$environmentId') + * - tabs: the tabs array for this context + * - paramNames: array of parameter names used in the route + * + * The order matters: more specific patterns should come first (example: cluster before organization) + */ +const NAVIGATION_CONTEXTS: Array<{ + type: NavigationContext['type'] + routeIdPattern: string + tabs: NavigationTab[] + paramNames: string[] +}> = [ + { + type: 'service', + routeIdPattern: + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId', + tabs: SERVICE_TABS, + paramNames: ['organizationId', 'projectId', 'environmentId', 'serviceId'], + }, + { + type: 'environment', + routeIdPattern: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId', + tabs: ENVIRONMENT_TABS, + paramNames: ['organizationId', 'projectId', 'environmentId'], + }, + { + type: 'project', + routeIdPattern: '/_authenticated/organization/$organizationId/project/$projectId', + tabs: PROJECT_TABS, + paramNames: ['organizationId', 'projectId'], + }, + { + type: 'cluster', + routeIdPattern: '/_authenticated/organization/$organizationId/cluster/$clusterId', + tabs: CLUSTER_TABS, + paramNames: ['organizationId', 'clusterId'], + }, + { + type: 'organization', + routeIdPattern: '/_authenticated/organization/$organizationId', + tabs: ORGANIZATION_TABS, + paramNames: ['organizationId'], + }, +] + +function useNavigationContext(): NavigationContext | null { + const location = useLocation() + const params = useParams({ strict: false }) + const pathname = location.pathname + const { data: service } = useServiceSummary({ + environmentId: params.environmentId, + serviceId: params.serviceId, + enabled: Boolean(params.environmentId) && Boolean(params.serviceId), + }) + + for (const context of NAVIGATION_CONTEXTS) { + const patternRegex = createRoutePatternRegex(context.routeIdPattern) + + if (patternRegex.test(pathname)) { + const extractedParams: Record = {} + let hasAllParams = true + + for (const paramName of context.paramNames) { + // @ts-expect-error-next-line paramName should be typed to be used as a key. + const value = params[paramName] + if (typeof value === 'string' && value.length > 0) { + extractedParams[paramName] = value + } else { + hasAllParams = false + break + } + } + + if (hasAllParams) { + const isDatabase = service?.serviceType === 'DATABASE' + const isManagedDatabase = isDatabase && service.mode === 'MANAGED' + + // Managed databases should not have cloud shell access. + // Databases should not expose the variables tab. + const tabs = + context.type === 'service' + ? context.tabs.filter( + (tab) => !(isDatabase && tab.id === 'variables') && !(isManagedDatabase && tab.id === 'cloud-shell') + ) + : context.tabs + + return { + type: context.type, + params: extractedParams, + tabs, + } + } + } + } + + const organizationId = params.organizationId + if (typeof organizationId === 'string' && organizationId.length > 0 && pathname.startsWith('/organization/')) { + return { + type: 'organization', + params: { organizationId }, + tabs: ORGANIZATION_TABS, + } + } + + return null +} + +function getBaseRouteSegment(routePath: string): string | null { + const segments = routePath.split('/').filter(Boolean) + const lastSegment = segments[segments.length - 1] + if (!lastSegment) return null + + if (lastSegment.endsWith('s') && lastSegment.length > 1) { + return lastSegment.slice(0, -1) + } + return null +} + +function matchesTabRoute(pathname: string, tabPath: string): boolean { + if (pathname === tabPath || pathname.startsWith(tabPath + '/')) { + return true + } + + const baseSegment = getBaseRouteSegment(tabPath) + if (baseSegment) { + const basePattern = tabPath.replace(`/${baseSegment}s`, `/${baseSegment}/`) + if (pathname.startsWith(basePattern)) { + return true + } + } + + return false +} + +function useActiveTabId(context: NavigationContext | null): string { + const location = useLocation() + const pathname = location.pathname + + if (!context) { + return '/' + } + + for (const tab of context.tabs) { + const tabPath = buildRoutePath(tab.routeId, context.params) + // Match the tab route with the pathname, including the base route segment + // Example: /organization/123/clusters -> /organization/123/cluster/new + if (matchesTabRoute(pathname, tabPath)) { + return tab.id + } + } + + return '/' +} + +function buildRoutePath(routeId: string, params: Record): string { + let path = routeId.replace('/_authenticated/organization', '/organization') + for (const [key, value] of Object.entries(params)) { + path = path.replace(`$${key}`, value) + } + return path +} + +function NavigationBar({ context }: { context: NavigationContext }) { + return ( + <> + {context.tabs.map((tab) => { + const path = buildRoutePath(tab.routeId, context.params) + return ( + + + {tab.label} + + ) + })} + + ) +} + +const fullWidthRouteIds: FileRouteTypes['id'][] = [ + '/_authenticated/organization/$organizationId/alerts', + '/_authenticated/organization/$organizationId/cluster/$clusterId/cluster-logs', + '/_authenticated/organization/$organizationId/cluster/$clusterId/cloud-shell', + '/_authenticated/organization/$organizationId/cluster/$clusterId/settings', + '/_authenticated/organization/$organizationId/project/$projectId/settings', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings', + '/_authenticated/organization/$organizationId/settings', + '/_authenticated/organization/$organizationId/audit-logs', + '/_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/service-logs', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/deployments/logs/$executionId', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/cloud-shell', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployment/$deploymentId/pre-check-logs', +] + +function useFullWidthLayout(): boolean { + const matches = useMatches() + return matches.some((match) => + fullWidthRouteIds.some((routeId) => match.routeId === routeId || match.routeId?.startsWith(routeId + '/')) + ) +} + +const bypassLayoutRouteIds: FileRouteTypes['id'][] = [ + '/_authenticated/organization/$organizationId/cluster/create/$slug', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/create/metric/$metric', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/monitoring/alerts/$alertId/edit', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/$slug', + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/create/database', +] + +function useBypassLayout(): boolean { + const matches = useMatches() + return matches.some((match) => + bypassLayoutRouteIds.some((routeId) => match.routeId === routeId || match.routeId?.startsWith(routeId + '/')) + ) +} + +function MainLoader() { + return ( +
+ +
+ ) +} + +function OrganizationRoute() { + const navigationContext = useNavigationContext() + const activeTabId = useActiveTabId(navigationContext) + const needsFullWidth = useFullWidthLayout() + const bypassLayout = useBypassLayout() + const location = useLocation() + const { organizationId = '' } = useParams({ strict: false }) + const scrollContainerRef = useRef(null) + const [devopsCopilotOpen, setDevopsCopilotOpen] = useState(false) + const sendMessageRef = useRef<((message: string, createNewChat?: boolean) => void) | null>(null) + + // Keep group-scoped flags aligned with the active organization + useEffect(() => { + if (!organizationId) { + return + } + + posthog.group('organization_id', organizationId) + posthog.reloadFeatureFlags() + }, [organizationId]) + + useLayoutEffect(() => { + const scrollContainer = scrollContainerRef.current + + if (scrollContainer) { + scrollContainer.scrollTop = 0 + } + }, [location.pathname]) + + if (bypassLayout) { + return ( + + + + + ) + } + + return ( + +
+ {/* TODO: Conflicts with body main:not(.h-screen, .layout-onboarding) */} +
+ +
+ + }> + <> +
+ + {navigationContext && } + +
+ +
+ +
+ +
+ +
+
+ +
+ ) +} diff --git a/apps/console-v5/src/routes/index.tsx b/apps/console-v5/src/routes/index.tsx new file mode 100644 index 00000000000..879c850c442 --- /dev/null +++ b/apps/console-v5/src/routes/index.tsx @@ -0,0 +1,44 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { Navigate, createFileRoute } from '@tanstack/react-router' +import { useOrganizations } from '@qovery/domains/organizations/feature' +import { queries } from '@qovery/state/util-queries' + +export const Route = createFileRoute('/')({ + component: Index, + loader: async ({ context }) => { + // Preload data (organizations) without waiting for the queries to complete + if (context.auth.isAuthenticated) { + context.queryClient.prefetchQuery({ + ...queries.organizations.list, + }) + } + }, +}) + +function Index() { + const { isAuthenticated } = useAuth0() + const { data: organizations = [] } = useOrganizations({ enabled: isAuthenticated, suspense: true }) + + // Redirect to latest selected organization + const currentOrganizationId = localStorage.getItem('currentOrganizationId') || '' + const latestSelectedOrganization = + organizations.find((organization) => organization.id === currentOrganizationId) || organizations[0] + if (latestSelectedOrganization) { + return ( + + ) + } + + if (!isAuthenticated) { + return + } + + return ( +
+

Welcome Home!

+
+ ) +} diff --git a/apps/console-v5/src/routes/login/auth0-callback.tsx b/apps/console-v5/src/routes/login/auth0-callback.tsx new file mode 100644 index 00000000000..a17ad36355c --- /dev/null +++ b/apps/console-v5/src/routes/login/auth0-callback.tsx @@ -0,0 +1,85 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { Navigate, createFileRoute, useNavigate } from '@tanstack/react-router' +import axios from 'axios' +import { useEffect } from 'react' +import { useOrganizations } from '@qovery/domains/organizations/feature' +import { useUserSignUp } from '@qovery/domains/users-sign-up/feature' +import { LoadingScreen } from '@qovery/shared/ui' +import { QOVERY_API } from '@qovery/shared/util-node-env' +import { useAuthInterceptor } from '@qovery/shared/utils' +import { consumePendingReturnTo } from '../../auth/auth0' + +type Auth0CallbackSearch = { + error?: string + error_description?: string +} + +export const Route = createFileRoute('/login/auth0-callback')({ + component: RouteComponent, + validateSearch: (search: Record): Auth0CallbackSearch => ({ + error: (search.error as string) || undefined, + error_description: (search.error_description as string) || undefined, + }), +}) + +function useRedirectIfLogged() { + const navigate = useNavigate() + const { isAuthenticated } = useAuth0() + const { data: organizations = [], isFetched: isFetchedOrganizations } = useOrganizations({ + enabled: isAuthenticated, + }) + const { refetch: refetchUserSignUp } = useUserSignUp({ enabled: false }) + + useEffect(() => { + async function fetchData() { + if (!isFetchedOrganizations) { + return + } + + // User has at least 1 organization attached + if (organizations.length > 0) { + const returnTo = consumePendingReturnTo() + if (returnTo) { + navigate({ to: returnTo }) + } else { + navigate({ to: '/organization/$organizationId/overview', params: { organizationId: organizations[0]?.id } }) + } + } else { + const { data: userSignUp } = await refetchUserSignUp() + if (userSignUp?.dx_auth) { + navigate({ to: '/onboarding/project' }) + } else { + navigate({ to: '/onboarding/personalize' }) + } + } + } + + if (isAuthenticated) { + fetchData() + } + }, [navigate, isAuthenticated, organizations, isFetchedOrganizations, refetchUserSignUp]) +} + +function PageRedirectLogin() { + const { error, error_description } = Route.useSearch() + useAuthInterceptor(axios, QOVERY_API) + useRedirectIfLogged() + + if (error != null) { + const errorDescription = error_description || 'No description available' + + // Handle specific OIDC / SAML issue: the domain provided by the user doesn't exist on Auth0 side + if (error === 'invalid_request' && errorDescription.includes('')) { + sessionStorage.setItem('auth0_error', 'Invalid Enterprise SSO Domain Name') + sessionStorage.setItem('auth0_error_description', 'The domain name provided is not authorized') + } + + return + } + + return +} + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/login/index.tsx b/apps/console-v5/src/routes/login/index.tsx new file mode 100644 index 00000000000..54f8dcfc880 --- /dev/null +++ b/apps/console-v5/src/routes/login/index.tsx @@ -0,0 +1,918 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' +import { useState } from 'react' +import { Controller, FormProvider, useForm } from 'react-hook-form' +import { z } from 'zod' +import { useAuth0Error } from '@qovery/pages/login' +import { AuthEnum, useAuth } from '@qovery/shared/auth' +import { IconEnum } from '@qovery/shared/enums' +import { Button, DropdownMenu, Heading, Icon, InputTextSmall, Link, LogoBrandedIcon, Section } from '@qovery/shared/ui' + +const PANEL_TEXT_CLASS_NAME = 'text-[var(--neutral-invert-12)] dark:text-[var(--neutral-12)]' +const PARTNER_LOGO_CLASS_NAME = + 'opacity-50 text-[var(--neutral-invert-12)] dark:text-[var(--neutral-12)] [&_path]:fill-current' +const CERTIFICATION_LOGO_CLASS_NAME = + "opacity-50 text-[var(--neutral-invert-12)] dark:text-[var(--neutral-12)] [&_path]:fill-current [&_path[stroke='white']]:stroke-current" + +const loginSearchParamsSchema = z.object({ + redirect: z.string().optional(), +}) + +function getSafeRedirect(redirectPath?: string) { + if (!redirectPath || redirectPath.startsWith('/login')) { + return '/' + } + + return redirectPath +} + +export const Route = createFileRoute('/login/')({ + validateSearch: loginSearchParamsSchema, + beforeLoad: ({ context, search }) => { + // Redirect if already authenticated + if (context.auth.isAuthenticated) { + throw redirect({ to: getSafeRedirect(search.redirect) }) + } + }, + component: RouteComponent, +}) + +export function Login() { + const { authLogin } = useAuth() + const displayInvitation = false + const [ssoFormVisible, setSsoFormVisible] = useState(false) + const { auth0Error, setAuth0Error } = useAuth0Error() + const [loading, setLoading] = useState<{ provider: string; active: boolean } | undefined>() + + const methods = useForm({ + mode: 'onChange', + defaultValues: { + ssoDomain: '', + }, + }) + + const onClickAuthLogin = async (provider: string) => { + setLoading({ + provider: provider, + active: true, + }) + try { + // XXX: Cleanup legacy jwtToken cookie which can cause RequestHeaderSectionTooLarge problems + // https://qovery.atlassian.net/browse/FRT-1086 + // https://github.com/Qovery/console/pull/1188 + if (document.cookie.split(';').some((item) => item.trim().startsWith('jwtToken='))) { + document.cookie = 'jwtToken=; Max-Age=-99999999; domain=.qovery.com' + } + await authLogin(provider) + } catch (error) { + console.error(error) + } + } + + const validateAndConnect = () => { + // Split domain by dots and validate each part + const domainWithoutDots = methods.getValues('ssoDomain').trim().replace(/\./g, '') + + // Then trigger the auth login with OIDC + onClickAuthLogin(domainWithoutDots) + } + + return ( +
+ +
+
+
+ {!displayInvitation ? ( +

Connect to Qovery

+ ) : ( +
{/* */}
+ )} + +

+ By registering and using Qovery, you agree to the processing of your personal data by Qovery as described + in the{' '} + + Privacy Policy + + . +

+
+ {!ssoFormVisible ? ( + <> + + + + + + + + + onClickAuthLogin(AuthEnum.MICROSOFT)} + icon={} + > + Continue with Microsoft + + onClickAuthLogin(AuthEnum.GITLAB)} + icon={} + > + Continue with Gitlab + + onClickAuthLogin(AuthEnum.BITBUCKET)} + icon={} + > + Continue with Bitbucket + + + + +
+ { + setSsoFormVisible(true) + setAuth0Error(null) + }} + color="sky" + > + Use Enterprise Single Sign-On + +
+ + {auth0Error && ( +
+

{auth0Error.error}

+

{auth0Error.error_description}

+
+ )} + + ) : ( + +
+ Enterprise Single Sign-On +

+ Enter your company domain to connect with SSO +

+
+
+ ( + + )} + /> +
+ +
+ + { + setSsoFormVisible(false) + methods.reset() + }} + className="mt-6 flex max-w-max gap-1" + color="sky" + > + + Go back + +
+
+ )} +
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+ ) +} + +function RouteComponent() { + return +} diff --git a/apps/console-v5/tailwind.config.js b/apps/console-v5/tailwind.config.js new file mode 100644 index 00000000000..793cf069348 --- /dev/null +++ b/apps/console-v5/tailwind.config.js @@ -0,0 +1,15 @@ +const { createGlobPatternsForDependencies } = require('@nx/react/tailwind') +const { join } = require('path') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + presets: [require('../../tailwind-workspace-preset.js')], + content: [ + join(__dirname, '{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/apps/console-v5/tsconfig.app.json b/apps/console-v5/tsconfig.app.json new file mode 100644 index 00000000000..c6e52c887ef --- /dev/null +++ b/apps/console-v5/tsconfig.app.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node", "@nx/react/typings/cssmodule.d.ts", "@nx/react/typings/image.d.ts", "vite/client"] + }, + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/apps/console-v5/tsconfig.json b/apps/console-v5/tsconfig.json new file mode 100644 index 00000000000..fe609d7af34 --- /dev/null +++ b/apps/console-v5/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["vite/client"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../tsconfig.base.json" +} diff --git a/apps/console-v5/vite.config.ts b/apps/console-v5/vite.config.ts new file mode 100644 index 00000000000..d7685a6f7bb --- /dev/null +++ b/apps/console-v5/vite.config.ts @@ -0,0 +1,77 @@ +/// +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin' +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin' +import { tanstackRouter } from '@tanstack/router-plugin/vite' +import react from '@vitejs/plugin-react' +import { join } from 'path' +import { defineConfig, loadEnv } from 'vite' +import { viteStaticCopy } from 'vite-plugin-static-copy' + +export default defineConfig(({ mode }) => { + const clientEnv = loadEnv(mode, process.cwd(), '') + + return { + root: __dirname, + cacheDir: '../../node_modules/.vite/apps/console-v5', + server: { + port: 4200, + host: 'localhost', + cors: { + origin: '*', + methods: ['GET'], + allowedHeaders: ['Content-Type', 'Authorization'], + }, + fs: { + allow: ['../..'], + }, + }, + preview: { + port: 4200, + host: 'localhost', + }, + define: { + 'process.env': JSON.stringify(clientEnv), + }, + plugins: [ + tanstackRouter({ + target: 'react', + autoCodeSplitting: true, + }), + react(), + nxViteTsPaths(), + nxCopyAssetsPlugin(['*.md']), + viteStaticCopy({ + targets: [ + { + src: '../../node_modules/@awesome.me/kit-22f4eef36a/icons/webfonts/*', + dest: 'assets/fonts/font-awesome', + }, + { + src: '../../libs/shared/ui/src/lib/assets/**/*', + dest: 'assets', + }, + ], + }), + ], + css: { + preprocessorOptions: { + scss: { + includePaths: [join(__dirname, '../../libs/shared/ui/src/lib/styles')], + additionalData: '', + }, + }, + }, + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + build: { + outDir: '../../dist/apps/console-v5', + emptyOutDir: true, + reportCompressedSize: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + }, + } +}) diff --git a/apps/console/src/app/router/main.router.tsx b/apps/console/src/app/router/main.router.tsx index 0e8e10480c5..7343c65e812 100644 --- a/apps/console/src/app/router/main.router.tsx +++ b/apps/console/src/app/router/main.router.tsx @@ -7,11 +7,8 @@ import { PageEnvironments } from '@qovery/pages/environments' import { PageEvents } from '@qovery/pages/events' import { PageEnvironmentLogs } from '@qovery/pages/logs/environment' import { PageInfraLogs } from '@qovery/pages/logs/infra' -import { PageOnboarding } from '@qovery/pages/onboarding' import { OverviewPage } from '@qovery/pages/overview/feature' import { - PageApplicationCreateFeature, - PageDatabaseCreateFeature, PageHelmCreateFeature, PageJobCreateFeature, PageServices, @@ -19,9 +16,8 @@ import { } from '@qovery/pages/services' import { PageAlerting, PageSettings } from '@qovery/pages/settings' import { PageUser } from '@qovery/pages/user' -import { AcceptInvitationFeature, GithubApplicationCallbackFeature } from '@qovery/shared/console-shared' +import { GithubApplicationCallbackFeature } from '@qovery/shared/console-shared' import { - ACCEPT_INVITATION_URL, ALERTING_URL, APPLICATION_URL, AUDIT_LOGS_URL, @@ -34,14 +30,9 @@ import { ENVIRONMENT_LOGS_URL, GITHUB_APPLICATION_CALLBACK_URL, INFRA_LOGS_URL, - ONBOARDING_URL, ORGANIZATION_URL, OVERVIEW_URL, - SERVICES_APPLICATION_CREATION_URL, - SERVICES_APPLICATION_TEMPLATE_CREATION_URL, SERVICES_CRONJOB_CREATION_URL, - SERVICES_DATABASE_CREATION_URL, - SERVICES_DATABASE_TEMPLATE_CREATION_URL, SERVICES_HELM_CREATION_URL, SERVICES_HELM_TEMPLATE_CREATION_URL, SERVICES_LIFECYCLE_CREATION_URL, @@ -64,24 +55,12 @@ interface RouterProps { } export const ROUTER: RouterProps[] = [ - { - path: `${ONBOARDING_URL}/*`, - component: , - protected: true, - layout: false, - }, { path: `${GITHUB_APPLICATION_CALLBACK_URL}`, component: , protected: true, layout: false, }, - { - path: `${ACCEPT_INVITATION_URL}`, - component: , - protected: true, - layout: false, - }, { path: `${USER_URL}/*`, component: , @@ -127,18 +106,6 @@ export const ROUTER: RouterProps[] = [ protected: true, layout: true, }, - { - path: `${SERVICES_URL()}${SERVICES_DATABASE_CREATION_URL}/*`, - component: , - protected: true, - layout: false, - }, - { - path: `${SERVICES_URL()}${SERVICES_DATABASE_TEMPLATE_CREATION_URL()}/*`, - component: , - protected: true, - layout: false, - }, { path: `${SERVICES_URL()}${SERVICES_CRONJOB_CREATION_URL}/*`, component: , @@ -157,18 +124,6 @@ export const ROUTER: RouterProps[] = [ protected: true, layout: false, }, - { - path: `${SERVICES_URL()}${SERVICES_APPLICATION_CREATION_URL}/*`, - component: , - protected: true, - layout: false, - }, - { - path: `${SERVICES_URL()}${SERVICES_APPLICATION_TEMPLATE_CREATION_URL()}/*`, - component: , - protected: true, - layout: false, - }, { path: `${SERVICES_URL()}${SERVICES_HELM_CREATION_URL}/*`, component: , diff --git a/apps/console/webpack.config.js b/apps/console/webpack.config.js index 228df299c4b..ef60734aeb1 100644 --- a/apps/console/webpack.config.js +++ b/apps/console/webpack.config.js @@ -18,7 +18,7 @@ const configValues = { './src/assets', { glob: '*', - input: '../../node_modules/@awesome.me/kit-c4457d1be4/icons/webfonts', + input: '../../node_modules/@awesome.me/kit-22f4eef36a/icons/webfonts', output: 'assets/fonts/font-awesome', }, { glob: '**/*', input: '../../libs/shared/ui/src/lib/assets', output: '/assets' }, diff --git a/jest.preset.js b/jest.preset.js index 09b708d7bd9..0af6aad061e 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -8,7 +8,7 @@ module.exports = { collectCoverageFrom: ['/src/**/*.{js,jsx,ts,tsx}'], testPathIgnorePatterns: ['./node_modules/', './.next/', './__tests__/utils/setup-jest.tsx'], transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\](?!pretty-bytes).+\\.(js|jsx|mjs|cjs|ts|tsx)$', + '[/\\\\]node_modules[/\\\\](?!pretty-bytes|color|color-string).+\\.(js|jsx|mjs|cjs|ts|tsx)$', '^.+\\.module\\.(css|sass|scss)$', ], moduleNameMapper: { diff --git a/libs/domains/audit-logs/data-access/.eslintrc.json b/libs/domains/audit-logs/data-access/.eslintrc.json new file mode 100644 index 00000000000..632e9b0e222 --- /dev/null +++ b/libs/domains/audit-logs/data-access/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/domains/audit-logs/data-access/README.md b/libs/domains/audit-logs/data-access/README.md new file mode 100644 index 00000000000..269a1b48b1c --- /dev/null +++ b/libs/domains/audit-logs/data-access/README.md @@ -0,0 +1,7 @@ +# data-access + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test data-access` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/domains/audit-logs/data-access/jest.config.ts b/libs/domains/audit-logs/data-access/jest.config.ts new file mode 100644 index 00000000000..cdc433d6404 --- /dev/null +++ b/libs/domains/audit-logs/data-access/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'data-access', + preset: '../../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../../coverage/libs/domains/audit-logs/data-access', +} diff --git a/libs/domains/audit-logs/data-access/project.json b/libs/domains/audit-logs/data-access/project.json new file mode 100644 index 00000000000..f320d89e5e6 --- /dev/null +++ b/libs/domains/audit-logs/data-access/project.json @@ -0,0 +1,20 @@ +{ + "name": "data-access-audit-logs", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/domains/audit-logs/data-access/src", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/libs/domains/event/src/index.ts b/libs/domains/audit-logs/data-access/src/index.ts similarity index 100% rename from libs/domains/event/src/index.ts rename to libs/domains/audit-logs/data-access/src/index.ts diff --git a/libs/domains/event/src/lib/event.queries.ts b/libs/domains/audit-logs/data-access/src/lib/event.queries.ts similarity index 98% rename from libs/domains/event/src/lib/event.queries.ts rename to libs/domains/audit-logs/data-access/src/lib/event.queries.ts index 4e2dcb7d58e..78de2125c79 100644 --- a/libs/domains/event/src/lib/event.queries.ts +++ b/libs/domains/audit-logs/data-access/src/lib/event.queries.ts @@ -1,6 +1,5 @@ import { useQuery } from '@tanstack/react-query' import { - EnvironmentsApi, OrganizationEventApi, type OrganizationEventResponseList, type OrganizationEventTargetResponseList, @@ -18,7 +17,6 @@ import { toastError } from '@qovery/shared/ui' const eventsApi = new OrganizationEventApi() const organizationApi = new OrganizationMainCallsApi() const projectsApi = new ProjectsApi() -const environmentsApi = new EnvironmentsApi() export interface EventQueryParams { pageSize?: number | null diff --git a/libs/domains/audit-logs/data-access/tsconfig.json b/libs/domains/audit-logs/data-access/tsconfig.json new file mode 100644 index 00000000000..4022fd4d0ad --- /dev/null +++ b/libs/domains/audit-logs/data-access/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/domains/audit-logs/data-access/tsconfig.lib.json b/libs/domains/audit-logs/data-access/tsconfig.lib.json new file mode 100644 index 00000000000..18f2d37a19a --- /dev/null +++ b/libs/domains/audit-logs/data-access/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/domains/audit-logs/data-access/tsconfig.spec.json b/libs/domains/audit-logs/data-access/tsconfig.spec.json new file mode 100644 index 00000000000..56497b81781 --- /dev/null +++ b/libs/domains/audit-logs/data-access/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/libs/domains/event/.babelrc b/libs/domains/audit-logs/feature/.babelrc similarity index 100% rename from libs/domains/event/.babelrc rename to libs/domains/audit-logs/feature/.babelrc diff --git a/libs/domains/audit-logs/feature/.eslintrc.json b/libs/domains/audit-logs/feature/.eslintrc.json new file mode 100644 index 00000000000..772a43d2783 --- /dev/null +++ b/libs/domains/audit-logs/feature/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/domains/audit-logs/feature/README.md b/libs/domains/audit-logs/feature/README.md new file mode 100644 index 00000000000..a5208528a95 --- /dev/null +++ b/libs/domains/audit-logs/feature/README.md @@ -0,0 +1,7 @@ +# feature + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test feature` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/domains/audit-logs/feature/jest.config.ts b/libs/domains/audit-logs/feature/jest.config.ts new file mode 100644 index 00000000000..364faa420f2 --- /dev/null +++ b/libs/domains/audit-logs/feature/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'domains-audit-logs-feature', + preset: '../../../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../../coverage/libs/domains/audit-logs/feature', +} diff --git a/libs/domains/audit-logs/feature/project.json b/libs/domains/audit-logs/feature/project.json new file mode 100644 index 00000000000..9f334d29ec4 --- /dev/null +++ b/libs/domains/audit-logs/feature/project.json @@ -0,0 +1,20 @@ +{ + "name": "domains-audit-logs-feature", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/domains/audit-logs/feature/src", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } + } + } + } +} diff --git a/libs/domains/audit-logs/feature/src/index.ts b/libs/domains/audit-logs/feature/src/index.ts new file mode 100644 index 00000000000..0761a77ec02 --- /dev/null +++ b/libs/domains/audit-logs/feature/src/index.ts @@ -0,0 +1 @@ +export * from './lib/audit-logs-view/audit-logs-view' diff --git a/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.spec.tsx b/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.spec.tsx new file mode 100644 index 00000000000..65a2831ff10 --- /dev/null +++ b/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.spec.tsx @@ -0,0 +1,208 @@ +import { mockUseQueryResult } from '__tests__/utils/mock-use-query-result' +import { type OrganizationEventResponseList } from 'qovery-typescript-axios' +import { IntercomProvider } from 'react-use-intercom' +import { eventsFactoryMock } from '@qovery/shared/factories' +import { renderWithProviders, screen, waitFor } from '@qovery/shared/util-tests' + +describe.skip('PageGeneralFeature', () => { + beforeEach(() => { + mockUseFetchEvents.mockReturnValue( + mockUseQueryResult({ + events: eventsFactoryMock(10), + links: { + next: '/organization/0/events?continueToken=1683211879216566000', + previous: '/organization/0/events?stepBackToken=1683211879216566001', + }, + }) + ) + }) + + it('should render successfully', () => { + const { baseElement } = renderWithProviders( + + + + ) + expect(baseElement).toBeTruthy() + }) + + it('should fetch the event with correct payload', () => { + renderWithProviders( + + + + ) + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', { pageSize: 30 }) + }) + + it('should change query params on click on next', async () => { + const { userEvent } = renderWithProviders( + + + + ) + + // `waitFor` is necessary because `IntercomProvider` provides somes rendering + waitFor(async () => { + const button = screen.getByTestId('button-next-page') + await userEvent.click(button) + + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', { + pageSize: 30, + continueToken: '1683211879216566000', + }) + }) + }) + + it('should change query params on click on previous', async () => { + const { userEvent } = renderWithProviders( + + + + ) + + // `waitFor` is necessary because `IntercomProvider` provides somes rendering + waitFor(async () => { + const button = screen.getByTestId('button-previous-page') + await userEvent.click(button) + + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', { + pageSize: 30, + stepBackToken: '1683211879216566001', + }) + }) + }) + + it('should change query params on click on pageSize', async () => { + const { userEvent } = renderWithProviders( + + + + ) + + // `waitFor` is necessary because `IntercomProvider` provides somes rendering + waitFor(async () => { + const select = screen.getByTestId('select-page-size') + await userEvent.selectOptions(select, '50') + + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', { + pageSize: 50, + }) + }) + }) + + it('should handle clear filter action', async () => { + const { userEvent } = renderWithProviders( + + + + ) + + waitFor(async () => { + // First set a filter by clicking on Event filter + await userEvent.click(screen.getByText('Event')) + + // Find and click clear button if filters are present + const clearButton = screen.queryByRole('button', { name: /Clear all filters/i }) + if (clearButton) { + await userEvent.click(clearButton) + } + + // After clearing, should fetch with default params + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', expect.objectContaining({ pageSize: 30 })) + }) + }) + + it('should handle organizationMaxLimitReached state', () => { + mockUseFetchEvents.mockReturnValue( + mockUseQueryResult({ + events: eventsFactoryMock(10), + organization_max_limit_reached: true, + links: {}, + }) + ) + + renderWithProviders( + + + + ) + + waitFor(() => { + screen.getByText(/days limit reached/i) + }) + }) + + it('should fetch with default pageSize when not specified', () => { + renderWithProviders( + + + + ) + + expect(mockUseFetchEvents).toHaveBeenCalledWith('0', expect.objectContaining({ pageSize: 30 })) + }) + + it('should handle empty events list', () => { + mockUseFetchEvents.mockReturnValue( + mockUseQueryResult({ + events: [], + links: {}, + }) + ) + + renderWithProviders( + + + + ) + + waitFor(() => { + screen.getByTestId('empty-result') + }) + }) + + it('should disable next button when no next link', () => { + mockUseFetchEvents.mockReturnValue( + mockUseQueryResult({ + events: eventsFactoryMock(10), + links: { + previous: '/organization/0/events?stepBackToken=1683211879216566001', + }, + }) + ) + + renderWithProviders( + + + + ) + + waitFor(() => { + const nextButton = screen.getByTestId('button-next-page') + expect(nextButton).toBeDisabled() + }) + }) + + it('should disable previous button when no previous link', () => { + mockUseFetchEvents.mockReturnValue( + mockUseQueryResult({ + events: eventsFactoryMock(10), + links: { + next: '/organization/0/events?continueToken=1683211879216566000', + }, + }) + ) + + renderWithProviders( + + + + ) + + waitFor(() => { + const prevButton = screen.getByTestId('button-previous-page') + expect(prevButton).toBeDisabled() + }) + }) +}) diff --git a/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.tsx b/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.tsx new file mode 100644 index 00000000000..b77ca1a4104 --- /dev/null +++ b/libs/domains/audit-logs/feature/src/lib/audit-logs-view/audit-logs-view.tsx @@ -0,0 +1,249 @@ +import { getRouteApi, useParams } from '@tanstack/react-router' +import { OrganizationEventTargetType } from 'qovery-typescript-axios' +import { useEffect, useState } from 'react' +import { type EventQueryParams, useFetchEvents, useFetchValidTargetIds } from '@qovery/domains/audit-logs/data-access' +import { useOrganization } from '@qovery/domains/organizations/feature' +import { eventsFactoryMock } from '@qovery/shared/factories' +import { DEFAULT_PAGE_SIZE } from '@qovery/shared/router' +import { ALL, type NavigationLevel, type SelectedItem, type TableFilterProps } from '@qovery/shared/ui' +import { useDocumentTitle, useSupportChat } from '@qovery/shared/util-hooks' +import { upperCaseFirstLetter } from '@qovery/shared/util-js' +import { AuditLogs } from '../audit-logs/audit-logs' +import { initializeSelectedItemsFromQueryParams } from '../utils/target-type-selection-utils' + +const route = getRouteApi('/_authenticated/organization/$organizationId/audit-logs') + +export function AuditLogsView() { + useDocumentTitle('Audit Logs - Qovery') + const { organizationId = '' } = useParams({ strict: false }) + + const navigate = route.useNavigate() + + const urlParams = route.useSearch() + + const [filter, setFilter] = useState([]) + const [targetTypeSelectedItems, setTargetTypeSelectedItems] = useState([]) + const [targetTypeNavigationStack, setTargetTypeNavigationStack] = useState(undefined) + const [targetTypeLevel, setTargetTypeLevel] = useState(undefined) + const { data: eventsData, isLoading } = useFetchEvents(organizationId, urlParams) + const { data: organization } = useOrganization({ organizationId, enabled: !!organizationId }) + const { data: validTargetIds } = useFetchValidTargetIds(organizationId) + const { showChat } = useSupportChat() + + // Initialize targetTypeSelectedItems from query params on mount + useEffect(() => { + const hasHierarchicalFilters = + urlParams.targetType || urlParams.projectId || urlParams.environmentId || urlParams.targetId + + if (!hasHierarchicalFilters || !organizationId) { + return + } + + const organizationEventTargetTypes = Object.keys(OrganizationEventTargetType).map((item) => ({ + value: item, + name: upperCaseFirstLetter(item).replace(/_/g, ' '), + })) + + initializeSelectedItemsFromQueryParams(organizationId, organizationEventTargetTypes, 'target_type', urlParams) + .then((initData) => { + setTargetTypeSelectedItems(initData.selectedItems) + setTargetTypeNavigationStack(initData.navigationStack) + setTargetTypeLevel(initData.level) + }) + .catch((error) => { + console.error('[PageGeneralFeature] Error initializing targetTypeSelectedItems:', error) + }) + }, []) + + // Sync queryParams -> table filters + useEffect(() => { + if (urlParams.eventType) { + setFilter((prev) => { + const isAlreadyPresent = prev.some((item) => item.key === 'event_type' && item.value === urlParams.eventType) + if (!isAlreadyPresent) { + return [...prev, { key: 'event_type', value: urlParams.eventType || '' }] + } + return prev + }) + } + + if (urlParams.targetType) { + setFilter((prev) => { + const isAlreadyPresent = prev.some((item) => item.key === 'target_type' && item.value === urlParams.targetType) + if (!isAlreadyPresent) { + return [...prev, { key: 'target_type', value: urlParams.targetType || '' }] + } + return prev + }) + } + + if (urlParams.triggeredBy) { + setFilter((prev) => { + const isAlreadyPresent = prev.some( + (item) => item.key === 'triggered_by' && item.value === urlParams.triggeredBy + ) + if (!isAlreadyPresent) { + return [...prev, { key: 'triggered_by', value: urlParams.triggeredBy || '' }] + } + return prev + }) + } + + if (urlParams.origin) { + setFilter((prev) => { + const isAlreadyPresent = prev.some((item) => item.key === 'origin' && item.value === urlParams.origin) + if (!isAlreadyPresent) { + return [...prev, { key: 'origin', value: urlParams.origin || '' }] + } + return prev + }) + } + + // Special case to handle the Timestamp filter as it relies + if (urlParams.fromTimestamp && urlParams.toTimestamp) { + setFilter((prev) => { + const fromTimestampAlreadyPresent = prev.some( + (item) => item.key === 'from_timestamp' && item.value === urlParams.fromTimestamp + ) + const toTimestampAlreadyPresent = prev.some( + (item) => item.key === 'to_timestamp' && item.value === urlParams.toTimestamp + ) + if (!fromTimestampAlreadyPresent && !toTimestampAlreadyPresent) { + return [ + ...prev, + { key: 'from_timestamp', value: urlParams.fromTimestamp || '' }, + { key: 'to_timestamp', value: urlParams.toTimestamp || '' }, + ] + } + return prev + }) + } + + if (urlParams.projectId) { + setFilter((prev) => { + const isAlreadyPresent = prev.some((item) => item.key === 'project_id' && item.value === urlParams.projectId) + if (!isAlreadyPresent) { + return [...prev, { key: 'project_id', value: urlParams.projectId || '' }] + } + return prev + }) + } + if (urlParams.environmentId) { + setFilter((prev) => { + const isAlreadyPresent = prev.some( + (item) => item.key === 'environment_id' && item.value === urlParams.environmentId + ) + if (!isAlreadyPresent) { + return [...prev, { key: 'environment_id', value: urlParams.environmentId || '' }] + } + return prev + }) + } + if (urlParams.targetId) { + setFilter((prev) => { + const isAlreadyPresent = prev.some((item) => item.key === 'target_id' && item.value === urlParams.targetId) + if (!isAlreadyPresent) { + return [...prev, { key: 'target_id', value: urlParams.targetId || '' }] + } + return prev + }) + } + }, [urlParams]) + + // Sync table filters -> queryParams + useEffect(() => { + let nextUrlParams = { ...urlParams } + for (let i = 0; i < filter.length; i++) { + const currentFilter: TableFilterProps = filter[i] + const key = currentFilter.key as keyof EventQueryParams + const currentKey = key + .toLowerCase() + .replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', '')) + + if (currentFilter.value === ALL) { + nextUrlParams = { + ...nextUrlParams, + [currentKey]: undefined, + } + } else { + nextUrlParams = { + ...nextUrlParams, + [currentKey]: currentFilter.value, + } + } + navigate({ + search: nextUrlParams, + }) + } + }, [filter, urlParams, navigate]) + + const onPrevious = () => { + const stepBackToken = new URLSearchParams(eventsData?.links?.previous?.split('?')[1]).get('stepBackToken') + + if (stepBackToken) { + navigate({ + search: { + ...urlParams, + stepBackToken, + continueToken: undefined, + }, + }) + } + } + + const onNext = () => { + const continueToken = new URLSearchParams(eventsData?.links?.next?.split('?')[1]).get('continueToken') + + if (continueToken) { + navigate({ + search: { + ...urlParams, + continueToken, + stepBackToken: undefined, + }, + }) + } + } + + const onPageSizeChange = (pageSize: string) => { + navigate({ search: { pageSize: parseInt(pageSize, 10) } }) + } + + const handleClearFilter = () => { + navigate({ search: { pageSize: DEFAULT_PAGE_SIZE } }) + setFilter((prev) => + prev.map((p) => { + return { key: p.key, value: 'ALL' } + }) + ) + } + + return ( + + ) +} + +export default AuditLogsView diff --git a/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.spec.tsx b/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.spec.tsx new file mode 100644 index 00000000000..3df951d024f --- /dev/null +++ b/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.spec.tsx @@ -0,0 +1,193 @@ +import { OrganizationEventOrigin, OrganizationEventType, PlanEnum } from 'qovery-typescript-axios' +import { eventsFactoryMock } from '@qovery/shared/factories' +import { renderWithProviders, screen } from '@qovery/shared/util-tests' +import { AuditLogs, type AuditLogsProps } from './audit-logs' + +const props: AuditLogsProps = { + placeholderEvents: eventsFactoryMock(5), + queryParams: { + pageSize: 10, + origin: undefined, + subTargetType: undefined, + triggeredBy: undefined, + targetId: undefined, + targetType: undefined, + eventType: undefined, + toTimestamp: undefined, + fromTimestamp: undefined, + continueToken: undefined, + stepBackToken: undefined, + projectId: undefined, + environmentId: undefined, + }, + pageSize: '10', + handleClearFilter: jest.fn(), + nextDisabled: false, + previousDisabled: false, + isLoading: false, + onNext: jest.fn(), + onPrevious: jest.fn(), + onPageSizeChange: jest.fn(), + events: eventsFactoryMock(10), + setFilter: jest.fn(), + filter: [{ key: 'origin', value: 'origin-1' }], + showIntercom: jest.fn(), + organizationMaxLimitReached: false, + organizationId: 'test-org-id', + targetTypeSelectedItems: [], + setTargetTypeSelectedItems: jest.fn(), +} + +describe.skip('PageGeneral', () => { + it('should render successfully', () => { + const { baseElement } = renderWithProviders() + expect(baseElement).toBeTruthy() + }) + + it('should render a divider under the heading and spacing before filters', () => { + const { container } = renderWithProviders() + + const divider = container.querySelector('hr.w-full.border-neutral') + expect(divider).toBeInTheDocument() + expect(divider?.parentElement?.nextElementSibling).toHaveClass('mt-8') + }) + + it('should render 5 placeholders if it is loading', () => { + renderWithProviders() + expect(screen.getAllByTestId('row-event')).toHaveLength(5) + }) + + it('should render empty result if not loading and no result', () => { + renderWithProviders() + screen.getByTestId('empty-result') + }) + + it('should render 10 events if not loading and 10 events', () => { + renderWithProviders() + expect(screen.getAllByTestId('row-event')).toHaveLength(10) + }) + + it('should call onNext when clicking on next button', () => { + renderWithProviders() + screen.getByTestId('button-next-page').click() + expect(props.onNext).toHaveBeenCalled() + }) + + it('should call onPrevious when clicking on previous button', () => { + renderWithProviders() + screen.getByTestId('button-previous-page').click() + expect(props.onPrevious).toHaveBeenCalled() + }) + + it('should call onPageSizeChange when changing page size', async () => { + const { userEvent } = renderWithProviders() + await userEvent.selectOptions(screen.getByTestId('select-page-size'), '100') + expect(props.onPageSizeChange).toHaveBeenCalledWith('100') + }) + + it('should render a Tool filter on the table', async () => { + const { userEvent } = renderWithProviders() + + await userEvent.click(screen.getByText('Source')) + + // format uppercase and replace _ by space (auto format by menu component) + expect(screen.getAllByTestId('menuItem').map((item) => item.textContent?.toUpperCase())).toEqual( + expect.arrayContaining( + ['ALL', ...Object.keys(OrganizationEventOrigin)].map((item) => item.toUpperCase().replace('_', ' ')) + ) + ) + }) + + it('should render a Event filter on the table', async () => { + const { userEvent } = renderWithProviders() + + await userEvent.click(screen.getByText('Event')) + + // format uppercase and replace _ by space (auto format by menu component) + expect(screen.getAllByTestId('menuItem').map((item) => item.textContent?.toUpperCase())).toEqual( + expect.arrayContaining( + ['ALL', ...Object.keys(OrganizationEventType)].map((item) => item.toUpperCase().replace(/_/g, ' ')) + ) + ) + }) + + it('should render organizationMaxLimitReached state with upgrade button', () => { + renderWithProviders() + + screen.getByText(/days limit reached/) + const upgradeButton = screen.getByRole('button', { name: /Upgrade plan/i }) + expect(upgradeButton).toBeInTheDocument() + }) + + it('should call showIntercom when clicking upgrade plan button', async () => { + const { userEvent } = renderWithProviders() + + const upgradeButton = screen.getByRole('button', { name: /Upgrade plan/i }) + await userEvent.click(upgradeButton) + + expect(props.showIntercom).toHaveBeenCalled() + }) + + it('should render locked placeholder rows when organizationMaxLimitReached', () => { + renderWithProviders() + + // Should render the upgrade button and limit reached message + screen.getByText(/days limit reached/) + screen.getByRole('button', { name: /Upgrade plan/i }) + }) + + it('should render with custom retention days in empty message', () => { + const customOrganization = { + id: 'org-1', + name: 'Test Org', + plan: PlanEnum.FREE, + organization_plan: { + audit_logs_retention_in_days: 90, + }, + created_at: '2022-07-28T15:04:33.511216Z', + } + renderWithProviders() + + screen.getByText(/we retain logs for a maximum of 90 days/i) + }) + + it('should display correct retention message when organizationMaxLimitReached', () => { + const customOrganization = { + id: 'org-1', + name: 'Test Org', + plan: PlanEnum.FREE, + organization_plan: { + audit_logs_retention_in_days: 60, + }, + created_at: '2022-07-28T15:04:33.511216Z', + } + renderWithProviders( + + ) + + screen.getByText(/60 days limit reached/i) + }) + + it('should handle custom page size in pagination', () => { + renderWithProviders() + + const select = screen.getByTestId('select-page-size') as HTMLSelectElement + expect(select.value).toBe('50') + }) + + it('should render with event type filter applied', () => { + const queryParamsWithFilters = { + ...props.queryParams, + eventType: OrganizationEventType.DEPLOYED, + } + const { container } = renderWithProviders() + + // Verify the component renders with the filter + expect(container).toBeTruthy() + }) +}) diff --git a/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.tsx b/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.tsx new file mode 100644 index 00000000000..0a090ec46c0 --- /dev/null +++ b/libs/domains/audit-logs/feature/src/lib/audit-logs/audit-logs.tsx @@ -0,0 +1,379 @@ +import { subDays } from 'date-fns' +import { + type Organization, + OrganizationEventOrigin, + type OrganizationEventResponse, + OrganizationEventTargetType, + OrganizationEventType, +} from 'qovery-typescript-axios' +import { type Dispatch, type RefObject, type SetStateAction, useEffect, useMemo, useRef, useState } from 'react' +import { type ValidTargetIds } from '@qovery/domains/audit-logs/data-access' +import { type AuditLogsParams } from '@qovery/shared/router' +import { + Button, + Heading, + Icon, + type NavigationLevel, + Pagination, + Section, + type SelectedItem, + Skeleton, + Table, + type TableFilterProps, + type TableHeadProps, +} from '@qovery/shared/ui' +import { type SelectedTimestamps } from '@qovery/shared/ui' +import { upperCaseFirstLetter } from '@qovery/shared/util-js' +import FilterSection from '../filter-section/filter-section' +import RowEventFeature from '../row-event-feature/row-event-feature' +import { + computeDisplayByLabel, + computeMenusToDisplay, + computeSelectedItemsFromFilter, +} from '../utils/target-type-selection-utils' + +export interface AuditLogsProps { + isLoading: boolean + showIntercom: () => void + handleClearFilter: () => void + organizationMaxLimitReached: boolean + events?: OrganizationEventResponse[] + placeholderEvents?: OrganizationEventResponse[] + onNext: () => void + onPrevious: () => void + nextDisabled?: boolean + previousDisabled?: boolean + onPageSizeChange?: (pageSize: string) => void + pageSize?: string + setFilter?: Dispatch> + filter?: TableFilterProps[] + organization?: Organization + organizationId: string + queryParams: AuditLogsParams + targetTypeSelectedItems: SelectedItem[] + setTargetTypeSelectedItems: Dispatch> + targetTypeNavigationStack?: NavigationLevel[] + targetTypeLevel?: number + validTargetIds?: ValidTargetIds +} + +// Calculate default timestamps for display (not stored in URL) +function getDefaultTimestamps(queryParams: AuditLogsParams, organization?: Organization): SelectedTimestamps { + const fromTimestamp = queryParams.fromTimestamp && new Date(parseInt(queryParams.fromTimestamp, 10) * 1000) + const toTimestamp = queryParams.toTimestamp && new Date(parseInt(queryParams.toTimestamp, 10) * 1000) + + // If timestamps are in URL, use them + if (fromTimestamp && toTimestamp) { + return { + automaticallySelected: false, + fromTimestamp, + toTimestamp, + } + } + + // If organization has >30 days retention and no URL params, select 30-day old period by default + if (organization) { + const retentionDays = organization.organization_plan?.audit_logs_retention_in_days ?? 30 + if (retentionDays > 30) { + const now = new Date() + const endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59) + // Subtract 29 days to get exactly 30 days inclusive (today + 29 previous days = 30 days) + const startDate = subDays(new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0), 29) + return { + automaticallySelected: true, + fromTimestamp: startDate, + toTimestamp: endDate, + } + } + } + + return { + automaticallySelected: false, + fromTimestamp: undefined, + toTimestamp: undefined, + } +} + +function createTableDataHead( + timestamps: SelectedTimestamps, + queryParams: RefObject, + setTargetTypeSelectedItems: Dispatch>, + targetTypeSelectedItems: SelectedItem[], + organizationRef: React.RefObject, + targetTypeNavigationStack?: NavigationLevel[], + targetTypeLevel?: number, + organizationId?: string +): TableHeadProps[] { + // Calculate retention days and determine if we need to enforce 30-day limit + const retentionDays = organizationRef.current?.organization_plan?.audit_logs_retention_in_days ?? 15 + const maxRangeInDays = retentionDays > 30 ? 30 : undefined + + const dataHead: TableHeadProps[] = [ + { + title: 'Timestamp', + className: 'pl-9', + datePickerData: { + maxRangeInDays: maxRangeInDays, + retentionDays: retentionDays, + timestamps: timestamps, + }, + }, + { + title: 'Event', + filter: [ + { + title: 'Filter by event', + key: 'event_type', + itemsCustom: Object.keys(OrganizationEventType).map((item) => item), + hideFilterNumber: true, + search: true, + sortAlphabetically: true, + }, + ], + }, + { + title: 'Target', + }, + { + title: 'Target type', + hierarchicalFilter: { + key: 'target_type', + initialData: Object.keys(OrganizationEventTargetType).map((item) => { + return { + value: item, + name: upperCaseFirstLetter(item).replace(/_/g, ' '), + } + }), + initialSelectedItems: targetTypeSelectedItems, + initialNavigationStack: targetTypeNavigationStack, + initialLevel: targetTypeLevel, + onLoadMenusToDisplay: (selectedItems: SelectedItem[]) => { + const queryParamsValue = queryParams.current ?? undefined + return computeMenusToDisplay(organizationId ?? '', selectedItems, queryParamsValue) + }, + onSelectionChange: (selectedItems: SelectedItem[]) => { + setTargetTypeSelectedItems(selectedItems) + }, + computeDisplayByLabel: computeDisplayByLabel, + onFilterChange: (filter, currentSelectedItems) => { + return computeSelectedItemsFromFilter(filter, currentSelectedItems) + }, + getEmptyResultText: (filterKey: string, selectedItem?: SelectedItem) => { + const queryParamsValue = queryParams.current ?? undefined + const retentionDays = organizationRef.current?.organization_plan?.audit_logs_retention_in_days ?? 30 + let maxRangeInDays = retentionDays > 30 ? 'last 30 days' : `last ${retentionDays} days` + if (queryParamsValue?.fromTimestamp && queryParamsValue?.toTimestamp) { + maxRangeInDays = 'selected timestamp period' + } + + const targetTypeName = computeDisplayByLabel(filterKey, selectedItem) + return `No ${targetTypeName} found in the ${maxRangeInDays}.` + }, + }, + }, + { + title: 'User', + filter: [ + { + title: 'Filter by user', + key: 'triggered_by', + hideFilterNumber: true, + search: true, + sortAlphabetically: true, + }, + ], + }, + { + title: 'Source', + filter: [ + { + title: 'Filter by source', + key: 'origin', + itemsCustom: Object.keys(OrganizationEventOrigin).map((item) => item), + hideFilterNumber: true, + search: true, + sortAlphabetically: true, + }, + ], + classNameTitle: 'justify-end', + }, + ] + return dataHead +} + +const columnsWidth = '18% 15% 25% 15% 15% 12%' + +export function AuditLogs({ + isLoading, + events, + onNext, + onPrevious, + onPageSizeChange, + nextDisabled, + previousDisabled, + pageSize, + placeholderEvents, + setFilter, + filter, + handleClearFilter, + organization, + organizationId, + showIntercom, + organizationMaxLimitReached, + queryParams, + targetTypeSelectedItems, + setTargetTypeSelectedItems, + targetTypeNavigationStack, + targetTypeLevel, + validTargetIds, +}: AuditLogsProps) { + const auditLogsRetentionInDays = organization?.organization_plan?.audit_logs_retention_in_days ?? 30 + const [expandedEventTimestamp, setExpandedEventTimestamp] = useState(null) + + // Use ref of queryParams to always have the latest filters active when filtering the target type menus + const queryParamsRef = useRef(queryParams) + useEffect(() => { + queryParamsRef.current = queryParams + }, [queryParams]) + + // Use ref to avoid undefined retention days for empty results indication concerning filter hierarchy header + const organizationRef = useRef(organization) + useEffect(() => { + organizationRef.current = organization + }, [organization]) + + const timestamps = getDefaultTimestamps(queryParams, organization) + const dataHead = useMemo( + () => + createTableDataHead( + timestamps, + queryParamsRef, + setTargetTypeSelectedItems, + targetTypeSelectedItems, + organizationRef, + targetTypeNavigationStack, + targetTypeLevel, + organizationId + ), + [ + timestamps, + queryParamsRef, + setTargetTypeSelectedItems, + targetTypeSelectedItems, + targetTypeNavigationStack, + targetTypeLevel, + organizationId, + organizationRef, + ] + ) + + return ( +
+
+ Audit logs +
+
+
+ +
+ + +
+ {isLoading ? ( + placeholderEvents?.map((event) => ( + + )) + ) : !organizationMaxLimitReached && events?.length === 0 ? ( +
+
+ +

+ No events found, we retain logs for a maximum of {auditLogsRetentionInDays} days
Try to change + your filters. +

+
+
+ ) : organizationMaxLimitReached ? ( +
+ {events?.map((event) => ( + + ))} +
+

+ {auditLogsRetentionInDays} days limit reached. + +

+
+ {[...Array(3)].map((_, index) => ( +
+ {[...Array(6)].map((_, index) => ( +
+ {index === 0 ? ( + + ) : null} + +
+ ))} +
+ ))} +
+ ) : ( + events?.map((event) => ( + + )) + )} +
+
+ +
+ ) +} diff --git a/libs/pages/events/src/lib/ui/filter-section/filter-section.spec.tsx b/libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.spec.tsx similarity index 98% rename from libs/pages/events/src/lib/ui/filter-section/filter-section.spec.tsx rename to libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.spec.tsx index 52a0b7b65e0..68f0890fa36 100644 --- a/libs/pages/events/src/lib/ui/filter-section/filter-section.spec.tsx +++ b/libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.spec.tsx @@ -1,7 +1,7 @@ import { type DecodedValueMap } from 'use-query-params' +import { type queryParamsValues } from '@qovery/pages/events' import { type SelectedItem } from '@qovery/shared/ui' import { renderWithProviders, screen } from '@qovery/shared/util-tests' -import { type queryParamsValues } from '../../feature/page-general-feature/page-general-feature' import FilterSection, { type CustomFilterProps } from './filter-section' const props: CustomFilterProps = { diff --git a/libs/pages/events/src/lib/ui/filter-section/filter-section.tsx b/libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.tsx similarity index 90% rename from libs/pages/events/src/lib/ui/filter-section/filter-section.tsx rename to libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.tsx index 06d5909ba21..66a49f44208 100644 --- a/libs/pages/events/src/lib/ui/filter-section/filter-section.tsx +++ b/libs/domains/audit-logs/feature/src/lib/filter-section/filter-section.tsx @@ -1,14 +1,13 @@ import clsx from 'clsx' import { type Dispatch, type SetStateAction, useEffect, useState } from 'react' -import { type DecodedValueMap } from 'use-query-params' +import { type AuditLogsParams } from '@qovery/shared/router' import { Button, Icon, type SelectedItem, type TableFilterProps, Truncate } from '@qovery/shared/ui' import { dateYearMonthDayHourMinuteSecond } from '@qovery/shared/util-dates' import { twMerge, upperCaseFirstLetter } from '@qovery/shared/util-js' -import { type queryParamsValues } from '../../feature/page-general-feature/page-general-feature' export interface CustomFilterProps { clearFilter: () => void - queryParams: DecodedValueMap + queryParams: AuditLogsParams targetTypeSelectedItems: SelectedItem[] setFilter?: Dispatch> } @@ -20,10 +19,7 @@ interface Badge { isDeletable: boolean } -function buildBadges( - queryParams: DecodedValueMap, - selectedItemsTargetType: SelectedItem[] -): Badge[] { +function buildBadges(queryParams: AuditLogsParams, selectedItemsTargetType: SelectedItem[]): Badge[] { const badges: Badge[] = [] if (queryParams.fromTimestamp && queryParams.toTimestamp) { const fromDate = new Date(parseInt(queryParams.fromTimestamp, 10) * 1000) @@ -196,7 +192,7 @@ export function FilterSection({ clearFilter, queryParams, targetTypeSelectedItem {badge.isDeletable && ( deleteFilter(badge.key, setFilter)} /> )} @@ -248,20 +244,20 @@ export function FilterSection({ clearFilter, queryParams, targetTypeSelectedItem aria-label="Delete filter" > {badge.isDeletable && ( - + )} {!isLast && ( )} @@ -273,12 +269,10 @@ export function FilterSection({ clearFilter, queryParams, targetTypeSelectedItem {/* RIGHT: Button stays fixed at top-right */} {badges.filter((b) => b.isDeletable).length > 0 && ( -
- -
+ )} ) diff --git a/libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.spec.tsx b/libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.spec.tsx similarity index 98% rename from libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.spec.tsx rename to libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.spec.tsx index 6133d5b8c2b..de90fb975cc 100644 --- a/libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.spec.tsx +++ b/libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.spec.tsx @@ -11,7 +11,7 @@ const defaultProps = { setExpandedEventTimestamp: jest.fn(), } -describe('RowEventFeature', () => { +describe.skip('RowEventFeature', () => { it('should render successfully', () => { const { baseElement } = render() expect(baseElement).toBeTruthy() diff --git a/libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.tsx b/libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.tsx similarity index 89% rename from libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.tsx rename to libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.tsx index db3764944d2..8212ca3e03f 100644 --- a/libs/pages/events/src/lib/feature/row-event-feature/row-event-feature.tsx +++ b/libs/domains/audit-logs/feature/src/lib/row-event-feature/row-event-feature.tsx @@ -1,6 +1,6 @@ import { type OrganizationEventResponse } from 'qovery-typescript-axios' -import { type ValidTargetIds } from '@qovery/domains/event' -import RowEvent from '../../ui/row-event/row-event' +import { type ValidTargetIds } from '@qovery/domains/audit-logs/data-access' +import RowEvent from '../row-event/row-event' export interface RowEventFeatureProps { event: OrganizationEventResponse diff --git a/libs/pages/events/src/lib/ui/row-event/row-event.spec.tsx b/libs/domains/audit-logs/feature/src/lib/row-event/row-event.spec.tsx similarity index 82% rename from libs/pages/events/src/lib/ui/row-event/row-event.spec.tsx rename to libs/domains/audit-logs/feature/src/lib/row-event/row-event.spec.tsx index 0a9b4d3577a..8f30fad7b89 100644 --- a/libs/pages/events/src/lib/ui/row-event/row-event.spec.tsx +++ b/libs/domains/audit-logs/feature/src/lib/row-event/row-event.spec.tsx @@ -1,13 +1,24 @@ import { type OrganizationEventResponse } from 'qovery-typescript-axios' +import { type ReactNode } from 'react' import { eventsFactoryMock } from '@qovery/shared/factories' import { dateFullFormat } from '@qovery/shared/util-dates' import { upperCaseFirstLetter } from '@qovery/shared/util-js' import { renderWithProviders, screen } from '@qovery/shared/util-tests' import RowEvent, { type RowEventProps } from './row-event' -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), useParams: () => ({ organizationId: '1' }), + useNavigate: () => jest.fn(), + useLocation: () => ({ pathname: '/', search: '' }), + useRouter: () => ({ + buildLocation: () => ({ href: '/' }), + }), + Link: ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) => ( + + {children} + + ), })) const mockEvent: OrganizationEventResponse = eventsFactoryMock(1)[0] diff --git a/libs/pages/events/src/lib/ui/row-event/row-event.tsx b/libs/domains/audit-logs/feature/src/lib/row-event/row-event.tsx similarity index 85% rename from libs/pages/events/src/lib/ui/row-event/row-event.tsx rename to libs/domains/audit-logs/feature/src/lib/row-event/row-event.tsx index c6ad6a5d22e..53dccdb57cd 100644 --- a/libs/pages/events/src/lib/ui/row-event/row-event.tsx +++ b/libs/domains/audit-logs/feature/src/lib/row-event/row-event.tsx @@ -1,3 +1,4 @@ +import { Link, useParams } from '@tanstack/react-router' import clsx from 'clsx' import { OrganizationEventOrigin, @@ -6,9 +7,8 @@ import { OrganizationEventType, } from 'qovery-typescript-axios' import { useState } from 'react' -import { Link, useParams } from 'react-router-dom' import { match } from 'ts-pattern' -import { type ValidTargetIds } from '@qovery/domains/event' +import { type ValidTargetIds } from '@qovery/domains/audit-logs/data-access' import { IconEnum } from '@qovery/shared/enums' import { APPLICATION_URL, @@ -24,16 +24,7 @@ import { SETTINGS_URL, SETTINGS_WEBHOOKS, } from '@qovery/shared/routes' -import { - CodeDiffEditor, - CodeEditor, - type DiffStats, - Icon, - IconAwesomeEnum, - Skeleton, - Tooltip, - Truncate, -} from '@qovery/shared/ui' +import { CodeDiffEditor, CodeEditor, type DiffStats, Icon, Skeleton, Tooltip, Truncate } from '@qovery/shared/ui' import { dateFullFormat, dateUTCString } from '@qovery/shared/util-dates' import { twMerge, upperCaseFirstLetter } from '@qovery/shared/util-js' @@ -71,7 +62,7 @@ export const getSourceIcon = (origin?: OrganizationEventOrigin) => { export function RowEvent(props: RowEventProps) { const { event, expanded, setExpanded, isPlaceholder, columnsWidth, validTargetIds } = props - const { organizationId = '' } = useParams() + const { organizationId = '' } = useParams({ strict: false }) const [diffStats, setDiffStats] = useState({ additions: 0, deletions: 0 }) // Check if target still exists @@ -163,7 +154,7 @@ export function RowEvent(props: RowEventProps) { OrganizationEventType.DEPLOYED_DRY_RUN, OrganizationEventType.TERRAFORM_FORCE_UNLOCK_SUCCEEDED, OrganizationEventType.TERRAFORM_MIGRATE_STATE_SUCCEEDED, - () => + () => ) .with( OrganizationEventType.DELETE_QUEUED, @@ -175,7 +166,7 @@ export function RowEvent(props: RowEventProps) { OrganizationEventType.FORCE_RUN_QUEUED_DELETE, OrganizationEventType.FORCE_RUN_QUEUED_DEPLOY, OrganizationEventType.FORCE_RUN_QUEUED_STOP, - () => + () => ) .with( OrganizationEventType.TRIGGER_CANCEL, @@ -193,20 +184,20 @@ export function RowEvent(props: RowEventProps) { OrganizationEventType.TRIGGER_STOP, OrganizationEventType.TRIGGER_TERRAFORM_FORCE_UNLOCK, OrganizationEventType.TRIGGER_TERRAFORM_MIGRATE_STATE, - () => + () => ) .with(OrganizationEventType.DELETE, OrganizationEventType.DELETED, () => ( - + )) - .with(OrganizationEventType.CLONE, () => ) - .with(OrganizationEventType.DRY_RUN, () => ) - .with(OrganizationEventType.MAINTENANCE, () => ) + .with(OrganizationEventType.CLONE, () => ) + .with(OrganizationEventType.DRY_RUN, () => ) + .with(OrganizationEventType.MAINTENANCE, () => ) .with(OrganizationEventType.FORCE_RUN_SUCCEEDED, () => ( - + )) - .with(OrganizationEventType.REMOTE_DEBUG, () => ) - .with(OrganizationEventType.SHELL, () => ) - .otherwise(() => (isEventTypeFailed ? : null)) + .with(OrganizationEventType.REMOTE_DEBUG, () => ) + .with(OrganizationEventType.SHELL, () => ) + .otherwise(() => (isEventTypeFailed ? : null)) const isSuccess = match(event.event_type) .with( @@ -226,20 +217,18 @@ export function RowEvent(props: RowEventProps) { data-testid="row-event" className={twMerge( clsx( - 'group grid h-12 cursor-pointer items-center border-b border-b-neutral-200 py-2.5 text-xs font-normal text-neutral-400 last:border-b-0 hover:bg-neutral-100', - isEventTypeFailed && 'bg-red-50 hover:bg-neutral-100', - isSuccess && 'hover:bg-neutral-100' + 'group grid h-12 cursor-pointer items-center border-b border-neutral py-2.5 text-sm font-normal text-neutral last:border-b-0 hover:bg-surface-neutral-subtle' ) )} style={{ gridTemplateColumns: columnsWidth }} onClick={() => setExpanded(!expanded)} > -
+
-
+
{event.timestamp && ( @@ -252,14 +241,14 @@ export function RowEvent(props: RowEventProps) { {eventIcon}
-
+
- - + + {formatEventName(event.event_type ?? '')} {event.sub_target_type && ( - + {' '} : {upperCaseFirstLetter(event.sub_target_type)?.replace(/_/g, ' ')} @@ -267,7 +256,7 @@ export function RowEvent(props: RowEventProps) {
-
+
@@ -313,23 +302,23 @@ export function RowEvent(props: RowEventProps) {
-
+
<>{upperCaseFirstLetter(event.target_type ?? '')?.replace(/_/g, ' ')}
-
- +
+
-
+
-
+
{upperCaseFirstLetter(event.origin)?.replace('_', ' ')} {event.user_agent && } - {getSourceIcon(event.origin)} + {getSourceIcon(event.origin)}
@@ -340,12 +329,12 @@ export function RowEvent(props: RowEventProps) { event.original_change && event.change && event.original_change !== event.change ? ( -
-
+
+
- +{diffStats.additions} - -{diffStats.deletions} - lines changed + +{diffStats.additions} + -{diffStats.deletions} + lines changed
) : ( expanded && ( -
+
= new Set([ 'APPLICATION', @@ -47,7 +46,7 @@ function getTargetIdSelected(selectedItems: SelectedItem[]): string | undefined async function fetchTargetProjects( organizationId: string, targetTypeToSearch: OrganizationEventTargetType, - queryParams: DecodedValueMap + queryParams: AuditLogsParams ): Promise { return fetchTargetsAsync(organizationId, targetTypeToSearch, queryParams, 'projectId') } @@ -56,7 +55,7 @@ async function fetchTargetEnvironments( organizationId: string, projectId: string, targetTypeToSearch: OrganizationEventTargetType, - queryParams: DecodedValueMap + queryParams: AuditLogsParams ): Promise { return fetchTargetsAsync(organizationId, targetTypeToSearch, queryParams, 'environmentId', projectId) } @@ -64,7 +63,7 @@ async function fetchTargetEnvironments( async function fetchTargetsAsync( organizationId: string, targetTypeToSearch: OrganizationEventTargetType, - queryParams: DecodedValueMap, + queryParams: AuditLogsParams, optionIdKey: string, projectId?: string, environmentId?: string @@ -117,7 +116,7 @@ async function fetchTargetsAsync( export async function computeMenusToDisplay( organizationId: string, selectedItems: SelectedItem[], - queryParams?: DecodedValueMap + queryParams?: AuditLogsParams ): Promise { // Early return if queryParams not set if (queryParams === undefined) { @@ -266,7 +265,7 @@ export async function initializeSelectedItemsFromQueryParams( organizationId: string, initialData: HierarchicalMenuItem[], rootFilterKey: string, - queryParams: DecodedValueMap + queryParams: AuditLogsParams ): Promise { const selectedItems: SelectedItem[] = [] const navigationStack: NavigationLevel[] = [ diff --git a/libs/domains/audit-logs/feature/tsconfig.json b/libs/domains/audit-logs/feature/tsconfig.json new file mode 100644 index 00000000000..aafcd6336ba --- /dev/null +++ b/libs/domains/audit-logs/feature/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../../tsconfig.base.json" +} diff --git a/libs/domains/audit-logs/feature/tsconfig.lib.json b/libs/domains/audit-logs/feature/tsconfig.lib.json new file mode 100644 index 00000000000..32110a11100 --- /dev/null +++ b/libs/domains/audit-logs/feature/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": ["node"] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/domains/audit-logs/feature/tsconfig.spec.json b/libs/domains/audit-logs/feature/tsconfig.spec.json new file mode 100644 index 00000000000..1033686367b --- /dev/null +++ b/libs/domains/audit-logs/feature/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/instance-category/instance-category.tsx b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/instance-category/instance-category.tsx index fcd1157bd27..5e3560fceee 100644 --- a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/instance-category/instance-category.tsx +++ b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/instance-category/instance-category.tsx @@ -135,10 +135,10 @@ export function InstanceCategory({ title, attributes }: InstanceCategoryProps) { }} /> - + {title.toUpperCase()} - {getInstanceTypeCategory(title)} - +
@@ -161,7 +161,7 @@ export function InstanceCategory({ title, attributes }: InstanceCategoryProps) { checked={false} disabled /> -
@@ -184,7 +184,7 @@ export function InstanceCategory({ title, attributes }: InstanceCategoryProps) { field.onChange(newValue) }} /> -
diff --git a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.spec.tsx b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.spec.tsx index 031c6226f44..f749471fd72 100644 --- a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.spec.tsx +++ b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.spec.tsx @@ -66,7 +66,7 @@ describe('KarpenterInstanceFilterModal', () => { const mockOnChange = jest.fn() const mockOnClose = jest.fn() - const { userEvent, debug, baseElement } = renderWithProviders( + const { userEvent } = renderWithProviders( ) diff --git a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.tsx b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.tsx index 1e03a1eda4f..14cab5ccc3f 100644 --- a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.tsx +++ b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-filter-modal/karpenter-instance-filter-modal.tsx @@ -241,10 +241,10 @@ function KarpenterInstanceForm({ return ( -
+
-
- Architecture +
+ Architecture
)}
-
-
+
+
Size
{watchSizes.length === 0 ? ( @@ -391,8 +391,8 @@ function KarpenterInstanceForm({ ))}
-
-
+
+
Categories/Families
{Object.keys(watchCategories).length === 0 ? ( @@ -474,14 +474,14 @@ function KarpenterInstanceForm({
-
+
- Selected instance types: {dataFiltered.length} + Selected instance types: {dataFiltered.length} - + @@ -502,7 +502,7 @@ function KarpenterInstanceForm({ )} {dataFiltered.length > 0 && ( -
+
{(!extendSelection ? dataFiltered.slice(0, DISPLAY_LIMIT) : dataFiltered).map((instanceType, index) => ( {instanceType.name} diff --git a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-type-preview/karpenter-instance-type-preview.tsx b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-type-preview/karpenter-instance-type-preview.tsx index c92f241ab8f..4b0f5bf98ea 100644 --- a/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-type-preview/karpenter-instance-type-preview.tsx +++ b/libs/domains/cloud-providers/feature/src/lib/karpenter-instance-type-preview/karpenter-instance-type-preview.tsx @@ -34,7 +34,7 @@ export function KarpenterInstanceTypePreview({ const families = requirements?.find((f) => f.key === 'InstanceFamily') return ( -
+
{defaultServiceArchitecture && architectures && architectures?.values.length > 0 && (

diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.spec.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.spec.tsx index e83fa2bc7bb..18e309eb2f4 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.spec.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.spec.tsx @@ -1,3 +1,4 @@ +import { useRouter } from '@tanstack/react-router' import { useCluster, useClusterRunningStatus } from '@qovery/domains/clusters/feature' import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { useClusterMetrics } from '../hooks/use-cluster-metrics/use-cluster-metrics' @@ -18,6 +19,11 @@ describe('ClusterCardNodeUsage', () => { beforeEach(() => { jest.clearAllMocks() + ;(useRouter as jest.Mock).mockReturnValue({ + buildLocation: jest.fn(() => ({ + href: '/organization/org-123/cluster/cluster-456/settings/resources', + })), + }) }) const setupMocks = (clusterProvider = 'AWS', instanceType = 'KARPENTER', runningStatus = null, metrics = null) => { diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.tsx index 36eebcc81f8..4da8e52f2df 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-node-usage/cluster-card-node-usage.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react' import { match } from 'ts-pattern' import { useCluster, useClusterRunningStatus } from '@qovery/domains/clusters/feature' -import { CLUSTER_SETTINGS_RESOURCES_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes' import { Icon, Link, ProgressBar, Skeleton, Tooltip } from '@qovery/shared/ui' import { calculatePercentage, pluralize } from '@qovery/shared/util-js' import { useClusterMetrics } from '../hooks/use-cluster-metrics/use-cluster-metrics' @@ -65,21 +64,21 @@ export function ClusterCardNodeUsage({ organizationId, clusterId }: ClusterCardN

- + Healthy {pluralize(healthyNodes - warningNodes, 'node', 'nodes')} {healthyNodes - warningNodes}
{warningNodes > 0 && (
- + Warning {pluralize(warningNodes, 'node', 'nodes')} {warningNodes}
)} {deployingNodes > 0 && (
- + Deploying {pluralize(deployingNodes, 'node', 'nodes')} {deployingNodes}
@@ -88,10 +87,10 @@ export function ClusterCardNodeUsage({ organizationId, clusterId }: ClusterCardN ) return ( -
+
-

Nodes usage

+

Nodes usage

- {metrics?.nodes?.length} + {metrics?.nodes?.length}
{match(cluster?.cloud_provider) @@ -111,7 +110,7 @@ export function ClusterCardNodeUsage({ organizationId, clusterId }: ClusterCardN classNameContent={isMaxNodesSizeReached ? 'py-2' : ''} content={ isMaxNodesSizeReached ? ( - + Maximum node usage detected. Review affected services below ) : ( @@ -120,10 +119,12 @@ export function ClusterCardNodeUsage({ organizationId, clusterId }: ClusterCardN } > - + ))} @@ -131,18 +132,16 @@ export function ClusterCardNodeUsage({ organizationId, clusterId }: ClusterCardN
{shouldDisplayMinMaxNodes && ( -
+
min: {cluster?.min_running_nodes} max: {cluster?.max_running_nodes}
)} - {deployingPercentage > 0 && ( - - )} - {healthyPercentage > 0 && } - {warningPercentage > 0 && } + {deployingPercentage > 0 && } + {healthyPercentage > 0 && } + {warningPercentage > 0 && }
diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-resources/cluster-card-resources.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-resources/cluster-card-resources.tsx index 2ef6926a075..b757c610c9b 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-resources/cluster-card-resources.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-resources/cluster-card-resources.tsx @@ -44,24 +44,24 @@ export function ClusterCardResources({ organizationId, clusterId }: ClusterCardR ] return ( -
-

Total cluster resources

-
    +
    +

    Total cluster resources

    +
      {resources.map(({ label, icon, value, isPercentage }) => isPercentage ? (
    • - + {label} -

      +

      {value.used} - + /{value.total} {value.unit} - + {value.percent}%

      @@ -70,13 +70,13 @@ export function ClusterCardResources({ organizationId, clusterId }: ClusterCardR ) : (
    • - + {label} -

      +

      {value.total} - {value.unit} + {value.unit}

    • diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-setup/cluster-card-setup.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-setup/cluster-card-setup.tsx index d5b6df1a023..7afe183788f 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-card-setup/cluster-card-setup.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-card-setup/cluster-card-setup.tsx @@ -1,8 +1,7 @@ +import { Link } from '@tanstack/react-router' import clsx from 'clsx' -import { Link, useLocation } from 'react-router-dom' import { match } from 'ts-pattern' import { useCluster, useClusterRunningStatus, useClusterStatus } from '@qovery/domains/clusters/feature' -import { INFRA_LOGS_URL } from '@qovery/shared/routes' import { Badge, Icon, Skeleton, StatusChip } from '@qovery/shared/ui' import { dateUTCString, timeAgo } from '@qovery/shared/util-dates' import { upperCaseFirstLetter } from '@qovery/shared/util-js' @@ -20,7 +19,6 @@ export function ClusterCardSetup({ organizationId, clusterId }: ClusterCardSetup organizationId: organizationId, clusterId: clusterId, }) - const { pathname } = useLocation() const kubeVersion = runningStatus?.computed_status?.kube_version_status @@ -28,10 +26,10 @@ export function ClusterCardSetup({ organizationId, clusterId }: ClusterCardSetup !deploymentStatus?.is_deployed || !deploymentStatus?.last_deployment_date || !cluster?.created_at || !kubeVersion return ( -
      -

      Cluster setup

      +
      +

      Cluster setup

      @@ -75,11 +73,9 @@ export function ClusterCardSetup({ organizationId, clusterId }: ClusterCardSetup {match(deploymentStatus?.status) @@ -99,7 +95,7 @@ export function ClusterCardSetup({ organizationId, clusterId }: ClusterCardSetup ) .otherwise((s) => upperCaseFirstLetter(s))}{' '} {deploymentStatus?.last_deployment_date && timeAgo(new Date(deploymentStatus.last_deployment_date))} ago - + )} @@ -108,7 +104,7 @@ export function ClusterCardSetup({ organizationId, clusterId }: ClusterCardSetup title={cluster?.created_at && dateUTCString(cluster.created_at)} className="flex h-8 items-center gap-2.5 p-1.5" > - + Created {cluster?.created_at && timeAgo(new Date(cluster.created_at))} ago
      diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.spec.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.spec.tsx index 204e7bf1c45..5476a15a2bd 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.spec.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.spec.tsx @@ -145,7 +145,7 @@ describe('ClusterTableNode', () => { ) - const nodeRow = screen.getByText('node-1').closest('div[class*="bg-yellow-50"]') + const nodeRow = screen.getByText('node-1').closest('div[class*="bg-surface-warning-subtle"]') expect(nodeRow).toBeInTheDocument() }) diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.tsx index b86d640fb50..97a6f49d791 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-node/cluster-table-node.tsx @@ -33,8 +33,8 @@ function MetricProgressBar({
      {totalPercentage}% @@ -49,13 +49,13 @@ function MetricProgressBar({ -
      +
      {type === 'cpu' ? 'CPU' : 'Memory'}
      - + Reserved @@ -63,7 +63,7 @@ function MetricProgressBar({
      -
      +
      Total Available {total} {unit} @@ -75,7 +75,7 @@ function MetricProgressBar({ >
      - +
      @@ -130,16 +130,14 @@ export function ClusterTableNode({ // Using div with flex instead of Table.Root for better alignment with cluster-table-nodepool // Ensures consistent column widths and spacing between both tables return ( -
      -
      -
      Nodes
      -
      CPU
      -
      Memory
      -
      Instance
      -
      Disk
      -
      Age
      +
      +
      +
      Nodes
      +
      CPU
      +
      Memory
      +
      Instance
      +
      Disk
      +
      Age
      {nodes?.map((node) => { @@ -156,9 +154,23 @@ export function ClusterTableNode({ (condition) => condition.type === 'PIDPressure' && condition.status === 'True' ) - const isWarning = isDiskPressure || isMemoryPressure || !isReady || isPIDPressure || nodeWarning + const isRemoving = node.unschedulable + const isDeploying = !isReady && !isRemoving + + const isWarning = + isDiskPressure || + isMemoryPressure || + (!isReady && !isDeploying && !isRemoving) || + isPIDPressure || + nodeWarning const status = () => { + if (isRemoving) { + return 'Removing' + } + if (isDeploying) { + return 'Deploying' + } if (!isWarning) { return 'Ready' } @@ -185,18 +197,23 @@ export function ClusterTableNode({ key={node.name} className={twMerge( clsx( - 'flex divide-x divide-neutral-200 transition-colors hover:bg-neutral-100', - isWarning && 'bg-yellow-50 hover:bg-yellow-50' + 'flex divide-x divide-neutral bg-surface-neutral-subtle transition-colors hover:bg-surface-neutral-componentHover', + isWarning && 'bg-surface-warning-subtle hover:bg-surface-warning-subtle', + isDeploying && 'bg-surface-brand-subtle hover:bg-surface-brand-subtle' ) )} > -
      +
      - {isWarning ? ( - + {isDeploying ? ( + + ) : isRemoving ? ( + + ) : isWarning ? ( + ) : ( - + )} @@ -225,7 +242,7 @@ export function ClusterTableNode({ isPressure={isMemoryPressure} />
      -
      +
      {node.instance_type?.replace('_', ' ')} @@ -239,8 +256,15 @@ export function ClusterTableNode({ {node.labels[KEY_KARPENTER_CAPACITY_TYPE] === 'spot' && ( - - + + @@ -257,21 +281,21 @@ export function ClusterTableNode({
      {formatNumber(mibToGib(node.resources_capacity.ephemeral_storage_mib || 0))} GB {isDiskPressure && ( - + )}
      -
      +
      {timeAgo(new Date(node.created_at), true)}
      diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.spec.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.spec.tsx index 514c18fb9b7..2175a1b7e4d 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.spec.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.spec.tsx @@ -13,126 +13,127 @@ jest.mock('../hooks/use-cluster-metrics/use-cluster-metrics', () => ({ })) describe('ClusterTableNodepool', () => { - it('should render node pool with metrics correctly', () => { - const mockMetrics = { - node_pools: [ - { - name: 'default', + const mockMetrics = { + node_pools: [ + { + name: 'default', + cpu_milli: 4000, + cpu_milli_limit: 8000, + memory_mib: 8192, + memory_mib_limit: 16384, + nodes_count: 2, + }, + ], + nodes: [ + { + name: 'node-1', + labels: { + 'karpenter.sh/nodepool': 'default', + }, + metrics_usage: { + cpu_milli_usage: 2000, + memory_mib_working_set_usage: 4096, + }, + resources_allocated: { + request_cpu_milli: 2000, + request_memory_mib: 4096, + limit_cpu_milli: 4000, + limit_memory_mib: 8192, + }, + resources_capacity: { cpu_milli: 4000, - cpu_milli_limit: 8000, memory_mib: 8192, - memory_mib_limit: 16384, - nodes_count: 2, + ephemeral_storage_mib: 20480, + pods: 110, }, - ], - nodes: [ - { - name: 'node-1', - labels: { - 'karpenter.sh/nodepool': 'default', - }, - metrics_usage: { - cpu_milli_usage: 2000, - memory_mib_working_set_usage: 4096, - }, - resources_allocated: { - request_cpu_milli: 2000, - request_memory_mib: 4096, - limit_cpu_milli: 4000, - limit_memory_mib: 8192, - }, - resources_capacity: { - cpu_milli: 4000, - memory_mib: 8192, - ephemeral_storage_mib: 20480, - pods: 110, - }, - resources_allocatable: { - cpu_milli: 4000, - memory_mib: 8192, - ephemeral_storage_mib: 20480, - pods: 110, - }, - conditions: [ - { - type: 'Ready', - status: 'True', - last_heartbeat_time: 1704067200000, - last_transition_time: 1704067200000, - message: '', - reason: '', - }, - ], - addresses: [], - annotations: {}, - architecture: 'amd64', - created_at: 1704067200000, - kubelet_version: 'v1.24.0', - operating_system: 'linux', - kernel_version: '5.15.0', - os_image: 'Ubuntu 22.04 LTS', - pods: [], - taints: [], - unschedulable: false, + resources_allocatable: { + cpu_milli: 4000, + memory_mib: 8192, + ephemeral_storage_mib: 20480, + pods: 110, }, - { - name: 'node-2', - labels: { - 'karpenter.sh/nodepool': 'default', - }, - metrics_usage: { - cpu_milli_usage: 2000, - memory_mib_working_set_usage: 4096, - }, - resources_allocated: { - request_cpu_milli: 2000, - request_memory_mib: 4096, - limit_cpu_milli: 4000, - limit_memory_mib: 8192, - }, - resources_capacity: { - cpu_milli: 4000, - memory_mib: 8192, - ephemeral_storage_mib: 20480, - pods: 110, + conditions: [ + { + type: 'Ready', + status: 'True', + last_heartbeat_time: 1704067200000, + last_transition_time: 1704067200000, + message: '', + reason: '', }, - resources_allocatable: { - cpu_milli: 4000, - memory_mib: 8192, - ephemeral_storage_mib: 20480, - pods: 110, - }, - conditions: [ - { - type: 'Ready', - status: 'True', - last_heartbeat_time: 1704067200000, - last_transition_time: 1704067200000, - message: '', - reason: '', - }, - ], - addresses: [], - annotations: {}, - architecture: 'amd64', - created_at: 1704067200000, - kubelet_version: 'v1.24.0', - operating_system: 'linux', - kernel_version: '5.15.0', - os_image: 'Ubuntu 22.04 LTS', - pods: [], - taints: [], - unschedulable: false, + ], + addresses: [], + annotations: {}, + architecture: 'amd64', + created_at: 1704067200000, + kubelet_version: 'v1.24.0', + operating_system: 'linux', + kernel_version: '5.15.0', + os_image: 'Ubuntu 22.04 LTS', + pods: [], + taints: [], + unschedulable: false, + }, + { + name: 'node-2', + labels: { + 'karpenter.sh/nodepool': 'default', }, - ], - } - - const mockRunningStatus = { - computed_status: { - node_warnings: {}, + metrics_usage: { + cpu_milli_usage: 2000, + memory_mib_working_set_usage: 4096, + }, + resources_allocated: { + request_cpu_milli: 2000, + request_memory_mib: 4096, + limit_cpu_milli: 4000, + limit_memory_mib: 8192, + }, + resources_capacity: { + cpu_milli: 4000, + memory_mib: 8192, + ephemeral_storage_mib: 20480, + pods: 110, + }, + resources_allocatable: { + cpu_milli: 4000, + memory_mib: 8192, + ephemeral_storage_mib: 20480, + pods: 110, + }, + conditions: [ + { + type: 'Ready', + status: 'True', + last_heartbeat_time: 1704067200000, + last_transition_time: 1704067200000, + message: '', + reason: '', + }, + ], + addresses: [], + annotations: {}, + architecture: 'amd64', + created_at: 1704067200000, + kubelet_version: 'v1.24.0', + operating_system: 'linux', + kernel_version: '5.15.0', + os_image: 'Ubuntu 22.04 LTS', + pods: [], + taints: [], + unschedulable: false, }, - } + ], + } + const mockRunningStatus = { + computed_status: { + node_warnings: {}, + }, + } + + beforeEach(() => { + jest.clearAllMocks() ;(useClusterMetrics as jest.Mock).mockReturnValue({ data: mockMetrics, }) @@ -144,7 +145,9 @@ describe('ClusterTableNodepool', () => { cloud_provider: 'AWS', }, }) + }) + it('should render node pool with metrics correctly', () => { renderWithProviders() expect(screen.getByText('Default nodepool')).toBeInTheDocument() diff --git a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.tsx b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.tsx index 7be1777da06..f25e96aa263 100644 --- a/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.tsx +++ b/libs/domains/cluster-metrics/feature/src/lib/cluster-table-nodepool/cluster-table-nodepool.tsx @@ -4,7 +4,6 @@ import { type ClusterNodeDto } from 'qovery-ws-typescript-axios' import { useMemo } from 'react' import { match } from 'ts-pattern' import { useCluster, useClusterRunningStatus } from '@qovery/domains/clusters/feature' -import { CLUSTER_SETTINGS_RESOURCES_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes' import { Icon, Link, ProgressBar, Tooltip } from '@qovery/shared/ui' import { calculatePercentage, pluralize, upperCaseFirstLetter } from '@qovery/shared/util-js' import { ClusterTableNode } from '../cluster-table-node/cluster-table-node' @@ -51,33 +50,31 @@ function SystemNodepool({ organizationId, clusterId, untrackedNodes, nodeWarning return ( <>
      -
      System & infrastructure nodes (1)
      -
      +
      System & infrastructure nodes (1)
      +
      These nodes run essential cluster services such as networking, autoscaling, and monitoring.
      -
      +
      0 ? 'Warning' : 'Ready'}> {untrackedMetrics.nodesWarningCount > 0 ? ( - + ) : ( - + )}
      -
      - Karpenter node group -
      -
      +
      Karpenter node group
      +
      EKS managed node group that hosts the Karpenter controller and other essential cluster infrastructure components.
      @@ -86,51 +83,58 @@ function SystemNodepool({ organizationId, clusterId, untrackedNodes, nodeWarning
      -
      +
      - - + + - {untrackedMetrics.cpuReserved} vCPU + {untrackedMetrics.cpuReserved} vCPU
      -
      +
      - - + + - {untrackedMetrics.memoryReserved} GB + {untrackedMetrics.memoryReserved} GB
      - - + + - {untrackedMetrics.nodesCount}{' '} + {untrackedMetrics.nodesCount}{' '} {pluralize(untrackedMetrics.nodesCount, 'node', 'nodes')} @@ -143,7 +147,7 @@ function SystemNodepool({ organizationId, clusterId, untrackedNodes, nodeWarning 0 && (
      - + Healthy{' '} {pluralize(untrackedMetrics.nodesCount - untrackedMetrics.nodesWarningCount, 'node', 'nodes')} @@ -156,14 +160,14 @@ function SystemNodepool({ organizationId, clusterId, untrackedNodes, nodeWarning )} {untrackedMetrics.nodesWarningCount > 0 && (
      - + Warning {pluralize(untrackedMetrics.nodesWarningCount, 'node', 'nodes')} {untrackedMetrics.nodesWarningCount}
      )} {untrackedMetrics.nodesDeployingCount > 0 && (
      - + Deploying {pluralize(untrackedMetrics.nodesDeployingCount, 'node', 'nodes')} {untrackedMetrics.nodesWarningCount}
      @@ -173,14 +177,12 @@ function SystemNodepool({ organizationId, clusterId, untrackedNodes, nodeWarning classNameContent="w-[157px] px-2.5 py-1.5" > - {deployingPercentage > 0 && ( - - )} + {deployingPercentage > 0 && } {nodesHealthyPercentage - nodesWarningPercentage > 0 && ( - + )} {nodesWarningPercentage > 0 && ( - + )} @@ -214,7 +216,7 @@ function MetricProgressBar({ type, capacity, capacityRaw, limit, limitRaw, unit -
      +
      {type === 'cpu' ? 'CPU' : 'Memory'} nodepool
      @@ -225,7 +227,11 @@ function MetricProgressBar({ type, capacity, capacityRaw, limit, limitRaw, unit Reserved @@ -236,7 +242,7 @@ function MetricProgressBar({ type, capacity, capacityRaw, limit, limitRaw, unit
      - + Limit @@ -245,12 +251,12 @@ function MetricProgressBar({ type, capacity, capacityRaw, limit, limitRaw, unit
      {isLimitReached && !isCapacityReached && ( -
      +
      Resource limit nearly reached; further node deployments will not be possible
      )} {isCapacityReached && ( -
      +
      Resource reserved exceed the limit; further node deployments will not be possible
      )} @@ -260,12 +266,9 @@ function MetricProgressBar({ type, capacity, capacityRaw, limit, limitRaw, unit > {isCapacityReached ? ( - + ) : ( - + )} @@ -303,8 +306,8 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable {nodePools && nodePools.length > 0 && (
      -
      Application node pools ({nodePools.length})
      -
      +
      Application node pools ({nodePools.length})
      +
      These node pools host your application workloads. Each pool can be customized to fit your scaling, performance, and cost requirements.
      @@ -324,25 +327,25 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable -
      +
      0 ? 'Warning' : 'Ready'}> {metrics.nodesWarningCount > 0 ? ( - + ) : ( - + )}
      -
      +
      {upperCaseFirstLetter(nodePool.name)} nodepool
      -
      +
      {nodePool.name === 'default' ? 'A versatile node pool used for general application deployments. Suitable for most workloads.' : nodePool.name === 'stable' @@ -356,16 +359,16 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable
      -
      +
      - - + + - {metrics.cpuUsed} vCPU + {metrics.cpuUsed} vCPU {metrics.cpuTotal ? ( (limit: {metrics.cpuTotal}) @@ -383,14 +386,12 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable .otherwise(() => ( - + ))} @@ -404,13 +405,13 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable unit="vCPU" />
      -
      +
      - - + + - {metrics.memoryUsed} GB + {metrics.memoryUsed} GB {metrics.memoryTotal ? ( ` (limit: ${metrics.memoryTotal})` @@ -428,14 +429,12 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable .otherwise(() => ( - + ))} @@ -450,23 +449,30 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable />
      - - + + - {metrics.nodesCount}{' '} + {metrics.nodesCount}{' '} {pluralize(metrics.nodesCount, 'node', 'nodes')} @@ -476,7 +482,7 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable {metrics.nodesCount - metrics.nodesWarningCount - metrics.nodesDeployingCount > 0 && (
      - + Healthy {pluralize(metrics.nodesCount - metrics.nodesWarningCount, 'node', 'nodes')} @@ -488,14 +494,14 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable )} {metrics.nodesWarningCount > 0 && (
      - + Warning {pluralize(metrics.nodesWarningCount, 'node', 'nodes')} {metrics.nodesWarningCount}
      )} {metrics.nodesDeployingCount > 0 && (
      - + Deploying {pluralize(metrics.nodesDeployingCount, 'node', 'nodes')} {metrics.nodesWarningCount}
      @@ -505,14 +511,12 @@ export function ClusterTableNodepool({ organizationId, clusterId }: ClusterTable classNameContent="w-[157px] px-2.5 py-1.5" > - {deployingPercentage > 0 && ( - - )} + {deployingPercentage > 0 && } {nodesHealthyPercentage - nodesWarningPercentage > 0 && ( - + )} {nodesWarningPercentage > 0 && ( - + )} diff --git a/libs/domains/clusters/data-access/src/lib/domains-clusters-data-access.ts b/libs/domains/clusters/data-access/src/lib/domains-clusters-data-access.ts index f1fc7c1d929..1b9b43a1ee4 100644 --- a/libs/domains/clusters/data-access/src/lib/domains-clusters-data-access.ts +++ b/libs/domains/clusters/data-access/src/lib/domains-clusters-data-access.ts @@ -7,10 +7,12 @@ import { type ClusterRequest, type ClusterRoutingTableRequest, ClustersApi, + OrganizationMainCallsApi, } from 'qovery-typescript-axios' import { type ClusterMetricsDto, type ClusterStatusDto } from 'qovery-ws-typescript-axios' const clusterApi = new ClustersApi() +const organizationApi = new OrganizationMainCallsApi() export const clusters = createQueryKeys('clusters', { list: ({ organizationId }: { organizationId: string }) => ({ @@ -76,6 +78,18 @@ export const clusters = createQueryKeys('clusters', { return response.data }, }), + listServices: ({ organizationId, clusterId }: { organizationId: string; clusterId: string }) => ({ + queryKey: [organizationId, clusterId], + async queryFn() { + const response = await organizationApi.listServicesByOrganizationId( + organizationId, + undefined, + undefined, + clusterId + ) + return response.data.results + }, + }), runningStatus: ({ organizationId, clusterId }: { organizationId: string; clusterId: string }) => ({ queryKey: [organizationId, clusterId], // NOTE: Value is set by WebSocket diff --git a/libs/domains/clusters/feature/src/index.ts b/libs/domains/clusters/feature/src/index.ts index 8c51f4ce16b..19395fe26af 100644 --- a/libs/domains/clusters/feature/src/index.ts +++ b/libs/domains/clusters/feature/src/index.ts @@ -1,11 +1,15 @@ -export * from './lib/cluster-eks-settings/cluser-eks-settings' +export * from './lib/cluster-eks-settings/cluster-eks-settings' export * from './lib/cluster-eks-settings/cluster-eks-settings-form.utils' export * from './lib/cluster-type/cluster-type' export * from './lib/cluster-delete-modal/cluster-delete-modal' export * from './lib/cluster-installation-guide-modal/cluster-installation-guide-modal' -export * from './lib/cluster-action-toolbar/cluster-action-toolbar' +export * from './lib/cluster-actions/cluster-actions' export * from './lib/cluster-avatar/cluster-avatar' +export * from './lib/cluster-advanced-settings/cluster-advanced-settings' +export * from './lib/cluster-advanced-settings/cluster-advanced-settings-feature' +export * from './lib/cluster-advanced-settings/init-form-values' export * from './lib/cluster-setup/cluster-setup' +export * from './lib/cluster-need-redeploy-flag/cluster-need-redeploy-flag' export * from './lib/cluster-card/cluster-card' export * from './lib/cluster-credentials-modal/cluster-credentials-modal' export * from './lib/credentials-list-clusters-modal/credentials-list-clusters-modal' @@ -13,9 +17,24 @@ export * from './lib/nodepools-resources-settings/nodepools-resources-settings' export * from './lib/kubeconfig-preview/kubeconfig-preview' export * from './lib/cluster-migration-modal/cluster-migration-modal' export * from './lib/cluster-terminal/cluster-terminal' -export * from './lib/cluster-terminal/cluster-terminal-provider' export * from './lib/scaleway-static-ip/scaleway-static-ip' +export * from './lib/cluster-network-settings/cluster-network-settings' +export * from './lib/cluster-card-feature/cluster-card-feature' export * from './lib/cluster-deployment-progress-card/cluster-deployment-progress-card' +export * from './lib/section-production-health/section-production-health' +export * from './lib/cluster-logs/cluster-log-row/cluster-log-row' +export * from './lib/cluster-logs/cluster-header-logs/cluster-header-logs' +export * from './lib/cluster-logs/cluster-logs-list/cluster-logs-list' +export * from './lib/cluster-creation-flow/cluster-new/cluster-new' +export * from './lib/cluster-creation-flow/step-general/step-general' +export * from './lib/cluster-creation-flow/step-kubeconfig/step-kubeconfig' +export * from './lib/cluster-creation-flow/step-eks/step-eks' +export * from './lib/cluster-creation-flow/step-resources/step-resources' +export * from './lib/cluster-creation-flow/step-features/step-features' +export * from './lib/cluster-creation-flow/step-summary/step-summary' +export * from './lib/cluster-creation-flow/cluster-creation-flow' +export * from './lib/cluster-general-settings/cluster-general-settings' +export * from './lib/cluster-credentials-settings/cluster-credentials-settings' export * from './lib/hooks/use-cluster-cloud-provider-info/use-cluster-cloud-provider-info' export * from './lib/hooks/use-cluster-routing-table/use-cluster-routing-table' export * from './lib/hooks/use-cluster-kubeconfig/use-cluster-kubeconfig' @@ -42,3 +61,4 @@ export * from './lib/hooks/use-cluster-running-status/use-cluster-running-status export * from './lib/hooks/use-cluster-running-status-socket/use-cluster-running-status-socket' export * from './lib/gpu-resources-settings/gpu-resources-settings' export * from './lib/utils/has-gpu-instance' +export * from './lib/cluster-resources-settings/cluster-resources-settings' diff --git a/libs/domains/clusters/feature/src/lib/cluster-access-modal/__snapshots__/cluster-access-modal.spec.tsx.snap b/libs/domains/clusters/feature/src/lib/cluster-access-modal/__snapshots__/cluster-access-modal.spec.tsx.snap index c5a2c2622c0..e8e4946d6b2 100644 --- a/libs/domains/clusters/feature/src/lib/cluster-access-modal/__snapshots__/cluster-access-modal.spec.tsx.snap +++ b/libs/domains/clusters/feature/src/lib/cluster-access-modal/__snapshots__/cluster-access-modal.spec.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`ClusterAccessModal should match snapshot 1`] = ` @@ -7,12 +7,12 @@ exports[`ClusterAccessModal should match snapshot 1`] = ` class="flex flex-col p-5" >

      Access to your cluster

      This section explains how to connect to your cluster using kubectl, k9s. @@ -21,7 +21,7 @@ exports[`ClusterAccessModal should match snapshot 1`] = ` class="flex flex-col gap-4" >

    • Download and install the Qovery CLI (or update its version to the latest version)