bfg-common 1.5.676 → 1.5.678

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.
@@ -0,0 +1,428 @@
1
+ <template>
2
+ <ui-data-table
3
+ :data="data"
4
+ :options="options"
5
+ :loading="props.loading"
6
+ test-id="resent-task-table"
7
+ @sort="onSort"
8
+ >
9
+ <template #icon="{ item }">
10
+ <span class="flex-align-center">
11
+ <span :class="['icon', item.data.icon]" />
12
+
13
+ <span
14
+ :data-id="`rtask-${item.data.testId}`"
15
+ :class="item.data.isLink && 'target-link'"
16
+ @click="onSelectNodeOfTree(item.data)"
17
+ >
18
+ {{ item.text }}
19
+ </span>
20
+ </span>
21
+ </template>
22
+
23
+ <template #status="{ item }">
24
+ <ui-chip :test-id="item.data.testId" :color="item.data.chipColor" rounded>
25
+ <ui-icon
26
+ :name="item.data.icon"
27
+ width="14px"
28
+ height="14px"
29
+ class="chip-icon"
30
+ ></ui-icon>
31
+ {{ item.text }}
32
+ </ui-chip>
33
+
34
+ <div v-if="item.data.operations.length" class="ml-2">
35
+ <common-tooltip-help
36
+ :test-id="item.data.index + '-operations'"
37
+ :help-id="item.data.index + '-operations-icon'"
38
+ :title="localization.tasks.vmUpdateSteps"
39
+ :help-text="item.data.error"
40
+ status="info"
41
+ @toggle-show="onToggleOperationsTooltip"
42
+ >
43
+ <template #default>
44
+ <div class="operations-container">
45
+ <div
46
+ v-for="(item2, key2) in item.data.operations"
47
+ :key="key2"
48
+ :data-id="item2.testId"
49
+ class="status-detail-item flex flex-align-center"
50
+ >
51
+ <div class="left-block">
52
+ <ui-icon
53
+ :name="item2.iconClassName"
54
+ class="status-detail-item__icon"
55
+ width="16"
56
+ height="16"
57
+ />
58
+ <span class="text-ellipsis">{{ item2.operation_type }}:</span>
59
+ </div>
60
+ <div class="right-block text-ellipsis">
61
+ <span
62
+ :title="item2.message"
63
+ class="status-detail-item-message text-ellipsis text-3 font-[500]"
64
+ >{{ item2.message || '--' }}</span
65
+ >
66
+ <ui-icon
67
+ name="arrow"
68
+ width="16"
69
+ height="16"
70
+ class="toggle-text-icon pointer"
71
+ :data-id="`toggle-text-icon-${key2}`"
72
+ @click="onToggleLongText(key2)"
73
+ />
74
+ </div>
75
+ </div>
76
+ <div class="bottom-info-block">
77
+ <span class="completed-text"
78
+ >{{
79
+ item.data.operations.length -
80
+ item.data.failedOperationsCount
81
+ }}
82
+ {{ localization.common.of }}
83
+ {{ item.data.operations.length }}
84
+ {{ localization.common.completed }}</span
85
+ >
86
+ <span
87
+ :class="[
88
+ 'failed-text',
89
+ { error: item.data.failedOperationsCount },
90
+ ]"
91
+ >{{ item.data.failedOperationsCount }}
92
+ {{ localization.common.failed }}</span
93
+ >
94
+ </div>
95
+ </div>
96
+ </template>
97
+ </common-tooltip-help>
98
+ </div>
99
+
100
+ <div v-if="item.data.error" class="ml-2">
101
+ <common-tooltip-help
102
+ :test-id="`${item.data.index}`"
103
+ :help-id="item.data.index + 'icon'"
104
+ :title="localization.common.failureReason"
105
+ :help-text="item.data.error"
106
+ dropdown-width="max-content"
107
+ dropdown-max-width="480px"
108
+ dropdown-max-height="200px"
109
+ status="error"
110
+ dropdown-top
111
+ dropdown-left
112
+ />
113
+ </div>
114
+ </template>
115
+
116
+ <template #default-actions="{ item }">
117
+ <div class="actions w-full">
118
+ <ui-button
119
+ :test-id="`resent-task-item-${item.data.id}-action`"
120
+ variant="text"
121
+ is-without-height
122
+ is-without-sizes
123
+ @click="onToggleActions(item.data.id)"
124
+ >
125
+ <span
126
+ :class="['action-icon', { active: actionsIsShow[item.data.id] }]"
127
+ >
128
+ <ui-icon name="vertical-dotes" width="20" height="20" />
129
+ </span>
130
+ </ui-button>
131
+ <ui-dropdown
132
+ :show="actionsIsShow[item.data.id] || false"
133
+ :test-id="`resent-task-item-action-${item.data.id}`"
134
+ :items="actions"
135
+ :elem-id="`resent-task-item-${item.data.id}-action`"
136
+ width="max-content"
137
+ left
138
+ @select="onSelectAction(item.data.target, $event, item.data.id)"
139
+ @hide="onHideActionsDropdown(item.data.id)"
140
+ >
141
+ <template #row="{ item: dropMenu }">
142
+ <ui-icon
143
+ v-if="dropMenu.iconName === 'hide'"
144
+ name="password-hide"
145
+ width="16"
146
+ height="16"
147
+ />
148
+ <span class="action-text">
149
+ {{ dropMenu.text }}
150
+ </span>
151
+ </template>
152
+ </ui-dropdown>
153
+ </div>
154
+ </template>
155
+ </ui-data-table>
156
+ </template>
157
+
158
+ <script setup lang="ts">
159
+ import type { UI_I_Dropdown } from '~/node_modules/bfg-uikit/components/ui/dropdown/models/interfaces'
160
+ import type {
161
+ UI_I_DataTable,
162
+ UI_I_DataTableHeader,
163
+ UI_I_DataTableBody,
164
+ } from '~/node_modules/bfg-uikit/components/ui/dataTable/models/interfaces'
165
+ import type { UI_I_Pagination } from '~/lib/models/table/interfaces'
166
+ import type { UI_I_Localization } from '~/lib/models/interfaces'
167
+ import type { UI_I_RecentTaskItem } from '~/lib/models/store/tasks/interfaces'
168
+ import type { UI_I_DataTargetForTable } from '~/components/common/layout/bottomPanel/recentTasks/lib/models/interfaces'
169
+ import type { UI_T_SelectedStatus } from '~/components/common/layout/bottomPanel/recentTasks/old/lib/models/types'
170
+ import type { UI_T_NodeType } from '~/components/common/pages/home/lib/models/types'
171
+ import {
172
+ options,
173
+ getHeaderDataFunc,
174
+ getBodyDataFunc,
175
+ } from '~/components/common/layout/bottomPanel/recentTasks/new/lib/config/config'
176
+
177
+ const statusFilter = defineModel<UI_T_SelectedStatus>('statusFilter', {
178
+ required: true,
179
+ })
180
+
181
+ const props = defineProps<{
182
+ dataTable: UI_I_RecentTaskItem<UI_T_NodeType>[]
183
+ loading: boolean
184
+ pagination: UI_I_Pagination // Для Warning props
185
+ }>()
186
+ const emits = defineEmits<{
187
+ (event: 'sort', value: string): void
188
+ }>()
189
+
190
+ const localization = computed<UI_I_Localization>(() => useLocal())
191
+ const { $store }: any = useNuxtApp()
192
+
193
+ const actionsIsShow = ref<boolean[]>([])
194
+
195
+ const data = computed<UI_I_DataTable>(() => ({
196
+ id: 'resent-task-table',
197
+ header: resentTaskHeadItems.value,
198
+ body: resentTaskBodyItems.value,
199
+ }))
200
+
201
+ const resentTaskHeadItems = computed<UI_I_DataTableHeader[]>(() =>
202
+ getHeaderDataFunc(localization.value)
203
+ )
204
+
205
+ const resentTaskBodyItems = ref<UI_I_DataTableBody[]>([])
206
+ watch(
207
+ () => [props.dataTable, statusFilter.value],
208
+ (newValue) => {
209
+ if (!newValue[0]?.length) {
210
+ resentTaskBodyItems.value = []
211
+ return
212
+ }
213
+
214
+ if (newValue[1] !== -1) {
215
+ newValue[0] = newValue[0].filter((task: UI_I_RecentTaskItem<UI_T_NodeType>) => task.status === newValue[1])
216
+ }
217
+
218
+ resentTaskBodyItems.value = getBodyDataFunc(newValue[0], localization.value)
219
+ },
220
+ { deep: true, immediate: true }
221
+ )
222
+ const onSort = (value: string): void => {
223
+ emits('sort', value)
224
+ }
225
+
226
+ const actions = computed<UI_I_Dropdown[]>(() => [
227
+ {
228
+ value: 'view',
229
+ text: localization.value.common.viewTarget,
230
+ iconName: 'hide',
231
+ selected: false,
232
+ },
233
+ ])
234
+ const onToggleActions = (id: number): void => {
235
+ actionsIsShow.value[id] = !actionsIsShow.value[id]
236
+ }
237
+ const onSelectAction = (
238
+ data: UI_I_DataTargetForTable,
239
+ action: 'view',
240
+ actionId: number
241
+ ): void => {
242
+ switch (action) {
243
+ case 'view':
244
+ onSelectNodeOfTree(data)
245
+ break
246
+ }
247
+ onHideActionsDropdown(actionId)
248
+ }
249
+ const onHideActionsDropdown = (id: number): void => {
250
+ actionsIsShow.value[id] = false
251
+ }
252
+
253
+ const onSelectNodeOfTree = (data: UI_I_DataTargetForTable): void => {
254
+ if (!data.isLink) return
255
+
256
+ const { type, id, nav } = data
257
+
258
+ const node = {
259
+ id,
260
+ type,
261
+ }
262
+
263
+ const path = `/inventory/type=${type};nav=${nav};id=${id}/summary`
264
+ navigateTo(path)
265
+
266
+ $store.dispatch('inventory/A_SELECT_NODE', { node, type })
267
+ }
268
+
269
+ const onToggleOperationsTooltip = (): void => {
270
+ setTimeout(() => {
271
+ document.querySelectorAll('.status-detail-item').forEach((item) => {
272
+ const messageItem = item.querySelector('.status-detail-item-message')
273
+
274
+ if (!messageItem) return
275
+
276
+ const isEllipsisActive = messageItem.scrollWidth > messageItem.clientWidth
277
+
278
+ isEllipsisActive &&
279
+ item.querySelector('.right-block')?.classList.toggle('active')
280
+ })
281
+ }, 0)
282
+ }
283
+ const onToggleLongText = (index: number): void => {
284
+ document
285
+ .querySelectorAll('.status-detail-item')
286
+ [index].querySelectorAll('.right-block')[0]
287
+ .classList.toggle('show-full')
288
+ }
289
+ </script>
290
+
291
+ <style>
292
+ /*TODO move up*/
293
+ :root {
294
+ --actions-icon-color: #4d5d69;
295
+ --actions-icon-hover-color: #213444;
296
+ --actions-icon-icative-color: #008fd6;
297
+ --status-operations-container-border: #e9ebeda3;
298
+ --status-operations-container-completed-info-text: #4d5d69;
299
+ --status-operations-container-failed-error-text: #ea3223;
300
+ }
301
+ :root.dark-theme {
302
+ --actions-icon-color: #e9eaec;
303
+ --actions-icon-hover-color: #ffffff;
304
+ --actions-icon-icative-color: #2ba2de;
305
+ --status-operations-container-border: #e9ebed1f;
306
+ --status-operations-container-completed-info-text: #e9eaec;
307
+ --status-operations-container-failed-error-text: #ec4235;
308
+ }
309
+ </style>
310
+ <style scoped lang="scss">
311
+ .resent-task-table {
312
+ height: inherit;
313
+
314
+ .target-link {
315
+ font-family: 'Inter', sans-serif;
316
+ font-size: 13px;
317
+ color: var(--bottom-pannel-rtask-link-text);
318
+ font-weight: 400;
319
+ line-height: 15.73px;
320
+ cursor: pointer;
321
+ &:hover {
322
+ color: var(--bottom-pannel-rtask-link-hover-text);
323
+ }
324
+ }
325
+ .chip-icon {
326
+ min-width: 14px;
327
+ //margin-right: 6px;
328
+ }
329
+ .icon {
330
+ margin-right: 4px;
331
+ }
332
+
333
+ .actions {
334
+ .action-icon {
335
+ width: 16px;
336
+ height: 16px;
337
+ color: var(--actions-icon-color);
338
+
339
+ &:hover {
340
+ color: var(--actions-icon-hover-color);
341
+ }
342
+ &.active {
343
+ color: var(--actions-icon-icative-color);
344
+ }
345
+ }
346
+
347
+ .action-text {
348
+ margin-left: 8px;
349
+ }
350
+ }
351
+
352
+ .operations-container {
353
+ max-width: 448px;
354
+ min-width: 288px;
355
+ padding-top: 16px;
356
+ border-top: 1px solid var(--status-operations-container-border);
357
+ display: grid;
358
+ grid-row-gap: 20px;
359
+
360
+ .status-detail-item {
361
+ display: grid;
362
+ grid-template-columns: 50% 50%;
363
+
364
+ .left-block {
365
+ display: flex;
366
+ align-items: center;
367
+ grid-gap: 8px;
368
+ padding-right: 24px;
369
+ }
370
+ .right-block {
371
+ display: grid;
372
+ align-items: flex-start;
373
+
374
+ &.active {
375
+ grid-template-columns: 1fr max-content;
376
+ grid-column-gap: 8px;
377
+
378
+ .toggle-text-icon {
379
+ display: unset;
380
+ }
381
+ }
382
+ &.show-full {
383
+ .status-detail-item-message {
384
+ white-space: break-spaces;
385
+ }
386
+ .toggle-text-icon {
387
+ transform: rotate(0deg);
388
+ }
389
+ }
390
+
391
+ .status-detail-item-message {
392
+ color: #9da6ad;
393
+ }
394
+ .toggle-text-icon {
395
+ transform: rotate(180deg);
396
+ display: none;
397
+ }
398
+ }
399
+
400
+ &__icon {
401
+ min-width: 16px;
402
+ color: #ea3223;
403
+ :deep(path) {
404
+ fill-opacity: 1;
405
+ }
406
+ }
407
+ }
408
+ .bottom-info-block {
409
+ display: flex;
410
+ justify-content: space-between;
411
+ padding-top: 16px;
412
+ border-top: 1px solid var(--status-operations-container-border);
413
+
414
+ .completed-text {
415
+ font-weight: 500;
416
+ color: var(--status-operations-container-completed-info-text);
417
+ }
418
+ .failed-text {
419
+ color: #9da6ad;
420
+
421
+ &.error {
422
+ color: var(--status-operations-container-failed-error-text);
423
+ }
424
+ }
425
+ }
426
+ }
427
+ }
428
+ </style>
@@ -0,0 +1,259 @@
1
+ import type {
2
+ UI_I_DataTableBody,
3
+ UI_I_DataTableOptions,
4
+ UI_I_DataTableHeader,
5
+ } from '~/node_modules/bfg-uikit/components/ui/dataTable/models/interfaces'
6
+ import type { UI_I_Localization } from '~/lib/models/interfaces'
7
+ import type { UI_I_RecentTaskItem } from '~/lib/models/store/tasks/interfaces'
8
+ import type { UI_T_NodeType } from '~/components/common/pages/home/lib/models/types'
9
+ import { UI_E_RecentTaskStatus } from '~/lib/models/store/tasks/enums'
10
+ import { UI_E_NodeIconsByState } from '~/lib/models/enums'
11
+ import { UI_E_TabsByTypeEnum } from '~/components/common/pages/home/widgets/warnings/table/lib/models/enums'
12
+ import {
13
+ UI_E_RTaskChipColor,
14
+ UI_E_RTaskStatusIcon,
15
+ } from '~/components/common/pages/tasks/table/lib/models/enums'
16
+
17
+ export const getHeaderDataFunc = (
18
+ localization: UI_I_Localization
19
+ ): UI_I_DataTableHeader[] => [
20
+ {
21
+ col: 0,
22
+ colName: 'taskName',
23
+ text: localization.tasks.taskName,
24
+ isSortable: true,
25
+ sort: 'asc',
26
+ sortColumn: true,
27
+ width: '180px',
28
+ show: true,
29
+ filter: true,
30
+ },
31
+ {
32
+ col: 1,
33
+ colName: 'target',
34
+ text: localization.common.target,
35
+ isSortable: true,
36
+ sort: 'asc',
37
+ width: '180px',
38
+ show: true,
39
+ filter: true,
40
+ },
41
+ {
42
+ col: 2,
43
+ colName: 'status',
44
+ text: localization.common.status,
45
+ isSortable: true,
46
+ sort: 'asc',
47
+ width: '180px',
48
+ show: true,
49
+ filter: true,
50
+ },
51
+ {
52
+ col: 3,
53
+ colName: 'details',
54
+ text: localization.common.details,
55
+ isSortable: true,
56
+ sort: 'asc',
57
+ width: '180px',
58
+ show: true,
59
+ filter: true,
60
+ },
61
+ {
62
+ col: 4,
63
+ colName: 'initiator',
64
+ text: localization.common.initiator,
65
+ isSortable: true,
66
+ sort: 'asc',
67
+ width: '180px',
68
+ show: true,
69
+ filter: true,
70
+ },
71
+ {
72
+ col: 5,
73
+ colName: 'queuedFor',
74
+ text: localization.common.queuedFor,
75
+ isSortable: true,
76
+ sort: 'asc',
77
+ width: '180px',
78
+ show: true,
79
+ filter: true,
80
+ },
81
+ {
82
+ col: 6,
83
+ colName: 'startTime',
84
+ text: localization.common.startTime,
85
+ isSortable: true,
86
+ sort: 'asc',
87
+ width: '180px',
88
+ show: true,
89
+ filter: true,
90
+ },
91
+ {
92
+ col: 7,
93
+ colName: 'completionTime',
94
+ text: localization.common.completionTime,
95
+ isSortable: true,
96
+ sort: 'asc',
97
+ width: '180px',
98
+ show: true,
99
+ filter: true,
100
+ },
101
+ {
102
+ col: 8,
103
+ colName: 'executionTime',
104
+ text: localization.common.executionTime,
105
+ isSortable: true,
106
+ sort: 'asc',
107
+ width: '180px',
108
+ show: true,
109
+ filter: true,
110
+ },
111
+ {
112
+ col: 9,
113
+ colName: 'server',
114
+ text: localization.common.server,
115
+ isSortable: true,
116
+ sort: 'asc',
117
+ width: '180px',
118
+ show: true,
119
+ filter: true,
120
+ },
121
+ {
122
+ col: 10,
123
+ colName: 'zone',
124
+ text: localization.common.zone,
125
+ isSortable: true,
126
+ sort: 'asc',
127
+ width: '180px',
128
+ show: true,
129
+ filter: true,
130
+ },
131
+ {
132
+ col: 11,
133
+ colName: 'default-actions',
134
+ text: '',
135
+ show: true,
136
+ },
137
+ ]
138
+
139
+ export const options: UI_I_DataTableOptions = {
140
+ perPageOptions: [
141
+ { text: '10', value: 10 },
142
+ { text: '25', value: 25 },
143
+ { text: '50', value: 50 },
144
+ { text: '100', value: 100 },
145
+ ],
146
+ isSelectable: false,
147
+ isFocusable: false,
148
+ showPagination: true,
149
+ showPageInfo: true,
150
+ isSortable: true,
151
+ server: false,
152
+ isResizable: true,
153
+ showSearch: false,
154
+ showColumnManager: true,
155
+ withActions: true,
156
+ inBlock: true,
157
+ inBlockOnlyLightMode: true,
158
+ }
159
+
160
+ export const getBodyDataFunc = (
161
+ bodyData: UI_I_RecentTaskItem<UI_T_NodeType>[],
162
+ localization: UI_I_Localization
163
+ ): UI_I_DataTableBody[] => {
164
+ const { $store, $text, $formattedDatetime }: any = useNuxtApp()
165
+
166
+ return bodyData.map((task, index: number) => {
167
+ const iconClassName = UI_E_NodeIconsByState[`${task.targetType}_Normal`]
168
+ const id = task.extra?.created_id || task.target
169
+ const targetData = {
170
+ id,
171
+ icon: iconClassName,
172
+ nav: UI_E_TabsByTypeEnum[task.targetType],
173
+ type: task.targetType,
174
+ isLink: true,
175
+ testId: `${task.targetType}-item`,
176
+ }
177
+
178
+ const lang = $store.getters['main/getInterfaceLang']
179
+ const currentOperations = task.statusDetails.map((item) => {
180
+ return {
181
+ ...item,
182
+ operation_type:
183
+ lang === 'en_US'
184
+ ? $text.toCapitalizeEveryWord(item.operation_type)
185
+ : item.operation_type,
186
+ }
187
+ })
188
+ const statusData = {
189
+ icon: UI_E_RTaskStatusIcon[task.status],
190
+ chipColor: UI_E_RTaskChipColor[task.status],
191
+ error: task.error,
192
+ operations: currentOperations,
193
+ failedOperationsCount: task.statusDetails?.filter(
194
+ (item) => item.status !== 4
195
+ ).length,
196
+ testId: `${task.taskName}-progress`,
197
+ index,
198
+ }
199
+
200
+ const formattedStartTime =
201
+ task.startTime === '--'
202
+ ? '--'
203
+ : $formattedDatetime(+task.startTime * 1000, {
204
+ hasSeconds: true,
205
+ })
206
+
207
+ const formattedCompletionTime =
208
+ task.completion === '--'
209
+ ? '--'
210
+ : $formattedDatetime(+task.completion * 1000, {
211
+ hasSeconds: true,
212
+ })
213
+
214
+ return {
215
+ row: index,
216
+ collapse: false,
217
+ isHiddenCollapse: false,
218
+ collapseToggle: false,
219
+ data: [
220
+ { col: 0, text: task.taskName },
221
+ {
222
+ key: 'icon',
223
+ col: 1,
224
+ text: task.target,
225
+ data: targetData,
226
+ },
227
+ {
228
+ key: 'status',
229
+ col: 2,
230
+ text: localization.common[UI_E_RecentTaskStatus[task.status]],
231
+ data: statusData,
232
+ },
233
+ { col: 3, text: task.details || '--' },
234
+ { col: 4, text: task.initiator },
235
+ { col: 5, text: `${task.queuedFor} ms` },
236
+ { col: 6, text: formattedStartTime },
237
+ { col: 7, text: formattedCompletionTime },
238
+ { col: 8, text: `${task.execution} ms` },
239
+ { col: 9, text: task.server },
240
+ {
241
+ key: 'icon',
242
+ col: 10,
243
+ text: task.zone,
244
+ data: {
245
+ icon: 'vsphere-icon-vcenter',
246
+ isLink: false,
247
+ testId: 'zone-item',
248
+ },
249
+ },
250
+ {
251
+ key: 'default-actions',
252
+ col: 'default-actions',
253
+ text: '',
254
+ data: { id: index, target: targetData },
255
+ },
256
+ ],
257
+ }
258
+ })
259
+ }