mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
feat(web-core): adapt card rows to their content in side panels (#10733)
* feat(web-core): adapt card rows to their content in side panels - Key and value stay on one line when they fit, the value moves below the key otherwise - New `values` prop on `VtsCardRowKeyValue`: one copy button per value, a "Copy all" action and an optional "see more" limit for fields with multiple values - New `VtsCardRowList` component holding the 0.8rem spacing between rows - `UiInfo` text is small inside side panels (icon stays medium), medium elsewhere - Side panel links use the small size * docs: add changelog entries
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
## **next**
|
||||
|
||||
- [Side panels] Side panels can be resized like the treeview, whose default width now adapts to the screen size (PR [#10725](https://github.com/vatesfr/xen-orchestra/pull/10725))
|
||||
- [Side panels] Values stay next to their label when they fit and move below it otherwise, fields with several values (IP addresses, bond devices) get a copy button per value and a "Copy all" action, and rows, links and statuses are more evenly spaced and sized (PR [#10733](https://github.com/vatesfr/xen-orchestra/pull/10733))
|
||||
|
||||
## **0.27.0** (2026-10-29)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- PIF -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="pif.uuid" size="medium" :label="isBond ? t('bond') : t('pif')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- NETWORK -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -82,34 +82,15 @@
|
||||
</UiTagsList>
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- NETWORK INFORMATION -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('network-information')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- IP ADDRESSES -->
|
||||
<div v-if="ipAddresses.length">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>
|
||||
<span v-tooltip class="text-ellipsis">{{ ip }}</span>
|
||||
</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<template #key>
|
||||
{{ t('ip-addresses') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="value" />
|
||||
</template>
|
||||
<VtsCardRowKeyValue :values="ipAddresses">
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- MAC ADDRESSES -->
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -173,26 +154,15 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- BOND DEVICES -->
|
||||
<div>
|
||||
<VtsCardRowKeyValue v-for="(device, index) in bondDevices" :key="device">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('bond-devices') }}</div>
|
||||
</template>
|
||||
<template #value>
|
||||
<span v-tooltip class="text-ellipsis">{{ device }}</span>
|
||||
</template>
|
||||
<template v-if="device" #addons>
|
||||
<VtsCopyButton :value="device" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && bondDevices.length > 1" :values="bondDevicesFiltered" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
<VtsCardRowKeyValue v-if="bondDevicesFiltered.length > 0" :values="bondDevicesFiltered">
|
||||
<template #key>{{ t('bond-devices') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- PROPERTIES -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('properties')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- MTU -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -226,7 +196,7 @@
|
||||
<VtsCopyButton :value="networkPurpose" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
</VtsSidePanel>
|
||||
@@ -239,7 +209,7 @@ import { useNetworkStore } from '@/stores/xen-api/network.store.ts'
|
||||
import { usePifMetricsStore } from '@/stores/xen-api/pif-metrics.store.ts'
|
||||
import { usePifStore } from '@/stores/xen-api/pif.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsForgetButton from '@core/components/forget-button/VtsForgetButton.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
@@ -331,12 +301,6 @@ const speed = computed(() => {
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.host-pif-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.network {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<template v-if="network" #default>
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="network.uuid" size="medium" :label="network.name_label" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- DESCRIPTION -->
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('description') }}</template>
|
||||
@@ -63,7 +63,7 @@
|
||||
<template #key>{{ t('locking-mode-default') }}</template>
|
||||
<template #value>{{ networkDefaultLockingMode }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<UiPanelCard v-if="pifsCount && pifsCount > 0">
|
||||
<UiPanelCardTitle size="medium" :label="t('pifs')" :counter="pifsCount" />
|
||||
@@ -97,6 +97,7 @@ import type { XenApiNetwork } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import { useNetworkDelete } from '@/modules/network/composables/use-network-delete.composable.ts'
|
||||
import { usePifStore } from '@/stores/xen-api/pif.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDeleteButton from '@core/components/delete-button/VtsDeleteButton.vue'
|
||||
import MenuItem from '@core/components/menu/MenuItem.vue'
|
||||
@@ -149,12 +150,6 @@ const { copy, copied, isSupported: isClipboardSupported } = useClipboard({ sourc
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.pool-network-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-hardware-specifications-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('hardware-specifications')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('manufacturer-info') }}</template>
|
||||
<template #value>{{ manufacturerInfo }}</template>
|
||||
@@ -16,7 +16,7 @@
|
||||
<VtsCopyButton :value="coreSocketInfo" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import { getHostCoreSocketInfo, getHostManufacturerInfo } from '@/modules/host/utils/host.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -40,13 +41,3 @@ const manufacturerInfo = computed(() => getHostManufacturerInfo(host))
|
||||
|
||||
const coreSocketInfo = computed(() => getHostCoreSocketInfo(host))
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.host-hardware-specifications-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:icon="hostIcon"
|
||||
:to="{ name: '/host/[uuid]/dashboard', params: { uuid: host.uuid } }"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('state') }}</template>
|
||||
<template #value>
|
||||
@@ -98,7 +98,7 @@
|
||||
<VtsCopyButton :value="noMissingPatches ? t('patches-up-to-date') : t('n-missing', nMissingPatches)" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -110,6 +110,7 @@ import { getHostIcon } from '@/modules/host/utils/host.util.ts'
|
||||
import { useHostMetricsStore } from '@/stores/xen-api/host-metrics.store.ts'
|
||||
import { usePoolStore } from '@/stores/xen-api/pool.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsEnabledState from '@core/components/enabled-state/VtsEnabledState.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
@@ -146,10 +147,6 @@ const noMissingPatches = computed(() => nMissingPatches.value === 0)
|
||||
<style lang="postcss" scoped>
|
||||
.host-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-network-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :label="t('network')" size="medium" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('management-ip') }}</template>
|
||||
<template #value>{{ host.address }}</template>
|
||||
@@ -9,37 +9,13 @@
|
||||
<VtsCopyButton :value="host.address" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="ipV4Addresses.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipV4Addresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ipv4-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipV4Addresses.length > 1" :values="ipV4Addresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowKeyValue :values="ipV4Addresses">
|
||||
<template #key>{{ t('ipv4-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="ipV6Addresses.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipV6Addresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ipv6-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipV6Addresses.length > 1" :values="ipV6Addresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowKeyValue :values="ipV6Addresses">
|
||||
<template #key>{{ t('ipv6-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -47,7 +23,7 @@
|
||||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import { usePifStore } from '@/stores/xen-api/pif.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -66,13 +42,3 @@ const pifs = computed(() => pifsByHost.value.get(host.$ref))
|
||||
const ipV4Addresses = computed(() => pifs.value?.map(pif => pif.IP).filter(ip => ip) ?? [])
|
||||
const ipV6Addresses = computed(() => pifs.value?.flatMap(pif => pif.IPv6.filter(ip => ip)) ?? [])
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.host-network-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-software-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('software')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('version') }}</template>
|
||||
<template #value>{{ host.software_version.product_version }}</template>
|
||||
@@ -9,13 +9,14 @@
|
||||
<VtsCopyButton :value="host.software_version.product_version" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -27,13 +28,3 @@ defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.host-software-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<UiPanelCard class="storage-repository-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle
|
||||
:id="sr.uuid"
|
||||
size="medium"
|
||||
:label="sr.name_label"
|
||||
:icon="sr.name_label ? srStatusIcon : undefined"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('status') }}</template>
|
||||
<template #value>
|
||||
@@ -56,7 +56,7 @@
|
||||
<template #key>{{ t('high-availability') }}</template>
|
||||
<template #value><VtsStatus :status="isHaSr" /></template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -66,6 +66,7 @@ import { useSrUtils } from '@/modules/storage-repository/composables/sr-utils.co
|
||||
import { useSrStore } from '@/stores/xen-api/sr.store.ts'
|
||||
import type { SrScope } from '@core/types/storage-repository.type.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -96,13 +97,3 @@ const allocationStrategy = computed(() => getAllocationStrategy(sr) ?? t('unknow
|
||||
|
||||
const isHaSr = computed(() => isHaSrForPool(sr, pool))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.storage-repository-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
{{ t('no-pbd-attached') }}
|
||||
</VtsStateHero>
|
||||
<template v-else>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsStatus :status="srConnectionStatus" />
|
||||
</div>
|
||||
<div v-if="areSomePbdsDisconnected" class="content">
|
||||
</VtsCardRowList>
|
||||
<VtsCardRowList v-if="areSomePbdsDisconnected" class="content">
|
||||
<template v-for="(pbd, index) in disconnectedPbds" :key="pbd.uuid">
|
||||
<VtsDivider v-if="index > 0" class="divider" type="stretch" />
|
||||
<UiPanelCardTitle size="small" :label="t('disconnected-pbd-number', { n: index + 1 })" class="subtitle" />
|
||||
@@ -31,7 +31,7 @@
|
||||
accent="info"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</template>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
@@ -43,6 +43,7 @@ import { usePbdUtils } from '@/modules/storage-repository/composables/pbd-utils.
|
||||
import { useSrUtils } from '@/modules/storage-repository/composables/sr-utils.composable.ts'
|
||||
import type { SrScope } from '@core/types/storage-repository.type.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -70,10 +71,6 @@ const { areSomePbdsDisconnected, disconnectedPbds } = usePbdUtils(pbdsInScope)
|
||||
<style scoped lang="postcss">
|
||||
.storage-repository-pbds-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.subtitle {
|
||||
margin-block-end: 0.4rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="card-container">
|
||||
<UiPanelCardTitle size="medium" :label="t('configuration')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VdiFormatCardItem :format="vdi.sm_config['image-format']" />
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -43,7 +43,7 @@
|
||||
<VtsStatus :status="isBootable" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -53,6 +53,7 @@ import type { XenApiVbd, XenApiVdi } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import VdiFormatCardItem from '@/modules/vdi/components/list/panel/card-items/VdiFormatCardItem.vue'
|
||||
import { useSrStore } from '@/stores/xen-api/sr.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
@@ -73,11 +74,6 @@ const isBootable = computed(() => vbd?.bootable ?? false)
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.card-container {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.storage {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="card-container">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="vdi.uuid" size="medium" :label="vdi.name_label" :icon="vdiIcon" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('description') }}</template>
|
||||
<template #value>{{ vdi.name_description }}</template>
|
||||
@@ -36,7 +36,7 @@
|
||||
<VtsCopyButton :value="vbd.device" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +45,7 @@ import type { XenApiVbd, XenApiVdi } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import { useVbdsStatus, type VbdAttachmentStatus } from '@/modules/vbd/composables/use-vbds-status.composable.ts'
|
||||
import type { IconName } from '@core/icons'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -79,13 +80,3 @@ const vbdsStatus = useMapper<VbdAttachmentStatus, (typeof CONNECTION_STATUS)[key
|
||||
'noneAttached'
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.card-container {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- VIF -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="vif.uuid" size="medium" :label="t('vif')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- NETWORK -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -79,32 +79,15 @@
|
||||
{{ isTxChecksummingEnabled(vif) }}
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- VIF NETWORK INFORMATION -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('network-information')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- IP ADDRESSES -->
|
||||
<div v-if="ipAddresses.length">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<template #key>
|
||||
{{ t('ip-addresses') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="value" />
|
||||
</template>
|
||||
<VtsCardRowKeyValue :values="ipAddresses">
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- MAC ADDRESSES -->
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -118,7 +101,7 @@
|
||||
<VtsCopyButton :value="vif.MAC" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
</VtsSidePanel>
|
||||
@@ -133,7 +116,7 @@ import { useNetworkStore } from '@/stores/xen-api/network.store.ts'
|
||||
import { useVmGuestMetricsStore } from '@/stores/xen-api/vm-guest-metrics.store.ts'
|
||||
import { useVmStore } from '@/stores/xen-api/vm.store.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsSidePanel from '@core/components/panel/VtsSidePanel.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -187,12 +170,6 @@ const rateLimit = computed(() => {
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vm-vifs-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.value:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
13
@xen-orchestra/lite/src/route-map.d.ts
vendored
13
@xen-orchestra/lite/src/route-map.d.ts
vendored
@@ -317,6 +317,13 @@ declare module 'vue-router/auto-routes' {
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
'/story/web-core/card/vts-card-row-list': RouteRecordInfo<
|
||||
'/story/web-core/card/vts-card-row-list',
|
||||
'/story/web-core/card/vts-card-row-list',
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
'/story/web-core/cell-object/vts-cell-object': RouteRecordInfo<
|
||||
'/story/web-core/cell-object/vts-cell-object',
|
||||
'/story/web-core/cell-object/vts-cell-object',
|
||||
@@ -1385,6 +1392,12 @@ declare module 'vue-router/auto-routes' {
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/stories/web-core/card/vts-card-row-list.story.vue': {
|
||||
routes:
|
||||
| '/story/web-core/card/vts-card-row-list'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/stories/web-core/cell-object/vts-cell-object.story.vue': {
|
||||
routes:
|
||||
| '/story/web-core/cell-object/vts-cell-object'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-slot="{ properties, settings }"
|
||||
:params="[
|
||||
slot('key').help('Meant to receive a title'),
|
||||
slot('value').help('Meant to receive text, tags or other value'),
|
||||
slot('value').help('Meant to receive text, tags or other value. Overrides the value rendered from `values`'),
|
||||
slot('addons').help('Meant to receive icons or other actions buttons '),
|
||||
prop('truncate')
|
||||
.type('boolean | number')
|
||||
@@ -21,6 +21,16 @@
|
||||
.help('Whether to align the key on the first line in case of multiline value')
|
||||
.type('boolean')
|
||||
.widget(),
|
||||
prop('values')
|
||||
.help('Values rendered as text with a copy button each, plus a copy all action when there are several')
|
||||
.type('string[]')
|
||||
.widget(object())
|
||||
.preset(['192.168.1.10', '192.168.1.11', '10.0.0.12', 'fe80::a00:27ff:fe4e:66a1']),
|
||||
prop('maxVisibleValues')
|
||||
.help('Number of values shown before a see more button')
|
||||
.type('number')
|
||||
.widget()
|
||||
.preset(3),
|
||||
setting('valueSlotDescription')
|
||||
.preset(
|
||||
'Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velit facilis pariatur maiores officia fugit odit, doloremque obcaecati quisquam distinctio adipisci fugiat incidunt hic rerum? Ullam, natus. Iste eum porro dolorum!'
|
||||
@@ -29,12 +39,16 @@
|
||||
]"
|
||||
>
|
||||
<UiCard class="card">
|
||||
<VtsCardRowKeyValue v-bind="properties">
|
||||
<VtsCardRowKeyValue :values="properties.values" :max-visible-values="properties.maxVisibleValues">
|
||||
<template #key>IP addresses</template>
|
||||
</VtsCardRowKeyValue>
|
||||
|
||||
<VtsCardRowKeyValue v-bind="{ ...properties, values: undefined }">
|
||||
<template #key>Description</template>
|
||||
<template #value>{{ settings.valueSlotDescription }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
|
||||
<VtsCardRowKeyValue v-bind="properties">
|
||||
<VtsCardRowKeyValue v-bind="{ ...properties, values: undefined }">
|
||||
<template #key>Network</template>
|
||||
<template #value>
|
||||
<UiObjectLink route="`/vm/test/console`" icon="object:network:connected">
|
||||
@@ -46,7 +60,7 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
|
||||
<VtsCardRowKeyValue v-bind="properties">
|
||||
<VtsCardRowKeyValue v-bind="{ ...properties, values: undefined }">
|
||||
<template #key>Status</template>
|
||||
<template #value>
|
||||
<UiInfo accent="success">Connected</UiInfo>
|
||||
@@ -57,7 +71,7 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
|
||||
<VtsCardRowKeyValue v-bind="{ ...properties, truncate: false }">
|
||||
<VtsCardRowKeyValue v-bind="{ ...properties, truncate: false, values: undefined }">
|
||||
<template #key>Tags</template>
|
||||
<template #value>
|
||||
<UiTagsList v-if="tags.length > 0">
|
||||
@@ -75,7 +89,7 @@
|
||||
<script lang="ts" setup>
|
||||
import ComponentStory from '@/components/component-story/ComponentStory.vue'
|
||||
import { prop, setting, slot } from '@/libs/story/story-param.ts'
|
||||
import { choice, text } from '@/libs/story/story-widget.ts'
|
||||
import { choice, object, text } from '@/libs/story/story-widget.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<ComponentStory :params="[slot().help('Meant to receive VtsCardRowKeyValue components')]">
|
||||
<UiCard class="card">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue :values="['10.0.0.12']">
|
||||
<template #key>IP address</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowKeyValue :values="['1500']">
|
||||
<template #key>MTU</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>Status</template>
|
||||
<template #value>
|
||||
<UiInfo accent="success">Connected</UiInfo>
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</VtsCardRowList>
|
||||
</UiCard>
|
||||
</ComponentStory>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComponentStory from '@/components/component-story/ComponentStory.vue'
|
||||
import { slot } from '@/libs/story/story-param.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiInfo from '@core/components/ui/info/UiInfo.vue'
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.card {
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,10 @@
|
||||
:params="[
|
||||
prop('accent').enum('info', 'success', 'warning', 'danger', 'muted').required().preset('info').widget(),
|
||||
prop('wrap').bool().help('Choose if the text should wrap if too long').widget(),
|
||||
prop('size').enum('small', 'medium').preset('medium').widget(),
|
||||
prop('size')
|
||||
.enum('small', 'medium')
|
||||
.help('Text size only, the icon is always medium. Defaults to small inside a VtsSidePanel, medium elsewhere')
|
||||
.widget(),
|
||||
slot(),
|
||||
setting('defaultSlot').widget(text()).preset('message'),
|
||||
]"
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="card-row-value-list">
|
||||
<div v-for="(value, index) in visibleValues" :key="index" class="value-row">
|
||||
<div class="value">{{ value }}</div>
|
||||
<div class="addons">
|
||||
<VtsCopyButton :value />
|
||||
<slot v-if="index === 0" name="first-row-addons" />
|
||||
</div>
|
||||
</div>
|
||||
<UiButton
|
||||
v-if="hiddenValuesCount > 0"
|
||||
class="see-more"
|
||||
size="small"
|
||||
accent="brand"
|
||||
variant="tertiary"
|
||||
@click="toggleExpanded()"
|
||||
>
|
||||
{{ isExpanded ? t('action:see-less') : t('action:see-n-more', { n: hiddenValuesCount }) }}
|
||||
</UiButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiButton from '@core/components/ui/button/UiButton.vue'
|
||||
import { useToggle } from '@vueuse/shared'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { values, maxVisibleValues } = defineProps<{
|
||||
values: string[]
|
||||
maxVisibleValues?: number
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
'first-row-addons'?(): any
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const [isExpanded, toggleExpanded] = useToggle(false)
|
||||
|
||||
const hiddenValuesCount = computed(() =>
|
||||
maxVisibleValues === undefined ? 0 : Math.max(values.length - maxVisibleValues, 0)
|
||||
)
|
||||
|
||||
const visibleValues = computed(() => (isExpanded.value ? values : values.slice(0, maxVisibleValues)))
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.card-row-value-list {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
|
||||
.value-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1.6rem;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--color-neutral-txt-primary);
|
||||
|
||||
&:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
}
|
||||
|
||||
.addons {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
height: 1lh;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.see-more {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,48 +1,67 @@
|
||||
<template>
|
||||
<div
|
||||
class="vts-card-row-key-value typo-body-regular-small"
|
||||
:class="{ truncate: shouldTruncate, 'align-top': alignTop }"
|
||||
>
|
||||
<div class="key">
|
||||
<slot name="key" />
|
||||
</div>
|
||||
<div class="value-container">
|
||||
<div ref="truncatableValueElement" class="value" :class="{ truncated: shouldTruncate && !isExpanded }">
|
||||
<slot name="value" />
|
||||
<div class="vts-card-row-key-value typo-body-regular-small" :class="className">
|
||||
<div ref="keyValue" class="key-value">
|
||||
<div class="key">
|
||||
<slot name="key" />
|
||||
<div v-if="multipleValues && isStacked" class="addons">
|
||||
<slot name="addons" />
|
||||
<VtsCopyAllMenuItem :values="multipleValues" />
|
||||
</div>
|
||||
</div>
|
||||
<CardRowValueList v-if="multipleValues" :values="multipleValues" :max-visible-values>
|
||||
<template v-if="!isStacked" #first-row-addons>
|
||||
<slot name="addons" />
|
||||
<VtsCopyAllMenuItem :values="multipleValues" />
|
||||
</template>
|
||||
</CardRowValueList>
|
||||
<div v-else class="value-container">
|
||||
<div ref="truncatableValueElement" class="value" :class="{ truncated: shouldTruncate && !isExpanded }">
|
||||
<slot name="value">
|
||||
<template v-if="singleValue !== undefined">{{ singleValue }}</template>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<UiButtonIcon
|
||||
v-if="shouldTruncate"
|
||||
v-tooltip="isExpanded ? t('action:show-less') : t('action:show-more')"
|
||||
:icon="isExpanded ? 'fa:chevron-up' : 'fa:chevron-down'"
|
||||
class="show-more"
|
||||
size="small"
|
||||
accent="brand"
|
||||
@click="toggleExpanded()"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="slots.addons" class="addons">
|
||||
<slot name="addons" />
|
||||
<div v-if="!multipleValues && (slots.addons || singleValue !== undefined || shouldTruncate)" class="actions">
|
||||
<div v-if="slots.addons || singleValue !== undefined" class="addons">
|
||||
<slot name="addons" />
|
||||
<VtsCopyButton v-if="singleValue !== undefined" :value="singleValue" />
|
||||
</div>
|
||||
<div v-if="shouldTruncate" class="show-more">
|
||||
<UiButtonIcon
|
||||
v-tooltip="isExpanded ? t('action:show-less') : t('action:show-more')"
|
||||
:icon="isExpanded ? 'fa:chevron-up' : 'fa:chevron-down'"
|
||||
size="small"
|
||||
accent="brand"
|
||||
@click="toggleExpanded()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CardRowValueList from '@core/components/card/CardRowValueList.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { calculateLineCount } from '@core/utils/line-height.util.ts'
|
||||
import { useStyleTag } from '@vueuse/core'
|
||||
import { useResizeObserver, useStyleTag } from '@vueuse/core'
|
||||
import { useToggle } from '@vueuse/shared'
|
||||
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { truncate } = defineProps<{
|
||||
const { truncate, alignTop, values } = defineProps<{
|
||||
truncate?: number | boolean
|
||||
alignTop?: boolean
|
||||
values?: string[]
|
||||
maxVisibleValues?: number
|
||||
}>()
|
||||
|
||||
const slots = defineSlots<{
|
||||
key(): any
|
||||
value(): any
|
||||
value?(): any
|
||||
addons?(): any
|
||||
}>()
|
||||
|
||||
@@ -50,6 +69,24 @@ const DEFAULT_MAX_LINES = 5
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const multipleValues = computed(() => (values !== undefined && values.length > 1 ? values : undefined))
|
||||
|
||||
const singleValue = computed(() => (values?.length === 1 ? values[0] : undefined))
|
||||
|
||||
const keyValueRef = useTemplateRef('keyValue')
|
||||
const isStacked = ref(false)
|
||||
|
||||
useResizeObserver(
|
||||
() => (multipleValues.value !== undefined ? keyValueRef.value : undefined),
|
||||
() => {
|
||||
const key = keyValueRef.value?.firstElementChild
|
||||
const valueList = keyValueRef.value?.lastElementChild
|
||||
|
||||
isStacked.value =
|
||||
key instanceof HTMLElement && valueList instanceof HTMLElement && valueList.offsetTop > key.offsetTop
|
||||
}
|
||||
)
|
||||
|
||||
const truncatableValueElementRef = useTemplateRef('truncatableValueElement')
|
||||
const lineCount = ref(0)
|
||||
|
||||
@@ -67,6 +104,13 @@ const shouldTruncate = computed(() => isTruncationEnabled.value && lineCount.val
|
||||
|
||||
const [isExpanded, toggleExpanded] = useToggle(false)
|
||||
|
||||
const className = computed(() => ({
|
||||
truncate: shouldTruncate.value,
|
||||
'align-top': alignTop,
|
||||
'multiple-values': multipleValues.value !== undefined,
|
||||
stacked: isStacked.value,
|
||||
}))
|
||||
|
||||
const calculateLines = async () => {
|
||||
await nextTick()
|
||||
|
||||
@@ -89,42 +133,36 @@ useStyleTag(style)
|
||||
|
||||
onMounted(() => calculateLines())
|
||||
|
||||
watch(
|
||||
() => slots.value?.(),
|
||||
() => calculateLines()
|
||||
)
|
||||
watch([() => slots.value?.(), () => values], () => calculateLines())
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.vts-card-row-key-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
column-gap: 1.6rem;
|
||||
|
||||
&.truncate {
|
||||
display: grid;
|
||||
row-gap: 0.4rem;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
.key {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.addons {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.value-container {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.key-value {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.4rem 1.6rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&.align-top:not(.truncate) {
|
||||
&.align-top .key-value,
|
||||
&.multiple-values .key-value {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&.multiple-values.stacked .key {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.key {
|
||||
overflow-wrap: break-word;
|
||||
color: var(--color-neutral-txt-secondary);
|
||||
@@ -132,15 +170,16 @@ watch(
|
||||
|
||||
.value-container {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 0.8rem;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
|
||||
.value {
|
||||
min-width: 0;
|
||||
color: var(--color-neutral-txt-primary);
|
||||
}
|
||||
|
||||
.show-more {
|
||||
flex-shrink: 0;
|
||||
:slotted(*) {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,12 +187,23 @@ watch(
|
||||
content: '-';
|
||||
}
|
||||
|
||||
.addons {
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.addons,
|
||||
.show-more {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
margin-left: auto;
|
||||
font-size: 1.6rem;
|
||||
height: 1lh;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
margin-block-start: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="vts-card-row-list">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineSlots<{
|
||||
default(): any
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.vts-card-row-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
@@ -49,7 +49,8 @@ import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { PANEL_TRANSITION_DURATION, usePanelStore } from '@core/stores/panel.store.ts'
|
||||
import { useUiStore } from '@core/stores/ui.store.ts'
|
||||
import { computed, watch } from 'vue'
|
||||
import { IK_INFO_SIZE } from '@core/utils/injection-keys.util.ts'
|
||||
import { computed, provide, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { hasSelection } = defineProps<{
|
||||
@@ -76,6 +77,8 @@ const showResizeHandle = computed(() => !uiStore.isSmall && panelStore.isExpande
|
||||
|
||||
const transformTransition = `transform ${PANEL_TRANSITION_DURATION}ms`
|
||||
|
||||
provide(IK_INFO_SIZE, 'small')
|
||||
|
||||
watch(
|
||||
[() => hasSelection, () => panelStore.isLocked, () => uiStore.isSmall],
|
||||
([hasSelection]) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- v6 -->
|
||||
<template>
|
||||
<div class="ui-info">
|
||||
<VtsIcon class="icon" :name="icon" :size />
|
||||
<VtsIcon class="icon" :name="icon" size="medium" />
|
||||
<p v-tooltip="!wrap" class="label" :class="[textSize, { 'text-ellipsis': !wrap }]">
|
||||
<slot />
|
||||
</p>
|
||||
@@ -13,14 +13,16 @@ import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import type { IconName } from '@core/icons'
|
||||
import { useMapper } from '@core/packages/mapper'
|
||||
import { IK_INFO_SIZE } from '@core/utils/injection-keys.util.ts'
|
||||
import { inject } from 'vue'
|
||||
|
||||
export type InfoAccent = 'info' | 'success' | 'warning' | 'danger' | 'muted'
|
||||
type Size = 'small' | 'medium'
|
||||
export type InfoSize = 'small' | 'medium'
|
||||
|
||||
const { accent, size = 'medium' } = defineProps<{
|
||||
const { accent, size } = defineProps<{
|
||||
accent: InfoAccent
|
||||
wrap?: boolean
|
||||
size?: Size
|
||||
size?: InfoSize
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
@@ -39,8 +41,10 @@ const icon = useMapper<InfoAccent, IconName>(
|
||||
'muted'
|
||||
)
|
||||
|
||||
const textSize = useMapper<Size, any>(
|
||||
() => size,
|
||||
const contextSize = inject(IK_INFO_SIZE, undefined)
|
||||
|
||||
const textSize = useMapper<InfoSize, any>(
|
||||
() => size ?? contextSize ?? 'medium',
|
||||
{
|
||||
small: 'typo-body-regular-small',
|
||||
medium: 'typo-body-regular',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { InputWrapperController } from '@core/components/input-wrapper/VtsInputWrapper.vue'
|
||||
import type { InfoSize } from '@core/components/ui/info/UiInfo.vue'
|
||||
import type { ModalAccent } from '@core/components/ui/modal/UiModal.vue'
|
||||
import type { ValueFormatter } from '@core/types/chart.ts'
|
||||
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
||||
@@ -24,3 +25,5 @@ export const IK_DISABLED = Symbol('IK_DISABLED') as InjectionKey<ComputedRef<boo
|
||||
export const IK_INPUT_WRAPPER_CONTROLLER = Symbol('IK_INPUT_WRAPPER_CONTROLLER') as InjectionKey<InputWrapperController>
|
||||
|
||||
export const IK_OVERLAY_ACCENT = Symbol('IK_OVERLAY_ACCENT') as InjectionKey<ComputedRef<ModalAccent>>
|
||||
|
||||
export const IK_INFO_SIZE = Symbol('IK_INFO_SIZE') as InjectionKey<InfoSize>
|
||||
|
||||
@@ -63,12 +63,13 @@ Prefer this shape: one assertion covering every row a user sees, and it fails lo
|
||||
|
||||
`src/test/find-rendered-values.ts` does exactly that reduction, so a card test does not re-roll it:
|
||||
|
||||
| Helper | Reads |
|
||||
| ------------------------ | ---------------------------------------------------------- |
|
||||
| `findLabelledValues` | every `VtsTabularKeyValueRow` / `VtsKeyValueRow` of a card |
|
||||
| `findCardLabelledValues` | every `VtsCardRowKeyValue` of a side-panel card |
|
||||
| `findCopiedValues` | what each copyable key/value row copies, keyed by label |
|
||||
| `findCardCopiedValues` | what each copyable `VtsCardRowKeyValue` copies, in order |
|
||||
| Helper | Reads |
|
||||
| ---------------------------- | ------------------------------------------------------------------ |
|
||||
| `findLabelledValues` | every `VtsTabularKeyValueRow` / `VtsKeyValueRow` of a card |
|
||||
| `findCardLabelledValues` | every `VtsCardRowKeyValue` of a side-panel card |
|
||||
| `findCardLabelledValueLists` | every `VtsCardRowKeyValue`, each value of a multi-value row listed |
|
||||
| `findCopiedValues` | what each copyable key/value row copies, keyed by label |
|
||||
| `findCardCopiedValues` | what each copyable `VtsCardRowKeyValue` copies, in order |
|
||||
|
||||
```typescript
|
||||
expect(findLabelledValues(wrapper)).toEqual({ [t('vga')]: t('disabled'), [t('video-ram')]: '8 B' })
|
||||
@@ -80,13 +81,10 @@ A row offers a copy button through its `copy` prop (copies its `value`) or its `
|
||||
expect(findCopiedValues(wrapper)).toEqual({ [t('ip-address')]: '10.0.0.1', [t('uuid')]: 'vm-42' })
|
||||
```
|
||||
|
||||
Both collapse rows that share a label into one entry — a list repeating the same label (an address list labelling only its first row) is queried directly instead:
|
||||
Both read one value per row. A `VtsCardRowKeyValue` given several `values` (an address list, report recipients) renders them all under a single label, so read it with `findCardLabelledValueLists` instead:
|
||||
|
||||
```typescript
|
||||
expect(wrapper.findAll('.vts-card-row-key-value').map(row => row.get('.value').text())).toEqual([
|
||||
'10.0.0.1',
|
||||
'10.0.0.2',
|
||||
])
|
||||
expect(findCardLabelledValueLists(wrapper)).toEqual({ [t('ip-addresses')]: ['10.0.0.1', '10.0.0.2'] })
|
||||
```
|
||||
|
||||
When a card mixes deterministic values with environment-derived ones (a relative start time, a formatted date), pin the deterministic rows with `toMatchObject` and cover the composition separately by asserting the **list of labels, in order** — the formatted values themselves belong in the test of the composable that derives them.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="isSmartModeEnabled">
|
||||
<VtsDivider class="divider" type="stretch" />
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue v-if="smartModePowerState !== undefined">
|
||||
<template #key>
|
||||
{{ t('power-state') }}
|
||||
@@ -42,7 +42,7 @@
|
||||
<BackupJobSmartModeTagsItem :tags="smartModeTags.included" :label="t('vms-tags')" />
|
||||
<BackupJobSmartModeTagsItem :tags="smartModeTags.excluded" :label="t('excluded-vms-tags')" />
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</template>
|
||||
<template v-if="backedUpVmsCount > 0">
|
||||
<VtsDivider class="divider" type="stretch" />
|
||||
@@ -68,6 +68,7 @@ import BackupJobSmartModeTagsItem from '@/modules/backup-job/components/list/pan
|
||||
import { useXoBackedUpVmsUtils } from '@/modules/backup-job/composables/use-xo-backed-up-vms-utils.composable.ts'
|
||||
import type { FrontXoVmBackupJob } from '@/modules/backup-job/remote-resources/use-xo-backup-job-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -95,10 +96,6 @@ const { backedUpVms, backedUpVmsCount, smartModePools, smartModePowerState, smar
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
|
||||
.smart-mode-power-state-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
import type { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import { createMetadataBackupJob, createVmBackupJob } from '@/test/create-backup-job.ts'
|
||||
import { createSr } from '@/test/create-sr.ts'
|
||||
import { findCardLabelledValues } from '@/test/find-rendered-values.ts'
|
||||
import { findCardLabelledValueLists, findCardLabelledValues } from '@/test/find-rendered-values.ts'
|
||||
import { createGlobalTestConfig } from '@/test/global-test-config.ts'
|
||||
import { t } from '@/test/i18n.ts'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
@@ -95,7 +95,7 @@ it('lists every type of the backup job, offering to copy them all', () => {
|
||||
const wrapper = mountInfosCard(backupJob)
|
||||
const typeLabels = [t('backup:rolling-snapshot'), t('backup:vms-backup'), t('backup:vms-replication')]
|
||||
|
||||
expect(findValues(wrapper).slice(0, 3)).toEqual(typeLabels)
|
||||
expect(findCardLabelledValueLists(wrapper)[t('type')]).toEqual(typeLabels)
|
||||
expect(wrapper.getComponent(VtsCopyAllMenuItem).props('values')).toEqual(typeLabels)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-job-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle
|
||||
:id="backupJob.id"
|
||||
size="medium"
|
||||
@@ -7,16 +7,9 @@
|
||||
:href="jobOverviewHref"
|
||||
icon="object:backup-job"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowKeyValue v-for="(typeLabel, index) in typeLabels" :key="typeLabel">
|
||||
<template #key>
|
||||
<template v-if="index === 0">{{ t('type') }}</template>
|
||||
</template>
|
||||
<template #value>{{ typeLabel }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="typeLabel" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && typeLabels.length > 1" :values="typeLabels" />
|
||||
</template>
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue :values="typeLabels">
|
||||
<template #key>{{ t('type') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('mode') }}</template>
|
||||
@@ -58,7 +51,7 @@
|
||||
<template #value />
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -74,7 +67,7 @@ import { useXoSrUtils } from '@/modules/storage-repository/composables/xo-sr-uti
|
||||
import { useXoSrCollection } from '@/modules/storage-repository/remote-resources/use-xo-sr-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
@@ -111,13 +104,3 @@ const healthCheckSrs = computed(() =>
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-job-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-job-logs-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('last-n-runs', { n: BACKUP_JOB_LAST_RUNS_COUNT })" />
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<template v-for="index in BACKUP_JOB_LAST_RUNS_COUNT" :key="index">
|
||||
<VtsDivider v-if="index > 1" class="divider" type="stretch" />
|
||||
<UiPanelCardTitle size="small" :label="t('last-run-number', { n: index })" />
|
||||
<BackupJobRunItem v-if="backupLogs[index - 1]" :backup-run="backupLogs[index - 1]" />
|
||||
<span v-else class="no-run typo-body-regular-small">{{ t('no-run') }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import BackupJobRunItem from '@/modules/backup-job/components/list/panel/card-items/BackupJobRunItem.vue'
|
||||
import { BACKUP_JOB_LAST_RUNS_COUNT } from '@/modules/backup-job/utils/xo-backup-job.util.ts'
|
||||
import type { FrontXoBackupLog } from '@/modules/backup-log/remote-resources/use-xo-backup-log-collection.ts'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -31,10 +32,6 @@ const { t } = useI18n()
|
||||
<style scoped lang="postcss">
|
||||
.backup-job-logs-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
|
||||
.divider {
|
||||
margin-block: 1.6rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-job-schedules-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('schedules')" :counter="schedules.length" />
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<template v-for="({ schedule, nextRun }, index) in schedulesWithNextRun" :key="schedule.id">
|
||||
<VtsDivider v-if="index > 0" class="divider" type="stretch" />
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -45,7 +45,7 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -54,6 +54,7 @@ import type { FrontXoSchedule } from '@/modules/schedule/remote-resources/use-xo
|
||||
import { getScheduleNextRun } from '@/modules/schedule/utils/xo-schedule.util.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -89,10 +90,6 @@ const schedulesWithNextRun = computed(() =>
|
||||
<style scoped lang="postcss">
|
||||
.backup-job-schedules-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
|
||||
.divider {
|
||||
margin-block: 1.6rem;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import BackupJobSettingsCard from '@/modules/backup-job/components/list/panel/ca
|
||||
import type { FrontAnyXoBackupJob } from '@/modules/backup-job/remote-resources/use-xo-backup-job-collection.ts'
|
||||
import type { FrontXoProxy, useXoProxyCollection } from '@/modules/proxy/remote-resources/use-xo-proxy-collection.ts'
|
||||
import { createMetadataBackupJob, createMirrorBackupJob, createVmBackupJob } from '@/test/create-backup-job.ts'
|
||||
import { findCardLabelledValues } from '@/test/find-rendered-values.ts'
|
||||
import { findCardLabelledValueLists, findCardLabelledValues } from '@/test/find-rendered-values.ts'
|
||||
import { createGlobalTestConfig } from '@/test/global-test-config.ts'
|
||||
import { t } from '@/test/i18n.ts'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
@@ -48,16 +48,6 @@ function findLabels(wrapper: ReturnType<typeof mountSettingsCard>) {
|
||||
return Object.keys(findCardLabelledValues(wrapper))
|
||||
}
|
||||
|
||||
function findRecipients(wrapper: ReturnType<typeof mountSettingsCard>) {
|
||||
const rows = wrapper.findAll('.vts-card-row-key-value')
|
||||
const firstRecipientIndex = rows.findIndex(row => row.get('.key').text() === t('report-recipients'))
|
||||
|
||||
return rows
|
||||
.slice(firstRecipientIndex)
|
||||
.filter((row, index) => index === 0 || row.get('.key').text() === '')
|
||||
.map(row => row.get('.value').text())
|
||||
}
|
||||
|
||||
describe('rows', () => {
|
||||
it('shows every setting of a full VM backup job without rolling snapshot', () => {
|
||||
expect(findLabels(mountSettingsCard(createVmBackupJob({ mode: 'full' })))).toEqual([
|
||||
@@ -163,7 +153,7 @@ describe('report recipients', () => {
|
||||
it('shows the first recipients, then all of them on demand', async () => {
|
||||
const wrapper = mountSettingsCard(createMetadataBackupJob({ settings: { '': { reportRecipients: recipients } } }))
|
||||
|
||||
expect(findRecipients(wrapper)).toEqual(recipients.slice(0, 5))
|
||||
expect(findCardLabelledValueLists(wrapper)[t('report-recipients')]).toEqual(recipients.slice(0, 5))
|
||||
|
||||
const seeMoreButton = wrapper.getComponent(UiButton)
|
||||
|
||||
@@ -171,7 +161,7 @@ describe('report recipients', () => {
|
||||
|
||||
await seeMoreButton.trigger('click')
|
||||
|
||||
expect(findRecipients(wrapper)).toEqual(recipients)
|
||||
expect(findCardLabelledValueLists(wrapper)[t('report-recipients')]).toEqual(recipients)
|
||||
expect(seeMoreButton.text()).toBe(t('action:see-less'))
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-job-settings-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('settings')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('proxy') }}</template>
|
||||
<template #value>{{ proxy?.name }}</template>
|
||||
@@ -39,35 +39,8 @@
|
||||
<VtsCopyButton :value="reportWhenValueTranslation" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="reportRecipients.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(recipient, index) in visibleReportRecipients" :key="recipient" align-top>
|
||||
<template #key>
|
||||
<template v-if="index === 0">{{ t('report-recipients') }}</template>
|
||||
</template>
|
||||
<template #value>{{ recipient }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="recipient" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && reportRecipients.length > 1" :values="reportRecipients" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<UiButton
|
||||
v-if="hiddenReportRecipientsCount > 0"
|
||||
class="see-more"
|
||||
size="small"
|
||||
accent="brand"
|
||||
variant="tertiary"
|
||||
@click="toggleReportRecipients()"
|
||||
>
|
||||
{{
|
||||
areReportRecipientsExpanded
|
||||
? t('action:see-less')
|
||||
: t('action:see-n-more', { n: hiddenReportRecipientsCount })
|
||||
}}
|
||||
</UiButton>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowKeyValue :values="reportRecipients" :max-visible-values="VISIBLE_REPORT_RECIPIENTS_COUNT" align-top>
|
||||
<template #key>{{ t('report-recipients') }}</template>
|
||||
<template #value />
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowKeyValue v-if="hasTransferSettings">
|
||||
<template #key>{{ t('concurrency') }}</template>
|
||||
@@ -98,7 +71,7 @@
|
||||
<VtsStatus :status="!!settings.mergeBackupsSynchronously" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -107,14 +80,12 @@ import { useXoBackupJobSettings } from '@/modules/backup-job/composables/use-xo-
|
||||
import type { FrontAnyXoBackupJob } from '@/modules/backup-job/remote-resources/use-xo-backup-job-collection.ts'
|
||||
import { hasBackupJobSnapshotRetention } from '@/modules/backup-job/utils/xo-backup-job.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiButton from '@core/components/ui/button/UiButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
import UiTag from '@core/components/ui/tag/UiTag.vue'
|
||||
import { useToggle } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -122,6 +93,8 @@ const { backupJob } = defineProps<{
|
||||
backupJob: FrontAnyXoBackupJob
|
||||
}>()
|
||||
|
||||
const VISIBLE_REPORT_RECIPIENTS_COUNT = 5
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const {
|
||||
@@ -142,33 +115,5 @@ const canSetCompression = computed(() => backupJob.type === 'backup' && backupJo
|
||||
|
||||
const canSetOfflineBackup = computed(() => backupJob.type === 'backup' && !hasBackupJobSnapshotRetention(backupJob))
|
||||
|
||||
const VISIBLE_REPORT_RECIPIENTS_COUNT = 5
|
||||
|
||||
const [areReportRecipientsExpanded, toggleReportRecipients] = useToggle(false)
|
||||
|
||||
const reportRecipients = computed(() => settings.reportRecipients ?? [])
|
||||
|
||||
const hiddenReportRecipientsCount = computed(() =>
|
||||
Math.max(reportRecipients.value.length - VISIBLE_REPORT_RECIPIENTS_COUNT, 0)
|
||||
)
|
||||
|
||||
const visibleReportRecipients = computed(() =>
|
||||
areReportRecipientsExpanded.value
|
||||
? reportRecipients.value
|
||||
: reportRecipients.value.slice(0, VISIBLE_REPORT_RECIPIENTS_COUNT)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-job-settings-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.see-more {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-log-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="backupLog.id" size="medium" :label="logName" :href="logHref" icon="object:backup-run" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('backup-date') }}</template>
|
||||
<template #value>{{ formattedStartDate }}</template>
|
||||
@@ -85,7 +85,7 @@
|
||||
<VtsStatus :status="backupLog.status" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +99,7 @@ import { useXoSrCollection } from '@/modules/storage-repository/remote-resources
|
||||
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiInfo from '@core/components/ui/info/UiInfo.vue'
|
||||
@@ -156,13 +157,3 @@ const srs = computed(() =>
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-log-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-log-size-and-speed-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('size-and-speed')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('size') }}</template>
|
||||
<template #value>{{ size }}</template>
|
||||
@@ -16,7 +16,7 @@
|
||||
<VtsCopyButton :value="speed" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import { useXoBackupLogsUtils } from '@/modules/backup-log/composables/use-xo-backup-log-utils.composable.ts'
|
||||
import type { FrontXoBackupLog } from '@/modules/backup-log/remote-resources/use-xo-backup-log-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -52,13 +53,3 @@ const speed = computed(() => {
|
||||
return transferSpeed !== undefined ? formatSpeed(transferSpeed) : undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-log-size-and-speed-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-azure-azurite-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle v-if="azure.type === 'azure'" size="medium" :label="t('azure')" />
|
||||
<UiPanelCardTitle v-else size="medium" :label="t('azurite')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('host') }}</template>
|
||||
<template #value>{{ azure.host }}</template>
|
||||
@@ -46,13 +46,17 @@
|
||||
<VtsCopyButton :value="splitPath.subPath" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MASKED_SECRET, splitBackupRepositoryPath } from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import {
|
||||
MASKED_SECRET,
|
||||
splitBackupRepositoryPath,
|
||||
} from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -69,13 +73,3 @@ const { t } = useI18n()
|
||||
|
||||
const splitPath = computed(() => splitBackupRepositoryPath(azure.path))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-azure-azurite-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle
|
||||
:id="br.id"
|
||||
:label="br.name"
|
||||
@@ -7,7 +7,7 @@
|
||||
:icon="brIcon"
|
||||
size="medium"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('status') }}</template>
|
||||
<template #value>
|
||||
@@ -44,7 +44,7 @@
|
||||
<VtsStatus :status="isEncrypted" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -53,6 +53,7 @@ import { useXoBackupRepositoryUtils } from '@/modules/backup-repository/composab
|
||||
import type { FrontXoBackupRepository } from '@/modules/backup-repository/remote-resources/use-xo-backup-repository-collection.ts'
|
||||
import { getBackupRepositoryIcon } from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -76,13 +77,3 @@ const { brStatus, brType, brStorageMode, brProxy, isEncrypted } = useXoBackupRep
|
||||
|
||||
const brIcon = computed(() => getBackupRepositoryIcon(br, parsedBrUrl?.type))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-local-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('local')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('path') }}</template>
|
||||
<template #value>{{ file.path }}</template>
|
||||
@@ -9,12 +9,13 @@
|
||||
<VtsCopyButton :value="file.path" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -27,13 +28,3 @@ defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-local-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-nfs-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('nfs')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('host') }}</template>
|
||||
<template #value>{{ nfs.host }}</template>
|
||||
@@ -33,13 +33,14 @@
|
||||
<VtsCopyButton :value="formattedOptions" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { formatMountOptions } from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -56,13 +57,3 @@ const { t } = useI18n()
|
||||
|
||||
const formattedOptions = computed(() => formatMountOptions(options))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-nfs-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-s3-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('s3')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('endpoint-url') }}</template>
|
||||
<template #value>{{ s3.host }}</template>
|
||||
@@ -60,13 +60,17 @@
|
||||
<VtsCopyButton :value="splitPath.subPath" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MASKED_SECRET, splitBackupRepositoryPath } from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import {
|
||||
MASKED_SECRET,
|
||||
splitBackupRepositoryPath,
|
||||
} from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -89,13 +93,3 @@ const truncatedAccessKeyId = computed(() =>
|
||||
s3.username.length > ACCESS_KEY_ID_LIMIT ? `${s3.username.slice(0, ACCESS_KEY_ID_LIMIT)}…` : s3.username
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-s3-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-smb-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('smb')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('path-on-share') }}</template>
|
||||
<template #value>{{ pathOnShare }}</template>
|
||||
@@ -38,7 +38,7 @@
|
||||
<VtsCopyButton :value="formattedOptions" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
MASKED_SECRET,
|
||||
} from '@/modules/backup-repository/utils/xo-backup-repository.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -67,13 +68,3 @@ const pathOnShare = computed(() => formatSmbPathOnShare(smb.host, smb.path))
|
||||
|
||||
const formattedOptions = computed(() => formatMountOptions(options))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-smb-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<UiPanelCard class="backup-repository-speed-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('speed')">
|
||||
<template #more-actions>
|
||||
<BackupRepositoryBenchmarkButton :br />
|
||||
</template>
|
||||
</UiPanelCardTitle>
|
||||
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('writing-speed') }}</template>
|
||||
<template #value>{{ writeSpeed }}</template>
|
||||
@@ -21,7 +21,7 @@
|
||||
<VtsCopyButton :value="readSpeed" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -30,6 +30,7 @@ import BackupRepositoryBenchmarkButton from '@/modules/backup-repository/compone
|
||||
import { useXoBackupRepositoryBenchmark } from '@/modules/backup-repository/composables/use-xo-backup-repository-benchmark.composable.ts'
|
||||
import type { FrontXoBackupRepository } from '@/modules/backup-repository/remote-resources/use-xo-backup-repository-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -43,13 +44,3 @@ const { t } = useI18n()
|
||||
|
||||
const { writeSpeed, readSpeed } = useXoBackupRepositoryBenchmark(() => br)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.backup-repository-speed-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-hardware-specifications-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('hardware-specifications')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('manufacturer-info') }}</template>
|
||||
<template #value>{{ manufacturerInfo }}</template>
|
||||
@@ -16,7 +16,7 @@
|
||||
<VtsCopyButton :value="coreSocketInfo" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { getHostCoreSocketInfo, getHostManufacturerInfo } from '@/modules/host/utils/xo-host.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -40,13 +41,3 @@ const manufacturerInfo = computed(() => getHostManufacturerInfo(host))
|
||||
|
||||
const coreSocketInfo = computed(() => getHostCoreSocketInfo(host))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.host-hardware-specifications-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:to="{ name: '/host/[id]/dashboard', params: { id: host.id } }"
|
||||
size="medium"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('state') }}</template>
|
||||
<template #value>
|
||||
@@ -88,7 +88,7 @@
|
||||
<VtsCopyButton :value="noMissingPatches ? t('patches-up-to-date') : t('n-missing', nMissingPatches)" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -100,6 +100,7 @@ import { useXoHostMissingPatchesCollection } from '@/modules/host/remote-resourc
|
||||
import { getHostIcon, getHostState } from '@/modules/host/utils/xo-host.util.ts'
|
||||
import { useXoPoolCollection } from '@/modules/pool/remote-resources/use-xo-pool-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsEnabledState from '@core/components/enabled-state/VtsEnabledState.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
@@ -147,10 +148,6 @@ const hostIcon = computed(() => getHostIcon(host))
|
||||
<style lang="postcss" scoped>
|
||||
.host-info-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-network-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :label="t('network')" size="medium" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('management-ip') }}</template>
|
||||
<template #value>{{ host.address }}</template>
|
||||
@@ -9,37 +9,13 @@
|
||||
<VtsCopyButton :value="host.address" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="ipV4Addresses.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipV4Addresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ipv4-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipV4Addresses.length > 1" :values="ipV4Addresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowKeyValue :values="ipV4Addresses">
|
||||
<template #key>{{ t('ipv4-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<template v-if="ipV6Addresses.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipV6Addresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ipv6-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipV6Addresses.length > 1" :values="ipV6Addresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowKeyValue :values="ipV6Addresses">
|
||||
<template #key>{{ t('ipv6-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -47,7 +23,7 @@
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { useXoPifCollection } from '@/modules/pif/remote-resources/use-xo-pif-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -74,13 +50,3 @@ const ipV6Addresses = computed(
|
||||
}, []) ?? []
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.host-network-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="host-software-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('software')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('version') }}</template>
|
||||
<template #value>{{ host.version }}</template>
|
||||
@@ -9,13 +9,14 @@
|
||||
<VtsCopyButton :value="host.version" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -27,13 +28,3 @@ defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.host-software-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
icon="object:network"
|
||||
:href="networkHref"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<!-- DESCRIPTION -->
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('description') }}</template>
|
||||
@@ -56,7 +56,7 @@
|
||||
<template #key>{{ t('locking-mode-default') }}</template>
|
||||
<template #value>{{ networkDefaultLockingMode }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<NetworkPifsInfoCard :network />
|
||||
</template>
|
||||
@@ -70,6 +70,7 @@ import type { FrontXoNetwork } from '@/modules/network/remote-resources/use-xo-n
|
||||
import { useXoPifCollection } from '@/modules/pif/remote-resources/use-xo-pif-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDeleteButton from '@core/components/delete-button/VtsDeleteButton.vue'
|
||||
import VtsSidePanel from '@core/components/panel/VtsSidePanel.vue'
|
||||
@@ -116,10 +117,6 @@ const networkDefaultLockingMode = computed(() => (network?.defaultIsLocked ? t('
|
||||
<style scoped lang="postcss">
|
||||
.network-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.value:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
<!-- PIF -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="pif.id" size="medium" :label="pif.isBondMaster ? t('bond') : t('pif')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- NETWORK -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
{{ t('network') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<UiLink v-if="network" size="medium" :to="networkTo" icon="object:network">
|
||||
<UiLink v-if="network" size="small" :to="networkTo" icon="object:network">
|
||||
{{ network.name_label }}
|
||||
</UiLink>
|
||||
</template>
|
||||
@@ -77,34 +77,15 @@
|
||||
<VtsCopyButton :value="network.tags.join(', ')" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- NETWORK INFORMATION -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('network-information')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- IP ADDRESSES -->
|
||||
<template v-if="ipAddresses.length">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="text-ellipsis">{{ ip }}</span>
|
||||
</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<template #key>
|
||||
{{ t('ip-addresses') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="value" />
|
||||
</template>
|
||||
<VtsCardRowKeyValue :values="ipAddresses">
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- MAC ADDRESSES -->
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -168,24 +149,15 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- BOND DEVICES -->
|
||||
<div>
|
||||
<VtsCardRowKeyValue v-for="(device, index) in bondDevices" :key="device">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('bond-devices') }}</div>
|
||||
</template>
|
||||
<template #value>{{ device }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="device" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && bondDevices.length > 1" :values="bondDevices" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
<VtsCardRowKeyValue v-if="bondDevices.length > 0" :values="bondDevices">
|
||||
<template #key>{{ t('bond-devices') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- PROPERTIES -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('properties')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- MTU -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -219,7 +191,7 @@
|
||||
<VtsCopyButton :value="networkNbd" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
</VtsSidePanel>
|
||||
@@ -231,7 +203,7 @@ import { getPoolNetworkRoute } from '@/modules/network/utils/xo-network.util.ts'
|
||||
import PifSetManagementButton from '@/modules/pif/components/actions/set-management/PifSetManagementButton.vue'
|
||||
import { type FrontXoPif, useXoPifCollection } from '@/modules/pif/remote-resources/use-xo-pif-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
import VtsSidePanel from '@core/components/panel/VtsSidePanel.vue'
|
||||
@@ -317,12 +289,6 @@ const speed = computed(() => {
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.pif-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.value:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="plugin-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="plugin.id" size="medium" :label="plugin.name" :to="pluginRoute" icon="object:plugin" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('version') }}</template>
|
||||
<template #value>{{ plugin.version }}</template>
|
||||
@@ -36,7 +36,7 @@
|
||||
<template #key>{{ t('load-at-startup') }}</template>
|
||||
<template #value><VtsStatus :status="plugin.autoload" /></template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
<UiAlert v-if="!plugin.configured" accent="warning">{{ t('plugin:needs-configuration-short') }}</UiAlert>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
@@ -46,6 +46,7 @@ import { useXoPluginUtils } from '@/modules/plugin/composables/xo-plugin-utils.c
|
||||
import type { FrontXoPlugin } from '@/modules/plugin/remote-resources/use-xo-plugin-collection.ts'
|
||||
import { getPluginRoute } from '@/modules/plugin/utils/xo-plugin.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -66,13 +67,3 @@ const { description, isLoaded, keywords, configurationStatus } = useXoPluginUtil
|
||||
|
||||
const pluginRoute = computed(() => getPluginRoute(plugin.id))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.plugin-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<VtsSidePanel :has-selection="!!server" class="pool-side-panel" @close="emit('close')">
|
||||
<VtsSidePanel :has-selection="!!server" @close="emit('close')">
|
||||
<template v-if="server" #actions>
|
||||
<PoolConnectionToggleButton :server-id="server.id" />
|
||||
<MenuList placement="bottom-end">
|
||||
@@ -34,7 +34,7 @@
|
||||
:icon="poolInfo.icon"
|
||||
:disabled="!isConnected"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- Description -->
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('description') }}</template>
|
||||
@@ -55,68 +55,70 @@
|
||||
<VtsCopyButton :value="pool.tags.join(', ')" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('connection')" />
|
||||
<!-- status -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('status') }}</template>
|
||||
<template #value>
|
||||
<UiInfo :accent="connectionStatus.accent">
|
||||
{{ connectionStatus.text }}
|
||||
</UiInfo>
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- primary-host -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('master') }}</template>
|
||||
<template #value>
|
||||
<HostLink v-if="primaryHost !== undefined" :host="primaryHost" size="small" />
|
||||
</template>
|
||||
<template v-if="primaryHost !== undefined" #addons>
|
||||
<VtsCopyButton :value="primaryHost.id" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- ip-address -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('ip-address') }}</template>
|
||||
<template #value>{{ server.host }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="server.host" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- proxy-url -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('proxy-url') }}</template>
|
||||
<template #value>{{ server.httpProxy }}</template>
|
||||
<template v-if="server.httpProxy !== undefined" #addons>
|
||||
<VtsCopyButton :value="server.httpProxy" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- username -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('username') }}</template>
|
||||
<template #value>{{ server.username }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="server.username" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- read-only -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('read-only') }}</template>
|
||||
<template #value>
|
||||
<VtsStatus :status="server.readOnly" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- self-signed-certificates -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('self-signed-certificates') }}</template>
|
||||
<template #value>
|
||||
<!-- todo add information button. waiting modal -->
|
||||
<VtsStatus :status="server.allowUnauthorized" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowList>
|
||||
<!-- status -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('status') }}</template>
|
||||
<template #value>
|
||||
<UiInfo :accent="connectionStatus.accent">
|
||||
{{ connectionStatus.text }}
|
||||
</UiInfo>
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- primary-host -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('master') }}</template>
|
||||
<template #value>
|
||||
<HostLink v-if="primaryHost !== undefined" :host="primaryHost" size="small" />
|
||||
</template>
|
||||
<template v-if="primaryHost !== undefined" #addons>
|
||||
<VtsCopyButton :value="primaryHost.id" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- ip-address -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('ip-address') }}</template>
|
||||
<template #value>{{ server.host }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="server.host" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- proxy-url -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('proxy-url') }}</template>
|
||||
<template #value>{{ server.httpProxy }}</template>
|
||||
<template v-if="server.httpProxy !== undefined" #addons>
|
||||
<VtsCopyButton :value="server.httpProxy" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- username -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('username') }}</template>
|
||||
<template #value>{{ server.username }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="server.username" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- read-only -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('read-only') }}</template>
|
||||
<template #value>
|
||||
<VtsStatus :status="server.readOnly" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- self-signed-certificates -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('self-signed-certificates') }}</template>
|
||||
<template #value>
|
||||
<!-- todo add information button. waiting modal -->
|
||||
<VtsStatus :status="server.allowUnauthorized" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<UiPanelCard v-if="hosts !== undefined">
|
||||
<UiPanelCardTitle size="medium" :label="t('hosts')" :counter="hosts.length" />
|
||||
@@ -146,6 +148,7 @@ import { useXoPoolCollection } from '@/modules/pool/remote-resources/use-xo-pool
|
||||
import { getPoolInfo } from '@/modules/pool/utils/xo-pool.util.ts'
|
||||
import type { FrontXoServer } from '@/modules/server/remote-resources/use-xo-server-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import MenuList from '@core/components/menu/MenuList.vue'
|
||||
import VtsSidePanel from '@core/components/panel/VtsSidePanel.vue'
|
||||
@@ -198,13 +201,3 @@ const connectionStatus = useMapper(
|
||||
'error'
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.pool-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UiLink :to="vdiRoute" :icon="vbdsSnapshotStatus" size="medium">
|
||||
<UiLink :to="vdiRoute" :icon="vbdsSnapshotStatus" size="small">
|
||||
{{ vdi.name_label }}
|
||||
</UiLink>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UiPanelCard class="snapshot-info-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle
|
||||
:id="snapshot.id"
|
||||
size="medium"
|
||||
@@ -7,7 +7,7 @@
|
||||
:href="buildXo5VmSnapshotRoute(snapshot.$snapshot_of, snapshot.id)"
|
||||
icon="object:vm-snapshot"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- DESCRIPTION -->
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>
|
||||
@@ -53,12 +53,12 @@
|
||||
{{ t('memory') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<UiInfo :accent="memoryAccent" size="medium">
|
||||
<UiInfo :accent="memoryAccent">
|
||||
{{ t(memoryLabel) }}
|
||||
</UiInfo>
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -67,6 +67,7 @@ import type { FrontXoVmSnapshot } from '@/modules/snapshot/components/remote-res
|
||||
import { useSnapshotTrigger } from '@/modules/snapshot/composables/xo-snapshot-trigger.composable.ts'
|
||||
import { useXo5VmSnapshotRoute } from '@/modules/snapshot/composables/xo-vm-snapshot-route-xo5.composable.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiInfo from '@core/components/ui/info/UiInfo.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
@@ -93,13 +94,3 @@ const memoryLabel = computed(() => (snapshot.power_state === VM_POWER_STATE.SUSP
|
||||
|
||||
const trigger = computed(() => getSnapshotTrigger(snapshot))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.snapshot-info-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<UiPanelCard v-if="vmSnapshotVdiSnapshots.length > 0" class="snapshot-vdi-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('vdis')" :counter="vmSnapshotVdiSnapshots.length" />
|
||||
<div class="content">
|
||||
<div v-for="(vmSnapshotVdi, index) in vmSnapshotVdiSnapshots" :key="vmSnapshotVdi.id" class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowList v-for="(vmSnapshotVdi, index) in vmSnapshotVdiSnapshots" :key="vmSnapshotVdi.id" class="content">
|
||||
<SnapshotVdiLinkCardItem :vdi="vmSnapshotVdi" />
|
||||
<!-- DESCRIPTION -->
|
||||
<VtsCardRowKeyValue truncate>
|
||||
@@ -19,8 +19,8 @@
|
||||
<!-- FORMAT -->
|
||||
<VdiFormatCardItem :format="vmSnapshotVdi.image_format" />
|
||||
<VtsDivider v-if="index < vmSnapshotVdiSnapshots.length - 1" class="divider" type="stretch" />
|
||||
</div>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -31,6 +31,7 @@ import type { FrontXoVmSnapshot } from '@/modules/snapshot/components/remote-res
|
||||
import { useXoVmSnapshotVdiCollection } from '@/modules/snapshot/components/remote-resources/use-xo-vm-snapshot-vdi-collection.ts'
|
||||
import VdiFormatCardItem from '@/modules/vdi/components/list/panel/card-items/VdiFormatCardItem.vue'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -47,10 +48,6 @@ const { vmSnapshotVdiSnapshots } = useXoVmSnapshotVdiCollection({}, () => snapsh
|
||||
<style scoped lang="postcss">
|
||||
.snapshot-vdi-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.divider {
|
||||
margin-block: 1.6rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="card-container">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="sr.id" size="medium" :label="sr.name_label" :to="srRoute" :icon="srStatusIcon" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('status') }}</template>
|
||||
<template #value>
|
||||
@@ -51,7 +51,7 @@
|
||||
<template #key>{{ t('high-availability') }}</template>
|
||||
<template #value><VtsStatus :status="isHaSr" /></template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -64,6 +64,7 @@ import {
|
||||
import { getSrPageLocation } from '@/modules/storage-repository/utils/xo-sr.util.ts'
|
||||
import type { SrScope } from '@core/types/storage-repository.type.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -95,13 +96,3 @@ const allocationStrategy = computed(() => getSrProvisioningLabel(sr))
|
||||
|
||||
const isHaSr = isHighAvailabilitySr(() => sr)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.storage-repository-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
{{ t('no-pbd-attached') }}
|
||||
</VtsStateHero>
|
||||
<template v-else>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsStatus :status="srConnectionStatus" />
|
||||
</div>
|
||||
<div v-if="areSomePbdsDisconnected" class="content">
|
||||
</VtsCardRowList>
|
||||
<VtsCardRowList v-if="areSomePbdsDisconnected" class="content">
|
||||
<template v-for="(pbd, index) in disconnectedPbds" :key="pbd.id">
|
||||
<VtsDivider v-if="index > 0" class="divider" type="stretch" />
|
||||
<UiPanelCardTitle size="small" :label="t('disconnected-pbd-number', { n: index + 1 })" class="subtitle" />
|
||||
@@ -29,7 +29,7 @@
|
||||
accent="info"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</template>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
@@ -41,6 +41,7 @@ import { useXoSrUtils } from '@/modules/storage-repository/composables/xo-sr-uti
|
||||
import type { FrontXoSr } from '@/modules/storage-repository/remote-resources/use-xo-sr-collection.ts'
|
||||
import type { SrScope } from '@core/types/storage-repository.type.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -68,10 +69,6 @@ const { areSomePbdsDisconnected, disconnectedPbds } = useXoPbdUtils(pbdsInScope)
|
||||
<style scoped lang="postcss">
|
||||
.storage-repository-pbds-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.subtitle {
|
||||
margin-block-end: 0.4rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="task-errors-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('errors')" :counter="{ value: 1, accent: 'danger' }" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('message') }}</template>
|
||||
<template #value>{{ task.result?.message }}</template>
|
||||
@@ -9,7 +9,7 @@
|
||||
<VtsCopyButton :value="String(task.result.message)" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
<UiLogEntryViewer
|
||||
v-if="task.result?.stack"
|
||||
:content="task.result.stack"
|
||||
@@ -23,6 +23,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiLogEntryViewer from '@core/components/ui/log-entry-viewer/UiLogEntryViewer.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -35,13 +36,3 @@ defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.task-errors-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="task-infos-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('info')" :counter="{ value: task.infos?.length ?? 0, accent: 'info' }" />
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<template v-for="(info, index) in task.infos" :key="index">
|
||||
<VtsDivider v-if="index > 0" class="divider" type="stretch" />
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -12,13 +12,14 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -35,10 +36,6 @@ const { t } = useI18n()
|
||||
<style scoped lang="postcss">
|
||||
.task-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.divider {
|
||||
margin-block: 1.6rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="task-objects-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('object')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('id') }}</template>
|
||||
<template #value>{{ task.properties.objectId }}</template>
|
||||
@@ -9,13 +9,14 @@
|
||||
<VtsCopyButton :value="String(task.properties.objectId)" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -27,13 +28,3 @@ defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.task-objects-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
<template v-if="nameParts" #label>
|
||||
<span>
|
||||
<template v-for="(part, index) in nameParts" :key="index">
|
||||
<UiLink v-if="part.to !== undefined" size="medium" :to="part.to">{{ part.text }}</UiLink>
|
||||
<UiLink v-if="part.to !== undefined" size="small" :to="part.to">{{ part.text }}</UiLink>
|
||||
<template v-else>{{ part.text }}</template>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
</UiPanelCardTitle>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('task-type') }}</template>
|
||||
<template #value>
|
||||
@@ -66,7 +66,7 @@
|
||||
<UiCircleProgressBar :accent="progressAccent" size="small" :value="progress" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -76,6 +76,7 @@ import type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-co
|
||||
import { getTaskAccents } from '@/modules/task/utils/xo-task.util.ts'
|
||||
import { useXoUserResource } from '@/modules/user/remote-resources/use-xo-user.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
import UiCircleProgressBar from '@core/components/ui/circle-progress-bar/UiCircleProgressBar.vue'
|
||||
@@ -146,10 +147,6 @@ const formattedEndDate = computed(() => {
|
||||
<style scoped lang="postcss">
|
||||
.task-quick-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:label="t('warnings')"
|
||||
:counter="{ value: task.warnings?.length ?? 0, accent: 'warning' }"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<template v-for="(warning, index) in task.warnings" :key="index">
|
||||
<VtsDivider v-if="index > 0" class="divider" type="stretch" />
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -16,13 +16,14 @@
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -39,10 +40,6 @@ const { t } = useI18n()
|
||||
<style scoped lang="postcss">
|
||||
.task-warnings-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.divider {
|
||||
margin-block: 1.6rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="traffic-rule-network-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('network')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
{{ t('network') }}
|
||||
@@ -82,7 +82,7 @@
|
||||
<VtsCopyButton :value="networkDefaultLockingMode" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -91,6 +91,7 @@ import type { FrontXoNetwork } from '@/modules/network/remote-resources/use-xo-n
|
||||
import { getPoolNetworkRoute } from '@/modules/network/utils/xo-network.util.ts'
|
||||
import { useXoPifCollection } from '@/modules/pif/remote-resources/use-xo-pif-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -124,13 +125,3 @@ const networkNbd = computed(() => (network.nbd ? t('on') : t('off')))
|
||||
|
||||
const networkDefaultLockingMode = computed(() => (network.defaultIsLocked ? t('disabled') : t('unlocked')))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.traffic-rule-network-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="traffic-rule-summary-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('traffic-rule')" icon="fa:traffic-rule" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
{{ t('rule-type') }}
|
||||
@@ -44,7 +44,7 @@
|
||||
<VtsCopyButton :value="description" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -57,6 +57,7 @@ import { useXoVifCollection } from '@/modules/vif/remote-resources/use-xo-vif-co
|
||||
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import { RULE_STATUS } from '@/shared/constants.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
@@ -127,12 +128,6 @@ const vifDevice = computed(() => `${t('vif')}${vif.value?.device}`)
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.traffic-rule-summary-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.value-content {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="traffic-rule-vif-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('vif')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
{{ t('name') }}
|
||||
@@ -115,7 +115,7 @@
|
||||
{{ vif.txChecksumming }}
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -127,6 +127,7 @@ import { useXoVifUtils } from '@/modules/vif/composables/xo-vif-utils.composable
|
||||
import type { FrontXoVif } from '@/modules/vif/remote-resources/use-xo-vif-collection.ts'
|
||||
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCodeSnippet from '@core/components/code-snippet/VtsCodeSnippet.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -176,13 +177,3 @@ const networkVlan = computed(() => {
|
||||
|
||||
const vifDevice = computed(() => `${t('vif')}${vif.device}`)
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.traffic-rule-vif-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
<template>
|
||||
<UiPanelCard class="traffic-rule-vif-network-info-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('vif-network-info')" />
|
||||
<div class="content">
|
||||
<template v-if="ipAddresses.length">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip" align-top>
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<template #key>
|
||||
{{ t('ip-addresses') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="value" />
|
||||
</template>
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue :values="ipAddresses" align-top>
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -33,7 +16,7 @@
|
||||
<VtsCopyButton :value="vif.MAC" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -41,7 +24,7 @@
|
||||
import type { FrontXoVif } from '@/modules/vif/remote-resources/use-xo-vif-collection.ts'
|
||||
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -65,10 +48,6 @@ const ipAddresses = computed(() => {
|
||||
<style scoped lang="postcss">
|
||||
.traffic-rule-vif-network-info-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.value:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
<template #label>
|
||||
<span class="name">
|
||||
<UiUserLogo size="small" />
|
||||
<UiLink :href="xo5UsersHref" size="medium">
|
||||
<UiLink :href="xo5UsersHref" size="small">
|
||||
{{ user.name }}
|
||||
</UiLink>
|
||||
</span>
|
||||
</template>
|
||||
</UiPanelCardTitle>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('xoa-manager') }}</template>
|
||||
<template #value>{{ userPermission }}</template>
|
||||
@@ -36,7 +36,7 @@
|
||||
<template #key>{{ t('providers') }}</template>
|
||||
<template #value>{{ providers }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +45,7 @@ import { useXoUserAuthenticationTokensCollection } from '@/modules/user/remote-r
|
||||
import type { FrontXoUser } from '@/modules/user/remote-resources/use-xo-user-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
@@ -104,11 +105,5 @@ const userPermission = computed(() => {
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="vdi-configuration-card">
|
||||
<UiPanelCardTitle size="medium" :label="t('configuration')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VdiFormatCardItem :format="vdi.image_format" />
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
@@ -42,7 +42,7 @@
|
||||
<VtsStatus :status="isBootable" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -54,6 +54,7 @@ import type { FrontXoVdi } from '@/modules/vdi/remote-resources/use-xo-vdi-colle
|
||||
import type { FrontXoVm } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
@@ -89,12 +90,6 @@ const isBootable = computed(() => vbd.value?.bootable ?? false)
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vdi-configuration-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.storage {
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UiPanelCard class="vdi-infos-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle
|
||||
:id="vdi.id"
|
||||
size="medium"
|
||||
@@ -7,7 +7,7 @@
|
||||
:to="{ name: '/vdi/[id]/general', params: { id: vdi.id }, query: { from: VDI_PAGE_CONTEXT.VM } }"
|
||||
:icon="vdiIcon"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue truncate align-top>
|
||||
<template #key>{{ t('description') }}</template>
|
||||
<template #value>{{ vdi.name_description }}</template>
|
||||
@@ -42,7 +42,7 @@
|
||||
<VtsCopyButton :value="vdiDevice" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -54,6 +54,7 @@ import type { FrontXoVm } from '@/modules/vm/remote-resources/use-xo-vm-collecti
|
||||
import { VDI_PAGE_CONTEXT } from '@/shared/constants.ts'
|
||||
import type { IconName } from '@core/icons'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -98,13 +99,3 @@ const { notCdDriveVbds } = useXoVmVbdsUtils(() => vm)
|
||||
|
||||
const vdiDevice = computed(() => notCdDriveVbds.value.find(vbd => vbd.VDI === vdi.id)?.device ?? '')
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vdi-infos-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<!-- VIF -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle :id="vif.id" size="medium" :to="vifTo" :label="t('vif')" icon="object:vif" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- NETWORK -->
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>
|
||||
{{ t('network') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<UiLink v-if="network" size="medium" :to="networkTo" icon="object:network">
|
||||
<UiLink v-if="network" size="small" :to="networkTo" icon="object:network">
|
||||
{{ network.name_label }}
|
||||
</UiLink>
|
||||
</template>
|
||||
@@ -85,32 +85,15 @@
|
||||
{{ vif.txChecksumming }}
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
<!-- NETWORK INFORMATION -->
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('network-information')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<!-- IP ADDRESSES -->
|
||||
<template v-if="ipAddresses.length">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<template #key>
|
||||
{{ t('ip-addresses') }}
|
||||
</template>
|
||||
<template #value>
|
||||
<span class="value" />
|
||||
</template>
|
||||
<VtsCardRowKeyValue :values="ipAddresses">
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
<!-- MAC ADDRESSES -->
|
||||
<VtsCardRowKeyValue>
|
||||
@@ -124,7 +107,7 @@
|
||||
<VtsCopyButton :value="vif.MAC" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
</VtsSidePanel>
|
||||
@@ -139,7 +122,7 @@ import type { FrontXoVif } from '@/modules/vif/remote-resources/use-xo-vif-colle
|
||||
import { getVifTrafficRoute } from '@/modules/vif/utils/xo-vif.util.ts'
|
||||
import { type FrontXoVm, useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsSidePanel from '@core/components/panel/VtsSidePanel.vue'
|
||||
import VtsStatus from '@core/components/status/VtsStatus.vue'
|
||||
@@ -188,12 +171,6 @@ const status = computed(() => (vif?.attached ? CONNECTION_STATUS.CONNECTED : CON
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vif-side-panel {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.value:empty::before {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:to="{ name: '/vm/[id]/dashboard', params: { id: vm.id } }"
|
||||
:icon="`object:vm:${toLower(vm.power_state)}`"
|
||||
/>
|
||||
<div class="content">
|
||||
<VtsCardRowList class="content">
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('state') }}</template>
|
||||
<template #value>
|
||||
@@ -116,7 +116,7 @@
|
||||
<VtsCopyButton :value="relativeStartTime" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -130,6 +130,7 @@ import { type FrontXoVm, useXoVmCollection } from '@/modules/vm/remote-resources
|
||||
import { useXoVmTemplateCollection } from '@/modules/vm/remote-resources/use-xo-vm-template-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
import VtsTag from '@core/components/tag/VtsTag.vue'
|
||||
@@ -179,10 +180,6 @@ const { powerState, installDateFormatted, relativeStartTime, guestToolsDisplay }
|
||||
<style scoped lang="postcss">
|
||||
.vm-info-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import VmNetworkCard from '@/modules/vm/components/list/panel/cards/VmNetworkCard.vue'
|
||||
import type { FrontXoVm } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import { createVm } from '@/test/create-vm.ts'
|
||||
import { findCardCopiedValues, findCardLabelledValueLists } from '@/test/find-rendered-values.ts'
|
||||
import { createGlobalTestConfig } from '@/test/global-test-config.ts'
|
||||
import { t } from '@/test/i18n.ts'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
function mountNetworkCard(vm: FrontXoVm = createVm()) {
|
||||
@@ -12,33 +14,37 @@ function mountNetworkCard(vm: FrontXoVm = createVm()) {
|
||||
})
|
||||
}
|
||||
|
||||
function findIpAddresses(wrapper: ReturnType<typeof mountNetworkCard>) {
|
||||
return wrapper.findAll('.vts-card-row-key-value').map(row => row.get('.value').text())
|
||||
}
|
||||
|
||||
it('renders the card title', () => {
|
||||
const wrapper = mountNetworkCard(createVm({ addresses: undefined }))
|
||||
|
||||
expect(wrapper.get('.ui-panel-card-title .title').text()).toBe(t('networks'))
|
||||
})
|
||||
|
||||
it('lists the IP addresses of the VM sorted alphabetically', () => {
|
||||
it('lists the IP addresses of the VM sorted alphabetically, under a single label', () => {
|
||||
const wrapper = mountNetworkCard(createVm({ addresses: { '0/ipv4/0': '10.0.0.2', '0/ipv4/1': '10.0.0.1' } }))
|
||||
|
||||
expect(findIpAddresses(wrapper)).toEqual(['10.0.0.1', '10.0.0.2'])
|
||||
expect(findCardLabelledValueLists(wrapper)).toEqual({ [t('ip-addresses')]: ['10.0.0.1', '10.0.0.2'] })
|
||||
})
|
||||
|
||||
it('labels only the first address row', () => {
|
||||
it('offers to copy each IP address and all of them', () => {
|
||||
const wrapper = mountNetworkCard(createVm({ addresses: { '0/ipv4/0': '10.0.0.1', '0/ipv4/1': '10.0.0.2' } }))
|
||||
|
||||
expect(wrapper.findAll('.vts-card-row-key-value').map(row => row.get('.key').text())).toEqual([t('ip-addresses'), ''])
|
||||
expect(findCardCopiedValues(wrapper)).toEqual(['10.0.0.1', '10.0.0.2'])
|
||||
expect(wrapper.getComponent(VtsCopyAllMenuItem).props('values')).toEqual(['10.0.0.1', '10.0.0.2'])
|
||||
})
|
||||
|
||||
it('offers no copy of all the IP addresses for a single one', () => {
|
||||
const wrapper = mountNetworkCard(createVm({ addresses: { '0/ipv4/0': '10.0.0.1' } }))
|
||||
|
||||
expect(findCardCopiedValues(wrapper)).toEqual(['10.0.0.1'])
|
||||
expect(wrapper.findComponent(VtsCopyAllMenuItem).exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows an empty address row when the VM has no IP address', () => {
|
||||
const wrapper = mountNetworkCard(createVm({ addresses: undefined }))
|
||||
|
||||
expect(wrapper.findAll('.vts-card-row-key-value').map(row => row.get('.key').text())).toEqual([t('ip-addresses')])
|
||||
expect(findIpAddresses(wrapper)).toEqual([''])
|
||||
expect(findCardLabelledValueLists(wrapper)).toEqual({ [t('ip-addresses')]: [''] })
|
||||
expect(findCardCopiedValues(wrapper)).toEqual([])
|
||||
})
|
||||
|
||||
it('links to the networks page when the VM has at least one IP address', () => {
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
<template>
|
||||
<UiPanelCard class="vm-network-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('networks')">
|
||||
<template v-if="ipAddresses.length > 0" #action>
|
||||
<UiLink size="medium" :to="{ name: '/vm/[id]/networks', params: { id: vm.id } }">
|
||||
<UiLink size="small" :to="{ name: '/vm/[id]/networks', params: { id: vm.id } }">
|
||||
{{ t('see-details') }}
|
||||
</UiLink>
|
||||
</template>
|
||||
</UiPanelCardTitle>
|
||||
<div class="content">
|
||||
<template v-if="ipAddresses.length > 0">
|
||||
<VtsCardRowKeyValue v-for="(ip, index) in ipAddresses" :key="ip">
|
||||
<template #key>
|
||||
<div v-if="index === 0">{{ t('ip-addresses') }}</div>
|
||||
</template>
|
||||
<template #value>{{ ip }}</template>
|
||||
<template #addons>
|
||||
<VtsCopyButton :value="ip" />
|
||||
<VtsCopyAllMenuItem v-if="index === 0 && ipAddresses.length > 1" :values="ipAddresses" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</template>
|
||||
<VtsCardRowKeyValue v-else>
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue :values="ipAddresses">
|
||||
<template #key>{{ t('ip-addresses') }}</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -31,8 +19,7 @@
|
||||
import type { FrontXoVm } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import { getVmIpAddresses } from '@/modules/vm/utils/xo-vm.util.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCopyAllMenuItem from '@core/components/copy-all-menu-item/VtsCopyAllMenuItem.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -47,13 +34,3 @@ const { t } = useI18n()
|
||||
|
||||
const ipAddresses = computed(() => getVmIpAddresses(vm))
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vm-network-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiPanelCard class="vm-resources-card">
|
||||
<UiPanelCard>
|
||||
<UiPanelCardTitle size="medium" :label="t('resources')" />
|
||||
<div class="content">
|
||||
<VtsCardRowList>
|
||||
<VtsCardRowKeyValue>
|
||||
<template #key>{{ t('vcpus') }}</template>
|
||||
<template #value>{{ vm.CPUs.number }}</template>
|
||||
@@ -37,7 +37,7 @@
|
||||
<VtsCopyButton :value="String(vm.snapshots.length)" />
|
||||
</template>
|
||||
</VtsCardRowKeyValue>
|
||||
</div>
|
||||
</VtsCardRowList>
|
||||
</UiPanelCard>
|
||||
</template>
|
||||
|
||||
@@ -46,6 +46,7 @@ import { useXoVbdCollection } from '@/modules/vbd/remote-resources/use-xo-vbd-co
|
||||
import { useXoVdiCollection } from '@/modules/vdi/remote-resources/use-xo-vdi-collection.ts'
|
||||
import type { FrontXoVm } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
import VtsCardRowKeyValue from '@core/components/card/VtsCardRowKeyValue.vue'
|
||||
import VtsCardRowList from '@core/components/card/VtsCardRowList.vue'
|
||||
import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
|
||||
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
|
||||
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
|
||||
@@ -75,13 +76,3 @@ const formattedDiskSpace = computed(() => {
|
||||
return formatSize(totalSize, 1)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.vm-resources-card {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,6 +37,22 @@ export function findCardLabelledValues(wrapper: QueryableWrapper): Record<string
|
||||
return Object.fromEntries(findPairs(wrapper, { root: CARD_KEY_VALUE_ROW_SELECTOR, label: '.key', value: '.value' }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads every `VtsCardRowKeyValue` as `{ label: values }`, each value of a
|
||||
* multi-value row (an address list, report recipients) being its own entry, in
|
||||
* the order the row lists them. A single-value row reads as a one-entry list.
|
||||
*/
|
||||
export function findCardLabelledValueLists(wrapper: QueryableWrapper): Record<string, string[]> {
|
||||
return Object.fromEntries(
|
||||
wrapper
|
||||
.findAll(CARD_KEY_VALUE_ROW_SELECTOR)
|
||||
.map(row => [
|
||||
row.get('.key').text(),
|
||||
row.findAll('.value-row > .value, .value-container > .value').map(value => value.text()),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value each `VtsTabularKeyValueRow` / `VtsKeyValueRow` offers to copy, keyed
|
||||
* by its label. Rows without a copy button are left out, so a single `toEqual` pins
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
> Users must be able to say: "Nice enhancement, I'm eager to test it"
|
||||
|
||||
- [Side panels] Side panels can be resized like the treeview, whose default width now adapts to the screen size (PR [#10725](https://github.com/vatesfr/xen-orchestra/pull/10725))
|
||||
- [Side panels] Values stay next to their label when they fit and move below it otherwise, fields with several values (IP addresses, report recipients…) get a copy button per value and a "Copy all" action, and rows, links and statuses are more evenly spaced and sized (PR [#10733](https://github.com/vatesfr/xen-orchestra/pull/10733))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user