bfg-common 1.3.15 → 1.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
:select-options-submit="selectOptionsSubmit"
|
|
40
40
|
@submit="onChangeSelectOptionsAndNext"
|
|
41
41
|
/>
|
|
42
|
-
<common-vm-actions-common-customize-hardware
|
|
42
|
+
<common-vm-actions-common-customize-hardware-old
|
|
43
43
|
v-show="stepPosition === 3"
|
|
44
44
|
v-model:vm-name="vmName"
|
|
45
45
|
v-model:guest-machine-type="vmSettings.guestMachineType"
|
|
@@ -346,10 +346,10 @@ const onFinish = (): void => {
|
|
|
346
346
|
|
|
347
347
|
const selectedNavItem = ref<UI_T_SelectedNavItem>(0)
|
|
348
348
|
|
|
349
|
-
const nameFormSubmit = ref<
|
|
350
|
-
const storageSubmit = ref<
|
|
349
|
+
const nameFormSubmit = ref<number>(0)
|
|
350
|
+
const storageSubmit = ref<number>(0)
|
|
351
351
|
const selectOptionsSubmit = ref<number>(0)
|
|
352
|
-
const customizeHardwareSubmit = ref<
|
|
352
|
+
const customizeHardwareSubmit = ref<number>(0)
|
|
353
353
|
|
|
354
354
|
const onNext = (force = false): void => {
|
|
355
355
|
const isValid = checkSubmit(force)
|
|
@@ -381,11 +381,11 @@ const checkLocalChangeAndChangeStepStatus = (): void => {
|
|
|
381
381
|
|
|
382
382
|
const checkSubmit = (force = false): boolean => {
|
|
383
383
|
if (stepPosition.value === 0 && !force) {
|
|
384
|
-
nameFormSubmit.value
|
|
384
|
+
nameFormSubmit.value++
|
|
385
385
|
return false
|
|
386
386
|
}
|
|
387
387
|
if (stepPosition.value === 1 && !force) {
|
|
388
|
-
storageSubmit.value
|
|
388
|
+
storageSubmit.value++
|
|
389
389
|
return false
|
|
390
390
|
}
|
|
391
391
|
if (stepPosition.value === 2 && !force) {
|
|
@@ -393,22 +393,22 @@ const checkSubmit = (force = false): boolean => {
|
|
|
393
393
|
return false
|
|
394
394
|
}
|
|
395
395
|
if (stepPosition.value === 3 && !force) {
|
|
396
|
-
customizeHardwareSubmit.value
|
|
396
|
+
customizeHardwareSubmit.value++
|
|
397
397
|
return false
|
|
398
398
|
}
|
|
399
399
|
return true
|
|
400
400
|
}
|
|
401
401
|
const checkSubmitByManual = (step: number): boolean => {
|
|
402
402
|
if (stepPosition.value === 0) {
|
|
403
|
-
nameFormSubmit.value
|
|
403
|
+
nameFormSubmit.value++
|
|
404
404
|
return false
|
|
405
405
|
}
|
|
406
406
|
if (stepPosition.value === 1) {
|
|
407
|
-
storageSubmit.value
|
|
407
|
+
storageSubmit.value++
|
|
408
408
|
return false
|
|
409
409
|
}
|
|
410
410
|
if (step === 3) {
|
|
411
|
-
customizeHardwareSubmit.value
|
|
411
|
+
customizeHardwareSubmit.value++
|
|
412
412
|
return false
|
|
413
413
|
}
|
|
414
414
|
return true
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="select-name">
|
|
3
|
+
<atoms-alert
|
|
4
|
+
v-show="errors.length"
|
|
5
|
+
status="alert-danger"
|
|
6
|
+
:items="errors"
|
|
7
|
+
@remove="onRemoveValidationErrors"
|
|
8
|
+
/>
|
|
9
|
+
|
|
10
|
+
<form id="virtual-machine-form" @submit.prevent>
|
|
11
|
+
<label for="virtual-machine-name"
|
|
12
|
+
>{{ localization.virtualMachineName }}:</label
|
|
13
|
+
>
|
|
14
|
+
<input id="virtual-machine-name" v-model.trim="vmName" type="text" />
|
|
15
|
+
<div id="vm-name-help-icon" class="content-signpost relative">
|
|
16
|
+
<atoms-the-icon
|
|
17
|
+
class="icon-show-help"
|
|
18
|
+
fill="#0072a3"
|
|
19
|
+
width="24px"
|
|
20
|
+
height="24px"
|
|
21
|
+
name="info-circle"
|
|
22
|
+
@click="isShowHelp = !isShowHelp"
|
|
23
|
+
/>
|
|
24
|
+
<atoms-tooltip-signpost
|
|
25
|
+
v-if="isShowHelp"
|
|
26
|
+
test-id="help-signpost"
|
|
27
|
+
elem-id="vm-name-help-icon"
|
|
28
|
+
@hide="isShowHelp = false"
|
|
29
|
+
>
|
|
30
|
+
<p>
|
|
31
|
+
{{ localization.vmNameValidationDescription2 }}
|
|
32
|
+
</p>
|
|
33
|
+
</atoms-tooltip-signpost>
|
|
34
|
+
</div>
|
|
35
|
+
</form>
|
|
36
|
+
|
|
37
|
+
<template v-if="props.project === 'sphere'">
|
|
38
|
+
<p class="new-vm-name-and-folder-instructions mt-1">
|
|
39
|
+
{{ localization.selectLocationVirtualMachine }}
|
|
40
|
+
</p>
|
|
41
|
+
<div class="tree-view-wrap">
|
|
42
|
+
<common-vm-actions-add-folder-tree-view
|
|
43
|
+
:data-center="props.dataCenter"
|
|
44
|
+
@select-node="onSelectNode"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script setup lang="ts">
|
|
52
|
+
// import { API_UI_I_Error } from '~/lib/models/store/interfaces'
|
|
53
|
+
import { UI_I_Localization } from '~/lib/models/interfaces'
|
|
54
|
+
import { UI_T_Project } from '~/lib/models/types'
|
|
55
|
+
import { UI_I_TreeNode } from '~/components/common/recursionTree/lib/models/interfaces'
|
|
56
|
+
|
|
57
|
+
const props = defineProps<{
|
|
58
|
+
nameFormSubmit: number
|
|
59
|
+
show: boolean
|
|
60
|
+
project: UI_T_Project
|
|
61
|
+
dataCenter?: UI_I_TreeNode // для сферы
|
|
62
|
+
}>()
|
|
63
|
+
const emits = defineEmits<{
|
|
64
|
+
(event: 'submit', value: [string, UI_I_TreeNode | null]): void
|
|
65
|
+
(event: 'loading', value: boolean): void
|
|
66
|
+
(
|
|
67
|
+
event: 'check-name',
|
|
68
|
+
value: [[string, UI_I_TreeNode | null], (error: any) => void]
|
|
69
|
+
): void
|
|
70
|
+
}>()
|
|
71
|
+
|
|
72
|
+
const localization = computed<UI_I_Localization>(() => useLocal())
|
|
73
|
+
|
|
74
|
+
const vmName = ref<string>('')
|
|
75
|
+
|
|
76
|
+
const selectedNode = ref<UI_I_TreeNode | null>(null)
|
|
77
|
+
const onSelectNode = (node: UI_I_TreeNode): void => {
|
|
78
|
+
selectedNode.value = node
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
watch(
|
|
82
|
+
() => props.nameFormSubmit,
|
|
83
|
+
() => {
|
|
84
|
+
submit()
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const isParentDatacenter = (node: UI_I_TreeNode | null): boolean => {
|
|
89
|
+
if (!node) return false
|
|
90
|
+
|
|
91
|
+
if (node.parent?.type === 'datacenter') return true
|
|
92
|
+
if (node.parent?.type === 'folder') return isParentDatacenter(node.parent)
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
const submit = async (): Promise<void> => {
|
|
96
|
+
const name = vmName.value
|
|
97
|
+
|
|
98
|
+
if (name !== '') {
|
|
99
|
+
const isNameValid = await checkNameIsValid(name)
|
|
100
|
+
if (!isNameValid) return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (props.project === 'sphere') {
|
|
104
|
+
const isDatacenterFolder = isParentDatacenter(selectedNode.value)
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
selectedNode.value?.type !== 'datacenter' &&
|
|
108
|
+
selectedNode.value?.type === 'folder' &&
|
|
109
|
+
!isDatacenterFolder
|
|
110
|
+
) {
|
|
111
|
+
showValidationErrors([
|
|
112
|
+
localization.value.enterValidLocationVirtualMachine,
|
|
113
|
+
])
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
onRemoveValidationErrors()
|
|
119
|
+
emits('submit', [name, selectedNode.value])
|
|
120
|
+
}
|
|
121
|
+
const checkNameIsValid = async (name: string): Promise<boolean> => {
|
|
122
|
+
emits('loading', true)
|
|
123
|
+
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
emits('check-name', [
|
|
126
|
+
[name, selectedNode.value],
|
|
127
|
+
(error) => {
|
|
128
|
+
emits('loading', false)
|
|
129
|
+
|
|
130
|
+
const status = error?.statusCode || 200
|
|
131
|
+
switch (status) {
|
|
132
|
+
case 406: // Invalid name
|
|
133
|
+
showValidationErrors([
|
|
134
|
+
localization.value.vmNameValidationDescription2,
|
|
135
|
+
])
|
|
136
|
+
resolve(false)
|
|
137
|
+
break
|
|
138
|
+
case 409: // Name exist
|
|
139
|
+
showValidationErrors([
|
|
140
|
+
localization.value.vmNameExistInSelectedLocation,
|
|
141
|
+
])
|
|
142
|
+
resolve(false)
|
|
143
|
+
break
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
resolve(true)
|
|
147
|
+
},
|
|
148
|
+
])
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const errors = ref<string[]>([])
|
|
153
|
+
const showValidationErrors = (data: string[]): void => {
|
|
154
|
+
errors.value = data
|
|
155
|
+
}
|
|
156
|
+
const onRemoveValidationErrors = (): void => {
|
|
157
|
+
errors.value = []
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
watch(
|
|
161
|
+
() => props.show,
|
|
162
|
+
(newValue) => {
|
|
163
|
+
if (!newValue) return
|
|
164
|
+
|
|
165
|
+
const input = document.getElementById('virtual-machine-name')
|
|
166
|
+
if (!input) return
|
|
167
|
+
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
input.focus()
|
|
170
|
+
}, 0)
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
const isShowHelp = ref<boolean>(false)
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<style scoped lang="scss">
|
|
178
|
+
.select-name {
|
|
179
|
+
form {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
padding-top: 20px;
|
|
183
|
+
|
|
184
|
+
label {
|
|
185
|
+
width: 216px;
|
|
186
|
+
}
|
|
187
|
+
input {
|
|
188
|
+
width: 345px;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.tree-view-wrap {
|
|
193
|
+
position: relative;
|
|
194
|
+
border: 1px solid #000;
|
|
195
|
+
padding: 5px;
|
|
196
|
+
max-height: 300px;
|
|
197
|
+
min-height: 200px;
|
|
198
|
+
overflow: auto;
|
|
199
|
+
margin-bottom: 10px;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
.content-signpost {
|
|
203
|
+
.icon-show-help {
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.help-title {
|
|
208
|
+
font-size: 22px;
|
|
209
|
+
margin-bottom: 24px;
|
|
210
|
+
}
|
|
211
|
+
.signpost {
|
|
212
|
+
max-width: 360px;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
</style>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="select-storage">
|
|
3
|
+
<atoms-alert
|
|
4
|
+
v-show="errors.length"
|
|
5
|
+
status="alert-danger"
|
|
6
|
+
:items="errors"
|
|
7
|
+
@remove="onRemoveValidationErrors"
|
|
8
|
+
/>
|
|
9
|
+
<div class="table-wrap">
|
|
10
|
+
<atoms-table-data-grid
|
|
11
|
+
v-model:selected-row="selectedRow"
|
|
12
|
+
v-model:column-keys="columnKeys"
|
|
13
|
+
v-model:page-size="pagination.pageSize"
|
|
14
|
+
v-model:page="pagination.page"
|
|
15
|
+
type="radio"
|
|
16
|
+
:head-items="headItems"
|
|
17
|
+
:body-items="bodyItems"
|
|
18
|
+
:total-items="bodyItems.length"
|
|
19
|
+
:total-pages="1"
|
|
20
|
+
server-off
|
|
21
|
+
hide-page-size
|
|
22
|
+
@sorting="sorting"
|
|
23
|
+
@change="changeStorage"
|
|
24
|
+
>
|
|
25
|
+
<template #icon="{ item }">
|
|
26
|
+
<span :class="['datagrid-cell-icon', item.data]" />
|
|
27
|
+
<span class="text-ellipsis">
|
|
28
|
+
{{ item.text }}
|
|
29
|
+
</span>
|
|
30
|
+
</template>
|
|
31
|
+
</atoms-table-data-grid>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div v-if="!hideCompatibility" class="compatibility-wrap">
|
|
35
|
+
<p class="compatibility">
|
|
36
|
+
{{ localization.compatibility }}
|
|
37
|
+
</p>
|
|
38
|
+
<div class="compatibility-message flex-align-center">
|
|
39
|
+
<template v-if="selectedStorage">
|
|
40
|
+
<div class="icon-status-ok" />
|
|
41
|
+
<span>{{ localization.compatibilityChecksSucceeded }}</span>
|
|
42
|
+
</template>
|
|
43
|
+
<div v-else>
|
|
44
|
+
<br />
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script setup lang="ts">
|
|
52
|
+
import { UI_I_Pagination } from '~/lib/models/interfaces'
|
|
53
|
+
import { UI_I_TablePayload } from '~/lib/models/store/interfaces'
|
|
54
|
+
import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
|
|
55
|
+
import { UI_I_Localization } from '~/lib/models/interfaces'
|
|
56
|
+
import {
|
|
57
|
+
UI_I_ColumnKey,
|
|
58
|
+
UI_I_HeadItem,
|
|
59
|
+
UI_I_BodyItem,
|
|
60
|
+
} from '~/components/atoms/table/dataGrid/lib/models/interfaces'
|
|
61
|
+
import * as table from '~/components/common/vm/actions/common/select/storage/lib/config/config'
|
|
62
|
+
|
|
63
|
+
const props = defineProps<{
|
|
64
|
+
storageSubmit: number
|
|
65
|
+
datastore: UI_I_DatastoreTableItem[]
|
|
66
|
+
storage: UI_I_DatastoreTableItem
|
|
67
|
+
hideCompatibility?: boolean
|
|
68
|
+
getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
|
|
69
|
+
}>()
|
|
70
|
+
const emits = defineEmits<{
|
|
71
|
+
(event: 'submit', value: UI_I_DatastoreTableItem): void
|
|
72
|
+
}>()
|
|
73
|
+
|
|
74
|
+
const localization = computed<UI_I_Localization>(() => useLocal())
|
|
75
|
+
|
|
76
|
+
const headItems = ref<UI_I_HeadItem[]>(table.headItems(localization.value))
|
|
77
|
+
const columnKeys = ref<UI_I_ColumnKey[]>(table.columnKeys(localization.value))
|
|
78
|
+
watch(localization, () => {
|
|
79
|
+
columnKeys.value = table.columnKeys(localization.value)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const bodyItems = computed<UI_I_BodyItem[][]>(() => {
|
|
83
|
+
return table.bodyItems(props.datastore || [], localization.value)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const sort = ref<string | null>(null)
|
|
87
|
+
const pagination = ref<UI_I_Pagination>({
|
|
88
|
+
page: 1,
|
|
89
|
+
pageSize: 100,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const sorting = (data: [string, boolean]): void => {
|
|
93
|
+
const [column, status] = data
|
|
94
|
+
const direction = status ? 'asc' : 'desc'
|
|
95
|
+
sort.value = `${column}.${direction}`
|
|
96
|
+
|
|
97
|
+
getStorage()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getStorage = async (): Promise<void> => {
|
|
101
|
+
const payload: UI_I_TablePayload = {
|
|
102
|
+
pagination: pagination.value,
|
|
103
|
+
sortBy: sort.value,
|
|
104
|
+
type: 'datastore',
|
|
105
|
+
schema: 'full',
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
props.getDatastoreTableFunc(payload).then(() => {
|
|
109
|
+
selectedRow.value = null
|
|
110
|
+
setStorageFromProps()
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
getStorage()
|
|
114
|
+
|
|
115
|
+
const selectedStorage = ref<UI_I_DatastoreTableItem | null>(null)
|
|
116
|
+
const selectedRow = ref<number | null>(null)
|
|
117
|
+
const changeStorage = (index: number): void => {
|
|
118
|
+
if (!props.datastore) return
|
|
119
|
+
selectedStorage.value = props.datastore[index]
|
|
120
|
+
selectedRow.value = index
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const setStorageFromProps = (): void => {
|
|
124
|
+
if (!props.storage) return
|
|
125
|
+
props.datastore.forEach((item, index) => {
|
|
126
|
+
if (item.id === props.storage.id) changeStorage(index)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
watch(
|
|
130
|
+
() => props.storage,
|
|
131
|
+
() => {
|
|
132
|
+
setStorageFromProps()
|
|
133
|
+
},
|
|
134
|
+
{ immediate: true }
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
watch(
|
|
138
|
+
() => props.storageSubmit,
|
|
139
|
+
() => {
|
|
140
|
+
submit()
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
const submit = (): void => {
|
|
144
|
+
if (!selectedStorage.value) {
|
|
145
|
+
showValidationErrors([localization.value.selectValidDestinationStorage])
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
emits('submit', selectedStorage.value)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const errors = ref<string[]>([])
|
|
153
|
+
const showValidationErrors = (arr: string[]): void => {
|
|
154
|
+
errors.value = arr
|
|
155
|
+
}
|
|
156
|
+
const onRemoveValidationErrors = (): void => {
|
|
157
|
+
errors.value = []
|
|
158
|
+
}
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<style scoped lang="scss">
|
|
162
|
+
.select-storage {
|
|
163
|
+
flex: 1;
|
|
164
|
+
display: flex;
|
|
165
|
+
flex-direction: column;
|
|
166
|
+
|
|
167
|
+
.table-wrap {
|
|
168
|
+
flex: 1;
|
|
169
|
+
|
|
170
|
+
.datagrid-cell-icon {
|
|
171
|
+
margin-right: 6px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
:deep(.relative) {
|
|
175
|
+
height: 100%;
|
|
176
|
+
|
|
177
|
+
.datagrid-outer-wrapper {
|
|
178
|
+
height: 100%;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.compatibility-wrap {
|
|
184
|
+
padding-top: 10px;
|
|
185
|
+
|
|
186
|
+
.compatibility-message {
|
|
187
|
+
border: 1px solid #000;
|
|
188
|
+
padding-top: 5px;
|
|
189
|
+
padding-left: 5px;
|
|
190
|
+
overflow: auto;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
</style>
|