/* Copyright (C) 2017 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 { observer } from "mobx-react"; import React from "react"; import styled, { css } from "styled-components"; import AssessedVmListItem from "@src/components/modules/AssessmentModule/AssessedVmListItem"; import DetailsNavigation from "@src/components/modules/NavigationModule/DetailsNavigation"; import { ThemePalette, ThemeProps } from "@src/components/Theme"; import Button from "@src/components/ui/Button"; import Checkbox from "@src/components/ui/Checkbox"; import DropdownFilter from "@src/components/ui/Dropdowns/DropdownFilter"; import DropdownLink from "@src/components/ui/Dropdowns/DropdownLink"; import SmallLoading from "@src/components/ui/SmallLoading"; import StatusImage from "@src/components/ui/StatusComponents/StatusImage"; import Table from "@src/components/ui/Table"; import DateUtils from "@src/utils/DateUtils"; import arrowImage from "./images/arrow.svg"; import azureMigrateImage from "./images/logo.svg"; import type { Assessment, VmItem, AzureLocation } from "@src/@types/Assessment"; import type { Endpoint } from "@src/@types/Endpoint"; import type { Instance, Nic } from "@src/@types/Instance"; import type { Network, NetworkMap } from "@src/@types/Network"; const Wrapper = styled.div` display: flex; justify-content: center; `; const Buttons = styled.div` margin-top: 46px; display: flex; flex-direction: column; button:first-child { margin-bottom: 16px; } `; const DetailsBody = styled.div` ${ThemeProps.exactWidth(ThemeProps.contentWidth)} margin-bottom: 32px; `; const Columns = styled.div` display: flex; margin-left: -32px; `; const Column = styled.div` width: 50%; margin-left: 32px; `; const Row = styled.div` margin-bottom: 32px; `; const Field = styled.div` display: flex; flex-direction: column; `; const Label = styled.div` font-size: 10px; color: ${ThemePalette.grayscale[3]}; font-weight: ${ThemeProps.fontWeights.medium}; text-transform: uppercase; `; const Value = styled.div` display: ${props => (props.flex ? "flex" : "inline-table")}; margin-top: 3px; ${props => (props.capitalize ? "text-transform: capitalize;" : "")} `; const AzureMigrateLogo = styled.div` width: 208px; height: 32px; background: url("${azureMigrateImage}") center no-repeat; `; const LoadingWrapper = styled.div` display: flex; flex-direction: column; align-items: center; margin: 32px 0; `; const LoadingText = styled.div` font-size: 18px; margin-top: 32px; `; const SmallLoadingWrapper = styled.div` display: flex; align-items: center; justify-content: center; `; const SmallLoadingText = styled.div` font-size: 14px; margin-left: 16px; `; const TableStyled = styled(Table)` margin-top: 62px; ${props => props.addWidthPadding ? css` margin-left: -24px; &:after { margin-left: 24px; } ` : ""} `; const TableHeaderStyle = css` margin-left: 24px; `; const TableBodyStyle = css` padding-left: 24px; `; const NetworkItem = styled.div` display: flex; width: 100%; `; const column = () => css` padding-right: 32px; width: 100%; max-width: 25%; `; const NetworkName = styled.div` ${column()} `; const Arrow = styled.div` width: 32px; height: 16px; position: absolute; right: 0; background: url("${arrowImage}") no-repeat; background-position-y: center; `; const ColumnStub = styled.div` ${column()} position: relative; &:last-child { padding-right: 0; } `; const VmHeaderItem = styled.div` display: flex; font-size: 14px; `; const VmHeaderItemLabel = styled.div` font-size: 10px; margin-left: 8px; `; const NavigationItems = [ { label: "Details", value: "", }, ]; type Props = { item: Assessment | null; detailsLoading: boolean; instancesDetailsLoading: boolean; instancesLoading: boolean; networksLoading: boolean; instancesDetailsProgress: number | null; targetEndpoint: Endpoint; targetEndpoints: Endpoint[]; onTargetEndpointChange: (endpoint: Endpoint) => void; targetEndpointsLoading: boolean; sourceEndpoints: Endpoint[]; sourceEndpoint: Endpoint | null; sourceEndpointsLoading: boolean; locations: AzureLocation[]; selectedLocation: string | null; onLocationChange: (locationName: string) => void; selectedResourceGroup: string; resourceGroups: string[]; onResourceGroupChange: (resourceGroupName: string) => void; targetOptionsLoading: boolean; assessedVmsCount: number; filteredAssessedVms: VmItem[]; selectedVms: string[]; instancesDetails: Instance[]; instances: Instance[]; loadingVmSizes: boolean; vmSizes: string[]; onVmSizeChange: (vmId: string, size: string) => void; onGetSelectedVmSize: (vm: VmItem) => string | null; networks: Network[]; page: string; onSourceEndpointChange: (endpoint: Endpoint) => void; onVmSearchValueChange: (value: string) => void; vmSearchValue: string; onVmSelectedChange: (vm: VmItem, selected: boolean) => void; onNetworkChange: (sourceNic: Nic, targetNetwork: Network) => void; onRefresh: () => void; onMigrateClick: () => void; selectedNetworks: NetworkMap[]; selectAllVmsChecked: boolean; onSelectAllVmsChange: (selected: boolean) => void; }; @observer class AssessmentDetailsContent extends React.Component { static defaultProps = { page: "", }; doesVmMatchSource(vm: VmItem) { if ( !this.props.sourceEndpoint || !this.props.sourceEndpoint.connection_info ) { return false; } if ( this.props.instances.length > 0 && !this.props.instances.find( i => i.name === vm.properties.displayName || i.instance_name === vm.properties.displayName ) ) { return false; } return ( this.props.sourceEndpoint.connection_info.host === vm.properties.datacenterManagementServerName ); } renderMainDetails() { if (this.props.page !== "" || !this.props.item || !this.props.item.id) { return null; } const status = this.props.item ? this.props.item.properties.status === "Completed" ? "Ready for Migration" : this.props.item.properties.status : ""; const locationItem: AzureLocation | undefined = this.props.locations.find( l => l.id === this.props.selectedLocation ); return ( {this.props.item ? DateUtils.getLocalDate( this.props.item.properties.updatedTimestamp ).toFormat("yyyy-LL-dd HH:mm:ss") : "-"} {this.props.item ? this.props.item.projectName : ""} {this.props.item ? this.props.item.groupName : ""} {status} ({ label: endpoint.name, value: endpoint.id, endpoint, }))} onChange={item => { this.props.onSourceEndpointChange(item.endpoint); }} selectItemLabel="Select Endpoint" noItemsLabel={ this.props.sourceEndpointsLoading ? "Loading ...." : "No matching endpoints" } /> ({ label: endpoint.name, value: endpoint.id, endpoint, }))} onChange={item => { this.props.onTargetEndpointChange(item.endpoint); }} selectItemLabel="Select Endpoint" noItemsLabel={ this.props.targetEndpointsLoading ? "Loading ...." : "No Azure endpoints" } /> ({ label: group, value: group, }))} onChange={item => { this.props.onResourceGroupChange(item.value); }} noItemsLabel={ this.props.targetOptionsLoading ? "Loading ...." : "No Resource Groups found" } /> ({ label: location.name, value: location.id, }))} onChange={item => { this.props.onLocationChange(item.value); }} noItemsLabel={ this.props.targetOptionsLoading ? "Loading ...." : "No Locations found" } /> ); } renderVmsTable() { const loading = this.props.instancesLoading; const items = this.props.filteredAssessedVms.map(vm => ( m === vm.properties.displayName) .length > 0 } onSelectedChange={(selectedVm, selected) => { this.props.onVmSelectedChange(selectedVm, selected); }} disabled={!this.doesVmMatchSource(vm)} loadingVmSizes={this.props.loadingVmSizes} recommendedVmSize={vm.properties.recommendedSize} vmSizes={this.props.vmSizes} selectedVmSize={this.props.onGetSelectedVmSize(vm)} onVmSizeChange={size => { this.props.onVmSizeChange(vm.properties.displayName, size); }} /> )); const vmCountLabel = `(${ this.props.filteredAssessedVms.length === this.props.assessedVmsCount ? this.props.assessedVmsCount : `${this.props.filteredAssessedVms.length} OUT OF ${this.props.assessedVmsCount}` })`; const vmHeaderItem = ( {loading ? null : ( { this.props.onSelectAllVmsChange(checked); }} /> )} Virtual Machine {vmCountLabel} { this.props.onVmSearchValueChange(value); }} /> ); return ( ); } renderNetworkTable() { const loading = this.props.networksLoading || this.props.instancesDetailsLoading; if (loading) { return ( ); } const nics: Nic[] = []; this.props.instancesDetails.forEach(instance => { if (!instance.devices || !instance.devices.nics) { return; } instance.devices.nics.forEach(nic => { if (nics.find(n => n.network_name === nic.network_name)) { return; } nics.push(nic); }); }); if (nics.length === 0) { return null; } const items = nics.map(nic => { let selectedNetworkName: string | undefined; const selectedNetwork = this.props.selectedNetworks && this.props.selectedNetworks.find( n => n.sourceNic.network_name === nic.network_name ); if (selectedNetwork) { selectedNetworkName = selectedNetwork.targetNetwork?.name; } return ( {nic.network_name} { this.props.onNetworkChange(nic, item.network); }} items={this.props.networks.map(network => ({ value: network.name || "", label: network.name || "", network, }))} /> ); }); return ( ); } renderNetworksLoading() { let loadingProgress = -1; if (this.props.instancesDetailsLoading) { if (this.props.instancesDetailsProgress != null) { loadingProgress = Math.round(this.props.instancesDetailsProgress * 100); } } return ( Loading networks, please wait ... ); } renderButtons() { return ( ); } renderLoading(message: string) { return ( {message} ); } render() { return ( "#"} /> {this.props.detailsLoading ? null : this.renderMainDetails()} {this.props.detailsLoading ? this.renderLoading("Loading assessment...") : null} {this.props.detailsLoading ? null : this.renderVmsTable()} {this.props.detailsLoading || this.props.instancesLoading ? null : this.renderNetworkTable()} {this.props.detailsLoading ? null : this.renderButtons()} ); } } export default AssessmentDetailsContent;