diff --git a/README.md b/README.md index c5718d1c..5350aa19 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Your server will be running at `http://localhost:3000/` (the port is configurabl ## Development mode - set env. variable `NODE_MODE='development'` -- run `yarn ui-dev` to start local development server (starts on port 3001) +- run `yarn client-dev` to start local development server (starts on port 3001) - run `yarn server-dev` to start the express server in development mode To debug the client code using VS Code, simply run the project's launch configuration from the 'Run' menu (Ctrl+Shift+D). diff --git a/config.ts b/config.ts index 29b4f2c5..356e06d6 100644 --- a/config.ts +++ b/config.ts @@ -109,6 +109,8 @@ const conf: Config = { // replicas, migrations, endpoints, users etc. mainListItemsPerPage: 20, + maxMinionPoolEventsPerPage: 50, + servicesUrls: { keystone: '{BASE_URL}/identity', barbican: '{BASE_URL}/barbican', diff --git a/src/@types/Config.ts b/src/@types/Config.ts index 9afaf1fc..f17429e9 100644 --- a/src/@types/Config.ts +++ b/src/@types/Config.ts @@ -34,4 +34,5 @@ export type Config = { passwordFields: string[], mainListItemsPerPage: number, servicesUrls: Services, + maxMinionPoolEventsPerPage: number, } diff --git a/src/@types/Field.ts b/src/@types/Field.ts index 8faf87d3..5c79b548 100644 --- a/src/@types/Field.ts +++ b/src/@types/Field.ts @@ -17,7 +17,14 @@ import LabelDictionary from '../utils/LabelDictionary' import { ProviderTypes } from './Providers' type Separator = { separator: boolean } -type EnumItemObject = { label?: string, value?: any, name?: string, id?: string | null } +type EnumItemObject = { + label?: string, + value?: any, + name?: string, + id?: string | null, + disabled?: boolean, + subtitleLabel?: string, +} export const isEnumSeparator = (e: any): e is Separator => (typeof e !== 'string' && e.separator === true) export type EnumItem = ( diff --git a/src/@types/MinionPool.ts b/src/@types/MinionPool.ts index ed23b6ee..94435998 100644 --- a/src/@types/MinionPool.ts +++ b/src/@types/MinionPool.ts @@ -12,23 +12,50 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -import { Execution } from './Execution' - +export type MinionMachine = { + id: string + created_at: string + updated_at: string + allocation_status: string + connection_info?: any + power_status: string + provider_properties: any + last_used_at?: string + allocated_action: string | null +} +export type MinionPoolEvent = { + id: string + index: number + level: 'INFO' | 'DEBUG' | 'ERROR' + message: string + created_at: string +} +export type MinionPoolProgressUpdate = { + id: string + current_step: number + message: string + created_at: string +} +export type MinionPoolEventProgressUpdate = MinionPoolEvent | MinionPoolProgressUpdate export type MinionPool = { id: string created_at: string updated_at: string | null - pool_name: string - pool_os_type: string - pool_status: string + name: string + os_type: 'linux' | 'windows' + status: string minimum_minions: number + maximum_minions: number environment_options: { [prop: string]: any } endpoint_id: string - last_execution_status: string notes?: string - pool_platform: 'source' | 'destination' + platform: 'source' | 'destination', + minion_machines: MinionMachine[], + minion_retention_strategy: 'poweroff' | 'delete' + minion_max_idle_time: number, } export type MinionPoolDetails = MinionPool & { - executions: Execution[] + events: MinionPoolEvent[], + progress_updates: MinionPoolProgressUpdate[] } diff --git a/src/components/atoms/StatusIcon/StatusIcon.tsx b/src/components/atoms/StatusIcon/StatusIcon.tsx index 6b87835a..9fca62d3 100644 --- a/src/components/atoms/StatusIcon/StatusIcon.tsx +++ b/src/components/atoms/StatusIcon/StatusIcon.tsx @@ -26,6 +26,7 @@ import warningImage from './images/warning' import pendingImage from './images/pending.svg' import successHollowImage from './images/success-hollow.svg' import errorHollowImage from './images/error-hollow.svg' +import warningHollowImage from './images/warning-hollow.svg' type Props = { status: string, @@ -33,7 +34,9 @@ type Props = { hollow?: boolean, secondary?: boolean, style?: React.CSSProperties + outlined?: boolean onClick?: (e: React.MouseEvent) => void + title?: string } const getSpinnerUrl = ( @@ -48,7 +51,7 @@ const getRunningImageUrl = (props: Props) => { const getWarningUrl = (background: string) => `url('data:image/svg+xml;utf8,${encodeURIComponent(warningImage(background))}')` -const statuses = (status: any, props: any) => { +const statuses = (status: any, props: Props) => { switch (status) { case 'COMPLETED': return css` @@ -57,6 +60,7 @@ const statuses = (status: any, props: any) => { case 'STARTING': case 'RUNNING': case 'PENDING': + case 'AWAITING_MINION_ALLOCATIONS': return css` background-image: ${getRunningImageUrl(props)}; ${StyleProps.animations.rotation} @@ -74,6 +78,7 @@ const statuses = (status: any, props: any) => { case 'FAILED_TO_SCHEDULE': case 'FAILED_TO_CANCEL': case 'ERROR': + case 'ERROR_ALLOCATING_MINIONS': return css` background-image: url('${props.hollow ? errorHollowImage : errorImage}'); ` @@ -90,10 +95,11 @@ const statuses = (status: any, props: any) => { return css` background-image: ${getWarningUrl('#424242')}; ` + case 'INFO': case 'UNSCHEDULED': case 'UNEXECUTED': return css` - background-image: ${getWarningUrl(Palette.grayscale[2])}; + background-image: ${props.hollow ? `url('${warningHollowImage}')` : getWarningUrl(Palette.grayscale[2])}; ` default: return null diff --git a/src/components/atoms/StatusIcon/images/warning-hollow.svg b/src/components/atoms/StatusIcon/images/warning-hollow.svg new file mode 100644 index 00000000..ad8bdca6 --- /dev/null +++ b/src/components/atoms/StatusIcon/images/warning-hollow.svg @@ -0,0 +1,82 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/components/atoms/StatusIcon/story.tsx b/src/components/atoms/StatusIcon/story.tsx index 54329014..7ca0a448 100644 --- a/src/components/atoms/StatusIcon/story.tsx +++ b/src/components/atoms/StatusIcon/story.tsx @@ -70,3 +70,8 @@ storiesOf('StatusIcon', module) )) + .add('info hollow', () => ( + + + + )) diff --git a/src/components/atoms/StatusPill/StatusPill.tsx b/src/components/atoms/StatusPill/StatusPill.tsx index a99d1759..e7187222 100644 --- a/src/components/atoms/StatusPill/StatusPill.tsx +++ b/src/components/atoms/StatusPill/StatusPill.tsx @@ -29,12 +29,31 @@ const LABEL_MAP: { [status: string]: string } = { CANCELLING_AFTER_COMPLETION: 'CANCELLING', FAILED_TO_SCHEDULE: 'UNSCHEDULABLE', FAILED_TO_CANCEL: 'CANCELED', + AWAITING_MINION_ALLOCATIONS: 'AWAITING MINIONS', + ERROR_ALLOCATING_MINIONS: 'MINIONS ERROR', + // Minion Pool statuses + VALIDATING_INPUTS: 'VALIDATING', + ALLOCATING_SHARED_RESOURCES: 'ALLOCATING', + ALLOCATING_MACHINES: 'ALLOCATING', + DEALLOCATING_MACHINES: 'DEALLOCATING', + DEALLOCATING_SHARED_RESOURCES: 'DEALLOCATING', + IN_MAINTENANCE: 'MAINTENANCE', + RESCALING: 'SCALING', + IN_USE: 'IN USE', + // Minion Machine power statuses + POWERING_OFF: 'POWERING OFF', + POWERING_ON: 'POWERING ON', + POWERED_ON: 'POWERED ON', + POWERED_OFF: 'POWERED OFF', + POWER_ERROR: 'ERROR', } const statuses = (status: any) => { switch (status) { case 'COMPLETED': - case 'ALLOCATED': + case 'ALLOCATED': // Minion Pool status + case 'POWERED_ON': // Minion Machine status + case 'AVAILABLE': // Minion Pool status return css` background: ${Palette.success}; color: white; @@ -43,6 +62,8 @@ const statuses = (status: any) => { case 'FAILED_TO_SCHEDULE': case 'FAILED_TO_CANCEL': case 'ERROR': + case 'ERROR_ALLOCATING_MINIONS': + case 'POWER_ERROR': // Minion Machine power status return css` background: ${Palette.alert}; color: white; @@ -66,9 +87,19 @@ const statuses = (status: any) => { case 'STARTING': case 'RUNNING': case 'PENDING': - case 'INITIALIZING': - case 'ALLOCATING': - case 'RECONFIGURING': + case 'AWAITING_MINION_ALLOCATIONS': + case 'INITIALIZING': // Minion Pool status + case 'ALLOCATING': // Minion Pool status + case 'RECONFIGURING': // Minion Pool status + case 'VALIDATING_INPUTS': // Minion Pool status + case 'ALLOCATING_SHARED_RESOURCES': // Minion Pool status + case 'ALLOCATING_MACHINES': // Minion Pool status + case 'SCALING': // Minion Pool status + case 'RESCALING': // Minion Pool status + case 'DEPLOYING': // Minion Pool status + case 'IN_USE': // Minion Pool status + case 'HEALTHCHECKING': // Minion Machine status + case 'POWERING_ON': // Minion Machine power status return css` background: url('${runningImage}'); animation: bgMotion 1s infinite linear; @@ -79,10 +110,15 @@ const statuses = (status: any) => { 100% { background-position: 0 -1px; } } ` + case 'CANCELLING': case 'UNINITIALIZING': - case 'DEALLOCATING': + case 'DEALLOCATING': // Minion Machine status + case 'POWERING_OFF': // Minion Machine power status case 'CANCELLING_AFTER_COMPLETION': + case 'IN_MAINTENANCE': // Minion Pool status + case 'DEALLOCATING_MACHINES': // Minion Pool status + case 'DEALLOCATING_SHARED_RESOURCES': // Minion Pool status return css` background: url('${cancellingImage}'); animation: bgMotion 1s infinite linear; @@ -101,9 +137,11 @@ const statuses = (status: any) => { border-color: transparent; ` case 'UNSCHEDULED': - case 'UNINITIALIZED': - case 'DEALLOCATED': - case 'INITIALIZED': + case 'UNKNOWN': // Minion Pool/Machine status + case 'UNINITIALIZED': // Minion Pool/Machine status + case 'DEALLOCATED': // Minion Pool status + case 'INITIALIZED': // Minion Pool status + case 'POWERED_OFF': // Minion Machine status return css` background: ${Palette.grayscale[2]}; color: ${Palette.black}; @@ -156,6 +194,7 @@ const Wrapper = styled.div` ${(props: any) => statuses(props.status)} ${(props: any) => (props.status === 'INFO' ? getInfoStatusColor(props) : '')} text-transform: uppercase; + overflow: hidden; ` type Props = { diff --git a/src/components/atoms/StatusPill/story.tsx b/src/components/atoms/StatusPill/story.tsx index 9a9d1751..921c5362 100644 --- a/src/components/atoms/StatusPill/story.tsx +++ b/src/components/atoms/StatusPill/story.tsx @@ -40,6 +40,8 @@ const STATUSES = [ 'FAILED_TO_SCHEDULE', 'DEADLOCKED', 'STRANDED_AFTER_DEADLOCK', + 'AWAITING_MINION_ALLOCATIONS', + 'ERROR_ALLOCATING_MINIONS', // Minion Pool statuses 'INITIALIZED', 'UNINITIALIZED', @@ -50,6 +52,8 @@ const STATUSES = [ 'ALLOCATING', 'ALLOCATED', 'RECONFIGURING', + 'IN_USE', + 'HEALTHCHECKING', ] const renderAllStatuses = (small?: boolean) => ( diff --git a/src/components/atoms/Switch/Switch.tsx b/src/components/atoms/Switch/Switch.tsx index 3e6276c2..0391da75 100644 --- a/src/components/atoms/Switch/Switch.tsx +++ b/src/components/atoms/Switch/Switch.tsx @@ -53,6 +53,13 @@ const InputWrapper = styled.div` } ` const inputBackground = (props: any) => { + if (props.checkedColor && props.checked) { + return props.checkedColor + } + if (props.uncheckedColor && !props.checked) { + return props.uncheckedColor + } + if (props.big) { if (props.checked) { return Palette.alert @@ -71,6 +78,13 @@ const inputBackground = (props: any) => { return 'white' } const getInputBorderColor = (props: any) => { + if (props.checkedColor && props.checked) { + return props.checkedColor + } + if (props.uncheckedColor && !props.checked) { + return props.uncheckedColor + } + if (props.big && props.checked) { return Palette.alert } @@ -148,6 +162,8 @@ type Props = { style?: React.CSSProperties, required?: boolean, highlight?: boolean, + checkedColor?: string, + uncheckedColor?: string, } type State = { lastChecked: boolean | null | undefined, @@ -218,6 +234,8 @@ class Switch extends React.Component { checked={this.props.checked} height={this.props.height} secondary={this.props.secondary} + checkedColor={this.props.checkedColor} + uncheckedColor={this.props.uncheckedColor} > { checked={this.props.checked} height={this.props.height} secondary={this.props.secondary} + checkedColor={this.props.checkedColor} + uncheckedColor={this.props.uncheckedColor} /> diff --git a/src/components/molecules/Dropdown/Dropdown.tsx b/src/components/molecules/Dropdown/Dropdown.tsx index 2d6907b5..4a34e7ee 100644 --- a/src/components/molecules/Dropdown/Dropdown.tsx +++ b/src/components/molecules/Dropdown/Dropdown.tsx @@ -107,17 +107,41 @@ const Checkmark = styled.div` } } ` +const getListItemColor = (props: any) => { + if (props.disabled) { + return Palette.grayscale[3] + } + if (props.multipleSelected) { + return Palette.primary + } + if (props.selected) { + return 'white' + } + if (props.dim) { + return Palette.grayscale[3] + } + return Palette.grayscale[4] +} +const getListBackgroundColor = (props: any) => { + if (props.arrowSelected) { + return css`background: ${Palette.primary}44;` + } + if (props.selected) { + return css`background: ${Palette.primary};` + } + return '' +} const ListItem = styled.div` position: relative; display: flex; - color: ${(props: any) => (props.multipleSelected ? Palette.primary : props.selected ? 'white' : props.dim ? Palette.grayscale[3] : Palette.grayscale[4])}; - ${(props: any) => (props.arrowSelected ? css`background: ${Palette.primary}44;` : '')} - ${(props: any) => (props.selected ? css`background: ${Palette.primary};` : '')} + color: ${(props: any) => getListItemColor(props)}; + ${(props: any) => getListBackgroundColor(props)} ${(props: any) => (props.selected ? css`font-weight: ${StyleProps.fontWeights.medium};` : '')} padding: 8px 16px; transition: all ${StyleProps.animations.swift}; padding-left: ${(props: any) => props.paddingLeft}px; word-break: break-word; + ${props => (props.disabled ? css`cursor: default;` : '')} &:first-child { border-top-left-radius: ${StyleProps.borderRadius}; @@ -137,7 +161,11 @@ const ListItem = styled.div` } } ` -const DuplicatedLabel = styled.div` +const SubtitleLabel = styled.div` + display: flex; + font-size: 11px; +` +const DuplicatedLabel = styled.div` display: flex; font-size: 11px; span { @@ -445,6 +473,12 @@ class Dropdown extends React.Component { this.wrapperRef.focus() setTimeout(() => { this.ignoreFocusHandler = false }, 100) } + + if (item.disabled) { + resetFocus() + return + } + if (!this.props.multipleSelection) { this.setState({ showDropdownList: false, firstItemHover: false }, () => { resetFocus() @@ -588,6 +622,7 @@ class Dropdown extends React.Component { dim={this.props.dimFirstItem && i === 0} paddingLeft={this.props.multipleSelection ? 8 : 16} arrowSelected={i === this.state.arrowSelection} + disabled={item.disabled} > {this.props.multipleSelection ? ( { ) : null} {label === '' ? '\u00A0' : label} - {duplicatedLabel ? ({value || ''}) : ''} + {item.subtitleLabel ? ( + {item.subtitleLabel} + ) : null} + + {duplicatedLabel ? ({value || ''}) : ''} ) diff --git a/src/components/molecules/Dropdown/story.tsx b/src/components/molecules/Dropdown/story.tsx index ee8d966b..532a0973 100644 --- a/src/components/molecules/Dropdown/story.tsx +++ b/src/components/molecules/Dropdown/story.tsx @@ -135,3 +135,19 @@ storiesOf('Dropdown', module) ]} /> )) + .add('subtitle label', () => ( + + )) diff --git a/src/components/molecules/FieldInput/FieldInput.tsx b/src/components/molecules/FieldInput/FieldInput.tsx index b12415e5..ca3d0ff9 100644 --- a/src/components/molecules/FieldInput/FieldInput.tsx +++ b/src/components/molecules/FieldInput/FieldInput.tsx @@ -228,11 +228,15 @@ class FieldInput extends React.Component { return { label: typeof e === 'string' ? (useDictionary ? LabelDictionary.get(e) : e) : e.name || e.label, value: typeof e === 'string' ? e : e.id || e.value, + disabled: typeof e !== 'string' ? Boolean(e.disabled) : false, + subtitleLabel: typeof e !== 'string' ? e.subtitleLabel || '' : false, } }) if (this.props.addNullValue) { items = [ - { label: 'Choose a value', value: null }, + { + label: 'Choose a value', value: null, disabled: false, subtitleLabel: '', + }, ...items, ] } diff --git a/src/components/molecules/MainDetailsTable/MainDetailsTable.tsx b/src/components/molecules/MainDetailsTable/MainDetailsTable.tsx index aa8b7b54..1f681338 100644 --- a/src/components/molecules/MainDetailsTable/MainDetailsTable.tsx +++ b/src/components/molecules/MainDetailsTable/MainDetailsTable.tsx @@ -280,7 +280,10 @@ class MainDetailsTable extends React.Component { destinationKey = destinationName as string destinationBody = getBody(transferDisk) } - } else if (this.props.item?.type === 'migration' && this.props.item.last_execution_status === 'RUNNING') { + } else if (this.props.item?.type === 'migration' && ( + this.props.item.last_execution_status === 'RUNNING' + || this.props.item.last_execution_status === 'AWAITING_MINION_ALLOCATIONS' + )) { destinationBody = ['Waiting for migration to finish'] } @@ -355,7 +358,10 @@ class MainDetailsTable extends React.Component { destinationNetworkName = destinationNic.network_name destinationBody = getBody(destinationNic) } - } else if (this.props.item?.type === 'migration' && this.props.item.last_execution_status === 'RUNNING') { + } else if (this.props.item?.type === 'migration' && ( + this.props.item.last_execution_status === 'RUNNING' + || this.props.item.last_execution_status === 'AWAITING_MINION_ALLOCATIONS' + )) { destinationBody = ['Waiting for migration to finish'] } @@ -389,7 +395,7 @@ class MainDetailsTable extends React.Component { && minionPoolMappings[instance.instance_name || instance.id || instance.name] if (minionPoolId) { const minionPool = this.props.minionPools.find(m => m.id === minionPoolId) - sourceBody.push(`Minion Pool: ${minionPool?.pool_name || minionPoolId}`) + sourceBody.push(`Minion Pool: ${minionPool?.name || minionPoolId}`) } let destinationBody: string[] = [] let destinationName: string = '' @@ -397,7 +403,10 @@ class MainDetailsTable extends React.Component { if (transferResult) { destinationName = transferResult.instance_name || transferResult.name destinationBody = getBody(transferResult) - } else if (this.props.item?.type === 'migration' && this.props.item.last_execution_status === 'RUNNING') { + } else if (this.props.item?.type === 'migration' && ( + this.props.item.last_execution_status === 'RUNNING' + || this.props.item.last_execution_status === 'AWAITING_MINION_ALLOCATIONS' + )) { destinationName = 'Waiting for migration to finish' } const instanceName = instance.instance_name || instance.id diff --git a/src/components/molecules/MinionPoolConfirmationModal/MinionPoolConfirmationModal.tsx b/src/components/molecules/MinionPoolConfirmationModal/MinionPoolConfirmationModal.tsx new file mode 100644 index 00000000..6d45dc9b --- /dev/null +++ b/src/components/molecules/MinionPoolConfirmationModal/MinionPoolConfirmationModal.tsx @@ -0,0 +1,110 @@ +/* +Copyright (C) 2020 Cloudbase Solutions SRL +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +import React from 'react' +import { observer } from 'mobx-react' +import styled from 'styled-components' + +import Button from '../../atoms/Button' + +import KeyboardManager from '../../../utils/KeyboardManager' +import StatusImage from '../../atoms/StatusImage/StatusImage' +import FieldInput from '../FieldInput/FieldInput' +import Modal from '../Modal/Modal' + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + padding: 0 32px 32px 32px; +` +const Header = styled.div` + display: flex; + flex-direction: column; + align-items: center; + margin: 32px; +` +const Description = styled.div` + margin-top: 32px; +` +const Form = styled.div` + height: 120px; +` +const FieldInputStyled = styled(FieldInput)` + width: 319px; + justify-content: space-between; +` +const Buttons = styled.div` + display: flex; + justify-content: space-between; + width: 100%; +` +type Props = { + onCancelClick: () => void, + onExecuteClick: (force: boolean) => void, +} +type State = { + force: boolean, +} +@observer +class MinionPoolConfirmationModal extends React.Component { + state: State = { + force: false, + } + + componentDidMount() { + KeyboardManager.onEnter('minion-pool-confirmation', () => { this.props.onExecuteClick(this.state.force) }, 2) + } + + componentWillUnmount() { + KeyboardManager.removeKeyDown('minion-pool-confirmation') + } + + render() { + return ( + + +
+ + Are you sure you want to deallocate the minion pool? +
+
+ { this.setState({ force: value }) }} + /> + + + + + +
+
+ ) + } +} + +export default MinionPoolConfirmationModal diff --git a/src/components/molecules/MinionPoolConfirmationModal/package.json b/src/components/molecules/MinionPoolConfirmationModal/package.json new file mode 100644 index 00000000..667ca8d7 --- /dev/null +++ b/src/components/molecules/MinionPoolConfirmationModal/package.json @@ -0,0 +1,6 @@ +{ + "name": "MinionPoolConfirmationModal", + "version": "0.0.0", + "private": true, + "main": "./MinionPoolConfirmationModal.tsx" +} diff --git a/src/components/molecules/MinionPoolListItem/MinionPoolListItem.tsx b/src/components/molecules/MinionPoolListItem/MinionPoolListItem.tsx index 4a0c6610..014805eb 100644 --- a/src/components/molecules/MinionPoolListItem/MinionPoolListItem.tsx +++ b/src/components/molecules/MinionPoolListItem/MinionPoolListItem.tsx @@ -59,7 +59,7 @@ const Wrapper = styled.div` } ` -const Image = styled.div` +const Image = styled.div` min-width: 48px; height: 48px; background: url('${itemImage}') no-repeat center; @@ -106,7 +106,7 @@ type Props = { @observer class MinionPoolListItem extends React.Component { getStatus() { - return this.props.item.pool_status + return this.props.item.status } renderCreationDate() { @@ -135,11 +135,14 @@ class MinionPoolListItem extends React.Component { ) } - renderUser() { + renderCreatedCount() { + const createdCount = this.props.item.minion_machines.filter(m => m.allocation_status === 'IN_USE' || m.allocation_status === 'AVAILABLE').length + const totalCount = this.props.item.minion_machines.length + return ( - + - OS Type + Allocated { overflow: 'hidden', }} > - {this.props.item.pool_os_type} + {createdCount} of {totalCount} machines
({this.props.item.maximum_minions} maximum)
) @@ -171,7 +174,7 @@ class MinionPoolListItem extends React.Component { - <TitleLabel>{this.props.item.pool_name}</TitleLabel> + <TitleLabel>{this.props.item.name}</TitleLabel> <StatusWrapper> {status ? ( <StatusPill @@ -184,7 +187,7 @@ class MinionPoolListItem extends React.Component<Props> { {endpointImage} {this.renderCreationDate()} {this.renderUpdateDate()} - {this.renderUser()} + {this.renderCreatedCount()} </Content> </Wrapper> ) diff --git a/src/components/molecules/MinionPoolListItem/images/minion-pool-list-item.svg b/src/components/molecules/MinionPoolListItem/images/minion-pool-list-item.svg index a48c72ac..ac8a9a2e 100644 --- a/src/components/molecules/MinionPoolListItem/images/minion-pool-list-item.svg +++ b/src/components/molecules/MinionPoolListItem/images/minion-pool-list-item.svg @@ -1,7 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" - xmlns:dc="http://purl.org/dc/elements/1.1/" +<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" @@ -9,7 +7,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="minion-pool-list-item.svg" + sodipodi:docname="minion-pool-list-item-3.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" id="svg8" version="1.1" @@ -18,14 +16,6 @@ width="48"> <defs id="defs2"> - <linearGradient - osb:paint="solid" - id="linearGradient892"> - <stop - id="stop890" - offset="0" - style="stop-color:#0044ca;stop-opacity:1;" /> - </linearGradient> <path d="M24,48 C37.254834,48 48,37.254834 48,24 C48,10.745166 37.254834,0 24,0 C10.745166,0 0,10.745166 0,24 C0,37.254834 10.745166,48 24,48 Z" id="path-1" /> @@ -33,17 +23,18 @@ <sodipodi:namedview inkscape:window-maximized="0" inkscape:window-y="0" - inkscape:window-x="98" + inkscape:window-x="220" inkscape:window-height="1040" inkscape:window-width="1274" + inkscape:snap-global="false" units="px" showgrid="false" inkscape:document-rotation="0" inkscape:current-layer="layer1" inkscape:document-units="mm" - inkscape:cy="12.413305" - inkscape:cx="32.047639" - inkscape:zoom="7.9195959" + inkscape:cy="1.1304786" + inkscape:cx="7.5499781" + inkscape:zoom="3.959798" inkscape:pageshadow="2" inkscape:pageopacity="0.0" borderopacity="1.0" @@ -66,45 +57,25 @@ id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1"> - <g - id="Icon/Project/ProjectListItem" - style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1" - transform="scale(0.26458333)"> - <mask - id="mask-2" - fill="#ffffff"> - <use - xlink:href="#path-1" - id="use15" - x="0" - y="0" - width="100%" - height="100%" /> - </mask> - <use - id="Pat-Benetar" - fill="#c8ccd7" - fill-rule="evenodd" - xlink:href="#path-1" - x="0" - y="0" - width="100%" - height="100%" /> - <path - sodipodi:nodetypes="cccccssssssssssssssccscsssscs" - d="M 28.176,16.752495 V 12.78 h -7.944 v 3.973 z M 11.77,19.831 v 14.996 c 0,0.775618 0.634385,1.41 1.41,1.41 h 22.047 c 0.775615,0 1.41,-0.634383 1.41,-1.41 V 19.831 c 0,-0.775615 -0.634385,-1.41 -1.41,-1.41 H 13.18 c -0.775615,0 -1.41,0.634386 -1.41,1.41 z m 23.71529,-3.078484 c 1.565332,0 2.820418,1.255085 2.820418,2.820417 v 15.5123 c 0,1.565331 -1.255086,2.820418 -2.820418,2.820418 H 12.921944 c -1.565332,0 -2.820418,-1.255087 -2.820418,-2.820418 l 0.0141,-15.5123 c 0,-1.565332 1.240984,-2.820417 2.806316,-2.820417 h 5.640837 v -2.820419 c 0,-1.565332 1.255085,-2.820419 2.820418,-2.820419 h 5.640835 c 1.565332,0 2.820418,1.255087 2.820418,2.820419 v 2.820419 z" - style="fill:#0044ca;fill-opacity:1;stroke:none;stroke-width:1.41;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path80" /> - <path - sodipodi:nodetypes="cccccssssssssssssssccscsssscs" - id="path917" - style="fill:none;fill-opacity:1;stroke:none;stroke-width:1.41021;stroke-opacity:1" - d="m 27.675789,16.752495 v -3.472062 h -6.944288 v 3.472083 z m -15.405554,3.578939 v 13.995287 c 0,0.775617 0.63465,1.408655 1.410265,1.408713 l 21.046288,0.0016 c 0.775615,5.7e-5 1.410265,-0.634648 1.410265,-1.410265 V 20.331434 c 0,-0.775615 -0.63465,-1.410266 -1.410265,-1.410266 H 13.6805 c -0.775615,0 -1.410265,0.634651 -1.410265,1.410266 z M 35.48529,16.752516 c 1.565332,0 2.820418,1.255085 2.820418,2.820417 v 15.5123 c 0,1.565331 -1.255086,2.820418 -2.820418,2.820418 H 12.921944 c -1.565332,0 -2.820418,-1.255087 -2.820418,-2.820418 l 0.0141,-15.5123 c 0,-1.565332 1.240984,-2.820417 2.806316,-2.820417 h 5.640837 v -2.820419 c 0,-1.565332 1.255085,-2.820419 2.820418,-2.820419 h 5.640835 c 1.565332,0 2.820418,1.255087 2.820418,2.820419 v 2.820419 z" /> - <path - id="path896" - style="fill:#0044ca;fill-opacity:1;stroke:none;stroke-width:1.41;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 28.176,16.752495 V 12.78 h -7.944 v 3.973 z M 11.77,19.831 v 14.996 c 0,0.775618 0.634385,1.41 1.41,1.41 h 22.047 c 0.775615,0 1.41,-0.634383 1.41,-1.41 V 19.831 c 0,-0.775615 -0.634385,-1.41 -1.41,-1.41 H 13.18 c -0.775615,0 -1.41,0.634386 -1.41,1.41 z m 23.71529,-3.078484 c 1.565332,0 2.820418,1.255085 2.820418,2.820417 v 15.5123 c 0,1.565331 -1.255086,2.820418 -2.820418,2.820418 H 12.921944 c -1.565332,0 -2.820418,-1.255087 -2.820418,-2.820418 l 0.0141,-15.5123 c 0,-1.565332 1.240984,-2.820417 2.806316,-2.820417 h 5.640837 v -2.820419 c 0,-1.565332 1.255085,-2.820419 2.820418,-2.820419 h 5.640835 c 1.565332,0 2.820418,1.255087 2.820418,2.820419 v 2.820419 z" - sodipodi:nodetypes="cccccssssssssssssssccscsssscs" /> - </g> + <use + style="stroke:none;stroke-width:1" + height="100%" + width="100%" + y="0" + x="0" + xlink:href="#path-1" + fill-rule="evenodd" + fill="#c8ccd7" + id="Pat-Benetar" + transform="scale(0.26458333)" /> + <path + d="m 5.3261719,1.7226562 c -0.1052735,0 -0.1914063,0.086067 -0.1914063,0.1914063 V 2.2714844 C 3.6758668,2.7814944 2.7018583,4.1595931 2.7070312,5.7050781 V 5.9589844 L 2.3417969,6.1132812 c -0.070614,0.029811 -0.1152344,0.099283 -0.1152344,0.1757813 v 1.0019531 c 0,0.1052735 0.084114,0.1894532 0.1894531,0.1894532 h 0.2480469 v 0.3828124 c 0,0.1052736 0.086069,0.1914063 0.1914063,0.1914063 h 0.6855468 c 0.065053,0.2388712 0.1595643,0.4672131 0.2832032,0.6816406 l -0.484375,0.484375 c -0.074041,0.074366 -0.074041,0.1951033 0,0.2695313 l 0.8105468,0.8105466 c 0.074363,0.07404 0.1951022,0.07404 0.2695313,0 l 0.484375,-0.4843748 c 0.2139106,0.1236391 0.4433523,0.2199098 0.6816406,0.2851558 v 0.683594 c 0,0.105275 0.086068,0.191406 0.1914063,0.191406 H 6.921875 c 0.1053385,0 0.1894531,-0.08613 0.1894531,-0.191406 V 10.101562 C 7.3501992,10.03651 7.5804294,9.9402393 7.7949219,9.8164062 l 0.484375,0.4843748 c 0.074366,0.07404 0.1951023,0.07404 0.2695312,0 L 9.359375,9.4902344 c 0.074041,-0.074428 0.074041,-0.1951667 0,-0.2695313 L 8.875,8.7363281 C 8.9986386,8.5219006 9.0951034,8.2935587 9.1601562,8.0546875 H 9.84375 c 0.1053392,0 0.191406,-0.086133 0.191406,-0.1914063 V 7.4804688 h 0.248047 c 0.105339,0 0.191406,-0.084179 0.191406,-0.1894532 V 6.2890625 c -3.76e-4,-0.077083 -0.04795,-0.1465526 -0.11914,-0.1757813 L 9.9882812,5.9550781 V 5.703125 C 9.9934673,4.1576409 9.0174931,2.7795412 7.5585938,2.2695312 V 1.9140625 c 0,-0.1053386 -0.086133,-0.1914062 -0.1914063,-0.1914063 z m 0.1875,0.3847657 h 1.6640625 v 3.1875 H 5.5175781 Z M 5.1347656,2.6757812 v 2.8105469 c 0,0.1052743 0.086072,0.1894531 0.1914063,0.1894531 h 2.0449219 c 0.1053394,0 0.1914062,-0.084179 0.1914062,-0.1894531 V 2.6757812 C 8.802897,3.1678795 9.6147421,4.3688991 9.609375,5.703125 v 0.3808594 c 0,0.076503 0.046577,0.1460354 0.1171875,0.1757812 L 10.09375,6.4179688 V 7.0996094 H 2.6054688 V 6.4140625 L 2.9746094,6.2558594 c 0.070614,-0.02975 0.1152343,-0.099219 0.1152344,-0.1757813 V 5.6992188 C 3.0856239,4.3666747 3.8964378,3.1680729 5.1347656,2.6757812 Z M 3.0429688,7.4804688 h 1.7441406 c 0.1045624,0.8620441 0.8879558,1.4778033 1.75,1.3730468 0.718165,-0.087167 1.2840553,-0.6548818 1.3710937,-1.3730468 H 9.6542969 L 9.65625,7.671875 H 9.0195312 C 8.9279663,7.669765 8.8493479,7.7328378 8.8300781,7.8222656 8.7658041,8.1210809 8.6470448,8.4058428 8.4804688,8.6621094 8.4313867,8.7375044 8.4425524,8.8366952 8.5058594,8.9003906 L 8.9609375,9.3574219 8.4160156,9.8984375 7.9589844,9.4433594 C 7.8952907,9.3800526 7.7961025,9.3688864 7.7207031,9.4179688 7.4644382,9.5844805 7.1796755,9.7033025 6.8808594,9.7675781 6.7913647,9.7868488 6.7284001,9.8674197 6.7304688,9.9589844 V 10.601562 H 5.9667969 V 9.953125 C 5.9689069,9.8615604 5.9058984,9.7809887 5.8164062,9.7617188 5.5176552,9.6975679 5.2327632,9.5788796 4.9765625,9.4121094 4.9011586,9.3630973 4.8020405,9.3741229 4.7382812,9.4375 L 4.2832031,9.8925781 3.7421875,9.3535156 4.1992188,8.8964844 C 4.2625898,8.8327907 4.271671,8.7336008 4.2226562,8.6582031 4.0545305,8.403482 3.9356804,8.1201112 3.8691406,7.8222656 3.8498064,7.7328343 3.7692995,7.6697414 3.6777344,7.671875 H 3.0429688 Z m 2.1328124,0 H 7.5234375 C 7.4184867,8.1295721 6.8072414,8.5717477 6.1582031,8.4667969 5.6523155,8.3850124 5.2581891,7.986139 5.1757812,7.4804688 Z" + inkscape:href="#path867" + id="path1461" + xlink:href="#path867" + inkscape:original="M 5.3261719 1.7226562 C 5.2208984 1.7226562 5.1347656 1.8087232 5.1347656 1.9140625 L 5.1347656 2.2714844 C 3.6758668 2.7814944 2.7018583 4.1595931 2.7070312 5.7050781 L 2.7070312 5.9589844 L 2.3417969 6.1132812 C 2.2711829 6.1430922 2.2265625 6.2125643 2.2265625 6.2890625 L 2.2265625 7.2910156 C 2.2265625 7.3962891 2.3106762 7.4804688 2.4160156 7.4804688 L 2.6640625 7.4804688 L 2.6640625 7.8632812 C 2.6640625 7.9685548 2.7501311 8.0546875 2.8554688 8.0546875 L 3.5410156 8.0546875 C 3.6060686 8.2935587 3.7005799 8.5219006 3.8242188 8.7363281 L 3.3398438 9.2207031 C 3.2658028 9.2950691 3.2658028 9.4158064 3.3398438 9.4902344 L 4.1503906 10.300781 C 4.2247536 10.374821 4.3454928 10.374821 4.4199219 10.300781 L 4.9042969 9.8164062 C 5.1182075 9.9400453 5.3476492 10.036316 5.5859375 10.101562 L 5.5859375 10.785156 C 5.5859375 10.890431 5.6720056 10.976562 5.7773438 10.976562 L 6.921875 10.976562 C 7.0272135 10.976562 7.1113281 10.890429 7.1113281 10.785156 L 7.1113281 10.101562 C 7.3501992 10.03651 7.5804294 9.9402393 7.7949219 9.8164062 L 8.2792969 10.300781 C 8.3536629 10.374821 8.4743992 10.374821 8.5488281 10.300781 L 9.359375 9.4902344 C 9.433416 9.4158064 9.433416 9.2950677 9.359375 9.2207031 L 8.875 8.7363281 C 8.9986386 8.5219006 9.0951034 8.2935587 9.1601562 8.0546875 L 9.84375 8.0546875 C 9.9490892 8.0546875 10.035156 7.9685545 10.035156 7.8632812 L 10.035156 7.4804688 L 10.283203 7.4804688 C 10.388542 7.4804688 10.474609 7.3962896 10.474609 7.2910156 L 10.474609 6.2890625 C 10.474233 6.2119795 10.426663 6.1425099 10.355469 6.1132812 L 9.9882812 5.9550781 L 9.9882812 5.703125 C 9.9934673 4.1576409 9.0174931 2.7795412 7.5585938 2.2695312 L 7.5585938 1.9140625 C 7.5585938 1.8087239 7.4724607 1.7226563 7.3671875 1.7226562 L 5.3261719 1.7226562 z M 5.5136719 2.1074219 L 7.1777344 2.1074219 L 7.1777344 5.2949219 L 5.5175781 5.2949219 L 5.5136719 2.1074219 z M 5.1347656 2.6757812 L 5.1347656 5.4863281 C 5.1347656 5.5916024 5.2208377 5.6757812 5.3261719 5.6757812 L 7.3710938 5.6757812 C 7.4764332 5.6757812 7.5625 5.5916021 7.5625 5.4863281 L 7.5625 2.6757812 C 8.802897 3.1678795 9.6147421 4.3688991 9.609375 5.703125 L 9.609375 6.0839844 C 9.609375 6.1604874 9.6559521 6.2300198 9.7265625 6.2597656 L 10.09375 6.4179688 L 10.09375 7.0996094 L 2.6054688 7.0996094 L 2.6054688 6.4140625 L 2.9746094 6.2558594 C 3.0452234 6.2261094 3.0898437 6.1566403 3.0898438 6.0800781 L 3.0898438 5.6992188 C 3.0856239 4.3666747 3.8964378 3.1680729 5.1347656 2.6757812 z M 3.0429688 7.4804688 L 4.7871094 7.4804688 C 4.8916718 8.3425129 5.6750652 8.9582721 6.5371094 8.8535156 C 7.2552744 8.7663488 7.8211647 8.1986338 7.9082031 7.4804688 L 9.6542969 7.4804688 L 9.65625 7.671875 L 9.0195312 7.671875 C 8.9279663 7.669765 8.8493479 7.7328378 8.8300781 7.8222656 C 8.7658041 8.1210809 8.6470448 8.4058428 8.4804688 8.6621094 C 8.4313867 8.7375044 8.4425524 8.8366952 8.5058594 8.9003906 L 8.9609375 9.3574219 L 8.4160156 9.8984375 L 7.9589844 9.4433594 C 7.8952907 9.3800526 7.7961025 9.3688864 7.7207031 9.4179688 C 7.4644382 9.5844805 7.1796755 9.7033025 6.8808594 9.7675781 C 6.7913647 9.7868488 6.7284001 9.8674197 6.7304688 9.9589844 L 6.7304688 10.601562 L 5.9667969 10.601562 L 5.9667969 9.953125 C 5.9689069 9.8615604 5.9058984 9.7809887 5.8164062 9.7617188 C 5.5176552 9.6975679 5.2327632 9.5788796 4.9765625 9.4121094 C 4.9011586 9.3630973 4.8020405 9.3741229 4.7382812 9.4375 L 4.2832031 9.8925781 L 3.7421875 9.3535156 L 4.1992188 8.8964844 C 4.2625898 8.8327907 4.271671 8.7336008 4.2226562 8.6582031 C 4.0545305 8.403482 3.9356804 8.1201112 3.8691406 7.8222656 C 3.8498064 7.7328343 3.7692995 7.6697414 3.6777344 7.671875 L 3.0429688 7.671875 L 3.0429688 7.4804688 z M 5.1757812 7.4804688 L 7.5234375 7.4804688 C 7.4184867 8.1295721 6.8072414 8.5717477 6.1582031 8.4667969 C 5.6523155 8.3850124 5.2581891 7.986139 5.1757812 7.4804688 z " + inkscape:radius="0" + sodipodi:type="inkscape:offset" + style="fill:#0044ca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0165542" /> </g> </svg> diff --git a/src/components/molecules/NewItemDropdown/NewItemDropdown.tsx b/src/components/molecules/NewItemDropdown/NewItemDropdown.tsx index 1c6825d5..8f3559c6 100644 --- a/src/components/molecules/NewItemDropdown/NewItemDropdown.tsx +++ b/src/components/molecules/NewItemDropdown/NewItemDropdown.tsx @@ -30,7 +30,7 @@ import replicaImage from './images/replica.svg' import endpointImage from './images/endpoint.svg' import userImage from './images/user.svg' import projectImage from './images/project.svg' -import minionPoolImage from './images/minionPool.svg' +import minionPoolImage from './images/minion-pool.svg' import { navigationMenu } from '../../../constants' diff --git a/src/components/molecules/NewItemDropdown/images/minion-pool.svg b/src/components/molecules/NewItemDropdown/images/minion-pool.svg new file mode 100644 index 00000000..6652b1f4 --- /dev/null +++ b/src/components/molecules/NewItemDropdown/images/minion-pool.svg @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="minion-pool.svg" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + id="svg886" + version="1.1" + viewBox="0 0 12.170833 12.170834" + height="46" + width="46"> + <defs + id="defs880"> + <path + d="M24,48 C37.254834,48 48,37.254834 48,24 C48,10.745166 37.254834,0 24,0 C10.745166,0 0,10.745166 0,24 C0,37.254834 10.745166,48 24,48 Z" + id="path-1" /> + </defs> + <sodipodi:namedview + inkscape:snap-global="false" + inkscape:window-maximized="0" + inkscape:window-y="0" + inkscape:window-x="147" + inkscape:window-height="1040" + inkscape:window-width="1274" + units="px" + showgrid="false" + inkscape:document-rotation="0" + inkscape:current-layer="layer1-6" + inkscape:document-units="mm" + inkscape:cy="25.881816" + inkscape:cx="28.550402" + inkscape:zoom="5.6" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" /> + <metadata + id="metadata883"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:groupmode="layer" + inkscape:label="Layer 1"> + <g + transform="translate(-3.9404017,-2.4757439)" + id="layer1-6" + inkscape:label="Layer 1"> + <path + d="m 8.6796875,2.5429688 c -0.1024615,0 -0.1855469,0.081681 -0.1855469,0.1835937 v 0.46875 a 0.06708357,0.06708357 0 0 1 -0.044922,0.0625 C 6.5572137,3.9192303 5.2940725,5.708545 5.3007812,7.7128906 V 8.046875 a 0.06708357,0.06708357 0 0 1 -0.041016,0.0625 L 4.7792969,8.3125 c -0.068195,0.028789 -0.1113281,0.093795 -0.1113281,0.1679688 v 1.3183593 c 0,0.1018315 0.083002,0.1835938 0.1855468,0.1835938 h 0.3261719 a 0.06708357,0.06708357 0 0 1 0.066406,0.066406 v 0.501953 c 0,0.102464 0.081683,0.185547 0.1835937,0.185547 h 0.9003906 a 0.06708357,0.06708357 0 0 1 0.064453,0.04883 c 0.084009,0.308479 0.2093469,0.605682 0.3691407,0.882813 a 0.06708357,0.06708357 0 0 1 -0.011719,0.08008 l -0.6386719,0.636719 c -0.071928,0.07224 -0.071959,0.187431 0,0.259765 l 1.0664063,1.066407 c 0.071917,0.07161 0.1896998,0.07164 0.2617187,0 L 8.078125,13.074219 a 0.06708357,0.06708357 0 0 1 0.080078,-0.01172 c 0.2762053,0.159647 0.5728314,0.282847 0.8808594,0.367188 a 0.06708357,0.06708357 0 0 1 0.048828,0.06445 v 0.902343 c 0,0.102463 0.081057,0.183594 0.1835938,0.183594 h 1.5058596 c 0.102541,0 0.185547,-0.08177 0.185547,-0.183594 v -0.902343 a 0.06708357,0.06708357 0 0 1 0.04883,-0.06445 c 0.308688,-0.08406 0.603767,-0.207212 0.880859,-0.367188 a 0.06708357,0.06708357 0 0 1 0.08008,0.01172 l 0.638672,0.636719 c 0.07225,0.07193 0.187428,0.07196 0.259766,0 L 13.9375,12.644531 c 0.07196,-0.07233 0.07192,-0.187527 0,-0.259765 l -0.636719,-0.636719 a 0.06708357,0.06708357 0 0 1 -0.01172,-0.08008 c 0.159677,-0.27693 0.283111,-0.574085 0.367188,-0.882813 a 0.06708357,0.06708357 0 0 1 0.06445,-0.04883 h 0.900391 c 0.101912,0 0.185547,-0.08372 0.185547,-0.185547 v -0.501953 a 0.06708357,0.06708357 0 0 1 0.06641,-0.066406 h 0.326172 c 0.102542,0 0.183593,-0.081136 0.183593,-0.1835938 V 8.4804688 C 15.382446,8.4054342 15.338234,8.3407068 15.269531,8.3125 a 0.06708357,0.06708357 0 0 1 -0.002,0 L 14.783203,8.1035156 a 0.06708357,0.06708357 0 0 1 -0.03906,-0.0625 V 7.7109375 C 14.750918,5.7066389 13.485769,3.9172819 11.59375,3.2558594 a 0.06708357,0.06708357 0 0 1 -0.04492,-0.0625 V 2.7265625 c 0,-0.1025404 -0.08114,-0.1835937 -0.183594,-0.1835937 z m 0.2460937,0.3710937 h 2.1874998 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 v 4.1933593 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 8.9296875 A 0.06708357,0.06708357 0 0 1 8.8632812,7.1738281 L 8.859375,2.9804688 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z M 8.4238281,3.6621094 a 0.06708357,0.06708357 0 0 1 0.070312,0.066406 v 3.6953125 c 0,0.1018289 0.083649,0.1855469 0.1855469,0.1855469 h 2.6894535 c 0.101914,0 0.185547,-0.083715 0.185547,-0.1855469 V 3.7285156 a 0.06708357,0.06708357 0 0 1 0.0918,-0.0625 c 1.656879,0.6573276 2.73959,2.2627547 2.732422,4.0449219 v 0.5019531 c 0,0.073313 0.04486,0.1391422 0.113282,0.1679688 a 0.06708357,0.06708357 0 0 1 0.002,0 l 0.484375,0.2089844 a 0.06708357,0.06708357 0 0 1 0.03906,0.0625 V 9.546875 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 5.1015625 A 0.06708357,0.06708357 0 0 1 5.0351562,9.546875 V 8.6464844 a 0.06708357,0.06708357 0 0 1 0.039063,-0.0625 L 5.5585938,8.375 a 0.06708357,0.06708357 0 0 1 0.00195,0 C 5.6287048,8.3462845 5.671875,8.2813212 5.671875,8.2070312 V 7.7050781 C 5.6662384,5.9251416 6.7482637,4.3235875 8.4023438,3.6660156 a 0.06708357,0.06708357 0 0 1 0.021484,-0.00391 z M 5.6757812,9.9824219 H 7.96875 a 0.06708357,0.06708357 0 0 1 0.066406,0.058594 c 0.1331549,1.097771 1.1307665,1.879493 2.2285158,1.746093 0.914206,-0.110965 1.633332,-0.831804 1.74414,-1.746093 a 0.06708357,0.06708357 0 0 1 0.06641,-0.058594 h 2.296875 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 l 0.002,0.251953 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 h -0.835938 a 0.06708357,0.06708357 0 0 1 -0.002,0 c -0.089,-0.002 -0.162914,0.05763 -0.18164,0.144531 -0.08624,0.400945 -0.245158,0.784924 -0.46875,1.128906 -0.04782,0.07346 -0.03979,0.166877 0.02148,0.228516 l 0.601562,0.599609 a 0.06708357,0.06708357 0 0 1 0,0.09375 l -0.71875,0.712891 a 0.06708357,0.06708357 0 0 1 -0.09375,0 l -0.599609,-0.59961 c -0.06162,-0.06126 -0.157872,-0.07069 -0.230469,-0.02344 -0.34387,0.223435 -0.725955,0.382495 -1.126953,0.46875 -0.0863,0.01858 -0.146542,0.09458 -0.144531,0.183594 a 0.06708357,0.06708357 0 0 1 0,0.002 v 0.845703 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 H 9.5234375 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.853516 a 0.06708357,0.06708357 0 0 1 0,-0.002 c 0.00202,-0.08827 -0.059338,-0.162876 -0.1464843,-0.181641 -0.4011171,-0.08613 -0.7832732,-0.246989 -1.1269531,-0.470703 -0.072662,-0.04723 -0.1687411,-0.03792 -0.2304688,0.02344 l -0.5996094,0.599609 a 0.06708357,0.06708357 0 0 1 -0.09375,0 L 6.5488281,12.558594 a 0.06708357,0.06708357 0 0 1 0,-0.09375 l 0.6015625,-0.59961 c 0.06093,-0.06124 0.069046,-0.157315 0.021484,-0.230468 -0.2256271,-0.341843 -0.3872726,-0.72337 -0.4765625,-1.123047 -0.018799,-0.08697 -0.092703,-0.146604 -0.1816406,-0.144531 a 0.06708357,0.06708357 0 0 1 -0.00195,0 H 5.6757812 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.251953 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z m 2.7949219,0 c 0.0026,0 0.00911,0 0.00977,0 h 3.0898432 a 0.06708357,0.06708357 0 0 1 0.06641,0.076172 C 11.492907,10.948055 10.655096,11.555939 9.765625,11.412109 9.0712748,11.299865 8.5243622,10.752942 8.4121094,10.058594 a 0.06708357,0.06708357 0 0 1 0.058594,-0.076172 z" + inkscape:original="M 8.6796875 2.4765625 C 8.5412293 2.4765625 8.4277344 2.5880175 8.4277344 2.7265625 L 8.4277344 3.1953125 C 6.5089469 3.8660931 5.2275715 5.6802221 5.234375 7.7128906 L 5.234375 8.046875 L 4.7539062 8.25 C 4.6610331 8.2892071 4.6015625 8.3798616 4.6015625 8.4804688 L 4.6015625 9.7988281 C 4.6015625 9.9372866 4.7149694 10.048828 4.8535156 10.048828 L 5.1796875 10.048828 L 5.1796875 10.550781 C 5.1796875 10.689241 5.2911441 10.802734 5.4296875 10.802734 L 6.3300781 10.802734 C 6.4156371 11.116904 6.5424639 11.41915 6.7050781 11.701172 L 6.0664062 12.337891 C 5.9690252 12.435701 5.9690253 12.593517 6.0664062 12.691406 L 7.1328125 13.757812 C 7.2306135 13.855192 7.39039 13.855193 7.4882812 13.757812 L 8.125 13.121094 C 8.4063408 13.283709 8.7080795 13.408328 9.0214844 13.494141 L 9.0214844 14.396484 C 9.0214844 14.534944 9.1329445 14.646484 9.2714844 14.646484 L 10.777344 14.646484 C 10.915888 14.646484 11.029297 14.53494 11.029297 14.396484 L 11.029297 13.494141 C 11.343468 13.408591 11.643675 13.283964 11.925781 13.121094 L 12.564453 13.757812 C 12.662263 13.855192 12.820077 13.855193 12.917969 13.757812 L 13.984375 12.691406 C 14.081755 12.593516 14.081755 12.435696 13.984375 12.337891 L 13.347656 11.701172 C 13.510269 11.41915 13.635144 11.116904 13.720703 10.802734 L 14.621094 10.802734 C 14.75964 10.802734 14.873047 10.689236 14.873047 10.550781 L 14.873047 10.048828 L 15.199219 10.048828 C 15.337764 10.048828 15.449219 9.9372836 15.449219 9.7988281 L 15.449219 8.4804688 C 15.448724 8.3790907 15.388557 8.2884428 15.294922 8.25 L 14.810547 8.0410156 L 14.810547 7.7109375 C 14.81742 5.6782702 13.534022 3.8641399 11.615234 3.1933594 L 11.615234 2.7265625 C 11.615234 2.5880179 11.503686 2.4765625 11.365234 2.4765625 L 8.6796875 2.4765625 z M 8.9257812 2.9804688 L 11.113281 2.9804688 L 11.113281 7.1738281 L 8.9296875 7.1738281 L 8.9257812 2.9804688 z M 8.4277344 3.7285156 L 8.4277344 7.4238281 C 8.4277344 7.5622878 8.541154 7.6757812 8.6796875 7.6757812 L 11.369141 7.6757812 C 11.507687 7.6757812 11.621094 7.5622872 11.621094 7.4238281 L 11.621094 3.7285156 C 13.252501 4.375738 14.319558 5.9561234 14.3125 7.7109375 L 14.3125 8.2128906 C 14.3125 8.3135061 14.373931 8.4042365 14.466797 8.4433594 L 14.951172 8.6523438 L 14.951172 9.546875 L 5.1015625 9.546875 L 5.1015625 8.6464844 L 5.5859375 8.4375 C 5.6788105 8.3983719 5.7382812 8.3077305 5.7382812 8.2070312 L 5.7382812 7.7050781 C 5.7327312 5.9524758 6.7990481 4.3759923 8.4277344 3.7285156 z M 5.6757812 10.048828 L 7.96875 10.048828 C 8.1062732 11.182614 9.1376971 11.991295 10.271484 11.853516 C 11.216037 11.738867 11.959743 10.99338 12.074219 10.048828 L 14.371094 10.048828 L 14.373047 10.300781 L 13.537109 10.300781 C 13.416686 10.298081 13.312455 10.380432 13.287109 10.498047 C 13.202579 10.891058 13.047211 11.266466 12.828125 11.603516 C 12.763565 11.702686 12.776095 11.832241 12.859375 11.916016 L 13.460938 12.515625 L 12.742188 13.228516 L 12.142578 12.628906 C 12.058818 12.545636 11.927292 12.531153 11.828125 12.595703 C 11.491077 12.814705 11.117621 12.970151 10.724609 13.054688 C 10.606909 13.080027 10.524623 13.186209 10.527344 13.306641 L 10.527344 14.152344 L 9.5234375 14.152344 L 9.5234375 13.298828 C 9.5261968 13.178406 9.4419223 13.074172 9.3242188 13.048828 C 8.9312923 12.96446 8.5576663 12.807233 8.2207031 12.587891 C 8.121535 12.523436 7.9901091 12.537737 7.90625 12.621094 L 7.3066406 13.220703 L 6.5957031 12.511719 L 7.1972656 11.912109 C 7.2806116 11.828339 7.2929896 11.696821 7.2285156 11.597656 C 7.0073924 11.262637 6.8492341 10.889781 6.7617188 10.498047 C 6.7362954 10.380426 6.6321479 10.297974 6.5117188 10.300781 L 5.6757812 10.300781 L 5.6757812 10.048828 z M 8.4785156 10.048828 L 8.4804688 10.048828 L 11.570312 10.048828 C 11.432279 10.902548 10.629026 11.483738 9.7753906 11.345703 C 9.1093725 11.238039 8.5861883 10.714845 8.4785156 10.048828 z " + inkscape:radius="-0.067076862" + sodipodi:type="inkscape:offset" + style="fill:#0044ca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0217726" + id="path867" /> + </g> + </g> +</svg> diff --git a/src/components/molecules/NewItemDropdown/images/minionPool.svg b/src/components/molecules/NewItemDropdown/images/minionPool.svg deleted file mode 100644 index ca894f48..00000000 --- a/src/components/molecules/NewItemDropdown/images/minionPool.svg +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="minionPool.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - id="svg858" - version="1.1" - viewBox="0 0 12.170833 12.170834" - height="46" - width="46"> - <defs - id="defs852"> - <linearGradient - osb:paint="solid" - id="linearGradient1457"> - <stop - id="stop1455" - offset="0" - style="stop-color:#0044ca;stop-opacity:1;" /> - </linearGradient> - </defs> - <sodipodi:namedview - inkscape:window-maximized="0" - inkscape:window-y="0" - inkscape:window-x="403" - inkscape:window-height="1040" - inkscape:window-width="1274" - units="px" - showgrid="false" - inkscape:document-rotation="0" - inkscape:current-layer="layer1" - inkscape:document-units="mm" - inkscape:cy="6.7745742" - inkscape:cx="9.8898401" - inkscape:zoom="5.6" - inkscape:pageshadow="2" - inkscape:pageopacity="0.0" - borderopacity="1.0" - bordercolor="#666666" - pagecolor="#ffffff" - id="base" /> - <metadata - id="metadata855"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer1" - inkscape:groupmode="layer" - inkscape:label="Layer 1"> - <path - d="M 7.5316674,2.8742546 V 1.4229441 H 4.628978 V 2.8742627 Z M 1.0921959,4.3702397 v 5.8499833 c 0,0.324205 0.2652822,0.589487 0.5894863,0.589487 h 8.7972798 c 0.324206,0 0.589485,-0.265282 0.589485,-0.589487 V 4.3702397 c 0,-0.324205 -0.265279,-0.5894871 -0.589485,-0.5894871 H 1.6816822 c -0.3242041,0 -0.5894863,0.2652821 -0.5894863,0.5894871 z m 9.7038161,-1.495977 c 0.654305,0 1.178927,0.5246217 1.178927,1.1789256 V 10.53728 c 0,0.654302 -0.524622,1.178926 -1.178927,1.178926 H 1.3646085 c -0.65430385,0 -1.17892609,-0.524624 -1.17892609,-1.178926 l 0.005892,-6.4840917 c 0,-0.6543039 0.5187271,-1.1789256 1.17303099,-1.1789256 h 2.357851 V 1.695337 c 0,-0.6543033 0.5246217,-1.17892568 1.1789258,-1.17892568 h 2.357851 c 0.6543032,0 1.1789251,0.52462238 1.1789251,1.17892568 v 1.1789257 z" - style="fill:none;fill-opacity:1;stroke:#0044ca;stroke-width:0.371364;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path10" /> - </g> -</svg> diff --git a/src/components/organisms/EditReplica/EditReplica.tsx b/src/components/organisms/EditReplica/EditReplica.tsx index f8247f7f..bcab49ad 100644 --- a/src/components/organisms/EditReplica/EditReplica.tsx +++ b/src/components/organisms/EditReplica/EditReplica.tsx @@ -581,7 +581,7 @@ class EditReplica extends React.Component<Props, State> { dictionaryKey = `${endpoint.type}-${type}` } const minionPools = minionPoolStore.minionPools - .filter(m => m.pool_platform === type && m.endpoint_id === endpoint.id) + .filter(m => m.platform === type && m.endpoint_id === endpoint.id) return ( <WizardOptions minionPools={minionPools} diff --git a/src/components/organisms/Executions/Executions.tsx b/src/components/organisms/Executions/Executions.tsx index 89ce5370..d927df1a 100644 --- a/src/components/organisms/Executions/Executions.tsx +++ b/src/components/organisms/Executions/Executions.tsx @@ -269,7 +269,7 @@ class Executions extends React.Component<Props, State> { return null } - if (this.state.selectedExecution.status === 'RUNNING') { + if (this.state.selectedExecution.status === 'RUNNING' || this.state.selectedExecution.status === 'AWAITING_MINION_ALLOCATIONS') { return ( <Button secondary diff --git a/src/components/organisms/MainDetails/MainDetails.tsx b/src/components/organisms/MainDetails/MainDetails.tsx index 79244310..5de7dc64 100644 --- a/src/components/organisms/MainDetails/MainDetails.tsx +++ b/src/components/organisms/MainDetails/MainDetails.tsx @@ -46,6 +46,17 @@ const Wrapper = styled.div<any>` flex-direction: column; padding-bottom: 48px; ` +const WarningWrapper = styled.div` + display: flex; + background: ${Palette.warning}66; + padding: 8px; + border-radius: 4px; + margin-bottom: 24px; + align-items: center; +` +const WarningText = styled.div` + margin-left: 8px; +` const ColumnsLayout = styled.div<any>` display: flex; ` @@ -328,7 +339,7 @@ class MainDetails extends React.Component<Props, State> { <Field> <Label>Source Minion Pool</Label> {sourceMinionPool ? ( - <ValueLink to={`/minion-pools/${sourceMinionPool.id}`}>{sourceMinionPool.pool_name}</ValueLink> + <ValueLink to={`/minion-pools/${sourceMinionPool.id}`}>{sourceMinionPool.name}</ValueLink> ) : ( <Value>{this.props.item.origin_minion_pool_id}</Value> )} @@ -376,7 +387,7 @@ class MainDetails extends React.Component<Props, State> { <Field> <Label>Target Minion Pool</Label> {destMinionPool ? ( - <ValueLink to={`/minion-pools/${destMinionPool.id}`}>{destMinionPool.pool_name}</ValueLink> + <ValueLink to={`/minion-pools/${destMinionPool.id}`}>{destMinionPool.name}</ValueLink> ) : ( <Value>{this.props.item.destination_minion_pool_id}</Value> )} @@ -408,9 +419,27 @@ class MainDetails extends React.Component<Props, State> { ) } + renderSpecialError() { + if (this.props.item?.last_execution_status !== 'ERROR_ALLOCATING_MINIONS') { + return null + } + + return ( + <WarningWrapper> + <StatusIcon status="ERROR" /> + <WarningText> + There was an error allocating minion machines for this {this.props.item.type}. + Please review the log events for the selected minion pool(s) + and the logs of the Coriolis Minion Manager component for full details. + </WarningText> + </WarningWrapper> + ) + } + render() { return ( <Wrapper> + {this.renderSpecialError()} {this.renderTable()} {this.props.instancesDetailsLoading || this.props.loading ? null : ( <MainDetailsTable diff --git a/src/components/organisms/MinionEndpointModal/MinionEndpointModal.tsx b/src/components/organisms/MinionEndpointModal/MinionEndpointModal.tsx index a2229f2f..8883904c 100644 --- a/src/components/organisms/MinionEndpointModal/MinionEndpointModal.tsx +++ b/src/components/organisms/MinionEndpointModal/MinionEndpointModal.tsx @@ -25,6 +25,7 @@ import { providerTypes } from '../../../constants' import EndpointLogos from '../../atoms/EndpointLogos/EndpointLogos' import Dropdown from '../../molecules/Dropdown/Dropdown' import Button from '../../atoms/Button/Button' +import Palette from '../../styleUtils/Palette' const Wrapper = styled.div`` const LoadingWrapper = styled.div` @@ -136,11 +137,13 @@ class MinionEndpointModal extends React.Component<Props, State> { return ( <PoolPlatformWrapper> <PoolPlatformOptions> - <PoolPlatformOption>Source Platform</PoolPlatformOption> + <PoolPlatformOption>Source Minion Pool</PoolPlatformOption> <SwitchWrapper> <Switch big checked={this.state.platform === 'destination'} + checkedColor={Palette.primary} + uncheckedColor={Palette.primary} onChange={value => { this.setState({ platform: value ? 'destination' : 'source', @@ -148,7 +151,7 @@ class MinionEndpointModal extends React.Component<Props, State> { }} /> </SwitchWrapper> - <PoolPlatformOption>Destination Platform</PoolPlatformOption> + <PoolPlatformOption>Destination Minion Pool</PoolPlatformOption> </PoolPlatformOptions> </PoolPlatformWrapper> ) diff --git a/src/components/organisms/MinionPoolDetailsContent/MinionPoolDetailsContent.tsx b/src/components/organisms/MinionPoolDetailsContent/MinionPoolDetailsContent.tsx index 0947a543..175c18c1 100644 --- a/src/components/organisms/MinionPoolDetailsContent/MinionPoolDetailsContent.tsx +++ b/src/components/organisms/MinionPoolDetailsContent/MinionPoolDetailsContent.tsx @@ -18,21 +18,26 @@ import { observer } from 'mobx-react' import Button from '../../atoms/Button/Button' import DetailsNavigation from '../../molecules/DetailsNavigation/DetailsNavigation' -import Executions from '../Executions/Executions' import type { Endpoint } from '../../../@types/Endpoint' -import type { Execution, ExecutionTasks } from '../../../@types/Execution' import type { Field } from '../../../@types/Field' import StyleProps from '../../styleUtils/StyleProps' -import { MinionPoolDetails } from '../../../@types/MinionPool' -import { MinionPoolAction } from '../../../stores/MinionPoolStore' import MinionPoolMainDetails from './MinionPoolMainDetails' import { ReplicaItem, MigrationItem } from '../../../@types/MainItem' +import { MinionPoolDetails } from '../../../@types/MinionPool' +import MinionPoolMachines from './MinionPoolMachines' +import StatusImage from '../../atoms/StatusImage/StatusImage' +import MinionPoolEvents from './MinionPoolEvents' const Wrapper = styled.div<any>` display: flex; justify-content: center; ` - +const Loading = styled.div<any>` + display: flex; + justify-content: center; + align-items: center; + height: 200px; +` const Buttons = styled.div<any>` display: flex; justify-content: space-between; @@ -58,33 +63,32 @@ const NavigationItems = [ value: '', }, { - label: 'Executions', - value: 'executions', + label: 'Machines', + value: 'machines', + }, + { + label: 'Events', + value: 'events', }, ] type Props = { item?: MinionPoolDetails | null, + itemId: string replicas: ReplicaItem[], migrations: MigrationItem[] endpoints: Endpoint[], schema: Field[], schemaLoading: boolean, + loading: boolean, page: string, - detailsLoading: boolean, - executions: Execution[], - executionsLoading: boolean, - executionsTasksLoading: boolean, - executionsTasks: ExecutionTasks[], - onRunAction: (action: MinionPoolAction) => void, - onExecutionChange: (executionId: string) => void, - onCancelExecutionClick: (execution: Execution | null, force?: boolean) => void, + onAllocate: () => void, onDeleteMinionPoolClick: () => void, } @observer class MinionPoolDetailsContent extends React.Component<Props> { getStatus() { - return this.props.item?.pool_status + return this.props.item?.status } isEndpointMissing() { @@ -95,34 +99,27 @@ class MinionPoolDetailsContent extends React.Component<Props> { } renderBottomControls() { - const uninitialized = this.props.item?.pool_status === 'UNINITIALIZED' - const deallocated = this.props.item?.pool_status === 'DEALLOCATED' + const status = this.props.item?.status + const deleteEnabled = status === 'DEALLOCATED' || status === 'ERROR' + const deallocated = this.props.item?.status === 'DEALLOCATED' return ( <Buttons> <ButtonColumn> - <Button - primary - hollow - disabled={this.isEndpointMissing() || !uninitialized} - onClick={() => { this.props.onRunAction('set-up-shared-resources') }} - >Setup Shared Resources - </Button> <Button primary hollow disabled={this.isEndpointMissing() || !deallocated} - onClick={() => { this.props.onRunAction('allocate-machines') }} - >Allocate Machines + onClick={() => { this.props.onAllocate() }} + >Allocate </Button> </ButtonColumn> <ButtonColumn> <Button alert hollow - disabled={!uninitialized} + disabled={!deleteEnabled} onClick={this.props.onDeleteMinionPoolClick} - data-test-id="rdContent-deleteButton" >Delete Minion Pool </Button> </ButtonColumn> @@ -130,38 +127,50 @@ class MinionPoolDetailsContent extends React.Component<Props> { ) } - renderMainDetails() { - if (this.props.page !== '') { + renderLoading() { + return ( + <Loading> + <StatusImage loading /> + </Loading> + ) + } + + renderMachines() { + if (this.props.page !== 'machines') { return null } return ( - <MinionPoolMainDetails + <MinionPoolMachines item={this.props.item} replicas={this.props.replicas} migrations={this.props.migrations} - schema={this.props.schema} - schemaLoading={this.props.schemaLoading} - loading={this.props.detailsLoading} - endpoints={this.props.endpoints} - bottomControls={this.renderBottomControls()} /> ) } - renderExecutions() { - if (this.props.page !== 'executions') { + renderEvents() { + if (this.props.page !== 'events') { + return null + } + + return <MinionPoolEvents item={this.props.item} /> + } + + renderMainDetails() { + if (this.props.page !== '') { return null } return ( - <Executions - executions={this.props.executions} - executionsTasks={this.props.executionsTasks} - onCancelExecutionClick={this.props.onCancelExecutionClick} - loading={this.props.executionsLoading} - onChange={this.props.onExecutionChange} - tasksLoading={this.props.executionsTasksLoading} + <MinionPoolMainDetails + item={this.props.item} + replicas={this.props.replicas} + migrations={this.props.migrations} + schema={this.props.schema} + schemaLoading={this.props.schemaLoading} + endpoints={this.props.endpoints} + bottomControls={this.renderBottomControls()} /> ) } @@ -172,12 +181,14 @@ class MinionPoolDetailsContent extends React.Component<Props> { <DetailsNavigation items={NavigationItems} selectedValue={this.props.page} - itemId={this.props.item ? this.props.item.id : ''} + itemId={this.props.itemId} itemType="minion-pool" /> <DetailsBody> - {this.renderMainDetails()} - {this.renderExecutions()} + {!this.props.loading ? this.renderMainDetails() : null} + {!this.props.loading ? this.renderMachines() : null} + {!this.props.loading ? this.renderEvents() : null} + {this.props.loading ? this.renderLoading() : null} </DetailsBody> </Wrapper> ) diff --git a/src/components/organisms/MinionPoolDetailsContent/MinionPoolEvents.tsx b/src/components/organisms/MinionPoolDetailsContent/MinionPoolEvents.tsx new file mode 100644 index 00000000..28f56f66 --- /dev/null +++ b/src/components/organisms/MinionPoolDetailsContent/MinionPoolEvents.tsx @@ -0,0 +1,338 @@ +/* +Copyright (C) 2020 Cloudbase Solutions SRL +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import * as React from 'react' +import moment from 'moment' +import styled from 'styled-components' +import { + MinionPoolDetails, MinionPoolEventProgressUpdate, +} from '../../../@types/MinionPool' +import Palette from '../../styleUtils/Palette' +import StyleProps from '../../styleUtils/StyleProps' +import StatusIcon from '../../atoms/StatusIcon/StatusIcon' +import Pagination from '../../atoms/Pagination/Pagination' +import configLoader from '../../../utils/Config' +import DropdownLink from '../../molecules/DropdownLink/DropdownLink' +import InfoIcon from '../../atoms/InfoIcon/InfoIcon' + +const Wrapper = styled.div`` +const Filters = styled.div` + margin-bottom: 24px; + display: flex; +` +const FilterDropdownWrapper = styled.div` + margin-left: 24px; +` +const EventsTable = styled.div` + background: ${Palette.grayscale[1]}; + border-radius: ${StyleProps.borderRadius}; + margin-bottom: 16px; +` +const Header = styled.div` + display: flex; + border-bottom: 1px solid ${Palette.grayscale[5]}; + padding: 4px 8px; +` +type DataDivProps = { + width?: string, + grow?: boolean + secondary?: boolean +} +const HeaderData = styled.div<DataDivProps>` + ${props => (props.width ? StyleProps.exactWidth(props.width) : '')} + ${props => (props.grow ? 'flex-grow: 1;' : '')} + font-size: 10px; + color: ${Palette.grayscale[5]}; + font-weight: ${StyleProps.fontWeights.medium}; + text-transform: uppercase; +` +const Body = styled.div`` +const Row = styled.div` + display: flex; + padding: 8px; + border-bottom: 1px solid white; +` +const RowData = styled.div<DataDivProps>` + ${props => (props.width ? StyleProps.exactWidth(props.width) : '')} + ${props => (props.grow ? 'flex-grow: 1;' : '')} + ${props => (props.secondary ? `color: ${Palette.grayscale[4]};` : '')} +` +const Message = styled.pre` + font-family: inherit; + white-space: pre-line; + margin: inherit; +` +const NoData = styled.div` + text-align: center; +` +type FilterType = 'all' | 'events' | 'progress' +type EventLevel = 'DEBUG' | 'INFO' | 'ERROR' +type OrderDir = 'asc' | 'desc' +type Props = { + item?: MinionPoolDetails | null, +} + +type State = { + allEvents: MinionPoolEventProgressUpdate[], + prevLenghts: number[] + currentPage: number + filterBy: FilterType + eventLevel: EventLevel + orderDir: OrderDir +} +class MinionPoolEvents extends React.Component<Props, State> { + static sortData(data: MinionPoolEventProgressUpdate[]): MinionPoolEventProgressUpdate[] { + return data.slice() + .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) + } + + state = { + allEvents: [] as MinionPoolEventProgressUpdate[], + prevLenghts: [0, 0], + currentPage: 1, + filterBy: 'events' as FilterType, + eventLevel: 'INFO' as EventLevel, + orderDir: 'desc' as OrderDir, + } + + get filteredEventsWithoutPagination() { + const shouldFilterByEventType = (event: any): boolean => { + if (this.state.filterBy === 'events') { + return event.level + } + if (this.state.filterBy === 'progress') { + return event.current_step != null + } + return true + } + const shouldFilterByLevel = (event: any): boolean => { + if (!event.level) { + return true + } + if (this.state.eventLevel === 'INFO') { + return event.level === 'INFO' || event.level === 'ERROR' + } + if (this.state.eventLevel === 'ERROR') { + return event.level === 'ERROR' + } + return true + } + + return this.state.allEvents + .filter((event: any) => shouldFilterByEventType(event) && shouldFilterByLevel(event)) + .sort((a: any, b: any) => { + if (a.index && b.index && this.state.filterBy !== 'all') { + return this.state.orderDir === 'asc' ? a.index - b.index : b.index - a.index + } + const aTime = new Date(a.created_at).getTime() + const bTime = new Date(b.created_at).getTime() + return this.state.orderDir === 'asc' ? aTime - bTime : bTime - aTime + }) + } + + get filteredEvents() { + return this.filteredEventsWithoutPagination + .filter((_, i) => { + const minI = configLoader.config.maxMinionPoolEventsPerPage * (this.state.currentPage - 1) + const maxI = minI + configLoader.config.maxMinionPoolEventsPerPage + return i >= minI && i < maxI + }) + } + + static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null { + if (!props.item) { + return null + } + const events = props.item?.events || [] + const progressUpdates = props.item?.progress_updates || [] + if (events.length === state.prevLenghts[0] && progressUpdates.length === state.prevLenghts[1]) { + return null + } + + return { + allEvents: events.concat(progressUpdates as any), + prevLenghts: [events.length, progressUpdates.length], + } + } + + setOrderDir(orderDir: OrderDir) { + this.setState({ orderDir, currentPage: 1 }) + } + + filterByType(filterBy: FilterType) { + this.setState({ filterBy, currentPage: 1 }) + } + + filterByLevel(eventLevel: EventLevel) { + this.setState({ eventLevel, currentPage: 1 }) + } + + handlePreviousPageClick() { + this.setState(state => ({ currentPage: state.currentPage - 1 })) + } + + handleNextPageClick() { + this.setState(state => ({ currentPage: state.currentPage + 1 })) + } + + renderHeader() { + return ( + <Header> + <HeaderData grow> + Event / Progress Update Message + </HeaderData> + <HeaderData width="192px"> + Timestamp + </HeaderData> + </Header> + ) + } + + renderBody() { + return ( + <Body> + {this.filteredEvents.map((event: any) => { + let status = 'INFO' + status = event.level || status + if (event.level === 'DEBUG') { + status = 'WARNING' + } + const title = event.current_step ? 'Progress Update' : 'Event' + return ( + <Row key={event.id}> + <RowData + grow + style={{ + display: 'flex', + alignItems: 'center', + paddingRight: '8px', + }} + > + <StatusIcon + style={{ marginRight: '8px' }} + status={status} + title={title} + hollow={event.current_step != null} + /> + <Message>{event.message}</Message> + </RowData> + <RowData width="192px" secondary>{moment(event.created_at).format('YYYY-MM-DD HH:mm:ss')}</RowData> + </Row> + ) + })} + </Body> + ) + } + + renderPagination() { + if (this.filteredEventsWithoutPagination.length + <= configLoader.config.maxMinionPoolEventsPerPage) { + return null + } + const totalPages = Math.ceil(this.filteredEventsWithoutPagination.length + / configLoader.config.maxMinionPoolEventsPerPage) + return ( + <Pagination + previousDisabled={this.state.currentPage === 1} + nextDisabled={this.state.currentPage === totalPages} + onPreviousClick={() => { this.handlePreviousPageClick() }} + onNextClick={() => { this.handleNextPageClick() }} + currentPage={this.state.currentPage} + totalPages={totalPages} + /> + ) + } + + renderEventsTable() { + return ( + <EventsTable> + {this.renderHeader()} + {this.renderBody()} + </EventsTable> + ) + } + + renderFilters() { + return ( + <Filters> + <FilterDropdownWrapper> + <DropdownLink + selectedItem={this.state.filterBy} + items={[ + { label: 'Events', value: 'events' }, + { label: 'Progress Updates', value: 'progress' }, + { label: 'Events & Progress Updates', value: 'all' }, + ]} + onChange={item => { this.filterByType(item.value as FilterType) }} + /> + </FilterDropdownWrapper> + <FilterDropdownWrapper style={{ opacity: this.state.filterBy === 'progress' ? 0.5 : 1 }}> + <DropdownLink + disabled={this.state.filterBy === 'progress'} + selectedItem={this.state.eventLevel} + items={[ + { label: 'DEBUG Event Level', value: 'DEBUG' }, + { label: 'INFO Event Level', value: 'INFO' }, + { label: 'ERROR Event Level', value: 'ERROR' }, + ]} + onChange={item => { this.filterByLevel(item.value as EventLevel) }} + /> + <InfoIcon text="The log level only applies to the events. The progress updates are not affected." /> + </FilterDropdownWrapper> + <FilterDropdownWrapper> + <DropdownLink + selectedItem={this.state.orderDir} + items={[ + { label: 'Ascending Order', value: 'asc' }, + { label: 'Descending Order', value: 'desc' }, + ]} + onChange={item => { this.setOrderDir(item.value as OrderDir) }} + /> + </FilterDropdownWrapper> + </Filters> + ) + } + + renderNoData() { + return ( + <NoData> + There are no events or progress updates associated with this minion pool. + </NoData> + ) + } + + renderNoDataFound() { + return ( + <NoData> + No events found + </NoData> + ) + } + + render() { + const isNoData = this.state.allEvents.length === 0 + const isNoDataFound = this.filteredEvents.length === 0 + return ( + <Wrapper> + {!isNoData ? this.renderFilters() : null} + {!isNoData && !isNoDataFound ? this.renderEventsTable() : null} + {!isNoData && !isNoDataFound ? this.renderPagination() : null} + {isNoData ? this.renderNoData() : null} + {isNoDataFound && !isNoData ? this.renderNoDataFound() : null} + </Wrapper> + ) + } +} + +export default MinionPoolEvents diff --git a/src/components/organisms/MinionPoolDetailsContent/MinionPoolMachines.tsx b/src/components/organisms/MinionPoolDetailsContent/MinionPoolMachines.tsx new file mode 100644 index 00000000..60f9a97f --- /dev/null +++ b/src/components/organisms/MinionPoolDetailsContent/MinionPoolMachines.tsx @@ -0,0 +1,312 @@ +/* +Copyright (C) 2020 Cloudbase Solutions SRL +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import * as React from 'react' +import styled, { createGlobalStyle, css } from 'styled-components' +import moment from 'moment' +import { Collapse } from 'react-collapse' + +import { Link } from 'react-router-dom' +import { MinionMachine, MinionPool } from '../../../@types/MinionPool' +import DropdownLink from '../../molecules/DropdownLink/DropdownLink' +import { ItemReplicaBadge } from '../../molecules/NotificationDropdown' +import Palette from '../../styleUtils/Palette' +import StyleProps from '../../styleUtils/StyleProps' +import Arrow from '../../atoms/Arrow/Arrow' + +import networkImage from './images/network.svg' +import StatusPill from '../../atoms/StatusPill/StatusPill' +import { MigrationItem, ReplicaItem, TransferItem } from '../../../@types/MainItem' + +const GlobalStyle = createGlobalStyle` + .ReactCollapse--collapse { + transition: height 0.4s ease-in-out; + } +` +const Wrapper = styled.div`` +const NoMachines = styled.div` + text-align: center; +` +const Header = styled.div` + display: flex; + align-items: center; + margin-bottom: 32px; + margin-left: 20px; +` +const ArrowStyled = styled(Arrow)` + position: absolute; + left: -24px; +` +const Row = styled.div<any>` + position: relative; + padding: 8px 0; + border-top: 1px solid white; + transition: all ${StyleProps.animations.swift}; + &:last-child { + border-bottom: 0; + border-bottom-left-radius: ${StyleProps.borderRadius}; + border-bottom-right-radius: ${StyleProps.borderRadius}; + } + &:hover { + background: ${Palette.grayscale[0]}; + ${ArrowStyled} { + opacity: 1; + } + } + cursor: pointer; +` +const RowHeader = styled.div<any>` + display: flex; + align-items: center; + padding: 0 16px; +` +const RowHeaderColumn = styled.div<any>` + display: flex; + align-items: center; + ${StyleProps.exactWidth('50%')} +` +const HeaderName = styled.div<any>` + overflow: hidden; + text-overflow: ellipsis; + ${props => StyleProps.exactWidth(`calc(100% - ${props.source ? 120 : 8}px)`)} +` +const HeaderIcon = styled.div<any>` + min-width: 16px; + min-height: 16px; + background: url('${networkImage}') center no-repeat; + margin-right: 16px; +` +const HeaderFilter = styled.div`` +const HeaderText = styled.div` + margin-left: 16px; +` +const RowBody = styled.div<any>` + display: flex; + color: ${Palette.grayscale[5]}; + padding: 0 16px; + margin-top: 4px; +` +const RowBodyColumn = styled.div<any>` + margin-top: 8px; + &:first-child { + ${StyleProps.exactWidth('calc(50% - 70px)')} + margin-right: 88px; + } + &:last-child { + ${StyleProps.exactWidth('calc(50% - 16px)')} + } +` +const RowBodyColumnValue = styled.div<any>` + overflow-wrap: break-word; +` +const MachinesWrapper = styled.div`` +const MachineWrapper = styled.div` + background: ${Palette.grayscale[1]}; + border-radius: ${StyleProps.borderRadius}; +` +const MachineTitle = styled.div` + padding: 16px; + border-bottom: 1px solid #7F8795; + font-size: 16px; +` +const MachineBody = styled.div` + padding: 16px; +` +const MachineRow = styled.div<{ secondary?: boolean }>` + display: flex; + margin-bottom: 8px; + align-items: center; + ${props => (props.secondary ? css` + color: ${Palette.grayscale[5]}; + margin-bottom: 4px; + ` : '')} +` +const ValueLink = styled(Link)` + display: flex; + color: ${Palette.primary}; + text-decoration: none; + cursor: pointer; +` + +type FilterType = 'all' | 'allocated' | 'not-allocated' +type Props = { + item?: MinionPool | null, + replicas: ReplicaItem[] + migrations: MigrationItem[] +} +type State = { + filterStatus: FilterType + openedRows: string[] +} +class MinionPoolMachines extends React.Component<Props, State> { + state = { + filterStatus: 'all' as FilterType, + openedRows: [], + } + + get machines() { + return this.props.item?.minion_machines || [] + } + + get filteredMachines() { + switch (this.state.filterStatus) { + case 'all': + return this.machines + case 'allocated': + return this.machines.filter(m => m.allocation_status === 'ALLOCATED' || m.allocation_status === 'AVAILABLE') + default: + return this.machines.filter(m => m.allocation_status !== 'ALLOCATED' && m.allocation_status !== 'AVAILABLE') + } + } + + handleRowClick(id: string) { + if (this.state.openedRows.find(i => i === id)) { + this.setState(prevState => ({ + openedRows: prevState.openedRows.filter(i => i !== id), + })) + } else { + this.setState(prevState => ({ + openedRows: [...prevState.openedRows, id], + })) + } + } + + renderNoMachines() { + return ( + <NoMachines>There are no Minion Machines allocated to this Minion Pool</NoMachines> + ) + } + + renderHeader() { + const plural = this.machines.length === 1 ? '' : 's' + return ( + <Header> + <HeaderFilter> + <DropdownLink + items={[ + { label: 'All', value: 'all' }, + { label: 'Allocated', value: 'allocated' }, + { label: 'Not Allocated', value: 'not-allocated' }, + ]} + selectedItem={this.state.filterStatus} + onChange={item => { + this.setState({ + filterStatus: item.value as FilterType, + }) + }} + /> + </HeaderFilter> + <HeaderText> + {this.machines.length} minion machine{plural}, {this.machines.filter(m => m.allocation_status === 'ALLOCATED' || m.allocation_status === 'AVAILABLE').length} allocated + </HeaderText> + </Header> + ) + } + + renderConnectionInfo(machine: MinionMachine) { + const isOpened: boolean = Boolean(this.state.openedRows.find(i => i === machine.id)) + + return ( + <Row onClick={() => { this.handleRowClick(machine.id) }}> + <ArrowStyled + primary + orientation={isOpened ? 'up' : 'down'} + opacity={isOpened ? 1 : 0} + thick + /> + <RowHeader> + <RowHeaderColumn> + <HeaderIcon /> + <HeaderName>Connection Info</HeaderName> + </RowHeaderColumn> + </RowHeader> + <Collapse isOpened={isOpened}> + <RowBody> + <RowBodyColumn> + {Object.keys(machine.connection_info).map(prop => ( + <RowBodyColumnValue key={prop}> + {prop}: {machine.connection_info[prop]} + </RowBodyColumnValue> + ))} + </RowBodyColumn> + </RowBody> + </Collapse> + </Row> + ) + } + + renderMachines() { + if (this.filteredMachines.length === 0) { + return ( + <NoMachines>No Minion Machines found</NoMachines> + ) + } + + return ( + <MachinesWrapper> + {this.filteredMachines.map(machine => { + const findTransferItem = (transferItems: TransferItem[]) => transferItems + .find(i => i.id === machine.allocated_action) + const allocatedAction = machine.allocated_action ? ( + findTransferItem(this.props.replicas) || findTransferItem(this.props.migrations) + ) : null + return ( + <MachineWrapper key={machine.id}> + <MachineTitle>ID: {machine.id}</MachineTitle> + <MachineBody> + <MachineRow> + Allocation Status: <StatusPill style={{ marginLeft: '8px' }} status={machine.allocation_status} /> + </MachineRow> + <MachineRow style={{ marginBottom: '16px' }}> + <span style={{ width: '114px' }}>Power Status:</span> <StatusPill style={{ marginLeft: '8px' }} status={machine.power_status} /> + </MachineRow> + <MachineRow secondary>Created At: {moment(machine.created_at).format('YYYY-MM-DD HH:mm:ss')}</MachineRow> + {machine.updated_at ? <MachineRow secondary>Updated At: {moment(machine.updated_at).format('YYYY-MM-DD HH:mm:ss')}</MachineRow> : null} + {machine.last_used_at ? <MachineRow secondary>Last Used At: {moment(machine.last_used_at).format('YYYY-MM-DD HH:mm:ss')}</MachineRow> : null} + {machine.allocated_action ? ( + <MachineRow secondary> + Allocated Action: + {allocatedAction ? ( + <> + <ItemReplicaBadge style={{ margin: '0px 4px 0 5px' }}>{allocatedAction.type === 'replica' ? 'RE' : 'MI'}</ItemReplicaBadge> + <ValueLink + to={`/${allocatedAction.type}s/${allocatedAction.id}`} + > + {allocatedAction.instances[0]} + </ValueLink> + </> + ) : <span> {machine.allocated_action}</span>} + </MachineRow> + ) : null} + </MachineBody> + {machine.connection_info ? this.renderConnectionInfo(machine) : null} + </MachineWrapper> + ) + })} + <GlobalStyle /> + </MachinesWrapper> + ) + } + + render() { + return ( + <Wrapper> + {this.props.item?.minion_machines.length ? this.renderHeader() : this.renderNoMachines()} + {this.props.item?.minion_machines.length ? this.renderMachines() : null} + </Wrapper> + ) + } +} + +export default MinionPoolMachines diff --git a/src/components/organisms/MinionPoolDetailsContent/MinionPoolMainDetails.tsx b/src/components/organisms/MinionPoolDetailsContent/MinionPoolMainDetails.tsx index 8350eee4..14373215 100644 --- a/src/components/organisms/MinionPoolDetailsContent/MinionPoolMainDetails.tsx +++ b/src/components/organisms/MinionPoolDetailsContent/MinionPoolMainDetails.tsx @@ -20,7 +20,6 @@ import styled, { css } from 'styled-components' import EndpointLogos from '../../atoms/EndpointLogos' import CopyValue from '../../atoms/CopyValue' import StatusIcon from '../../atoms/StatusIcon' -import StatusImage from '../../atoms/StatusImage' import CopyMultilineValue from '../../atoms/CopyMultilineValue' import type { Endpoint } from '../../../@types/Endpoint' @@ -33,9 +32,8 @@ import DateUtils from '../../../utils/DateUtils' import LabelDictionary from '../../../utils/LabelDictionary' import { OptionsSchemaPlugin } from '../../../plugins/endpoint' -import { MinionPoolDetails } from '../../../@types/MinionPool' -import StatusPill from '../../atoms/StatusPill/StatusPill' import { TransferItem, ReplicaItem, MigrationItem } from '../../../@types/MainItem' +import { MinionPool } from '../../../@types/MinionPool' const Wrapper = styled.div<any>` display: flex; @@ -81,12 +79,6 @@ const ValueLink = styled(Link)` text-decoration: none; cursor: pointer; ` -const Loading = styled.div<any>` - display: flex; - justify-content: center; - align-items: center; - height: 200px; -` const PropertiesTable = styled.div<any>`` const PropertyRow = styled.div<any>` display: flex; @@ -111,14 +103,13 @@ const PropertyValue = styled.div<any>` ` type Props = { - item?: MinionPoolDetails | null, + item?: MinionPool | null, replicas: ReplicaItem[] migrations: MigrationItem[] schema: FieldType[], schemaLoading: boolean, endpoints: Endpoint[], bottomControls: React.ReactNode, - loading: boolean, } @observer class MinionPoolMainDetails extends React.Component<Props> { @@ -215,22 +206,18 @@ class MinionPoolMainDetails extends React.Component<Props> { renderUsage(items: TransferItem[]) { return items.map(item => ( - <span> + <div> <ValueLink key={item.id} to={`/${item.type}s/${item.id}`} > {item.instances[0]} </ValueLink> - <br /> - </span> + </div> )) } renderTable() { - if (this.props.loading) { - return null - } const endpoint = this.getEndpoint() const lastUpdated = this.renderLastExecutionTime() @@ -268,7 +255,13 @@ class MinionPoolMainDetails extends React.Component<Props> { <Row> <Field> <Label>Pool Platform</Label> - {this.renderValue(this.props.item?.pool_platform || '-', true)} + {this.renderValue(this.props.item?.platform || '-', true)} + </Field> + </Row> + <Row> + <Field> + <Label>Pool OS Type</Label> + {this.renderValue(this.props.item?.os_type || '-', true)} </Field> </Row> <Row> @@ -304,12 +297,6 @@ class MinionPoolMainDetails extends React.Component<Props> { </Column> <Column width="9.5%" /> <Column width="48%" style={{ flexGrow: 1 }}> - <Row> - <Field> - <Label>Last Execution Status</Label> - <Value>{this.props.item?.last_execution_status ? <StatusPill status={this.props.item.last_execution_status} /> : '-'}</Value> - </Field> - </Row> {getPropertyNames().length > 0 ? ( <Row> <Field> @@ -321,36 +308,43 @@ class MinionPoolMainDetails extends React.Component<Props> { </Field> </Row> ) : null} + <Row> + <Field> + <Label>Minimum Minions</Label> + <Value>{this.props.item?.minimum_minions || '1'}</Value> + </Field> + </Row> + <Row> + <Field> + <Label>Maximum Minions</Label> + <Value>{this.props.item?.maximum_minions || '1'}</Value> + </Field> + </Row> + <Row> + <Field> + <Label>Minion Max Idle Time (s)</Label> + <Value>{this.props.item?.minion_max_idle_time || '-'}</Value> + </Field> + </Row> + <Row> + <Field> + <Label>Minion Retention Strategy</Label> + <Value>{this.props.item?.minion_retention_strategy || 'delete'}</Value> + </Field> + </Row> </Column> </ColumnsLayout> ) } renderBottomControls() { - if (this.props.loading) { - return null - } - return this.props.bottomControls } - renderLoading() { - if (!this.props.loading) { - return null - } - - return ( - <Loading> - <StatusImage loading /> - </Loading> - ) - } - render() { return ( <Wrapper> {this.renderTable()} - {this.renderLoading()} {this.renderBottomControls()} </Wrapper> ) diff --git a/src/components/organisms/MinionPoolDetailsContent/images/network.svg b/src/components/organisms/MinionPoolDetailsContent/images/network.svg new file mode 100644 index 00000000..a4bbcf45 --- /dev/null +++ b/src/components/organisms/MinionPoolDetailsContent/images/network.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 52.4 (67378) - http://www.bohemiancoding.com/sketch --> + + <desc>Created with Sketch.</desc> + <g id="Coriolis" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Migration/Details/Overview-Closed" transform="translate(-336.000000, -681.000000)" stroke="#0044CA" stroke-width="1.5"> + <g id="Icon/Network/16-Copy-2" transform="translate(336.000000, 681.000000)"> + <path d="M0.5,8 L14,8" id="Path-5"></path> + <path d="M3,1 L3,2 C3,4.209139 4.790861,6 7,6 L9.96429232,6 L14,6" id="Path-6-Copy" stroke-linejoin="round" transform="translate(8.500000, 3.500000) scale(1, -1) translate(-8.500000, -3.500000) "></path> + <path d="M3,10 L3,11 C3,13.209139 4.790861,15 7,15 L9.96429232,15 L14,15" id="Path-6-Copy-2" stroke-linejoin="round"></path> + </g> + </g> + </g> +</svg> diff --git a/src/components/organisms/MinionPoolModal/MinionPoolModal.tsx b/src/components/organisms/MinionPoolModal/MinionPoolModal.tsx index e96a7c61..3752af88 100644 --- a/src/components/organisms/MinionPoolModal/MinionPoolModal.tsx +++ b/src/components/organisms/MinionPoolModal/MinionPoolModal.tsx @@ -140,7 +140,7 @@ class MinionPoolModal extends React.Component<Props, State> { this.setState(prevState => ({ minionPool: { ...prevState.minionPool, - pool_platform: props.platform, + platform: props.platform, }, })) } @@ -203,40 +203,35 @@ class MinionPoolModal extends React.Component<Props, State> { return } this.setState({ saving: true }) - if (this.state.minionPool?.id) { - await this.update() - } else { - await this.add() + try { + if (this.state.minionPool?.id) { + await this.update() + } else { + await this.add() + } + } catch (err) { + this.setState({ saving: false }) } } async update() { const stateMinionPool = { ...this.state.minionPool } - await minionPoolStore.loadMinionPools() const minionPool = minionPoolStore.minionPools.find(e => e.id === stateMinionPool.id) if (!minionPool) { throw new Error('Minion pool not found!') } - try { - delete stateMinionPool.pool_platform - delete stateMinionPool.endpoint_id - await minionPoolStore.update(stateMinionPool) - if (this.props.onUpdateComplete) { - this.props.onUpdateComplete(`/minion-pools/${stateMinionPool.id}`) - } - } catch (err) { - this.props.onRequestClose() + delete stateMinionPool.platform + delete stateMinionPool.endpoint_id + await minionPoolStore.update(stateMinionPool) + if (this.props.onUpdateComplete) { + this.props.onUpdateComplete(`/minion-pools/${stateMinionPool.id}`) } } async add() { - try { - await minionPoolStore.add(this.props.endpoint.id, this.state.minionPool) - notificationStore.alert('Minion Pool created', 'success') - this.props.onRequestClose() - } catch (err) { - this.props.onRequestClose() - } + await minionPoolStore.add(this.props.endpoint.id, this.state.minionPool) + notificationStore.alert('Minion Pool created', 'success') + this.props.onRequestClose() } fillRequiredDefaults() { @@ -309,6 +304,8 @@ class MinionPoolModal extends React.Component<Props, State> { <Content> <MinionPoolModalContent endpoint={this.props.endpoint} + platform={this.props.platform} + envOptionsDisabled={this.props.minionPool != null && this.props.minionPool.status !== 'DEALLOCATED'} defaultSchema={minionPoolStore.minionPoolDefaultSchema} envSchema={minionPoolStore.minionPoolEnvSchema} invalidFields={this.state.invalidFields} @@ -338,7 +335,7 @@ class MinionPoolModal extends React.Component<Props, State> { return ( <LoadingWrapper> <StatusImage loading /> - <LoadingText>Loading schema ...</LoadingText> + <LoadingText>Loading Pool Options ...</LoadingText> </LoadingWrapper> ) } diff --git a/src/components/organisms/MinionPoolModal/MinionPoolModalContent.tsx b/src/components/organisms/MinionPoolModal/MinionPoolModalContent.tsx index d1bad8e7..ee4c7847 100644 --- a/src/components/organisms/MinionPoolModal/MinionPoolModalContent.tsx +++ b/src/components/organisms/MinionPoolModal/MinionPoolModalContent.tsx @@ -24,6 +24,7 @@ import StyleProps from '../../styleUtils/StyleProps' import Palette from '../../styleUtils/Palette' import EndpointLogos from '../../atoms/EndpointLogos/EndpointLogos' import { Endpoint } from '../../../@types/Endpoint' +import ToggleButtonBar from '../../atoms/ToggleButtonBar/ToggleButtonBar' const Wrapper = styled.div<any>` display: flex; @@ -37,6 +38,9 @@ const Fields = styled.div<any>` flex-direction: column; overflow: auto; ` +const ToggleButtonBarStyled = styled(ToggleButtonBar)` + margin-top: 16px; +` const FieldStyled = styled(FieldInput)` min-width: ${props => (props.useTextArea ? '100%' : '224px')}; max-width: ${StyleProps.inputSizes.large.width}px; @@ -68,6 +72,7 @@ const EndpointFieldLabelText = styled.span` const EndpointFieldValue = styled.div` display: flex; align-items: center; + height: 29px; ` const EndpointFieldValueText = styled.div` overflow: hidden; @@ -76,11 +81,11 @@ const EndpointFieldValueText = styled.div` font-size: 12px; color: ${Palette.grayscale[4]}; ` -const PoolPlatformFieldText = styled.div` +const EndpointFieldValueLabel = styled.div` text-transform: capitalize; ` const EndpointFieldValueLogo = styled.div`` -const Group = styled.div<any>` +const Group = styled.div` display: flex; flex-direction: column; flex-shrink: 0; @@ -90,6 +95,14 @@ const GroupName = styled.div<any>` align-items: center; margin: 32px 0 24px 0; ` +const DisabledMessage = styled.div` + display: flex; + align-items: center; + width: 340px; + margin: 0 auto 32px auto; + text-align: center; + font-size: 13px; +` const GroupNameText = styled.div<any>` margin: 0 32px; font-size: 16px; @@ -105,10 +118,12 @@ const GroupFields = styled.div<any>` flex-direction: column; ` type Props = { + envOptionsDisabled: boolean defaultSchema: Field[], envSchema: Field[], invalidFields: string[], endpoint: Endpoint + platform: 'source' | 'destination' getFieldValue: (field: Field | null | undefined) => any, onFieldChange: (field: Field | null, value: any) => void, disabled: boolean, @@ -118,7 +133,28 @@ type Props = { onCreateClick: () => void onCancelClick: () => void } -class MinionPoolModalContent extends React.Component<Props> { +type State = { + useAdvancedOptions: boolean +} +class MinionPoolModalContent extends React.Component<Props, State> { + state = { + useAdvancedOptions: false, + } + + componentDidUpdate(_: Props, prevState: State) { + if (prevState.useAdvancedOptions !== this.state.useAdvancedOptions) { + this.props.onResizeUpdate(0) + } + } + + filterBySimpleAdvanced(fields: Field[]): Field[] { + if (this.state.useAdvancedOptions) { + return fields + } + const exceptions = ['endpoint_id', 'platform', 'os_type'] + return fields.filter(f => (f.required && f.default == null) || exceptions.indexOf(f.name) > -1) + } + renderEndpoint() { return ( <EndpointField> @@ -142,24 +178,24 @@ class MinionPoolModalContent extends React.Component<Props> { ) } - renderPoolPlatform() { + renderReadOnlyField(field: Field) { return ( <EndpointField> <EndpointFieldLabel> <EndpointFieldLabelText> - Pool Platform + {field.title} </EndpointFieldLabelText> </EndpointFieldLabel> <EndpointFieldValue> - <PoolPlatformFieldText> - {this.props.getFieldValue(this.props.defaultSchema.find(f => f.name === 'pool_platform'))} - </PoolPlatformFieldText> + <EndpointFieldValueLabel> + {this.props.getFieldValue(field)} + </EndpointFieldValueLabel> </EndpointFieldValue> </EndpointField> ) } - renderFieldSet(customFields: Field[]) { + renderFieldSet(customFields: Field[], options?: {disabled?: boolean}) { const rows: JSX.Element[] = [] let lastField: JSX.Element let i = 0 @@ -167,19 +203,19 @@ class MinionPoolModalContent extends React.Component<Props> { let currentField if (field.name === 'endpoint_id') { currentField = this.renderEndpoint() - } else if (field.name === 'pool_platform') { - currentField = this.renderPoolPlatform() + } else if (field.name === 'platform' || (field.name === 'os_type' && this.props.platform === 'source')) { + currentField = this.renderReadOnlyField(field) } else { currentField = ( <FieldStyled {...field} label={field.title || LabelDictionary.get(field.name)} width={StyleProps.inputSizes.large.width} - disabled={this.props.disabled} + disabled={this.props.disabled || options?.disabled} highlight={this.props.invalidFields.findIndex(fn => fn === field.name) > -1} value={this.props.getFieldValue(field)} onChange={value => { this.props.onFieldChange(field, value) }} - nullableBoolean + nullableBoolean={field.nullableBoolean != null ? field.nullableBoolean : true} /> ) } @@ -216,7 +252,7 @@ class MinionPoolModalContent extends React.Component<Props> { <Fields ref={(ref: HTMLElement) => { this.props.scrollableRef(ref) }}> <Group> <GroupFields> - {this.renderFieldSet(this.props.defaultSchema)} + {this.renderFieldSet(this.filterBySimpleAdvanced(this.props.defaultSchema))} </GroupFields> </Group> <Group> @@ -225,17 +261,36 @@ class MinionPoolModalContent extends React.Component<Props> { <GroupNameText>Environment Options</GroupNameText> <GroupNameBar /> </GroupName> + {this.props.envOptionsDisabled ? ( + <DisabledMessage> + The environment options are disabled while the minion pool is not deallocated. + </DisabledMessage> + ) : null} <GroupFields> - {this.renderFieldSet(this.props.envSchema)} + {this.renderFieldSet( + this.filterBySimpleAdvanced(this.props.envSchema), + { disabled: this.props.envOptionsDisabled }, + )} </GroupFields> </Group> </Fields> ) } + renderSimpleAdvancedToggle() { + return ( + <ToggleButtonBarStyled + items={[{ label: 'Simple', value: 'simple' }, { label: 'Advanced', value: 'advanced' }]} + selectedValue={this.state.useAdvancedOptions ? 'advanced' : 'simple'} + onChange={item => { this.setState({ useAdvancedOptions: item.value === 'advanced' }) }} + /> + ) + } + render() { return ( <Wrapper> + {this.renderSimpleAdvancedToggle()} {this.renderFields()} </Wrapper> ) diff --git a/src/components/organisms/MinionPoolModal/images/minion-pool.svg b/src/components/organisms/MinionPoolModal/images/minion-pool.svg index ace32baf..8941cd9e 100644 --- a/src/components/organisms/MinionPoolModal/images/minion-pool.svg +++ b/src/components/organisms/MinionPoolModal/images/minion-pool.svg @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" +<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" @@ -7,59 +7,32 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="minion-pool-empty-list.svg" + sodipodi:docname="minion-pool-modal-2.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - id="svg8" + id="svg4378" version="1.1" viewBox="0 0 25.399999 25.400001" height="96" width="96"> <defs - id="defs2"> - <marker - inkscape:isstock="true" - style="overflow:visible;" - id="Arrow1Mend" - refX="0.0" - refY="0.0" - orient="auto" - inkscape:stockid="Arrow1Mend"> - <path - transform="scale(0.4) rotate(180) translate(10,0)" - style="fill-rule:evenodd;stroke:#f91661;stroke-width:1pt;stroke-opacity:1;fill:#f91661;fill-opacity:1" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - id="path3085" /> - </marker> - <linearGradient - osb:paint="solid" - id="linearGradient2826"> - <stop - id="stop2824" - offset="0" - style="stop-color:#feffff;stop-opacity:1;" /> - </linearGradient> - <linearGradient - osb:paint="solid" - id="linearGradient2804"> - <stop - id="stop2802" - offset="0" - style="stop-color:#feffff;stop-opacity:1;" /> - </linearGradient> + id="defs4372"> + <path + d="M48,96 C74.509668,96 96,74.509668 96,48 C96,21.490332 74.509668,0 48,0 C21.490332,0 0,21.490332 0,48 C0,74.509668 21.490332,96 48,96 Z" + id="path-1" /> </defs> <sodipodi:namedview inkscape:window-maximized="0" - inkscape:window-y="55" - inkscape:window-x="42" - inkscape:window-height="868" - inkscape:window-width="1032" + inkscape:window-y="0" + inkscape:window-x="73" + inkscape:window-height="1040" + inkscape:window-width="1274" units="px" showgrid="false" inkscape:document-rotation="0" inkscape:current-layer="layer1" inkscape:document-units="mm" - inkscape:cy="88.210625" - inkscape:cx="16.023473" + inkscape:cy="70.191481" + inkscape:cx="17.324006" inkscape:zoom="2.8" inkscape:pageshadow="2" inkscape:pageopacity="0.0" @@ -68,7 +41,7 @@ pagecolor="#ffffff" id="base" /> <metadata - id="metadata5"> + id="metadata4375"> <rdf:RDF> <cc:Work rdf:about=""> @@ -84,12 +57,9 @@ inkscape:groupmode="layer" inkscape:label="Layer 1"> <path - style="fill:none;fill-rule:evenodd;stroke:#f91661;stroke-width:0.396875;stroke-linecap:round;stroke-dasharray:0.812599, 1.6255, 2.84456, 1.6255" - d="m 41.444302,-33.90587 c -1.884894,-0.770533 -3.026318,-1.774198 -3.026318,-2.870415 0,-1.06957 1.087078,-2.051097 2.891361,-2.813875 -0.736607,-2.778883 -0.463145,-5.26087 0.991399,-6.741615 0.843592,-0.859273 2.038861,-1.311051 3.462288,-1.311051 0.04207,0 0.08468,5.27e-4 0.127294,0.0011 1.589632,0.02962 3.370966,0.599304 5.147193,1.599436 1.776205,-1.000036 3.557442,-1.569661 5.146719,-1.599281 0.04261,-5.26e-4 0.08522,-0.0011 0.127294,-0.0011 1.423427,0 2.618696,0.451778 3.462288,1.311051 1.479773,1.506429 1.737136,4.049133 0.95208,6.886477 1.606922,0.74035 2.562916,1.665894 2.562916,2.668858 0,1.030501 -1.008771,1.979213 -2.69564,2.729355 0.7522,2.797791 0.483864,5.299341 -0.978836,6.788389 -0.868403,0.884578 -2.108981,1.333192 -3.589582,1.309997 -1.541991,-0.02873 -3.264367,-0.565642 -4.987551,-1.510678 -1.723091,0.944945 -3.445435,1.481842 -4.987402,1.510833 -1.477365,0.03005 -2.720638,-0.425419 -3.589582,-1.309996 -1.4381,-1.464006 -1.721672,-3.906743 -1.015921,-6.647485 z m 15.960981,0.863336 c 1.219029,-0.268082 2.29785,-0.608598 3.187893,-1.004396 -0.225822,-0.839943 -0.543626,-1.706587 -0.948706,-2.58055 -0.586926,1.202586 -1.337385,2.41431 -2.239187,3.584946 z m -12.511027,-7.589544 c -1.372306,0.26584 -2.588069,0.620494 -3.584911,1.041918 0.254945,0.961792 0.630887,1.959148 1.120734,2.962867 0.621565,-1.341034 1.448621,-2.699302 2.464177,-4.004785 z m -3.449954,6.726208 c 0.93273,0.381294 2.047513,0.7055 3.296128,0.955585 0.355771,0.456975 0.734638,0.907463 1.135872,1.34846 1.587854,1.745062 3.374812,3.163067 5.160905,4.142603 1.786055,-0.979473 3.572911,-2.397359 5.160515,-4.142448 0.428301,-0.470693 0.831117,-0.952198 1.207561,-1.440864 -1.903765,0.418666 -4.149485,0.660668 -6.552153,0.660668 -2.220303,0 -4.306522,-0.20666 -6.1127,-0.568419 -0.933538,-1.199099 -1.708049,-2.442863 -2.310351,-3.677008 -0.427766,0.922912 -0.758204,1.83766 -0.985777,2.721423 z m 9.593217,-12.136466 c -1.732824,0.975611 -3.460859,2.360865 -5.001198,4.054001 -0.403562,0.443504 -0.784497,0.896609 -1.142065,1.356257 1.770395,-0.342957 3.801329,-0.538099 5.958874,-0.538099 2.337293,0 4.526062,0.229011 6.395997,0.626865 0.984144,1.278164 1.787879,2.605228 2.395343,3.915832 0.465263,-0.953303 0.827764,-1.900864 1.08143,-2.817663 -0.95172,-0.438482 -2.131771,-0.812001 -3.476773,-1.098169 -0.377397,-0.490146 -0.781323,-0.9731 -1.210884,-1.445179 -1.540258,-1.693045 -3.268061,-3.078242 -5.000724,-4.053845 z" - id="Combined-Shape" /> - <path - d="M 15.75651,5.8275724 V 2.7334477 H 9.6265686 V 5.8275886 Z M 2.1575418,9.0169413 V 21.488827 c 0,0.691189 0.5601197,1.256759 1.244792,1.256759 H 21.980562 c 0.684672,0 1.244792,-0.56557 1.244792,-1.256759 V 9.0169413 c 0,-0.6911908 -0.560303,-1.2567597 -1.244792,-1.2567597 H 3.4023338 c -0.6846723,0 -1.244792,0.5655689 -1.244792,1.2567597 z M 22.650196,5.8275886 c 1.3818,0 2.489676,1.118469 2.489676,2.5134137 V 22.164776 c 0,1.394943 -1.107968,2.513413 -2.489676,2.513413 H 2.7328093 c -1.3817997,0 -2.48965731,-1.11847 -2.48965731,-2.513413 L 0.25558892,8.3410023 c 0,-1.3949447 1.09551228,-2.5134137 2.47722038,-2.5134137 H 7.7121605 V 3.3141751 c 0,-1.3949428 1.1079675,-2.51341364 2.4896755,-2.51341364 h 4.979333 c 1.3818,0 2.489676,1.11847074 2.489676,2.51341364 v 2.5134135 z" - style="opacity:1;fill:none;fill-opacity:1;stroke:#0044ca;stroke-width:0.486304;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.9;stroke-dasharray:0.995704, 1.99178, 3.48554, 1.99178;stroke-dashoffset:0;stroke-opacity:1" - id="path10" /> + id="path867" + style="fill:#0044ca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0459433" + d="m 9.8596476,0.12344215 c -0.1501682,0 -0.267889,0.116254 -0.267889,0.2637676 V 1.3763385 C 9.6200546,1.6313737 9.4145397,1.6236206 9.4145397,1.6236206 5.4717801,3.0019528 2.8393387,6.7320668 2.8533194,10.909066 v 0.704754 c -0.00494,0.160614 -0.030802,0.17368 -0.1607337,0.247281 l -1.0138568,0.428623 c -0.099451,0.04198 -0.1607337,0.130693 -0.1607337,0.239039 v 2.781925 c 0,0.147357 0.117556,0.263767 0.267889,0.263767 H 2.474153 c 0.3038525,0.0065 0.2706112,-0.0073 0.2637412,0.264163 v 1.059192 c 0,0.150179 0.1162583,0.267889 0.2637677,0.267889 h 1.8999513 c 0.2332753,-0.0034 0.2124617,-0.0192 0.2554986,0.194006 0.1743547,0.640225 0.4345452,1.25817 0.7665747,1.83401 0.1369629,0.205676 0.1619997,0.151361 -0.04533,0.31783 l -1.3476879,1.343569 c -0.1051905,0.105655 -0.1053108,0.269184 0,0.375046 l 2.2502677,2.250267 c 0.1036987,0.103249 0.2752341,0.103376 0.379166,0 L 8.50367,22.136856 c 0.1608919,-0.167292 0.1190394,-0.165984 0.3173927,-0.04651 0.5734405,0.331449 1.2109153,0.566373 1.8298883,0.762453 0.215507,0.06425 0.186097,-0.003 0.193685,0.255921 v 1.904071 c 0,0.150685 0.112948,0.263767 0.263768,0.263767 h 3.177578 c 0.150327,0 0.267889,-0.116437 0.267889,-0.263767 V 23.10872 c -0.0076,-0.245775 -0.0192,-0.212935 0.194007,-0.255921 0.641058,-0.174572 1.254352,-0.430173 1.829887,-0.762453 0.188984,-0.120278 0.15136,-0.132643 0.317829,0.04543 l 1.347688,1.343567 c 0.105676,0.105211 0.269168,0.105317 0.375044,0 l 2.250267,-2.250268 c 0.105296,-0.105844 0.10517,-0.269405 0,-0.375045 L 19.52502,19.51046 c -0.188415,-0.152352 -0.170415,-0.110951 -0.04543,-0.317829 0.33157,-0.575045 0.58783,-1.192795 0.762453,-1.83401 0.06843,-0.23219 0.03876,-0.211552 0.255922,-0.194007 h 1.899952 c 0.146992,0 0.267889,-0.121058 0.267889,-0.267888 v -1.059192 c 0.0018,-0.295517 -0.01562,-0.270918 0.264162,-0.263086 h 0.688269 c 0.150833,0 0.263768,-0.113101 0.263768,-0.263768 v -2.78192 c -5.36e-4,-0.109873 -0.06022,-0.197773 -0.16073,-0.239039 -0.0027,-0.0013 -0.0055,-0.0026 -0.0082,-0.0041 v 0.0041 l -1.022106,-0.440987 c -0.166934,-0.05259 -0.148343,-0.03102 -0.156911,-0.24727 V 10.904945 C 22.546898,6.7281312 19.910325,3.039611 15.967512,1.6612603 15.830142,1.5906866 15.772239,1.6190237 15.790281,1.3722435 V 0.38720975 c 0,-0.1508244 -0.113104,-0.2637678 -0.263768,-0.2637676 z m 0.5192924,0.5357781 h 4.615934 c 0.276051,-0.00612 0.270383,-2.644e-4 0.264162,0.2637411 V 9.7715416 c -0.0055,0.2694434 0.02817,0.2662504 -0.264162,0.2637944 h -4.607691 c -0.291583,0.0048 -0.263287,0.002 -0.263767,-0.2637414 l -0.0083,-8.84858025 c -0.0063,-0.3095261 0.07492,-0.2701047 0.263775,-0.2637941 z M 9.2290781,2.2541901 C 9.5249988,2.1913965 9.6016767,2.244188 9.5917133,2.5014459 v 7.7976311 c 0,0.146823 0.12093,0.267889 0.2678889,0.267889 H 15.53473 c 0.147014,0 0.267889,-0.121044 0.267889,-0.267889 V 2.5014723 c -0.0059,-0.2978464 0.07698,-0.3367133 0.363276,-0.2472558 3.543465,1.4057863 5.859429,4.8395038 5.844101,8.6507545 v 1.059192 c 0,0.104011 0.06495,0.196946 0.164865,0.239039 0.0014,-2.3e-5 0.0027,-2.3e-5 0.0042,0 l 1.022099,0.440987 c 0.183646,0.07348 0.160899,0.03519 0.15691,0.24727 v 1.887573 c -0.0065,0.270443 0.01984,0.270616 -0.264164,0.263768 H 2.3092986 C 2.0096318,15.0488 2.0512159,15.06304 2.045531,14.779058 v -1.899977 c 0.00155,-0.211646 -0.037736,-0.143536 0.1566125,-0.247283 l 1.0220997,-0.440986 c 0.00137,-2.3e-5 0.00274,-2.3e-5 0.00411,0 0.099335,-0.04185 0.1607336,-0.130413 0.1607336,-0.23904 V 10.89258 C 3.3770427,7.0860694 5.6916524,3.6604748 9.2290718,2.2541901 Z M 3.5209812,15.574456 h 4.838488 c 0.2617307,-0.0019 0.2305977,-0.02552 0.2637676,0.230803 0.2728913,2.249801 2.3126082,3.850742 4.5623552,3.57735 1.872941,-0.227336 3.346197,-1.704074 3.573229,-3.57735 0.0575,-0.281622 0.03087,-0.240691 0.264162,-0.230803 h 4.846731 c 0.308002,-0.006 0.262778,-0.02399 0.264164,0.264164 l 0.0042,0.531657 c -0.0065,0.274592 0.0574,0.258602 -0.264164,0.264162 h -1.763947 c -0.0027,9.1e-5 -0.0055,9.1e-5 -0.0082,0 -0.130786,-0.003 -0.231849,0.07708 -0.259646,0.206076 -0.185144,0.860786 -0.525438,1.684644 -1.005613,2.423365 -0.07115,0.109305 -0.06001,0.240311 0.02885,0.329709 l 1.269381,1.26526 c 0.211567,0.202194 0.210233,0.152542 0,0.371511 l -1.516664,1.504299 c -0.193837,0.190693 -0.165076,0.201877 -0.371511,0 l -1.26526,-1.26526 c -0.08867,-0.08814 -0.22906,-0.10116 -0.333833,-0.03298 -0.738283,0.479713 -1.558242,0.820412 -2.419243,1.005613 -0.125574,0.02703 -0.209009,0.133551 -0.206076,0.263767 9e-5,0.0027 9e-5,0.0055 0,0.0082 v 1.784553 c 0.01013,0.287122 0.04068,0.258602 -0.264165,0.264164 h -2.117892 c -0.312204,0.0018 -0.26644,0.0032 -0.263768,-0.264164 v -1.801039 c -9.1e-5,-0.0027 -9.1e-5,-0.0055 0,-0.0082 0.0029,-0.127115 -0.08082,-0.231791 -0.210189,-0.259647 -0.861639,-0.185018 -1.6815741,-0.529549 -2.4192591,-1.009737 -0.1051062,-0.06833 -0.2448435,-0.05548 -0.3338311,0.03298 l -1.2652615,1.263879 c -0.2230803,0.173981 -0.1936645,0.168453 -0.370883,0 L 5.2767248,21.22073 c -0.2197525,-0.202321 -0.210092,-0.164929 0,-0.371512 l 1.2693818,-1.265259 c 0.085655,-0.08609 0.095976,-0.223013 0.02885,-0.32971 v -0.0042 C 6.0914722,18.517453 5.7442555,17.70075 5.5528165,16.843837 c -2.39e-5,-0.0014 -2.39e-5,-0.0027 0,-0.0042 -0.027938,-0.129247 -0.1290816,-0.209115 -0.2596463,-0.206077 -0.00274,9.1e-5 -0.00551,9.1e-5 -0.00825,0 h -1.763939 c -0.287147,0.0061 -0.2455603,0.01562 -0.2637677,-0.264162 v -0.531656 c 0.00199,-0.299678 -0.02859,-0.258421 0.2637677,-0.263268 z m 5.8976798,0 h 0.020616 6.520005 c 0.328376,0.01711 0.323614,-0.0122 0.264162,0.305481 -0.314594,1.942649 -2.149408,3.272915 -4.092921,2.958644 C 10.612718,18.593222 9.4167557,17.397234 9.1713787,15.879437 9.1544307,15.568154 9.1292977,15.59537 9.418661,15.574456 Z" + sodipodi:nodetypes="ssccccccsssccssccccccccccccssssccccccccccccssccsscccccccccscscccccccccccsssscccsccccccccccccscccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" /> </g> </svg> diff --git a/src/components/organisms/Navigation/Navigation.tsx b/src/components/organisms/Navigation/Navigation.tsx index d0e16a6d..eaaffbb7 100644 --- a/src/components/organisms/Navigation/Navigation.tsx +++ b/src/components/organisms/Navigation/Navigation.tsx @@ -37,7 +37,7 @@ import projectImage from './images/project-menu.svg' import userImage from './images/user-menu.svg' import logsImage from './images/logs-menu.svg' import dashboardImage from './images/dashboard-menu.svg' -import minionPoolsImage from './images/minion-pools-menu.svg' +import minionPoolsImage from './images/minion-pool-menu.svg' const isCollapsed = (props: any) => props.collapsed || (window.outerWidth <= StyleProps.mobileMaxWidth) diff --git a/src/components/organisms/Navigation/images/minion-pool-menu.svg b/src/components/organisms/Navigation/images/minion-pool-menu.svg new file mode 100644 index 00000000..8dd451db --- /dev/null +++ b/src/components/organisms/Navigation/images/minion-pool-menu.svg @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="minion-pool-menu.svg" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + id="svg2846" + version="1.1" + viewBox="0 0 6.3499999 6.3500002" + height="24" + width="24"> + <defs + id="defs2840" /> + <sodipodi:namedview + inkscape:window-maximized="0" + inkscape:window-y="0" + inkscape:window-x="171" + inkscape:window-height="1040" + inkscape:window-width="1274" + inkscape:snap-global="false" + units="px" + showgrid="false" + inkscape:document-rotation="0" + inkscape:current-layer="layer1" + inkscape:document-units="mm" + inkscape:cy="27.171163" + inkscape:cx="29.130502" + inkscape:zoom="5.6" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" /> + <metadata + id="metadata2843"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:groupmode="layer" + inkscape:label="Layer 1"> + <path + transform="matrix(0.66894872,0,0,0.66894872,-1.0732164,-1.0725631)" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0165542" + sodipodi:type="inkscape:offset" + inkscape:radius="0.069459192" + inkscape:original="M 5.3261719 1.7226562 C 5.2208984 1.7226562 5.1347656 1.8087232 5.1347656 1.9140625 L 5.1347656 2.2714844 C 3.6758668 2.7814944 2.7018583 4.1595931 2.7070312 5.7050781 L 2.7070312 5.9589844 L 2.3417969 6.1132812 C 2.2711829 6.1430922 2.2265625 6.2125643 2.2265625 6.2890625 L 2.2265625 7.2910156 C 2.2265625 7.3962891 2.3106762 7.4804688 2.4160156 7.4804688 L 2.6640625 7.4804688 L 2.6640625 7.8632812 C 2.6640625 7.9685548 2.7501311 8.0546875 2.8554688 8.0546875 L 3.5410156 8.0546875 C 3.6060686 8.2935587 3.7005799 8.5219006 3.8242188 8.7363281 L 3.3398438 9.2207031 C 3.2658028 9.2950691 3.2658028 9.4158064 3.3398438 9.4902344 L 4.1503906 10.300781 C 4.2247536 10.374821 4.3454928 10.374821 4.4199219 10.300781 L 4.9042969 9.8164062 C 5.1182075 9.9400453 5.3476492 10.036316 5.5859375 10.101562 L 5.5859375 10.785156 C 5.5859375 10.890431 5.6720056 10.976562 5.7773438 10.976562 L 6.921875 10.976562 C 7.0272135 10.976562 7.1113281 10.890429 7.1113281 10.785156 L 7.1113281 10.101562 C 7.3501992 10.03651 7.5804294 9.9402393 7.7949219 9.8164062 L 8.2792969 10.300781 C 8.3536629 10.374821 8.4743992 10.374821 8.5488281 10.300781 L 9.359375 9.4902344 C 9.433416 9.4158064 9.433416 9.2950677 9.359375 9.2207031 L 8.875 8.7363281 C 8.9986386 8.5219006 9.0951034 8.2935587 9.1601562 8.0546875 L 9.84375 8.0546875 C 9.9490892 8.0546875 10.035156 7.9685545 10.035156 7.8632812 L 10.035156 7.4804688 L 10.283203 7.4804688 C 10.388542 7.4804688 10.474609 7.3962896 10.474609 7.2910156 L 10.474609 6.2890625 C 10.474233 6.2119795 10.426663 6.1425099 10.355469 6.1132812 L 9.9882812 5.9550781 L 9.9882812 5.703125 C 9.9934673 4.1576409 9.0174931 2.7795412 7.5585938 2.2695312 L 7.5585938 1.9140625 C 7.5585938 1.8087239 7.4724607 1.7226563 7.3671875 1.7226562 L 5.3261719 1.7226562 z M 5.5136719 2.1074219 L 7.1777344 2.1074219 L 7.1777344 5.2949219 L 5.5175781 5.2949219 L 5.5136719 2.1074219 z M 5.1347656 2.6757812 L 5.1347656 5.4863281 C 5.1347656 5.5916024 5.2208377 5.6757812 5.3261719 5.6757812 L 7.3710938 5.6757812 C 7.4764332 5.6757812 7.5625 5.5916021 7.5625 5.4863281 L 7.5625 2.6757812 C 8.802897 3.1678795 9.6147421 4.3688991 9.609375 5.703125 L 9.609375 6.0839844 C 9.609375 6.1604874 9.6559521 6.2300198 9.7265625 6.2597656 L 10.09375 6.4179688 L 10.09375 7.0996094 L 2.6054688 7.0996094 L 2.6054688 6.4140625 L 2.9746094 6.2558594 C 3.0452234 6.2261094 3.0898437 6.1566403 3.0898438 6.0800781 L 3.0898438 5.6992188 C 3.0856239 4.3666747 3.8964378 3.1680729 5.1347656 2.6757812 z M 3.0429688 7.4804688 L 4.7871094 7.4804688 C 4.8916718 8.3425129 5.6750652 8.9582721 6.5371094 8.8535156 C 7.2552744 8.7663488 7.8211647 8.1986338 7.9082031 7.4804688 L 9.6542969 7.4804688 L 9.65625 7.671875 L 9.0195312 7.671875 C 8.9279663 7.669765 8.8493479 7.7328378 8.8300781 7.8222656 C 8.7658041 8.1210809 8.6470448 8.4058428 8.4804688 8.6621094 C 8.4313867 8.7375044 8.4425524 8.8366952 8.5058594 8.9003906 L 8.9609375 9.3574219 L 8.4160156 9.8984375 L 7.9589844 9.4433594 C 7.8952907 9.3800526 7.7961025 9.3688864 7.7207031 9.4179688 C 7.4644382 9.5844805 7.1796755 9.7033025 6.8808594 9.7675781 C 6.7913647 9.7868488 6.7284001 9.8674197 6.7304688 9.9589844 L 6.7304688 10.601562 L 5.9667969 10.601562 L 5.9667969 9.953125 C 5.9689069 9.8615604 5.9058984 9.7809887 5.8164062 9.7617188 C 5.5176552 9.6975679 5.2327632 9.5788796 4.9765625 9.4121094 C 4.9011586 9.3630973 4.8020405 9.3741229 4.7382812 9.4375 L 4.2832031 9.8925781 L 3.7421875 9.3535156 L 4.1992188 8.8964844 C 4.2625898 8.8327907 4.271671 8.7336008 4.2226562 8.6582031 C 4.0545305 8.403482 3.9356804 8.1201112 3.8691406 7.8222656 C 3.8498064 7.7328343 3.7692995 7.6697414 3.6777344 7.671875 L 3.0429688 7.671875 L 3.0429688 7.4804688 z M 5.1757812 7.4804688 L 7.5234375 7.4804688 C 7.4184867 8.1295721 6.8072414 8.5717477 6.1582031 8.4667969 C 5.6523155 8.3850124 5.2581891 7.986139 5.1757812 7.4804688 z " + xlink:href="#path867" + id="path1461" + inkscape:href="#path867" + d="m 5.3261719,1.6523438 c -0.1431314,0 -0.2617188,0.1185163 -0.2617188,0.2617187 v 0.3125 C 3.6051295,2.7597755 2.631506,4.1476744 2.6367188,5.7050781 V 5.9121094 L 2.3144531,6.0488281 C 2.2178762,6.0895999 2.15625,6.1855545 2.15625,6.2890625 v 1.0019531 c 0,0.1422722 0.1174217,0.2597656 0.2597656,0.2597656 H 2.59375 v 0.3125 C 2.59375,8.0064134 2.7122674,8.125 2.8554688,8.125 H 3.4921875 C 3.552925,8.3327747 3.6355631,8.5330691 3.7402344,8.7226562 L 3.2910156,9.171875 c -0.1005628,0.1010042 -0.1005295,0.2661325 0,0.3671875 l 0.8105469,0.8105465 c 0.1010024,0.100564 0.266131,0.100529 0.3671875,0 L 4.9179688,9.9003906 C 5.1072301,10.005237 5.3079867,10.089258 5.515625,10.150391 v 0.634765 c 0,0.143133 0.1185178,0.261719 0.2617188,0.261719 H 6.921875 c 0.143202,0 0.2597656,-0.119447 0.2597656,-0.261719 V 10.150391 C 7.3898931,10.08938 7.5913775,10.005446 7.78125,9.9003906 l 0.4492188,0.4492184 c 0.1010041,0.100562 0.2661318,0.100528 0.3671874,0 L 9.4082031,9.5390625 c 0.1005299,-0.1010553 0.1005639,-0.2661841 0,-0.3671875 L 8.9589844,8.7226562 C 9.0637834,8.5332501 9.1479431,8.333048 9.2089844,8.125 H 9.84375 c 0.1432025,0 0.261719,-0.1185876 0.261719,-0.2617188 v -0.3125 h 0.177734 c 0.142344,0 0.261719,-0.1166339 0.261719,-0.2597656 V 6.2890625 C 10.544407,6.1836073 10.47972,6.0886137 10.382812,6.0488281 L 10.058594,5.9101562 V 5.703125 C 10.06382,4.1456636 9.0882555,2.7578151 7.6289062,2.2246094 V 1.9140625 c 0,-0.1432021 -0.1185873,-0.2617186 -0.2617187,-0.2617187 z m 0.2578125,0.5253906 h 1.5234375 v 3.046875 H 5.5878906 Z m -0.5195313,0.609375 v 2.6992187 c 0,0.1431341 0.1193786,0.2597656 0.2617188,0.2597657 h 2.0449219 c 0.1423436,0 0.2617187,-0.1166341 0.2617187,-0.2597657 V 2.7871094 c 1.1579711,0.5050512 1.9113587,1.646015 1.90625,2.9160156 v 0.3808594 c 0,0.1046954 0.063928,0.1996966 0.1601563,0.2402344 L 10.023438,6.4628906 V 7.0292969 H 2.6757812 V 6.4589844 L 3.0019531,6.3203125 C 3.0985724,6.2796064 3.1601561,6.1836274 3.1601562,6.0800781 V 5.6992188 C 3.1561399,4.430949 3.9085857,3.2922944 5.0644531,2.7871094 Z M 3.1132812,7.5507812 h 1.6191407 c 0.1394508,0.8653435 0.9347494,1.477759 1.8125,1.3710938 0.7281757,-0.088382 1.3021665,-0.6520113 1.4179687,-1.3710938 h 1.6230469 v 0.050781 H 9.0214844 9.0195312 C 8.8960018,7.5996544 8.7876293,7.6883473 8.7617188,7.8085938 8.6992359,8.0990818 8.5838585,8.3757987 8.421875,8.625 8.3549682,8.7277755 8.3711863,8.8628472 8.4570312,8.9492188 l 0.40625,0.4082031 -0.4472656,0.4433593 -0.4082031,-0.40625 C 7.921443,9.3086864 7.7863717,9.2924701 7.6835938,9.359375 7.4343825,9.5213035 7.1576675,9.6367362 6.8671875,9.6992188 6.7452981,9.7254649 6.6573479,9.8366348 6.6601562,9.9609375 V 10.53125 H 6.0371094 V 9.9550781 9.953125 C 6.0390362,9.8295795 5.9513256,9.719467 5.8300781,9.6933594 5.5396588,9.6309975 5.2627698,9.5156624 5.0136719,9.3535156 4.9109569,9.2867515 4.7758826,9.3027605 4.6894531,9.3886719 l -0.40625,0.40625 L 3.8398438,9.3535156 4.2480469,8.9453125 C 4.3345082,8.8584109 4.3475741,8.7231178 4.28125,8.6210938 a 0.06946614,0.06946614 0 0 0 0,-0.00195 C 4.1178805,8.3716255 4.0022465,8.0964592 3.9375,7.8066406 3.9111764,7.6848796 3.8001755,7.5986639 3.6757812,7.6015625 h -0.5625 z m 2.1582032,0 H 7.4335938 C 7.2980097,8.1163956 6.753423,8.4927908 6.1699219,8.3984375 5.7226459,8.3261285 5.3773985,7.9860294 5.2714844,7.5507812 Z" /> + </g> +</svg> diff --git a/src/components/organisms/Navigation/images/minion-pools-menu.svg b/src/components/organisms/Navigation/images/minion-pools-menu.svg deleted file mode 100644 index 9f1325ae..00000000 --- a/src/components/organisms/Navigation/images/minion-pools-menu.svg +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 6.3499999 6.3500002" - height="24" - width="24"> - <g - transform="scale(0.89) translate(0.5 0.2)" - > - <path - id="path833" - style="fill:#feffff;fill-opacity:1;stroke-width:0.3175" - d="M 3.9567384,1.4287455 V 0.64703203 H 2.3932744 V 1.42875 Z M 0.48827166,2.234522 v 3.1509535 c 0,0.1746256 0.1428877,0.3175127 0.31751267,0.3175127 H 5.5442279 c 0.174625,0 0.3175127,-0.1428871 0.3175127,-0.3175127 V 2.234522 c 0,-0.1746251 -0.1428877,-0.3175128 -0.3175127,-0.3175128 H 0.80578433 c -0.17462497,0 -0.31751267,0.1428877 -0.31751267,0.3175128 z M 5.715,1.42875 c 0.352425,0 0.635,0.282575 0.635,0.635 v 3.4925001 c 0,0.3524247 -0.282575,0.635 -0.635,0.635 H 0.63500003 C 0.28257503,6.1912501 0,5.9086748 0,5.5562501 L 0.00317498,2.06375 c 0,-0.352425 0.27940005,-0.635 0.63182505,-0.635 H 1.9050001 V 0.79375013 C 1.9050001,0.44132511 2.1875751,0.15875 2.5400002,0.15875 H 3.8100003 C 4.162425,0.15875 4.445,0.44132511 4.445,0.79375013 V 1.42875 Z" /> - </g> -</svg> diff --git a/src/components/organisms/PageHeader/PageHeader.tsx b/src/components/organisms/PageHeader/PageHeader.tsx index 42e5a0db..54f0e1eb 100644 --- a/src/components/organisms/PageHeader/PageHeader.tsx +++ b/src/components/organisms/PageHeader/PageHeader.tsx @@ -41,6 +41,7 @@ import StyleProps from '../../styleUtils/StyleProps' import { ProviderTypes } from '../../../@types/Providers' import MinionEndpointModal from '../MinionEndpointModal/MinionEndpointModal' import MinionPoolModal from '../MinionPoolModal' +import ObjectUtils from '../../../utils/ObjectUtils' const Wrapper = styled.div<any>` display: flex; @@ -385,7 +386,7 @@ class PageHeader extends React.Component<Props, State> { {this.state.showMinionPoolModal ? ( <Modal isOpen - title="New Minion Pool" + title={`New ${ObjectUtils.capitalizeFirstLetter(this.state.selectedMinionPoolPlatform)} Minion Pool`} onRequestClose={() => { this.handleCloseMinionPoolModalRequest() }} > <MinionPoolModal diff --git a/src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.tsx b/src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.tsx index 0974fcc1..29c100af 100644 --- a/src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.tsx +++ b/src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.tsx @@ -204,7 +204,7 @@ class ReplicaMigrationOptions extends React.Component<Props, State> { label: instance.name, type: 'string', enum: minionPools.map(minionPool => ({ - name: minionPool.pool_name, + name: minionPool.name, id: minionPool.id, })), })) diff --git a/src/components/organisms/WizardOptions/WizardOptions.tsx b/src/components/organisms/WizardOptions/WizardOptions.tsx index ef986ea2..33d4744f 100644 --- a/src/components/organisms/WizardOptions/WizardOptions.tsx +++ b/src/components/organisms/WizardOptions/WizardOptions.tsx @@ -34,6 +34,7 @@ import Palette from '../../styleUtils/Palette' import endpointImage from './images/endpoint.svg' import { MinionPool } from '../../../@types/MinionPool' +import { MinionPoolStoreUtils } from '../../../stores/MinionPoolStore' export const INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS = 'instance_osmorphing_minion_pool_mappings' @@ -195,32 +196,34 @@ class WizardOptions extends React.Component<Props> { getDefaultSimpleFieldsSchema() { let fieldsSchema: Field[] = [] - if (this.props.wizardType === 'migration' || this.props.wizardType === 'replica') { - fieldsSchema.push({ name: 'description', type: 'string' }) - } - - if (this.props.wizardType === 'migration' || this.props.wizardType === 'migration-destination-options-edit') { - fieldsSchema.unshift({ name: 'skip_os_morphing', type: 'boolean', default: false }) + if (this.props.minionPools.length) { + fieldsSchema.push({ + name: 'minion_pool_id', + label: `${this.props.isSource ? 'Source' : 'Target'} Minion Pool`, + type: 'string', + enum: this.props.minionPools.map(minionPool => ({ + label: minionPool.name, + value: minionPool.id, + disabled: !MinionPoolStoreUtils.isActive(minionPool), + subtitleLabel: !MinionPoolStoreUtils.isActive(minionPool) ? `Pool is in ${minionPool.status} status instead of being ALLOCATED.` : '', + })), + }) } if (this.props.showSeparatePerVm) { const dictionaryLabel = LabelDictionary.get('separate_vm') const label = this.props.wizardType === 'migration' ? dictionaryLabel : dictionaryLabel.replace('Migration', 'Replica') - fieldsSchema.unshift({ + fieldsSchema.push({ name: 'separate_vm', label, type: 'boolean', default: true, }) } - if (this.props.minionPools.length) { - fieldsSchema.push({ - name: 'minion_pool_id', - label: 'Minion Pool', - type: 'string', - enum: this.props.minionPools.map(minionPool => ({ - label: minionPool.pool_name, - value: minionPool.id, - })), - }) + if (this.props.wizardType === 'migration' || this.props.wizardType === 'migration-destination-options-edit') { + fieldsSchema.push({ name: 'skip_os_morphing', type: 'boolean', default: false }) + } + + if (this.props.wizardType === 'migration' || this.props.wizardType === 'replica') { + fieldsSchema.push({ name: 'description', type: 'string' }) } if (this.props.wizardType === 'replica') { @@ -250,7 +253,7 @@ class WizardOptions extends React.Component<Props> { label: instance.name, type: 'string', enum: this.props.minionPools.map(minionPool => ({ - name: minionPool.pool_name, + name: minionPool.name, id: minionPool.id, })), })) diff --git a/src/components/organisms/WizardPageContent/WizardPageContent.tsx b/src/components/organisms/WizardPageContent/WizardPageContent.tsx index 59882ab4..d16f9fa5 100644 --- a/src/components/organisms/WizardPageContent/WizardPageContent.tsx +++ b/src/components/organisms/WizardPageContent/WizardPageContent.tsx @@ -427,7 +427,7 @@ class WizardPageContent extends React.Component<Props, State> { || this.props.providerStore.sourceOptionsPrimaryLoading || this.props.minionPoolStore.loadingMinionPools} minionPools={this.props.minionPoolStore.minionPools - .filter(m => m.pool_platform === 'source' && m.endpoint_id === this.props.wizardData.source?.id)} + .filter(m => m.platform === 'source' && m.endpoint_id === this.props.wizardData.source?.id)} optionsLoading={this.props.providerStore.sourceOptionsSecondaryLoading} optionsLoadingSkipFields={getOptionsLoadingSkipFields('source')} fields={this.props.providerStore.sourceSchema} @@ -449,7 +449,7 @@ class WizardPageContent extends React.Component<Props, State> { || this.props.providerStore.destinationOptionsPrimaryLoading || this.props.minionPoolStore.loadingMinionPools} minionPools={this.props.minionPoolStore.minionPools - .filter(m => m.pool_platform === 'destination' && m.endpoint_id === this.props.wizardData.target?.id)} + .filter(m => m.platform === 'destination' && m.endpoint_id === this.props.wizardData.target?.id)} optionsLoading={this.props.providerStore.destinationOptionsSecondaryLoading} optionsLoadingSkipFields={[ ...getOptionsLoadingSkipFields('destination'), 'description', 'execute_now', diff --git a/src/components/organisms/WizardSummary/WizardSummary.tsx b/src/components/organisms/WizardSummary/WizardSummary.tsx index 6591d8f3..770a6f36 100644 --- a/src/components/organisms/WizardSummary/WizardSummary.tsx +++ b/src/components/organisms/WizardSummary/WizardSummary.tsx @@ -367,7 +367,7 @@ class WizardSummary extends React.Component<Props> { const getMinionPoolName = (id: string) => { const minionPool = this.props.minionPools.find(m => m.id === id) - return minionPool?.pool_name || id + return minionPool?.name || id } return ( diff --git a/src/components/pages/MigrationDetailsPage/MigrationDetailsPage.tsx b/src/components/pages/MigrationDetailsPage/MigrationDetailsPage.tsx index 58dd3afc..8cec2fa3 100644 --- a/src/components/pages/MigrationDetailsPage/MigrationDetailsPage.tsx +++ b/src/components/pages/MigrationDetailsPage/MigrationDetailsPage.tsx @@ -319,7 +319,7 @@ class MigrationDetailsPage extends React.Component<Props, State> { const dropdownActions = [ { label: 'Cancel', - disabled: this.getStatus() !== 'RUNNING', + disabled: this.getStatus() !== 'RUNNING' && this.getStatus() !== 'AWAITING_MINION_ALLOCATIONS', hidden: this.getStatus() === 'CANCELLING', action: () => { this.handleCancelMigrationClick() }, }, diff --git a/src/components/pages/MigrationsPage/MigrationsPage.tsx b/src/components/pages/MigrationsPage/MigrationsPage.tsx index 63d6378e..0282ee59 100644 --- a/src/components/pages/MigrationsPage/MigrationsPage.tsx +++ b/src/components/pages/MigrationsPage/MigrationsPage.tsx @@ -127,7 +127,8 @@ class MigrationsPage extends React.Component<{ history: any }, State> { cancelSelectedMigrations() { this.state.selectedMigrations.forEach(migration => { - if (this.getStatus(migration.id) === 'RUNNING') { + const status = this.getStatus(migration.id) + if (status === 'RUNNING' || status === 'AWAITING_MINION_ALLOCATIONS') { migrationStore.cancel(migration.id) } }) @@ -213,7 +214,8 @@ class MigrationsPage extends React.Component<{ history: any }, State> { render() { let atLeaseOneIsRunning = false this.state.selectedMigrations.forEach(migration => { - atLeaseOneIsRunning = atLeaseOneIsRunning || this.getStatus(migration.id) === 'RUNNING' + const status = this.getStatus(migration.id) + atLeaseOneIsRunning = atLeaseOneIsRunning || status === 'RUNNING' || status === 'AWAITING_MINION_ALLOCATIONS' }) const BulkActions = [ { diff --git a/src/components/pages/MinionPoolDetailsPage/MinionPoolDetailsPage.tsx b/src/components/pages/MinionPoolDetailsPage/MinionPoolDetailsPage.tsx index 1d425c64..f4293367 100644 --- a/src/components/pages/MinionPoolDetailsPage/MinionPoolDetailsPage.tsx +++ b/src/components/pages/MinionPoolDetailsPage/MinionPoolDetailsPage.tsx @@ -22,7 +22,6 @@ import DetailsContentHeader from '../../organisms/DetailsContentHeader/DetailsCo import Modal from '../../molecules/Modal/Modal' import AlertModal from '../../organisms/AlertModal/AlertModal' -import type { Execution } from '../../../@types/Execution' import type { Action as DropdownAction } from '../../molecules/ActionDropdown/ActionDropdown' import userStore from '../../../stores/UserStore' @@ -33,12 +32,12 @@ import configLoader from '../../../utils/Config' import minionPoolImage from './images/minion-pool.svg' import Palette from '../../styleUtils/Palette' -import ObjectUtils from '../../../utils/ObjectUtils' -import minionPoolStore, { MinionPoolAction } from '../../../stores/MinionPoolStore' +import minionPoolStore from '../../../stores/MinionPoolStore' import MinionPoolModal from '../../organisms/MinionPoolModal/MinionPoolModal' import MinionPoolDetailsContent from '../../organisms/MinionPoolDetailsContent/MinionPoolDetailsContent' import replicaStore from '../../../stores/ReplicaStore' import migrationStore from '../../../stores/MigrationStore' +import MinionPoolConfirmationModal from '../../molecules/MinionPoolConfirmationModal/MinionPoolConfirmationModal' const Wrapper = styled.div<any>`` @@ -49,10 +48,8 @@ type Props = { type State = { showEditModal: boolean, showDeleteMinionPoolConfirmation: boolean, - showCancelConfirmation: boolean - forceCancel: boolean, - confirmationExecution: Execution | null, pausePolling: boolean, + showDeallocateConfirmation: boolean } @observer @@ -60,10 +57,8 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { state: State = { showEditModal: false, showDeleteMinionPoolConfirmation: false, - confirmationExecution: null, - showCancelConfirmation: false, pausePolling: false, - forceCancel: false, + showDeallocateConfirmation: false, } stopPolling: boolean | null = null @@ -83,8 +78,8 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { } componentWillUnmount() { - this.stopPolling = true minionPoolStore.clearMinionPoolDetails() + this.stopPolling = true } get minionPoolId() { @@ -94,29 +89,42 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { return this.props.match.params.id } + get minionPool() { + return minionPoolStore.minionPoolDetails + } + getStatus() { - return minionPoolStore.minionPoolDetails?.pool_status + return this.minionPool?.status } async loadMinionPool(minionPoolId?: string) { + const usableId = minionPoolId || this.minionPoolId await Promise.all([ endpointStore.getEndpoints({ showLoading: true }), - minionPoolStore - .loadMinionPoolDetails(minionPoolId || this.minionPoolId, { showLoading: true }), + minionPoolStore.loadMinionPoolDetails(this.minionPoolId, { showLoading: true }), + replicaStore.getReplicas(), + migrationStore.getMigrations(), ]) + const minionPool = this.minionPool + if (!minionPool) { + notificationStore.alert(`Minion pool with ID '${usableId}' was not found`, 'error') + return + } + const endpoint = endpointStore.endpoints - .find(e => e.id === minionPoolStore.minionPoolDetails?.endpoint_id) + .find(e => e.id === minionPool.endpoint_id) if (!endpoint) { + notificationStore.alert('The endpoint associated to this minion pool was not found', 'error') return } await minionPoolStore.loadMinionPoolSchema( endpoint.type, - minionPoolStore.minionPoolDetails!.pool_platform, + minionPool.platform, ) await minionPoolStore.loadEnvOptions( endpoint.id, endpoint.type, - minionPoolStore.minionPoolDetails!.pool_platform, + minionPool.platform, { useCache: true }, ) } @@ -130,27 +138,14 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { } } - handleCancelExecutionConfirmation() { - if (!minionPoolStore.minionPoolDetails) { - return - } - minionPoolStore.cancelExecution( - minionPoolStore.minionPoolDetails.id, - this.state.forceCancel, - this.state.confirmationExecution?.id, - ) - - this.handleCloseCancelConfirmation() - } - handleDeleteMinionPoolClick() { this.setState({ showDeleteMinionPoolConfirmation: true }) } handleDeleteMinionPool() { this.setState({ showDeleteMinionPoolConfirmation: false }) - this.props.history.push('/replicas') - minionPoolStore.deleteMinionPool(minionPoolStore.minionPoolDetails!.id) + this.props.history.push('/minion-pools') + minionPoolStore.deleteMinionPool(this.minionPool!.id) } handleCloseDeleteMinionPoolConfirmation() { @@ -161,20 +156,6 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { this.setState({ showEditModal: true, pausePolling: true }) } - handleCancelExecution(confirmationExecution: Execution | null, force?: boolean) { - this.setState({ - showCancelConfirmation: true, - confirmationExecution, - forceCancel: force || false, - }) - } - - handleCloseCancelConfirmation() { - this.setState({ - showCancelConfirmation: false, - }) - } - async pollData(showLoading: boolean) { if (this.state.pausePolling || this.stopPolling) { return @@ -182,16 +163,11 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { await Promise.all([ minionPoolStore.loadMinionPoolDetails(this.minionPoolId, { - showLoading, skipLog: true, + showLoading, + skipLog: true, }), - (async () => { - if (window.location.pathname.indexOf('executions') > -1) { - await minionPoolStore.loadExecutionTasks({ - minionPoolId: this.minionPoolId, - skipLog: true, - }) - } - })(), + replicaStore.getReplicas(), + migrationStore.getMigrations(), ]) setTimeout(() => { this.pollData(false) }, configLoader.config.requestPollTimeout) @@ -208,44 +184,41 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { this.closeEditModal() } - async handleExecutionChange(executionId: string) { - await ObjectUtils.waitFor(() => Boolean(minionPoolStore.minionPoolDetails)) - if (!minionPoolStore.minionPoolDetails?.id) { + async handleAllocate() { + if (!this.minionPool) { return } - minionPoolStore.loadExecutionTasks( - { - minionPoolId: minionPoolStore.minionPoolDetails.id, - executionId, - }, - ) + notificationStore.alert('Allocating minion pool...') + await minionPoolStore.runAction(this.minionPool.id, 'allocate') + await minionPoolStore.loadMinionPoolDetails(this.minionPool.id) + } + + handleDeallocate() { + this.setState({ + showDeallocateConfirmation: true, + }) } - async handleAction(action: MinionPoolAction) { - const runAction = async (message: string) => { - if (!minionPoolStore.minionPoolDetails) { - return - } - notificationStore.alert(message) - await minionPoolStore.runAction(minionPoolStore.minionPoolDetails.id, action) - await minionPoolStore.loadMinionPoolDetails(minionPoolStore.minionPoolDetails.id) + async handleRefresh() { + if (!this.minionPool) { + return } + notificationStore.alert('Refreshing minion pool...') + await minionPoolStore.runAction(this.minionPool.id, 'refresh') + await minionPoolStore.loadMinionPoolDetails(this.minionPool.id) + this.props.history.push(`/minion-pools/${this.minionPool.id}/machines`) + } - switch (action) { - case 'set-up-shared-resources': - runAction('Setting up shared resources...') - break - case 'tear-down-shared-resources': - runAction('Tearing up shared resources...') - break - case 'allocate-machines': - runAction('Allocating machines...') - break - case 'deallocate-machines': - runAction('Deallocating machines...') - break - default: + async handleDeallocateConfirmation(force: boolean) { + this.setState({ + showDeallocateConfirmation: false, + }) + if (!this.minionPool) { + return } + notificationStore.alert('Deallocating minion pool...') + await minionPoolStore.runAction(this.minionPool.id, 'deallocate', { force }) + await minionPoolStore.loadMinionPoolDetails(this.minionPool.id) } renderEditMinionPool() { @@ -253,7 +226,7 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { return null } const endpoint = endpointStore.endpoints - .find(e => e.id === minionPoolStore.minionPoolDetails?.endpoint_id) + .find(e => e.id === this.minionPool?.endpoint_id) if (!endpoint) { return null } @@ -268,8 +241,8 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { endpoint={endpoint} onCancelClick={() => { this.closeEditModal() }} onRequestClose={() => { this.closeEditModal() }} - minionPool={minionPoolStore.minionPoolDetails} - platform={minionPoolStore.minionPoolDetails?.pool_platform || 'source'} + minionPool={this.minionPool} + platform={this.minionPool?.platform || 'source'} onUpdateComplete={r => { this.handleUpdateComplete(r) }} /> </Modal> @@ -277,63 +250,40 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { } render() { - const uninitialized = minionPoolStore.minionPoolDetails?.pool_status === 'UNINITIALIZED' - const deallocated = minionPoolStore.minionPoolDetails?.pool_status === 'DEALLOCATED' - const allocated = minionPoolStore.minionPoolDetails?.pool_status === 'ALLOCATED' - const isRunning = minionPoolStore.minionPoolDetails?.pool_status?.indexOf('ING') === ((minionPoolStore.minionPoolDetails?.pool_status?.length || -100) - 3) + const status = this.minionPool?.status + const deallocated = status === 'DEALLOCATED' + const allocated = status === 'ALLOCATED' + const error = status === 'ERROR' const dropdownActions: DropdownAction[] = [ { label: 'Edit', action: () => { this.handleMinionPoolEditClick() }, - disabled: !uninitialized, - title: !uninitialized ? 'The minion pool should be uninitialized' : '', - }, - { - label: 'Setup shared resources', - color: Palette.primary, - action: () => { - this.handleAction('set-up-shared-resources') - }, - disabled: !uninitialized, - title: !uninitialized ? 'The minion pool should be uninitialized' : '', - }, - { - label: 'Tear down shared resources', - action: () => { - this.handleAction('tear-down-shared-resources') - }, disabled: !deallocated, title: !deallocated ? 'The minion pool should be deallocated' : '', }, { - label: 'Allocate Machines', + label: 'Allocate', color: Palette.primary, - action: () => { - this.handleAction('allocate-machines') - }, + action: () => { this.handleAllocate() }, disabled: !deallocated, title: !deallocated ? 'The minion pool should be deallocated' : '', }, { - label: 'Deallocate Machines', + label: 'Deallocate', action: () => { - this.handleAction('deallocate-machines') + this.handleDeallocate() }, - disabled: !allocated, - title: !allocated ? 'The minion pool should be allocated' : '', + disabled: !allocated && !error, + title: !allocated && !error ? 'The minion pool should be allocated' : '', }, { - label: 'Cancel Execution', + label: 'Refresh', action: () => { - this.setState({ - showCancelConfirmation: true, - confirmationExecution: null, - forceCancel: false, - }) + this.handleRefresh() }, - disabled: !isRunning, - title: !isRunning ? 'The minion pool do not have an active execution' : '', + disabled: !allocated, + title: !allocated ? 'The minion pool should be allocated' : '', }, { label: 'Delete Minion Pool', @@ -341,8 +291,8 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { action: () => { this.setState({ showDeleteMinionPoolConfirmation: true }) }, - disabled: !uninitialized, - title: !uninitialized ? 'The minion pool should be uninitialized' : '', + disabled: !deallocated && !error, + title: (!deallocated && !error) ? 'The minion pool should be deallocated' : '', }, ] @@ -357,8 +307,8 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { )} contentHeaderComponent={( <DetailsContentHeader - statusPill={minionPoolStore.minionPoolDetails?.pool_status} - itemTitle={minionPoolStore.minionPoolDetails?.pool_name} + statusPill={this.minionPool?.status} + itemTitle={this.minionPool?.name} itemType="minion pool" dropdownActions={dropdownActions} largeDropdownActionItems @@ -368,28 +318,22 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { )} contentComponent={( <MinionPoolDetailsContent - item={minionPoolStore.minionPoolDetails} + item={this.minionPool} + itemId={this.minionPoolId} replicas={replicaStore.replicas - .filter(r => r.origin_minion_pool_id === minionPoolStore.minionPoolDetails?.id - || r.destination_minion_pool_id === minionPoolStore.minionPoolDetails?.id)} + .filter(r => r.origin_minion_pool_id === this.minionPool?.id + || r.destination_minion_pool_id === this.minionPool?.id)} migrations={migrationStore.migrations - .filter(r => r.origin_minion_pool_id === minionPoolStore.minionPoolDetails?.id - || r.destination_minion_pool_id === minionPoolStore.minionPoolDetails?.id)} + .filter(r => r.origin_minion_pool_id === this.minionPool?.id + || r.destination_minion_pool_id === this.minionPool?.id)} endpoints={endpointStore.endpoints} - detailsLoading={minionPoolStore.loadingMinionPoolDetails || endpointStore.loading} schema={minionPoolStore.minionPoolCombinedSchema} schemaLoading={minionPoolStore.loadingMinionPoolSchema || minionPoolStore.loadingEnvOptions} - executionsLoading={minionPoolStore.loadingMinionPoolDetails} - onExecutionChange={id => { this.handleExecutionChange(id) }} - executions={minionPoolStore.minionPoolDetails?.executions || []} - executionsTasksLoading={minionPoolStore.loadingMinionPoolDetails - || minionPoolStore.loadingExecutionsTasks} - executionsTasks={minionPoolStore.executionsTasks} page={this.props.match.params.page || ''} - onCancelExecutionClick={(e, f) => { this.handleCancelExecution(e, f) }} + loading={minionPoolStore.loadingMinionPoolDetails} onDeleteMinionPoolClick={() => { this.handleDeleteMinionPoolClick() }} - onRunAction={a => { this.handleAction(a) }} + onAllocate={() => { this.handleAllocate() }} /> )} /> @@ -403,14 +347,12 @@ class MinionPoolDetailsPage extends React.Component<Props, State> { onRequestClose={() => { this.setState({ showDeleteMinionPoolConfirmation: false }) }} /> ) : null} - <AlertModal - isOpen={this.state.showCancelConfirmation} - title="Cancel Execution?" - message="Are you sure you want to cancel the current execution?" - extraMessage=" " - onConfirmation={() => { this.handleCancelExecutionConfirmation() }} - onRequestClose={() => { this.handleCloseCancelConfirmation() }} - /> + {this.state.showDeallocateConfirmation ? ( + <MinionPoolConfirmationModal + onCancelClick={() => { this.setState({ showDeallocateConfirmation: false }) }} + onExecuteClick={force => { this.handleDeallocateConfirmation(force) }} + /> + ) : null} {this.renderEditMinionPool()} </Wrapper> ) diff --git a/src/components/pages/MinionPoolDetailsPage/images/minion-pool.svg b/src/components/pages/MinionPoolDetailsPage/images/minion-pool.svg index e652b628..7e151379 100644 --- a/src/components/pages/MinionPoolDetailsPage/images/minion-pool.svg +++ b/src/components/pages/MinionPoolDetailsPage/images/minion-pool.svg @@ -1,55 +1,35 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg - xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="minion-pool.svg" + sodipodi:docname="minion-pool-details.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - id="svg8" + id="svg2230" version="1.1" viewBox="0 0 16.933333 16.933334" height="64" width="64"> <defs - id="defs2"> - <path - d="M 24,48 C 37.254834,48 48,37.254834 48,24 48,10.745166 37.254834,0 24,0 10.745166,0 0,10.745166 0,24 0,37.254834 10.745166,48 24,48 Z" - id="path-1" /> - <path - d="M24,48 C37.254834,48 48,37.254834 48,24 C48,10.745166 37.254834,0 24,0 C10.745166,0 0,10.745166 0,24 C0,37.254834 10.745166,48 24,48 Z" - id="path-1-2" /> - <linearGradient - osb:paint="solid" - id="linearGradient892"> - <stop - id="stop890" - offset="0" - style="stop-color:#0044ca;stop-opacity:1;" /> - </linearGradient> - <path - d="M24,48 C37.254834,48 48,37.254834 48,24 C48,10.745166 37.254834,0 24,0 C10.745166,0 0,10.745166 0,24 C0,37.254834 10.745166,48 24,48 Z" - id="path-1-3" /> - </defs> + id="defs2224" /> <sodipodi:namedview inkscape:window-maximized="0" inkscape:window-y="0" - inkscape:window-x="355" + inkscape:window-x="98" inkscape:window-height="1040" inkscape:window-width="1274" units="px" showgrid="false" inkscape:document-rotation="0" - inkscape:current-layer="Icon/Project/ProjectListItem-4" + inkscape:current-layer="Icon/Endpoint/Item-64" inkscape:document-units="mm" - inkscape:cy="59.411436" - inkscape:cx="0.035328786" - inkscape:zoom="1.979899" + inkscape:cy="76.103225" + inkscape:cx="-27.670379" + inkscape:zoom="1.4" inkscape:pageshadow="2" inkscape:pageopacity="0.0" borderopacity="1.0" @@ -57,7 +37,7 @@ pagecolor="#ffffff" id="base" /> <metadata - id="metadata5"> + id="metadata2227"> <rdf:RDF> <cc:Work rdf:about=""> @@ -73,56 +53,22 @@ inkscape:groupmode="layer" inkscape:label="Layer 1"> <g - transform="scale(0.35158512)" + transform="matrix(0.26458333,0,0,0.26458333,24.407485,-3.3885455)" style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1" - id="Icon/Project/ProjectListItem"> - <mask + id="Icon/Endpoint/Item-64"> + <path + style="fill:#ffffff;fill-opacity:1" fill="#ffffff" - id="mask-2"> - <use - height="100%" - width="100%" - y="0" - x="0" - id="use15" - xlink:href="#path-1-2" /> - </mask> - <use - height="100%" - width="100%" - y="0" - x="0" - xlink:href="#path-1-2" - fill-rule="evenodd" - fill="#c8ccd7" id="Pat-Benetar" - style="fill:#ffffff;fill-opacity:1" /> - </g> - <g - transform="matrix(0.26458333,0,0,0.26458333,29.063212,-7.2854432)" - style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1" - id="Icon/Project/ProjectListItem-4"> - <mask - fill="#ffffff" - id="mask-2-1"> - <use - height="100%" - width="100%" - y="0" - x="0" - id="use15-5" - xlink:href="#path-1-3" /> - </mask> - <path - d="m 27.675789,16.752495 v -3.472062 h -6.944288 v 3.472083 z m -15.405554,3.578939 v 13.995287 c 0,0.775617 0.63465,1.408655 1.410265,1.408713 l 21.046288,0.0016 c 0.775615,5.7e-5 1.410265,-0.634648 1.410265,-1.410265 V 20.331434 c 0,-0.775615 -0.63465,-1.410266 -1.410265,-1.410266 H 13.6805 c -0.775615,0 -1.410265,0.634651 -1.410265,1.410266 z M 35.48529,16.752516 c 1.565332,0 2.820418,1.255085 2.820418,2.820417 v 15.5123 c 0,1.565331 -1.255086,2.820418 -2.820418,2.820418 H 12.921944 c -1.565332,0 -2.820418,-1.255087 -2.820418,-2.820418 l 0.0141,-15.5123 c 0,-1.565332 1.240984,-2.820417 2.806316,-2.820417 h 5.640837 v -2.820419 c 0,-1.565332 1.255085,-2.820419 2.820418,-2.820419 h 5.640835 c 1.565332,0 2.820418,1.255087 2.820418,2.820419 v 2.820419 z" - style="fill:none;fill-opacity:1;stroke:none;stroke-width:1.41021;stroke-opacity:1" - id="path917-7" - sodipodi:nodetypes="cccccssssssssssssssccscsssscs" /> + d="m -60.248763,76.807102 c 17.673112,0 32,-14.326888 32,-32 0,-17.673112 -14.326888,-32 -32,-32 -17.673112,0 -32,14.326888 -32,32 0,17.673112 14.326888,32 32,32 z" /> <path - sodipodi:nodetypes="cccccssssssssssssssccscsssscs" - d="m -72.499106,49.675981 v -5.272804 h -10.544297 v 5.273474 z m -21.776155,4.086187 v 19.904625 c 0,1.029498 0.842037,1.871532 1.871536,1.871532 h 29.263615 c 1.029495,0 1.871533,-0.842036 1.871533,-1.871532 V 53.762168 c 0,-1.029493 -0.842038,-1.871533 -1.871533,-1.871533 h -29.263615 c -1.029499,0 -1.871536,0.842041 -1.871536,1.871533 z m 31.477988,-4.086158 c 2.07771,0 3.74362,1.665912 3.74362,3.743619 v 20.589922 c 0,2.07771 -1.66591,3.743622 -3.74362,3.743622 h -29.948978 c -2.077712,0 -3.743623,-1.665912 -3.743623,-3.743622 l 0.01871,-20.589922 c 0,-2.077707 1.647194,-3.743619 3.724906,-3.743619 h 7.487245 v -3.743624 c 0,-2.077712 1.665911,-3.743624 3.743622,-3.743624 h 7.487243 c 2.077708,0 3.743621,1.665912 3.743621,3.743624 v 3.743624 z" - style="fill:#0044ca;fill-opacity:1;stroke:none;stroke-width:1.87158;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path896" /> + transform="matrix(3.7795276,0,0,3.7795276,-98.140003,12.448588)" + id="path867" + style="fill:#0044ca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0217726" + sodipodi:type="inkscape:offset" + inkscape:radius="-0.067076862" + inkscape:original="M 8.6796875 2.4765625 C 8.5412293 2.4765625 8.4277344 2.5880175 8.4277344 2.7265625 L 8.4277344 3.1953125 C 6.5089469 3.8660931 5.2275715 5.6802221 5.234375 7.7128906 L 5.234375 8.046875 L 4.7539062 8.25 C 4.6610331 8.2892071 4.6015625 8.3798616 4.6015625 8.4804688 L 4.6015625 9.7988281 C 4.6015625 9.9372866 4.7149694 10.048828 4.8535156 10.048828 L 5.1796875 10.048828 L 5.1796875 10.550781 C 5.1796875 10.689241 5.2911441 10.802734 5.4296875 10.802734 L 6.3300781 10.802734 C 6.4156371 11.116904 6.5424639 11.41915 6.7050781 11.701172 L 6.0664062 12.337891 C 5.9690252 12.435701 5.9690253 12.593517 6.0664062 12.691406 L 7.1328125 13.757812 C 7.2306135 13.855192 7.39039 13.855193 7.4882812 13.757812 L 8.125 13.121094 C 8.4063408 13.283709 8.7080795 13.408328 9.0214844 13.494141 L 9.0214844 14.396484 C 9.0214844 14.534944 9.1329445 14.646484 9.2714844 14.646484 L 10.777344 14.646484 C 10.915888 14.646484 11.029297 14.53494 11.029297 14.396484 L 11.029297 13.494141 C 11.343468 13.408591 11.643675 13.283964 11.925781 13.121094 L 12.564453 13.757812 C 12.662263 13.855192 12.820077 13.855193 12.917969 13.757812 L 13.984375 12.691406 C 14.081755 12.593516 14.081755 12.435696 13.984375 12.337891 L 13.347656 11.701172 C 13.510269 11.41915 13.635144 11.116904 13.720703 10.802734 L 14.621094 10.802734 C 14.75964 10.802734 14.873047 10.689236 14.873047 10.550781 L 14.873047 10.048828 L 15.199219 10.048828 C 15.337764 10.048828 15.449219 9.9372836 15.449219 9.7988281 L 15.449219 8.4804688 C 15.448724 8.3790907 15.388557 8.2884428 15.294922 8.25 L 14.810547 8.0410156 L 14.810547 7.7109375 C 14.81742 5.6782702 13.534022 3.8641399 11.615234 3.1933594 L 11.615234 2.7265625 C 11.615234 2.5880179 11.503686 2.4765625 11.365234 2.4765625 L 8.6796875 2.4765625 z M 8.9257812 2.9804688 L 11.113281 2.9804688 L 11.113281 7.1738281 L 8.9296875 7.1738281 L 8.9257812 2.9804688 z M 8.4277344 3.7285156 L 8.4277344 7.4238281 C 8.4277344 7.5622878 8.541154 7.6757812 8.6796875 7.6757812 L 11.369141 7.6757812 C 11.507687 7.6757812 11.621094 7.5622872 11.621094 7.4238281 L 11.621094 3.7285156 C 13.252501 4.375738 14.319558 5.9561234 14.3125 7.7109375 L 14.3125 8.2128906 C 14.3125 8.3135061 14.373931 8.4042365 14.466797 8.4433594 L 14.951172 8.6523438 L 14.951172 9.546875 L 5.1015625 9.546875 L 5.1015625 8.6464844 L 5.5859375 8.4375 C 5.6788105 8.3983719 5.7382812 8.3077305 5.7382812 8.2070312 L 5.7382812 7.7050781 C 5.7327312 5.9524758 6.7990481 4.3759923 8.4277344 3.7285156 z M 5.6757812 10.048828 L 7.96875 10.048828 C 8.1062732 11.182614 9.1376971 11.991295 10.271484 11.853516 C 11.216037 11.738867 11.959743 10.99338 12.074219 10.048828 L 14.371094 10.048828 L 14.373047 10.300781 L 13.537109 10.300781 C 13.416686 10.298081 13.312455 10.380432 13.287109 10.498047 C 13.202579 10.891058 13.047211 11.266466 12.828125 11.603516 C 12.763565 11.702686 12.776095 11.832241 12.859375 11.916016 L 13.460938 12.515625 L 12.742188 13.228516 L 12.142578 12.628906 C 12.058818 12.545636 11.927292 12.531153 11.828125 12.595703 C 11.491077 12.814705 11.117621 12.970151 10.724609 13.054688 C 10.606909 13.080027 10.524623 13.186209 10.527344 13.306641 L 10.527344 14.152344 L 9.5234375 14.152344 L 9.5234375 13.298828 C 9.5261968 13.178406 9.4419223 13.074172 9.3242188 13.048828 C 8.9312923 12.96446 8.5576663 12.807233 8.2207031 12.587891 C 8.121535 12.523436 7.9901091 12.537737 7.90625 12.621094 L 7.3066406 13.220703 L 6.5957031 12.511719 L 7.1972656 11.912109 C 7.2806116 11.828339 7.2929896 11.696821 7.2285156 11.597656 C 7.0073924 11.262637 6.8492341 10.889781 6.7617188 10.498047 C 6.7362954 10.380426 6.6321479 10.297974 6.5117188 10.300781 L 5.6757812 10.300781 L 5.6757812 10.048828 z M 8.4785156 10.048828 L 8.4804688 10.048828 L 11.570312 10.048828 C 11.432279 10.902548 10.629026 11.483738 9.7753906 11.345703 C 9.1093725 11.238039 8.5861883 10.714845 8.4785156 10.048828 z " + d="m 8.6796875,2.5429688 c -0.1024615,0 -0.1855469,0.081681 -0.1855469,0.1835937 v 0.46875 a 0.06708357,0.06708357 0 0 1 -0.044922,0.0625 C 6.5572137,3.9192303 5.2940725,5.708545 5.3007812,7.7128906 V 8.046875 a 0.06708357,0.06708357 0 0 1 -0.041016,0.0625 L 4.7792969,8.3125 c -0.068195,0.028789 -0.1113281,0.093795 -0.1113281,0.1679688 v 1.3183593 c 0,0.1018315 0.083002,0.1835938 0.1855468,0.1835938 h 0.3261719 a 0.06708357,0.06708357 0 0 1 0.066406,0.066406 v 0.501953 c 0,0.102464 0.081683,0.185547 0.1835937,0.185547 h 0.9003906 a 0.06708357,0.06708357 0 0 1 0.064453,0.04883 c 0.084009,0.308479 0.2093469,0.605682 0.3691407,0.882813 a 0.06708357,0.06708357 0 0 1 -0.011719,0.08008 l -0.6386719,0.636719 c -0.071928,0.07224 -0.071959,0.187431 0,0.259765 l 1.0664063,1.066407 c 0.071917,0.07161 0.1896998,0.07164 0.2617187,0 L 8.078125,13.074219 a 0.06708357,0.06708357 0 0 1 0.080078,-0.01172 c 0.2762053,0.159647 0.5728314,0.282847 0.8808594,0.367188 a 0.06708357,0.06708357 0 0 1 0.048828,0.06445 v 0.902343 c 0,0.102463 0.081057,0.183594 0.1835938,0.183594 h 1.5058596 c 0.102541,0 0.185547,-0.08177 0.185547,-0.183594 v -0.902343 a 0.06708357,0.06708357 0 0 1 0.04883,-0.06445 c 0.308688,-0.08406 0.603767,-0.207212 0.880859,-0.367188 a 0.06708357,0.06708357 0 0 1 0.08008,0.01172 l 0.638672,0.636719 c 0.07225,0.07193 0.187428,0.07196 0.259766,0 L 13.9375,12.644531 c 0.07196,-0.07233 0.07192,-0.187527 0,-0.259765 l -0.636719,-0.636719 a 0.06708357,0.06708357 0 0 1 -0.01172,-0.08008 c 0.159677,-0.27693 0.283111,-0.574085 0.367188,-0.882813 a 0.06708357,0.06708357 0 0 1 0.06445,-0.04883 h 0.900391 c 0.101912,0 0.185547,-0.08372 0.185547,-0.185547 v -0.501953 a 0.06708357,0.06708357 0 0 1 0.06641,-0.066406 h 0.326172 c 0.102542,0 0.183593,-0.081136 0.183593,-0.1835938 V 8.4804688 C 15.382446,8.4054342 15.338234,8.3407068 15.269531,8.3125 a 0.06708357,0.06708357 0 0 1 -0.002,0 L 14.783203,8.1035156 a 0.06708357,0.06708357 0 0 1 -0.03906,-0.0625 V 7.7109375 C 14.750918,5.7066389 13.485769,3.9172819 11.59375,3.2558594 a 0.06708357,0.06708357 0 0 1 -0.04492,-0.0625 V 2.7265625 c 0,-0.1025404 -0.08114,-0.1835937 -0.183594,-0.1835937 z m 0.2460937,0.3710937 h 2.1874998 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 v 4.1933593 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 8.9296875 A 0.06708357,0.06708357 0 0 1 8.8632812,7.1738281 L 8.859375,2.9804688 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z M 8.4238281,3.6621094 a 0.06708357,0.06708357 0 0 1 0.070312,0.066406 v 3.6953125 c 0,0.1018289 0.083649,0.1855469 0.1855469,0.1855469 h 2.6894535 c 0.101914,0 0.185547,-0.083715 0.185547,-0.1855469 V 3.7285156 a 0.06708357,0.06708357 0 0 1 0.0918,-0.0625 c 1.656879,0.6573276 2.73959,2.2627547 2.732422,4.0449219 v 0.5019531 c 0,0.073313 0.04486,0.1391422 0.113282,0.1679688 a 0.06708357,0.06708357 0 0 1 0.002,0 l 0.484375,0.2089844 a 0.06708357,0.06708357 0 0 1 0.03906,0.0625 V 9.546875 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 5.1015625 A 0.06708357,0.06708357 0 0 1 5.0351562,9.546875 V 8.6464844 a 0.06708357,0.06708357 0 0 1 0.039063,-0.0625 L 5.5585938,8.375 a 0.06708357,0.06708357 0 0 1 0.00195,0 C 5.6287048,8.3462845 5.671875,8.2813212 5.671875,8.2070312 V 7.7050781 C 5.6662384,5.9251416 6.7482637,4.3235875 8.4023438,3.6660156 a 0.06708357,0.06708357 0 0 1 0.021484,-0.00391 z M 5.6757812,9.9824219 H 7.96875 a 0.06708357,0.06708357 0 0 1 0.066406,0.058594 c 0.1331549,1.097771 1.1307665,1.879493 2.2285158,1.746093 0.914206,-0.110965 1.633332,-0.831804 1.74414,-1.746093 a 0.06708357,0.06708357 0 0 1 0.06641,-0.058594 h 2.296875 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 l 0.002,0.251953 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 h -0.835938 a 0.06708357,0.06708357 0 0 1 -0.002,0 c -0.089,-0.002 -0.162914,0.05763 -0.18164,0.144531 -0.08624,0.400945 -0.245158,0.784924 -0.46875,1.128906 -0.04782,0.07346 -0.03979,0.166877 0.02148,0.228516 l 0.601562,0.599609 a 0.06708357,0.06708357 0 0 1 0,0.09375 l -0.71875,0.712891 a 0.06708357,0.06708357 0 0 1 -0.09375,0 l -0.599609,-0.59961 c -0.06162,-0.06126 -0.157872,-0.07069 -0.230469,-0.02344 -0.34387,0.223435 -0.725955,0.382495 -1.126953,0.46875 -0.0863,0.01858 -0.146542,0.09458 -0.144531,0.183594 a 0.06708357,0.06708357 0 0 1 0,0.002 v 0.845703 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 H 9.5234375 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.853516 a 0.06708357,0.06708357 0 0 1 0,-0.002 c 0.00202,-0.08827 -0.059338,-0.162876 -0.1464843,-0.181641 -0.4011171,-0.08613 -0.7832732,-0.246989 -1.1269531,-0.470703 -0.072662,-0.04723 -0.1687411,-0.03792 -0.2304688,0.02344 l -0.5996094,0.599609 a 0.06708357,0.06708357 0 0 1 -0.09375,0 L 6.5488281,12.558594 a 0.06708357,0.06708357 0 0 1 0,-0.09375 l 0.6015625,-0.59961 c 0.06093,-0.06124 0.069046,-0.157315 0.021484,-0.230468 -0.2256271,-0.341843 -0.3872726,-0.72337 -0.4765625,-1.123047 -0.018799,-0.08697 -0.092703,-0.146604 -0.1816406,-0.144531 a 0.06708357,0.06708357 0 0 1 -0.00195,0 H 5.6757812 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.251953 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z m 2.7949219,0 c 0.0026,0 0.00911,0 0.00977,0 h 3.0898432 a 0.06708357,0.06708357 0 0 1 0.06641,0.076172 C 11.492907,10.948055 10.655096,11.555939 9.765625,11.412109 9.0712748,11.299865 8.5243622,10.752942 8.4121094,10.058594 a 0.06708357,0.06708357 0 0 1 0.058594,-0.076172 z" /> </g> </g> </svg> diff --git a/src/components/pages/MinionPoolsPage/MinionPoolsPage.tsx b/src/components/pages/MinionPoolsPage/MinionPoolsPage.tsx index 805ff7dc..073ab475 100644 --- a/src/components/pages/MinionPoolsPage/MinionPoolsPage.tsx +++ b/src/components/pages/MinionPoolsPage/MinionPoolsPage.tsx @@ -34,14 +34,16 @@ import { MinionPool } from '../../../@types/MinionPool' import emptyListImage from './images/minion-pool-empty-list.svg' import providerStore from '../../../stores/ProviderStore' import endpointStore from '../../../stores/EndpointStore' -import minionPoolStore, { MinionPoolAction } from '../../../stores/MinionPoolStore' +import minionPoolStore from '../../../stores/MinionPoolStore' import { Endpoint } from '../../../@types/Endpoint' import MinionEndpointModal from '../../organisms/MinionEndpointModal/MinionEndpointModal' import MinionPoolModal from '../../organisms/MinionPoolModal/MinionPoolModal' import MinionPoolListItem from '../../molecules/MinionPoolListItem/MinionPoolListItem' import Palette from '../../styleUtils/Palette' -import notificationStore from '../../../stores/NotificationStore' import AlertModal from '../../organisms/AlertModal/AlertModal' +import MinionPoolConfirmationModal from '../../molecules/MinionPoolConfirmationModal/MinionPoolConfirmationModal' +import notificationStore from '../../../stores/NotificationStore' +import ObjectUtils from '../../../utils/ObjectUtils' const Wrapper = styled.div<any>`` @@ -51,8 +53,8 @@ type State = { showChooseMinionEndpointModal: boolean, showMinionPoolModal: boolean, selectedMinionPoolEndpoint: Endpoint | null - showCancelExecutionModal: boolean, showDeletePoolsModal: boolean, + showDeallocateConfirmation: boolean, selectedMinionPoolPlatform: 'source' | 'destination' } @@ -64,8 +66,8 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { showChooseMinionEndpointModal: false, selectedMinionPoolEndpoint: null, showMinionPoolModal: false, - showCancelExecutionModal: false, showDeletePoolsModal: false, + showDeallocateConfirmation: false, selectedMinionPoolPlatform: 'source', } @@ -90,14 +92,28 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { getFilterItems() { return [ { label: 'All', value: 'all' }, - { label: 'Allocating', value: 'ALLOCATING' }, { label: 'Allocated', value: 'ALLOCATED' }, - { label: 'Initializing', value: 'INITIALIZING' }, - { label: 'Initialized', value: 'INITIALIZED' }, + { label: 'Allocating', value: 'ALLOCATING_MACHINES' }, + { label: 'Deallocated', value: 'DEALLOCATED' }, + { label: 'Deallocating', value: 'DEALLOCATING_MACHINES' }, { label: 'Error', value: 'ERROR' }, ] } + getMinionsThatCanBe(action: 'allocated' | 'deallocated' | 'refreshed' | 'deleted'): MinionPool[] { + const minions = this.state.selectedMinionPools + switch (action) { + case 'allocated': + return minions.filter(minion => minion.status === 'DEALLOCATED') + case 'deallocated': + return minions.filter(minion => minion.status === 'ALLOCATED' || minion.status === 'ERROR') + case 'refreshed': + return minions.filter(minion => minion.status === 'ALLOCATED') + default: + return minions.filter(minion => minion.status === 'DEALLOCATED' || minion.status === 'ERROR') + } + } + getEndpoint(endpointId: string) { return endpointStore.endpoints.find(endpoint => endpoint.id === endpointId) } @@ -118,14 +134,14 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { searchText(item: MinionPool, text?: string | null) { const result = false - if (item.pool_name.toLowerCase().indexOf(text || '') > -1) { + if (item.name.toLowerCase().indexOf(text || '') > -1) { return true } return result } itemFilterFunction(item: MinionPool, filterStatus?: string | null, filterText?: string) { - if ((filterStatus !== 'all' && item.pool_status !== filterStatus) + if ((filterStatus !== 'all' && item.status !== filterStatus) || !this.searchText(item, filterText) ) { return false @@ -134,15 +150,9 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { return true } - cancelExecutions() { - this.state.selectedMinionPools.forEach(pool => { - minionPoolStore.cancelExecution(pool.id) - }) - this.setState({ showCancelExecutionModal: false }) - } - deleteSelectedMinionPools() { - this.state.selectedMinionPools.forEach(pool => { + const pools = this.getMinionsThatCanBe('deleted') + pools.forEach(pool => { minionPoolStore.deleteMinionPool(pool.id) }) this.setState({ showDeletePoolsModal: false }) @@ -210,92 +220,69 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { }) } - async handleAction(action: MinionPoolAction) { - const runAction = async (message: string) => { - notificationStore.alert(message) - await Promise.all(this.state.selectedMinionPools - .map(minionPool => minionPoolStore.runAction(minionPool.id, action))) - await minionPoolStore.loadMinionPools() - } + async handleAllocate() { + const pools = this.getMinionsThatCanBe('allocated') - switch (action) { - case 'set-up-shared-resources': - runAction('Setting up shared resources...') - break - case 'tear-down-shared-resources': - runAction('Tearing up shared resources...') - break - case 'allocate-machines': - runAction('Allocating machines...') - break - case 'deallocate-machines': - runAction('Deallocating machines...') - break - default: - } + const plural = pools.length === 1 ? '' : 's' + notificationStore.alert(`Allocating minion pool${plural}...`) + await Promise.all(pools.map(minionPool => minionPoolStore.runAction(minionPool.id, 'allocate'))) + await minionPoolStore.loadMinionPools() + } + + handleDeallocate() { + this.setState({ + showDeallocateConfirmation: true, + }) + } + + async handleDeallocateConfirmation(force: boolean) { + this.setState({ + showDeallocateConfirmation: false, + }) + const pools = this.getMinionsThatCanBe('deallocated') + const plural = pools.length === 1 ? '' : 's' + notificationStore.alert(`Deallocating minion pool${plural}...`) + await Promise.all(pools.map(minionPool => minionPoolStore.runAction(minionPool.id, 'deallocate', { force }))) + await minionPoolStore.loadMinionPools() + } + + async handleRefreshAction() { + const pools = this.getMinionsThatCanBe('refreshed') + const plural = pools.length === 1 ? '' : 's' + notificationStore.alert(`Refreshing minion pool${plural}...`) + await Promise.all(pools.map(minionPool => minionPoolStore.runAction(minionPool.id, 'refresh'))) + await minionPoolStore.loadMinionPools() } render() { - const allPoolsAreStatus = () => { - const statuses: any = {} - this.state.selectedMinionPools.forEach(pool => { - if (!statuses[pool.pool_status]) { - statuses[pool.pool_status] = 0 - } - statuses[pool.pool_status] += 1 - }) - if (Object.keys(statuses).length === 1) { - return Object.keys(statuses)[0] - } - return null - } - const allPoolStatusResult = allPoolsAreStatus() - const uninitialized = allPoolStatusResult === 'UNINITIALIZED' - const deallocated = allPoolStatusResult === 'DEALLOCATED' - const allocated = allPoolStatusResult === 'ALLOCATED' - const isRunning = allPoolStatusResult?.indexOf('ING') === ((allPoolStatusResult?.length || -100) - 3) + const canBeAllocated = this.getMinionsThatCanBe('allocated').length > 0 + const canBeDeallocated = this.getMinionsThatCanBe('deallocated').length > 0 + const canBeRefreshed = this.getMinionsThatCanBe('refreshed').length > 0 + const canBeDeleted = this.getMinionsThatCanBe('deleted').length > 0 + const BulkActions: DropdownAction[] = [ { - label: 'Setup shared resources', + label: 'Allocate', color: Palette.primary, - action: () => { - this.handleAction('set-up-shared-resources') - }, - disabled: !uninitialized, - title: !uninitialized ? 'The minion pools should be uninitialized' : '', + action: () => { this.handleAllocate() }, + disabled: !canBeAllocated, + title: !canBeAllocated ? 'The minion pool should be deallocated' : '', }, { - label: 'Tear down shared resources', - action: () => { - this.handleAction('tear-down-shared-resources') - }, - disabled: !deallocated, - title: !deallocated ? 'The minion pools should be deallocated' : '', - }, - { - label: 'Allocate Machines', - color: Palette.primary, + label: 'Deallocate', action: () => { - this.handleAction('allocate-machines') + this.handleDeallocate() }, - disabled: !deallocated, - title: !deallocated ? 'The minion pools should be deallocated' : '', + disabled: !canBeDeallocated, + title: !canBeDeallocated ? 'The minion pool should be allocated' : '', }, { - label: 'Deallocate Machines', + label: 'Refresh', action: () => { - this.handleAction('deallocate-machines') + this.handleRefreshAction() }, - disabled: !allocated, - title: !allocated ? 'The minion pools should be allocated' : '', - }, - { - label: 'Cancel Execution', - action: () => { - this.setState({ showCancelExecutionModal: true }) - }, - disabled: !isRunning, - title: !isRunning ? 'The minion pools do not have an active execution' : '', + disabled: !canBeRefreshed, + title: !canBeRefreshed ? 'The minion pool should be allocated' : '', }, { label: 'Delete Minion Pools', @@ -303,8 +290,8 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { action: () => { this.setState({ showDeletePoolsModal: true }) }, - disabled: !uninitialized, - title: !uninitialized ? 'The minion pools should be uninitialized' : '', + disabled: !canBeDeleted, + title: !canBeDeleted ? 'The minion pool should be deallocated' : '', }, ] @@ -371,7 +358,7 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { {this.state.showMinionPoolModal ? ( <Modal isOpen - title="New Minion Pool" + title={`New ${ObjectUtils.capitalizeFirstLetter(this.state.selectedMinionPoolPlatform)} Minion Pool`} onRequestClose={() => { this.handleCloseMinionPoolModalRequest() }} > <MinionPoolModal @@ -383,16 +370,6 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { /> </Modal> ) : null} - {this.state.showCancelExecutionModal ? ( - <AlertModal - isOpen - title="Cancel Executions?" - message="Are you sure you want to cancel the selected Minion Pools executions?" - extraMessage=" " - onConfirmation={() => { this.cancelExecutions() }} - onRequestClose={() => { this.setState({ showCancelExecutionModal: false }) }} - /> - ) : null} {this.state.showDeletePoolsModal ? ( <AlertModal isOpen @@ -403,6 +380,12 @@ class MinionPoolsPage extends React.Component<RouteComponentProps, State> { onRequestClose={() => { this.setState({ showDeletePoolsModal: false }) }} /> ) : null} + {this.state.showDeallocateConfirmation ? ( + <MinionPoolConfirmationModal + onCancelClick={() => { this.setState({ showDeallocateConfirmation: false }) }} + onExecuteClick={force => { this.handleDeallocateConfirmation(force) }} + /> + ) : null} </Wrapper> ) } diff --git a/src/components/pages/MinionPoolsPage/images/minion-pool-empty-list.svg b/src/components/pages/MinionPoolsPage/images/minion-pool-empty-list.svg index ace32baf..1f3845fc 100644 --- a/src/components/pages/MinionPoolsPage/images/minion-pool-empty-list.svg +++ b/src/components/pages/MinionPoolsPage/images/minion-pool-empty-list.svg @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" +<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" @@ -7,59 +7,43 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="minion-pool-empty-list.svg" + sodipodi:docname="minion-pool-empty.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - id="svg8" + id="svg3477" version="1.1" viewBox="0 0 25.399999 25.400001" height="96" width="96"> <defs - id="defs2"> + id="defs3471"> <marker inkscape:isstock="true" - style="overflow:visible;" - id="Arrow1Mend" + style="overflow:visible" + id="Arrow1Lstart" refX="0.0" refY="0.0" orient="auto" - inkscape:stockid="Arrow1Mend"> + inkscape:stockid="Arrow1Lstart"> <path - transform="scale(0.4) rotate(180) translate(10,0)" - style="fill-rule:evenodd;stroke:#f91661;stroke-width:1pt;stroke-opacity:1;fill:#f91661;fill-opacity:1" + transform="scale(0.8) translate(12.5,0)" + style="fill-rule:evenodd;stroke:#0044ca;stroke-width:1pt;stroke-opacity:1;fill:#0044ca;fill-opacity:1" d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - id="path3085" /> + id="path4096" /> </marker> - <linearGradient - osb:paint="solid" - id="linearGradient2826"> - <stop - id="stop2824" - offset="0" - style="stop-color:#feffff;stop-opacity:1;" /> - </linearGradient> - <linearGradient - osb:paint="solid" - id="linearGradient2804"> - <stop - id="stop2802" - offset="0" - style="stop-color:#feffff;stop-opacity:1;" /> - </linearGradient> </defs> <sodipodi:namedview inkscape:window-maximized="0" - inkscape:window-y="55" - inkscape:window-x="42" - inkscape:window-height="868" - inkscape:window-width="1032" + inkscape:window-y="0" + inkscape:window-x="245" + inkscape:window-height="1040" + inkscape:window-width="1274" units="px" showgrid="false" inkscape:document-rotation="0" inkscape:current-layer="layer1" inkscape:document-units="mm" - inkscape:cy="88.210625" - inkscape:cx="16.023473" + inkscape:cy="45.522314" + inkscape:cx="105.20221" inkscape:zoom="2.8" inkscape:pageshadow="2" inkscape:pageopacity="0.0" @@ -68,7 +52,7 @@ pagecolor="#ffffff" id="base" /> <metadata - id="metadata5"> + id="metadata3474"> <rdf:RDF> <cc:Work rdf:about=""> @@ -84,12 +68,12 @@ inkscape:groupmode="layer" inkscape:label="Layer 1"> <path - style="fill:none;fill-rule:evenodd;stroke:#f91661;stroke-width:0.396875;stroke-linecap:round;stroke-dasharray:0.812599, 1.6255, 2.84456, 1.6255" - d="m 41.444302,-33.90587 c -1.884894,-0.770533 -3.026318,-1.774198 -3.026318,-2.870415 0,-1.06957 1.087078,-2.051097 2.891361,-2.813875 -0.736607,-2.778883 -0.463145,-5.26087 0.991399,-6.741615 0.843592,-0.859273 2.038861,-1.311051 3.462288,-1.311051 0.04207,0 0.08468,5.27e-4 0.127294,0.0011 1.589632,0.02962 3.370966,0.599304 5.147193,1.599436 1.776205,-1.000036 3.557442,-1.569661 5.146719,-1.599281 0.04261,-5.26e-4 0.08522,-0.0011 0.127294,-0.0011 1.423427,0 2.618696,0.451778 3.462288,1.311051 1.479773,1.506429 1.737136,4.049133 0.95208,6.886477 1.606922,0.74035 2.562916,1.665894 2.562916,2.668858 0,1.030501 -1.008771,1.979213 -2.69564,2.729355 0.7522,2.797791 0.483864,5.299341 -0.978836,6.788389 -0.868403,0.884578 -2.108981,1.333192 -3.589582,1.309997 -1.541991,-0.02873 -3.264367,-0.565642 -4.987551,-1.510678 -1.723091,0.944945 -3.445435,1.481842 -4.987402,1.510833 -1.477365,0.03005 -2.720638,-0.425419 -3.589582,-1.309996 -1.4381,-1.464006 -1.721672,-3.906743 -1.015921,-6.647485 z m 15.960981,0.863336 c 1.219029,-0.268082 2.29785,-0.608598 3.187893,-1.004396 -0.225822,-0.839943 -0.543626,-1.706587 -0.948706,-2.58055 -0.586926,1.202586 -1.337385,2.41431 -2.239187,3.584946 z m -12.511027,-7.589544 c -1.372306,0.26584 -2.588069,0.620494 -3.584911,1.041918 0.254945,0.961792 0.630887,1.959148 1.120734,2.962867 0.621565,-1.341034 1.448621,-2.699302 2.464177,-4.004785 z m -3.449954,6.726208 c 0.93273,0.381294 2.047513,0.7055 3.296128,0.955585 0.355771,0.456975 0.734638,0.907463 1.135872,1.34846 1.587854,1.745062 3.374812,3.163067 5.160905,4.142603 1.786055,-0.979473 3.572911,-2.397359 5.160515,-4.142448 0.428301,-0.470693 0.831117,-0.952198 1.207561,-1.440864 -1.903765,0.418666 -4.149485,0.660668 -6.552153,0.660668 -2.220303,0 -4.306522,-0.20666 -6.1127,-0.568419 -0.933538,-1.199099 -1.708049,-2.442863 -2.310351,-3.677008 -0.427766,0.922912 -0.758204,1.83766 -0.985777,2.721423 z m 9.593217,-12.136466 c -1.732824,0.975611 -3.460859,2.360865 -5.001198,4.054001 -0.403562,0.443504 -0.784497,0.896609 -1.142065,1.356257 1.770395,-0.342957 3.801329,-0.538099 5.958874,-0.538099 2.337293,0 4.526062,0.229011 6.395997,0.626865 0.984144,1.278164 1.787879,2.605228 2.395343,3.915832 0.465263,-0.953303 0.827764,-1.900864 1.08143,-2.817663 -0.95172,-0.438482 -2.131771,-0.812001 -3.476773,-1.098169 -0.377397,-0.490146 -0.781323,-0.9731 -1.210884,-1.445179 -1.540258,-1.693045 -3.268061,-3.078242 -5.000724,-4.053845 z" - id="Combined-Shape" /> - <path - d="M 15.75651,5.8275724 V 2.7334477 H 9.6265686 V 5.8275886 Z M 2.1575418,9.0169413 V 21.488827 c 0,0.691189 0.5601197,1.256759 1.244792,1.256759 H 21.980562 c 0.684672,0 1.244792,-0.56557 1.244792,-1.256759 V 9.0169413 c 0,-0.6911908 -0.560303,-1.2567597 -1.244792,-1.2567597 H 3.4023338 c -0.6846723,0 -1.244792,0.5655689 -1.244792,1.2567597 z M 22.650196,5.8275886 c 1.3818,0 2.489676,1.118469 2.489676,2.5134137 V 22.164776 c 0,1.394943 -1.107968,2.513413 -2.489676,2.513413 H 2.7328093 c -1.3817997,0 -2.48965731,-1.11847 -2.48965731,-2.513413 L 0.25558892,8.3410023 c 0,-1.3949447 1.09551228,-2.5134137 2.47722038,-2.5134137 H 7.7121605 V 3.3141751 c 0,-1.3949428 1.1079675,-2.51341364 2.4896755,-2.51341364 h 4.979333 c 1.3818,0 2.489676,1.11847074 2.489676,2.51341364 v 2.5134135 z" - style="opacity:1;fill:none;fill-opacity:1;stroke:#0044ca;stroke-width:0.486304;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.9;stroke-dasharray:0.995704, 1.99178, 3.48554, 1.99178;stroke-dashoffset:0;stroke-opacity:1" - id="path10" /> + transform="matrix(1.9184766,0,0,1.9184766,-6.5334769,-3.7250823)" + id="path867" + style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0044ca;stroke-width:0.13791324;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stroke-dashoffset:0;stroke-linecap:round" + sodipodi:type="inkscape:offset" + inkscape:radius="-0.067076862" + inkscape:original="M 8.6796875 2.4765625 C 8.5412293 2.4765625 8.4277344 2.5880175 8.4277344 2.7265625 L 8.4277344 3.1953125 C 6.5089469 3.8660931 5.2275715 5.6802221 5.234375 7.7128906 L 5.234375 8.046875 L 4.7539062 8.25 C 4.6610331 8.2892071 4.6015625 8.3798616 4.6015625 8.4804688 L 4.6015625 9.7988281 C 4.6015625 9.9372866 4.7149694 10.048828 4.8535156 10.048828 L 5.1796875 10.048828 L 5.1796875 10.550781 C 5.1796875 10.689241 5.2911441 10.802734 5.4296875 10.802734 L 6.3300781 10.802734 C 6.4156371 11.116904 6.5424639 11.41915 6.7050781 11.701172 L 6.0664062 12.337891 C 5.9690252 12.435701 5.9690253 12.593517 6.0664062 12.691406 L 7.1328125 13.757812 C 7.2306135 13.855192 7.39039 13.855193 7.4882812 13.757812 L 8.125 13.121094 C 8.4063408 13.283709 8.7080795 13.408328 9.0214844 13.494141 L 9.0214844 14.396484 C 9.0214844 14.534944 9.1329445 14.646484 9.2714844 14.646484 L 10.777344 14.646484 C 10.915888 14.646484 11.029297 14.53494 11.029297 14.396484 L 11.029297 13.494141 C 11.343468 13.408591 11.643675 13.283964 11.925781 13.121094 L 12.564453 13.757812 C 12.662263 13.855192 12.820077 13.855193 12.917969 13.757812 L 13.984375 12.691406 C 14.081755 12.593516 14.081755 12.435696 13.984375 12.337891 L 13.347656 11.701172 C 13.510269 11.41915 13.635144 11.116904 13.720703 10.802734 L 14.621094 10.802734 C 14.75964 10.802734 14.873047 10.689236 14.873047 10.550781 L 14.873047 10.048828 L 15.199219 10.048828 C 15.337764 10.048828 15.449219 9.9372836 15.449219 9.7988281 L 15.449219 8.4804688 C 15.448724 8.3790907 15.388557 8.2884428 15.294922 8.25 L 14.810547 8.0410156 L 14.810547 7.7109375 C 14.81742 5.6782702 13.534022 3.8641399 11.615234 3.1933594 L 11.615234 2.7265625 C 11.615234 2.5880179 11.503686 2.4765625 11.365234 2.4765625 L 8.6796875 2.4765625 z M 8.9257812 2.9804688 L 11.113281 2.9804688 L 11.113281 7.1738281 L 8.9296875 7.1738281 L 8.9257812 2.9804688 z M 8.4277344 3.7285156 L 8.4277344 7.4238281 C 8.4277344 7.5622878 8.541154 7.6757812 8.6796875 7.6757812 L 11.369141 7.6757812 C 11.507687 7.6757812 11.621094 7.5622872 11.621094 7.4238281 L 11.621094 3.7285156 C 13.252501 4.375738 14.319558 5.9561234 14.3125 7.7109375 L 14.3125 8.2128906 C 14.3125 8.3135061 14.373931 8.4042365 14.466797 8.4433594 L 14.951172 8.6523438 L 14.951172 9.546875 L 5.1015625 9.546875 L 5.1015625 8.6464844 L 5.5859375 8.4375 C 5.6788105 8.3983719 5.7382812 8.3077305 5.7382812 8.2070312 L 5.7382812 7.7050781 C 5.7327312 5.9524758 6.7990481 4.3759923 8.4277344 3.7285156 z M 5.6757812 10.048828 L 7.96875 10.048828 C 8.1062732 11.182614 9.1376971 11.991295 10.271484 11.853516 C 11.216037 11.738867 11.959743 10.99338 12.074219 10.048828 L 14.371094 10.048828 L 14.373047 10.300781 L 13.537109 10.300781 C 13.416686 10.298081 13.312455 10.380432 13.287109 10.498047 C 13.202579 10.891058 13.047211 11.266466 12.828125 11.603516 C 12.763565 11.702686 12.776095 11.832241 12.859375 11.916016 L 13.460938 12.515625 L 12.742188 13.228516 L 12.142578 12.628906 C 12.058818 12.545636 11.927292 12.531153 11.828125 12.595703 C 11.491077 12.814705 11.117621 12.970151 10.724609 13.054688 C 10.606909 13.080027 10.524623 13.186209 10.527344 13.306641 L 10.527344 14.152344 L 9.5234375 14.152344 L 9.5234375 13.298828 C 9.5261968 13.178406 9.4419223 13.074172 9.3242188 13.048828 C 8.9312923 12.96446 8.5576663 12.807233 8.2207031 12.587891 C 8.121535 12.523436 7.9901091 12.537737 7.90625 12.621094 L 7.3066406 13.220703 L 6.5957031 12.511719 L 7.1972656 11.912109 C 7.2806116 11.828339 7.2929896 11.696821 7.2285156 11.597656 C 7.0073924 11.262637 6.8492341 10.889781 6.7617188 10.498047 C 6.7362954 10.380426 6.6321479 10.297974 6.5117188 10.300781 L 5.6757812 10.300781 L 5.6757812 10.048828 z M 8.4785156 10.048828 L 8.4804688 10.048828 L 11.570312 10.048828 C 11.432279 10.902548 10.629026 11.483738 9.7753906 11.345703 C 9.1093725 11.238039 8.5861883 10.714845 8.4785156 10.048828 z " + d="m 8.6796875,2.5429688 c -0.1024615,0 -0.1855469,0.081681 -0.1855469,0.1835937 v 0.46875 a 0.06708357,0.06708357 0 0 1 -0.044922,0.0625 C 6.5572137,3.9192303 5.2940725,5.708545 5.3007812,7.7128906 V 8.046875 a 0.06708357,0.06708357 0 0 1 -0.041016,0.0625 L 4.7792969,8.3125 c -0.068195,0.028789 -0.1113281,0.093795 -0.1113281,0.1679688 v 1.3183593 c 0,0.1018315 0.083002,0.1835938 0.1855468,0.1835938 h 0.3261719 a 0.06708357,0.06708357 0 0 1 0.066406,0.066406 v 0.501953 c 0,0.102464 0.081683,0.185547 0.1835937,0.185547 h 0.9003906 a 0.06708357,0.06708357 0 0 1 0.064453,0.04883 c 0.084009,0.308479 0.2093469,0.605682 0.3691407,0.882813 a 0.06708357,0.06708357 0 0 1 -0.011719,0.08008 l -0.6386719,0.636719 c -0.071928,0.07224 -0.071959,0.187431 0,0.259765 l 1.0664063,1.066407 c 0.071917,0.07161 0.1896998,0.07164 0.2617187,0 L 8.078125,13.074219 a 0.06708357,0.06708357 0 0 1 0.080078,-0.01172 c 0.2762053,0.159647 0.5728314,0.282847 0.8808594,0.367188 a 0.06708357,0.06708357 0 0 1 0.048828,0.06445 v 0.902343 c 0,0.102463 0.081057,0.183594 0.1835938,0.183594 h 1.5058596 c 0.102541,0 0.185547,-0.08177 0.185547,-0.183594 v -0.902343 a 0.06708357,0.06708357 0 0 1 0.04883,-0.06445 c 0.308688,-0.08406 0.603767,-0.207212 0.880859,-0.367188 a 0.06708357,0.06708357 0 0 1 0.08008,0.01172 l 0.638672,0.636719 c 0.07225,0.07193 0.187428,0.07196 0.259766,0 L 13.9375,12.644531 c 0.07196,-0.07233 0.07192,-0.187527 0,-0.259765 l -0.636719,-0.636719 a 0.06708357,0.06708357 0 0 1 -0.01172,-0.08008 c 0.159677,-0.27693 0.283111,-0.574085 0.367188,-0.882813 a 0.06708357,0.06708357 0 0 1 0.06445,-0.04883 h 0.900391 c 0.101912,0 0.185547,-0.08372 0.185547,-0.185547 v -0.501953 a 0.06708357,0.06708357 0 0 1 0.06641,-0.066406 h 0.326172 c 0.102542,0 0.183593,-0.081136 0.183593,-0.1835938 V 8.4804688 C 15.382446,8.4054342 15.338234,8.3407068 15.269531,8.3125 a 0.06708357,0.06708357 0 0 1 -0.002,0 L 14.783203,8.1035156 a 0.06708357,0.06708357 0 0 1 -0.03906,-0.0625 V 7.7109375 C 14.750918,5.7066389 13.485769,3.9172819 11.59375,3.2558594 a 0.06708357,0.06708357 0 0 1 -0.04492,-0.0625 V 2.7265625 c 0,-0.1025404 -0.08114,-0.1835937 -0.183594,-0.1835937 z m 0.2460937,0.3710937 h 2.1874998 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 v 4.1933593 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 8.9296875 A 0.06708357,0.06708357 0 0 1 8.8632812,7.1738281 L 8.859375,2.9804688 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z M 8.4238281,3.6621094 a 0.06708357,0.06708357 0 0 1 0.070312,0.066406 v 3.6953125 c 0,0.1018289 0.083649,0.1855469 0.1855469,0.1855469 h 2.6894535 c 0.101914,0 0.185547,-0.083715 0.185547,-0.1855469 V 3.7285156 a 0.06708357,0.06708357 0 0 1 0.0918,-0.0625 c 1.656879,0.6573276 2.73959,2.2627547 2.732422,4.0449219 v 0.5019531 c 0,0.073313 0.04486,0.1391422 0.113282,0.1679688 a 0.06708357,0.06708357 0 0 1 0.002,0 l 0.484375,0.2089844 a 0.06708357,0.06708357 0 0 1 0.03906,0.0625 V 9.546875 a 0.06708357,0.06708357 0 0 1 -0.06641,0.066406 H 5.1015625 A 0.06708357,0.06708357 0 0 1 5.0351562,9.546875 V 8.6464844 a 0.06708357,0.06708357 0 0 1 0.039063,-0.0625 L 5.5585938,8.375 a 0.06708357,0.06708357 0 0 1 0.00195,0 C 5.6287048,8.3462845 5.671875,8.2813212 5.671875,8.2070312 V 7.7050781 C 5.6662384,5.9251416 6.7482637,4.3235875 8.4023438,3.6660156 a 0.06708357,0.06708357 0 0 1 0.021484,-0.00391 z M 5.6757812,9.9824219 H 7.96875 a 0.06708357,0.06708357 0 0 1 0.066406,0.058594 c 0.1331549,1.097771 1.1307665,1.879493 2.2285158,1.746093 0.914206,-0.110965 1.633332,-0.831804 1.74414,-1.746093 a 0.06708357,0.06708357 0 0 1 0.06641,-0.058594 h 2.296875 a 0.06708357,0.06708357 0 0 1 0.06641,0.066406 l 0.002,0.251953 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 h -0.835938 a 0.06708357,0.06708357 0 0 1 -0.002,0 c -0.089,-0.002 -0.162914,0.05763 -0.18164,0.144531 -0.08624,0.400945 -0.245158,0.784924 -0.46875,1.128906 -0.04782,0.07346 -0.03979,0.166877 0.02148,0.228516 l 0.601562,0.599609 a 0.06708357,0.06708357 0 0 1 0,0.09375 l -0.71875,0.712891 a 0.06708357,0.06708357 0 0 1 -0.09375,0 l -0.599609,-0.59961 c -0.06162,-0.06126 -0.157872,-0.07069 -0.230469,-0.02344 -0.34387,0.223435 -0.725955,0.382495 -1.126953,0.46875 -0.0863,0.01858 -0.146542,0.09458 -0.144531,0.183594 a 0.06708357,0.06708357 0 0 1 0,0.002 v 0.845703 a 0.06708357,0.06708357 0 0 1 -0.06641,0.06641 H 9.5234375 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.853516 a 0.06708357,0.06708357 0 0 1 0,-0.002 c 0.00202,-0.08827 -0.059338,-0.162876 -0.1464843,-0.181641 -0.4011171,-0.08613 -0.7832732,-0.246989 -1.1269531,-0.470703 -0.072662,-0.04723 -0.1687411,-0.03792 -0.2304688,0.02344 l -0.5996094,0.599609 a 0.06708357,0.06708357 0 0 1 -0.09375,0 L 6.5488281,12.558594 a 0.06708357,0.06708357 0 0 1 0,-0.09375 l 0.6015625,-0.59961 c 0.06093,-0.06124 0.069046,-0.157315 0.021484,-0.230468 -0.2256271,-0.341843 -0.3872726,-0.72337 -0.4765625,-1.123047 -0.018799,-0.08697 -0.092703,-0.146604 -0.1816406,-0.144531 a 0.06708357,0.06708357 0 0 1 -0.00195,0 H 5.6757812 a 0.06708357,0.06708357 0 0 1 -0.066406,-0.06641 v -0.251953 a 0.06708357,0.06708357 0 0 1 0.066406,-0.066406 z m 2.7949219,0 c 0.0026,0 0.00911,0 0.00977,0 h 3.0898432 a 0.06708357,0.06708357 0 0 1 0.06641,0.076172 C 11.492907,10.948055 10.655096,11.555939 9.765625,11.412109 9.0712748,11.299865 8.5243622,10.752942 8.4121094,10.058594 a 0.06708357,0.06708357 0 0 1 0.058594,-0.076172 z" /> </g> </svg> diff --git a/src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.tsx b/src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.tsx index 71c64cfa..6636f0da 100644 --- a/src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.tsx +++ b/src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.tsx @@ -232,8 +232,9 @@ class ReplicaDetailsPage extends React.Component<Props, State> { const originEndpoint = endpointStore.endpoints.find(e => e.id === replica.origin_endpoint_id) const targetEndpoint = endpointStore.endpoints .find(e => e.id === replica.destination_endpoint_id) + const status = this.getStatus() - return Boolean(!originEndpoint || !targetEndpoint || this.getStatus() === 'RUNNING' || this.getStatus() === 'CANCELLING') + return Boolean(!originEndpoint || !targetEndpoint || status === 'RUNNING' || status === 'CANCELLING' || status === 'AWAITING_MINION_ALLOCATIONS') } handleUserItemClick(item: { value: string }) { @@ -516,7 +517,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> { }, { label: 'Cancel', - hidden: this.getStatus() !== 'RUNNING', + hidden: this.getStatus() !== 'RUNNING' && this.getStatus() !== 'AWAITING_MINION_ALLOCATIONS', action: () => { this.handleCancelLastExecutionClick() }, }, { @@ -639,7 +640,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> { > <ReplicaMigrationOptions transferItem={this.replica} - minionPools={minionPoolStore.minionPools.filter(m => m.endpoint_id === this.replica?.destination_endpoint_id && m.pool_platform === 'destination')} + minionPools={minionPoolStore.minionPools.filter(m => m.endpoint_id === this.replica?.destination_endpoint_id && m.platform === 'destination')} loadingInstances={instanceStore.loadingInstancesDetails} instances={instanceStore.instancesDetails} onCancelClick={() => { this.handleCloseMigrationModal() }} diff --git a/src/components/pages/ReplicasPage/ReplicasPage.tsx b/src/components/pages/ReplicasPage/ReplicasPage.tsx index ac64404e..3cd36f97 100644 --- a/src/components/pages/ReplicasPage/ReplicasPage.tsx +++ b/src/components/pages/ReplicasPage/ReplicasPage.tsx @@ -184,7 +184,7 @@ class ReplicasPage extends React.Component<{ history: any }, State> { cancelExecutions() { this.state.selectedReplicas.forEach(replica => { const actualReplica = replicaStore.replicas.find(r => r.id === replica.id) - if (actualReplica?.last_execution_status === 'RUNNING') { + if (actualReplica?.last_execution_status === 'RUNNING' || actualReplica?.last_execution_status === 'AWAITING_MINION_ALLOCATIONS') { replicaStore.cancelExecution({ replicaId: replica.id }) } }) @@ -200,7 +200,8 @@ class ReplicasPage extends React.Component<{ history: any }, State> { .find(e => e.id === usableReplica.origin_endpoint_id) const targetEndpoint = endpointStore.endpoints .find(e => e.id === usableReplica.destination_endpoint_id) - return Boolean(originEndpoint && targetEndpoint && this.getStatus(usableReplica) !== 'RUNNING') + const status = this.getStatus(usableReplica) + return Boolean(originEndpoint && targetEndpoint && status !== 'RUNNING' && status !== 'AWAITING_MINION_ALLOCATIONS') } deleteSelectedReplicas() { @@ -303,7 +304,8 @@ class ReplicasPage extends React.Component<{ history: any }, State> { const storeReplica = replicaStore.replicas.find(r => r.id === replica.id) atLeastOneHasExecuteEnabled = atLeastOneHasExecuteEnabled || this.isExecuteEnabled(storeReplica) - atLeaseOneIsRunning = atLeaseOneIsRunning || this.getStatus(storeReplica) === 'RUNNING' + const status = this.getStatus(storeReplica) + atLeaseOneIsRunning = atLeaseOneIsRunning || status === 'RUNNING' || status === 'AWAITING_MINION_ALLOCATIONS' }) const BulkActions: DropdownAction[] = [{ diff --git a/src/plugins/endpoint/default/OptionsSchemaPlugin.ts b/src/plugins/endpoint/default/OptionsSchemaPlugin.ts index 69feb4ae..2e27465a 100644 --- a/src/plugins/endpoint/default/OptionsSchemaPlugin.ts +++ b/src/plugins/endpoint/default/OptionsSchemaPlugin.ts @@ -81,7 +81,7 @@ export const defaultGetDestinationEnv = ( oldOptions?: { [prop: string]: any } | null, ): any => { const env: any = {} - const specialOptions = ['execute_now', 'execute_now_options', 'separate_vm', 'skip_os_morphing', 'description'] + const specialOptions = ['execute_now', 'execute_now_options', 'separate_vm', 'skip_os_morphing', 'description', 'minion_pool_id'] .concat(migrationFields.map(f => f.name)) .concat(executionOptions.map(o => o.name)) .concat(migrationImageOsTypes) diff --git a/src/sources/MigrationSource.ts b/src/sources/MigrationSource.ts index a6428fdd..3ba100f3 100644 --- a/src/sources/MigrationSource.ts +++ b/src/sources/MigrationSource.ts @@ -186,11 +186,11 @@ class MigrationSource { } const updatedSourceEnv = opts.updatedSourceEnv ? sourceParser.getDestinationEnv(opts.updatedSourceEnv) : {} - const sourceMinionPoolId = updatedSourceEnv.minion_pool_id || migration.origin_minion_pool_id + const sourceMinionPoolId = opts?.updatedSourceEnv?.minion_pool_id + || migration.origin_minion_pool_id if (sourceMinionPoolId) { payload.migration.origin_minion_pool_id = sourceMinionPoolId } - delete updatedSourceEnv.minion_pool_id payload.migration.source_environment = { ...sourceEnv, ...updatedSourceEnv, @@ -201,11 +201,11 @@ class MigrationSource { } const updatedDestEnv = opts.updatedDestEnv ? sourceParser.getDestinationEnv(opts.updatedDestEnv) : {} - const destMinionPoolId = updatedDestEnv.minion_pool_id || migration.destination_minion_pool_id + const destMinionPoolId = opts?.updatedDestEnv?.minion_pool_id + || migration.destination_minion_pool_id if (destMinionPoolId) { payload.migration.destination_minion_pool_id = destMinionPoolId } - delete updatedDestEnv.minion_pool_id const updatedDestEnvMappings = updatedDestEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] || {} const oldMappings = migration[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] || {} diff --git a/src/sources/MinionPoolSource.ts b/src/sources/MinionPoolSource.ts index ae2a3960..603703a6 100644 --- a/src/sources/MinionPoolSource.ts +++ b/src/sources/MinionPoolSource.ts @@ -12,8 +12,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import moment from 'moment' - import Api from '../utils/ApiCaller' import configLoader from '../utils/Config' @@ -24,9 +22,8 @@ import { providerTypes } from '../constants' import { SchemaParser } from './Schemas' import { OptionValues } from '../@types/Endpoint' import { MinionPoolAction } from '../stores/MinionPoolStore' -import { Execution, ExecutionTasks } from '../@types/Execution' -import { sortTasks } from './ReplicaSource' -import { ProgressUpdate } from '../@types/Task' +import { Execution } from '../@types/Execution' +import DomUtils from '../utils/DomUtils' const transformFieldsToPayload = (schema: Field[], data: any) => { const payload: any = {} @@ -51,26 +48,29 @@ class MinionPoolSource { type: 'string', }, { - name: 'pool_platform', + name: 'platform', type: 'string', + title: 'Pool Platform', }, { - name: 'pool_name', + name: 'name', type: 'string', required: true, + title: 'Pool Name', }, { - name: 'pool_os_type', + name: 'os_type', type: 'string', required: true, + title: 'Pool OS Type', + default: 'linux', enum: [ { - value: 'linux', label: 'Linux', - }, - { - value: 'windows', + value: 'linux', + }, { label: 'Windows', + value: 'windows', }, ], }, @@ -80,6 +80,38 @@ class MinionPoolSource { minimum: 1, default: 1, }, + { + name: 'maximum_minions', + type: 'integer', + minimum: 1, + default: 1, + }, + { + name: 'minion_max_idle_time', + type: 'integer', + minimum: 0, + default: 3600, + }, + { + name: 'minion_retention_strategy', + type: 'string', + default: 'delete', + enum: [ + { + value: 'delete', + label: 'Delete', + }, + { + value: 'poweroff', + label: 'Power Off', + }, + ], + }, + { + name: 'skip_allocation', + type: 'boolean', + nullableBoolean: false, + }, { name: 'notes', type: 'string', @@ -92,12 +124,27 @@ class MinionPoolSource { url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools`, skipLog: options?.skipLog, }) - return response.data.minion_pools + const minionPools: MinionPool[] = response.data.minion_pools + minionPools.sort((a, b) => new Date(b.updated_at || b.created_at || '').getTime() + - new Date(a.updated_at || a.created_at || '').getTime()) + return minionPools + } + + async loadMinionPoolDetails( + id: string, + options?: { skipLog?: boolean }, + ): Promise<MinionPoolDetails> { + const response = await Api.send({ + url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${id}`, + skipLog: options?.skipLog, + }) + return response.data.minion_pool } async loadEnvOptions(endpointId: string, platform: 'source' | 'destination', useCache?: boolean): Promise<OptionValues[]> { + const env = DomUtils.encodeToBase64Url({ list_all_destination_networks: true }) const response = await Api.send({ - url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpointId}/${platform}-minion-pool-options`, + url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/endpoints/${endpointId}/${platform}-minion-pool-options?env=${env}`, cache: useCache, }) return response.data[`${platform}_minion_pool_options`] @@ -115,6 +162,8 @@ class MinionPoolSource { if (schema) { fields = SchemaParser.optionsSchemaToFields(providerName, schema, `${providerName}-minion-pool`) } + // Remove this field, as all networks are always listed + fields = fields.filter(f => f.name !== 'list_all_destination_networks') return fields } catch (err) { console.error(err) @@ -129,6 +178,7 @@ class MinionPoolSource { endpoint_id: endpointId, environment_options: { ...transformFieldsToPayload(envSchema, data), + list_all_destination_networks: true, }, }, } @@ -146,6 +196,7 @@ class MinionPoolSource { ...transformFieldsToPayload(defaultSchema, data), environment_options: { ...transformFieldsToPayload(envSchema, data), + list_all_destination_networks: true, }, }, } @@ -157,9 +208,18 @@ class MinionPoolSource { return response.data.minion_pool } - async runAction(minionPoolId: string, minionPoolAction: MinionPoolAction): Promise<Execution> { + async runAction( + minionPoolId: string, + minionPoolAction: MinionPoolAction, + actionOptions?: any, + ): Promise<Execution> { const payload: any = {} - payload[minionPoolAction] = null + + if (actionOptions) { + payload[minionPoolAction] = { ...actionOptions } + } else { + payload[minionPoolAction] = null + } const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/actions`, @@ -169,41 +229,6 @@ class MinionPoolSource { return response.data.execution } - async getMinionPoolDetails( - minionPoolId: string, - options?: { skipLog?: boolean }, - ): Promise<MinionPoolDetails> { - const response = await Api.send({ - url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}`, - skipLog: options?.skipLog, - }) - const minionPool: MinionPoolDetails = response.data.minion_pool - minionPool.executions.sort((a, b) => a.number - b.number) - return minionPool - } - - async cancelExecution(minionPoolId: string, force?: boolean, executionId?: string) { - let usableExecutionId = executionId - if (!usableExecutionId) { - const details = await this.getMinionPoolDetails(minionPoolId) - const lastExecution = details.executions[details.executions.length - 1] - - if (!lastExecution) { - return null - } - usableExecutionId = lastExecution.id - } - - const payload: any = { cancel: { force: force || false } } - - await Api.send({ - url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/executions/${usableExecutionId}/actions`, - method: 'POST', - data: payload, - }) - return null - } - async deleteMinionPool(minionPoolId: string) { const response = await Api.send({ url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}`, @@ -211,32 +236,6 @@ class MinionPoolSource { }) return response.data.execution } - - async getExecutionTasks(options: { - minionPoolId: string, - executionId?: string, - skipLog?: boolean, - }): Promise<ExecutionTasks> { - const { - minionPoolId, executionId, skipLog, - } = options - - const response = await Api.send({ - url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/minion_pools/${minionPoolId}/executions/${executionId}`, - skipLog, - quietError: true, - }) - const execution: ExecutionTasks = response.data.execution - const sortTaskUpdates = (updates: ProgressUpdate[]) => { - if (!updates) { - return - } - updates.sort((a, b) => moment(a.created_at) - .toDate().getTime() - moment(b.created_at).toDate().getTime()) - } - sortTasks(execution.tasks, sortTaskUpdates) - return execution - } } export default new MinionPoolSource() diff --git a/src/sources/ReplicaSource.ts b/src/sources/ReplicaSource.ts index 903c213f..7bfa7202 100644 --- a/src/sources/ReplicaSource.ts +++ b/src/sources/ReplicaSource.ts @@ -229,9 +229,8 @@ class ReplicaSource { } if (Object.keys(updateData.source).length > 0) { const sourceEnv = parser.getDestinationEnv(updateData.source, replica.source_environment) - if (sourceEnv.minion_pool_id) { - payload.replica.origin_minion_pool_id = sourceEnv.minion_pool_id - delete sourceEnv.minion_pool_id + if (updateData.source.minion_pool_id !== undefined) { + payload.replica.origin_minion_pool_id = updateData.source.minion_pool_id } if (Object.keys(sourceEnv).length) { payload.replica.source_environment = sourceEnv @@ -250,9 +249,8 @@ class ReplicaSource { } delete destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] - if (destEnv.minion_pool_id) { - payload.replica.destination_minion_pool_id = destEnv.minion_pool_id - delete destEnv.minion_pool_id + if (updateData.destination.minion_pool_id !== undefined) { + payload.replica.destination_minion_pool_id = updateData.destination.minion_pool_id } if (Object.keys(destEnv).length) { payload.replica.destination_environment = destEnv diff --git a/src/sources/WizardSource.ts b/src/sources/WizardSource.ts index 94ae32ec..3a803a3b 100644 --- a/src/sources/WizardSource.ts +++ b/src/sources/WizardSource.ts @@ -53,17 +53,15 @@ class WizardSource { if (data.sourceOptions) { const sourceEnv = sourceParser.getDestinationEnv(data.sourceOptions) - if (sourceEnv.minion_pool_id) { - payload[type].origin_minion_pool_id = sourceEnv.minion_pool_id - delete sourceEnv.minion_pool_id + if (data.sourceOptions.minion_pool_id) { + payload[type].origin_minion_pool_id = data.sourceOptions.minion_pool_id } payload[type].source_environment = sourceEnv } const destEnv = destParser.getDestinationEnv(data.destOptions) - if (destEnv.minion_pool_id) { - payload[type].destination_minion_pool_id = destEnv.minion_pool_id - delete destEnv.minion_pool_id + if (data.destOptions?.minion_pool_id) { + payload[type].destination_minion_pool_id = data.destOptions.minion_pool_id } const poolMappings = destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS] diff --git a/src/stores/MinionPoolStore.ts b/src/stores/MinionPoolStore.ts index f7073125..d2cc3450 100644 --- a/src/stores/MinionPoolStore.ts +++ b/src/stores/MinionPoolStore.ts @@ -20,9 +20,12 @@ import MinionPoolSource from '../sources/MinionPoolSource' import { Field } from '../@types/Field' import { ProviderTypes } from '../@types/Providers' import { OptionsSchemaPlugin } from '../plugins/endpoint' -import { ExecutionTasks } from '../@types/Execution' -export type MinionPoolAction = 'set-up-shared-resources' | 'allocate-machines' | 'deallocate-machines' | 'tear-down-shared-resources' +export type MinionPoolAction = 'allocate' | 'deallocate' | 'refresh' + +export const MinionPoolStoreUtils = { + isActive: (minionPool: MinionPool) => minionPool.status === 'ALLOCATED' || minionPool.status === 'SCALING' || minionPool.status === 'RESCALING', +} class MinionPoolStore { @observable @@ -32,29 +35,23 @@ class MinionPoolStore { minionPools: MinionPool[] = [] @observable - loadingMinionPoolSchema: boolean = false + loadingMinionPoolDetails: boolean = false @observable - minionPoolDefaultSchema: Field[] = [] + minionPoolDetails: MinionPoolDetails | null = null @observable - minionPoolEnvSchema: Field[] = [] + loadingMinionPoolSchema: boolean = false @observable - loadingMinionPoolDetails: boolean = false + minionPoolDefaultSchema: Field[] = [] @observable - minionPoolDetails: MinionPoolDetails | null = null + minionPoolEnvSchema: Field[] = [] @observable loadingEnvOptions: boolean = false - @observable - executionsTasks: ExecutionTasks[] = [] - - @observable - loadingExecutionsTasks: boolean = false - @computed get minionPoolCombinedSchema() { return this.minionPoolDefaultSchema.concat(this.minionPoolEnvSchema) @@ -84,14 +81,15 @@ class MinionPoolStore { if (options?.showLoading) { this.loadingMinionPoolDetails = true } + try { - const minionPoolDetails = await MinionPoolSource.getMinionPoolDetails( + const minionPool = await MinionPoolSource.loadMinionPoolDetails( id, { skipLog: options?.skipLog }, ) runInAction(() => { - this.minionPoolDetails = minionPoolDetails + this.minionPoolDetails = minionPool }) } finally { runInAction(() => { @@ -100,8 +98,7 @@ class MinionPoolStore { } } - @action - clearMinionPoolDetails() { + @action clearMinionPoolDetails() { this.minionPoolDetails = null } @@ -169,63 +166,13 @@ class MinionPoolStore { } @action - async runAction(minionPoolId: string, minionPoolAction: MinionPoolAction) { - return MinionPoolSource.runAction(minionPoolId, minionPoolAction) - } - - @action - async cancelExecution(minionPoolId: string, force?: boolean, executionId?: string) { - return MinionPoolSource.cancelExecution(minionPoolId, force, executionId) + async runAction(minionPoolId: string, minionPoolAction: MinionPoolAction, actionOptions?: any) { + return MinionPoolSource.runAction(minionPoolId, minionPoolAction, actionOptions) } async deleteMinionPool(minionPoolId: string) { return MinionPoolSource.deleteMinionPool(minionPoolId) } - - private currentlyLoadingExecution: string = '' - - @action - async loadExecutionTasks( - options: { - minionPoolId: string, - executionId?: string, - skipLog?: boolean, - }, - ) { - const { - minionPoolId, executionId, skipLog, - } = options - - if (!skipLog && this.currentlyLoadingExecution === executionId) { - return - } - this.currentlyLoadingExecution = skipLog ? this.currentlyLoadingExecution : executionId || '' - if (!this.currentlyLoadingExecution) { - return - } - - if (!this.executionsTasks.find(e => e.id === this.currentlyLoadingExecution)) { - this.loadingExecutionsTasks = true - } - - try { - const executionTasks = await MinionPoolSource.getExecutionTasks({ - minionPoolId, executionId: this.currentlyLoadingExecution, skipLog, - }) - runInAction(() => { - this.executionsTasks = [ - ...this.executionsTasks.filter(e => e.id !== this.currentlyLoadingExecution), - executionTasks, - ] - }) - } catch (err) { - console.error(err) - } finally { - runInAction(() => { - this.loadingExecutionsTasks = false - }) - } - } } export default new MinionPoolStore() diff --git a/src/utils/ObjectUtils.ts b/src/utils/ObjectUtils.ts index 446b78bf..65f52d69 100644 --- a/src/utils/ObjectUtils.ts +++ b/src/utils/ObjectUtils.ts @@ -97,6 +97,10 @@ class ObjectUtils { || fieldName.toLowerCase().indexOf('password') > -1 return typeof value === 'string' && !isPassword ? value.trim() : value } + + static capitalizeFirstLetter(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1) + } } export default ObjectUtils