Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Navigation/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,21 +285,21 @@
{
id: 'observability',
title: 'Observability',
icon: 'ic-binoculars',

Check failure on line 288 in src/components/Navigation/constants.ts

View workflow job for this annotation

GitHub Actions / ci

Type '"ic-binoculars"' is not assignable to type '"ic-arrow-clockwise" | "ic-73strings" | "ic-aborted" | "ic-activity" | "ic-add" | "ic-alibaba" | "ic-amazon-eks" | "ic-app-group" | "ic-app-template" | "ic-argocd-app" | "ic-arrow-line-down" | ... 262 more ... | "ic-world-globe"'.
items: [
{
title: 'Overview',
dataTestId: 'observability-overview',
id: 'infrastructure-management-overview',
icon: 'ic-speedometer',
href: COMMON_URLS.OBSERVABILITY_OVERVIEW,

Check failure on line 295 in src/components/Navigation/constants.ts

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY_OVERVIEW' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.
},
{
title: 'Customers',
dataTestId: 'observability-vms',
id: 'observability-vms',

Check failure on line 300 in src/components/Navigation/constants.ts

View workflow job for this annotation

GitHub Actions / ci

Type '"observability-vms"' is not assignable to type 'NavigationItemID'.
icon: 'ic-cluster',
href: COMMON_URLS.OBSERVABILITY_LIST,
href: COMMON_URLS.OBSERVABILITY_CUSTOMER_LIST,

Check failure on line 302 in src/components/Navigation/constants.ts

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY_CUSTOMER_LIST' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.
},
],
},
Expand Down
5 changes: 1 addition & 4 deletions src/components/common/navigation/NavigationRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -561,10 +561,7 @@
>
<ResourceBrowser />
</Route>
<Route
key={CommonURLS.OBSERVABILITY_OVERVIEW}
path={CommonURLS.OBSERVABILITY_OVERVIEW}
>
<Route key={CommonURLS.OBSERVABILITY} path={CommonURLS.OBSERVABILITY}>

Check failure on line 564 in src/components/common/navigation/NavigationRoutes.tsx

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.

Check failure on line 564 in src/components/common/navigation/NavigationRoutes.tsx

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.
<Observability />
</Route>
<Route
Expand Down
51 changes: 51 additions & 0 deletions src/components/observability/CustomerList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useMemo } from 'react'

import { FiltersTypeEnum, PaginationEnum, Table, useAsync } from '@devtron-labs/devtron-fe-common-lib/dist'

import { CUSTOMER_TABLE_COLUMN } from './constants'
import { getCustomerListData } from './service'
import { CustomerObservabilityDTO, CustomerTableProps } from './types'

export const CustomerList = () => {
// ASYNC CALLS
const [isFetching, customerData] = useAsync(() => getCustomerListData(), [])

// CONFIGS
const rows = useMemo<CustomerTableProps['rows']>(
() =>
(customerData || []).map((data) => ({
id: `observe_project_${data.id.toString()}`,
data,
})),
[customerData],
)

const filter: CustomerTableProps['filter'] = (
rowData: { id: string; data: CustomerObservabilityDTO },
filterData: { searchKey: string },
) => rowData.data.name.toLowerCase().includes(filterData.searchKey.toLowerCase())

return (
<div className="observability-table-wrapper flexbox-col flex-grow-1 dc__overflow-auto">
<Table<CustomerObservabilityDTO, FiltersTypeEnum.STATE, {}>
id="table__customer-list"
loading={isFetching}
stylesConfig={{ showSeparatorBetweenRows: true }}
columns={CUSTOMER_TABLE_COLUMN}
rows={rows}
filtersVariant={FiltersTypeEnum.STATE}
emptyStateConfig={{
noRowsConfig: {
title: 'No resources found',
subTitle: `No resources found in this cluster for upgrade compatibility check`,
},
}}
filter={filter}
additionalFilterProps={{
initialSortKey: 'name',
}}
paginationVariant={PaginationEnum.PAGINATED}
/>
</div>
)
}
76 changes: 76 additions & 0 deletions src/components/observability/CustomerListCellComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { FunctionComponent, useEffect, useRef } from 'react'
import { Link, useRouteMatch } from 'react-router-dom'

