@xen-orchestra/web-core 0.28.0 → 0.30.0

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.
Files changed (41) hide show
  1. package/lib/assets/css/typography/_utils.pcss +11 -0
  2. package/lib/assets/no-result.svg +276 -80
  3. package/lib/components/console/VtsRemoteConsole.vue +6 -2
  4. package/lib/components/data-table/VtsDataTable.vue +11 -6
  5. package/lib/components/progress-bar/VtsProgressBar.vue +54 -0
  6. package/lib/components/progress-bar-group/VtsProgressBarGroup.vue +76 -0
  7. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +2 -2
  8. package/lib/components/quick-info-row/VtsQuickInfoRow.vue +1 -1
  9. package/lib/components/select/VtsSelect.vue +1 -1
  10. package/lib/components/state-hero/VtsStateHero.vue +107 -47
  11. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  12. package/lib/components/ui/card/UiCard.vue +6 -1
  13. package/lib/components/ui/collapsible-list/UiCollapsibleList.vue +1 -2
  14. package/lib/components/ui/data-ruler/UiDataRuler.vue +54 -6
  15. package/lib/components/ui/progress-bar/UiProgressBar.vue +14 -70
  16. package/lib/composables/tree-filter.composable.ts +4 -2
  17. package/lib/icons/index.ts +1 -1
  18. package/lib/icons/object-icons.ts +33 -2
  19. package/lib/locales/cs.json +1 -1
  20. package/lib/locales/de.json +1 -1
  21. package/lib/locales/en.json +24 -2
  22. package/lib/locales/es.json +1 -1
  23. package/lib/locales/fa.json +1 -1
  24. package/lib/locales/fr.json +24 -2
  25. package/lib/locales/it.json +1 -1
  26. package/lib/locales/nl.json +1 -1
  27. package/lib/locales/sv.json +1 -1
  28. package/lib/packages/progress/use-progress-group.ts +1 -1
  29. package/lib/utils/progress.util.ts +72 -0
  30. package/lib/utils/size.util.ts +6 -0
  31. package/package.json +1 -1
  32. package/lib/components/state-hero/VtsAllDoneHero.vue +0 -16
  33. package/lib/components/state-hero/VtsAllGoodHero.vue +0 -16
  34. package/lib/components/state-hero/VtsComingSoonHero.vue +0 -16
  35. package/lib/components/state-hero/VtsErrorNoDataHero.vue +0 -14
  36. package/lib/components/state-hero/VtsLoadingHero.vue +0 -57
  37. package/lib/components/state-hero/VtsNoDataHero.vue +0 -14
  38. package/lib/components/state-hero/VtsNoSelectionHero.vue +0 -16
  39. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +0 -17
  40. package/lib/components/state-hero/VtsOfflineHero.vue +0 -16
  41. package/lib/components/state-hero/VtsPageNotFoundHero.vue +0 -33
@@ -5,7 +5,7 @@
5
5
  {{ label }}
6
6
  </slot>
7
7
  </span>
8
- <span class="typo-body-regular value">
8
+ <span v-tooltip class="typo-body-regular value text-ellipsis">
9
9
  <slot name="value">
10
10
  {{ value }}
11
11
  </slot>
@@ -28,7 +28,7 @@
28
28
  </div>
29
29
  </template>
30
30
  <UiDropdown v-if="isLoading || options.length === 0" accent="normal" disabled>
31
- {{ isLoading ? t('loading-in-progress') : t('no-results') }}
31
+ {{ isLoading ? t('loading') : t('no-results') }}
32
32
  </UiDropdown>
33
33
  <template v-for="option of options" :key="option.id">
34
34
  <slot :option="option as FormSelectIdToOption<TSelectId>">
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <div :class="[type, { error }]" class="vts-state-hero">
2
+ <div :class="[className, { horizontal, error, success, 'no-background': noBackground }]" class="vts-state-hero">
3
3
  <UiLoader v-if="busy" class="loader" />
4
- <img v-else-if="imageSrc" :src="imageSrc" alt="" class="image" />
4
+ <img v-else-if="imageSrc" :src="imageSrc" :alt="type" class="image" />
5
5
  <div v-if="slots.default" :class="typoClass" class="content">
6
6
  <slot />
7
7
  </div>
@@ -10,23 +10,28 @@
10
10
 
11
11
  <script lang="ts" setup>
