mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 20:59:16 +00:00
feat(forklift): Design changes
Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com>
This commit is contained in:
parent
6e17a84581
commit
7c8f172012
@ -1846,6 +1846,11 @@ harvester:
|
|||||||
availableVms: Available Virtual Machines
|
availableVms: Available Virtual Machines
|
||||||
selected: selected
|
selected: selected
|
||||||
saveSelection: Save Selection and Continue
|
saveSelection: Save Selection and Continue
|
||||||
|
selectAllBanner:
|
||||||
|
pageOnly: "Your current selection includes only VMs currently shown on the page."
|
||||||
|
selectAll: "Select all {count} VMs from this provider"
|
||||||
|
allSelected: "Your current selection includes all VMs."
|
||||||
|
clearSelection: "Clear Selection"
|
||||||
manualEntry:
|
manualEntry:
|
||||||
title: Add Virtual Machines
|
title: Add Virtual Machines
|
||||||
description: No VMs were discovered from the provider inventory. Add VMs manually by entering their IDs and names.
|
description: No VMs were discovered from the provider inventory. Add VMs manually by entering their IDs and names.
|
||||||
|
|||||||
@ -558,6 +558,7 @@ init();
|
|||||||
:key="entry._key"
|
:key="entry._key"
|
||||||
:variant="'small'"
|
:variant="'small'"
|
||||||
:header="{}"
|
:header="{}"
|
||||||
|
class="bg-light-gray"
|
||||||
>
|
>
|
||||||
<template #item-card-content>
|
<template #item-card-content>
|
||||||
<div class="card-content-column">
|
<div class="card-content-column">
|
||||||
@ -613,6 +614,7 @@ init();
|
|||||||
:key="entry._key"
|
:key="entry._key"
|
||||||
:variant="'small'"
|
:variant="'small'"
|
||||||
:header="{}"
|
:header="{}"
|
||||||
|
class="bg-light-gray"
|
||||||
>
|
>
|
||||||
<template #item-card-content>
|
<template #item-card-content>
|
||||||
<div class="card-content-column">
|
<div class="card-content-column">
|
||||||
@ -747,4 +749,9 @@ init();
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-light-gray {
|
||||||
|
background-color: var(--category-active) !important;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -57,6 +57,24 @@ const providerOptions = computed(() => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
allProviders.value.forEach((p) => {
|
allProviders.value.forEach((p) => {
|
||||||
|
if (!p.spec?.url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretRef = p.spec?.secret;
|
||||||
|
|
||||||
|
if (secretRef) {
|
||||||
|
const secret = allSecrets.value.find(
|
||||||
|
(s) => s.metadata.name === secretRef.name && s.metadata.namespace === secretRef.namespace
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!secret?.data?.user || !secret?.data?.password) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
options.push({
|
options.push({
|
||||||
label: p.metadata.name,
|
label: p.metadata.name,
|
||||||
value: p.metadata.name,
|
value: p.metadata.name,
|
||||||
@ -575,9 +593,4 @@ init();
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.test-passed-btn {
|
|
||||||
color: var(--success) !important;
|
|
||||||
border-color: var(--success) !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -356,136 +356,138 @@ init();
|
|||||||
v-else
|
v-else
|
||||||
class="review-migration"
|
class="review-migration"
|
||||||
>
|
>
|
||||||
<Masthead
|
<div
|
||||||
:schema="schema"
|
class="review-migration-content"
|
||||||
:resource="schema.id"
|
|
||||||
:type-display="t('harvester.addons.forklift.reviewMigration.title')"
|
|
||||||
:is-creatable="false"
|
|
||||||
>
|
>
|
||||||
<template #subHeader>
|
<Masthead
|
||||||
<p class="text-muted mt-5">
|
:schema="schema"
|
||||||
{{ t('harvester.addons.forklift.reviewMigration.description') }}
|
:resource="schema.id"
|
||||||
</p>
|
:type-display="t('harvester.addons.forklift.reviewMigration.title')"
|
||||||
</template>
|
:is-creatable="false"
|
||||||
</Masthead>
|
class="line-height-32"
|
||||||
|
>
|
||||||
|
<template #subHeader>
|
||||||
|
<p class="text-muted mt-5">
|
||||||
|
{{ t('harvester.addons.forklift.reviewMigration.description') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</Masthead>
|
||||||
|
|
||||||
<div class="mb-20">
|
<!-- Migration Details Summary -->
|
||||||
<LabeledInput
|
<div class="migration-details">
|
||||||
v-model:value="planName"
|
<h3 class="section-title m-0">
|
||||||
:label="t('harvester.addons.forklift.reviewMigration.planName')"
|
{{ t('harvester.addons.forklift.reviewMigration.migrationDetails') }}
|
||||||
:placeholder="t('harvester.addons.forklift.reviewMigration.planNamePlaceholder')"
|
</h3>
|
||||||
required
|
<div class="span-9">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="planName"
|
||||||
|
:label="t('harvester.addons.forklift.reviewMigration.planName')"
|
||||||
|
:placeholder="t('harvester.addons.forklift.reviewMigration.planNamePlaceholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="details-grid span-9">
|
||||||
|
<div class="detail-column">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.totalVms') }}</span>
|
||||||
|
<span class="detail-value detail-value-large">{{ vms.length }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-column">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.source') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-value">{{ providerName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-column">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.targetNamespace') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-value">{{ TARGET_NAMESPACE }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-column">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.vcpu') }}</span>
|
||||||
|
<span class="detail-value">{{ totalVCpu }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.memory') }}</span>
|
||||||
|
<span class="detail-value">{{ totalMemoryGB }} GB</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.storage') }}</span>
|
||||||
|
<span class="detail-value">{{ totalStorageGB }} GB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-column grid-column-2">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.migrationMode') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-value">
|
||||||
|
{{ t('harvester.addons.forklift.reviewMigration.coldMigration') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Virtual Machines -->
|
||||||
|
<div class="vm-section">
|
||||||
|
<h3 class="section-title m-0">
|
||||||
|
{{ t('harvester.addons.forklift.reviewMigration.virtualMachines') }} ({{ vms.length }})
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="vm-cards-grid">
|
||||||
|
<RcItemCard
|
||||||
|
v-for="vm in vmCards"
|
||||||
|
:id="vm.id"
|
||||||
|
:key="vm.id"
|
||||||
|
:variant="'small'"
|
||||||
|
:header="{ title: { text: vm.name }, statuses: [{ icon: 'icon-notify-tick', color: 'text-success' }] }"
|
||||||
|
>
|
||||||
|
<template #item-card-content>
|
||||||
|
<div class="vm-card-content">
|
||||||
|
<div class="vm-card-specs">
|
||||||
|
<span class="vm-os text-muted">{{ vm.os }}</span>
|
||||||
|
<span class="vm-resources">
|
||||||
|
<i class="icon icon-disk" />
|
||||||
|
{{ vm.cpus }} vCPU • {{ vm.memGB }} • {{ vm.diskDisplay }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<MappingsCell
|
||||||
|
:network-entries="vm.networkMappings.map(m => `${m.source} → ${m.target}`)"
|
||||||
|
:storage-entries="vm.storageMappings.map(m => `${m.source} → ${m.target}`)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RcItemCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cold Migration Warning -->
|
||||||
|
<Banner
|
||||||
|
color="warning"
|
||||||
|
class="m-0"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span class="banner-title">{{ t('harvester.addons.forklift.reviewMigration.warningTitle') }}</span><br>
|
||||||
|
{{ t('harvester.addons.forklift.reviewMigration.warningMessage') }}
|
||||||
|
</div>
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
<!-- Error banner -->
|
||||||
|
<Banner
|
||||||
|
v-for="(err, i) in errors"
|
||||||
|
:key="i"
|
||||||
|
color="error"
|
||||||
|
:label="err"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Migration Details Summary -->
|
|
||||||
<div class="migration-details">
|
|
||||||
<h3 class="section-title">
|
|
||||||
{{ t('harvester.addons.forklift.reviewMigration.migrationDetails') }}
|
|
||||||
</h3>
|
|
||||||
<div class="details-grid">
|
|
||||||
<div class="detail-column">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.totalVms') }}</span>
|
|
||||||
<span class="detail-value detail-value-large">{{ vms.length }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="detail-column">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.source') }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-value">{{ providerName }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="detail-column">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.targetNamespace') }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-value">{{ TARGET_NAMESPACE }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="detail-column">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.vcpu') }}</span>
|
|
||||||
<span class="detail-value">{{ totalVCpu }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.memory') }}</span>
|
|
||||||
<span class="detail-value">{{ totalMemoryGB }} GB</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.storage') }}</span>
|
|
||||||
<span class="detail-value">{{ totalStorageGB }} GB</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="detail-column grid-column-2">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">{{ t('harvester.addons.forklift.reviewMigration.migrationMode') }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-value">
|
|
||||||
{{ t('harvester.addons.forklift.reviewMigration.coldMigration') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Virtual Machines -->
|
|
||||||
<div class="vm-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
{{ t('harvester.addons.forklift.reviewMigration.virtualMachines') }} ({{ vms.length }})
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="vm-cards-grid">
|
|
||||||
<RcItemCard
|
|
||||||
v-for="vm in vmCards"
|
|
||||||
:id="vm.id"
|
|
||||||
:key="vm.id"
|
|
||||||
:variant="'small'"
|
|
||||||
:header="{ title: { text: vm.name }, statuses: [{ icon: 'icon-notify-tick', color: 'text-success' }] }"
|
|
||||||
>
|
|
||||||
<template #item-card-content>
|
|
||||||
<div class="vm-card-content">
|
|
||||||
<div class="vm-card-specs">
|
|
||||||
<span class="vm-os text-muted">{{ vm.os }}</span>
|
|
||||||
<span class="vm-resources">
|
|
||||||
<i class="icon icon-disk" />
|
|
||||||
{{ vm.cpus }} vCPU • {{ vm.memGB }} • {{ vm.diskDisplay }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<MappingsCell
|
|
||||||
:network-entries="vm.networkMappings.map(m => `${m.source} → ${m.target}`)"
|
|
||||||
:storage-entries="vm.storageMappings.map(m => `${m.source} → ${m.target}`)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</RcItemCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Cold Migration Warning -->
|
|
||||||
<Banner
|
|
||||||
color="warning"
|
|
||||||
class="mt-20"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<span class="banner-title">{{ t('harvester.addons.forklift.reviewMigration.warningTitle') }}</span><br>
|
|
||||||
{{ t('harvester.addons.forklift.reviewMigration.warningMessage') }}
|
|
||||||
</div>
|
|
||||||
</Banner>
|
|
||||||
|
|
||||||
<!-- Error banner -->
|
|
||||||
<Banner
|
|
||||||
v-for="(err, i) in errors"
|
|
||||||
:key="i"
|
|
||||||
color="error"
|
|
||||||
:label="err"
|
|
||||||
class="mt-10"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<div class="actions-footer">
|
<div class="actions-footer">
|
||||||
<button
|
<button
|
||||||
@ -509,15 +511,17 @@ init();
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.review-migration {
|
.review-migration {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
gap: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-migration-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0 0 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.migration-details {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-grid {
|
.details-grid {
|
||||||
@ -559,10 +563,6 @@ init();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vm-section {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vm-cards-grid {
|
.vm-cards-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
||||||
@ -625,11 +625,30 @@ init();
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner-title {
|
.banner-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.migration-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-height-32 {
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, watch, nextTick } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import Loading from '@shell/components/Loading';
|
import Loading from '@shell/components/Loading';
|
||||||
import Masthead from '@shell/components/ResourceList/Masthead';
|
import Masthead from '@shell/components/ResourceList/Masthead';
|
||||||
@ -32,11 +32,33 @@ const tableRows = ref([]);
|
|||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const networkMap = ref({});
|
const networkMap = ref({});
|
||||||
const datastoreMap = ref({});
|
const datastoreMap = ref({});
|
||||||
|
const sortableTableRef = ref(null);
|
||||||
|
const allVMsSelected = ref(false);
|
||||||
|
const selectedVMIds = ref(new Set());
|
||||||
|
let skipNextSelectionEvent = false;
|
||||||
|
|
||||||
const providerName = computed(() => provider.value?.metadata?.name || currentRoute().query.provider || '');
|
const providerName = computed(() => provider.value?.metadata?.name || currentRoute().query.provider || '');
|
||||||
const vmCount = computed(() => discoveredVMs.value.length);
|
const vmCount = computed(() => discoveredVMs.value.length);
|
||||||
const selectedCount = computed(() => selectedVMs.value.length);
|
const selectedCount = computed(() => selectedVMs.value.length);
|
||||||
|
|
||||||
|
const showSelectAllBanner = computed(() => {
|
||||||
|
if (allVMsSelected.value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pagedRows = sortableTableRef.value?.pagedRows || [];
|
||||||
|
|
||||||
|
if (pagedRows.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allPageSelected = pagedRows.every((row) => selectedVMIds.value.has(row._original?.id));
|
||||||
|
|
||||||
|
return allPageSelected && tableRows.value.length > pagedRows.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const theadElement = computed(() => sortableTableRef.value?.$el?.querySelector('thead'));
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
{
|
{
|
||||||
name: 'vmName',
|
name: 'vmName',
|
||||||
@ -144,9 +166,70 @@ const buildTableRows = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSelect = (rows) => {
|
const onSelect = (rows) => {
|
||||||
selectedVMs.value = rows.map((r) => r._original);
|
if (skipNextSelectionEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPageRows = sortableTableRef.value?.pagedRows || [];
|
||||||
|
const selectedKeys = new Set(rows.map((r) => r._key));
|
||||||
|
|
||||||
|
currentPageRows.forEach((row) => {
|
||||||
|
const vmId = row._original?.id;
|
||||||
|
|
||||||
|
if (selectedKeys.has(row._key)) {
|
||||||
|
selectedVMIds.value.add(vmId);
|
||||||
|
} else {
|
||||||
|
selectedVMIds.value.delete(vmId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
allVMsSelected.value = selectedVMIds.value.size === discoveredVMs.value.length;
|
||||||
|
selectedVMs.value = discoveredVMs.value.filter((vm) => selectedVMIds.value.has(vm.id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearSelection = () => {
|
||||||
|
allVMsSelected.value = false;
|
||||||
|
selectedVMIds.value = new Set();
|
||||||
|
selectedVMs.value = [];
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
const table = sortableTableRef.value;
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
table.clearSelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectAllVMs = () => {
|
||||||
|
allVMsSelected.value = true;
|
||||||
|
selectedVMIds.value = new Set(discoveredVMs.value.map((vm) => vm.id));
|
||||||
|
selectedVMs.value = discoveredVMs.value.slice();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => sortableTableRef.value?.page,
|
||||||
|
() => {
|
||||||
|
skipNextSelectionEvent = true;
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
const table = sortableTableRef.value;
|
||||||
|
|
||||||
|
if (!table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowsToReselect = (table.pagedRows || []).filter((row) => selectedVMIds.value.has(row._original?.id));
|
||||||
|
|
||||||
|
if (rowsToReselect.length > 0) {
|
||||||
|
table.update(rowsToReselect, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipNextSelectionEvent = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
currentRouter().push({
|
currentRouter().push({
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-forklift`,
|
name: `${ PRODUCT_NAME }-c-cluster-forklift`,
|
||||||
@ -255,6 +338,7 @@ init();
|
|||||||
|
|
||||||
<!-- Discovered VMs table -->
|
<!-- Discovered VMs table -->
|
||||||
<SortableTable
|
<SortableTable
|
||||||
|
ref="sortableTableRef"
|
||||||
:rows="tableRows"
|
:rows="tableRows"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:search="true"
|
:search="true"
|
||||||
@ -316,6 +400,37 @@ init();
|
|||||||
</template>
|
</template>
|
||||||
</SortableTable>
|
</SortableTable>
|
||||||
|
|
||||||
|
<Teleport
|
||||||
|
v-if="showSelectAllBanner && theadElement"
|
||||||
|
:to="theadElement"
|
||||||
|
>
|
||||||
|
<tr class="select-all-banner-row">
|
||||||
|
<td
|
||||||
|
:colspan="headers.length + 1"
|
||||||
|
class="select-all-banner-cell"
|
||||||
|
>
|
||||||
|
<template v-if="allVMsSelected">
|
||||||
|
<span>{{ t('harvester.addons.forklift.selectVms.selectAllBanner.allSelected') }}</span>
|
||||||
|
<a
|
||||||
|
role="button"
|
||||||
|
@click.prevent="clearSelection"
|
||||||
|
>
|
||||||
|
{{ t('harvester.addons.forklift.selectVms.selectAllBanner.clearSelection') }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>{{ t('harvester.addons.forklift.selectVms.selectAllBanner.pageOnly') }}</span>
|
||||||
|
<a
|
||||||
|
role="button"
|
||||||
|
@click.prevent="selectAllVMs"
|
||||||
|
>
|
||||||
|
{{ t('harvester.addons.forklift.selectVms.selectAllBanner.selectAll', { count: vmCount }) }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
<div class="actions-footer">
|
<div class="actions-footer">
|
||||||
<button
|
<button
|
||||||
class="btn role-secondary"
|
class="btn role-secondary"
|
||||||
@ -365,6 +480,27 @@ init();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.select-all-banner-row) {
|
||||||
|
.select-all-banner-cell {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: var(--sortable-table-header-bg);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary);
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.actions-footer {
|
.actions-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user