From dd7e49f7809e5f3cc21212861a2dc27eb4f8a296 Mon Sep 17 00:00:00 2001 From: Michelle Bergquist Date: Thu, 5 Dec 2024 17:05:40 -0700 Subject: [PATCH] Add dash navigation to individual aws resource tables --- web/packages/design/src/Tabs/Tabs.ts | 58 ++++++++++ .../status/AwsOidc/AwsOidcDashboard.tsx | 31 ++++-- .../status/AwsOidc/AwsOidcHeader.tsx | 41 +++++-- .../status/AwsOidc/AwsOidcRoutes.tsx | 10 +- .../status/AwsOidc/Details/Agents.tsx | 62 +++++++++++ .../status/AwsOidc/Details/Details.tsx | 51 +++++++++ .../status/AwsOidc/Details/Ec2.tsx | 57 ++++++++++ .../status/AwsOidc/Details/Eks.tsx | 56 ++++++++++ .../status/AwsOidc/Details/Rds.tsx | 101 ++++++++++++++++++ .../status/AwsOidc/Details/Rules.tsx | 62 +++++++++++ .../Integrations/status/AwsOidc/StatCard.tsx | 43 ++++++-- web/packages/teleport/src/config.ts | 16 +++ 12 files changed, 567 insertions(+), 21 deletions(-) create mode 100644 web/packages/design/src/Tabs/Tabs.ts create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Agents.tsx create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Details.tsx create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Ec2.tsx create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Eks.tsx create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rds.tsx create mode 100644 web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rules.tsx diff --git a/web/packages/design/src/Tabs/Tabs.ts b/web/packages/design/src/Tabs/Tabs.ts new file mode 100644 index 0000000000000..b8e7a3dd15826 --- /dev/null +++ b/web/packages/design/src/Tabs/Tabs.ts @@ -0,0 +1,58 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import styled from 'styled-components'; + +import { NavLink } from 'react-router-dom'; + +export const TabsContainer = styled.div` + position: relative; + display: flex; + gap: ${p => p.theme.space[5]}px; + align-items: center; + padding: 0 ${p => p.theme.space[5]}px; + border-bottom: 1px solid ${p => p.theme.colors.spotBackground[0]}; +`; + +export const TabContainer = styled(NavLink)<{ selected?: boolean }>` + padding: ${p => p.theme.space[1] + p.theme.space[2]}px + ${p => p.theme.space[2]}px; + position: relative; + cursor: pointer; + z-index: 2; + opacity: ${p => (p.selected ? 1 : 0.5)}; + transition: opacity 0.3s linear; + color: ${p => p.theme.colors.text.main}; + font-weight: 300; + font-size: 22px; + line-height: ${p => p.theme.space[5]}px; + white-space: nowrap; + text-decoration: none; + + &:hover { + opacity: 1; + } +`; + +export const TabBorder = styled.div` + position: absolute; + bottom: -1px; + background: ${p => p.theme.colors.brand}; + height: 2px; + transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1); +`; diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcDashboard.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcDashboard.tsx index e755de5bc75f8..47f08872cfe07 100644 --- a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcDashboard.tsx +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcDashboard.tsx @@ -33,13 +33,18 @@ import { export function AwsOidcDashboard() { const { statsAttempt, integrationAttempt } = useAwsOidcStatus(); - if (statsAttempt.status == 'processing') { + if ( + statsAttempt.status == 'processing' || + integrationAttempt.status == 'processing' + ) { return ; } - if (statsAttempt.status == 'error') { + + if (integrationAttempt.status == 'error') { return {statsAttempt.statusText}; } - if (!statsAttempt.data) { + + if (!statsAttempt.data || !integrationAttempt.data) { return null; } @@ -48,12 +53,24 @@ export function AwsOidcDashboard() { const { data: integration } = integrationAttempt; return ( - {integration && } +

Auto-Enrollment

- - - + + +
); diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcHeader.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcHeader.tsx index e2c58ab85b50a..7380cb38c2141 100644 --- a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcHeader.tsx +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcHeader.tsx @@ -20,14 +20,23 @@ import React from 'react'; import { Link as InternalLink } from 'react-router-dom'; import { ButtonIcon, Flex, Label, Text } from 'design'; -import { ArrowLeft } from 'design/Icon'; +import { ArrowLeft, ChevronRight } from 'design/Icon'; import { HoverTooltip } from 'design/Tooltip'; +import Link from 'design/Link'; + import cfg from 'teleport/config'; import { getStatusAndLabel } from 'teleport/Integrations/helpers'; import { Integration } from 'teleport/services/integrations'; +import { AwsResource } from 'teleport/Integrations/status/AwsOidc/StatCard'; -export function AwsOidcHeader({ integration }: { integration: Integration }) { +export function AwsOidcHeader({ + integration, + resource = undefined, +}: { + integration: Integration; + resource?: AwsResource; +}) { const { status, labelKind } = getStatusAndLabel(integration); return ( @@ -40,10 +49,30 @@ export function AwsOidcHeader({ integration }: { integration: Integration }) { - - {integration.name} - - diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcRoutes.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcRoutes.tsx index b218fc905706d..689a145656b05 100644 --- a/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcRoutes.tsx +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/AwsOidcRoutes.tsx @@ -23,6 +23,8 @@ import cfg from 'teleport/config'; import { AwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/useAwsOidcStatus'; +import { Details } from 'teleport/Integrations/status/AwsOidc/Details/Details'; + import { AwsOidcDashboard } from './AwsOidcDashboard'; export function AwsOidcRoutes() { @@ -30,7 +32,13 @@ export function AwsOidcRoutes() { + . + */ + +import React from 'react'; + +import Table, { LabelCell } from 'design/DataTable'; + +export function Agents() { + return ( + { + const aStr = a.tags.toString(); + const bStr = b.tags.toString(); + + if (aStr < bStr) { + return -1; + } + if (aStr > bStr) { + return 1; + } + + return 0; + }, + render: ({ tags }) => , + }, + ]} + emptyText="Agents details coming soon" + isSearchable + /> + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Details.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Details.tsx new file mode 100644 index 0000000000000..dd999cd7d92e2 --- /dev/null +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Details.tsx @@ -0,0 +1,51 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import { useParams } from 'react-router'; + +import { FeatureBox } from 'teleport/components/Layout'; +import { AwsOidcHeader } from 'teleport/Integrations/status/AwsOidc/AwsOidcHeader'; +import { useAwsOidcStatus } from 'teleport/Integrations/status/AwsOidc/useAwsOidcStatus'; +import { AwsResource } from 'teleport/Integrations/status/AwsOidc/StatCard'; +import { IntegrationKind } from 'teleport/services/integrations'; +import { Rds } from 'teleport/Integrations/status/AwsOidc/Details/Rds'; +import { Ec2 } from 'teleport/Integrations/status/AwsOidc/Details/Ec2'; +import { Eks } from 'teleport/Integrations/status/AwsOidc/Details/Eks'; + +export function Details() { + const { resourceKind } = useParams<{ + type: IntegrationKind; + name: string; + resourceKind: AwsResource; + }>(); + + const { integrationAttempt } = useAwsOidcStatus(); + const { data: integration } = integrationAttempt; + return ( + + {integration && ( + + )} + {resourceKind == AwsResource.ec2 && } + {resourceKind == AwsResource.eks && } + {resourceKind == AwsResource.rds && } + + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Ec2.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Ec2.tsx new file mode 100644 index 0000000000000..8d91e43a12eca --- /dev/null +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Ec2.tsx @@ -0,0 +1,57 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import Table, { LabelCell } from 'design/DataTable'; + +export function Ec2() { + return ( +
{ + const aStr = a.labels.toString(); + const bStr = b.labels.toString(); + + if (aStr < bStr) { + return -1; + } + if (aStr > bStr) { + return 1; + } + + return 0; + }, + render: ({ labels }) => , + }, + ]} + emptyText="EC2 details coming soon" + isSearchable + /> + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Eks.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Eks.tsx new file mode 100644 index 0000000000000..db5b34372c4e0 --- /dev/null +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Eks.tsx @@ -0,0 +1,56 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; +import Table, { LabelCell } from 'design/DataTable'; + +export function Eks() { + return ( +
{ + const aStr = a.labels.toString(); + const bStr = b.labels.toString(); + + if (aStr < bStr) { + return -1; + } + if (aStr > bStr) { + return 1; + } + + return 0; + }, + render: ({ labels }) => , + }, + ]} + emptyText="EKS details coming soon" + isSearchable + /> + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rds.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rds.tsx new file mode 100644 index 0000000000000..5feb3f8ff1b79 --- /dev/null +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rds.tsx @@ -0,0 +1,101 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { useEffect, useRef } from 'react'; + +import { TabBorder, TabContainer, TabsContainer } from 'design/Tabs/Tabs'; +import { useLocation, useParams } from 'react-router'; + +import { IntegrationKind } from 'teleport/services/integrations'; +import { AwsResource } from 'teleport/Integrations/status/AwsOidc/StatCard'; +import cfg from 'teleport/config'; +import { Rules } from 'teleport/Integrations/status/AwsOidc/Details/Rules'; +import { Agents } from 'teleport/Integrations/status/AwsOidc/Details/Agents'; + +export enum RdsTab { + Agents = 'agents', + Rules = 'rules', +} + +export function Rds() { + const { type, name, resourceKind } = useParams<{ + type: IntegrationKind; + name: string; + resourceKind: AwsResource; + }>(); + + const { search } = useLocation(); + const searchParams = new URLSearchParams(search); + const tab = (searchParams.get('tab') as RdsTab) || RdsTab.Rules; + + const borderRef = useRef(null); + const parentRef = useRef(); + + useEffect(() => { + if (!parentRef.current || !borderRef.current) { + return; + } + + const activeElement = parentRef.current.querySelector( + `[data-tab-id="${tab}"]` + ); + + if (activeElement) { + const parentBounds = parentRef.current.getBoundingClientRect(); + const activeBounds = activeElement.getBoundingClientRect(); + + const left = activeBounds.left - parentBounds.left; + const width = activeBounds.width; + + borderRef.current.style.left = `${left}px`; + borderRef.current.style.width = `${width}px`; + } + }, [tab]); + + return ( + <> + + + Enrollment Rules + + + Agents + + + + {tab == RdsTab.Rules && } + {tab == RdsTab.Agents && } + + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rules.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rules.tsx new file mode 100644 index 0000000000000..9f00e38573f3f --- /dev/null +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/Details/Rules.tsx @@ -0,0 +1,62 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import Table, { LabelCell } from 'design/DataTable'; + +export function Rules() { + return ( +
{ + const aStr = a.tags.toString(); + const bStr = b.tags.toString(); + + if (aStr < bStr) { + return -1; + } + if (aStr > bStr) { + return 1; + } + + return 0; + }, + render: ({ tags }) => , + }, + ]} + emptyText="Rules details coming soon" + isSearchable + /> + ); +} diff --git a/web/packages/teleport/src/Integrations/status/AwsOidc/StatCard.tsx b/web/packages/teleport/src/Integrations/status/AwsOidc/StatCard.tsx index 395debb99ed0f..805f1d24c3bcd 100644 --- a/web/packages/teleport/src/Integrations/status/AwsOidc/StatCard.tsx +++ b/web/packages/teleport/src/Integrations/status/AwsOidc/StatCard.tsx @@ -25,7 +25,15 @@ import * as Icons from 'design/Icon'; import { formatDistanceStrict } from 'date-fns'; -import { ResourceTypeSummary } from 'teleport/services/integrations'; +import styled from 'styled-components'; + +import history from 'teleport/services/history'; + +import { + IntegrationKind, + ResourceTypeSummary, +} from 'teleport/services/integrations'; +import cfg from 'teleport/config'; export enum AwsResource { ec2 = 'ec2', @@ -34,22 +42,29 @@ export enum AwsResource { } type StatCardProps = { + name: string; resource: AwsResource; summary?: ResourceTypeSummary; }; -export function StatCard({ resource, summary }: StatCardProps) { +export function StatCard({ name, resource, summary }: StatCardProps) { const updated = summary?.discoverLastSync ? new Date(summary?.discoverLastSync) : undefined; const term = getResourceTerm(resource); return ( - { + history.push( + cfg.getIntegrationStatusResourcesRoute( + IntegrationKind.AwsOidc, + name, + resource + ) + ); + }} > )} - + ); } @@ -113,3 +128,17 @@ function getResourceTerm(resource: AwsResource): string { return 'Instances'; } } + +export const SelectCard = styled(Card)` + width: 33%; + background-color: ${props => props.theme.colors.levels.surface}; + padding: 12px; + border-radius: ${props => props.theme.radii[2]}px; + border: ${props => `1px solid ${props.theme.colors.levels.surface}`}; + cursor: pointer; + + &:hover { + background-color: ${props => props.theme.colors.levels.elevated}; + box-shadow: ${({ theme }) => theme.boxShadow[2]}; + } +`; diff --git a/web/packages/teleport/src/config.ts b/web/packages/teleport/src/config.ts index 62964f0258031..75e0a6cc54e9e 100644 --- a/web/packages/teleport/src/config.ts +++ b/web/packages/teleport/src/config.ts @@ -20,6 +20,8 @@ import { generatePath } from 'react-router'; import { mergeDeep } from 'shared/utils/highbar'; import { IncludedResourceMode } from 'shared/components/UnifiedResources'; +import { AwsResource } from 'teleport/Integrations/status/AwsOidc/StatCard'; + import generateResourcePath from './generateResourcePath'; import { defaultEntitlements } from './entitlement'; @@ -196,6 +198,8 @@ const cfg = { headlessSso: `/web/headless/:requestId`, integrations: '/web/integrations', integrationStatus: '/web/integrations/status/:type/:name', + integrationStatusResources: + '/web/integrations/status/:type/:name/resources/:resourceKind', integrationEnroll: '/web/integrations/new/:type?', locks: '/web/locks', newLock: '/web/locks/new', @@ -538,6 +542,18 @@ const cfg = { return generatePath(cfg.routes.integrationStatus, { type, name }); }, + getIntegrationStatusResourcesRoute( + type: PluginKind | IntegrationKind, + name: string, + resourceKind: AwsResource + ) { + return generatePath(cfg.routes.integrationStatusResources, { + type, + name, + resourceKind, + }); + }, + getMsTeamsAppZipRoute(clusterId: string, plugin: string) { return generatePath(cfg.api.msTeamsAppZipPath, { clusterId, plugin }); },