@nethserver/ns8-ui-lib 0.0.40 → 0.0.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nethserver/ns8-ui-lib",
3
- "version": "0.0.40",
3
+ "version": "0.0.44",
4
4
  "description": "Vue.js library for NethServer 8 UI",
5
5
  "keywords": [
6
6
  "nethserver",
@@ -61,6 +61,7 @@
61
61
  "@rollup/plugin-json": "^4.1.0",
62
62
  "core-js": "^3.15.2",
63
63
  "date-fns": "^2.23.0",
64
- "date-fns-tz": "^1.1.6"
64
+ "date-fns-tz": "^1.1.6",
65
+ "vue-date-fns": "^2.0.1"
65
66
  }
66
67
  }
@@ -0,0 +1,357 @@
1
+ <template>
2
+ <cv-tile kind="standard" :light="light" class="ns-backup-card">
3
+ <!-- icon -->
4
+ <div class="row">
5
+ <NsSvg :svg="Save32" />
6
+ </div>
7
+ <div class="row">
8
+ <h3 class="title">{{ title }}</h3>
9
+ </div>
10
+ <div v-if="loading" class="row">
11
+ <cv-skeleton-text
12
+ :paragraph="true"
13
+ :line-count="5"
14
+ class="mg-top-sm"
15
+ ></cv-skeleton-text>
16
+ </div>
17
+ <template v-else-if="!backupsContainingInstance.length">
18
+ <div class="row">
19
+ <!-- no backup -->
20
+ <NsInlineNotification
21
+ kind="warning"
22
+ :title="noBackupMessage"
23
+ :showCloseButton="false"
24
+ :actionLabel="scheduleBackupLabel"
25
+ @action="goToBackup"
26
+ />
27
+ </div>
28
+ </template>
29
+ <template v-else>
30
+ <div v-for="backup in backupsContainingInstance" :key="backup.id">
31
+ <div class="row">
32
+ <h5
33
+ v-if="backupsContainingInstance.length > 1"
34
+ class="title mg-top-sm"
35
+ >
36
+ {{ backup.name }}
37
+ </h5>
38
+ </div>
39
+ <div class="table-wrapper">
40
+ <div class="table">
41
+ <!-- status -->
42
+ <div class="tr">
43
+ <div class="td label">{{ statusLabel }}</div>
44
+ <div class="td status">
45
+ <span v-if="!backup.enabled" class="ns-warning">
46
+ {{ backupDisabledLabel }}
47
+ </span>
48
+ <span
49
+ v-else-if="
50
+ status[backup.id] && status[backup.id].success == true
51
+ "
52
+ class="ns-success"
53
+ >
54
+ <span>{{ statusSuccessLabel }}</span>
55
+ </span>
56
+ <span
57
+ v-else-if="
58
+ status[backup.id] && status[backup.id].success == false
59
+ "
60
+ class="ns-error"
61
+ >
62
+ {{ statusErrorLabel }}
63
+ </span>
64
+ <span v-else class="ns-warning">
65
+ {{ statusNotRunLabel }}
66
+ </span>
67
+ </div>
68
+ </div>
69
+ <!-- repository -->
70
+ <div class="tr">
71
+ <div class="td label">{{ repositoryLabel }}</div>
72
+ <div class="td">
73
+ {{ backup.repoName }}
74
+ </div>
75
+ </div>
76
+ <!-- completed -->
77
+ <div class="tr">
78
+ <div class="td label">{{ completedLabel }}</div>
79
+ <div class="td">
80
+ <span v-if="status[backup.id] && status[backup.id].end">
81
+ <cv-tooltip
82
+ alignment="center"
83
+ direction="bottom"
84
+ :tip="
85
+ (status[backup.id].end * 1000)
86
+ | date('yyyy-MM-dd HH:mm:ss')
87
+ "
88
+ class="info tooltip-with-text-trigger"
89
+ >
90
+ {{
91
+ formatDateDistance(
92
+ status[backup.id].end * 1000,
93
+ new Date(),
94
+ {
95
+ addSuffix: true,
96
+ }
97
+ )
98
+ }}
99
+ </cv-tooltip>
100
+ </span>
101
+ <span v-else>-</span>
102
+ </div>
103
+ </div>
104
+ <!-- duration -->
105
+ <div class="tr">
106
+ <div class="td label">{{ durationLabel }}</div>
107
+ <div class="td">
108
+ <span
109
+ v-if="
110
+ status[backup.id] &&
111
+ status[backup.id].end &&
112
+ status[backup.id].start
113
+ "
114
+ >
115
+ {{
116
+ (status[backup.id].end - status[backup.id].start)
117
+ | secondsFormat
118
+ }}
119
+ </span>
120
+ <span v-else>-</span>
121
+ </div>
122
+ </div>
123
+ <!-- total size -->
124
+ <div class="tr">
125
+ <div class="td label">{{ totalSizeLabel }}</div>
126
+ <div class="td">
127
+ <span v-if="status[backup.id] && status[backup.id].total_size">
128
+ {{ status[backup.id].total_size | byteFormat }}
129
+ </span>
130
+ <span v-else>-</span>
131
+ </div>
132
+ </div>
133
+ <!-- total file count -->
134
+ <div class="tr">
135
+ <div class="td label">{{ totalFileCountLabel }}</div>
136
+ <div class="td">
137
+ <span
138
+ v-if="status[backup.id] && status[backup.id].total_file_count"
139
+ >
140
+ {{ status[backup.id].total_file_count | humanFormat }}
141
+ ({{ status[backup.id].total_file_count }})
142
+ </span>
143
+ <span v-else>-</span>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </template>
150
+ <div>
151
+ <div class="row mg-top-sm">
152
+ <NsButton
153
+ kind="ghost"
154
+ :icon="ArrowRight20"
155
+ @click="goToBackup"
156
+ :disabled="loading"
157
+ >{{ goToBackupLabel }}
158
+ </NsButton>
159
+ </div>
160
+ </div>
161
+ </cv-tile>
162
+ </template>
163
+
164
+ <script>
165
+ import IconService from "../lib-mixins/icon.js";
166
+ import DateTimeService from "../lib-mixins/dateTime.js";
167
+
168
+ export default {
169
+ name: "NsBackupCard",
170
+ mixins: [IconService, DateTimeService],
171
+ props: {
172
+ title: {
173
+ type: String,
174
+ default: "Backup",
175
+ },
176
+ noBackupMessage: {
177
+ type: String,
178
+ default: "No backup configured",
179
+ },
180
+ scheduleBackupLabel: {
181
+ type: String,
182
+ default: "Configure",
183
+ },
184
+ goToBackupLabel: {
185
+ type: String,
186
+ default: "Go to Backup",
187
+ },
188
+ repositoryLabel: {
189
+ type: String,
190
+ default: "Repository",
191
+ },
192
+ statusLabel: {
193
+ type: String,
194
+ default: "Status",
195
+ },
196
+ statusSuccessLabel: {
197
+ type: String,
198
+ default: "Success",
199
+ },
200
+ statusNotRunLabel: {
201
+ type: String,
202
+ default: "Backup has not run yet",
203
+ },
204
+ statusErrorLabel: {
205
+ type: String,
206
+ default: "Error",
207
+ },
208
+ completedLabel: {
209
+ type: String,
210
+ default: "Completed",
211
+ },
212
+ durationLabel: {
213
+ type: String,
214
+ default: "Duration",
215
+ },
216
+ totalSizeLabel: {
217
+ type: String,
218
+ default: "Total size",
219
+ },
220
+ totalFileCountLabel: {
221
+ type: String,
222
+ default: "Total file count",
223
+ },
224
+ backupDisabledLabel: {
225
+ type: String,
226
+ default: "Disabled",
227
+ },
228
+ moduleId: {
229
+ type: String,
230
+ required: true,
231
+ },
232
+ moduleUiName: {
233
+ type: String,
234
+ default: "",
235
+ },
236
+ repositories: {
237
+ type: Array,
238
+ required: true,
239
+ },
240
+ backups: {
241
+ type: Array,
242
+ required: true,
243
+ },
244
+ loading: {
245
+ type: Boolean,
246
+ default: false,
247
+ },
248
+ coreContext: {
249
+ type: Object,
250
+ required: true,
251
+ },
252
+ light: Boolean,
253
+ },
254
+ data() {
255
+ return {
256
+ backupsContainingInstance: [],
257
+ status: [],
258
+ };
259
+ },
260
+ watch: {
261
+ repositories: function () {
262
+ this.updateData();
263
+ },
264
+ backups: function () {
265
+ this.updateData();
266
+ },
267
+ },
268
+ created() {
269
+ this.updateData();
270
+ },
271
+ methods: {
272
+ updateData() {
273
+ let backupsContainingInstance = [];
274
+
275
+ for (const backup of this.backups) {
276
+ for (const instance of backup.instances) {
277
+ if (instance.module_id == this.moduleId) {
278
+ backupsContainingInstance.push(backup);
279
+ }
280
+ }
281
+ }
282
+ this.backupsContainingInstance = backupsContainingInstance;
283
+
284
+ // status
285
+ for (const backup of this.backupsContainingInstance) {
286
+ const instance = backup.instances.find(
287
+ (i) => i.module_id == this.moduleId
288
+ );
289
+ const status = instance.status;
290
+ this.status[backup.id] = status;
291
+ }
292
+ },
293
+ goToBackup() {
294
+ if (this.coreContext && this.coreContext.$router) {
295
+ this.coreContext.$router.push("/backup");
296
+ }
297
+ },
298
+ },
299
+ };
300
+ </script>
301
+
302
+ <style scoped lang="scss">
303
+ .ns-backup-card {
304
+ display: flex;
305
+ flex-direction: column;
306
+ justify-content: center;
307
+ min-height: 7rem;
308
+ }
309
+
310
+ .row {
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: center;
314
+ margin-bottom: 0.5rem;
315
+ }
316
+
317
+ .title {
318
+ margin-left: 0.25rem;
319
+ margin-right: 0.25rem;
320
+ overflow: hidden;
321
+ text-overflow: ellipsis;
322
+ white-space: nowrap;
323
+ }
324
+
325
+ .table-wrapper {
326
+ display: flex;
327
+ justify-content: center;
328
+ margin-top: 0.5rem;
329
+ }
330
+
331
+ .table {
332
+ display: table;
333
+ }
334
+
335
+ .tr {
336
+ display: table-row;
337
+ }
338
+
339
+ .td {
340
+ display: table-cell;
341
+ }
342
+
343
+ .label {
344
+ padding-right: 0.75rem;
345
+ font-weight: bold;
346
+ text-align: right;
347
+ padding-bottom: 0.5rem;
348
+ }
349
+
350
+ .status {
351
+ font-weight: bold;
352
+ }
353
+
354
+ .backup-status-icon {
355
+ margin-right: 0.25rem;
356
+ }
357
+ </style>
@@ -72,8 +72,6 @@
72
72
  <script>