import {
FiltersTypeEnum,
getUrlWithSearchParams,
TableCellComponentProps,
TableSignalEnum,
Tooltip,
} from '@devtron-labs/devtron-fe-common-lib/dist'

import { CustomerObservabilityDTO, ProjectListFields } from './types'

export const CustomerListCellComponent: FunctionComponent<
TableCellComponentProps<CustomerObservabilityDTO, FiltersTypeEnum.STATE, {}>
> = ({
field,
row: {
data: { id, name, status, project, totalVms, activeVms, healthStatus },
},
isRowActive,
signals,
}: TableCellComponentProps<CustomerObservabilityDTO, FiltersTypeEnum.STATE, {}>) => {
const linkRef = useRef<HTMLAnchorElement>(null)

const match = useRouteMatch()

useEffect(() => {
const handleEnter = ({ detail: { activeRowData } }) => {
if (activeRowData.data.id === id) {
linkRef.current?.click()
}
}

if (isRowActive) {
signals.addEventListener(TableSignalEnum.ENTER_PRESSED, handleEnter)
}

return () => {
signals.removeEventListener(TableSignalEnum.ENTER_PRESSED, handleEnter)
}
}, [isRowActive])

switch (field) {
case ProjectListFields.PROJECT_NAME:
return (
<Link
ref={linkRef}
to={getUrlWithSearchParams(match.path, { customer: name })}
className="flex left py-10"
>
<Tooltip content={name}>
<span className="dc__truncate">{name}</span>
</Tooltip>
</Link>
)
case ProjectListFields.STATUS:
return <span className="flex left py-10">{status}</span>
case ProjectListFields.PROJECTS:
return <span className="flex left py-10">{project}</span>
case ProjectListFields.TOTAL_VMS:
return <span className="flex left py-10">{totalVms}</span>
case ProjectListFields.ACTIVE_VMS:
return <span className="flex left py-10">{activeVms}</span>
case ProjectListFields.HEALTH_STATUS:
return (
<div className="flex left py-10">
<Tooltip content={healthStatus}>
<span className="dc__truncate">{healthStatus}</span>
</Tooltip>
</div>
)
default:
return null
}
}
116 changes: 116 additions & 0 deletions src/components/observability/Customers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { useEffect, useState } from 'react'

import {
BreadCrumb,
BreadcrumbText,
ComponentSizeType,
handleUTCTime,
PageHeader,
SearchBar,
useBreadcrumb,
} from '@devtron-labs/devtron-fe-common-lib'

import { CustomerList } from './CustomerList'
import ObservabilityIconComponent from './ObservabilityIcon'

