diff --git a/packages/manager/.changeset/pr-10318-upcoming-features-1711489006250.md b/packages/manager/.changeset/pr-10318-upcoming-features-1711489006250.md new file mode 100644 index 00000000000..6e9cb8591cf --- /dev/null +++ b/packages/manager/.changeset/pr-10318-upcoming-features-1711489006250.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Update Placement Groups landing page table ([#10318](https://github.com/linode/manager/pull/10318)) diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx index b53cf3edc2b..821645bd3af 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx @@ -20,7 +20,7 @@ import { import { LinodeSelect } from '../Linodes/LinodeSelect/LinodeSelect'; import { - getAffinityEnforcement, + getAffinityTypeEnforcement, getLinodesFromAllPlacementGroups, } from './utils'; @@ -139,7 +139,7 @@ export const PlacementGroupsAssignLinodesDrawer = ( {generalError ? : null} Affinity Enforcement: - {getAffinityEnforcement(selectedPlacementGroup.is_strict)} + {getAffinityTypeEnforcement(selectedPlacementGroup.is_strict)}
diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsEditDrawer.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsEditDrawer.tsx index 121571edcc9..b2424096141 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsEditDrawer.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsEditDrawer.tsx @@ -23,7 +23,7 @@ import { import { getFormikErrorsFromAPIErrors } from 'src/utilities/formikErrorUtils'; import { scrollErrorIntoView } from 'src/utilities/scrollErrorIntoView'; -import { getAffinityEnforcement } from './utils'; +import { getAffinityTypeEnforcement } from './utils'; import type { PlacementGroupsEditDrawerProps } from './types'; import type { UpdatePlacementGroupPayload } from '@linode/api-v4'; @@ -137,7 +137,7 @@ export const PlacementGroupsEditDrawer = ( Affinity Enforcement: - {getAffinityEnforcement(placementGroup.is_strict)} + {getAffinityTypeEnforcement(placementGroup.is_strict)} diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsLanding.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsLanding.tsx index 815e3e62c11..93a6f7fef2f 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsLanding.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsLanding.tsx @@ -1,8 +1,10 @@ import CloseIcon from '@mui/icons-material/Close'; +import { useMediaQuery, useTheme } from '@mui/material'; import * as React from 'react'; import { useHistory } from 'react-router-dom'; import { CircleProgress } from 'src/components/CircleProgress'; +import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Hidden } from 'src/components/Hidden'; import { IconButton } from 'src/components/IconButton'; @@ -14,8 +16,8 @@ import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; +import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; import { TableSortCell } from 'src/components/TableSortCell/TableSortCell'; -import { TextField } from 'src/components/TextField'; import { getRestrictedResourceText } from 'src/features/Account/utils'; import { useOrder } from 'src/hooks/useOrder'; import { usePagination } from 'src/hooks/usePagination'; @@ -36,10 +38,12 @@ const preferenceKey = 'placement-groups'; export const PlacementGroupsLanding = React.memo(() => { const history = useHistory(); const pagination = usePagination(1, preferenceKey); + const theme = useTheme(); const [query, setQuery] = React.useState(''); const [selectedPlacementGroup, setSelectedPlacementGroup] = React.useState< PlacementGroup | undefined >(undefined); + const matchesSmDown = useMediaQuery(theme.breakpoints.down('md')); const { handleOrderChange, order, orderBy } = useOrder( { order: 'asc', @@ -57,13 +61,16 @@ export const PlacementGroupsLanding = React.memo(() => { filter['label'] = { '+contains': query }; } - const params = { - page: pagination.page, - page_size: pagination.pageSize, - }; - - const { data: placementGroups, error, isLoading } = usePlacementGroupsQuery( - params, + const { + data: placementGroups, + error, + isFetching, + isLoading, + } = usePlacementGroupsQuery( + { + page: pagination.page, + page_size: pagination.pageSize, + }, filter ); @@ -101,7 +108,7 @@ export const PlacementGroupsLanding = React.memo(() => { return ; } - if (placementGroups?.results === 0) { + if (placementGroups?.results === 0 && query === '') { return ( <> { onButtonClick={handleCreatePlacementGroup} title="Placement Groups" /> - + {isFetching && } + setQuery('')} @@ -161,6 +170,7 @@ export const PlacementGroupsLanding = React.memo(() => { ), }} + debounceTime={250} hideLabel label="Filter" onChange={(e) => setQuery(e.target.value)} @@ -176,10 +186,31 @@ export const PlacementGroupsLanding = React.memo(() => { direction={order} handleClick={handleOrderChange} label="label" - sx={{ width: '40%' }} + sx={{ width: matchesSmDown ? '40%' : '20%' }} > Label + + + Affinity Type + + + + + Affinity Type Enforcement + + Linodes { + {placementGroups?.data.length === 0 && } {placementGroups?.data.map((placementGroup) => ( diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx index 4b2ed2bc05f..149635793be 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx @@ -88,7 +88,7 @@ describe('PlacementGroupsLanding', () => { ); expect(getByTestId('link-to-placement-group-1')).toHaveTextContent( - 'group 1 (Anti-affinity)' + 'group 1' ); expect(getByText('Non-compliant')).toBeInTheDocument(); expect(getByTestId('placement-group-1-assigned-linodes')).toHaveTextContent( diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.tsx index 7a7135024fb..7ab28641b81 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.tsx @@ -12,6 +12,7 @@ import { TextTooltip } from 'src/components/TextTooltip'; import { Typography } from 'src/components/Typography'; import { usePlacementGroupData } from 'src/hooks/usePlacementGroupsData'; +import { getAffinityTypeEnforcement } from '../utils'; import { StyledWarningIcon } from './PlacementGroupsRow.styles'; import type { PlacementGroup } from '@linode/api-v4'; @@ -31,7 +32,13 @@ export const PlacementGroupsRow = React.memo( handleEditPlacementGroup, placementGroup, }: PlacementGroupsRowProps) => { - const { affinity_type, id, is_compliant, label } = placementGroup; + const { + affinity_type, + id, + is_compliant, + is_strict, + label, + } = placementGroup; const { assignedLinodes, linodesCount, region } = usePlacementGroupData({ placementGroup, }); @@ -57,7 +64,7 @@ export const PlacementGroupsRow = React.memo( style={{ marginRight: 8 }} to={`/placement-groups/${id}`} > - {label} ({AFFINITY_TYPES[affinity_type]}) + {label} {!is_compliant && ( @@ -66,19 +73,29 @@ export const PlacementGroupsRow = React.memo( )} + + {AFFINITY_TYPES[affinity_type]} + + + {getAffinityTypeEnforcement(is_strict)} + - - {assignedLinodes?.map((linode, idx) => ( - {linode.label} - ))} - - } - displayText={`${linodesCount}`} - minWidth={200} - /> -   of {region?.maximum_vms_per_pg} + {assignedLinodes?.length === 0 ? ( + '0' + ) : ( + + {assignedLinodes?.map((linode, idx) => ( + {linode.label} + ))} + + } + displayText={`${linodesCount}`} + minWidth={200} + /> + )} +   of {region?.maximum_vms_per_pg ?? 'unknown'} {region?.label} diff --git a/packages/manager/src/features/PlacementGroups/utils.test.ts b/packages/manager/src/features/PlacementGroups/utils.test.ts index dca7ea1a3ef..cfdaeb70ef3 100644 --- a/packages/manager/src/features/PlacementGroups/utils.test.ts +++ b/packages/manager/src/features/PlacementGroups/utils.test.ts @@ -2,7 +2,7 @@ import { placementGroupFactory, regionFactory } from 'src/factories'; import { affinityTypeOptions, - getAffinityEnforcement, + getAffinityTypeEnforcement, getLinodesFromAllPlacementGroups, getPlacementGroupLinodeCount, hasPlacementGroupReachedCapacity, @@ -109,11 +109,11 @@ describe('getLinodesFromAllPlacementGroups', () => { describe('getAffinityEnforcement', () => { it('returns "Strict" if `is_strict` is true', () => { - expect(getAffinityEnforcement(true)).toBe('Strict'); + expect(getAffinityTypeEnforcement(true)).toBe('Strict'); }); it('returns "Flexible" if `is_strict` is false', () => { - expect(getAffinityEnforcement(false)).toBe('Flexible'); + expect(getAffinityTypeEnforcement(false)).toBe('Flexible'); }); }); diff --git a/packages/manager/src/features/PlacementGroups/utils.ts b/packages/manager/src/features/PlacementGroups/utils.ts index d95101aa4ed..f0b323831bc 100644 --- a/packages/manager/src/features/PlacementGroups/utils.ts +++ b/packages/manager/src/features/PlacementGroups/utils.ts @@ -10,7 +10,7 @@ import type { /** * Helper to get the affinity enforcement readable string. */ -export const getAffinityEnforcement = ( +export const getAffinityTypeEnforcement = ( is_strict: boolean ): AffinityEnforcement => { return is_strict ? 'Strict' : 'Flexible';