Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8bffec1
feat: add support for state based collapsible list
arunjaindev Sep 27, 2024
4f408af
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
arunjaindev Oct 7, 2024
da3edaa
feat: add key for drift in Node
arunjaindev Oct 8, 2024
a2e1a61
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
arunjaindev Oct 8, 2024
40330c2
Merge branch 'feat/collapsible-list-state' of https://github.com/devt…
arunjaindev Oct 8, 2024
02b5542
feat: add filter for drifted nodes in status filter button
arunjaindev Oct 9, 2024
df994f6
chore: use enum for drifted node
arunjaindev Oct 9, 2024
9551c44
chore: version bump
arunjaindev Oct 9, 2024
7115262
chore: edit typing for collapsible list
arunjaindev Oct 9, 2024
03ce4a3
Merge branch 'feat/collapsible-list-state' of https://github.com/devt…
arunjaindev Oct 9, 2024
858e259
chore: version bump
arunjaindev Oct 9, 2024
3ddb76c
chore: version bump
arunjaindev Oct 9, 2024
3e119ee
chore: rename Drifted filter to drifted
arunjaindev Oct 10, 2024
69cab58
chore: version bump
arunjaindev Oct 10, 2024
eca0ef1
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
arunjaindev Oct 16, 2024
3ec13ae
feat: add flag for config drift feature
arunjaindev Oct 17, 2024
16e71b5
Merge pull request #361 from devtron-labs/feat/config-drift-flag
arunjaindev Oct 17, 2024
4f98578
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
arunjaindev Oct 17, 2024
da74177
Merge branch 'feat/collapsible-list-state' of https://github.com/devt…
arunjaindev Oct 17, 2024
5df60eb
Merge branch 'feat/config-drift' of https://github.com/devtron-labs/d…
arunjaindev Oct 17, 2024
4a6ca3d
chore: version bump
arunjaindev Oct 17, 2024
2e4f158
chore: version bump
arunjaindev Oct 17, 2024
387d6e4
fix: hide drift filter when flag is off
arunjaindev Oct 18, 2024
fe512a6
Merge pull request #342 from devtron-labs/feat/config-drift
arunjaindev Oct 21, 2024
d7a667d
Merge branch 'develop' of https://github.com/devtron-labs/devtron-fe-…
arunjaindev Oct 21, 2024
ae14f5c
chore: version bump
arunjaindev Oct 21, 2024
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
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "0.5.6",
"version": "0.5.8",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down Expand Up @@ -84,7 +84,6 @@
"react-draggable": "^4.4.5",
"react-ga4": "^1.4.1",
"react-mde": "^11.5.0",
"react-router": "^5.3.0",
"react-router-dom": "^5.3.0",
"react-select": "5.8.0",
"rxjs": "^7.8.1",
Expand Down
1 change: 1 addition & 0 deletions src/Common/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const URLS = {
GLOBAL_CONFIG_SCOPED_VARIABLES: '/global-config/scoped-variables',
GLOBAL_CONFIG_DEPLOYMENT_CHARTS_LIST: '/global-config/deployment-charts',
NETWORK_STATUS_INTERFACE: '/network-status-interface',
CONFIG_DRIFT: 'config-drift',
}

