Skip to content

Commit

Permalink
Jake/prod 3253 add filter for cluster upgradeability (#1944)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman authored Feb 28, 2025
1 parent b3c7aa6 commit 2a505d5
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 22 deletions.
16 changes: 14 additions & 2 deletions assets/src/components/cd/clusters/Clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { useCDEnabled } from '../utils/useCDEnabled'
import {
ClusterStatusTabKey,
ClustersFilters,
UpgradeableFilterKey,
} from '../services/ClustersFilters'

import { TagsFilter } from '../services/ClusterTagsFilter'
Expand Down Expand Up @@ -109,6 +110,8 @@ export default function Clusters() {
const tabStateRef = useRef<any>(null)
const [statusFilter, setStatusFilter] = useState<ClusterStatusTabKey>('ALL')
const [selectedTagKeys, setSelectedTagKeys] = useState(new Set<Key>())
const [upgradeableFilter, setUpgradeableFilter] =
useState<UpgradeableFilterKey>('ALL')

const [searchString, setSearchString] = useState<string>()
const debouncedSearchString = useDebounce(searchString, 100)
Expand Down Expand Up @@ -145,6 +148,10 @@ export default function Clusters() {
...(!isEmpty(searchTags)
? { tagQuery: { op: tagOp, tags: searchTags } }
: {}),
upgradeable:
upgradeableFilter === 'ALL'
? undefined
: upgradeableFilter === 'UPGRADEABLE',
}
)

Expand Down Expand Up @@ -194,9 +201,11 @@ export default function Clusters() {
useSetBreadcrumbs(CD_CLUSTERS_BASE_CRUMBS)

const clusterEdges = data?.clusters?.edges
const isDemo = statusCounts.ALL === 0 || !cdIsEnabled
const hasStatFilters = !!debouncedSearchString || !!projectId
const isDemo = (statusCounts.ALL === 0 && !hasStatFilters) || !cdIsEnabled
const tableData = isDemo ? DEMO_CLUSTERS : clusterEdges
const showGettingStarted = isDemo || (statusCounts.ALL ?? 0) < 2
const showGettingStarted =
isDemo || ((statusCounts.ALL ?? 0) < 2 && !hasStatFilters)

useSetPageScrollable(showGettingStarted || isDemo)

Expand Down Expand Up @@ -229,6 +238,9 @@ export default function Clusters() {
setTagOp={
setTagOp as ComponentProps<typeof TagsFilter>['setSearchOp']
}
upgradeableFilter={upgradeableFilter}
setUpgradeableFilter={setUpgradeableFilter}
upgradeStats={data.upgradeStatistics}
/>
<TabPanel
stateRef={tabStateRef}
Expand Down
23 changes: 17 additions & 6 deletions assets/src/components/cd/logs/DateTimeFormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SegmentedInputHandle,
SemanticColorKey,
Toast,
useIsFocused,
} from '@pluralsh/design-system'
import { Body2P } from 'components/utils/typography/Text'

Expand Down Expand Up @@ -43,6 +44,7 @@ export function DateTimeFormInput({
const [isEnteringTimestamp, setIsEnteringTimestamp] = useState(false)
const [customTimestamp, setCustomTimestamp] = useState('')
const [timestampError, setTimestampError] = useState(false)
const [isFocused, focusCallbacks] = useIsFocused({})

const initDateStr = formatDateTime(initialDate, DATE_FORMAT) || EMPTY_DATE_STR
const initTimeStr = formatDateTime(initialDate, TIME_FORMAT) || EMPTY_TIME_STR
Expand Down Expand Up @@ -86,15 +88,15 @@ export function DateTimeFormInput({
else setDate?.(initialDate)
}, [dateStr, dateValid, initialDate, isSetToNow, setDate, timeStr, timeValid])

const setValsFromTimestamp = (val?: DateParam) => {
const timestamp = val ?? customTimestamp
const setValsFromTimestamp = (val?: string) => {
const timestamp = handleUnixTS(val ?? customTimestamp)
if (!isValidDateTime(timestamp)) {
setTimestampError(true)
return
}
setIsEnteringTimestamp(false)
const date = formatDateTime(timestamp, DATE_FORMAT)
const time = formatDateTime(timestamp, TIME_FORMAT)
const date = formatDateTime(timestamp, DATE_FORMAT, true)
const time = formatDateTime(timestamp, TIME_FORMAT, true)
runAfterLayout(() => {
dateInputRef.current?.setValue(date)
timeInputRef.current?.setValue(time)
Expand Down Expand Up @@ -130,6 +132,7 @@ export function DateTimeFormInput({
)
}
{...props}
{...focusCallbacks}
>
{isEnteringTimestamp ? (
<Input
Expand Down Expand Up @@ -159,7 +162,7 @@ export function DateTimeFormInput({
>
<SegmentedInput
ref={dateInputRef}
{...(isSetToNow && { value: 'Today' })}
{...(isSetToNow && !isFocused && { value: 'Today' })}
style={{ borderColor: dateError && colors['border-danger'] }}
prefix="Date"
endIcon={<Body2P $color="text-xlight">MM/DD/YYYY</Body2P>}
Expand All @@ -173,7 +176,7 @@ export function DateTimeFormInput({
/>
<SegmentedInput
ref={timeInputRef}
{...(isSetToNow && { value: 'Now' })}
{...(isSetToNow && !isFocused && { value: 'Now' })}
style={{ borderColor: timeError && colors['border-danger'] }}
prefix="Time"
endIcon={<Body2P $color="text-xlight">UTC</Body2P>}
Expand Down Expand Up @@ -220,3 +223,11 @@ const CaptionTextBtnSC = styled.span<{
opacity: $disabled ? 0.4 : 1,
'&:hover': { textDecoration: $disabled ? 'none' : 'underline' },
}))

const handleUnixTS = (val: string) => {
// parse as a unix timestamp if it's a valid number
// otherwise keep it as is
const valNum = Number(val)
if (!isNaN(valNum)) return val.length === 10 ? valNum * 1000 : valNum
return val
}
12 changes: 12 additions & 0 deletions assets/src/components/cd/logs/LogsFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ListBoxItem,
SearchIcon,
Select,
Toast,
} from '@pluralsh/design-system'
import { useUpdateState } from 'components/hooks/useUpdateState'
import { LogFacetInput } from 'generated/graphql'
Expand Down Expand Up @@ -114,8 +115,10 @@ function FiltersForm({
onSubmit: (form: LogsFlyoverFiltersT) => void
setLive: (live: boolean) => void
}) {
const { spacing } = useTheme()
const [hasDTErrors, setHasDTErrors] = useState(false)
const clearDTFormRef = useRef<() => void>(null)
const [showSuccessToast, setShowSuccessToast] = useState(false)

const { state, update, initialState, hasUpdates } =
useUpdateState(initialForm)
Expand All @@ -125,6 +128,7 @@ function FiltersForm({
onSubmit(state)
// by default, logs should be live if no specific date is set, and vice versa
setLive(!state.date)
setShowSuccessToast(true)
}

const resetToDefault = () => {
Expand Down Expand Up @@ -202,6 +206,14 @@ function FiltersForm({
</Button>
</Flex>
</WrapperFormSC>
<Toast
show={showSuccessToast}
closeTimeout={2000}
onClose={() => setShowSuccessToast(false)}
margin={spacing.xlarge}
>
Filters applied
</Toast>
</FillLevelContext>
)
}
Expand Down
61 changes: 55 additions & 6 deletions assets/src/components/cd/services/ClustersFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { type ComponentProps, useDeferredValue, useState } from 'react'
import {
Chip,
Input,
ListBoxItem,
SearchIcon,
Select,
SubTab,
TabList,
} from '@pluralsh/design-system'
import styled from 'styled-components'
import { Dispatch, MutableRefObject, SetStateAction, useEffect } from 'react'
import isNil from 'lodash/isNil'
import {
type ComponentProps,
Dispatch,
RefObject,
SetStateAction,
useDeferredValue,
useEffect,
useState,
} from 'react'
import styled from 'styled-components'

import { useDebounce } from '@react-hooks-library/core'

import { serviceStatusToSeverity } from './ServiceStatusChip'
import { UpgradeStatistics } from 'generated/graphql'
import { isNumber } from 'lodash'
import { TagsFilter } from './ClusterTagsFilter'
import { serviceStatusToSeverity } from './ServiceStatusChip'

export type ClusterStatusTabKey = 'HEALTHY' | 'UNHEALTHY' | 'ALL'
export type UpgradeableFilterKey = 'ALL' | 'UPGRADEABLE' | 'NON-UPGRADEABLE'
export const statusTabs = Object.entries({
ALL: { label: 'All' },
HEALTHY: {
Expand Down Expand Up @@ -46,20 +58,28 @@ export function ClustersFilters({
setSelectedTagKeys,
tagOp,
setTagOp,
upgradeableFilter,
setUpgradeableFilter,
upgradeStats,
}: {
setQueryStatusFilter: Dispatch<SetStateAction<ClusterStatusTabKey>>
setQueryString: (string) => void
tabStateRef: MutableRefObject<any>
tabStateRef: RefObject<any>
statusCounts: Record<ClusterStatusTabKey, number | undefined>
selectedTagKeys: ComponentProps<typeof TagsFilter>['selectedTagKeys']
setSelectedTagKeys: ComponentProps<typeof TagsFilter>['setSelectedTagKeys']
tagOp: ComponentProps<typeof TagsFilter>['searchOp']
setTagOp: ComponentProps<typeof TagsFilter>['setSearchOp']
upgradeableFilter: UpgradeableFilterKey
setUpgradeableFilter: (val: UpgradeableFilterKey) => void
upgradeStats: Nullable<Pick<UpgradeStatistics, 'upgradeable' | 'count'>>
}) {
const [searchString, setSearchString] = useState('')
const debouncedSearchString = useDebounce(searchString, 400)
const [statusFilter, setStatusFilter] = useState<ClusterStatusTabKey>('ALL')
const deferredStatusFilter = useDeferredValue(statusFilter)
const { count, upgradeable } = upgradeStats ?? {}
const hasUpgradeStats = isNumber(count) && isNumber(upgradeable)

useEffect(() => {
setQueryString(debouncedSearchString)
Expand All @@ -79,7 +99,7 @@ export function ClustersFilters({
setSearchOp={setTagOp}
/>
</div>
<div css={{ flex: '1 1 50%' }}>
<div css={{ flex: '1 1 50%', minWidth: 120 }}>
<Input
placeholder="Search"
startIcon={<SearchIcon />}
Expand Down Expand Up @@ -118,6 +138,35 @@ export function ClustersFilters({
</SubTab>
))}
</TabList>
<div css={{ minWidth: 240 }}>
<Select
label={upgradeableFilter}
selectedKey={upgradeableFilter}
onSelectionChange={(key) =>
setUpgradeableFilter(key as UpgradeableFilterKey)
}
>
<ListBoxItem
key="ALL"
label="All upgrade statuses"
rightContent={hasUpgradeStats && <Chip size="small">{count}</Chip>}
/>
<ListBoxItem
key="UPGRADEABLE"
label="Upgradeable only"
rightContent={
hasUpgradeStats && <Chip size="small">{upgradeable}</Chip>
}
/>
<ListBoxItem
key="NON-UPGRADEABLE"
label="Non-upgradeable only"
rightContent={
hasUpgradeStats && <Chip size="small">{count - upgradeable}</Chip>
}
/>
</Select>
</div>
</ClustersFiltersSC>
)
}
Loading

0 comments on commit 2a505d5

Please sign in to comment.