let interval
const Customers = () => {
const [lastDataSyncTimeString, setLastDataSyncTimeString] = useState<React.ReactNode>('')
const [isDataSyncing, setDataSyncing] = useState(false)
const [syncListData, setSyncListData] = useState<boolean>()
// TODO: Remove later
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [fetchingExternalApps, setFetchingExternalApps] = useState<boolean>(false)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [selectedTabIndex, setSelectedTabIndex] = useState(0)
const renderDataSyncingText = () => <span className="dc__loading-dots">Syncing</span>
useEffect(() => {
if (isDataSyncing) {
setLastDataSyncTimeString(renderDataSyncingText)
} else {
const _lastDataSyncTime = Date()
setLastDataSyncTimeString(`Last synced ${handleUTCTime(_lastDataSyncTime, true)}`)
interval = setInterval(() => {
setLastDataSyncTimeString(`Last synced ${handleUTCTime(_lastDataSyncTime, true)}`)
}, 1000)
}
return () => {
if (interval) {
clearInterval(interval)
}
}
}, [isDataSyncing])

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const updateDataSyncing = (loading: boolean): void => {
setDataSyncing(loading)
}

const syncNow = (): void => {
setSyncListData(!syncListData)
}

const renderLastSyncComponent = () => (
<div className="flex fs-13">
{lastDataSyncTimeString && (
<>
<span data-testid="sync-now-text">{lastDataSyncTimeString}</span>
{!isDataSyncing && (
<>
&nbsp;
<button
className="btn btn-link p-0 fw-6 cb-5 mb-2"
type="button"
onClick={syncNow}
data-testid="sync-now-button"
>
Sync now
</button>
</>
)}
</>
)}
{fetchingExternalApps && renderDataSyncingText()}
</div>
)

const { breadcrumbs } = useBreadcrumb({
alias: {
observability: {
component: <ObservabilityIconComponent />,
linked: true,
},
customers: {
component: <BreadcrumbText heading="Customers" isActive />,
linked: false,
},
},
})
const renderBreadcrumbs = () => <BreadCrumb breadcrumbs={breadcrumbs} />
const searchKey = ''
const handleSearch = () => {}
return (
<div className="observability-overview flex-grow-1 dc__overflow-auto bg__secondary">
<PageHeader isBreadcrumbs breadCrumbs={renderBreadcrumbs} />
<div className="flex dc__content-space p-16 w-100 dc__gap-8 ">
<div className="flexbox dc__align-items-center dc__mxw-fit-content">
<SearchBar
containerClassName="w-250"
dataTestId="search-customer-env"
initialSearchText={searchKey}
inputProps={{
placeholder: 'Search customer',
}}
handleEnter={handleSearch}
size={ComponentSizeType.medium}
keyboardShortcut="/"
/>
</div>
{renderLastSyncComponent()}
</div>
<CustomerList />
</div>
)
}

export default Customers
35 changes: 21 additions & 14 deletions src/components/observability/ObservabilityRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import { Redirect, Route, Switch } from 'react-router-dom'
import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'

import { URLS } from '@devtron-labs/devtron-fe-common-lib'

import Project from './Project'
import VMList from './VMList'
import Project from './ProjectObservability/Project'
import Customers from './Customers'
import { Overview } from './Overview'

const ObservabilityRouter: React.FC = () => (
<Switch>
<Route exact path={URLS.OBSERVABILITY_LIST}>
<Project />
</Route>
const ObservabilityRouter: React.FC = () => {
const { path } = useRouteMatch()
return (
<Switch>
<Route exact path={URLS.OBSERVABILITY_OVERVIEW}>

Check failure on line 13 in src/components/observability/ObservabilityRouter.tsx

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY_OVERVIEW' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.
<Overview />
</Route>

<Route exact path={URLS.OBSERVABILITY_OVERVIEW}>
<Project/>
</Route>
<Route exact path={URLS.OBSERVABILITY_CUSTOMER_LIST}>

Check failure on line 17 in src/components/observability/ObservabilityRouter.tsx

View workflow job for this annotation

GitHub Actions / ci

Property 'OBSERVABILITY_CUSTOMER_LIST' does not exist on type '{ readonly LOGIN: "/login"; readonly LOGIN_SSO: "/login/sso"; readonly APP_LIST: "list"; readonly CREATE_JOB: "create-job"; readonly GETTING_STARTED: "getting-started"; readonly STACK_MANAGER_ABOUT: "/stack-manager/about"; ... 63 more ...; readonly EXTERNAL_APPS: "ea"; }'.
<Customers />
</Route>

<Redirect exact to={URLS.OBSERVABILITY_OVERVIEW} />
</Switch>
)
<Route exact path={`${URLS.OBSERVABILITY_CUSTOMER_LIST}/:customer`}>
<Project />
</Route>

<Redirect exact from={path} to={URLS.OBSERVABILITY_OVERVIEW} />
</Switch>
)
}

export default ObservabilityRouter
Loading
Loading