diff --git a/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx index ac1bc4ca3bf..35a2916c351 100644 --- a/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx +++ b/apps/console-v5/src/app/components/header/breadcrumbs/breadcrumbs.tsx @@ -1,6 +1,7 @@ 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 { Avatar } from '@qovery/shared/ui' @@ -9,7 +10,7 @@ import { BreadcrumbItem, type BreadcrumbItemData } from './breadcrumb-item' export function Breadcrumbs() { const { buildLocation } = useRouter() - const { organizationId = '', clusterId = '', projectId = '' } = useParams({ strict: false }) + const { organizationId = '', clusterId = '', projectId = '', environmentId = '' } = useParams({ strict: false }) const { data: organizations = [] } = useOrganizations({ enabled: true, @@ -18,6 +19,7 @@ export function Breadcrumbs() { 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 }) // Necessary to keep the organization from client by Qovery team const allOrganizations = @@ -59,6 +61,18 @@ export function Breadcrumbs() { }).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 currentCluster = useMemo( () => clusterItems.find((cluster) => cluster.id === clusterId), [clusterId, clusterItems] @@ -69,6 +83,11 @@ export function Breadcrumbs() { [projectId, projectItems] ) + const currentEnvironment = useMemo( + () => environmentItems.find((environment) => environment.id === environmentId), + [environmentId, environmentItems] + ) + const breadcrumbData: Array<{ item: BreadcrumbItemData; items: BreadcrumbItemData[] }> = [] if (currentOrg) { @@ -106,6 +125,13 @@ export function Breadcrumbs() { }) } + if (currentEnvironment) { + breadcrumbData.push({ + item: currentEnvironment, + items: environmentItems, + }) + } + return (
{breadcrumbData.map((data, index) => ( diff --git a/apps/console-v5/src/app/components/header/header.tsx b/apps/console-v5/src/app/components/header/header.tsx index 08b8b450f0c..5d95d8fc989 100644 --- a/apps/console-v5/src/app/components/header/header.tsx +++ b/apps/console-v5/src/app/components/header/header.tsx @@ -22,9 +22,7 @@ export function Header() {
- {/* Loading...
}> */} - {/* */}
diff --git a/apps/console-v5/src/routeTree.gen.ts b/apps/console-v5/src/routeTree.gen.ts index e4bd1fa622b..af23f92bf3e 100644 --- a/apps/console-v5/src/routeTree.gen.ts +++ b/apps/console-v5/src/routeTree.gen.ts @@ -40,6 +40,7 @@ import { Route as AuthenticatedOrganizationOrganizationIdClusterNewRouteImport } import { Route as AuthenticatedOrganizationOrganizationIdClustersRouteImport } from './routes/_authenticated/organization/$organizationId/clusters' import { Route as AuthenticatedOrganizationOrganizationIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/index' import { Route as AuthenticatedOrganizationOrganizationIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/overview' +import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/index' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' import { Route as AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsDangerZoneRouteImport } from './routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings/danger-zone' @@ -470,6 +471,12 @@ const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironm path: '/project/$projectId/environment/$environmentId/overview', getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, } as any) +const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute = + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRouteImport.update({ + id: '/project/$projectId/environment/$environmentId/deployments', + path: '/project/$projectId/environment/$environmentId/deployments', + getParentRoute: () => AuthenticatedOrganizationOrganizationIdRouteRoute, + } as any) const AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute = AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteImport.update({ id: '/project/$projectId/environment/$environmentId/settings', @@ -581,6 +588,7 @@ export interface FileRoutesByFullPath { '/organization/$organizationId/cluster/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute '/organization/$organizationId/project/$projectId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren + '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute '/organization/$organizationId/project/$projectId/environment/$environmentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute @@ -647,6 +655,7 @@ export interface FileRoutesByTo { '/organization/$organizationId/cluster/$clusterId/settings': typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdSettingsIndexRoute '/organization/$organizationId/cluster/create/$slug': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute '/organization/$organizationId/project/$projectId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute + '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute '/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute '/organization/$organizationId/project/$projectId/environment/$environmentId': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute @@ -723,6 +732,7 @@ export interface FileRoutesById { '/_authenticated/organization/$organizationId/cluster/create/$slug/': typeof AuthenticatedOrganizationOrganizationIdClusterCreateSlugIndexRoute '/_authenticated/organization/$organizationId/project/$projectId/settings/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdSettingsIndexRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/': typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute @@ -799,6 +809,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/cluster/create/$slug/' | '/organization/$organizationId/project/$projectId/settings/' | '/organization/$organizationId/project/$projectId/environment/$environmentId/settings' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' | '/organization/$organizationId/project/$projectId/environment/$environmentId/overview' | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables' | '/organization/$organizationId/project/$projectId/environment/$environmentId' @@ -865,6 +876,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/cluster/$clusterId/settings' | '/organization/$organizationId/cluster/create/$slug' | '/organization/$organizationId/project/$projectId/settings' + | '/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' | '/organization/$organizationId/project/$projectId/environment/$environmentId/overview' | '/organization/$organizationId/project/$projectId/environment/$environmentId/variables' | '/organization/$organizationId/project/$projectId/environment/$environmentId' @@ -940,6 +952,7 @@ export interface FileRouteTypes { | '/_authenticated/organization/$organizationId/cluster/create/$slug/' | '/_authenticated/organization/$organizationId/project/$projectId/settings/' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings' + | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/variables' | '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/' @@ -1428,6 +1441,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRouteImport 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/environment/$environmentId/settings': { id: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/settings' path: '/project/$projectId/environment/$environmentId/settings' @@ -1685,6 +1705,7 @@ interface AuthenticatedOrganizationOrganizationIdRouteRouteChildren { AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdClusterClusterIdIndexRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute: typeof AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdIndexRoute @@ -1723,6 +1744,8 @@ const AuthenticatedOrganizationOrganizationIdRouteRouteChildren: AuthenticatedOr AuthenticatedOrganizationOrganizationIdProjectProjectIdIndexRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRoute: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdSettingsRouteRouteWithChildren, + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute: + AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdDeploymentsRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute: AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdOverviewRoute, AuthenticatedOrganizationOrganizationIdProjectProjectIdEnvironmentEnvironmentIdVariablesRoute: 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..e28fe96c3ae --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments.tsx @@ -0,0 +1,31 @@ +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' + +export const Route = createFileRoute( + '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments' +)({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+
+
+
+ Deployments +
+
+
+
+ }> + + +
+
+
+ ) +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/route.tsx index d1bd7e65b6f..9963bbbe636 100644 --- a/apps/console-v5/src/routes/_authenticated/organization/route.tsx +++ b/apps/console-v5/src/routes/_authenticated/organization/route.tsx @@ -114,6 +114,12 @@ const ENVIRONMENT_TABS: NavigationTab[] = [ 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', diff --git a/libs/domains/environments/feature/src/index.ts b/libs/domains/environments/feature/src/index.ts index c6bb2f683a3..6f4873f82c1 100644 --- a/libs/domains/environments/feature/src/index.ts +++ b/libs/domains/environments/feature/src/index.ts @@ -40,3 +40,4 @@ export * from './lib/settings-deployment-rules/settings-deployment-rules' export * from './lib/settings-general/settings-general' export * from './lib/settings-preview-environments/settings-preview-environments' export * from './lib/settings-danger-zone/settings-danger-zone' +export * from './lib/environment-deployment-list/environment-deployment-list-skeleton' diff --git a/libs/domains/environments/feature/src/lib/environment-deployment-list/dropdown-services/dropdown-services.tsx b/libs/domains/environments/feature/src/lib/environment-deployment-list/dropdown-services/dropdown-services.tsx index 14bef086d0a..ac2ab1d37f9 100644 --- a/libs/domains/environments/feature/src/lib/environment-deployment-list/dropdown-services/dropdown-services.tsx +++ b/libs/domains/environments/feature/src/lib/environment-deployment-list/dropdown-services/dropdown-services.tsx @@ -1,4 +1,5 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu' +import { Link } from '@tanstack/react-router' import clsx from 'clsx' import { AnimatePresence, motion } from 'framer-motion' import { @@ -9,7 +10,6 @@ import { type QueuedDeploymentRequestWithStagesStagesInner, } from 'qovery-typescript-axios' import { useState } from 'react' -import { Link, useLocation } from 'react-router-dom' import { P, match } from 'ts-pattern' import { type AnyService } from '@qovery/domains/services/data-access' import { ServiceAvatar } from '@qovery/domains/services/feature' @@ -35,7 +35,6 @@ const MAX_VISIBLE_STAGES = 4 // for the DropdownMenu when using Radix, inspired by the discussion in this issue: // https://github.com/radix-ui/primitives/issues/1294 export function DropdownServices({ environment, deploymentHistory, stages }: DropdownServicesProps) { - const { pathname } = useLocation() const [open, setOpen] = useState(false) const [currentIndex, setCurrentIndex] = useState() const [direction, setDirection] = useState(0) @@ -113,7 +112,7 @@ export function DropdownServices({ environment, deploymentHistory, stages }: Dro }} onPointerLeave={() => setOpen(false)} style={{ pointerEvents: 'auto' }} - className="flex items-center outline-none after:block after:h-[1px] after:w-0.5 after:bg-neutral-250 after:content-[''] last:after:hidden focus:outline-none" + className="flex items-center py-2 outline-none after:block after:h-[1px] after:w-0.5 after:border-b after:border-neutral after:content-[''] last:after:hidden focus:outline-none" > @@ -154,7 +153,7 @@ export function DropdownServices({ environment, deploymentHistory, stages }: Dro onPointerEnter={() => setOpen(true)} onPointerLeave={() => setOpen(false)} className={clsx( - 'relative flex max-h-96 w-56 animate-[scalein_0.18s_ease_both] flex-col overflow-y-scroll rounded-md bg-neutral-50 p-2 shadow-lg shadow-gray-900/10', + 'relative flex max-h-96 w-56 animate-[scalein_0.18s_ease_both] flex-col overflow-y-scroll rounded-md border border-neutral bg-surface-neutral-subtle shadow-lg shadow-surface-neutral-subtle', { 'hidden opacity-0': currentIndex === undefined, '-left-[26px]': stages.length === 3, @@ -194,12 +193,12 @@ export function DropdownServices({ environment, deploymentHistory, stages }: Dro
-
+
@@ -222,7 +221,7 @@ export function DropdownServices({ environment, deploymentHistory, stages }: Dro -
+
{upperCaseFirstLetter(stage.name)} {match(stage) .with(P.when(isDeploymentStageQueue), () => null) @@ -240,7 +239,7 @@ export function DropdownServices({ environment, deploymentHistory, stages }: Dro return ( {service.details && ( ( {service.details && ( ) } - -export default EnvironmentDeploymentListSkeleton diff --git a/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.spec.tsx b/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.spec.tsx index a9e81e2629e..ba8b1b8820a 100644 --- a/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.spec.tsx +++ b/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.spec.tsx @@ -100,9 +100,22 @@ jest.mock('../hooks/use-deployment-queue/use-deployment-queue', () => ({ }), })) +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 }) => ( + {typeof children === 'function' ? children({ isActive: false }) : children} + ), +})) + describe('EnvironmentDeploymentList', () => { it('should render the deployment list', async () => { - renderWithProviders() + renderWithProviders() expect(screen.getByText('Date')).toBeInTheDocument() expect(screen.getByText('Status deployment')).toBeInTheDocument() @@ -116,7 +129,7 @@ describe('EnvironmentDeploymentList', () => { }) it('should render the queue item', async () => { - renderWithProviders() + renderWithProviders() expect(screen.getAllByText('In queue...')[0]).toBeInTheDocument() }) diff --git a/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.tsx b/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.tsx index ea4d187b2f7..f24d39aa4c7 100644 --- a/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.tsx +++ b/libs/domains/environments/feature/src/lib/environment-deployment-list/environment-deployment-list.tsx @@ -1,3 +1,4 @@ +import { useParams } from '@tanstack/react-router' import { type SortingState, createColumnHelper, @@ -12,13 +13,12 @@ import { import clsx from 'clsx' import { type DeploymentHistoryEnvironmentV2, OrganizationEventOrigin, StateEnum } from 'qovery-typescript-axios' import { Fragment, useCallback, useMemo, useState } from 'react' -import { useLocation } from 'react-router-dom' import { P, match } from 'ts-pattern' import { IconEnum } from '@qovery/shared/enums' import { ENVIRONMENT_LOGS_URL, ENVIRONMENT_STAGES_URL } from '@qovery/shared/routes' import { - ActionToolbar, ActionTriggerStatusChip, + Button, DropdownMenu, EmptyState, Icon, @@ -37,7 +37,6 @@ import { useDeploymentHistory } from '../hooks/use-deployment-history/use-deploy import { useDeploymentQueue } from '../hooks/use-deployment-queue/use-deployment-queue' import { useEnvironment } from '../hooks/use-environment/use-environment' import { DropdownServices } from './dropdown-services/dropdown-services' -import { EnvironmentDeploymentListSkeleton } from './environment-deployment-list-skeleton' import { TableFilterTriggerBy } from './table-filter-trigger-by/table-filter-trigger-by' const { Table } = TablePrimitives @@ -52,17 +51,18 @@ export const isDeploymentHistory = (data: unknown): data is DeploymentHistoryEnv ) } -export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeploymentListProps) { - const { data: environment, isFetched: isFetchedEnvironment } = useEnvironment({ environmentId }) +export function EnvironmentDeploymentList() { + const { environmentId = '' } = useParams({ + from: '/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/deployments', + }) + const { data: environment } = useEnvironment({ environmentId, suspense: true }) const logsLink = ENVIRONMENT_LOGS_URL(environment?.organization.id, environment?.project.id, environment?.id) + ENVIRONMENT_STAGES_URL() - const { data: deploymentHistory = [], isFetched: isFetchedDeloymentHistory } = useDeploymentHistory({ environmentId }) - const { data: deploymentHistoryQueue = [], isFetched: isFetchedDeloymentQueue } = useDeploymentQueue({ - environmentId, - }) + const { data: deploymentHistory = [] } = useDeploymentHistory({ environmentId, suspense: true }) + const { data: deploymentHistoryQueue = [] } = useDeploymentQueue({ environmentId, suspense: true }) const { mutate: cancelDeploymentEnvironment } = useCancelDeploymentEnvironment({ projectId: environment?.project.id ?? '', @@ -72,7 +72,6 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme environmentId, }) - const { pathname } = useLocation() const { openModalConfirmation } = useModalConfirmation() const [sorting, setSorting] = useState([]) @@ -109,9 +108,9 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme
{state === 'QUEUED' ? ( -
+
In queue... --
) : ( -
- +
+ {dateFullFormat( isDeploymentHistory(data) ? data.auditing_data.created_at : '', undefined, 'dd MMM, HH:mm a' )} - + {isDeploymentHistory(data) ? data.identifier.execution_id : '--'}
)} - +
{match(state) .with( 'DEPLOYING', @@ -165,12 +164,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme () => ( - + {(isCancelBuildAvailable(state) || state === 'QUEUED') && ( @@ -213,25 +207,22 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme ) .otherwise(() => null)} - - - - - + + + - +
) }, @@ -248,7 +239,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme return ( <> {upperCaseFirstLetter(value)} - {count} + {count} ) }, @@ -276,9 +267,9 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme ) .otherwise(() => undefined)} /> -
- {upperCaseFirstLetter(trigger_action)} - {upperCaseFirstLetter(action_status)} +
+ {upperCaseFirstLetter(trigger_action)} + {upperCaseFirstLetter(action_status)}
) @@ -286,9 +277,9 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme .otherwise(() => (
-
- {upperCaseFirstLetter(trigger_action)} - In queue... +
+ {upperCaseFirstLetter(trigger_action)} + In queue...
)) @@ -338,16 +329,16 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme 'DELETE_QUEUED', 'STOP_QUEUED', 'RESTART_QUEUED', - () => -- + () => -- ) .otherwise(() => ( - + {formatDuration(data.total_duration)} )) } else { - return --- + return --- } }, }), @@ -371,7 +362,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme return (
-
+
{match(origin) .with(OrganizationEventOrigin.GIT, () => ) .with(OrganizationEventOrigin.CONSOLE, () => ) @@ -381,11 +372,11 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme .with(OrganizationEventOrigin.TERRAFORM_PROVIDER, () => ) .otherwise(() => null)}
-
- +
+ - + {origin !== 'CLI' && origin !== 'API' ? upperCaseFirstLetter(origin?.replace('_', ' ')) : origin}
@@ -394,7 +385,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme }, }), ], - [columnHelper, environment, mutationCancelDeployment, pathname] + [columnHelper, environment, mutationCancelDeployment] ) const data = useMemo( @@ -422,34 +413,19 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme }, }) - if (!isFetchedEnvironment || !isFetchedDeloymentHistory || !isFetchedDeloymentQueue) - return - - if ( - isFetchedEnvironment && - isFetchedDeloymentHistory && - isFetchedDeloymentQueue && - !deploymentHistory.length && - !deploymentHistoryQueue.length - ) { - return ( - - ) + if (!deploymentHistory.length && !deploymentHistoryQueue.length) { + return } return (
- + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header, i) => ( @@ -470,9 +446,14 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme > {flexRender(header.column.columnDef.header, header.getContext())} {match(header.column.getIsSorted()) - .with('asc', () => ) - .with('desc', () => ) - .with(false, () => null) + .with('asc', () => ) + .with('desc', () => ) + .with(false, () => ( + + )) .exhaustive()} ) : ( @@ -486,7 +467,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme {table.getRowModel().rows.map((row) => ( - + {row.getVisibleCells().map((cell, i) => ( {column.getIsFiltered() ? ( - {displayValue !== 'API' && displayValue !== 'CLI' - ? displayValue?.toLowerCase().replace('_', ' ') ?? '' - : displayValue} + ) : ( <> @@ -100,7 +105,7 @@ export function TableFilterTriggerBy({ {column.getIsFiltered() ? (
- - Trigger by + + Trigger by {sortedUniqueValues.map( ([value]) => value != null && ( @@ -128,8 +133,8 @@ export function TableFilterTriggerBy({ ) )} -
- From +
+ From {triggeredByValues.map((value) => ( diff --git a/libs/domains/environments/feature/src/lib/environment-list/__snapshots__/environment-list.spec.tsx.snap b/libs/domains/environments/feature/src/lib/environment-list/__snapshots__/environment-list.spec.tsx.snap index feac9733ffd..3d60116954e 100644 --- a/libs/domains/environments/feature/src/lib/environment-list/__snapshots__/environment-list.spec.tsx.snap +++ b/libs/domains/environments/feature/src/lib/environment-list/__snapshots__/environment-list.spec.tsx.snap @@ -3,7 +3,7 @@ exports[`EnvironmentList should match snapshot 1`] = `
{ if (!data || !Array.isArray(data)) return data return [...data].sort((a, b) => diff --git a/libs/domains/services/feature/src/lib/pods-metrics/__snapshots__/pods-metrics.spec.tsx.snap b/libs/domains/services/feature/src/lib/pods-metrics/__snapshots__/pods-metrics.spec.tsx.snap index 7e25c8acba1..33fedf35d32 100644 --- a/libs/domains/services/feature/src/lib/pods-metrics/__snapshots__/pods-metrics.spec.tsx.snap +++ b/libs/domains/services/feature/src/lib/pods-metrics/__snapshots__/pods-metrics.spec.tsx.snap @@ -6,7 +6,7 @@ exports[`PodsMetrics should match snapshot with data 1`] = ` class="overflow-x-scroll rounded border border-neutral-200 xl:overflow-hidden" >
(function Can ) { return ( (function Queue ) { return ( (function Link(props, forwardedRef) { return match(props) - .with({ as: 'button' }, ({ className, children, color, radius, size, variant, as, params, ...rest }) => ( + .with({ as: 'button' }, ({ className, children, color, radius, size, variant, as, params, iconOnly, ...rest }) => ( ['params'] } : {})} > diff --git a/libs/shared/ui/src/lib/components/stage-status-chip/stage-status-chip.tsx b/libs/shared/ui/src/lib/components/stage-status-chip/stage-status-chip.tsx index ff258b22d5f..acdedf530ab 100644 --- a/libs/shared/ui/src/lib/components/stage-status-chip/stage-status-chip.tsx +++ b/libs/shared/ui/src/lib/components/stage-status-chip/stage-status-chip.tsx @@ -81,7 +81,7 @@ export function StageStatusChip({ status, className = '' }: StageStatusChipProps d="M1.5 8.993v0c0-.91.23-1.804.669-2.591A5.098 5.098 0 013.99 4.507s0 0 0 0L9.49 1.2h0a4.863 4.863 0 012.508-.7 4.864 4.864 0 012.51.7l5.5 3.31h0a5.097 5.097 0 011.82 1.892c.439.787.67 1.68.671 2.59v6.015c0 .91-.23 1.804-.669 2.591a5.097 5.097 0 01-1.822 1.895l-5.5 3.307h0c-.763.459-1.628.7-2.508.7-.88 0-1.746-.241-2.51-.7 0 0 0 0 0 0l-5.5-3.31h0a5.098 5.098 0 01-1.82-1.892 5.333 5.333 0 01-.671-2.589V8.993z" > diff --git a/libs/shared/ui/src/lib/components/status-chip/__snapshots__/status-chip.spec.tsx.snap b/libs/shared/ui/src/lib/components/status-chip/__snapshots__/status-chip.spec.tsx.snap index c8863e569d4..c9399604fbb 100644 --- a/libs/shared/ui/src/lib/components/status-chip/__snapshots__/status-chip.spec.tsx.snap +++ b/libs/shared/ui/src/lib/components/status-chip/__snapshots__/status-chip.spec.tsx.snap @@ -67,7 +67,7 @@ exports[`StatusChip should match snapshot for QUEUED status 1`] = ` data-state="closed" > , TableRootProps>(function Table ref ) { return ( -
+