@xen-orchestra/web-core 0.0.3 → 0.0.5
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.
- package/env.d.ts +0 -1
- package/lib/assets/css/_typography.pcss +1 -0
- package/lib/assets/css/typography/_utils.pcss +6 -0
- package/lib/components/LegendTitle.vue +9 -7
- package/lib/components/UiTag.vue +3 -7
- package/lib/components/backdrop/Backdrop.vue +11 -0
- package/lib/components/cell-object/CellObject.vue +54 -0
- package/lib/components/cell-text/CellText.vue +40 -0
- package/lib/components/chip/UiChip.vue +3 -4
- package/lib/components/console/RemoteConsole.vue +1 -1
- package/lib/components/divider/Divider.vue +25 -0
- package/lib/components/{DonutChart.vue → donut-chart/DonutChart.vue} +28 -24
- package/lib/components/donut-chart-with-legend/DonutChartWithLegend.vue +27 -0
- package/lib/components/dropdown/DropdownItem.vue +1 -4
- package/lib/components/head-bar/HeadBar.vue +78 -0
- package/lib/components/input/UiInput.vue +133 -0
- package/lib/components/legend/LegendGroup.vue +44 -0
- package/lib/components/{UiLegend.vue → legend/LegendItem.vue} +35 -17
- package/lib/components/legend/LegendList.vue +19 -0
- package/lib/components/object-link/ObjectLink.vue +87 -0
- package/lib/components/search-bar/SearchBar.vue +60 -0
- package/lib/components/stacked-bar/StackedBarSegment.vue +2 -8
- package/lib/components/state-hero/ComingSoonHero.vue +6 -2
- package/lib/components/state-hero/LoadingHero.vue +8 -2
- package/lib/components/state-hero/ObjectNotFoundHero.vue +1 -1
- package/lib/components/state-hero/StateHero.vue +27 -9
- package/lib/components/tab/TabList.vue +1 -0
- package/lib/components/table/UiTable.vue +6 -0
- package/lib/components/task/QuickTaskButton.vue +62 -0
- package/lib/components/task/QuickTaskItem.vue +91 -0
- package/lib/components/task/QuickTaskList.vue +48 -0
- package/lib/components/task/QuickTaskPanel.vue +65 -0
- package/lib/components/task/QuickTaskTabBar.vue +46 -0
- package/lib/components/tree/TreeItem.vue +8 -8
- package/lib/components/tree/TreeItemLabel.vue +28 -16
- package/lib/composables/item-counter.composable.md +25 -0
- package/lib/composables/item-counter.composable.ts +32 -0
- package/lib/composables/tab-list.composable.ts +33 -0
- package/lib/composables/tree/branch.ts +5 -5
- package/lib/i18n.ts +53 -0
- package/lib/layouts/CoreLayout.vue +2 -11
- package/lib/locales/de.json +7 -1
- package/lib/locales/en.json +23 -1
- package/lib/locales/fa.json +46 -0
- package/lib/locales/fr.json +23 -1
- package/lib/types/novnc.d.ts +342 -0
- package/lib/types/tab.type.ts +17 -0
- package/lib/types/task.type.ts +13 -0
- package/lib/types/utility.type.ts +1 -1
- package/lib/utils/if-else.utils.ts +3 -3
- package/lib/utils/open-url.utils.ts +3 -0
- package/package.json +3 -3
- package/lib/components/UiSeparator.vue +0 -11
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="{ mobile: isMobile }" class="vts-quick-tasks">
|
|
3
|
+
<QuickTaskTabBar
|
|
4
|
+
v-model="currentTab"
|
|
5
|
+
:failure-count="failureTasks.length"
|
|
6
|
+
:loading
|
|
7
|
+
:pending-count="pendingTasks.length"
|
|
8
|
+
:success-count="successTasks.length"
|
|
9
|
+
/>
|
|
10
|
+
<QuickTaskList :loading :tasks="currentTasks" />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script lang="ts" setup>
|
|
15
|
+
import QuickTaskList from '@core/components/task/QuickTaskList.vue'
|
|
16
|
+
import QuickTaskTabBar from '@core/components/task/QuickTaskTabBar.vue'
|
|
17
|
+
import { useUiStore } from '@core/stores/ui.store'
|
|
18
|
+
import type { Task, TaskTab } from '@core/types/task.type'
|
|
19
|
+
import { computed, ref } from 'vue'
|
|
20
|
+
|
|
21
|
+
const props = defineProps<{
|
|
22
|
+
tasks: Task[]
|
|
23
|
+
loading?: boolean
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
const { isMobile } = useUiStore()
|
|
27
|
+
|
|
28
|
+
const currentTab = ref<TaskTab>('pending')
|
|
29
|
+
|
|
30
|
+
const pendingTasks = computed(() => props.tasks.filter(task => task.status === 'pending'))
|
|
31
|
+
const successTasks = computed(() => props.tasks.filter(task => task.status === 'success'))
|
|
32
|
+
const failureTasks = computed(() => props.tasks.filter(task => task.status === 'failure'))
|
|
33
|
+
|
|
34
|
+
const currentTasks = computed(() => {
|
|
35
|
+
switch (currentTab.value) {
|
|
36
|
+
case 'pending':
|
|
37
|
+
return pendingTasks.value
|
|
38
|
+
case 'success':
|
|
39
|
+
return successTasks.value
|
|
40
|
+
case 'failure':
|
|
41
|
+
return failureTasks.value
|
|
42
|
+
case 'all':
|
|
43
|
+
return props.tasks
|
|
44
|
+
default:
|
|
45
|
+
return []
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<style lang="postcss" scoped>
|
|
51
|
+
.vts-quick-tasks {
|
|
52
|
+
width: fit-content;
|
|
53
|
+
min-width: 65rem;
|
|
54
|
+
border-radius: 0.8rem;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
border: 0.1rem solid var(--color-grey-500);
|
|
57
|
+
z-index: 1001;
|
|
58
|
+
|
|
59
|
+
&.mobile {
|
|
60
|
+
width: 100%;
|
|
61
|
+
min-width: 0;
|
|
62
|
+
border-radius: 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TabList :disabled="loading">
|
|
3
|
+
<TabItem v-bind="tabs.pending.bindings">
|
|
4
|
+
{{ $t('tasks.quick-view.in-progress') }}
|
|
5
|
+
<UiCounter v-if="pendingCount !== undefined" :value="pendingCount" color="info" />
|
|
6
|
+
</TabItem>
|
|
7
|
+
<TabItem v-bind="tabs.success.bindings">
|
|
8
|
+
{{ $t('tasks.quick-view.done') }}
|
|
9
|
+
<UiCounter v-if="successCount !== undefined" :value="successCount" color="success" />
|
|
10
|
+
</TabItem>
|
|
11
|
+
<TabItem v-bind="tabs.failure.bindings">
|
|
12
|
+
{{ $t('tasks.quick-view.failed') }}
|
|
13
|
+
<UiCounter v-if="failureCount !== undefined" :value="failureCount" color="danger" />
|
|
14
|
+
</TabItem>
|
|
15
|
+
<Divider type="tab" />
|
|
16
|
+
<TabItem v-bind="tabs.all.bindings">
|
|
17
|
+
{{ $t('tasks.quick-view.all') }}
|
|
18
|
+
</TabItem>
|
|
19
|
+
<!--
|
|
20
|
+
TODO
|
|
21
|
+
<UiButton :right-icon="faAngleRight" class="see-all" level="tertiary" size="extra-small">
|
|
22
|
+
{{ $t('see-all') }}
|
|
23
|
+
</UiButton>
|
|
24
|
+
-->
|
|
25
|
+
</TabList>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script lang="ts" setup>
|
|
29
|
+
import Divider from '@core/components/divider/Divider.vue'
|
|
30
|
+
import TabItem from '@core/components/tab/TabItem.vue'
|
|
31
|
+
import TabList from '@core/components/tab/TabList.vue'
|
|
32
|
+
import UiCounter from '@core/components/UiCounter.vue'
|
|
33
|
+
import { useTabList } from '@core/composables/tab-list.composable'
|
|
34
|
+
import type { TaskTab } from '@core/types/task.type'
|
|
35
|
+
|
|
36
|
+
defineProps<{
|
|
37
|
+
loading?: boolean
|
|
38
|
+
pendingCount?: number
|
|
39
|
+
successCount?: number
|
|
40
|
+
failureCount?: number
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const currentTab = defineModel<TaskTab>({ required: true })
|
|
44
|
+
|
|
45
|
+
const { tabs } = useTabList<TaskTab>(['pending', 'success', 'failure', 'all'], currentTab)
|
|
46
|
+
</script>
|
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
<template>
|
|
3
3
|
<li class="tree-item">
|
|
4
4
|
<slot />
|
|
5
|
-
<slot v-if="
|
|
5
|
+
<slot v-if="expanded" name="sublist" />
|
|
6
6
|
</li>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script lang="ts" setup>
|
|
10
|
-
import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN
|
|
11
|
-
import {
|
|
12
|
-
|
|
10
|
+
import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN } from '@core/utils/injection-keys.util'
|
|
11
|
+
import { onBeforeMount, onBeforeUpdate, provide, ref, toRef, useSlots } from 'vue'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
expanded?: boolean
|
|
15
|
+
}>()
|
|
13
16
|
|
|
14
17
|
defineSlots<{
|
|
15
18
|
default: () => void
|
|
16
19
|
sublist: () => void
|
|
17
20
|
}>()
|
|
18
21
|
|
|
19
|
-
const [isExpanded, toggle] = useToggle(true)
|
|
20
|
-
|
|
21
22
|
const hasChildren = ref(false)
|
|
22
23
|
|
|
23
24
|
const updateHasChildren = () => {
|
|
@@ -29,6 +30,5 @@ onBeforeMount(() => updateHasChildren())
|
|
|
29
30
|
onBeforeUpdate(() => updateHasChildren())
|
|
30
31
|
|
|
31
32
|
provide(IK_TREE_ITEM_HAS_CHILDREN, hasChildren)
|
|
32
|
-
provide(
|
|
33
|
-
provide(IK_TREE_ITEM_EXPANDED, isExpanded)
|
|
33
|
+
provide(IK_TREE_ITEM_EXPANDED, toRef(props, 'expanded'))
|
|
34
34
|
</script>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<!-- v1.1 -->
|
|
2
2
|
<template>
|
|
3
|
-
<RouterLink v-slot="{ isExactActive, href, navigate }" :to="route" custom>
|
|
3
|
+
<RouterLink v-slot="{ isExactActive, isActive, href, navigate }" :to="route" custom>
|
|
4
4
|
<div
|
|
5
|
-
:class="isExactActive ? 'exact-active' : active ? 'active' : undefined"
|
|
5
|
+
:class="isExactActive ? 'exact-active' : active || isActive ? 'active' : undefined"
|
|
6
6
|
class="tree-item-label"
|
|
7
7
|
v-bind="$attrs"
|
|
8
8
|
>
|
|
@@ -17,16 +17,17 @@
|
|
|
17
17
|
<ButtonIcon
|
|
18
18
|
v-if="hasToggle"
|
|
19
19
|
v-tooltip="isExpanded ? $t('core.close') : $t('core.open')"
|
|
20
|
+
class="toggle"
|
|
20
21
|
:icon="isExpanded ? faAngleDown : faAngleRight"
|
|
21
22
|
size="small"
|
|
22
|
-
@click="toggle
|
|
23
|
+
@click="emit('toggle')"
|
|
23
24
|
/>
|
|
24
|
-
<
|
|
25
|
+
<div v-else class="h-line" />
|
|
25
26
|
<a v-tooltip="{ selector: '.text' }" :href class="link typo p2-medium" @click="navigate">
|
|
26
27
|
<slot name="icon">
|
|
27
28
|
<UiIcon :icon class="icon" />
|
|
28
29
|
</slot>
|
|
29
|
-
<div class="text">
|
|
30
|
+
<div class="text text-ellipsis">
|
|
30
31
|
<slot />
|
|
31
32
|
</div>
|
|
32
33
|
</a>
|
|
@@ -40,12 +41,7 @@ import ButtonIcon from '@core/components/button/ButtonIcon.vue'
|
|
|
40
41
|
import UiIcon from '@core/components/icon/UiIcon.vue'
|
|
41
42
|
import TreeLine from '@core/components/tree/TreeLine.vue'
|
|
42
43
|
import { vTooltip } from '@core/directives/tooltip.directive'
|
|
43
|
-
import {
|
|
44
|
-
IK_TREE_ITEM_EXPANDED,
|
|
45
|
-
IK_TREE_ITEM_HAS_CHILDREN,
|
|
46
|
-
IK_TREE_ITEM_TOGGLE,
|
|
47
|
-
IK_TREE_LIST_DEPTH,
|
|
48
|
-
} from '@core/utils/injection-keys.util'
|
|
44
|
+
import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN, IK_TREE_LIST_DEPTH } from '@core/utils/injection-keys.util'
|
|
49
45
|
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
|
|
50
46
|
import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
|
51
47
|
import { inject, ref } from 'vue'
|
|
@@ -59,12 +55,14 @@ defineProps<{
|
|
|
59
55
|
icon?: IconDefinition
|
|
60
56
|
route: RouteLocationRaw
|
|
61
57
|
active?: boolean
|
|
62
|
-
|
|
58
|
+
}>()
|
|
59
|
+
|
|
60
|
+
const emit = defineEmits<{
|
|
61
|
+
toggle: []
|
|
63
62
|
}>()
|
|
64
63
|
|
|
65
64
|
const hasToggle = inject(IK_TREE_ITEM_HAS_CHILDREN, ref(false))
|
|
66
65
|
|
|
67
|
-
const toggle = inject(IK_TREE_ITEM_TOGGLE, () => undefined)
|
|
68
66
|
const isExpanded = inject(IK_TREE_ITEM_EXPANDED, ref(true))
|
|
69
67
|
|
|
70
68
|
const depth = inject(IK_TREE_LIST_DEPTH, 0)
|
|
@@ -120,13 +118,27 @@ const depth = inject(IK_TREE_LIST_DEPTH, 0)
|
|
|
120
118
|
}
|
|
121
119
|
|
|
122
120
|
.text {
|
|
123
|
-
overflow: hidden;
|
|
124
|
-
white-space: nowrap;
|
|
125
|
-
text-overflow: ellipsis;
|
|
126
121
|
padding-inline-end: 0.4rem;
|
|
127
122
|
}
|
|
128
123
|
|
|
129
124
|
.icon {
|
|
130
125
|
font-size: 1.6rem;
|
|
131
126
|
}
|
|
127
|
+
|
|
128
|
+
.h-line {
|
|
129
|
+
width: 2rem;
|
|
130
|
+
border-bottom: 0.1rem solid var(--color-purple-base);
|
|
131
|
+
margin-left: -0.4rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Increase the size of the clickable area,
|
|
136
|
+
* without changing the padding of the ButtonIcon component
|
|
137
|
+
*/
|
|
138
|
+
.toggle::after {
|
|
139
|
+
content: '';
|
|
140
|
+
position: absolute;
|
|
141
|
+
inset: 0;
|
|
142
|
+
transform: scale(1.5, 2);
|
|
143
|
+
}
|
|
132
144
|
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# `useItemCounter`
|
|
2
|
+
|
|
3
|
+
This composable is meant to count items based on given filters then output the categorized count.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
const collection = [
|
|
9
|
+
{ id: 'a', size: 'S' },
|
|
10
|
+
{ id: 'b', size: 'S' },
|
|
11
|
+
{ id: 'c', size: 'XL' },
|
|
12
|
+
{ id: 'd', size: 'M' },
|
|
13
|
+
{ id: 'e', size: 'L' },
|
|
14
|
+
{ id: 'f', size: 'S' },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const count = useItemCounter(collection, {
|
|
18
|
+
small: item => item.size === 'S',
|
|
19
|
+
medium: item => item.size === 'M',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
console.log(count.small) // 3
|
|
23
|
+
console.log(count.medium) // 1
|
|
24
|
+
console.log(count.$other) // 2
|
|
25
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { computed, type MaybeRefOrGetter, toValue } from 'vue'
|
|
2
|
+
|
|
3
|
+
type Counters<TConfig> = { [K in keyof TConfig | '$other']: number }
|
|
4
|
+
|
|
5
|
+
export function useItemCounter<TItem, TConfig extends Record<string, (item: TItem) => boolean>>(
|
|
6
|
+
items: MaybeRefOrGetter<TItem[]>,
|
|
7
|
+
config: TConfig
|
|
8
|
+
) {
|
|
9
|
+
const keys = Object.keys(config) as (keyof TConfig)[]
|
|
10
|
+
|
|
11
|
+
return computed(() => {
|
|
12
|
+
const counters = Object.fromEntries(keys.concat('$other').map(key => [key, 0])) as Counters<TConfig>
|
|
13
|
+
|
|
14
|
+
for (const item of toValue(items)) {
|
|
15
|
+
let matched = false
|
|
16
|
+
|
|
17
|
+
for (const key of keys) {
|
|
18
|
+
if (config[key](item)) {
|
|
19
|
+
matched = true
|
|
20
|
+
counters[key] += 1
|
|
21
|
+
break
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!matched) {
|
|
26
|
+
counters.$other += 1
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return counters
|
|
31
|
+
})
|
|
32
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Tab, TabList } from '@core/types/tab.type'
|
|
2
|
+
import { computed, type MaybeRefOrGetter, type Ref, toRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function useTabList<TName extends string>(names: TName[], initialTab?: MaybeRefOrGetter<TName>) {
|
|
5
|
+
const currentTab = toRef(initialTab) as Ref<TName | undefined>
|
|
6
|
+
|
|
7
|
+
const activate = (name: TName | undefined) => {
|
|
8
|
+
currentTab.value = name
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const isActive = (name: TName) => currentTab.value === name
|
|
12
|
+
|
|
13
|
+
const tabs = computed(() => {
|
|
14
|
+
return Object.fromEntries(
|
|
15
|
+
names.map(name => [
|
|
16
|
+
name,
|
|
17
|
+
{
|
|
18
|
+
isActive: isActive(name),
|
|
19
|
+
activate: () => activate(name),
|
|
20
|
+
bindings: {
|
|
21
|
+
onClick: (event: MouseEvent) => {
|
|
22
|
+
event.preventDefault()
|
|
23
|
+
activate(name)
|
|
24
|
+
},
|
|
25
|
+
active: isActive(name),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
])
|
|
29
|
+
) as Record<TName, Tab>
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return { currentTab, activate, isActive, tabs } as TabList<TName>
|
|
33
|
+
}
|
|
@@ -40,7 +40,11 @@ export class Branch<
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
get failsFilterDownwards(): boolean {
|
|
43
|
-
|
|
43
|
+
if (this.passesFilter !== undefined) {
|
|
44
|
+
return !this.passesFilter
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.rawChildren.length > 0 && this.rawChildren.every(child => child.failsFilterDownwards)
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
get isExcluded() {
|
|
@@ -48,10 +52,6 @@ export class Branch<
|
|
|
48
52
|
return true
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
if (!this.hasChildren) {
|
|
52
|
-
return true
|
|
53
|
-
}
|
|
54
|
-
|
|
55
55
|
if (this.passesFilterUpwards || this.passesFilterDownwards) {
|
|
56
56
|
return false
|
|
57
57
|
}
|
package/lib/i18n.ts
CHANGED
|
@@ -21,6 +21,10 @@ export const locales: Locales = {
|
|
|
21
21
|
code: 'de',
|
|
22
22
|
name: 'Deutsch',
|
|
23
23
|
},
|
|
24
|
+
fa: {
|
|
25
|
+
code: 'fa',
|
|
26
|
+
name: 'Persian',
|
|
27
|
+
},
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export default createI18n({
|
|
@@ -163,6 +167,48 @@ export default createI18n({
|
|
|
163
167
|
minute: '2-digit',
|
|
164
168
|
},
|
|
165
169
|
},
|
|
170
|
+
fa: {
|
|
171
|
+
date_short: {
|
|
172
|
+
year: 'numeric',
|
|
173
|
+
month: 'numeric',
|
|
174
|
+
day: 'numeric',
|
|
175
|
+
},
|
|
176
|
+
date_medium: {
|
|
177
|
+
year: 'numeric',
|
|
178
|
+
month: 'short',
|
|
179
|
+
day: 'numeric',
|
|
180
|
+
},
|
|
181
|
+
date_long: {
|
|
182
|
+
year: 'numeric',
|
|
183
|
+
month: 'long',
|
|
184
|
+
day: 'numeric',
|
|
185
|
+
},
|
|
186
|
+
datetime_short: {
|
|
187
|
+
year: 'numeric',
|
|
188
|
+
month: 'numeric',
|
|
189
|
+
day: 'numeric',
|
|
190
|
+
hour: '2-digit',
|
|
191
|
+
minute: '2-digit',
|
|
192
|
+
},
|
|
193
|
+
datetime_medium: {
|
|
194
|
+
year: 'numeric',
|
|
195
|
+
month: 'short',
|
|
196
|
+
day: 'numeric',
|
|
197
|
+
hour: '2-digit',
|
|
198
|
+
minute: '2-digit',
|
|
199
|
+
},
|
|
200
|
+
datetime_long: {
|
|
201
|
+
year: 'numeric',
|
|
202
|
+
month: 'long',
|
|
203
|
+
day: 'numeric',
|
|
204
|
+
hour: '2-digit',
|
|
205
|
+
minute: '2-digit',
|
|
206
|
+
},
|
|
207
|
+
time: {
|
|
208
|
+
hour: '2-digit',
|
|
209
|
+
minute: '2-digit',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
166
212
|
},
|
|
167
213
|
numberFormats: {
|
|
168
214
|
en: {
|
|
@@ -186,5 +232,12 @@ export default createI18n({
|
|
|
186
232
|
maximumFractionDigits: 2,
|
|
187
233
|
},
|
|
188
234
|
},
|
|
235
|
+
fa: {
|
|
236
|
+
percent: {
|
|
237
|
+
style: 'percent',
|
|
238
|
+
minimumFractionDigits: 0,
|
|
239
|
+
maximumFractionDigits: 2,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
189
242
|
},
|
|
190
243
|
})
|
|
@@ -14,11 +14,7 @@
|
|
|
14
14
|
<slot name="app-header" />
|
|
15
15
|
</header>
|
|
16
16
|
<div class="container">
|
|
17
|
-
<
|
|
18
|
-
v-if="sidebarStore.isExpanded && !sidebarStore.isLocked"
|
|
19
|
-
class="sidebar-close-backdrop"
|
|
20
|
-
@click="sidebarStore.toggleExpand(false)"
|
|
21
|
-
/>
|
|
17
|
+
<Backdrop v-if="sidebarStore.isExpanded && !sidebarStore.isLocked" @click="sidebarStore.toggleExpand(false)" />
|
|
22
18
|
<LayoutSidebar class="sidebar">
|
|
23
19
|
<template #header>
|
|
24
20
|
<slot name="sidebar-header" />
|
|
@@ -38,6 +34,7 @@
|
|
|
38
34
|
</template>
|
|
39
35
|
|
|
40
36
|
<script lang="ts" setup>
|
|
37
|
+
import Backdrop from '@core/components/backdrop/Backdrop.vue'
|
|
41
38
|
import UiButtonIcon from '@core/components/button/ButtonIcon.vue'
|
|
42
39
|
import LayoutSidebar from '@core/components/layout/LayoutSidebar.vue'
|
|
43
40
|
import { vTooltip } from '@core/directives/tooltip.directive'
|
|
@@ -48,12 +45,6 @@ const sidebarStore = useSidebarStore()
|
|
|
48
45
|
</script>
|
|
49
46
|
|
|
50
47
|
<style lang="postcss" scoped>
|
|
51
|
-
.sidebar-close-backdrop {
|
|
52
|
-
position: fixed;
|
|
53
|
-
inset: 0;
|
|
54
|
-
z-index: 1000;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
48
|
.core-layout {
|
|
58
49
|
display: flex;
|
|
59
50
|
height: 100dvh;
|
package/lib/locales/de.json
CHANGED
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
"dashboard": "Dashboard",
|
|
21
21
|
"documentation-name": "{name} Dokumentation",
|
|
22
|
+
"hosts": "Hosts",
|
|
22
23
|
"loading-in-progress": "Ladevorgang läuft…",
|
|
23
24
|
"log-out": "Abmelden",
|
|
24
25
|
"master": "Primärer Host",
|
|
26
|
+
"n-vms": "1 VM | {n} VMs",
|
|
25
27
|
"network": "Netzwerk",
|
|
26
28
|
"object-not-found": "Objekt {id} wurde nicht gefunden…",
|
|
27
29
|
"power-on-for-console": "Konsole ist nach Start der VM verfügbar",
|
|
@@ -29,5 +31,9 @@
|
|
|
29
31
|
"storage": "Speicher",
|
|
30
32
|
"support-name": "{name} pro support",
|
|
31
33
|
"system": "System",
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
"tasks": "Aufgaben",
|
|
36
|
+
"tasks.quick-view": "Schnellansicht der Aufgaben",
|
|
37
|
+
|
|
38
|
+
"vms": "VMs"
|
|
33
39
|
}
|
package/lib/locales/en.json
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"coming-soon": "Coming soon!",
|
|
9
9
|
"console": "Console",
|
|
10
10
|
|
|
11
|
+
"core.copied": "Copied",
|
|
12
|
+
"core.copy-id": "Copy ID",
|
|
11
13
|
"core.close": "Close",
|
|
12
14
|
"core.current": "Current",
|
|
13
15
|
"core.filter": "Filter",
|
|
@@ -16,6 +18,12 @@
|
|
|
16
18
|
"core.open": "Open",
|
|
17
19
|
"core.quick-actions": "Quick actions",
|
|
18
20
|
|
|
21
|
+
"core.search": "Search",
|
|
22
|
+
|
|
23
|
+
"core.search-bar.label": "Search Engine",
|
|
24
|
+
"core.search-bar.placeholder": "Write your query…",
|
|
25
|
+
"core.search-bar.use-query-builder": "Use query builder",
|
|
26
|
+
|
|
19
27
|
"core.select.all": "Select all",
|
|
20
28
|
"core.select.none": "Select none",
|
|
21
29
|
|
|
@@ -33,15 +41,29 @@
|
|
|
33
41
|
|
|
34
42
|
"dashboard": "Dashboard",
|
|
35
43
|
"documentation-name": "{name} documentation",
|
|
44
|
+
"hosts": "Hosts",
|
|
36
45
|
"loading-in-progress": "Loading in progress…",
|
|
37
46
|
"log-out": "Log out",
|
|
38
47
|
"master": "Primary host",
|
|
48
|
+
"n-vms": "1 VM | {n} VMs",
|
|
39
49
|
"network": "Network",
|
|
40
50
|
"object-not-found": "Object {id} can't be found…",
|
|
41
51
|
"power-on-for-console": "Power on your VM to access its console",
|
|
52
|
+
"see-all": "See all",
|
|
42
53
|
"stats": "Stats",
|
|
43
54
|
"storage": "Storage",
|
|
44
55
|
"support-name": "{name} pro support",
|
|
45
56
|
"system": "System",
|
|
46
|
-
|
|
57
|
+
|
|
58
|
+
"tasks": "Tasks",
|
|
59
|
+
"tasks.n-subtasks": "{n} subtask | {n} subtasks",
|
|
60
|
+
"tasks.no-tasks": "No tasks",
|
|
61
|
+
"tasks.quick-view": "Tasks' quick view",
|
|
62
|
+
"tasks.quick-view.all": "All",
|
|
63
|
+
"tasks.quick-view.done": "Done",
|
|
64
|
+
"tasks.quick-view.failed": "Failed",
|
|
65
|
+
"tasks.quick-view.in-progress": "In progress",
|
|
66
|
+
|
|
67
|
+
"total": "Total",
|
|
68
|
+
"vms": "VMs"
|
|
47
69
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"alarms": "هشدارها",
|
|
3
|
+
|
|
4
|
+
"bytes.ki": "KiB",
|
|
5
|
+
"bytes.mi": "MiB",
|
|
6
|
+
"bytes.gi": "GiB",
|
|
7
|
+
|
|
8
|
+
"coming-soon": "به زودی!",
|
|
9
|
+
"console": "کنسول",
|
|
10
|
+
|
|
11
|
+
"core.close": "بستن",
|
|
12
|
+
"core.current": "جاری",
|
|
13
|
+
"core.filter": "فیلتر",
|
|
14
|
+
"core.group": "گروه",
|
|
15
|
+
"core.hide": "مخفی کردن",
|
|
16
|
+
"core.open": "باز کردن",
|
|
17
|
+
"core.quick-actions": "اقدامات سریع",
|
|
18
|
+
|
|
19
|
+
"core.select.all": "انتخاب همه",
|
|
20
|
+
"core.select.none": "انتخاب هیچکدام",
|
|
21
|
+
|
|
22
|
+
"core.sidebar.close": "بستن نوار کناری",
|
|
23
|
+
"core.sidebar.lock": "قفل کردن نوار کناری باز شده",
|
|
24
|
+
"core.sidebar.open": "باز کردن نوار کناری",
|
|
25
|
+
"core.sidebar.unlock": "از حالت قفل خارج کردن نوار کناری",
|
|
26
|
+
|
|
27
|
+
"core.sort.ascending": "مرتب سازی صعودی",
|
|
28
|
+
"core.sort.descending": "مرتب سازی نزولی",
|
|
29
|
+
|
|
30
|
+
"dark-mode.enable": "فعال کردن حالت تاریک",
|
|
31
|
+
"dark-mode.disable": "غیر فعال کردن حالت تاریک",
|
|
32
|
+
"dark-mode.auto": "حالت خودکار تاریک",
|
|
33
|
+
|
|
34
|
+
"dashboard": "داشبورد",
|
|
35
|
+
"documentation-name": "اسناد {name}",
|
|
36
|
+
"loading-in-progress": "بارگیری در حال انجام است…",
|
|
37
|
+
"log-out": "خروج",
|
|
38
|
+
"master": "میزبان اصلی",
|
|
39
|
+
"network": "شبکه",
|
|
40
|
+
"power-on-for-console": "ماشین مجازی خود را روشن کنید تا به کنسول آن دسترسی داشته باشید",
|
|
41
|
+
"stats": "آمار",
|
|
42
|
+
"storage": "ذخیره سازی",
|
|
43
|
+
"support-name": "پشتیبانی حرفه ای {name}",
|
|
44
|
+
"system": "سیستم",
|
|
45
|
+
"tasks": "کارها"
|
|
46
|
+
}
|
package/lib/locales/fr.json
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"coming-soon": "Bientôt disponible !",
|
|
9
9
|
"console": "Console",
|
|
10
10
|
|
|
11
|
+
"core.copied": "Copié",
|
|
12
|
+
"core.copy-id": "Copier l'ID",
|
|
11
13
|
"core.close": "Fermer",
|
|
12
14
|
"core.current": "Actuel",
|
|
13
15
|
"core.filter": "Filtrer",
|
|
@@ -16,6 +18,12 @@
|
|
|
16
18
|
"core.open": "Ouvrir",
|
|
17
19
|
"core.quick-actions": "Actions rapides",
|
|
18
20
|
|
|
21
|
+
"core.search": "Rechercher",
|
|
22
|
+
|
|
23
|
+
"core.search-bar.label": "Moteur de recherche",
|
|
24
|
+
"core.search-bar.placeholder": "Écrivez votre requête…",
|
|
25
|
+
"core.search-bar.use-query-builder": "Utiliser le constructeur de requête",
|
|
26
|
+
|
|
19
27
|
"core.select.all": "Tout sélectionner",
|
|
20
28
|
"core.select.none": "Tout désélectionner",
|
|
21
29
|
|
|
@@ -33,15 +41,29 @@
|
|
|
33
41
|
|
|
34
42
|
"dashboard": "Tableau de bord",
|
|
35
43
|
"documentation-name": "Documentation {name}",
|
|
44
|
+
"hosts": "Hôtes",
|
|
36
45
|
"loading-in-progress": "Chargement en cours…",
|
|
37
46
|
"log-out": "Se déconnecter",
|
|
38
47
|
"master": "Hôte primaire",
|
|
48
|
+
"n-vms": "1 VM | {n} VMs",
|
|
39
49
|
"network": "Réseau",
|
|
40
50
|
"object-not-found": "L'objet {id} est introuvable…",
|
|
41
51
|
"power-on-for-console": "Allumez votre VM pour accéder à sa console",
|
|
52
|
+
"see-all": "Voir tout",
|
|
42
53
|
"stats": "Stats",
|
|
43
54
|
"storage": "Stockage",
|
|
44
55
|
"support-name": "Support pro {name}",
|
|
45
56
|
"system": "Système",
|
|
46
|
-
|
|
57
|
+
|
|
58
|
+
"tasks": "Tâches",
|
|
59
|
+
"tasks.n-subtasks": "{n} sous-tâche | {n} sous-tâches",
|
|
60
|
+
"tasks.no-tasks": "Aucune tâche",
|
|
61
|
+
"tasks.quick-view": "Vue rapide des tâches",
|
|
62
|
+
"tasks.quick-view.all": "Toutes",
|
|
63
|
+
"tasks.quick-view.done": "Terminées",
|
|
64
|
+
"tasks.quick-view.failed": "Échouées",
|
|
65
|
+
"tasks.quick-view.in-progress": "En cours",
|
|
66
|
+
|
|
67
|
+
"total": "Total",
|
|
68
|
+
"vms": "VMs"
|
|
47
69
|
}
|