@appscode/design-system 0.0.21-alpha.2 → 0.4.27

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 (165) hide show
  1. package/base/utilities/_all.scss +7 -0
  2. package/base/utilities/_customize-bulma.scss +191 -0
  3. package/base/utilities/_default.scss +319 -110
  4. package/base/utilities/_derived-variables.scss +8 -15
  5. package/base/utilities/_grid.scss +29 -0
  6. package/base/utilities/_initial-variables.scss +111 -72
  7. package/base/utilities/_mixin.scss +90 -10
  8. package/base/utilities/_typography.scss +29 -19
  9. package/base/utilities/dark-theme.scss +26 -0
  10. package/components/_ac-accordion.scss +15 -5
  11. package/components/_ac-alert-box.scss +109 -26
  12. package/components/_ac-card.scss +71 -24
  13. package/components/_ac-code-highlight.scss +7 -1
  14. package/components/_ac-content-layout.scss +5 -5
  15. package/components/_ac-drag.scss +8 -6
  16. package/components/_ac-input.scss +196 -38
  17. package/components/_ac-modal.scss +6 -5
  18. package/components/_ac-multi-select.scss +281 -23
  19. package/components/_ac-options.scss +31 -16
  20. package/components/_ac-report.scss +53 -0
  21. package/components/_ac-select-box.scss +15 -5
  22. package/components/_ac-table.scss +160 -39
  23. package/components/_ac-tabs.scss +86 -23
  24. package/components/_ac-tags.scss +87 -2
  25. package/components/_ac-terminal.scss +270 -0
  26. package/components/_all.scss +28 -0
  27. package/components/_app-drawer.scss +134 -0
  28. package/components/_breadcumb.scss +8 -3
  29. package/components/_buttons.scss +96 -62
  30. package/components/_card-body-wrapper.scss +3 -3
  31. package/components/_dashboard-header.scss +33 -1
  32. package/components/_direct-deploy.scss +69 -0
  33. package/components/_go-to-top.scss +1 -1
  34. package/components/_graph.scss +45 -0
  35. package/components/_image-upload.scss +6 -4
  36. package/components/_left-sidebar-menu.scss +212 -52
  37. package/components/_monaco-editor.scss +1 -1
  38. package/components/_navbar.scss +193 -31
  39. package/components/_overview-info.scss +4 -4
  40. package/components/_overview-page.scss +1 -2
  41. package/components/_pagination.scss +45 -7
  42. package/components/_payment-card.scss +28 -12
  43. package/components/_preloader.scss +1 -1
  44. package/components/_preview-modal.scss +22 -9
  45. package/components/_pricing-table.scss +1 -1
  46. package/components/_progress-bar.scss +5 -5
  47. package/components/_subscription-card.scss +15 -8
  48. package/components/_table-of-content.scss +1 -1
  49. package/components/_tfa.scss +69 -0
  50. package/components/_transitions.scss +261 -0
  51. package/components/_widget-menu.scss +9 -9
  52. package/components/_wizard.scss +33 -20
  53. package/components/ac-toaster/_ac-toasted.scss +40 -8
  54. package/components/bbum/_all.scss +9 -0
  55. package/components/bbum/_card-team.scss +18 -10
  56. package/components/bbum/_information-center.scss +19 -5
  57. package/components/bbum/_mobile-desktop.scss +6 -6
  58. package/components/bbum/_post.scss +5 -4
  59. package/components/bbum/_sign-up-notification.scss +6 -6
  60. package/components/bbum/_single-post-preview.scss +10 -10
  61. package/components/bbum/_user-profile.scss +97 -90
  62. package/components/ui-builder/_ui-builder.scss +98 -21
  63. package/components/ui-builder/_vue-open-api.scss +104 -0
  64. package/layouts/_404.scss +159 -0
  65. package/layouts/_all.scss +2 -0
  66. package/layouts/_code-preview.scss +19 -8
  67. package/main.scss +6 -53
  68. package/package.json +4 -7
  69. package/plugins/caching.ts +243 -0
  70. package/plugins/theme.js +142 -0
  71. package/plugins/time-convert.js +49 -0
  72. package/plugins/vue-toaster.js +10 -6
  73. package/vue-components/v2/banner/Banner.vue +2 -2
  74. package/vue-components/v2/breadcrumbs/Breadcrumb.vue +97 -0
  75. package/vue-components/v2/button/Button.vue +10 -1
  76. package/vue-components/v2/button/DownloadBtn.vue +45 -0
  77. package/vue-components/v2/card/Card.vue +1 -0
  78. package/vue-components/v2/card/CardContent.vue +5 -0
  79. package/vue-components/v2/card/CardHeader.vue +12 -0
  80. package/vue-components/v2/card/OverviewCard.vue +10 -0
  81. package/vue-components/v2/card/OverviewCards.vue +5 -0
  82. package/vue-components/v2/card/PaymentCard.vue +69 -0
  83. package/vue-components/v2/card/PaymentCardOptionButtons.vue +35 -0
  84. package/vue-components/v2/card/PaymentCards.vue +44 -0
  85. package/vue-components/v2/content/ContentHeader.vue +9 -5
  86. package/vue-components/v2/content/ContentTable.vue +12 -7
  87. package/vue-components/v2/editor/Editor.vue +38 -5
  88. package/vue-components/v2/editor/FilteredFileEditor.vue +189 -0
  89. package/vue-components/v2/editor/MonacoEditor.vue +125 -0
  90. package/vue-components/v2/editor/ResourceKeyValueEditor.vue +209 -0
  91. package/vue-components/v2/form/Form.vue +12 -7
  92. package/vue-components/v2/form-fields/Input.vue +1 -1
  93. package/vue-components/v2/header/Header.vue +0 -1
  94. package/vue-components/v2/loaders/ResourceLoader.vue +101 -0
  95. package/vue-components/v2/loaders/SidebarLoader.vue +43 -0
  96. package/vue-components/v2/modal/Modal.vue +40 -7
  97. package/vue-components/v2/modals/DeleteConfirmationModal.vue +79 -0
  98. package/vue-components/v2/modals/JsonShowModal.vue +12 -3
  99. package/vue-components/v2/navbar/Appdrawer.vue +10 -9
  100. package/vue-components/v2/navbar/ThemeMode.vue +120 -0
  101. package/vue-components/v2/navbar/User.vue +229 -17
  102. package/vue-components/v2/notification/Notification.vue +101 -0
  103. package/vue-components/v2/notification/NotificationItem.vue +44 -0
  104. package/vue-components/v2/pagination/Pagination.vue +24 -4
  105. package/vue-components/v2/preloader/Preloader.vue +5 -5
  106. package/vue-components/v2/sidebar/ClusterSwitcher.vue +126 -0
  107. package/vue-components/v2/sidebar/SidebarItem.vue +24 -2
  108. package/vue-components/v2/sidebar/SidebarItemWithDropDown.vue +19 -20
  109. package/vue-components/v2/tab/TabItem.vue +1 -1
  110. package/vue-components/v2/table/FakeTableCell.vue +36 -0
  111. package/vue-components/v2/table/InfoTable.vue +13 -3
  112. package/vue-components/v2/table/NarrowTable.vue +0 -2
  113. package/vue-components/v2/table/Table.vue +170 -10
  114. package/vue-components/v2/table/TableRow.vue +29 -2
  115. package/vue-components/v2/table/table-cell/CellValue.vue +33 -4
  116. package/vue-components/v2/table/table-cell/GenericCell.vue +56 -0
  117. package/vue-components/v2/table/table-cell/ObjectCell.vue +4 -1
  118. package/vue-components/v2/tabs/EditorTabs.vue +1 -1
  119. package/vue-components/v3/button/Button.vue +78 -0
  120. package/vue-components/v3/content/ContentHeader.vue +55 -0
  121. package/vue-components/v3/content/ContentTable.vue +83 -0
  122. package/vue-components/v3/dropdown/DropdownDivider.vue +3 -0
  123. package/vue-components/v3/dropdown/DropdownItem.vue +5 -0
  124. package/vue-components/v3/dropdown/DropdownMenu.vue +111 -0
  125. package/vue-components/v3/editor/Editor.vue +160 -0
  126. package/vue-components/v3/editor/FilteredFileEditor.vue +186 -0
  127. package/vue-components/v3/editor/MonacoEditor.vue +131 -0
  128. package/vue-components/v3/editor/ResourceKeyValueEditor.vue +125 -0
  129. package/vue-components/v3/form/Form.vue +63 -0
  130. package/vue-components/v3/form-fields/Input.vue +22 -0
  131. package/vue-components/v3/header/Header.vue +45 -0
  132. package/vue-components/v3/header/HeaderItem.vue +5 -0
  133. package/vue-components/v3/header/HeaderItems.vue +5 -0
  134. package/vue-components/v3/loaders/ResourceLoader.vue +83 -0
  135. package/vue-components/v3/loaders/SidebarLoader.vue +34 -0
  136. package/vue-components/v3/long-running-tasks/LongRunningTaskItem.vue +92 -0
  137. package/vue-components/v3/modal/Modal.vue +158 -0
  138. package/vue-components/v3/modals/DeleteConfirmationModal.vue +85 -0
  139. package/vue-components/v3/modals/JsonShowModal.vue +96 -0
  140. package/vue-components/v3/modals/LongRunningTasksModal.vue +373 -0
  141. package/vue-components/v3/navbar/Appdrawer.vue +63 -0
  142. package/vue-components/v3/navbar/ThemeMode.vue +120 -0
  143. package/vue-components/v3/navbar/User.vue +288 -0
  144. package/vue-components/v3/notification/Notification.vue +98 -0
  145. package/vue-components/v3/notification/NotificationItem.vue +52 -0
  146. package/vue-components/v3/pagination/Pagination.vue +172 -0
  147. package/vue-components/v3/searchbars/SearchBar.vue +47 -0
  148. package/vue-components/v3/sidebar/ClusterSwitcher.vue +133 -0
  149. package/vue-components/v3/sidebar/SidebarItemWithDropDown.vue +120 -0
  150. package/vue-components/v3/tab/TabItem.vue +17 -0
  151. package/vue-components/v3/table/FakeTableCell.vue +39 -0
  152. package/vue-components/v3/table/InfoTable.vue +105 -0
  153. package/vue-components/v3/table/MultiInfoTable.vue +143 -0
  154. package/vue-components/v3/table/Table.vue +272 -0
  155. package/vue-components/v3/table/TableCell.vue +28 -0
  156. package/vue-components/v3/table/TableContainer.vue +34 -0
  157. package/vue-components/v3/table/TableRow.vue +147 -0
  158. package/vue-components/v3/table/table-cell/ArrayCell.vue +111 -0
  159. package/vue-components/v3/table/table-cell/CellValue.vue +133 -0
  160. package/vue-components/v3/table/table-cell/GenericCell.vue +75 -0
  161. package/vue-components/v3/table/table-cell/ObjectCell.vue +110 -0
  162. package/vue-components/v3/table/table-cell/ValueWithModal.vue +43 -0
  163. package/vue-components/v3/tabs/EditorTabs.vue +36 -0
  164. package/vue-components/v3/tag/Tag.vue +17 -0
  165. package/vue-components/v3/terminal/LongRunningTaskTerminal.vue +148 -0
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <teleport to="#modals">
3
+ <!-- for transition https://github.com/adamwathan/vue-tailwind-examples/blob/master/src/components/Modal.vue -->
4
+ <!-- modal start -->
5
+ <div
6
+ v-if="showModal"
7
+ class="ac-modal is-middle-alignment"
8
+ :class="modifierClasses"
9
+ @click.self="onModalOutsideClick"
10
+ >
11
+ <div class="ac-modal-inner">
12
+ <!-- modal header start -->
13
+ <div class="ac-modal-header">
14
+ <h6>{{ title }}</h6>
15
+ <header-items>
16
+ <slot name="modal-header-controls" />
17
+ <header-item>
18
+ <ac-button
19
+ modifier-classes="is-square is-transparent"
20
+ :disabled="isCloseOptionDisabled"
21
+ :icon-image="crossIcon"
22
+ @click.stop="destroyModal"
23
+ data-testid="modal-generic-close-icon"
24
+ />
25
+ </header-item>
26
+ </header-items>
27
+ </div>
28
+ <!-- modal header end -->
29
+
30
+ <!-- modal body start -->
31
+ <div
32
+ class="ac-modal-body ac-vscrollbar"
33
+ data-testid="ac-modal-content-with-scroll"
34
+ >
35
+ <div class="ac-modal-content">
36
+ <!-- freedom content start -->
37
+ <slot />
38
+ <!-- freedom content end -->
39
+ </div>
40
+ </div>
41
+ <!-- modal body end -->
42
+
43
+ <!-- modal footer start -->
44
+ <div
45
+ v-if="!hideActionFooter"
46
+ class="ac-modal-footer action-footer is-flex is-align-items-center is-justify-content-space-between"
47
+ >
48
+ <div>
49
+ <slot name="modal-footer-left" />
50
+ </div>
51
+ <buttons class="has-text-right is-block">
52
+ <slot name="modal-footer-controls" />
53
+ </buttons>
54
+ <!-- modal footer end -->
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </teleport>
59
+ </template>
60
+
61
+ <script>
62
+ import { defineComponent, defineAsyncComponent } from "vue";
63
+
64
+ const modalCloseIcon = import.meta.glob(
65
+ "/src/assets/icons/modal/close-icon.svg",
66
+ { eager: true }
67
+ );
68
+
69
+ export default defineComponent({
70
+ props: {
71
+ open: {
72
+ type: Boolean,
73
+ default: false,
74
+ },
75
+ title: {
76
+ type: String,
77
+ default: "Modal",
78
+ },
79
+ modifierClasses: {
80
+ type: String,
81
+ default: "",
82
+ },
83
+ isCloseOptionDisabled: {
84
+ type: Boolean,
85
+ default: false,
86
+ },
87
+ ignoreOutsideClick: {
88
+ type: Boolean,
89
+ default: false,
90
+ },
91
+ hideActionFooter: {
92
+ type: Boolean,
93
+ default: false,
94
+ },
95
+ },
96
+ emits: ["closemodal"],
97
+
98
+ components: {
99
+ HeaderItems: defineAsyncComponent(() =>
100
+ import("../../v2/header/HeaderItems.vue").then((module) => module.default)
101
+ ),
102
+ HeaderItem: defineAsyncComponent(() =>
103
+ import("../../v2/header/HeaderItem.vue").then((module) => module.default)
104
+ ),
105
+ Buttons: defineAsyncComponent(() =>
106
+ import("../../v2/button/Buttons.vue").then((module) => module.default)
107
+ ),
108
+ AcButton: defineAsyncComponent(() =>
109
+ import("../button/Button.vue").then((module) => module.default)
110
+ ),
111
+ },
112
+
113
+ data() {
114
+ return {
115
+ showModal: false,
116
+ crossIcon:
117
+ modalCloseIcon["/src/assets/icons/modal/close-icon.svg"].default,
118
+ };
119
+ },
120
+
121
+ watch: {
122
+ open: {
123
+ immediate: true,
124
+ handler(n) {
125
+ if (n) {
126
+ this.initializeModal();
127
+ } else {
128
+ this.destroyModal();
129
+ }
130
+ },
131
+ },
132
+ },
133
+
134
+ methods: {
135
+ onKeyDown(e) {
136
+ if (this.open && e.keyCode === 27) {
137
+ // escape key
138
+ this.destroyModal();
139
+ }
140
+ },
141
+ initializeModal() {
142
+ this.showModal = true;
143
+ document.addEventListener("keydown", this.onKeyDown);
144
+ },
145
+ onModalOutsideClick() {
146
+ if (this.ignoreOutsideClick) return;
147
+ this.destroyModal();
148
+ },
149
+ destroyModal() {
150
+ if (this.isCloseOptionDisabled) return;
151
+ this.showModal = false;
152
+ document.removeEventListener("keydown", this.onKeyDown);
153
+
154
+ this.$emit("closemodal", true);
155
+ },
156
+ },
157
+ });
158
+ </script>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <!-- modal start -->
3
+ <modal
4
+ :title="title"
5
+ modifier-classes="is-normal"
6
+ :open="open"
7
+ @closemodal="closeModal"
8
+ >
9
+ <!-- freedom content start -->
10
+ <div class="action-message pt-35 pb-35 has-text-centered">
11
+ <h5 class="is-message">{{ message }} {{ itemName ? "" : "?" }}</h5>
12
+ <p class="is-description">{{ itemName }} {{ itemName ? "?" : "" }}</p>
13
+ </div>
14
+
15
+ <!-- freedom content end -->
16
+
17
+ <!-- modal footer start -->
18
+ <template #modal-footer-controls>
19
+ <ac-button
20
+ @click.stop="closeModal"
21
+ title="Cancel"
22
+ modifier-classes="is-outlined"
23
+ data-testid="delete-confirmation-modal-close-button"
24
+ />
25
+ <ac-button
26
+ modifier-classes="is-danger"
27
+ :is-loader-active="isDeleteActive"
28
+ title="Yes"
29
+ @click.stop="confirm(true)"
30
+ data-testid="delete-confirmation-modal-confirm-button"
31
+ />
32
+ </template>
33
+ </modal>
34
+ <!-- modal end -->
35
+ </template>
36
+
37
+ <script>
38
+ import { defineComponent, defineAsyncComponent } from "vue";
39
+ export default defineComponent({
40
+ components: {
41
+ Modal: defineAsyncComponent(() =>
42
+ import("../modal/Modal.vue").then((module) => module.default)
43
+ ),
44
+ AcButton: defineAsyncComponent(() =>
45
+ import("../button/Button.vue").then((module) => module.default)
46
+ ),
47
+ },
48
+ props: {
49
+ open: {
50
+ type: Boolean,
51
+ default: false,
52
+ },
53
+ title: {
54
+ type: String,
55
+ default: "",
56
+ },
57
+ message: {
58
+ type: String,
59
+ default: "",
60
+ },
61
+ itemName: {
62
+ type: String,
63
+ default: "",
64
+ },
65
+ isLoading: {
66
+ type: Boolean,
67
+ default: false,
68
+ },
69
+ isDeleteActive: {
70
+ type: Boolean,
71
+ default: false,
72
+ },
73
+ },
74
+ emits: ["closemodal", "delete-confirmation-modal$confirm"],
75
+ methods: {
76
+ confirm(response) {
77
+ this.$emit("delete-confirmation-modal$confirm", response);
78
+ },
79
+ closeModal() {
80
+ this.confirm(false);
81
+ this.$emit("closemodal", true);
82
+ },
83
+ },
84
+ });
85
+ </script>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <modal
3
+ :title="editorTitle"
4
+ :open="open"
5
+ @closemodal="closeModal"
6
+ modifier-classes="is-medium"
7
+ >
8
+ <template #modal-header-controls>
9
+ <header-item>
10
+ <ac-button
11
+ modifier-classes="is-square is-primary"
12
+ icon-class="copy"
13
+ v-clipboard:copy="
14
+ `${editorTitle}: ${JSON.stringify(parsedContent, null, 4)}`
15
+ "
16
+ v-clipboard:success="onCopy"
17
+ v-clipboard:error="onError"
18
+ />
19
+ </header-item>
20
+ </template>
21
+ <editor
22
+ :model-value="JSON.stringify(parsedContent, null, 4)"
23
+ :read-only="true"
24
+ language="json"
25
+ :show-minimap="false"
26
+ />
27
+ </modal>
28
+ </template>
29
+
30
+ <script>
31
+ import { defineComponent, defineAsyncComponent } from "vue";
32
+ import { useToast } from "vue-toastification";
33
+ import Preloader from "../../v2/preloader/Preloader.vue";
34
+ import Banner from "../../v2/banner/Banner.vue";
35
+
36
+ export default defineComponent({
37
+ props: {
38
+ open: {
39
+ type: Boolean,
40
+ default: false
41
+ },
42
+ editorTitle: {
43
+ type: String,
44
+ default: ""
45
+ },
46
+ editorContent: {
47
+ type: null,
48
+ default: () => ({})
49
+ }
50
+ },
51
+ emits: ["closemodal"],
52
+ components: {
53
+ Modal: defineAsyncComponent(() =>
54
+ import("../modal/Modal.vue").then(module => module.default)
55
+ ),
56
+ Editor: defineAsyncComponent({
57
+ loader: () =>
58
+ import("../editor/Editor.vue").then(module => module.default),
59
+ loadingComponent: Preloader,
60
+ delay: 200,
61
+ errorComponent: Banner,
62
+ timeout: 100000,
63
+ }),
64
+ AcButton: defineAsyncComponent(() =>
65
+ import("../button/Button.vue").then(module => module.default)
66
+ ),
67
+ HeaderItem: defineAsyncComponent(() =>
68
+ import("../../v2/header/HeaderItem.vue").then(module => module.default)
69
+ )
70
+ },
71
+ computed: {
72
+ parsedContent() {
73
+ try {
74
+ return JSON.parse(this.editorContent);
75
+ } catch (e) {
76
+ return this.editorContent;
77
+ }
78
+ }
79
+ },
80
+ setup() {
81
+ const toast = useToast();
82
+ return { toast };
83
+ },
84
+ methods: {
85
+ onCopy() {
86
+ this.toast.success("Value copied successfully", { timeout: 1000 });
87
+ },
88
+ onError() {
89
+ this.toast.error("Copying failed", { timeout: 1000 });
90
+ },
91
+ closeModal() {
92
+ this.$emit("closemodal", true);
93
+ }
94
+ }
95
+ });
96
+ </script>
@@ -0,0 +1,373 @@
1
+ <template>
2
+ <modal
3
+ :open="open"
4
+ :title="title"
5
+ :is-close-option-disabled="!enableModalClose"
6
+ :ignore-outside-click="true"
7
+ :hide-action-footer="!enableModalFooter"
8
+ @closemodal="$emit('close')"
9
+ >
10
+ <div v-if="connectionError" class="task-simple-wrapper">
11
+ <div class="task-cogs-icon">
12
+ <i class="fa fa-times-circle has-text-danger fa-5x fa-fw"></i>
13
+ </div>
14
+ <div class="task-log">
15
+ <span class="task-title">
16
+ <i class="fa fa-times-circle mr-5 is-failed" />
17
+ <span> Connection error </span>
18
+ </span>
19
+ <span>{{ connectionError }}</span>
20
+ </div>
21
+ </div>
22
+ <div
23
+ v-else-if="isNatsConnectionLoading || !activeStepId"
24
+ class="is-justify-content-center"
25
+ :class="simple ? 'task-simple-wrapper' : 'task-complex-wrapper'"
26
+ >
27
+ <preloader
28
+ :style="{ height: '100%' }"
29
+ class="is-fullheight"
30
+ message="Connecting..."
31
+ />
32
+ </div>
33
+ <div v-else-if="simple" class="task-simple-wrapper">
34
+ <div class="task-cogs-icon">
35
+ <i class="fa fa-cog fa-spin fa-5x fa-fw"></i>
36
+ <span class="is-flex is-flex-direction-column">
37
+ <i class="fa fa-cog fa-spin fa-3x fa-bw"></i>
38
+ <i class="fa fa-cog fa-spin fa-3x fa-bw"></i>
39
+ </span>
40
+ </div>
41
+ <div class="task-log">
42
+ <span class="task-title">
43
+ <i
44
+ v-if="activeTask?.status === 'Running'"
45
+ class="fa fa-circle-o-notch fa-spin mr-5"
46
+ />
47
+ <i
48
+ v-else-if="activeTask?.status === 'Success'"
49
+ class="fa fa-check-circle mr-5 is-success"
50
+ />
51
+ <i
52
+ v-else-if="activeTask?.status === 'Failed'"
53
+ class="fa fa-times-circle mr-5 is-failed"
54
+ />
55
+ <span>
56
+ {{ activeTask?.step }}
57
+ </span>
58
+ </span>
59
+ <span>{{ activeTask?.logs[activeTask?.logs.length - 1] }}</span>
60
+ </div>
61
+ </div>
62
+ <div v-else class="task-complex-wrapper">
63
+ <ul class="task-list">
64
+ <li v-for="task in tasks" :key="task.step">
65
+ <long-running-task-item
66
+ :title="task.step"
67
+ :status="task.status"
68
+ :class="{ 'is-active': activeStepId === task.id }"
69
+ @click="activeStepId = task.id"
70
+ />
71
+ </li>
72
+ </ul>
73
+ <long-running-task-terminal
74
+ :key="activeTask?.id"
75
+ :theme="theme"
76
+ :logs="activeTask?.logs"
77
+ class="task-log"
78
+ />
79
+ </div>
80
+
81
+ <template #modal-footer-controls>
82
+ <ac-button
83
+ title="Close"
84
+ modifier-classes="is-outlined"
85
+ @click.stop="$emit('close')"
86
+ />
87
+ <ac-button
88
+ v-if="showSuccessButton"
89
+ :title="successCtx?.btnTitle"
90
+ :is-loader-active="successCtx?.isLoaderActive"
91
+ modifier-classes="is-primary"
92
+ icon-class="step-forward"
93
+ @click="successCtx.onSuccessBtnClick"
94
+ />
95
+ <ac-button
96
+ v-if="showReportButton"
97
+ title="Report Issue"
98
+ modifier-classes="is-danger"
99
+ icon-class="external-link"
100
+ @click="onReportIssueClick"
101
+ />
102
+ </template>
103
+ </modal>
104
+ </template>
105
+ <script setup lang="ts">
106
+ import {
107
+ computed,
108
+ getCurrentInstance,
109
+ onBeforeUnmount,
110
+ Ref,
111
+ toRefs,
112
+ watch,
113
+ watchEffect,
114
+ } from "vue";
115
+ import { defineAsyncComponent, ref } from "vue";
116
+ import { Task, TaskLog } from "../../../typings/long-running-tasks.ts";
117
+ import { Subscription, StringCodec } from "nats.ws";
118
+
119
+ const Modal = defineAsyncComponent(() => import("../modal/Modal.vue"));
120
+ const LongRunningTaskItem = defineAsyncComponent(
121
+ () => import("../long-running-tasks/LongRunningTaskItem.vue")
122
+ );
123
+ const LongRunningTaskTerminal = defineAsyncComponent(
124
+ () => import("../terminal/LongRunningTaskTerminal.vue")
125
+ );
126
+ const Preloader = defineAsyncComponent(
127
+ () => import("../../v2/preloader/Preloader.vue")
128
+ );
129
+ const AcButton = defineAsyncComponent(() => import("../button/Button.vue"));
130
+
131
+ defineEmits(["close"]);
132
+
133
+ const props = withDefaults(
134
+ defineProps<{
135
+ open: boolean;
136
+ theme: string;
137
+ title: string;
138
+ simple: boolean;
139
+ natsSubject: string;
140
+ isNatsConnectionLoading: boolean;
141
+ errorCtx?: {
142
+ connectionError: string;
143
+ onError: (msg: string) => void;
144
+ };
145
+ successCtx?: {
146
+ btnTitle?: string;
147
+ isLoaderActive?: boolean;
148
+ onSuccess: () => void;
149
+ onSuccessBtnClick?: () => void;
150
+ };
151
+ }>(),
152
+ {
153
+ open: true,
154
+ theme: "light",
155
+ simple: true,
156
+ title: "Sample title",
157
+ natsSubject: "",
158
+ isNatsConnectionLoading: false,
159
+ errorCtx: undefined,
160
+ successCtx: undefined,
161
+ }
162
+ );
163
+
164
+ const { natsSubject, open, errorCtx, successCtx } = toRefs(props);
165
+ const connectionError = computed(() => errorCtx.value?.connectionError);
166
+ const currentInstance = getCurrentInstance();
167
+ const $nats = currentInstance?.appContext.config.globalProperties.$nc;
168
+ let subscription: Subscription;
169
+
170
+ const tasks: Ref<Array<Task>> = ref([]);
171
+ const activeStepId: Ref<string> = ref("");
172
+ // to maintain stepId to stepIndex map
173
+ // to find active task faster
174
+ const idToStepIndex: Ref<Record<string, number>> = ref({});
175
+ const activeTask = computed(() => {
176
+ const task = tasks.value.find((task) => task.id === activeStepId.value);
177
+ return task;
178
+ });
179
+
180
+ function handleTaskLog(log: TaskLog) {
181
+ if (log.step) {
182
+ // log has a step
183
+ // so add new task
184
+ tasks.value.push({
185
+ ...log,
186
+ logs: [(log.msg && log.msg) || ""],
187
+ });
188
+
189
+ // recent pushed task index
190
+ const latestStepIndex = tasks.value.length - 1;
191
+
192
+ // map taskid to stepIndex
193
+ idToStepIndex.value[log.id] = latestStepIndex;
194
+
195
+ // update active step index for first task
196
+ // or if current active step is in latest step
197
+ if (
198
+ latestStepIndex === 0 ||
199
+ latestStepIndex === idToStepIndex.value[activeStepId.value] + 1
200
+ ) {
201
+ activeStepId.value = log.id;
202
+ }
203
+ } else {
204
+ // get active task
205
+ const task = tasks.value[idToStepIndex.value[log.id]];
206
+ if (task) {
207
+ task.status = log.status;
208
+ if (log.status === "Failed") {
209
+ task.logs.push(log.error || "");
210
+ } else {
211
+ task.logs.push(log.msg || "");
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ async function subscribeToChannel(channelId: string) {
218
+ subscription = $nats?.subscribe(channelId);
219
+
220
+ console.log("Started listening", channelId);
221
+
222
+ if (subscription) {
223
+ // listen to channel events
224
+ for await (const msg of subscription) {
225
+ console.log("Long Running Tasks Modal=>");
226
+ console.log({ data: StringCodec().decode(msg.data) });
227
+ const log: TaskLog = JSON.parse(StringCodec().decode(msg.data));
228
+ console.log({ log });
229
+ handleTaskLog(log);
230
+ }
231
+ console.log("Stopped listening", channelId);
232
+ console.log("Closed Channel", channelId);
233
+ }
234
+ }
235
+
236
+ watch(
237
+ natsSubject,
238
+ (n) => {
239
+ if (n) {
240
+ subscribeToChannel(n);
241
+ }
242
+ },
243
+ { immediate: true }
244
+ );
245
+
246
+ watch(open, (n) => {
247
+ if (!n) {
248
+ subscription && subscription.unsubscribe();
249
+ }
250
+ });
251
+ onBeforeUnmount(() => {
252
+ subscription && subscription.unsubscribe();
253
+ });
254
+
255
+ // modal close / footer feature
256
+ const enableModalClose = computed(() => {
257
+ return (
258
+ connectionError.value ||
259
+ activeTask.value?.status === "Failed" ||
260
+ activeTask.value?.status === "Success"
261
+ );
262
+ });
263
+ const enableModalFooter = computed(() => {
264
+ return showReportButton.value || showSuccessButton.value;
265
+ });
266
+
267
+ // generate report issue title with error step title
268
+ const getReportIssueTitle = (): string => {
269
+ const stepTitlesFromErrorTasks: Array<string> = [];
270
+ tasks.value.forEach((task) => {
271
+ // if this taskLog is error task, push it to array
272
+ if (task.error) {
273
+ stepTitlesFromErrorTasks.push(task.step);
274
+ }
275
+ });
276
+ // return final string
277
+ return stepTitlesFromErrorTasks.join(", ");
278
+ };
279
+
280
+ // report button
281
+ const showReportButton = computed(() => activeTask.value?.status === "Failed");
282
+ function onReportIssueClick() {
283
+ const url = `https://github.com/bytebuilders/community/issues/new?title=Chart Install: ${getReportIssueTitle()}&labels[]=bug&body=${
284
+ window.location.href
285
+ } %0A%0A %60%60%60 %0A ${
286
+ activeTask.value?.logs[activeTask.value?.logs.length - 1 || 0]
287
+ } %0A %60%60%60`;
288
+ window.open(url, "_blank");
289
+ }
290
+
291
+ // success button
292
+ const showSuccessButton = computed(
293
+ () => activeTask.value?.status === "Success" && successCtx.value?.btnTitle
294
+ );
295
+
296
+ // execute on success and on error functions
297
+ watch(
298
+ () => activeTask.value?.status,
299
+ (n) => {
300
+ if (n === "Success") {
301
+ successCtx.value.onSuccess();
302
+ } else if (n === "Failed") {
303
+ errorCtx.value.onError(
304
+ activeTask.value?.logs[activeTask.value?.logs.length - 1 || 0] || ""
305
+ );
306
+ }
307
+ }
308
+ );
309
+ </script>
310
+
311
+ <style scoped lang="scss">
312
+ .task-simple-wrapper {
313
+ display: flex;
314
+ flex-direction: column;
315
+ justify-content: space-between;
316
+ width: 40vw;
317
+ height: 40vh;
318
+ .task-cogs-icon {
319
+ width: 100%;
320
+ height: 70%;
321
+ display: flex;
322
+ align-items: center;
323
+ justify-content: center;
324
+ font-size: 20px;
325
+ color: var(--ac-primary);
326
+ }
327
+ .task-log {
328
+ width: 100%;
329
+ height: 30%;
330
+ display: flex;
331
+ flex-direction: column;
332
+ align-items: center;
333
+ justify-content: center;
334
+ .task-title {
335
+ span,
336
+ i {
337
+ font-size: 16px;
338
+ }
339
+ i {
340
+ color: var(--ac-primary);
341
+ &.is-success {
342
+ color: #158748;
343
+ }
344
+ &.is-failed {
345
+ color: #ff3729;
346
+ }
347
+ }
348
+ font-weight: 500;
349
+ }
350
+ span {
351
+ font-size: 14px;
352
+ }
353
+ }
354
+ }
355
+ .task-complex-wrapper {
356
+ display: flex;
357
+ flex-direction: row;
358
+ justify-content: space-between;
359
+ width: 60vw;
360
+ height: 60vh;
361
+
362
+ .task-list {
363
+ width: 25%;
364
+ height: 100%;
365
+ }
366
+ .task-log {
367
+ width: 70%;
368
+ height: 100%;
369
+ border-radius: 1rem;
370
+ font-size: 13px;
371
+ }
372
+ }
373
+ </style>