12
12
  import UiLoader from '@core/components/ui/loader/UiLoader.vue'
13
+ import { toVariants } from '@core/utils/to-variants.util.ts'
13
14
  import { computed } from 'vue'
14
15
 
15
- export type StateHeroType = 'page' | 'card' | 'panel' | 'table'
16
-
17
- const { type, busy, image, noBackground } = defineProps<{
18
- type: StateHeroType
16
+ export type StateHeroFormat = 'page' | 'card' | 'panel' | 'table'
17
+
18
+ type StateHeroType =
19
+ | 'no-result'
20
+ | 'under-construction'
21
+ | 'no-data'
22
+ | 'no-selection'
23
+ | 'error'
24
+ | 'not-found'
25
+ | 'offline'
26
+ | 'all-good'
27
+ | 'all-done'
28
+
29
+ const { format, type, size, busy } = defineProps<{
30
+ format: StateHeroFormat
31
+ type?: StateHeroType
32
+ size: 'extra-small' | 'small' | 'medium' | 'large'
33
+ horizontal?: boolean
19
34
  busy?: boolean
20
- image?:
21
- | 'no-result'
22
- | 'under-construction'
23
- | 'no-data'
24
- | 'no-selection'
25
- | 'error'
26
- | 'not-found'
27
- | 'offline'
28
- | 'all-good'
29
- | 'all-done'
30
35
  noBackground?: boolean
31
36
  }>()
32
37
 
@@ -34,15 +39,20 @@ const slots = defineSlots<{
34
39
  default?(): any
35
40
  }>()
36
41
 
37
- const typoClass = computed(() => (type === 'page' ? 'typo-h2' : 'typo-h4'))
38
- const error = computed(() => !noBackground && !busy && image === 'error')
42
+ const typoClass = computed(() => (format === 'page' ? 'typo-h2' : 'typo-h4'))
43
+
44
+ const className = computed(() => toVariants({ size, format }))
45
+
46
+ const error = computed(() => !busy && type === 'error')
47
+
48
+ const success = computed(() => !busy && (type === 'all-good' || type === 'all-done'))
39
49
 
40
50
  const imageSrc = computed(() => {
41
- if (!image) {
51
+ if (!type) {
42
52
  return undefined
43
53
  }
44
54
 
45
- return new URL(`../../assets/${image}.svg`, import.meta.url).href
55
+ return new URL(`../../assets/${type}.svg`, import.meta.url).href
46
56
  })
47
57
  </script>
48
58
 
@@ -50,33 +60,54 @@ const imageSrc = computed(() => {
50
60
  .vts-state-hero {
51
61
  flex: 1;
52
62
  display: flex;
53
- flex-direction: column;
54
63
  align-items: center;
55
64
  justify-content: center;
65
+ gap: 2.4rem;
56
66
 
57
- &.error {
58
- background-color: var(--color-danger-background-selected);
67
+ &:not(.horizontal) {
68
+ flex-direction: column;
59
69
 
60
70
  .content {
61
- color: var(--color-danger-txt-base);
71
+ align-items: center;
62
72
  }
63
73
  }
64
74
 
75
+ .image {
76
+ order: 2;
77
+ }
78
+
79
+ .content {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: 1.6rem;
83
+ }
84
+
65
85
  .loader,
66
86
  .content {
87
+ order: 3;
67
88
  color: var(--color-brand-txt-base);
68
89
  }
69
90
 
70
- .image {
71
- order: 2;
91
+ &.success {
92
+ .content {
93
+ color: var(--color-success-txt-base);
94
+ }
72
95
  }
73
96
 
74
- .content {
75
- order: 3;
97
+ &.error {
98
+ background-color: var(--color-danger-background-selected);
99
+
100
+ &.no-background {
101
+ background-color: transparent;
102
+ }
103
+
104
+ .content {
105
+ color: var(--color-danger-txt-base);
106
+ }
76
107
  }
77
108
 
78
- &.page {
79
- gap: 2.4rem;
109
+ &.format--card {
110
+ gap: 2rem;
80
111
 
81
112
  .content {
82
113
  order: 3;
@@ -84,36 +115,31 @@ const imageSrc = computed(() => {
84
115
 
85
116
  .loader {
86
117
  order: 1;
87
- font-size: 10rem;
88
118
  }
89
119
 
90
120
  .image {
91
121
  order: 2;
92
- width: 90%;
93
- max-height: none;
94
122
  }
95
123
  }
96
124
 
97
- &.card {
98
- gap: 2rem;
125
+ &.format--table {
126
+ padding: 4rem;
127
+ gap: 2.4rem;
99
128
 
100
129
  .content {
101
130
  order: 3;
102
131
  }
103
132
 
104
133
  .loader {
105
- font-size: 6rem;
106
134
  order: 1;
107
135
  }
108
136
 
109
137
  .image {
110
138
  order: 2;
111
- width: 70%;
112
- max-height: 20rem;
113
139
  }
114
140
  }
115
141
 
116
- &.panel {
142
+ &.format--panel {
117
143
  gap: 4rem;
118
144
  justify-content: unset;
119
145
  padding-top: 8rem;
@@ -124,31 +150,65 @@ const imageSrc = computed(() => {
124
150
 
125
151
  .loader {
126
152
  order: 3;
127
- font-size: 6.4rem;
128
153
  }
129
154
 
130
155
  .image {
131
156
  order: 2;
132
- width: 80%;
133
157
  }
134
158
  }
135
159
 
136
- &.table {
137
- padding: 4rem;
138
- gap: 2.4rem;
160
+ &.format--page {
161
+ gap: 10rem;
139
162
 
140
163
  .content {
141
164
  order: 3;
142
165
  }
143
166
 
167
+ .loader {
168
+ order: 1;
169
+ }
170
+
144
171
  .image {
145
172
  order: 2;
146
- max-height: 20rem;
147
173
  }
174
+ }
148
175
 
176
+ &.size--extra-small {
149
177
  .loader {
150
- order: 1;
151
- font-size: 10rem;
178
+ font-size: 1.6rem;
179
+ }
180
+
181
+ .image {
182
+ max-height: 14rem;
183
+ }
184
+ }
185
+
186
+ &.size--small {
187
+ .loader {
188
+ font-size: 2.4rem;
189
+ }
190
+
191
+ .image {
192
+ max-height: 18rem;
193
+ }
194
+ }
195
+
196
+ &.size--medium {
197
+ .loader {
198
+ font-size: 6.4rem;
199
+ }
200
+
201
+ .image {
202
+ max-height: 30rem;
203
+ }
204
+ }
205
+
206
+ &.size--large {
207
+ .loader {
208
+ font-size: 9.6rem;
209
+ }
210
+ .image {
211
+ max-height: 50rem;
152
212
  }
153
213
  }
154
214
  }
@@ -3,7 +3,7 @@
3
3
  <li v-if="loading">
4
4
  <div class="loading">
5
5
  <UiLoader />
6
- <div>{{ t('loading-in-progress') }}</div>
6
+ <div>{{ t('loading') }}</div>
7
7
  </div>
8
8
  </li>
9
9
  <template v-else>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="ui-card" :class="{ horizontal }">
2
+ <div class="ui-card" :class="{ horizontal, 'has-error': hasError }">
3
3
  <slot />
4
4
  </div>
5
5
  </template>
@@ -7,6 +7,7 @@
7
7
  <script lang="ts" setup>
8
8
  defineProps<{
9
9
  horizontal?: boolean
10
+ hasError?: boolean
10
11
  }>()
11
12
 
12
13
  defineSlots<{
@@ -39,5 +40,9 @@ defineSlots<{
39
40
  &.horizontal {
40
41
  flex-direction: row;
41
42
  }
43
+
44
+ &.has-error {
45
+ background-color: var(--color-danger-background-selected);
46
+ }
42
47
  }
43
48
  </style>
@@ -5,9 +5,8 @@
5
5
  <slot />
6
6
  </component>
7
7
  <div v-if="hasMoreItems" class="footer">
8
- <span v-if="!isExpanded" class="typo-body-regular-small">{{ t('n-more', { n: remainingItems }) }}</span>
9
8
  <UiButton size="small" accent="brand" variant="tertiary" @click="isExpanded = !isExpanded">
10
- {{ isExpanded ? t('see-less') : t('see-all') }}
9
+ {{ isExpanded ? t('see-less') : t('see-n-more', { n: remainingItems }) }}
11
10
  </UiButton>
12
11
  </div>
13
12
  </div>
@@ -1,24 +1,72 @@
1
1
  <!-- v1 -->
2
2
  <template>
3
3
  <div class="ui-data-ruler typo-body-regular-small">
4
- <span>{{ t('n-percent', 0) }}</span>
5
- <span>{{ t('n-percent', 50) }}</span>
6
- <span>{{ t('n-percent', 100) }}</span>
4
+ <span>{{ n(0, 'percent') }}</span>
5
+ <span>{{ n(max / 200, 'percent') }}</span>
6
+ <span class="max">
7
+ <VtsIcon
8
+ v-if="warning?.accent || warning?.tooltip"
9
+ v-tooltip="warning.tooltip ?? false"
10
+ :class="warning.accent ?? 'warning'"
11
+ class="icon"
12
+ name="fa:exclamation-circle"
13
+ size="small"
14
+ />
15
+ {{ n(max / 100, 'percent') }}</span
16
+ >
7
17
  </div>
8
18
  </template>
9
19
 
10
20
  <script lang="ts" setup>
21
+ import VtsIcon from '@core/components/icon/VtsIcon.vue'
22
+ import { vTooltip } from '@core/directives/tooltip.directive.ts'
11
23
  import { useI18n } from 'vue-i18n'
12
24
 
13
- const { t } = useI18n()
25
+ const { max = 100 } = defineProps<{
26
+ max?: number
27
+ warning?: {
28
+ accent?: 'info' | 'warning' | 'danger'
29
+ tooltip?: string
30
+ }
31
+ }>()
32
+
33
+ const { n } = useI18n()
14
34
  </script>
15
35
 
16
36
  <style lang="postcss" scoped>
17
37
  .ui-data-ruler {
18
- display: flex;
38
+ display: grid;
39
+ grid-template-columns: 1fr auto 1fr;
19
40
  align-items: center;
20
- justify-content: space-between;
21
41
  gap: 1rem;
22
42
  color: var(--color-neutral-txt-secondary);
43
+
44
+ .icon {
45
+ &.warning {
46
+ color: var(--color-warning-item-base);
47
+ }
48
+
49
+ &.danger {
50
+ color: var(--color-danger-item-base);
51
+ }
52
+ }
53
+
54
+ .max {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 0.8rem;
58
+ }
59
+
60
+ & > span:first-child {
61
+ justify-self: start;
62
+ }
63
+
64
+ & > span:nth-child(2) {
65
+ justify-self: center;
66
+ }
67
+
68
+ & > span:last-child {
69
+ justify-self: end;
70
+ }
23
71
  }
24
72
  </style>
@@ -1,19 +1,12 @@
1
1
  <!-- v2 -->
2
2
  <template>
3
- <div class="ui-progress-bar" :class="className">
3
+ <div :class="className" class="ui-progress-bar">
4
4
  <div class="progress-bar">
5
- <div class="fill" :style="{ width: `${fillWidth}%` }" />
5
+ <div :style="{ width: fillWidth }" class="fill" />
6
6
  </div>
7
- <div v-if="shouldShowSteps" class="steps typo-body-regular-small">
8
- <span>{{ n(0, 'percent') }}</span>
9
- <span v-for="step in steps" :key="step">{{ n(step, 'percent') }}</span>
10
- </div>
11
- <VtsLegendList class="legend">
12
- <UiLegend v-if="displayMode === 'percent'" :accent :value="Math.round(percentage)" unit="%">
13
- {{ legend }}
14
- </UiLegend>
15
- <UiLegend v-else :accent :value="legendValue">
16
- {{ legend }}
7
+ <VtsLegendList v-if="legend" class="legend">
8
+ <UiLegend :accent :value="legend.value">
9
+ {{ legend.label }}
17
10
  </UiLegend>
18
11
  </VtsLegendList>
19
12
  </div>
@@ -22,61 +15,20 @@
22
15
  <script lang="ts" setup>
23
16
  import VtsLegendList from '@core/components/legend-list/VtsLegendList.vue'
24
17
  import UiLegend from '@core/components/ui/legend/UiLegend.vue'
25
- import { formatSizeRaw } from '@core/utils/size.util.ts'
26
18
  import { toVariants } from '@core/utils/to-variants.util'
27
- import { useClamp, useMax } from '@vueuse/math'
28
19
  import { computed } from 'vue'
29
- import { useI18n } from 'vue-i18n'
30
-
31
- interface Props {
32
- legend: string
33
- value: number
34
- showSteps?: boolean
35
- }
36
- interface PercentageProps {
37
- max?: number
38
- displayMode: 'percent'
39
- }
40
- interface ValueProps {
41
- max: number
42
- displayMode: 'value'
43
- }
44
-
45
- const { value: _value, max: _max, showSteps, displayMode } = defineProps<Props & (PercentageProps | ValueProps)>()
46
-
47
- const { n } = useI18n()
48
20
 
49
- const value = useMax(0, () => _value)
21
+ export type ProgressBarAccent = 'info' | 'warning' | 'danger'
50
22
 
51
- const max = computed(() => _max ?? 100)
23
+ export type ProgressBarLegend = { label: string; value?: string | number }
52
24
 
53
- const percentage = computed(() => (max.value <= 0 ? 0 : (value.value / max.value) * 100))
54
- const maxPercentage = computed(() => Math.ceil(percentage.value / 100) * 100)
55
- const fillWidth = useClamp(() => (percentage.value / maxPercentage.value) * 100 || 0, 0, 100)
56
- const shouldShowSteps = computed(() => showSteps || percentage.value > 100)
57
- const steps = useMax(1, () => Math.floor(maxPercentage.value / 100))
58
-
59
- const accent = computed(() => {
60
- if (percentage.value >= 90) {
61
- return 'danger'
62
- }
25
+ const { accent, legend } = defineProps<{
26
+ accent: ProgressBarAccent
27
+ fillWidth: string
28
+ legend?: ProgressBarLegend
29
+ }>()
63
30
 
64
- if (percentage.value >= 80) {
65
- return 'warning'
66
- }
67
-
68
- return 'info'
69
- })
70
-
71
- const className = computed(() => toVariants({ accent: accent.value }))
72
-
73
- const formattedValue = computed(() => formatSizeRaw(value.value, 1))
74
-
75
- const formattedMax = computed(() => formatSizeRaw(max.value, 0))
76
-
77
- const legendValue = computed(() => {
78
- return `${formattedValue.value.value} ${formattedValue.value.prefix} / ${formattedMax.value.value} ${formattedMax.value.prefix}`
79
- })
31
+ const className = computed(() => toVariants({ accent }))
80
32
  </script>
81
33
 
82
34
  <style lang="postcss" scoped>
@@ -87,30 +39,22 @@ const legendValue = computed(() => {
87
39
 
88
40
  .progress-bar {
89
41
  width: 100%;
90
- height: 1.2rem;
91
42
  border-radius: 0.4rem;
92
43
  overflow: hidden;
93
44
  background-color: var(--color-neutral-background-disabled);
94
45
 
95
46
  .fill {
96
47
  width: 0;
97
- height: 100%;
48
+ height: 1.2rem;
98
49
  transition: width 0.25s ease-in-out;
99
50
  }
100
51
  }
101
52
 
102
- .steps {
103
- color: var(--color-neutral-txt-secondary);
104
- display: flex;
105
- justify-content: space-between;
106
- }
107
-
108
53
  .legend {
109
54
  margin-inline-start: auto;
110
55
  }
111
56
 
112
57
  /* ACCENT */
113
-
114
58
  &.accent--info {
115
59
  .fill {
116
60
  background-color: var(--color-info-item-base);
@@ -1,12 +1,14 @@
1
1
  import type { TreeNodeBase } from '@core/composables/tree/tree-node-base'
2
+ import { refDebounced } from '@vueuse/shared'
2
3
  import { computed, ref } from 'vue'
3
4
 
4
5
  export function useTreeFilter() {
5
6
  const filter = ref('')
6
- const hasFilter = computed(() => filter.value.trim().length > 0)
7
+ const debouncedFilter = refDebounced(filter, 500)
8
+ const hasFilter = computed(() => debouncedFilter.value.trim().length > 0)
7
9
 
8
10
  const predicate = (node: TreeNodeBase) =>
9
- hasFilter.value ? node.label.toLocaleLowerCase().includes(filter.value.toLocaleLowerCase()) : undefined
11
+ hasFilter.value ? node.label.toLocaleLowerCase().includes(debouncedFilter.value.toLocaleLowerCase()) : undefined
10
12
 
11
13
  return { filter, predicate }
12
14
  }
@@ -12,7 +12,7 @@ export const icons = defineIconPack({
12
12
 
13
13
  export type IconName = Exclude<keyof typeof icons, typeof ICON_SYMBOL>
14
14
 
15
- export type ObjectIconName = Extract<IconName, `object:${string}`>
15
+ export type ObjectIconName = Extract<IconName, `object:${string}:${string}`>
16
16
 
17
17
  export function icon<TName extends IconName>(name: TName): TName {
18
18
  return name
@@ -3,9 +3,12 @@ import { defineIcon } from '@core/packages/icon/define-icon.ts'
3
3
  import type { IconTransforms } from '@core/packages/icon/types.ts'
4
4
  import { createMapper } from '@core/packages/mapper/create-mapper.ts'
5
5
  import {
6
+ faArrowLeft,
6
7
  faBan,
8
+ faBoxArchive,
7
9
  faCheck,
8
10
  faCircle,
11
+ faClock,
9
12
  faDatabase,
10
13
  faDesktop,
11
14
  faMinus,
@@ -17,7 +20,6 @@ import {
17
20
  faServer,
18
21
  faStop,
19
22
  faTriangleExclamation,
20
- faWarehouse,
21
23
  faXmark,
22
24
  } from '@fortawesome/free-solid-svg-icons'
23
25
 
@@ -148,6 +150,32 @@ function getMainColor(state: string) {
148
150
  : 'var(--color-neutral-txt-primary)'
149
151
  }
150
152
 
153
+ const backupIcon = defineIcon({ icon: faBoxArchive })
154
+
155
+ const backupRepository = defineIcon([
156
+ { icon: backupIcon, translate: [0, -4], borderColor: defaultTransforms.borderColor, size: 10 },
157
+ { icon: backupIcon, translate: [-4, 4], borderColor: defaultTransforms.borderColor, size: 10 },
158
+ { icon: backupIcon, translate: [4, 4], borderColor: defaultTransforms.borderColor, size: 10 },
159
+ ])
160
+
161
+ const backupJob = defineIcon([
162
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
163
+ { icon: faCircle, translate: [6, 6], borderColor: defaultTransforms.borderColor, size: 12 },
164
+ { icon: faArrowLeft, translate: [6, 6], color: 'var(--color-neutral-background-primary)', size: 8 },
165
+ ])
166
+
167
+ const backupSchedule = defineIcon([
168
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
169
+ { icon: faCircle, translate: [6, 6], size: 12, color: 'var(--color-neutral-background-primary)' },
170
+ { icon: faClock, translate: [6, 6], size: 11 },
171
+ ])
172
+
173
+ const backupLog = defineIcon([
174
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
175
+ { icon: faCircle, translate: [6, 6], borderColor: defaultTransforms.borderColor, size: 12 },
176
+ { icon: faPlay, translate: [6.5, 6], color: 'var(--color-neutral-background-primary)', size: 6 },
177
+ ])
178
+
151
179
  export const objectIcons = defineIconPack({
152
180
  vm: defineIcon([['running', 'halted', 'suspended', 'paused', 'muted']], state => [
153
181
  {
@@ -179,9 +207,12 @@ export const objectIcons = defineIconPack({
179
207
  ]),
180
208
  'backup-repository': defineIcon([['connected', 'disconnected']], state => [
181
209
  {
182
- icon: faWarehouse,
210
+ icon: backupRepository,
183
211
  color: getMainColor(state),
184
212
  },
185
213
  { icon: getStatusIcon(state) },
186
214
  ]),
215
+ 'backup-job': backupJob,
216
+ 'backup-schedule': backupSchedule,
217
+ 'backup-log': backupLog,
187
218
  })
@@ -313,7 +313,7 @@
313
313
  "load-average": "Průměrné vytížení",
314
314
  "load-now": "Načíst nyní",
315
315
  "loading-hosts": "Načítání hostitelů…",
316
- "loading-in-progress": "Probíhá načítání…",
316
+ "loading": "Probíhá načítání…",
317
317
  "locking-mode": "Režim zamykání",
318
318
  "locking-mode-default": "Výchozí režim zamykání",
319
319
  "log-out": "Odhlásit",
@@ -310,7 +310,7 @@
310
310
  "load-average": "Durchschnittsauslastung",
311
311
  "load-now": "Jetzt laden",
312
312
  "loading-hosts": "Lade Hosts…",
313
- "loading-in-progress": "Ladevorgang läuft…",
313
+ "loading": "Ladevorgang läuft…",
314
314
  "locking-mode": "Sperrmodus",
315
315
  "locking-mode-default": "Standard-Sperrmodus",
316
316
  "log-out": "Abmelden",