export const ROUTES = {
Expand Down
42 changes: 38 additions & 4 deletions src/Shared/Components/CICDHistory/AppStatusDetailsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,34 @@

import { useMemo, useState } from 'react'
import Tippy from '@tippyjs/react'
import { useHistory } from 'react-router-dom'
import { URLS } from '@Common/Constants'
import { ReactComponent as InfoIcon } from '../../../Assets/Icon/ic-info-filled.svg'
import { ReactComponent as Chat } from '../../../Assets/Icon/ic-chat-circle-dots.svg'
import { AppStatusDetailsChartType, AggregatedNodes, STATUS_SORTING_ORDER } from './types'
import { AppStatusDetailsChartType, AggregatedNodes, STATUS_SORTING_ORDER, NodeFilters } from './types'
import { StatusFilterButtonComponent } from './StatusFilterButtonComponent'
import { DEPLOYMENT_STATUS, APP_STATUS_HEADERS } from '../../constants'
import { DEPLOYMENT_STATUS, APP_STATUS_HEADERS, ComponentSizeType } from '../../constants'
import { IndexStore } from '../../Store'
import { aggregateNodes } from '../../Helpers'
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'

const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppStatusDetailsChartType) => {
const AppStatusDetailsChart = ({
filterRemoveHealth = false,
showFooter,
showConfigDriftInfo = false,
onClose,
}: AppStatusDetailsChartType) => {
const history = useHistory()
const _appDetails = IndexStore.getAppDetails()
const [currentFilter, setCurrentFilter] = useState('')

const { appId, environmentId: envId } = _appDetails

const handleCompareDesiredManifest = () => {
onClose()
history.push(`${URLS.APP}/${appId}${URLS.DETAILS}/${envId}/${URLS.APP_DETAILS_K8}/${URLS.CONFIG_DRIFT}`)
}

const nodes: AggregatedNodes = useMemo(
() => aggregateNodes(_appDetails.resourceTree?.nodes || [], _appDetails.resourceTree?.podMetadata || []),
[_appDetails],
Expand Down Expand Up @@ -100,6 +116,7 @@ const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppSt
.filter(
(nodeDetails) =>
currentFilter === 'all' ||
(currentFilter === NodeFilters.drifted && nodeDetails.hasDrift) ||
nodeDetails.health.status?.toLowerCase() === currentFilter,
)
.map((nodeDetails) => (
Expand All @@ -123,7 +140,24 @@ const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppSt
>
{nodeDetails.status ? nodeDetails.status : nodeDetails.health.status}
</div>
<div>{getNodeMessage(nodeDetails.kind, nodeDetails.name)}</div>
<div className="flexbox-col dc__gap-4">
{showConfigDriftInfo && nodeDetails.hasDrift && (
<div className="flexbox dc__gap-8 dc__align-items-center">
<span className="fs-13 fw-4 lh-20 cy-7">Config drift detected</span>
{onClose && appId && envId && (
<Button
dataTestId="show-config-drift"
text="Compare with desired"
variant={ButtonVariantType.text}
style={ButtonStyleType.default}
onClick={handleCompareDesiredManifest}
size={ComponentSizeType.small}
/>
)}
</div>
)}
<div>{getNodeMessage(nodeDetails.kind, nodeDetails.name)}</div>
</div>
</div>
))
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
import { useEffect, useState } from 'react'
import { ReactComponent as ICCaretDown } from '@Icons/ic-caret-down.svg'
import { PopupMenu, StyledRadioGroup as RadioGroup } from '../../../Common'
import { NodeStatus, StatusFilterButtonType } from './types'
import { NodeFilters, NodeStatus, StatusFilterButtonType } from './types'
import { IndexStore } from '../../Store'

import './StatusFilterButtonComponent.scss'

export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: StatusFilterButtonType) => {
Expand All @@ -32,10 +31,15 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
let progressingNodeCount: number = 0
let failedNodeCount: number = 0
let missingNodeCount: number = 0
let driftedNodeCount: number = 0

nodes?.forEach((_node) => {
const _nodeHealth = _node.health?.status

if (_node.hasDrift) {
driftedNodeCount += 1
}

if (_nodeHealth?.toLowerCase() === NodeStatus.Healthy) {
healthyNodeCount += 1
} else if (_nodeHealth?.toLowerCase() === NodeStatus.Degraded) {
Expand All @@ -58,6 +62,11 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
isSelected: NodeStatus.Progressing == selectedTab,
},
{ status: NodeStatus.Healthy, count: healthyNodeCount, isSelected: NodeStatus.Healthy == selectedTab },
window._env_.FEATURE_CONFIG_DRIFT_ENABLE && {
status: NodeFilters.drifted,
count: driftedNodeCount,
isSelected: selectedTab === NodeFilters.drifted,
},
]
const validFilterOptions = filterOptions.filter(({ count }) => count > 0)
const displayedInlineFilters = validFilterOptions.slice(
Expand All @@ -72,7 +81,8 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
(selectedTab === NodeStatus.Healthy && healthyNodeCount === 0) ||
(selectedTab === NodeStatus.Degraded && failedNodeCount === 0) ||
(selectedTab === NodeStatus.Progressing && progressingNodeCount === 0) ||
(selectedTab === NodeStatus.Missing && missingNodeCount === 0)
(selectedTab === NodeStatus.Missing && missingNodeCount === 0) ||
(selectedTab === NodeFilters.drifted && driftedNodeCount === 0)
) {
setSelectedTab('all')
} else if (handleFilterClick) {
Expand Down
6 changes: 6 additions & 0 deletions src/Shared/Components/CICDHistory/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ export interface DeploymentHistorySidebarType {
export interface AppStatusDetailsChartType {
filterRemoveHealth?: boolean
showFooter: boolean
showConfigDriftInfo?: boolean
onClose?: () => void
}

export interface StatusFilterButtonType {
Expand All @@ -535,6 +537,10 @@ export enum NodeStatus {
Unknown = 'unknown',
}

export enum NodeFilters {
drifted = 'drifted',
}

type NodesMap = {
[key in NodeType]?: Map<string, any>
}
Expand Down
116 changes: 78 additions & 38 deletions src/Shared/Components/CollapsibleList/CollapsibleList.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ConditionalWrap } from '@Common/Helper'
import { ReactComponent as ICExpand } from '@Icons/ic-expand.svg'

import { Collapse } from '../Collapse'
import { CollapsibleListProps } from './CollapsibleList.types'
import { CollapsibleListItem, CollapsibleListProps, TabOptions } from './CollapsibleList.types'
import './CollapsibleList.scss'

const renderWithTippy = (tippyProps: TippyProps) => (children: React.ReactElement) => (
Expand All @@ -14,9 +14,80 @@ const renderWithTippy = (tippyProps: TippyProps) => (children: React.ReactElemen
</Tippy>
)

export const CollapsibleList = ({ config, onCollapseBtnClick }: CollapsibleListProps) => {
export const CollapsibleList = <TabType extends TabOptions>({
config,
tabType,
onCollapseBtnClick,
}: CollapsibleListProps<TabType>) => {
const { pathname } = useLocation()

const getTabContent = (item: CollapsibleListItem<TabOptions>) => {
const { title, subtitle, strikeThrough, iconConfig } = item
return (
<>
<div className="flexbox-col flex-grow-1 mw-none">
<span
className={`collapsible__item__title dc__truncate fs-13 lh-20 ${strikeThrough ? 'dc__strike-through' : ''}`}
>
{title}
</span>
{subtitle && <span className="dc__truncate fw-4 lh-1-5 cn-7">{subtitle}</span>}
</div>
{iconConfig && (
<ConditionalWrap
condition={!!iconConfig.tooltipProps}
wrap={renderWithTippy(iconConfig.tooltipProps)}
>
<iconConfig.Icon
{...iconConfig.props}
className={`icon-dim-20 p-2 dc__no-shrink cursor ${iconConfig.props?.className || ''}`}
/>
</ConditionalWrap>
)}
</>
)
}

const getButtonTabItem = (item: CollapsibleListItem<'button'>) => {
const { title, isActive, onClick } = item
return (
<button
key={title}
className={`collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor ${isActive ? 'active' : ''} dc__unset-button-styles w-100`}
onClick={(e) => {
// Prevent navigation to the same page
if (isActive) {
e.preventDefault()
}
onClick?.(e)
}}
type="button"
>
{getTabContent(item)}
</button>
)
}

const getNavLinkTabItem = (item: CollapsibleListItem<'navLink'>) => {
const { title, href, onClick } = item
return (
<NavLink
key={title}
to={href}
className="collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor"
onClick={(e) => {
// Prevent navigation to the same page
if (href === pathname) {
e.preventDefault()
}
onClick?.(e)
}}
>
{getTabContent(item)}
</NavLink>
)
}

return (
<div className="mw-none bcn-0">
{config.map(({ id, header, headerIconConfig, items, noItemsText, isExpanded }) => (
Expand Down Expand Up @@ -60,42 +131,11 @@ export const CollapsibleList = ({ config, onCollapseBtnClick }: CollapsibleListP
</span>
</div>
) : (
items.map(({ title, strikeThrough, href, iconConfig, subtitle, onClick }) => (
<NavLink
key={title}
to={href}
className="collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor"
onClick={(e) => {
// Prevent navigation to the same page
if (href === pathname) {
e.preventDefault()
}
onClick?.(e)
}}
>
<div className="flexbox-col flex-grow-1 mw-none">
<span
className={`collapsible__item__title dc__truncate fs-13 lh-20 ${strikeThrough ? 'dc__strike-through' : ''}`}
>
{title}
</span>
{subtitle && (
<span className="dc__truncate fw-4 lh-1-5 cn-7">{subtitle}</span>
)}
</div>
{iconConfig && (
<ConditionalWrap
condition={!!iconConfig.tooltipProps}
wrap={renderWithTippy(iconConfig.tooltipProps)}
>
<iconConfig.Icon
{...iconConfig.props}
className={`icon-dim-20 p-2 dc__no-shrink cursor ${iconConfig.props?.className || ''}`}
/>
</ConditionalWrap>
)}
</NavLink>
))
items.map((item) =>
tabType === 'button'
? getButtonTabItem(item as CollapsibleListItem<'button'>)
: getNavLinkTabItem(item as CollapsibleListItem<'navLink'>),
)
)}
</div>
</Collapse>
Expand Down
Loading