/*
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;