-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add slider to adjust capacity_adjustment #10063
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,55 @@ | ||
import React from 'react'; | ||
import React, { useState, useCallback } from 'react'; | ||
import { bool, func } from 'prop-types'; | ||
|
||
import { t } from '@lingui/macro'; | ||
import { t, Plural } from '@lingui/macro'; | ||
import styled from 'styled-components'; | ||
import 'styled-components/macro'; | ||
import { | ||
Badge as PFBadge, | ||
Progress, | ||
ProgressMeasureLocation, | ||
ProgressSize, | ||
DataListAction, | ||
DataListAction as PFDataListAction, | ||
DataListCheck, | ||
DataListItem, | ||
DataListItemRow, | ||
DataListItemCells, | ||
DataListItem as PFDataListItem, | ||
DataListItemRow as PFDataListItemRow, | ||
DataListItemCells as PFDataListItemCells, | ||
Slider, | ||
} from '@patternfly/react-core'; | ||
|
||
import _DataListCell from '../../../components/DataListCell'; | ||
import InstanceToggle from '../../../components/InstanceToggle'; | ||
import { Instance } from '../../../types'; | ||
import useRequest, { useDismissableError } from '../../../util/useRequest'; | ||
import useDebounce from '../../../util/useDebounce'; | ||
import { InstancesAPI } from '../../../api'; | ||
import { useConfig } from '../../../contexts/Config'; | ||
import AlertModal from '../../../components/AlertModal'; | ||
import ErrorDetail from '../../../components/ErrorDetail'; | ||
|
||
const DataListItem = styled(PFDataListItem)` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
`; | ||
|
||
const DataListItemRow = styled(PFDataListItemRow)` | ||
align-items: center; | ||
`; | ||
|
||
const DataListItemCells = styled(PFDataListItemCells)` | ||
align-items: center; | ||
`; | ||
|
||
const DataListAction = styled(PFDataListAction)` | ||
align-items: center; | ||
`; | ||
const Unavailable = styled.span` | ||
color: var(--pf-global--danger-color--200); | ||
`; | ||
|
||
const DataListCell = styled(_DataListCell)` | ||
white-space: nowrap; | ||
align-items: center; | ||
`; | ||
|
||
const Badge = styled(PFBadge)` | ||
|
@@ -40,7 +64,37 @@ const ListGroup = styled.span` | |
} | ||
`; | ||
|
||
const SliderHolder = styled.div` | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
`; | ||
|
||
const SliderForks = styled.div` | ||
flex-grow: 1; | ||
margin-right: 8px; | ||
margin-left: 8px; | ||
text-align: center; | ||
`; | ||
|
||
function computeForks(memCapacity, cpuCapacity, selectedCapacityAdjustment) { | ||
const minCapacity = Math.min(memCapacity, cpuCapacity); | ||
const maxCapacity = Math.max(memCapacity, cpuCapacity); | ||
|
||
return Math.floor( | ||
minCapacity + (maxCapacity - minCapacity) * selectedCapacityAdjustment | ||
); | ||
} | ||
|
||
function InstanceListItem({ instance, isSelected, onSelect, fetchInstances }) { | ||
const { me = {} } = useConfig(); | ||
const [forks, setForks] = useState( | ||
computeForks( | ||
instance.mem_capacity, | ||
instance.cpu_capacity, | ||
instance.capacity_adjustment | ||
) | ||
); | ||
const labelId = `check-action-${instance.id}`; | ||
|
||
function usedCapacity(item) { | ||
|
@@ -57,68 +111,133 @@ function InstanceListItem({ instance, isSelected, onSelect, fetchInstances }) { | |
return <Unavailable>{t`Unavailable`}</Unavailable>; | ||
} | ||
|
||
const { error: updateInstanceError, request: updateInstance } = useRequest( | ||
useCallback( | ||
async values => { | ||
await InstancesAPI.update(instance.id, values); | ||
}, | ||
[instance] | ||
) | ||
); | ||
|
||
const { | ||
error: updateError, | ||
dismissError: dismissUpdateError, | ||
} = useDismissableError(updateInstanceError); | ||
|
||
const debounceUpdateInstance = useDebounce(updateInstance, 200); | ||
|
||
const handleChangeValue = value => { | ||
const roundedValue = Math.round(value * 100) / 100; | ||
setForks( | ||
computeForks(instance.mem_capacity, instance.cpu_capacity, roundedValue) | ||
); | ||
debounceUpdateInstance({ capacity_adjustment: roundedValue }); | ||
}; | ||
|
||
return ( | ||
<DataListItem | ||
aria-labelledby={labelId} | ||
id={`${instance.id}`} | ||
key={instance.id} | ||
> | ||
<DataListItemRow> | ||
<DataListCheck | ||
aria-labelledby={labelId} | ||
checked={isSelected} | ||
id={`instances-${instance.id}`} | ||
onChange={onSelect} | ||
/> | ||
<> | ||
<DataListItem | ||
aria-labelledby={labelId} | ||
id={`${instance.id}`} | ||
key={instance.id} | ||
> | ||
<DataListItemRow> | ||
<DataListCheck | ||
aria-labelledby={labelId} | ||
checked={isSelected} | ||
id={`instances-${instance.id}`} | ||
onChange={onSelect} | ||
/> | ||
|
||
<DataListItemCells | ||
dataListCells={[ | ||
<DataListCell key="name" aria-label={t`instance host name`}> | ||
<b>{instance.hostname}</b> | ||
</DataListCell>, | ||
<DataListCell key="type" aria-label={t`instance type`}> | ||
<b css="margin-right: 24px">{t`Type`}</b> | ||
<span id={labelId}> | ||
{instance.managed_by_policy ? t`Auto` : t`Manual`} | ||
</span> | ||
</DataListCell>, | ||
<DataListCell | ||
key="related-field-counts" | ||
aria-label={t`instance counts`} | ||
width={2} | ||
> | ||
<ListGroup> | ||
<b>{t`Running jobs`}</b> | ||
<Badge isRead>{instance.jobs_running}</Badge> | ||
</ListGroup> | ||
<ListGroup> | ||
<b>{t`Total jobs`}</b> | ||
<Badge isRead>{instance.jobs_total}</Badge> | ||
</ListGroup> | ||
</DataListCell>, | ||
<DataListCell | ||
key="capacity" | ||
aria-label={t`instance group used capacity`} | ||
> | ||
{usedCapacity(instance)} | ||
</DataListCell>, | ||
]} | ||
/> | ||
<DataListAction | ||
aria-label={t`actions`} | ||
aria-labelledby={labelId} | ||
id={labelId} | ||
> | ||
<InstanceToggle | ||
css="display: inline-flex;" | ||
fetchInstances={fetchInstances} | ||
instance={instance} | ||
<DataListItemCells | ||
dataListCells={[ | ||
<DataListCell key="name" aria-label={t`instance host name`}> | ||
<b>{instance.hostname}</b> | ||
</DataListCell>, | ||
<DataListCell key="type" aria-label={t`instance type`}> | ||
<b css="margin-right: 24px">{t`Type`}</b> | ||
<span id={labelId}> | ||
{instance.managed_by_policy ? t`Auto` : t`Manual`} | ||
</span> | ||
</DataListCell>, | ||
<DataListCell | ||
key="related-field-counts" | ||
aria-label={t`instance counts`} | ||
width={3} | ||
> | ||
<ListGroup> | ||
<b>{t`Running jobs`}</b> | ||
<Badge isRead>{instance.jobs_running}</Badge> | ||
</ListGroup> | ||
<ListGroup> | ||
<b>{t`Total jobs`}</b> | ||
<Badge isRead>{instance.jobs_total}</Badge> | ||
</ListGroup> | ||
</DataListCell>, | ||
<DataListCell | ||
key="capacity-adjustment" | ||
aria-label={t`capacity adjustment`} | ||
width={4} | ||
> | ||
<SliderHolder data-cy="slider-holder"> | ||
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div> | ||
<SliderForks data-cy="slider-forks"> | ||
<div data-cy="number-forks"> | ||
<Plural value={forks} one="# fork" other="# forks" /> | ||
</div> | ||
<Slider | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slider does not support ouiaId yet. @tiagodread I added |
||
areCustomStepsContinuous | ||
max={1} | ||
min={0} | ||
step={0.1} | ||
value={instance.capacity_adjustment} | ||
onChange={handleChangeValue} | ||
isDisabled={!me?.is_superuser || !instance.enabled} | ||
data-cy="slider" | ||
/> | ||
</SliderForks> | ||
|
||
<div data-cy="mem-capacity">{t`RAM ${instance.mem_capacity}`}</div> | ||
</SliderHolder> | ||
</DataListCell>, | ||
|
||
<DataListCell | ||
key="capacity" | ||
aria-label={t`instance group used capacity`} | ||
> | ||
{usedCapacity(instance)} | ||
</DataListCell>, | ||
]} | ||
/> | ||
</DataListAction> | ||
</DataListItemRow> | ||
</DataListItem> | ||
<DataListAction | ||
aria-label={t`actions`} | ||
aria-labelledby={labelId} | ||
id={labelId} | ||
> | ||
<InstanceToggle | ||
css="display: inline-flex;" | ||
fetchInstances={fetchInstances} | ||
instance={instance} | ||
/> | ||
</DataListAction> | ||
</DataListItemRow> | ||
</DataListItem> | ||
{updateError && ( | ||
<AlertModal | ||
variant="error" | ||
title={t`Error!`} | ||
isOpen | ||
onClose={dismissUpdateError} | ||
> | ||
{t`Failed to update capacity adjustment.`} | ||
<ErrorDetail error={updateError} /> | ||
</AlertModal> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
InstanceListItem.prototype = { | ||
instance: Instance.isRequired, | ||
isSelected: bool.isRequired, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch