mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 11:46:06 +08:00
Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d34279d98f | ||
|
|
efd93c5e52 | ||
|
|
8d338b5f72 | ||
|
|
f59c81da31 | ||
|
|
cf6d44601e | ||
|
|
89b43060df | ||
|
|
7194d52000 | ||
|
|
a8ea0645e4 | ||
|
|
713173b0e4 | ||
|
|
6ed13bcec1 | ||
|
|
02519820d7 | ||
|
|
2604101eb2 | ||
|
|
1cb16f0c74 | ||
|
|
752cf2e93a | ||
|
|
f4ea7b6ea0 | ||
|
|
a6405989e9 | ||
|
|
fb8a81fd0f | ||
|
|
3a5ead3c2c | ||
|
|
def4b64cd3 | ||
|
|
a191b845d8 | ||
|
|
32a57d791a | ||
|
|
2a7e51ef4c | ||
|
|
f726fb99e5 | ||
|
|
9c19ae1f9b | ||
|
|
6ece2d0284 | ||
|
|
aec424b007 | ||
|
|
ff80b91311 | ||
|
|
52252c16f7 | ||
|
|
19cf1e9302 | ||
|
|
ddb8e8106b | ||
|
|
9af5ff7b12 | ||
|
|
fff6153f15 | ||
|
|
1ecac7adf4 | ||
|
|
408818904a | ||
|
|
b96637e6d9 | ||
|
|
8bdaca777f | ||
|
|
d278687e75 | ||
|
|
3ea8edb824 | ||
|
|
e33302050b | ||
|
|
8fefb59802 | ||
|
|
872aec1c10 | ||
|
|
8814bd4cda | ||
|
|
72a17cd9c2 | ||
|
|
8d79b69748 | ||
|
|
47760a8426 | ||
|
|
e6fc641a29 | ||
|
|
90b85d2be7 | ||
|
|
ee9575f710 | ||
|
|
7d97e6821c | ||
|
|
42fc9d8fae | ||
|
|
0f03fc0805 | ||
|
|
6d4a7cd1b1 | ||
|
|
f69d445ef7 | ||
|
|
d358a0a618 | ||
|
|
47841e49b1 | ||
|
|
57c7f0f856 |
@@ -0,0 +1,19 @@
|
||||
pull_request_rules:
|
||||
- name: Automatically open backport PR to release-harvester-v1.5
|
||||
conditions:
|
||||
- base=main
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- "release-harvester-v1.5"
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
labels:
|
||||
- "v1.5 backport PR"
|
||||
|
||||
- name: Ask to resolve conflict
|
||||
conditions:
|
||||
- conflict
|
||||
actions:
|
||||
comment:
|
||||
message: This pull request is now in conflict. Could you fix it @{{author}}? 🙏
|
||||
@@ -0,0 +1,21 @@
|
||||
name: Build Dashboard (Release)
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v[1-9].*
|
||||
|
||||
jobs:
|
||||
build-validation:
|
||||
name: Build Test
|
||||
uses: ./.github/workflows/test.yaml
|
||||
build:
|
||||
name: Build and Upload Package
|
||||
uses: ./.github/workflows/build-and-upload.yaml
|
||||
needs:
|
||||
- build-validation
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
with:
|
||||
CI_BUILD_TAG: ${{github.ref_name}}
|
||||
@@ -70,7 +70,7 @@ For developers, after reading through the introduction on this page, head over t
|
||||
|
||||
License
|
||||
=======
|
||||
Copyright (c) 2014-2023 [Rancher Labs, Inc.](http://rancher.com)
|
||||
Copyright (c) 2014-2025 [SUSE, LLC.](https://www.suse.com/)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
"color": "4.2.3",
|
||||
"ip": "2.0.1",
|
||||
"node-polyfill-webpack-plugin": "^3.0.0",
|
||||
"sortablejs-vue3": "^1.2.11",
|
||||
"vue-draggable-next": "^2.2.1",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"resolutions": {
|
||||
@@ -46,4 +46,4 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^7.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ export default {
|
||||
|
||||
return Array.from(new Set(options));
|
||||
},
|
||||
enableFilterButton() {
|
||||
return this.rows.some((r) => r.sourceSchedule !== undefined);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -63,27 +66,30 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vm-schedule-filter">
|
||||
<template>
|
||||
<span
|
||||
v-if="selected"
|
||||
class="banner-item bg-warning"
|
||||
>
|
||||
{{ t('harvester.tableHeaders.vmSchedule') }}{{ selected ? ` = ${selected}`: '' }}<i
|
||||
class="icon icon-close ml-5"
|
||||
@click="remove"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="vm-schedule-filter"
|
||||
>
|
||||
<span
|
||||
v-if="selected"
|
||||
class="banner-item bg-warning"
|
||||
>
|
||||
{{ t('harvester.tableHeaders.vmSchedule') }}{{ selected ? ` = ${selected}`: '' }}
|
||||
<i
|
||||
class="icon icon-close ml-5"
|
||||
@click="remove"
|
||||
/>
|
||||
</span>
|
||||
<v-dropdown
|
||||
popper-class="vm-schedule-dropdown"
|
||||
:triggers="scheduleOptions.length ? ['click'] : []"
|
||||
placement="bottom-end"
|
||||
offset="1"
|
||||
:distance="20"
|
||||
>
|
||||
<button
|
||||
ref="actionDropDown"
|
||||
class="btn bg-primary mr-10"
|
||||
:disabled="!enableFilterButton"
|
||||
>
|
||||
<slot name="title">
|
||||
{{ t('harvester.fields.filterSchedule') }}
|
||||
@@ -98,7 +104,7 @@ export default {
|
||||
name="model"
|
||||
:options="scheduleOptions"
|
||||
:labels="scheduleOptions"
|
||||
@input="onSelect"
|
||||
@update:value="onSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -106,6 +112,12 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.vm-schedule-dropdown .v-popper__arrow-container {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vm-schedule-filter {
|
||||
display: inline-block;
|
||||
|
||||
@@ -60,7 +60,8 @@ export default {
|
||||
});
|
||||
|
||||
this.queue = [];
|
||||
}, 5)
|
||||
}, 10),
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ const featuresV132 = [
|
||||
'improveMaintenanceMode',
|
||||
];
|
||||
|
||||
// TODO: add v1.3.3 official release note
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.3.3-dev-20250105
|
||||
const featuresV133 = [
|
||||
...featuresV132,
|
||||
];
|
||||
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0
|
||||
const featuresV140 = [
|
||||
...featuresV132,
|
||||
@@ -35,10 +41,17 @@ const featuresV141 = [
|
||||
...featuresV140
|
||||
];
|
||||
|
||||
// TODO: add v1.5.0 official release note
|
||||
const featuresV150 = [
|
||||
...featuresV141
|
||||
];
|
||||
|
||||
export const RELEASE_FEATURES = {
|
||||
'v1.3.0': featuresV130,
|
||||
'v1.3.1': featuresV131,
|
||||
'v1.3.2': featuresV132,
|
||||
'v1.3.3': featuresV133,
|
||||
'v1.4.0': featuresV140,
|
||||
'v1.4.1': featuresV141,
|
||||
'v1.5.0': featuresV150
|
||||
};
|
||||
|
||||
@@ -24,35 +24,24 @@ export default {
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
const hash = await allHash({
|
||||
await allHash({
|
||||
vms: this.$store.dispatch('harvester/findAll', { type: HCI.VM }),
|
||||
vmis: this.$store.dispatch('harvester/findAll', { type: HCI.VMI }),
|
||||
allClusterNetwork: this.$store.dispatch('harvester/findAll', { type: HCI.CLUSTER_NETWORK }),
|
||||
});
|
||||
const instanceMap = {};
|
||||
|
||||
(hash.vmis || []).forEach((vmi) => {
|
||||
const vmiUID = vmi?.metadata?.ownerReferences?.[0]?.uid;
|
||||
|
||||
if (vmiUID) {
|
||||
instanceMap[vmiUID] = vmi;
|
||||
}
|
||||
});
|
||||
|
||||
this.allClusterNetwork = hash.allClusterNetwork;
|
||||
this.rows = hash.vms.filter((row) => {
|
||||
return instanceMap[row.metadata?.uid]?.status?.nodeName === this.node?.metadata?.labels?.[HOSTNAME];
|
||||
});
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
rows: [],
|
||||
allClusterNetwork: []
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
allClusterNetwork() {
|
||||
return this.$store.getters['harvester/all'](HCI.CLUSTER_NETWORK);
|
||||
},
|
||||
|
||||
rows() {
|
||||
const vms = this.$store.getters['harvester/all'](HCI.VM);
|
||||
|
||||
return vms.filter((vm) => vm.vmi?.status?.nodeName === this.node?.metadata?.labels?.[HOSTNAME]);
|
||||
},
|
||||
|
||||
headers() {
|
||||
return [
|
||||
STATE,
|
||||
@@ -87,8 +76,6 @@ export default {
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||
import { alternateLabel } from '@shell/utils/platform';
|
||||
import AsyncButton from '@shell/components/AsyncButton';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { Card } from '@components/Card';
|
||||
import { escapeHtml } from '@shell/utils/string';
|
||||
|
||||
/**
|
||||
* @name ConfirmExecutionDialog
|
||||
* @description Dialog component to confirm the related resources before executing the action.
|
||||
*/
|
||||
export default {
|
||||
name: 'ConfirmExecutionDialog',
|
||||
|
||||
emits: ['close'],
|
||||
|
||||
components: {
|
||||
AsyncButton,
|
||||
Banner,
|
||||
Card,
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* @property resources to be deleted.
|
||||
* @type {Resource[]} Array of the resource model's instance
|
||||
*/
|
||||
resources: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return { errors: [] };
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState('action-menu', ['modalData']),
|
||||
|
||||
warningMessageKey() {
|
||||
return this.modalData.warningMessageKey;
|
||||
},
|
||||
|
||||
names() {
|
||||
return this.resources.map((obj) => obj.nameDisplay).slice(0, 5);
|
||||
},
|
||||
|
||||
resourceNames() {
|
||||
return this.names.reduce((res, name, i) => {
|
||||
if (i >= 5) {
|
||||
return res;
|
||||
}
|
||||
res += `<b>${ escapeHtml(name) }</b>`;
|
||||
if (i === this.names.length - 1) {
|
||||
res += this.plusMore;
|
||||
} else {
|
||||
res += i === this.resources.length - 2 ? ' and ' : ', ';
|
||||
}
|
||||
|
||||
return res;
|
||||
}, '');
|
||||
},
|
||||
|
||||
plusMore() {
|
||||
const remaining = this.resources.length - this.names.length;
|
||||
|
||||
return this.t('dialog.confirmExecution.andOthers', { count: remaining });
|
||||
},
|
||||
|
||||
type() {
|
||||
const types = new Set(this.resources.reduce((array, each) => {
|
||||
array.push(each.type);
|
||||
|
||||
return array;
|
||||
}, []));
|
||||
|
||||
if (types.size > 1) {
|
||||
return this.t('generic.resource', { count: this.resources.length });
|
||||
}
|
||||
|
||||
const schema = this.resources[0]?.schema;
|
||||
|
||||
if ( !schema ) {
|
||||
return `resource${ this.resources.length === 1 ? '' : 's' }`;
|
||||
}
|
||||
|
||||
return this.$store.getters['type-map/labelFor'](schema, this.resources.length);
|
||||
},
|
||||
|
||||
protip() {
|
||||
return this.t('dialog.confirmExecution.protip', { alternateLabel });
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
escapeHtml,
|
||||
|
||||
close() {
|
||||
this.errors = [];
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
async apply(buttonDone) {
|
||||
try {
|
||||
for (const resource of this.resources) {
|
||||
await resource.doActionGrowl(this.modalData.action, {});
|
||||
}
|
||||
buttonDone(true);
|
||||
this.close();
|
||||
} catch (e) {
|
||||
this.errors = exceptionToErrorsArray(e);
|
||||
buttonDone(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card :show-highlight-border="false">
|
||||
<template #title>
|
||||
<h4 class="text-default-text">
|
||||
{{ t('dialog.confirmExecution.title') }}
|
||||
</h4>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div class="pl-10 pr-10">
|
||||
<span
|
||||
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
|
||||
></span>
|
||||
<div class="text-info mt-20">
|
||||
{{ protip }}
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(error, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<div class="actions">
|
||||
<button
|
||||
class="btn role-secondary"
|
||||
@click="close"
|
||||
>
|
||||
{{ t('generic.cancel') }}
|
||||
</button>
|
||||
<AsyncButton
|
||||
mode="apply"
|
||||
class="btn bg-primary ml-10"
|
||||
:disabled="applyDisabled"
|
||||
@click="apply"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.modal-container {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -1,34 +1,31 @@
|
||||
<script>
|
||||
import merge from 'lodash/merge';
|
||||
import jsyaml from 'js-yaml';
|
||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||
import Tabbed from '@shell/components/Tabbed';
|
||||
import Tab from '@shell/components/Tabbed/Tab';
|
||||
import { RadioGroup } from '@components/Form/Radio';
|
||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||
|
||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||
|
||||
import { STORAGE_CLASS } from '@shell/config/types';
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import { set, get, clone } from '@shell/utils/object';
|
||||
|
||||
const VALUES_YAML_KEYS = [
|
||||
'resources.requests.cpu',
|
||||
'resources.requests.memory',
|
||||
'resources.limits.cpu',
|
||||
'resources.limits.memory',
|
||||
'pvcClaim.enabled',
|
||||
'pvcClaim.size',
|
||||
'pvcClaim.storageClassName',
|
||||
];
|
||||
|
||||
const DEFAULT_VALUES = {
|
||||
'resources.requests.cpu': '0.5',
|
||||
'resources.requests.memory': '2Gi',
|
||||
'resources.limits.cpu': '2',
|
||||
'resources.limits.memory': '4Gi',
|
||||
'pvcClaim.enabled': false,
|
||||
'pvcClaim.size': '200Gi',
|
||||
'pvcClaim.storageClassName': '',
|
||||
resources: {
|
||||
requests: {
|
||||
cpu: '0.5',
|
||||
memory: '2Gi'
|
||||
},
|
||||
limits: {
|
||||
cpu: '2',
|
||||
memory: '4Gi'
|
||||
}
|
||||
},
|
||||
pvcClaim: {
|
||||
enabled: false,
|
||||
size: '200Gi',
|
||||
storageClassName: ''
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -64,19 +61,7 @@ export default {
|
||||
},
|
||||
|
||||
data() {
|
||||
let valuesObj = {};
|
||||
|
||||
try {
|
||||
valuesObj = JSON.parse(this.value?.spec?.valuesContent || '{}');
|
||||
} catch (err) {}
|
||||
|
||||
const valuesContent = clone(valuesObj);
|
||||
|
||||
VALUES_YAML_KEYS.map((key) => {
|
||||
if (!get(valuesObj, key)) {
|
||||
set(valuesContent, key, DEFAULT_VALUES[key]);
|
||||
}
|
||||
});
|
||||
const valuesContent = this.parseValuesContent();
|
||||
|
||||
return { valuesContent };
|
||||
},
|
||||
@@ -100,8 +85,21 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
parseValuesContent() {
|
||||
try {
|
||||
return merge({}, DEFAULT_VALUES, jsyaml.load(this.value.spec.valuesContent));
|
||||
} catch (err) {
|
||||
this.$store.dispatch('growl/fromError', {
|
||||
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
|
||||
err: err.data || err,
|
||||
}, { root: true });
|
||||
|
||||
return DEFAULT_VALUES;
|
||||
}
|
||||
},
|
||||
|
||||
update() {
|
||||
set(this.value, 'spec.valuesContent', JSON.stringify(this.valuesContent));
|
||||
this.value.spec.valuesContent = jsyaml.dump(this.valuesContent);
|
||||
},
|
||||
|
||||
setDefaultClassName() {
|
||||
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
const selectedProviderDefault = providers.find((p) => p.name === selectedProvider)?.default || providers[0].default;
|
||||
|
||||
if (this.mode !== _VIEW) {
|
||||
this.$set(this.value.spec, selectedProvider, this.value.spec[selectedProvider] || clone(selectedProviderDefault));
|
||||
set(this.value.spec, selectedProvider, this.value.spec[selectedProvider] || clone(selectedProviderDefault));
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -24,6 +24,7 @@ const CLONE = 'clone';
|
||||
const DOWNLOAD = 'download';
|
||||
const UPLOAD = 'upload';
|
||||
const rawORqcow2 = 'raw_qcow2';
|
||||
const LONGHORN = 'longhorn';
|
||||
|
||||
export default {
|
||||
name: 'EditImage',
|
||||
@@ -60,10 +61,7 @@ export default {
|
||||
images: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IMAGE }),
|
||||
storageClasses: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }),
|
||||
});
|
||||
|
||||
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
|
||||
|
||||
this['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
|
||||
this['storageClassName'] = this.storageClassName || this.defaultStorageClassName();
|
||||
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
|
||||
|
||||
const { securityParameters } = this.value.spec;
|
||||
@@ -144,10 +142,23 @@ export default {
|
||||
];
|
||||
},
|
||||
|
||||
storageClassOptions() {
|
||||
encryptedStorageClasses() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
||||
|
||||
return storages.filter((s) => s.isEncrypted);
|
||||
},
|
||||
|
||||
nonEncryptedStorageClasses() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
||||
|
||||
return storages.filter((s) => !s.isEncrypted);
|
||||
},
|
||||
|
||||
storageClassOptions() {
|
||||
const storages = this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT ? this.encryptedStorageClasses : this.nonEncryptedStorageClasses;
|
||||
|
||||
return storages
|
||||
.filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported.
|
||||
.map((s) => {
|
||||
@@ -239,6 +250,13 @@ export default {
|
||||
this.$refs.file.value = null;
|
||||
}
|
||||
},
|
||||
'value.spec.securityParameters.cryptoOperation'() {
|
||||
if (this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT) {
|
||||
this.storageClassName = this.encryptedStorageClasses[0]?.name || '';
|
||||
} else { // URL / FILE / DECRYPT should use default storage class
|
||||
this.storageClassName = this.defaultStorageClassName();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -385,6 +403,18 @@ export default {
|
||||
return str.toLowerCase().includes(os.value.toLowerCase()) ? os.value : false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
defaultStorageClassName() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
|
||||
|
||||
if (!defaultStorage) {
|
||||
return LONGHORN;
|
||||
}
|
||||
|
||||
// if default sc is encrypted, use longhorn as default
|
||||
return defaultStorage.isEncrypted ? LONGHORN : defaultStorage?.metadata?.name;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { Sortable } from 'sortablejs-vue3';
|
||||
import { VueDraggableNext } from 'vue-draggable-next';
|
||||
import InfoBox from '@shell/components/InfoBox';
|
||||
import { Banner } from '@components/Banner';
|
||||
import BadgeStateFormatter from '@shell/components/formatter/BadgeStateFormatter';
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
emits: ['update:value'],
|
||||
|
||||
components: {
|
||||
Banner, BadgeStateFormatter, Sortable, InfoBox, LabeledInput, UnitInput, LabeledSelect, ModalWithCard
|
||||
Banner, BadgeStateFormatter, VueDraggableNext, InfoBox, LabeledInput, UnitInput, LabeledSelect, ModalWithCard
|
||||
},
|
||||
|
||||
props: {
|
||||
@@ -272,129 +272,130 @@ export default {
|
||||
color="info"
|
||||
label-key="harvester.virtualMachine.volume.dragTip"
|
||||
/>
|
||||
<Sortable
|
||||
<VueDraggableNext
|
||||
:list="rows"
|
||||
:options="{disabled: isView}"
|
||||
:disabled="isView"
|
||||
item-key="id"
|
||||
@end="update"
|
||||
>
|
||||
<template #item="{element: volume, index: i}">
|
||||
<div :key="volume.name">
|
||||
<InfoBox class="box">
|
||||
<button
|
||||
<div
|
||||
v-for="(volume, i) in rows"
|
||||
:key="volume.id"
|
||||
>
|
||||
<InfoBox class="box">
|
||||
<button
|
||||
v-if="!isView"
|
||||
type="button"
|
||||
class="role-link btn btn-sm remove"
|
||||
@click="removeVolume(volume)"
|
||||
>
|
||||
<i class="icon icon-x" />
|
||||
</button>
|
||||
<button
|
||||
v-if="volume.hotpluggable && isView"
|
||||
type="button"
|
||||
class="role-link btn remove"
|
||||
@click="unplugVolume(volume)"
|
||||
>
|
||||
{{ t('harvester.virtualMachine.unplug.detachVolume') }}
|
||||
</button>
|
||||
<h3>
|
||||
<span
|
||||
v-if="volume.to && isVirtualType"
|
||||
class="title"
|
||||
>
|
||||
<router-link :to="volume.to">
|
||||
{{ t('harvester.virtualMachine.volume.edit') }} {{ headerFor(volume.source) }}
|
||||
</router-link>
|
||||
|
||||
<BadgeStateFormatter
|
||||
v-if="volume.pvc"
|
||||
class="ml-10 state"
|
||||
:arbitrary="true"
|
||||
:row="volume.pvc"
|
||||
:value="volume.pvc.state"
|
||||
/>
|
||||
<a
|
||||
v-if="dev && !!volume.pvc && !!volume.pvc.resourceExternalLink"
|
||||
v-clean-tooltip="t(volume.pvc.resourceExternalLink.tipsKey || 'generic.resourceExternalLinkTips')"
|
||||
class="ml-5 resource-external"
|
||||
rel="nofollow noopener noreferrer"
|
||||
target="_blank"
|
||||
:href="volume.pvc.resourceExternalLink.url"
|
||||
>
|
||||
<i class="icon icon-external-link" />
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<span v-else>
|
||||
{{ headerFor(volume.source, !!volume?.volumeBackups) }}
|
||||
</span>
|
||||
</h3>
|
||||
<div>
|
||||
<component
|
||||
:is="componentFor(volume.source)"
|
||||
:value="rows[i]"
|
||||
:rows="rows"
|
||||
:namespace="namespace"
|
||||
:is-create="isCreate"
|
||||
:is-edit="isEdit"
|
||||
:is-view="isView"
|
||||
:is-virtual-type="isVirtualType"
|
||||
:mode="mode"
|
||||
:idx="i"
|
||||
:validate-required="validateRequired"
|
||||
@update="update"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="bootOrder">
|
||||
<div
|
||||
v-if="!isView"
|
||||
type="button"
|
||||
class="role-link btn btn-sm remove"
|
||||
@click="removeVolume(volume)"
|
||||
class="mr-15"
|
||||
>
|
||||
<i class="icon icon-x" />
|
||||
</button>
|
||||
<button
|
||||
v-if="volume.hotpluggable && isView"
|
||||
type="button"
|
||||
class="role-link btn remove"
|
||||
@click="unplugVolume(volume)"
|
||||
>
|
||||
{{ t('harvester.virtualMachine.unplug.detachVolume') }}
|
||||
</button>
|
||||
<h3>
|
||||
<span
|
||||
v-if="volume.to && isVirtualType"
|
||||
class="title"
|
||||
<button
|
||||
:disabled="i === 0"
|
||||
class="btn btn-sm role-primary"
|
||||
@click.prevent="changeSort(i, false)"
|
||||
>
|
||||
<router-link :to="volume.to">
|
||||
{{ t('harvester.virtualMachine.volume.edit') }} {{ headerFor(volume.source) }}
|
||||
</router-link>
|
||||
<i class="icon icon-lg icon-chevron-up"></i>
|
||||
</button>
|
||||
|
||||
<BadgeStateFormatter
|
||||
v-if="volume.pvc"
|
||||
class="ml-10 state"
|
||||
:arbitrary="true"
|
||||
:row="volume.pvc"
|
||||
:value="volume.pvc.state"
|
||||
/>
|
||||
<a
|
||||
v-if="dev && !!volume.pvc && !!volume.pvc.resourceExternalLink"
|
||||
v-clean-tooltip="t(volume.pvc.resourceExternalLink.tipsKey || 'generic.resourceExternalLinkTips')"
|
||||
class="ml-5 resource-external"
|
||||
rel="nofollow noopener noreferrer"
|
||||
target="_blank"
|
||||
:href="volume.pvc.resourceExternalLink.url"
|
||||
>
|
||||
<i class="icon icon-external-link" />
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<span v-else>
|
||||
{{ headerFor(volume.source, !!volume?.volumeBackups) }}
|
||||
</span>
|
||||
</h3>
|
||||
<div>
|
||||
<component
|
||||
:is="componentFor(volume.source)"
|
||||
:value="rows[i]"
|
||||
:rows="rows"
|
||||
:namespace="namespace"
|
||||
:is-create="isCreate"
|
||||
:is-edit="isEdit"
|
||||
:is-view="isView"
|
||||
:is-virtual-type="isVirtualType"
|
||||
:mode="mode"
|
||||
:idx="i"
|
||||
:validate-required="validateRequired"
|
||||
@update="update"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="bootOrder">
|
||||
<div
|
||||
v-if="!isView"
|
||||
class="mr-15"
|
||||
<button
|
||||
:disabled="i === rows.length -1"
|
||||
class="btn btn-sm role-primary"
|
||||
@click.prevent="changeSort(i, true)"
|
||||
>
|
||||
<button
|
||||
:disabled="i === 0"
|
||||
class="btn btn-sm role-primary"
|
||||
@click.prevent="changeSort(i, false)"
|
||||
>
|
||||
<i class="icon icon-lg icon-chevron-up"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
:disabled="i === rows.length -1"
|
||||
class="btn btn-sm role-primary"
|
||||
@click.prevent="changeSort(i, true)"
|
||||
>
|
||||
<i class="icon icon-lg icon-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-muted">
|
||||
bootOrder: {{ i + 1 }}
|
||||
</div>
|
||||
<i class="icon icon-lg icon-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-15">
|
||||
<Banner
|
||||
v-if="volume.volumeStatus && !isCreate"
|
||||
class="volume-status"
|
||||
color="warning"
|
||||
:label="ucFirst(volume.volumeStatus)"
|
||||
/>
|
||||
<Banner
|
||||
v-if="value.volumeBackups && value.volumeBackups.error && value.volumeBackups.error.message"
|
||||
color="error"
|
||||
:label="ucFirst(value.volumeBackups.error.message)"
|
||||
/>
|
||||
<Banner
|
||||
v-if="isLonghornV2(volume) && !isView"
|
||||
color="warning"
|
||||
:label="t('harvester.volume.longhorn.disableResize')"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
bootOrder: {{ i + 1 }}
|
||||
</div>
|
||||
</InfoBox>
|
||||
</div>
|
||||
</template>
|
||||
</Sortable>
|
||||
</div>
|
||||
|
||||
<div class="mt-15">
|
||||
<Banner
|
||||
v-if="volume.volumeStatus && !isCreate"
|
||||
class="volume-status"
|
||||
color="warning"
|
||||
:label="ucFirst(volume.volumeStatus)"
|
||||
/>
|
||||
<Banner
|
||||
v-if="value.volumeBackups && value.volumeBackups.error && value.volumeBackups.error.message"
|
||||
color="error"
|
||||
:label="ucFirst(value.volumeBackups.error.message)"
|
||||
/>
|
||||
<Banner
|
||||
v-if="isLonghornV2(volume) && !isView"
|
||||
color="warning"
|
||||
:label="t('harvester.volume.longhorn.disableResize')"
|
||||
/>
|
||||
</div>
|
||||
</InfoBox>
|
||||
</div>
|
||||
</VueDraggableNext>
|
||||
<Banner
|
||||
v-if="showVolumeTip"
|
||||
color="warning"
|
||||
|
||||
@@ -38,6 +38,21 @@ asyncButton:
|
||||
success: Restarted
|
||||
waiting: Restarting…
|
||||
|
||||
dialog:
|
||||
confirmExecution:
|
||||
title: Are you sure?
|
||||
andOthers: |-
|
||||
{count, plural,
|
||||
=0 {}
|
||||
=1 { and <b>one other </b>}
|
||||
other { and <b>{count} other </b>}
|
||||
}
|
||||
protip: "Tip: Hold the {alternateLabel} key while clicking action to bypass this confirmation"
|
||||
stop:
|
||||
message: "Are you sure you want to continue stop the {type} {names}?"
|
||||
pause:
|
||||
message: "Are you sure you want to continue pause the {type} {names}?"
|
||||
|
||||
harvester:
|
||||
productLabel: 'Harvester'
|
||||
modal:
|
||||
@@ -742,6 +757,9 @@ harvester:
|
||||
terminationGracePeriodSeconds:
|
||||
label: Termination Grace Period
|
||||
affinity:
|
||||
addLabel: Add Workload Selector
|
||||
topologyKey:
|
||||
placeholder: 'topology.kubernetes.io/zone'
|
||||
thisPodNamespace: This virtual machine's namespace
|
||||
matchExpressions:
|
||||
inNamespaces: "Workloads in these namespaces"
|
||||
@@ -858,14 +876,14 @@ harvester:
|
||||
doc: Read the <a href="{url}" target="_blank">documentation</a> before starting the upgrade process. Ensure that you complete procedures that are relevant to your environment and the version you are upgrading to.
|
||||
tip: Unmet system requirements and incorrectly performed procedures may cause complete upgrade failure and other issues that require manual workarounds.
|
||||
moreNotes: For more details about the release notes, please visit -
|
||||
|
||||
|
||||
schedule:
|
||||
label: Virtual Machine Schedules
|
||||
createTitle: Create Schedule
|
||||
createButtonText: Create Schedule
|
||||
scheduleType: Virtual Machine Schedule Type
|
||||
cron: Cron Schedule
|
||||
detail:
|
||||
detail:
|
||||
namespace: Namespace
|
||||
sourceVM: Source Virtual Machine
|
||||
tabs:
|
||||
@@ -875,12 +893,12 @@ harvester:
|
||||
message:
|
||||
noSetting:
|
||||
suffix: before creating a backup schedule
|
||||
retain:
|
||||
retain:
|
||||
label: Retain
|
||||
count: Count
|
||||
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
|
||||
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
|
||||
maxFailure:
|
||||
label: Max Failure
|
||||
label: Max Failure
|
||||
count: Count
|
||||
tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count
|
||||
virtualMachine:
|
||||
@@ -1494,18 +1512,6 @@ harvester:
|
||||
pollingInterval:
|
||||
label: Polling Interval
|
||||
|
||||
affinity:
|
||||
thisPodNamespace: This virtual machine's namespace
|
||||
matchExpressions:
|
||||
inNamespaces: "Workloads in these namespaces"
|
||||
vmAffinityTitle: Virtual Machine Scheduling
|
||||
namespaces:
|
||||
placeholder: e.g. default,system,base
|
||||
label: Namespaces
|
||||
addLabel: Add Workload Selector
|
||||
topologyKey:
|
||||
placeholder: 'topology.kubernetes.io/zone'
|
||||
|
||||
advancedSettings:
|
||||
experimental: 'Experimental features allow users to test and evaluate early-access functionality prior to official supported releases'
|
||||
descriptions:
|
||||
|
||||
@@ -217,7 +217,7 @@ export default {
|
||||
const selectedVMNetworks = networkNames.map((name) => vmNetworks.find((n) => n.id === name)).filter((n) => n?.id);
|
||||
const clusterNetworks = uniq(selectedVMNetworks.map((n) => n.clusterNetworkResource?.id));
|
||||
|
||||
return nodes.filter((N) => !N.isUnSchedulable).map((node) => {
|
||||
return nodes.filter((N) => !N.isUnSchedulable && N.isEtcd !== 'true').map((node) => {
|
||||
const requireLabelKeys = [];
|
||||
let isNetworkSchedule = true;
|
||||
|
||||
@@ -282,14 +282,14 @@ export default {
|
||||
|
||||
affinityLabels() {
|
||||
return {
|
||||
namespaceInputLabel: this.t('harvesterManager.affinity.namespaces.label'),
|
||||
namespaceInputLabel: this.t('harvester.virtualMachine.affinity.namespaces.label'),
|
||||
namespaceSelectionLabels: [
|
||||
this.t('harvesterManager.affinity.thisPodNamespace'),
|
||||
this.t('harvester.virtualMachine.affinity.thisPodNamespace'),
|
||||
this.t('workload.scheduling.affinity.allNamespaces'),
|
||||
this.t('harvesterManager.affinity.matchExpressions.inNamespaces')
|
||||
this.t('harvester.virtualMachine.affinity.matchExpressions.inNamespaces')
|
||||
],
|
||||
addLabel: this.t('harvesterManager.affinity.addLabel'),
|
||||
topologyKeyPlaceholder: this.t('harvesterManager.affinity.topologyKey.placeholder')
|
||||
addLabel: this.t('harvester.virtualMachine.affinity.addLabel'),
|
||||
topologyKeyPlaceholder: this.t('harvester.virtualMachine.affinity.topologyKey.placeholder')
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -470,7 +470,7 @@ export default class HciNode extends HarvesterResource {
|
||||
get canDelete() {
|
||||
const nodes = this.$rootGetters['harvester/all'](NODE) || [];
|
||||
|
||||
return nodes.length > 1;
|
||||
return nodes.length > 1 && super.canDelete;
|
||||
}
|
||||
|
||||
get vlanStatuses() {
|
||||
|
||||
@@ -58,6 +58,10 @@ export default class HciStorageClass extends StorageClass {
|
||||
return key ? this.$rootGetters['i18n/t'](key) : this.provisioner;
|
||||
}
|
||||
|
||||
get isEncrypted() {
|
||||
return this.parameters?.encrypted === 'true';
|
||||
}
|
||||
|
||||
get isLonghornV2() {
|
||||
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import HarvesterResource from './harvester';
|
||||
import { get } from '@shell/utils/object';
|
||||
import { findBy } from '@shell/utils/array';
|
||||
import { colorForState, stateDisplay, STATES } from '@shell/plugins/dashboard-store/resource-class';
|
||||
import { _CREATE } from '@shell/config/query-params';
|
||||
import { ucFirst, escapeHtml } from '@shell/utils/string';
|
||||
@@ -66,15 +64,12 @@ export default class ScheduleVmBackup extends HarvesterResource {
|
||||
}
|
||||
}
|
||||
|
||||
get stateObj() {
|
||||
return this?.metadata?.state || {};
|
||||
}
|
||||
|
||||
get state() {
|
||||
const conditions = get(this, 'status.conditions');
|
||||
const isSuspended = findBy(conditions, 'type', 'BackupSuspend')?.status === 'True';
|
||||
|
||||
if (isSuspended) {
|
||||
return STATES.suspended.label;
|
||||
}
|
||||
|
||||
return this.metadata.state.name;
|
||||
return this.status?.suspended === true ? STATES.suspended.label : STATES.active.label;
|
||||
}
|
||||
|
||||
get stateDescription() {
|
||||
|
||||
@@ -132,7 +132,7 @@ export default class HciVmBackup extends HarvesterResource {
|
||||
}
|
||||
|
||||
get sourceSchedule() {
|
||||
return this.metadata?.annotations[HCI_ANNOTATIONS.SVM_BACKUP_ID];
|
||||
return this.metadata?.annotations?.[HCI_ANNOTATIONS.SVM_BACKUP_ID];
|
||||
}
|
||||
|
||||
get attachVM() {
|
||||
|
||||
@@ -102,11 +102,13 @@ export default class VirtVm extends HarvesterResource {
|
||||
|
||||
return [
|
||||
{
|
||||
action: 'stopVM',
|
||||
enabled: !!this.actions?.stop,
|
||||
icon: 'icon icon-close',
|
||||
label: this.t('harvester.action.stop'),
|
||||
bulkable: true
|
||||
action: 'stopVM',
|
||||
altAction: 'altStopVM',
|
||||
enabled: !!this.actions?.stop,
|
||||
icon: 'icon icon-close',
|
||||
label: this.t('harvester.action.stop'),
|
||||
bulkable: true,
|
||||
bulkAction: 'stopVM',
|
||||
},
|
||||
{
|
||||
action: 'forceStop',
|
||||
@@ -116,10 +118,11 @@ export default class VirtVm extends HarvesterResource {
|
||||
bulkable: true
|
||||
},
|
||||
{
|
||||
action: 'pauseVM',
|
||||
enabled: !!this.actions?.pause,
|
||||
icon: 'icon icon-pause',
|
||||
label: this.t('harvester.action.pause')
|
||||
action: 'pauseVM',
|
||||
altAction: 'altPauseVM',
|
||||
enabled: !!this.actions?.pause,
|
||||
icon: 'icon icon-pause',
|
||||
label: this.t('harvester.action.pause')
|
||||
},
|
||||
{
|
||||
action: 'unpauseVM',
|
||||
@@ -402,7 +405,16 @@ export default class VirtVm extends HarvesterResource {
|
||||
return node?.id;
|
||||
}
|
||||
|
||||
pauseVM() {
|
||||
pauseVM(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
action: 'pause',
|
||||
warningMessageKey: 'dialog.confirmExecution.pause.message',
|
||||
component: 'ConfirmExecutionDialog'
|
||||
});
|
||||
}
|
||||
|
||||
altPauseVM() {
|
||||
this.doActionGrowl('pause', {});
|
||||
}
|
||||
|
||||
@@ -417,7 +429,16 @@ export default class VirtVm extends HarvesterResource {
|
||||
this.doActionGrowl('unpause', {});
|
||||
}
|
||||
|
||||
stopVM() {
|
||||
stopVM(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
action: 'stop',
|
||||
warningMessageKey: 'dialog.confirmExecution.stop.message',
|
||||
component: 'ConfirmExecutionDialog'
|
||||
});
|
||||
}
|
||||
|
||||
altStopVM() {
|
||||
this.doActionGrowl('stop', {});
|
||||
}
|
||||
|
||||
@@ -1120,10 +1141,6 @@ export default class VirtVm extends HarvesterResource {
|
||||
return qemu?.status === 'True';
|
||||
}
|
||||
|
||||
get warnDeletionMessage() {
|
||||
return this.t('harvester.virtualMachine.promptRemove.tips');
|
||||
}
|
||||
|
||||
get instanceLabels() {
|
||||
const all = this.spec?.template?.metadata?.labels || {};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "harvester",
|
||||
"description": "Rancher UI Extension for Harvester",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"private": false,
|
||||
"rancher": {
|
||||
"annotations": {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isEmpty } from '@shell/utils/object';
|
||||
import Parse from 'url-parse';
|
||||
import { resourceNames } from '@shell/utils/string';
|
||||
import { HCI } from '../types';
|
||||
import { alternateLabel as alternateLabelButton } from '@shell/utils/platform';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterPromptRemove',
|
||||
@@ -39,8 +40,9 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
checkedList: [],
|
||||
checkAll: true
|
||||
checkedList: [],
|
||||
checkAll: true,
|
||||
alternateLabel: alternateLabelButton
|
||||
};
|
||||
},
|
||||
|
||||
@@ -138,44 +140,53 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-10">
|
||||
{{ t('promptRemove.attemptingToRemove', {type}) }}
|
||||
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
|
||||
|
||||
<div>
|
||||
<div class="mt-10">
|
||||
{{ t('harvester.virtualMachine.promptRemove.title') }}
|
||||
</div>
|
||||
<div v-if="value.length === 1">
|
||||
<span
|
||||
v-for="(name, i) in removeNameArr[value[0].id]"
|
||||
:key="i"
|
||||
>
|
||||
<label class="checkbox-container mr-15"><input
|
||||
v-model="checkedList"
|
||||
type="checkbox"
|
||||
:label="name"
|
||||
:value="name"
|
||||
/>
|
||||
{{ t('promptRemove.attemptingToRemove', {type}) }}
|
||||
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ t('harvester.virtualMachine.promptRemove.title') }}
|
||||
</div>
|
||||
<div v-if="value.length === 1">
|
||||
<span
|
||||
v-for="(name, i) in removeNameArr[value[0].id]"
|
||||
:key="i"
|
||||
>
|
||||
<label class="checkbox-container mr-15">
|
||||
<input
|
||||
v-model="checkedList"
|
||||
type="checkbox"
|
||||
:label="name"
|
||||
:value="name"
|
||||
/>
|
||||
<span
|
||||
class="checkbox-custom mr-5"
|
||||
role="checkbox"
|
||||
/>
|
||||
{{ name }}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label class="checkbox-container mr-15">
|
||||
<input
|
||||
v-model="checkedList"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="checkbox-custom mr-5"
|
||||
role="checkbox"
|
||||
/>
|
||||
{{ name }}
|
||||
{{ t('harvester.virtualMachine.promptRemove.deleteAll') }}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<label class="checkbox-container mr-15"><input
|
||||
v-model="checkAll"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="checkbox-custom mr-5"
|
||||
role="checkbox"
|
||||
/>
|
||||
{{ t('harvester.virtualMachine.promptRemove.deleteAll') }}
|
||||
</label>
|
||||
<div class="text-warning mb-10 mt-10">
|
||||
{{ t('harvester.virtualMachine.promptRemove.tips') }}
|
||||
</div>
|
||||
<div class="text-info mt-20">
|
||||
{{ t('promptRemove.protip', { alternateLabel }) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4313,17 +4313,6 @@
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-core@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.7.tgz#04300bdc9fb52f89e6f250bbac16e03f0e0ed914"
|
||||
integrity sha512-A0gay3lK71MddsSnGlBxRPOugIVdACze9L/rCo5X5srCyjQfZOfYtSFMJc3aOZCM+xN55EQpb4R97rYn/iEbSw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.25.3"
|
||||
"@vue/shared" "3.5.7"
|
||||
entities "^4.5.0"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-dom@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz#a0b06caf7ef7056939e563dcaa9cbde30794f305"
|
||||
@@ -4348,14 +4337,6 @@
|
||||
"@vue/compiler-core" "3.5.1"
|
||||
"@vue/shared" "3.5.1"
|
||||
|
||||
"@vue/compiler-dom@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.7.tgz#604ced082189b66cb811068332a45dcc11ae0af3"
|
||||
integrity sha512-GYWl3+gO8/g0ZdYaJ18fYHdI/WVic2VuuUd1NsPp60DWXKy+XjdhFsDW7FbUto8siYYZcosBGn9yVBkjhq1M8Q==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
"@vue/compiler-sfc@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
|
||||
@@ -4387,21 +4368,6 @@
|
||||
postcss "^8.4.44"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-sfc@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.7.tgz#1150c49c0e3b39d40b2cf0f7de9edfcba98fa3e9"
|
||||
integrity sha512-EjOJtCWJrC7HqoCEzOwpIYHm+JH7YmkxC1hG6VkqIukYRqj8KFUlTLK6hcT4nGgtVov2+ZfrdrRlcaqS78HnBA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.25.3"
|
||||
"@vue/compiler-core" "3.5.7"
|
||||
"@vue/compiler-dom" "3.5.7"
|
||||
"@vue/compiler-ssr" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.11"
|
||||
postcss "^8.4.47"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-sfc@^3.4.15":
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.0.tgz#dc1e34a331d5deea9e258a1e10e1b4d711b1afd6"
|
||||
@@ -4441,14 +4407,6 @@
|
||||
"@vue/compiler-dom" "3.5.1"
|
||||
"@vue/shared" "3.5.1"
|
||||
|
||||
"@vue/compiler-ssr@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.7.tgz#042144dfd574a1f64b685e87730b0196dc1846d2"
|
||||
integrity sha512-oZx+jXP2k5arV/8Ly3TpQbfFyimMw2ANrRqvHJoKjPqtEzazxQGZjCLOfq8TnZ3wy2TOXdqfmVp4q7FyYeHV4g==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.3.0":
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9"
|
||||
@@ -4495,13 +4453,6 @@
|
||||
dependencies:
|
||||
"@vue/shared" "3.5.1"
|
||||
|
||||
"@vue/reactivity@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.7.tgz#a52237fce841d92fc861220a8f26b51f5c3245e2"
|
||||
integrity sha512-yF0EpokpOHRNXyn/h6abXc9JFIzfdAf0MJHIi92xxCWS0mqrXH6+2aZ+A6EbSrspGzX5MHTd5N8iBA28HnXu9g==
|
||||
dependencies:
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
"@vue/runtime-core@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz#406ebade3d5551c00fc6409bbc1eeb10f32e121d"
|
||||
@@ -4518,14 +4469,6 @@
|
||||
"@vue/reactivity" "3.5.1"
|
||||
"@vue/shared" "3.5.1"
|
||||
|
||||
"@vue/runtime-core@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.7.tgz#4181b0a921d331f2efd5eda9aa35549ac97e6530"
|
||||
integrity sha512-OzLpBpKbZEaZVSNfd+hQbfBrDKux+b7Yl5hYhhWWWhHD7fEpF+CdI3Brm5k5GsufHEfvMcjruPxwQZuBN6nFYQ==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
"@vue/runtime-dom@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz#93e760eeaeab84dedfb7c3eaf3ed58d776299382"
|
||||
@@ -4545,16 +4488,6 @@
|
||||
"@vue/shared" "3.5.1"
|
||||
csstype "^3.1.3"
|
||||
|
||||
"@vue/runtime-dom@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.7.tgz#646e006d403f2e6337f566fdf461fbe400e8487d"
|
||||
integrity sha512-fL7cETfE27U2jyTgqzE382IGFY6a6uyznErn27KbbEzNctzxxUWYDbaN3B55l9nXh0xW2LRWPuWKOvjtO2UewQ==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.5.7"
|
||||
"@vue/runtime-core" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
csstype "^3.1.3"
|
||||
|
||||
"@vue/server-renderer@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz#8aa1d1871fc4eb5a7851aa7f741f8f700e6de3c0"
|
||||
@@ -4571,14 +4504,6 @@
|
||||
"@vue/compiler-ssr" "3.5.1"
|
||||
"@vue/shared" "3.5.1"
|
||||
|
||||
"@vue/server-renderer@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.7.tgz#65ba8b60c0ee9e791619c0f8b2b6209a258484e5"
|
||||
integrity sha512-peRypij815eIDjpPpPXvYQGYqPH6QXwLJGWraJYPPn8JqWGl29A8QXnS7/Mh3TkMiOcdsJNhbFCoW2Agc2NgAQ==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
"@vue/shared@3.2.47":
|
||||
version "3.2.47"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c"
|
||||
@@ -4594,11 +4519,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.1.tgz#f9418dae5ac194a4f19023d812978c21a15412a1"
|
||||
integrity sha512-NdcTRoO4KuW2RSFgpE2c+E/R/ZHaRzWPxAGxhmxZaaqLh6nYCXx7lc9a88ioqOCxCaV2SFJmujkxbUScW7dNsQ==
|
||||
|
||||
"@vue/shared@3.5.7":
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.7.tgz#1eedd1ffbf804c488fe806a17ff26c22e0ddb72f"
|
||||
integrity sha512-NBE1PBIvzIedxIc2RZiKXvGbJkrZ2/hLf3h8GlS4/sP9xcXEZMFWOazFkNd6aGeUCMaproe5MHVYB3/4AW9q9g==
|
||||
|
||||
"@vue/test-utils@~2.0.0-0":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.2.tgz#0b5edd683366153d5bc5a91edc62f292118710eb"
|
||||
@@ -11526,7 +11446,7 @@ picocolors@^0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
||||
integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0:
|
||||
picocolors@^1.0.0, picocolors@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
|
||||
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
|
||||
@@ -11862,15 +11782,6 @@ postcss@^8.1.10, postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.3
|
||||
picocolors "^1.0.1"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
postcss@^8.4.47:
|
||||
version "8.4.47"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.1.0"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@@ -12805,34 +12716,16 @@ sockjs@^0.3.24:
|
||||
uuid "^8.3.2"
|
||||
websocket-driver "^0.7.4"
|
||||
|
||||
sortablejs-vue3@^1.2.11:
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs-vue3/-/sortablejs-vue3-1.2.11.tgz#c04f767253a03862a5445d310b335d9fdab71261"
|
||||
integrity sha512-oKOA6N7yu2ktmqYXPHlPTQWbe9G4v16mn5ewogb+Ybc9Bk1Y+MIURrpbgedEv7f9TS5Bptvh81HGjazh5FEyJw==
|
||||
dependencies:
|
||||
sortablejs "^1.15.0"
|
||||
vue "^3.3.7"
|
||||
|
||||
sortablejs@1.14.0:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
|
||||
integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
|
||||
|
||||
sortablejs@^1.15.0:
|
||||
version "1.15.3"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.3.tgz#033668db5ebfb11167d1249ab88e748f27959e29"
|
||||
integrity sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
|
||||
source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.20:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
@@ -13817,6 +13710,11 @@ vue-demi@*:
|
||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
|
||||
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
|
||||
|
||||
vue-draggable-next@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-draggable-next/-/vue-draggable-next-2.2.1.tgz#adbe98c74610cca8f4eb63f92042681f96920451"
|
||||
integrity sha512-EAMS1IRHF0kZO0o5PMOinsQsXIqsrKT1hKmbICxG3UEtn7zLFkLxlAtajcCcUTisNvQ6TtCB5COjD9a1raNADw==
|
||||
|
||||
vue-eslint-parser@^9.0.1:
|
||||
version "9.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz#9b04b22c71401f1e8bca9be7c3e3416a4bde76a8"
|
||||
@@ -13898,17 +13796,6 @@ vue3-virtual-scroll-list@0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/vue3-virtual-scroll-list/-/vue3-virtual-scroll-list-0.2.1.tgz#7fdc5be066c9b8d877614626a0bc02eb1fd12ad0"
|
||||
integrity sha512-G4KxITUOy9D4ro15zOp40D6ogmMefzjIyMsBKqN3xGbV1P6dlKYMx+BBXCKm3Nr/6iipcUKM272Sh2AJRyWMyQ==
|
||||
|
||||
vue@^3.3.7:
|
||||
version "3.5.7"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.7.tgz#511df1fab33a4c20cfe6b59659d6f601f0c26625"
|
||||
integrity sha512-JcFm0f5j8DQO9E07pZRxqZ/ZsNopMVzHYXpKvnfqXFcA4JTi+4YcrikRn9wkzWsdj0YsLzlLIsR0zzGxA2P6Wg==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.5.7"
|
||||
"@vue/compiler-sfc" "3.5.7"
|
||||
"@vue/runtime-dom" "3.5.7"
|
||||
"@vue/server-renderer" "3.5.7"
|
||||
"@vue/shared" "3.5.7"
|
||||
|
||||
vue@^3.4.31:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.1.tgz#507eb4ab010a2ad894f8d7765f32e68be1604815"
|
||||
|
||||
Reference in New Issue
Block a user