73
73
  import { CvOverflowMenu } from "@carbon/vue";
74
74
 
75
- //// move to ns8-ui-lib
76
-
77
75
  export default {
78
76
  name: "NsIconMenu",
79
77
  extends: CvOverflowMenu,
@@ -0,0 +1,179 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ `cv-text-input`,
5
+ `ns-text-input`,
6
+ `${carbonPrefix}--form-item`,
7
+ `${carbonPrefix}--text-input-wrapper`,
8
+ { [`${carbonPrefix}--password-input-wrapper`]: isPassword },
9
+ ]"
10
+ >
11
+ <div v-if="hasTooltipSlot">
12
+ <label
13
+ :for="uid"
14
+ :class="[
15
+ `${carbonPrefix}--label`,
16
+ {
17
+ [`${carbonPrefix}--label--disabled`]:
18
+ $attrs.disabled !== undefined && $attrs.disabled,
19
+ },
20
+ ]"
21
+ >{{ label }}</label
22
+ >
23
+ <!-- tooltip -->
24
+ <cv-interactive-tooltip
25
+ :alignment="tooltipAlignment"
26
+ :direction="tooltipDirection"
27
+ class="tooltip"
28
+ >
29
+ <template slot="content">
30
+ <slot name="tooltip"></slot>
31
+ </template>
32
+ </cv-interactive-tooltip>
33
+ </div>
34
+ <label
35
+ v-else
36
+ :for="uid"
37
+ :class="[
38
+ `${carbonPrefix}--label`,
39
+ {
40
+ [`${carbonPrefix}--label--disabled`]:
41
+ $attrs.disabled !== undefined && $attrs.disabled,
42
+ },
43
+ ]"
44
+ >{{ label }}</label
45
+ >
46
+ <div
47
+ :class="[
48
+ `${carbonPrefix}--text-input__field-wrapper`,
49
+ {
50
+ [`${carbonPrefix}--text-input__field-wrapper--warning`]:
51
+ !isInvalid && isWarn,
52
+ },
53
+ ]"
54
+ :data-invalid="isInvalid"
55
+ >
56
+ <WarningFilled16
57
+ v-if="isInvalid"
58
+ :class="`${carbonPrefix}--text-input__invalid-icon`"
59
+ />
60
+ <WarningAltFilled16
61
+ v-if="isWarn"
62
+ :class="`${carbonPrefix}--text-input__invalid-icon ${carbonPrefix}--text-input__invalid-icon--warning`"
63
+ />
64
+
65
+ <input
66
+ :id="uid"
67
+ :class="[
68
+ `${carbonPrefix}--text-input`,
69
+ {
70
+ [`${carbonPrefix}--text-input--light`]: isLight,
71
+ [`${carbonPrefix}--text-input--invalid`]: isInvalid,
72
+ [`${carbonPrefix}--text-input--warning`]: isWarn,
73
+ [`${carbonPrefix}--password-input`]: isPassword,
74
+ },
75
+ ]"
76
+ v-bind="$attrs"
77
+ :value="value"
78
+ v-on="inputListeners"
79
+ :data-toggle-password-visibility="isPassword"
80
+ :type="dataType"
81
+ ref="input"
82
+ />
83
+ <button
84
+ v-if="isPassword"
85
+ :class="[
86
+ `${carbonPrefix}--btn`,
87
+ `${carbonPrefix}--text-input--password__visibility__toggle`,
88
+ `${carbonPrefix}--tooltip__trigger`,
89
+ `${carbonPrefix}--tooltip--a11y`,
90
+ `${carbonPrefix}--tooltip--bottom`,
91
+ `${carbonPrefix}--tooltip--align-center`,
92
+ ]"
93
+ @click="togglePasswordVisibility"
94
+ type="button"
95
+ >
96
+ <span :class="`${carbonPrefix}--assistive-text`">{{
97
+ passwordHideShowLabel
98
+ }}</span>
99
+ <ViewOff16
100
+ v-if="isPasswordVisible"
101
+ :class="`${carbonPrefix}--icon-visibility-off`"
102
+ />
103
+ <View16 v-else :class="`${carbonPrefix}--icon-visibility-off`" />
104
+ </button>
105
+ </div>
106
+ <div :class="`${carbonPrefix}--form-requirement`" v-if="isInvalid">
107
+ <slot name="invalid-message">{{ invalidMessage }}</slot>
108
+ </div>
109
+ <div v-if="isWarn" :class="`${carbonPrefix}--form__requirement`">
110
+ <slot name="warn-text">{{ warnText }}</slot>
111
+ </div>
112
+ <div
113
+ v-if="isHelper"
114
+ :class="[
115
+ `${carbonPrefix}--form__helper-text`,
116
+ { [`${carbonPrefix}--form__helper-text--disabled`]: $attrs.disabled },
117
+ ]"
118
+ >
119
+ <slot name="helper-text">{{ helperText }}</slot>
120
+ </div>
121
+ </div>
122
+ </template>
123
+
124
+ <script>
125
+ import { CvTextInput } from "@carbon/vue";
126
+ import {
127
+ WarningFilled16,
128
+ WarningAltFilled16,
129
+ View16,
130
+ ViewOff16,
131
+ } from "@carbon/icons-vue";
132
+
133
+ export default {
134
+ name: "NsTextInput",
135
+ extends: CvTextInput,
136
+ components: { WarningFilled16, WarningAltFilled16, View16, ViewOff16 },
137
+ props: {
138
+ helperText: { type: String, default: undefined },
139
+ invalidMessage: { type: String, default: undefined },
140
+ label: String,
141
+ passwordHideLabel: { type: String, default: "Hide password" },
142
+ passwordShowLabel: { type: String, default: "Show password" },
143
+ passwordVisible: Boolean,
144
+ type: String,
145
+ value: String,
146
+ warnText: { type: String, default: undefined },
147
+ tooltipAlignment: {
148
+ type: String,
149
+ default: "center",
150
+ validator: (val) => ["start", "center", "end"].includes(val),
151
+ },
152
+ tooltipDirection: {
153
+ type: String,
154
+ default: "bottom",
155
+ validator: (val) => ["top", "left", "bottom", "right".includes(val)],
156
+ },
157
+ },
158
+ computed: {
159
+ hasTooltipSlot() {
160
+ return !!this.$slots.tooltip;
161
+ },
162
+ },
163
+ };
164
+ </script>
165
+
166
+ <style scoped lang="scss">
167
+ .tooltip {
168
+ display: inline-block;
169
+ position: absolute;
170
+ }
171
+ </style>
172
+
173
+ <style lang="scss">
174
+ // global styles
175
+
176
+ .ns-text-input .bx--tooltip__label .bx--tooltip__trigger {
177
+ margin-left: 0.25rem;
178
+ }
179
+ </style>