@appscode/design-system 1.1.0-alpha.2 → 1.1.0-alpha.4
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 +1 -1
- package/vue-components/v2/content/ContentTable.vue +14 -9
- package/vue-components/v2/table/Table.vue +5 -0
- package/vue-components/v3/content/ContentTable.vue +5 -0
- package/vue-components/v3/modals/LongRunningTasksModal.vue +52 -25
- package/vue-components/v3/notification/AlertBox.vue +61 -0
- package/vue-components/v3/table/Table.vue +5 -0
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
v-if="!hideHeader"
|
|
6
6
|
:header-title="tableTitle"
|
|
7
7
|
:header-sub-title="tableSubTitle"
|
|
8
|
+
:remove-border-bottom="removeBorderBottom"
|
|
8
9
|
:class="{ 'pl-0 pr-0': removeTableHeaderPadding }"
|
|
9
10
|
>
|
|
10
11
|
<header-item>
|
|
@@ -22,36 +23,40 @@ export default {
|
|
|
22
23
|
props: {
|
|
23
24
|
removeTableHeaderPadding: {
|
|
24
25
|
type: Boolean,
|
|
25
|
-
default: false
|
|
26
|
+
default: false,
|
|
26
27
|
},
|
|
27
28
|
tableTitle: {
|
|
28
29
|
type: String,
|
|
29
|
-
default: "Table"
|
|
30
|
+
default: "Table",
|
|
30
31
|
},
|
|
31
32
|
tableSubTitle: {
|
|
32
33
|
type: String,
|
|
33
|
-
default: ""
|
|
34
|
+
default: "",
|
|
34
35
|
},
|
|
35
36
|
searchable: {
|
|
36
37
|
type: Boolean,
|
|
37
|
-
default: true
|
|
38
|
+
default: true,
|
|
38
39
|
},
|
|
39
40
|
hideHeader: {
|
|
40
41
|
type: Boolean,
|
|
41
|
-
default: false
|
|
42
|
-
}
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
removeBorderBottom: {
|
|
45
|
+
type: Boolean,
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
43
48
|
},
|
|
44
49
|
components: {
|
|
45
50
|
ContentLayout: () => import("./ContentLayout.vue"),
|
|
46
51
|
ContentHeader: () => import("./ContentHeader.vue"),
|
|
47
52
|
HeaderItem: () => import("../header/HeaderItem.vue"),
|
|
48
|
-
SearchBar: () => import("../searchbars/SearchBar.vue")
|
|
53
|
+
SearchBar: () => import("../searchbars/SearchBar.vue"),
|
|
49
54
|
},
|
|
50
55
|
|
|
51
56
|
data() {
|
|
52
57
|
return {
|
|
53
|
-
searchText: ""
|
|
58
|
+
searchText: "",
|
|
54
59
|
};
|
|
55
|
-
}
|
|
60
|
+
},
|
|
56
61
|
};
|
|
57
62
|
</script>
|
|
@@ -110,6 +110,11 @@
|
|
|
110
110
|
</tbody>
|
|
111
111
|
</template>
|
|
112
112
|
</table>
|
|
113
|
+
|
|
114
|
+
<!-- table footer info start -->
|
|
115
|
+
<slot name="table-footer-info" />
|
|
116
|
+
<!-- table footer info end -->
|
|
117
|
+
|
|
113
118
|
<!-- pagination start -->
|
|
114
119
|
<slot name="table-pagination" />
|
|
115
120
|
<!-- pagination end -->
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
v-if="!hideHeader"
|
|
6
6
|
:header-title="tableTitle"
|
|
7
7
|
:header-sub-title="tableSubTitle"
|
|
8
|
+
:remove-border-bottom="removeBorderBottom"
|
|
8
9
|
:class="{ 'pl-0 pr-0': removeTableHeaderPadding }"
|
|
9
10
|
>
|
|
10
11
|
<template #title-right-actions>
|
|
@@ -50,6 +51,10 @@ export default defineComponent({
|
|
|
50
51
|
type: Boolean,
|
|
51
52
|
default: false,
|
|
52
53
|
},
|
|
54
|
+
removeBorderBottom: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
53
58
|
},
|
|
54
59
|
components: {
|
|
55
60
|
ContentLayout: defineAsyncComponent(() =>
|
|
@@ -110,7 +110,6 @@ import {
|
|
|
110
110
|
Ref,
|
|
111
111
|
toRefs,
|
|
112
112
|
watch,
|
|
113
|
-
watchEffect,
|
|
114
113
|
} from "vue";
|
|
115
114
|
import { defineAsyncComponent, ref } from "vue";
|
|
116
115
|
import { Task, TaskLog } from "../../../typings/long-running-tasks.ts";
|
|
@@ -173,7 +172,7 @@ const activeStepId: Ref<string> = ref("");
|
|
|
173
172
|
// to find active task faster
|
|
174
173
|
const idToStepIndex: Ref<Record<string, number>> = ref({});
|
|
175
174
|
const activeTask = computed(() => {
|
|
176
|
-
const task = tasks.value.
|
|
175
|
+
const task = tasks.value[idToStepIndex.value[activeStepId.value]];
|
|
177
176
|
return task;
|
|
178
177
|
});
|
|
179
178
|
|
|
@@ -207,6 +206,7 @@ function handleTaskLog(log: TaskLog) {
|
|
|
207
206
|
task.status = log.status;
|
|
208
207
|
if (log.status === "Failed") {
|
|
209
208
|
task.logs.push(log.error || "");
|
|
209
|
+
errorCtx.value?.onError(log.error);
|
|
210
210
|
} else {
|
|
211
211
|
task.logs.push(log.msg || "");
|
|
212
212
|
}
|
|
@@ -252,12 +252,36 @@ onBeforeUnmount(() => {
|
|
|
252
252
|
subscription && subscription.unsubscribe();
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
const longRunningTaskStatus = computed(() => {
|
|
256
|
+
let successTaskCount = 0;
|
|
257
|
+
let failedTaskCount = 0;
|
|
258
|
+
|
|
259
|
+
// get count of success and failed task
|
|
260
|
+
tasks.value.forEach((task) => {
|
|
261
|
+
if (task?.status === "Success") successTaskCount++;
|
|
262
|
+
else if (task?.status === "Failed") failedTaskCount++;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (tasks.value.length === 0) return "NotStarted";
|
|
266
|
+
// if all the task has been successful
|
|
267
|
+
else if (successTaskCount === tasks.value.length) {
|
|
268
|
+
return "Success";
|
|
269
|
+
}
|
|
270
|
+
// if all the task has been completed and some tasks are failed
|
|
271
|
+
else if (
|
|
272
|
+
failedTaskCount &&
|
|
273
|
+
successTaskCount + failedTaskCount === tasks.value.length
|
|
274
|
+
) {
|
|
275
|
+
return "Failed";
|
|
276
|
+
} else return "Pending";
|
|
277
|
+
});
|
|
278
|
+
|
|
255
279
|
// modal close / footer feature
|
|
256
280
|
const enableModalClose = computed(() => {
|
|
257
281
|
return (
|
|
258
282
|
connectionError.value ||
|
|
259
|
-
|
|
260
|
-
|
|
283
|
+
longRunningTaskStatus.value === "Failed" ||
|
|
284
|
+
longRunningTaskStatus.value === "Success"
|
|
261
285
|
);
|
|
262
286
|
});
|
|
263
287
|
const enableModalFooter = computed(() => {
|
|
@@ -265,47 +289,50 @@ const enableModalFooter = computed(() => {
|
|
|
265
289
|
});
|
|
266
290
|
|
|
267
291
|
// generate report issue title with error step title
|
|
268
|
-
const
|
|
292
|
+
const getReportIssueInfo = (): { title: string; body: string } => {
|
|
269
293
|
const stepTitlesFromErrorTasks: Array<string> = [];
|
|
294
|
+
const stepLogsFromErrorTasks: Array<string> = [];
|
|
270
295
|
tasks.value.forEach((task) => {
|
|
271
296
|
// if this taskLog is error task, push it to array
|
|
272
297
|
if (task.error) {
|
|
273
|
-
stepTitlesFromErrorTasks.push(task
|
|
298
|
+
stepTitlesFromErrorTasks.push(task?.step);
|
|
299
|
+
stepLogsFromErrorTasks.push(task?.logs[task?.logs?.length - 1] || "");
|
|
274
300
|
}
|
|
275
301
|
});
|
|
276
|
-
// return final
|
|
277
|
-
return
|
|
302
|
+
// return final object
|
|
303
|
+
return {
|
|
304
|
+
title: stepTitlesFromErrorTasks.join(", "),
|
|
305
|
+
body: stepLogsFromErrorTasks.join(", "),
|
|
306
|
+
};
|
|
278
307
|
};
|
|
279
308
|
|
|
280
309
|
// report button
|
|
281
|
-
const showReportButton = computed(
|
|
310
|
+
const showReportButton = computed(
|
|
311
|
+
() => longRunningTaskStatus.value === "Failed"
|
|
312
|
+
);
|
|
282
313
|
function onReportIssueClick() {
|
|
283
|
-
const url = `https://github.com/bytebuilders/community/issues/new?title=Chart Install: ${
|
|
284
|
-
|
|
285
|
-
} %0A%0A %60%60%60 %0A ${
|
|
286
|
-
|
|
314
|
+
const url = `https://github.com/bytebuilders/community/issues/new?title=Chart Install: ${
|
|
315
|
+
getReportIssueInfo().title
|
|
316
|
+
}&labels[]=bug&body=${window.location.href} %0A%0A %60%60%60 %0A ${
|
|
317
|
+
getReportIssueInfo().body
|
|
287
318
|
} %0A %60%60%60`;
|
|
288
319
|
window.open(url, "_blank");
|
|
289
320
|
}
|
|
290
321
|
|
|
291
322
|
// success button
|
|
292
323
|
const showSuccessButton = computed(
|
|
293
|
-
() =>
|
|
324
|
+
() =>
|
|
325
|
+
longRunningTaskStatus.value === "Success" && !!successCtx.value?.btnTitle
|
|
294
326
|
);
|
|
295
327
|
|
|
296
328
|
// execute on success and on error functions
|
|
297
|
-
watch(
|
|
298
|
-
()
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} else if (n === "Failed") {
|
|
303
|
-
errorCtx.value.onError(
|
|
304
|
-
activeTask.value?.logs[activeTask.value?.logs.length - 1 || 0] || ""
|
|
305
|
-
);
|
|
306
|
-
}
|
|
329
|
+
watch(longRunningTaskStatus, (n) => {
|
|
330
|
+
if (n === "Success") {
|
|
331
|
+
successCtx.value.onSuccess();
|
|
332
|
+
} else if (n === "Failed") {
|
|
333
|
+
errorCtx.value?.onError("Operation Failed");
|
|
307
334
|
}
|
|
308
|
-
);
|
|
335
|
+
});
|
|
309
336
|
</script>
|
|
310
337
|
|
|
311
338
|
<style scoped lang="scss">
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- alert-message area start -->
|
|
3
|
+
<!-- plsease, use this class name ('.is-info' 'is-success', 'is-error', 'is-warning') -->
|
|
4
|
+
<div :class="`ac-notification is-${notificationType} mb-15`">
|
|
5
|
+
<p>
|
|
6
|
+
<i v-if="!hideIcon" :class="`fa ${iconClass} mr-5`"></i
|
|
7
|
+
><span v-html="getSanitizedHtml(content)"></span>
|
|
8
|
+
<ac-button
|
|
9
|
+
v-if="actionButton?.show"
|
|
10
|
+
:title="actionButton?.title"
|
|
11
|
+
:icon-class="actionButton?.iconClass"
|
|
12
|
+
data-testid="notification-action-button"
|
|
13
|
+
@click.prevent="actionButton?.action()"
|
|
14
|
+
>
|
|
15
|
+
</ac-button>
|
|
16
|
+
</p>
|
|
17
|
+
</div>
|
|
18
|
+
<!-- alert-message area end -->
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import { toRefs, computed, defineAsyncComponent } from "vue";
|
|
23
|
+
import DOMPurify from "dompurify";
|
|
24
|
+
|
|
25
|
+
const AcButton = defineAsyncComponent(
|
|
26
|
+
() => import("@appscode/design-system/vue-components/v3/button/Button.vue")
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const props = withDefaults(
|
|
30
|
+
defineProps<{
|
|
31
|
+
notificationType: string;
|
|
32
|
+
content: string;
|
|
33
|
+
hideIcon: boolean;
|
|
34
|
+
actionButton?: {
|
|
35
|
+
show: boolean;
|
|
36
|
+
title: string;
|
|
37
|
+
iconClass: string;
|
|
38
|
+
action: (...args: any) => void | undefined;
|
|
39
|
+
};
|
|
40
|
+
}>(),
|
|
41
|
+
{
|
|
42
|
+
notificationType: "",
|
|
43
|
+
content: "",
|
|
44
|
+
hideIcon: false,
|
|
45
|
+
actionButton: undefined,
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const { notificationType, content, hideIcon, actionButton } = toRefs(props);
|
|
50
|
+
|
|
51
|
+
const iconClass = computed(() => {
|
|
52
|
+
if (notificationType.value === "success") return "fa-check-circle";
|
|
53
|
+
else if (notificationType.value === "info") return "fa-info-circle";
|
|
54
|
+
else if (notificationType.value === "error") return "fa-times-circle";
|
|
55
|
+
else return "fa-info-circle";
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const getSanitizedHtml = (content: string) => {
|
|
59
|
+
return DOMPurify.sanitize(content || "");
|
|
60
|
+
};
|
|
61
|
+
</script>
|
|
@@ -109,6 +109,11 @@
|
|
|
109
109
|
</tbody>
|
|
110
110
|
</template>
|
|
111
111
|
</table>
|
|
112
|
+
|
|
113
|
+
<!-- table footer info start -->
|
|
114
|
+
<slot name="table-footer-info" />
|
|
115
|
+
<!-- table footer info end -->
|
|
116
|
+
|
|
112
117
|
<!-- pagination start -->
|
|
113
118
|
<slot name="table-pagination" />
|
|
114
119
|
<!-- pagination end -->
|