mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 12:49:14 +00:00
feat(forklift): Fixed some UI changes
Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com>
This commit is contained in:
parent
de7598b7af
commit
1d26c48117
@ -2,11 +2,10 @@
|
|||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import Loading from '@shell/components/Loading';
|
import Loading from '@shell/components/Loading';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
||||||
import { RcItemCard } from '@components/RcItemCard';
|
|
||||||
import { STORAGE_CLASS, NETWORK_ATTACHMENT } from '@shell/config/types';
|
import { STORAGE_CLASS, NETWORK_ATTACHMENT } from '@shell/config/types';
|
||||||
import { useI18n } from '@shell/composables/useI18n';
|
import { useI18n } from '@shell/composables/useI18n';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
|
import MappingColumn from './MappingColumn.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
providerName: { type: String, default: '' },
|
providerName: { type: String, default: '' },
|
||||||
@ -268,7 +267,7 @@ const formatStorageDetail = (entry) => {
|
|||||||
|
|
||||||
const buildNetworkMapSpec = (providerRef) => {
|
const buildNetworkMapSpec = (providerRef) => {
|
||||||
return {
|
return {
|
||||||
map: networkEntries.value.map((entry) => {
|
map: networkEntries.value.filter((entry) => !!entry.target).map((entry) => {
|
||||||
if (entry.target === 'pod') {
|
if (entry.target === 'pod') {
|
||||||
return {
|
return {
|
||||||
source: { name: entry.name, id: entry.id },
|
source: { name: entry.name, id: entry.id },
|
||||||
@ -302,7 +301,7 @@ const buildNetworkMapSpec = (providerRef) => {
|
|||||||
|
|
||||||
const buildStorageMapSpec = (providerRef) => {
|
const buildStorageMapSpec = (providerRef) => {
|
||||||
return {
|
return {
|
||||||
map: storageEntries.value.map((entry) => ({
|
map: storageEntries.value.filter((entry) => !!entry.target).map((entry) => ({
|
||||||
source: { name: entry.name, id: entry.id },
|
source: { name: entry.name, id: entry.id },
|
||||||
destination: { storageClass: entry.target },
|
destination: { storageClass: entry.target },
|
||||||
})),
|
})),
|
||||||
@ -478,16 +477,23 @@ const init = async() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.existingNetworkMap?.spec?.map) {
|
const hasExistingNetworkTargets = networkEntries.value.some((e) => !!e.target);
|
||||||
applyNetworkMapTargets(props.existingNetworkMap.spec.map);
|
const hasExistingStorageTargets = storageEntries.value.some((e) => !!e.target);
|
||||||
} else {
|
|
||||||
applyDefaultNetworkMap();
|
if (!hasExistingNetworkTargets) {
|
||||||
|
if (props.existingNetworkMap?.spec?.map) {
|
||||||
|
applyNetworkMapTargets(props.existingNetworkMap.spec.map);
|
||||||
|
} else {
|
||||||
|
applyDefaultNetworkMap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.existingStorageMap?.spec?.map) {
|
if (!hasExistingStorageTargets) {
|
||||||
applyStorageMapTargets(props.existingStorageMap.spec.map);
|
if (props.existingStorageMap?.spec?.map) {
|
||||||
} else {
|
applyStorageMapTargets(props.existingStorageMap.spec.map);
|
||||||
applyDefaultStorageMap();
|
} else {
|
||||||
|
applyDefaultStorageMap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@ -502,114 +508,45 @@ init();
|
|||||||
v-else
|
v-else
|
||||||
class="configure-mappings"
|
class="configure-mappings"
|
||||||
>
|
>
|
||||||
<p class="text-muted line-height-20">
|
<p class="text-deemphasized line-height-20">
|
||||||
{{ t('harvester.addons.vmMigration.configureMappings.description') }}
|
{{ t('harvester.addons.vmMigration.configureMappings.description') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="mappings-columns">
|
<div class="mappings-columns">
|
||||||
<!-- Network Mapping -->
|
<MappingColumn
|
||||||
<div class="mapping-column">
|
:title="t('harvester.addons.vmMigration.configureMappings.networkMapping.title')"
|
||||||
<div>
|
:description="t('harvester.addons.vmMigration.configureMappings.networkMapping.description')"
|
||||||
<h3 class="mapping-section-title">
|
:entries="networkEntries"
|
||||||
{{ t('harvester.addons.vmMigration.configureMappings.networkMapping.title') }}
|
:options="harvesterNetworkOptions"
|
||||||
</h3>
|
:placeholder="t('harvester.addons.vmMigration.configureMappings.networkMapping.placeholder')"
|
||||||
<p class="text-muted line-height-20">
|
:show-used-by="!useAllProviderData"
|
||||||
{{ t('harvester.addons.vmMigration.configureMappings.networkMapping.description') }}
|
:clearable="useAllProviderData"
|
||||||
</p>
|
>
|
||||||
</div>
|
<template #source-detail="{ entry }">
|
||||||
|
<span class="source-detail text-deemphasized">VLAN {{ entry.vlanId || '0' }}</span>
|
||||||
|
</template>
|
||||||
|
</MappingColumn>
|
||||||
|
|
||||||
<RcItemCard
|
<MappingColumn
|
||||||
v-for="entry in networkEntries"
|
:title="t('harvester.addons.vmMigration.configureMappings.storageMapping.title')"
|
||||||
:id="entry._key"
|
:description="t('harvester.addons.vmMigration.configureMappings.storageMapping.description')"
|
||||||
:key="entry._key"
|
:entries="storageEntries"
|
||||||
:variant="'small'"
|
:options="storageClassOptions"
|
||||||
:header="{}"
|
:placeholder="t('harvester.addons.vmMigration.configureMappings.storageMapping.placeholder')"
|
||||||
class="bg-light-gray"
|
:show-used-by="!useAllProviderData"
|
||||||
>
|
:clearable="useAllProviderData"
|
||||||
<template #item-card-content>
|
>
|
||||||
<div class="card-content-column">
|
<template #source-detail="{ entry }">
|
||||||
<div class="card-content-row">
|
<span
|
||||||
<div class="mapping-source">
|
v-if="entry.type || entry.capacity"
|
||||||
<span class="source-name">{{ entry.name }}</span>
|
class="source-detail text-deemphasized"
|
||||||
<span class="source-detail text-muted">VLAN {{ entry.vlanId || '0' }}</span>
|
>{{ formatStorageDetail(entry) }}</span>
|
||||||
</div>
|
</template>
|
||||||
<div :class="['mapping-arrow', entry.target ? 'text-success' : 'text-muted']">
|
</MappingColumn>
|
||||||
<i class="icon icon-right-arrow-alt" />
|
|
||||||
</div>
|
|
||||||
<div class="mapping-target">
|
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="entry.target"
|
|
||||||
:options="harvesterNetworkOptions"
|
|
||||||
:placeholder="t('harvester.addons.vmMigration.configureMappings.networkMapping.placeholder')"
|
|
||||||
:searchable="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!useAllProviderData && entry.usedBy.length">
|
|
||||||
<span class="used-by">
|
|
||||||
Used by: <b>{{ entry.usedBy.join(', ') }}</b>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</RcItemCard>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Storage Mapping -->
|
|
||||||
<div class="mapping-column">
|
|
||||||
<div>
|
|
||||||
<h3 class="mapping-section-title">
|
|
||||||
{{ t('harvester.addons.vmMigration.configureMappings.storageMapping.title') }}
|
|
||||||
</h3>
|
|
||||||
<p class="text-muted">
|
|
||||||
{{ t('harvester.addons.vmMigration.configureMappings.storageMapping.description') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<RcItemCard
|
|
||||||
v-for="entry in storageEntries"
|
|
||||||
:id="entry._key"
|
|
||||||
:key="entry._key"
|
|
||||||
:variant="'small'"
|
|
||||||
:header="{}"
|
|
||||||
class="bg-light-gray"
|
|
||||||
>
|
|
||||||
<template #item-card-content>
|
|
||||||
<div class="card-content-column">
|
|
||||||
<div class="card-content-row">
|
|
||||||
<div class="mapping-source">
|
|
||||||
<span class="source-name">{{ entry.name }}</span>
|
|
||||||
<span
|
|
||||||
v-if="entry.type || entry.capacity"
|
|
||||||
class="source-detail text-muted"
|
|
||||||
>{{ formatStorageDetail(entry) }}</span>
|
|
||||||
</div>
|
|
||||||
<div :class="['mapping-arrow', entry.target ? 'text-success' : 'text-muted']">
|
|
||||||
<i class="icon icon-right-arrow-alt" />
|
|
||||||
</div>
|
|
||||||
<div class="mapping-target">
|
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="entry.target"
|
|
||||||
:options="storageClassOptions"
|
|
||||||
:placeholder="t('harvester.addons.vmMigration.configureMappings.storageMapping.placeholder')"
|
|
||||||
:searchable="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!useAllProviderData && entry.usedBy.length">
|
|
||||||
<span class="used-by">
|
|
||||||
Used by: <b>{{ entry.usedBy.join(', ') }}</b>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</RcItemCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
.mappings-columns {
|
.mappings-columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -629,85 +566,6 @@ init();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapping-column {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
:deep(.item-card-header) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-section-title {
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
line-height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content-column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-source {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 100px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
|
|
||||||
.source-name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-detail {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.used-by {
|
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-arrow {
|
|
||||||
font-size: 22px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-target {
|
|
||||||
flex: 3;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-height-20 {
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-light-gray {
|
|
||||||
background-color: var(--category-active) !important;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.configure-mappings {
|
.configure-mappings {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -718,4 +576,9 @@ init();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.source-detail {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -518,7 +518,7 @@ defineExpose({ testConnection, clickTestButton });
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="configure-provider-step">
|
<div class="configure-provider-step">
|
||||||
<p class="text-muted line-height-20">
|
<p class="text-deemphasized line-height-20">
|
||||||
{{ t('harvester.addons.vmMigration.configureProvider.description') }}
|
{{ t('harvester.addons.vmMigration.configureProvider.description') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -567,7 +567,7 @@ defineExpose({ testConnection, clickTestButton });
|
|||||||
:disabled="isExistingProvider && !editMode"
|
:disabled="isExistingProvider && !editMode"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p class="text-muted mt-5">
|
<p class="text-deemphasized mt-5">
|
||||||
{{ t('harvester.addons.vmMigration.configureProvider.urlHint') }}
|
{{ t('harvester.addons.vmMigration.configureProvider.urlHint') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -598,7 +598,7 @@ defineExpose({ testConnection, clickTestButton });
|
|||||||
:label="t('harvester.addons.vmMigration.configureProvider.skipSsl')"
|
:label="t('harvester.addons.vmMigration.configureProvider.skipSsl')"
|
||||||
:disabled="isExistingProvider && !editMode"
|
:disabled="isExistingProvider && !editMode"
|
||||||
/>
|
/>
|
||||||
<p class="text-muted ml-20">
|
<p class="text-deemphasized ml-20">
|
||||||
{{ t('harvester.addons.vmMigration.configureProvider.skipSslHint') }}
|
{{ t('harvester.addons.vmMigration.configureProvider.skipSslHint') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -670,5 +670,9 @@ defineExpose({ testConnection, clickTestButton });
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.checkbox-label) {
|
||||||
|
color: var(--body-text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
181
pkg/harvester/components/vm-migration/MappingColumn.vue
Normal file
181
pkg/harvester/components/vm-migration/MappingColumn.vue
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import { RcItemCard } from '@components/RcItemCard';
|
||||||
|
import { useI18n } from '@shell/composables/useI18n';
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const { t } = useI18n(store);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: { type: String, required: true },
|
||||||
|
description: { type: String, default: '' },
|
||||||
|
entries: { type: Array, default: () => [] },
|
||||||
|
options: { type: Array, default: () => [] },
|
||||||
|
placeholder: { type: String, default: '' },
|
||||||
|
showUsedBy: { type: Boolean, default: false },
|
||||||
|
clearable: { type: Boolean, default: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectOptions = computed(() => {
|
||||||
|
if (!props.clearable) {
|
||||||
|
return props.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: t('harvester.addons.vmMigration.configureMappings.removeMap'),
|
||||||
|
value: null,
|
||||||
|
kind: 'highlighted',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'divider',
|
||||||
|
disabled: true,
|
||||||
|
kind: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${ props.placeholder }:`,
|
||||||
|
disabled: true,
|
||||||
|
kind: 'title',
|
||||||
|
},
|
||||||
|
...props.options,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mapping-column">
|
||||||
|
<div>
|
||||||
|
<h3 class="mapping-section-title">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<p
|
||||||
|
v-if="description"
|
||||||
|
class="text-deemphasized line-height-20"
|
||||||
|
>
|
||||||
|
{{ description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RcItemCard
|
||||||
|
v-for="entry in entries"
|
||||||
|
:id="entry._key"
|
||||||
|
:key="entry._key"
|
||||||
|
:variant="'small'"
|
||||||
|
:header="{}"
|
||||||
|
class="bg-light-gray"
|
||||||
|
>
|
||||||
|
<template #item-card-content>
|
||||||
|
<div class="card-content-column">
|
||||||
|
<div class="card-content-row">
|
||||||
|
<div class="mapping-source">
|
||||||
|
<span class="source-name">{{ entry.name }}</span>
|
||||||
|
<slot
|
||||||
|
name="source-detail"
|
||||||
|
:entry="entry"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div :class="['mapping-arrow', entry.target ? 'text-success' : 'text-deemphasized']">
|
||||||
|
<i class="icon icon-right-arrow-alt" />
|
||||||
|
</div>
|
||||||
|
<div class="mapping-target">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="entry.target"
|
||||||
|
:options="selectOptions"
|
||||||
|
:placeholder="placeholder+'...'"
|
||||||
|
:searchable="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showUsedBy && entry.usedBy && entry.usedBy.length">
|
||||||
|
<span class="used-by">
|
||||||
|
Used by: <b>{{ entry.usedBy.join(', ') }}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RcItemCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mapping-column {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:deep(.item-card-header) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-section-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-source {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 2;
|
||||||
|
min-width: 100px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
.source-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-detail {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.used-by {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-arrow {
|
||||||
|
font-size: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-target {
|
||||||
|
flex: 3;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-height-20 {
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-light-gray {
|
||||||
|
background-color: var(--category-active) !important;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -338,7 +338,7 @@ defineExpose({ startMigration: startMigrationAction });
|
|||||||
<template #item-card-content>
|
<template #item-card-content>
|
||||||
<div class="vm-card-content">
|
<div class="vm-card-content">
|
||||||
<div class="vm-card-specs">
|
<div class="vm-card-specs">
|
||||||
<span class="vm-os text-muted">{{ vm.os }}</span>
|
<span class="vm-os text-deemphasized">{{ vm.os }}</span>
|
||||||
<span class="vm-resources">
|
<span class="vm-resources">
|
||||||
<i class="icon icon-disk" />
|
<i class="icon icon-disk" />
|
||||||
{{ vm.cpus }} vCPU • {{ vm.memGB }} • {{ vm.diskDisplay }}
|
{{ vm.cpus }} vCPU • {{ vm.memGB }} • {{ vm.diskDisplay }}
|
||||||
|
|||||||
@ -357,6 +357,8 @@ const refreshVMs = async() => {
|
|||||||
emit('complete', { selectedVMs: selectedVMs.value });
|
emit('complete', { selectedVMs: selectedVMs.value });
|
||||||
refreshing.value = false;
|
refreshing.value = false;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
const table = sortableTableRef.value;
|
const table = sortableTableRef.value;
|
||||||
|
|
||||||
if (table) {
|
if (table) {
|
||||||
@ -366,6 +368,8 @@ const refreshVMs = async() => {
|
|||||||
table.update(rowsToReselect, []);
|
table.update(rowsToReselect, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skipNextSelectionEvent = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = async() => {
|
const init = async() => {
|
||||||
@ -403,7 +407,7 @@ init();
|
|||||||
v-else
|
v-else
|
||||||
class="select-vms-step"
|
class="select-vms-step"
|
||||||
>
|
>
|
||||||
<p class="text-muted line-height-20">
|
<p class="text-deemphasized line-height-20">
|
||||||
{{ t('harvester.addons.vmMigration.selectVms.discovered', { count: vmCount }) }}
|
{{ t('harvester.addons.vmMigration.selectVms.discovered', { count: vmCount }) }}
|
||||||
<router-link
|
<router-link
|
||||||
v-if="provider"
|
v-if="provider"
|
||||||
@ -446,18 +450,18 @@ init();
|
|||||||
<h3 class="m-0">
|
<h3 class="m-0">
|
||||||
{{ t('harvester.addons.vmMigration.selectVms.availableVms') }}
|
{{ t('harvester.addons.vmMigration.selectVms.availableVms') }}
|
||||||
</h3>
|
</h3>
|
||||||
<span class="text-muted">{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}</span>
|
<span class="text-deemphasized">{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell:vmName="{ row }">
|
<template #cell:vmName="{ row }">
|
||||||
<div class="vm-name-cell">
|
<div class="vm-name-cell">
|
||||||
<span class="vm-name">{{ row.vmName }}</span>
|
<span class="vm-name">{{ row.vmName }}</span>
|
||||||
<span class="vm-id text-muted">{{ row.vmId }}</span>
|
<span class="vm-id text-deemphasized">{{ row.vmId }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell:resources="{ row }">
|
<template #cell:resources="{ row }">
|
||||||
<span>{{ row.resourcesDisplay }}</span><br>
|
<span>{{ row.resourcesDisplay }}</span><br>
|
||||||
<span class="text-muted">{{ row.resourcesSub }}</span>
|
<span class="text-deemphasized">{{ row.resourcesSub }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #cell:powerState="{ row }">
|
<template #cell:powerState="{ row }">
|
||||||
<BadgeState
|
<BadgeState
|
||||||
@ -473,7 +477,7 @@ init();
|
|||||||
:class="{ 'mt-4': i > 0 }"
|
:class="{ 'mt-4': i > 0 }"
|
||||||
>
|
>
|
||||||
<span>{{ net.name }}</span><br>
|
<span>{{ net.name }}</span><br>
|
||||||
<span class="text-muted">{{ net.id }}</span>
|
<span class="text-deemphasized">{{ net.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -485,7 +489,7 @@ init();
|
|||||||
:class="{ 'mt-4': i > 0 }"
|
:class="{ 'mt-4': i > 0 }"
|
||||||
>
|
>
|
||||||
<span>{{ ds.name }}</span><br>
|
<span>{{ ds.name }}</span><br>
|
||||||
<span class="text-muted">{{ ds.id }}</span>
|
<span class="text-deemphasized">{{ ds.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -531,7 +535,7 @@ init();
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-muted {
|
.text-deemphasized {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1910,10 +1910,11 @@ harvester:
|
|||||||
description: Map VMware networks and datastores to Harvester and Longhorn target resources
|
description: Map VMware networks and datastores to Harvester and Longhorn target resources
|
||||||
save: Save Mappings and Continue
|
save: Save Mappings and Continue
|
||||||
noTemplate: Start from scratch
|
noTemplate: Start from scratch
|
||||||
|
removeMap: Remove Map
|
||||||
networkMapping:
|
networkMapping:
|
||||||
title: Network Mapping
|
title: Network Mapping
|
||||||
description: Map VMware port groups to Harvester networks
|
description: Map VMware port groups to Harvester networks
|
||||||
placeholder: Choose a Harvester network...
|
placeholder: Choose a Harvester network
|
||||||
template: Use existing network mapping as template
|
template: Use existing network mapping as template
|
||||||
storageMapping:
|
storageMapping:
|
||||||
title: Storage Mapping
|
title: Storage Mapping
|
||||||
|
|||||||
@ -132,29 +132,29 @@ export default class ForkliftPlan extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get _availableActions() {
|
get _availableActions() {
|
||||||
|
const canStop = this.isMigrating && !this.planCanceled;
|
||||||
|
const canStart = !this.planSucceeded && (!this.isMigrating || this.planFailed || this.planCanceled || this.planCritical);
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
if (canStart) {
|
||||||
|
out.push({
|
||||||
|
action: 'startMigration',
|
||||||
|
enabled: true,
|
||||||
|
icon: 'icon icon-play',
|
||||||
|
label: this.isForkliftDashboard ? 'Restart' : 'Start',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canStop) {
|
||||||
|
out.push({
|
||||||
|
action: 'stopMigration',
|
||||||
|
enabled: true,
|
||||||
|
icon: 'icon icon-pause',
|
||||||
|
label: 'Stop',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isForkliftDashboard) {
|
if (this.isForkliftDashboard) {
|
||||||
const canStop = this.isMigrating && !this.planCanceled;
|
|
||||||
const canStart = !this.planSucceeded && (!this.isMigrating || this.planFailed || this.planCanceled || this.planCritical);
|
|
||||||
const out = [];
|
|
||||||
|
|
||||||
if (canStart) {
|
|
||||||
out.push({
|
|
||||||
action: 'startMigration',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'icon icon-play',
|
|
||||||
label: 'Start',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canStop) {
|
|
||||||
out.push({
|
|
||||||
action: 'stopMigration',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'icon icon-pause',
|
|
||||||
label: 'Stop',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
action: 'promptRemove',
|
action: 'promptRemove',
|
||||||
altAction: 'remove',
|
altAction: 'remove',
|
||||||
@ -167,9 +167,11 @@ export default class ForkliftPlan extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
} else {
|
||||||
|
out.push(...super._availableActions);
|
||||||
|
|
||||||
return super._availableActions;
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopMigration() {
|
async stopMigration() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user