diff --git a/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue b/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue index dd57e8e2..5311ddee 100644 --- a/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue +++ b/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue @@ -2,11 +2,12 @@ import { ref, computed, watch } from 'vue'; import { useStore } from 'vuex'; import Loading from '@shell/components/Loading'; +import SortableTable from '@shell/components/SortableTable'; import { Banner } from '@components/Banner'; -import { RcItemCard } from '@components/RcItemCard'; +import { BadgeState } from '@components/BadgeState'; +import { RcDropdown, RcDropdownItem, RcDropdownTrigger } from '@components/RcDropdown'; import { LabeledInput } from '@components/Form/LabeledInput'; import LabeledSelect from '@shell/components/form/LabeledSelect'; -import MappingsCell from '../MappingsCell'; import { useI18n } from '@shell/composables/useI18n'; import { NAMESPACE } from '@shell/config/types'; import { HCI } from '../../types'; @@ -23,12 +24,12 @@ const props = defineProps({ stepData: { type: Object, required: true }, }); -const emit = defineEmits(['ready']); +const emit = defineEmits(['ready', 'edit-selection', 'edit-mappings', 'remove-vm']); const store = useStore(); const { t } = useI18n(store); -const vms = ref([]); +const vms = computed(() => props.selectedVms); const networkMappings = ref([]); const storageMappings = ref([]); const planName = ref(''); @@ -87,7 +88,7 @@ const totalStorageGB = computed(() => { return bytesToGB(totalBytes); }); -const vmCards = computed(() => { +const vmRows = computed(() => { return vms.value.map((vm) => { const cpus = vm.cpuCount || vm.numCPU || 0; const memMB = vm.memoryMB || vm.memory || 0; @@ -101,23 +102,93 @@ const vmCards = computed(() => { const diskDisplay = totalDiskBytes ? t('harvester.addons.vmMigration.generic.memoryGb', { value: bytesToGB(totalDiskBytes) }) : '-'; const os = vm.guestName || vm.guestOS || vm.os || '-'; + const rawPowerState = vm.powerState || vm.status?.phase || '-'; + const powerState = rawPowerState.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, (c) => c.toUpperCase()); + const powerStateClass = powerState.toLowerCase().includes('on') || powerState.toLowerCase().includes('running') ? 'power-on' : 'power-off'; const vmNetworkMappings = networkMappings.value.filter((m) => m.usedBy && m.usedBy.includes(vm.name || vm.id)); const vmStorageMappings = storageMappings.value.filter((m) => m.usedBy && m.usedBy.includes(vm.name || vm.id)); + const networkMappingSort = vmNetworkMappings.map((m) => `${ m.source } ${ formatNetworkTarget(m.target) }`).join(', '); + const storageMappingSort = vmStorageMappings.map((m) => `${ m.source } ${ m.target }`).join(', '); + return { + _key: vm.id || vm.vmId || vm.metadata?.name, id: vm.id, name: vm.name || vm.id, + identifier: vm.id || vm.vmId || vm.metadata?.name || '-', os, - cpus, - memGB, - diskDisplay, + resources: t('harvester.addons.vmMigration.generic.vCpu', { count: cpus }), + resourcesSub: `${ memGB } • ${ diskDisplay }`, + powerState, + powerStateClass, networkMappings: vmNetworkMappings, storageMappings: vmStorageMappings, + networkMapping: networkMappingSort, + storageMapping: storageMappingSort, }; }); }); +const vmHeaders = [ + { + name: 'vmName', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.vmName', + value: 'name', + sort: ['name'], + subLabel: t('harvester.addons.vmMigration.generic.identifier'), + width: 200, + }, + { + name: 'os', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.os', + value: 'os', + sort: ['os'], + width: 140, + }, + { + name: 'resources', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.resources', + value: 'resources', + sort: false, + width: 120, + }, + { + name: 'powerState', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.powerState', + value: 'powerState', + sort: ['powerState'], + width: 100, + }, + { + name: 'networkMapping', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.networkMapping', + value: 'networkMapping', + sort: ['networkMapping'], + subLabel: t('harvester.addons.vmMigration.reviewMigration.columns.sourceTarget'), + width: 220, + }, + { + name: 'storageMapping', + labelKey: 'harvester.addons.vmMigration.reviewMigration.columns.storageMapping', + value: 'storageMapping', + sort: ['storageMapping'], + subLabel: t('harvester.addons.vmMigration.reviewMigration.columns.sourceTarget'), + width: 220, + }, + { + name: 'actions', + label: ' ', + align: 'right', + sort: false, + width: 40, + }, +]; + +const onEditSelection = () => emit('edit-selection'); +const onEditMappings = () => emit('edit-mappings'); +const onRemoveVm = (id) => emit('remove-vm', id); + const startMigrationAction = async() => { const inStore = store.getters['currentProduct'].inStore; @@ -201,8 +272,6 @@ const startMigrationAction = async() => { }; const init = async() => { - vms.value = props.selectedVms; - const inStore = store.getters['currentProduct'].inStore; try { @@ -246,10 +315,23 @@ defineExpose({ startMigration: startMigrationAction }); >
-

- {{ t('harvester.addons.vmMigration.reviewMigration.migrationDetails') }} -

+
+

+ {{ t('harvester.addons.vmMigration.reviewMigration.migrationDetails') }} +

+ + {{ t('harvester.addons.vmMigration.reviewMigration.migrationDetailsDescription') }} + +
+
+ -
-
-
-
- {{ t('harvester.addons.vmMigration.reviewMigration.totalVms') }} - {{ vms.length }} -
+ +
+
+ {{ t('harvester.addons.vmMigration.reviewMigration.migrationMode') }} + {{ t('harvester.addons.vmMigration.reviewMigration.coldMigration') }} + {{ t('harvester.addons.vmMigration.reviewMigration.coldMigrationDescription') }} + {{ t('harvester.addons.vmMigration.reviewMigration.coldMigrationHint') }}
-
-
- {{ t('harvester.addons.vmMigration.reviewMigration.source') }} -
-
- {{ providerName }} -
-
-
-
- {{ t('harvester.addons.vmMigration.reviewMigration.targetNamespace') }} -
-
- {{ targetNamespace }} -
-
-
-
- {{ t('harvester.addons.vmMigration.reviewMigration.vcpu') }} - {{ totalVCpu }} -
-
- {{ t('harvester.addons.vmMigration.reviewMigration.memory') }} - {{ t('harvester.addons.vmMigration.generic.memoryGb', { value: totalMemoryGB }) }} -
-
- {{ t('harvester.addons.vmMigration.reviewMigration.storage') }} - {{ t('harvester.addons.vmMigration.generic.memoryGb', { value: totalStorageGB }) }} -
-
-
-
- {{ t('harvester.addons.vmMigration.reviewMigration.migrationMode') }} -
-
- - {{ t('harvester.addons.vmMigration.reviewMigration.coldMigration') }} - -
+ +
+ {{ t('harvester.addons.vmMigration.reviewMigration.provider') }} + {{ providerName }} + {{ t('harvester.addons.vmMigration.reviewMigration.totalVms') }} + {{ vms.length }} + {{ t('harvester.addons.vmMigration.reviewMigration.vcpu') }} + {{ totalVCpu }} + {{ t('harvester.addons.vmMigration.reviewMigration.memory') }} + {{ t('harvester.addons.vmMigration.generic.memoryGb', { value: totalMemoryGB }) }} + {{ t('harvester.addons.vmMigration.reviewMigration.storage') }} + {{ t('harvester.addons.vmMigration.generic.memoryGb', { value: totalStorageGB }) }}
-

- {{ t('harvester.addons.vmMigration.reviewMigration.virtualMachines') }} ({{ vms.length }}) -

- -
- -