@appscode/design-system 1.0.43-alpha.19 → 1.0.43-alpha.190

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 (124) hide show
  1. package/base/utilities/_all.scss +6 -0
  2. package/base/utilities/_default.scss +269 -4
  3. package/base/utilities/_derived-variables.scss +0 -1
  4. package/base/utilities/_initial-variables.scss +17 -13
  5. package/base/utilities/_mixin.scss +1 -17
  6. package/base/utilities/_typography.scss +14 -4
  7. package/base/utilities/dark-theme.scss +9 -146
  8. package/components/_ac-accordion.scss +8 -4
  9. package/components/_ac-alert-box.scss +15 -7
  10. package/components/_ac-card.scss +33 -6
  11. package/components/_ac-code-highlight.scss +5 -1
  12. package/components/_ac-content-layout.scss +2 -2
  13. package/components/_ac-input.scss +63 -23
  14. package/components/_ac-multi-select.scss +187 -5
  15. package/components/_ac-options.scss +24 -9
  16. package/components/_ac-select-box.scss +13 -3
  17. package/components/_ac-table.scss +7 -5
  18. package/components/_ac-tabs.scss +42 -5
  19. package/components/_ac-terminal.scss +270 -0
  20. package/components/_all.scss +35 -0
  21. package/components/_app-drawer.scss +2 -2
  22. package/components/_breadcumb.scss +2 -0
  23. package/components/_buttons.scss +45 -36
  24. package/components/_card-body-wrapper.scss +2 -2
  25. package/components/_dashboard-header.scss +32 -0
  26. package/components/_direct-deploy.scss +69 -0
  27. package/components/_go-to-top.scss +1 -1
  28. package/components/_graph.scss +45 -0
  29. package/components/_image-upload.scss +5 -3
  30. package/components/_left-sidebar-menu.scss +193 -39
  31. package/components/_monaco-editor.scss +1 -1
  32. package/components/_navbar.scss +125 -8
  33. package/components/_overview-info.scss +4 -4
  34. package/components/_pagination.scss +8 -0
  35. package/components/_payment-card.scss +10 -1
  36. package/components/_preview-modal.scss +15 -4
  37. package/components/_pricing-table.scss +1 -1
  38. package/components/_progress-bar.scss +4 -4
  39. package/components/_subscription-card.scss +12 -5
  40. package/components/_table-of-content.scss +1 -1
  41. package/components/_tfa.scss +69 -0
  42. package/components/_transitions.scss +261 -0
  43. package/components/_wizard.scss +16 -3
  44. package/components/ac-toaster/_ac-toasted.scss +1 -1
  45. package/components/bbum/_card-team.scss +1 -1
  46. package/components/bbum/_information-center.scss +15 -1
  47. package/components/bbum/_sign-up-notification.scss +1 -1
  48. package/components/bbum/_single-post-preview.scss +1 -1
  49. package/components/bbum/_user-profile.scss +91 -90
  50. package/components/ui-builder/_ui-builder.scss +43 -3
  51. package/components/ui-builder/_vue-open-api.scss +104 -0
  52. package/layouts/_all.scss +2 -0
  53. package/layouts/_code-preview.scss +5 -2
  54. package/main.scss +4 -54
  55. package/package.json +2 -7
  56. package/plugins/theme.js +4 -0
  57. package/plugins/time-convert.js +49 -0
  58. package/plugins/vue-toaster.js +3 -0
  59. package/vue-components/v2/banner/Banner.vue +2 -2
  60. package/vue-components/v2/breadcrumbs/Breadcrumb.vue +97 -0
  61. package/vue-components/v2/button/Button.vue +5 -0
  62. package/vue-components/v2/button/DownloadBtn.vue +45 -0
  63. package/vue-components/v2/card/Card.vue +1 -0
  64. package/vue-components/v2/card/PaymentCards.vue +11 -2
  65. package/vue-components/v2/content/ContentTable.vue +12 -7
  66. package/vue-components/v2/editor/Editor.vue +38 -5
  67. package/vue-components/v2/editor/FilteredFileEditor.vue +189 -0
  68. package/vue-components/v2/editor/MonacoEditor.vue +125 -0
  69. package/vue-components/v2/editor/ResourceKeyValueEditor.vue +209 -0
  70. package/vue-components/v2/form-fields/Input.vue +1 -1
  71. package/vue-components/v2/loaders/ResourceLoader.vue +101 -0
  72. package/vue-components/v2/loaders/SidebarLoader.vue +43 -0
  73. package/vue-components/v2/modal/Modal.vue +35 -4
  74. package/vue-components/v2/modals/DeleteConfirmationModal.vue +79 -0
  75. package/vue-components/v2/modals/JsonShowModal.vue +12 -2
  76. package/vue-components/v2/navbar/Appdrawer.vue +10 -9
  77. package/vue-components/v2/navbar/ThemeMode.vue +50 -44
  78. package/vue-components/v2/navbar/User.vue +202 -19
  79. package/vue-components/v2/notification/Notification.vue +101 -0
  80. package/vue-components/v2/notification/NotificationItem.vue +44 -0
  81. package/vue-components/v2/preloader/Preloader.vue +5 -5
  82. package/vue-components/v2/sidebar/ClusterSwitcher.vue +126 -0
  83. package/vue-components/v2/sidebar/SidebarItem.vue +23 -1
  84. package/vue-components/v2/sidebar/SidebarItemWithDropDown.vue +19 -20
  85. package/vue-components/v2/tab/TabItem.vue +1 -1
  86. package/vue-components/v2/table/Table.vue +44 -8
  87. package/vue-components/v2/table/TableRow.vue +12 -2
  88. package/vue-components/v2/table/table-cell/CellValue.vue +33 -4
  89. package/vue-components/v2/table/table-cell/GenericCell.vue +56 -0
  90. package/vue-components/v2/table/table-cell/ObjectCell.vue +4 -1
  91. package/vue-components/v2/tabs/EditorTabs.vue +1 -1
  92. package/vue-components/v3/button/Button.vue +5 -0
  93. package/vue-components/v3/content/ContentTable.vue +5 -0
  94. package/vue-components/v3/editor/Editor.vue +50 -30
  95. package/vue-components/v3/editor/FilteredFileEditor.vue +184 -0
  96. package/vue-components/v3/editor/MonacoEditor.vue +131 -0
  97. package/vue-components/v3/editor/ResourceKeyValueEditor.vue +125 -0
  98. package/vue-components/v3/form/Form.vue +63 -0
  99. package/vue-components/v3/form-fields/Input.vue +10 -10
  100. package/vue-components/v3/header/HeaderItem.vue +5 -0
  101. package/vue-components/v3/header/HeaderItems.vue +5 -0
  102. package/vue-components/v3/loaders/ResourceLoader.vue +83 -0
  103. package/vue-components/v3/loaders/SidebarLoader.vue +34 -0
  104. package/vue-components/v3/long-running-tasks/LongRunningTaskItem.vue +92 -0
  105. package/vue-components/v3/modal/Modal.vue +40 -16
  106. package/vue-components/v3/modals/DeleteConfirmationModal.vue +83 -0
  107. package/vue-components/v3/modals/JsonShowModal.vue +25 -16
  108. package/vue-components/v3/modals/LongRunningTasksModal.vue +337 -0
  109. package/vue-components/v3/navbar/Appdrawer.vue +12 -7
  110. package/vue-components/v3/navbar/ThemeMode.vue +49 -47
  111. package/vue-components/v3/navbar/User.vue +190 -16
  112. package/vue-components/v3/notification/Notification.vue +98 -0
  113. package/vue-components/v3/notification/NotificationItem.vue +52 -0
  114. package/vue-components/v3/sidebar/ClusterSwitcher.vue +133 -0
  115. package/vue-components/v3/sidebar/SidebarItemWithDropDown.vue +120 -0
  116. package/vue-components/v3/table/MultiInfoTable.vue +143 -0
  117. package/vue-components/v3/table/Table.vue +35 -12
  118. package/vue-components/v3/table/TableContainer.vue +34 -0
  119. package/vue-components/v3/table/TableRow.vue +10 -2
  120. package/vue-components/v3/table/table-cell/CellValue.vue +26 -3
  121. package/vue-components/v3/table/table-cell/GenericCell.vue +62 -0
  122. package/vue-components/v3/table/table-cell/ObjectCell.vue +5 -1
  123. package/vue-components/v3/tabs/EditorTabs.vue +1 -1
  124. package/vue-components/v3/terminal/LongRunningTaskTerminal.vue +148 -0
@@ -1,7 +1,24 @@
1
1
  <template>
2
- <button v-if="themeMode" class="button ac-nav-button" @click="toggleTheme" :title="themeModeIconTooltip" >
3
- <i :class="`fa ${themeModeIconClass} width-15`" />
4
- </button>
2
+ <li class="mt-10 b-t-1 pt-10">
3
+ <ul class="ac-vscrollbar">
4
+ <li>
5
+ <div class="ac-menu-contentt theme-choicee">
6
+ <ul class="is-flex is-flex-direction-row is-justify-content-center">
7
+ <li
8
+ v-for="theme of Object.keys(themeModes)"
9
+ :title="themeModes[theme].displayName"
10
+ @click="themeMode = theme"
11
+ class="p-10 pl-30 pr-30 is-flex-grow-1 has-text-centered"
12
+ :class="{ 'is-active': themeMode === theme }"
13
+ :key="theme"
14
+ >
15
+ <i :class="['fa', themeModes[theme].iconClass]" />
16
+ </li>
17
+ </ul>
18
+ </div>
19
+ </li>
20
+ </ul>
21
+ </li>
5
22
  </template>
6
23
  <script>
7
24
  import { defineComponent } from "vue";
@@ -10,41 +27,31 @@ export default defineComponent({
10
27
  data() {
11
28
  return {
12
29
  themeMode: "",
30
+ themeModes: {
31
+ light: {
32
+ displayName: "Light Theme",
33
+ iconClass: "fa-sun-o",
34
+ },
35
+ dark: {
36
+ displayName: "Dark Theme",
37
+ iconClass: "fa-moon-o",
38
+ },
39
+ system: {
40
+ displayName: "System Theme",
41
+ iconClass: "fa-desktop",
42
+ },
43
+ },
13
44
  };
14
45
  },
15
46
 
16
- emits: ['set:theme'],
17
-
18
- computed: {
19
- // to set icon class for theme mode
20
- themeModeIconClass() {
21
- if(this.themeMode === "system") {
22
- return "fa-desktop";
23
- } else if(this.themeMode === "light") {
24
- return "fa-sun-o";
25
- } else if(this.themeMode === "dark") {
26
- return "fa-moon-o";
27
- }
28
- },
29
-
30
- // to set icon tooltip for theme mode
31
- themeModeIconTooltip() {
32
- if(this.themeMode === "system") {
33
- return "System theme";
34
- } else if(this.themeMode === "light") {
35
- return "Light theme";
36
- } else if(this.themeMode === "dark") {
37
- return "Dark theme";
38
- }
39
- }
40
- },
47
+ emits: ["set:theme"],
41
48
 
42
49
  mounted() {
43
50
  // get theme mode from localStorage or set default one
44
51
  this.themeMode = localStorage.getItem("themeMode") || "light";
45
52
  },
46
53
 
47
- destroyed() {
54
+ unmounted() {
48
55
  this.removeColorSchemeEventListener();
49
56
  },
50
57
 
@@ -52,19 +59,16 @@ export default defineComponent({
52
59
  themeMode: {
53
60
  handler(n) {
54
61
  this.onThemeModeChange(n);
55
- }
56
- }
62
+ },
63
+ },
57
64
  },
58
65
 
59
66
  methods: {
60
67
  // handle theme mode button click
61
68
  toggleTheme() {
62
- if(this.themeMode === "light")
63
- this.themeMode = "dark";
64
- else if(this.themeMode === "dark")
65
- this.themeMode = "system";
66
- else if(this.themeMode === "system")
67
- this.themeMode = "light";
69
+ if (this.themeMode === "light") this.themeMode = "dark";
70
+ else if (this.themeMode === "dark") this.themeMode = "system";
71
+ else if (this.themeMode === "system") this.themeMode = "light";
68
72
  },
69
73
 
70
74
  // triggered when theme mode is updated
@@ -72,8 +76,10 @@ export default defineComponent({
72
76
  localStorage.setItem("themeMode", n);
73
77
 
74
78
  let theme = n;
75
- if(n === "system") {
76
- const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
79
+ if (n === "system") {
80
+ const isDarkMode =
81
+ window.matchMedia &&
82
+ window.matchMedia("(prefers-color-scheme: dark)").matches;
77
83
  this.addColorSchemeEventListener();
78
84
  theme = isDarkMode ? "dark" : "light";
79
85
  } else {
@@ -85,7 +91,7 @@ export default defineComponent({
85
91
 
86
92
  // add proper css class to update the ui theme
87
93
  handleDarkThemeClass(currentTheme) {
88
- if(currentTheme === "light") {
94
+ if (currentTheme === "light") {
89
95
  document.documentElement.classList.remove("is-dark-theme");
90
96
  } else {
91
97
  document.documentElement.classList.add("is-dark-theme");
@@ -96,23 +102,19 @@ export default defineComponent({
96
102
  addColorSchemeEventListener() {
97
103
  window
98
104
  .matchMedia("(prefers-color-scheme: dark)")
99
- .addEventListener(
100
- "change", this.handleSystemThemeChange
101
- );
105
+ .addEventListener("change", this.handleSystemThemeChange);
102
106
  },
103
107
 
104
108
  // remove system theme listener event
105
109
  removeColorSchemeEventListener() {
106
110
  window
107
111
  .matchMedia("(prefers-color-scheme: dark)")
108
- .removeEventListener(
109
- "change", this.handleSystemThemeChange
110
- );
112
+ .removeEventListener("change", this.handleSystemThemeChange);
111
113
  },
112
114
 
113
115
  handleSystemThemeChange() {
114
116
  this.onThemeModeChange(this.themeMode);
115
117
  },
116
- }
118
+ },
117
119
  });
118
120
  </script>
@@ -7,31 +7,114 @@
7
7
  <img :src="user.avatar_url" alt="User Photo" />
8
8
  </div>
9
9
  </button>
10
- <navbar-item-content>
11
- <div v-if="user.username" class="user-profile-wrapper">
10
+ <navbar-item-content class="navbar-dropdown-wrapper">
11
+ <div v-if="user.username" class="user-profile-wrapper" @mouseleave="onMouseLeave()">
12
12
  <div class="profile-area">
13
13
  <div class="profile-photo">
14
- <img :src="user.avatar_url" alt="User Photo" />
14
+ <img :src="user.avatar_url" alt="User Photo" class="width-50 height-50" />
15
15
  <button class="camera-icon"></button>
16
16
  </div>
17
- <div class="profile-info">
18
- <p>{{ user.username.toUpperCase() }}</p>
17
+ <div class="profile-info" style="width: calc(100% - 60px);">
18
+ <a
19
+ :href="`${serverDomain}/${user.username}`"
20
+ :title="user.username.toUpperCase()"
21
+ class="line-break-anywhere is-ellipsis-1"
22
+ >{{ user.username.toUpperCase() }}</a
23
+ >
19
24
  <a :href="`mailto:${user.email}`"> {{ user.email }}</a>
20
25
  </div>
21
26
  </div>
22
- <ul>
23
- <li>
27
+ <transition-group name="list" tag="ul">
28
+ <li key="settings">
24
29
  <a :href="`${serverDomain}/user/settings/`">Settings</a>
25
30
  </li>
26
- <template v-if="user.is_admin">
27
- <li>
28
- <a :href="`${serverDomain}/admin`"> Site Administration </a>
29
- </li>
30
- </template>
31
- <li>
32
- <a :href="`${serverDomain}/user/logout`"> Sign out </a>
31
+ <li v-if="user.is_admin" key="site-admin">
32
+ <a :href="`${accountsDomain}/admin`">Site Administration</a>
33
33
  </li>
34
- </ul>
34
+ <li v-if="showAccountSwitcher" :class="`is-${dropDownStatus}`" key="switcher">
35
+ <a class="
36
+ ac-dropdown-button
37
+ is-fullwidth
38
+ is-flex
39
+ is-justify-content-space-between
40
+ is-align-items-center
41
+ "
42
+ @click="toggleList()"
43
+ >
44
+ <span>Switch Account</span>
45
+ <span
46
+ ><i
47
+ :class="`fa fa-angle-${
48
+ dropDownStatus === 'open' ? 'up' : 'down'
49
+ }`"
50
+ ></i
51
+ ></span>
52
+ </a>
53
+ <transition-group name="list" tag="ul"
54
+ class="ac-vscrollbar"
55
+ ref="dropdownItems"
56
+ :style="{ maxHeight: dropDownSectionHeight }"
57
+ >
58
+ <li
59
+ v-for="(org, idx) in formattedOrganizations"
60
+ :key="org.username"
61
+ >
62
+ <a
63
+ class="is-flex is-align-items-center"
64
+ @click="onOrganizationClick(org.username)"
65
+ >
66
+ <div class="width-30 height-30 image">
67
+ <img
68
+ :src="org.avatar_url"
69
+ class="ac-user-profile is-rounded"
70
+ alt="icon"
71
+ />
72
+ </div>
73
+ <div
74
+ class="
75
+ is-flex
76
+ is-align-items-center
77
+ is-justify-content-space-between
78
+ is-fullwidth
79
+ ml-10
80
+ "
81
+ >
82
+ <div class="org-info">
83
+ <strong :title="org.username" class="line-break-anywhere is-ellipsis-1">{{ org.username }}</strong>
84
+ <p>
85
+ {{
86
+ org.isPersonalAccount
87
+ ? "Personal Account"
88
+ : "Organization"
89
+ }}
90
+ </p>
91
+ </div>
92
+ <span
93
+ v-if="idx === 0"
94
+ class="
95
+ material-icons-outlined
96
+ font-size-18
97
+ ml-10
98
+ is-pulled-right
99
+ "
100
+ >
101
+ check
102
+ </span>
103
+ </div>
104
+ </a>
105
+ </li>
106
+ </transition-group>
107
+ </li>
108
+ <li key="dashboard">
109
+ <a :href="`${serverDomain}/dashboard`"> Dashboard </a>
110
+ </li>
111
+ <li key="signout">
112
+ <a :href="`${accountsDomain}/user/logout`"> Sign out </a>
113
+ </li>
114
+ <li key="theme" v-if="showThemeMode">
115
+ <theme-mode @set:theme="setTheme" />
116
+ </li>
117
+ </transition-group>
35
118
  </div>
36
119
  </navbar-item-content>
37
120
  </navbar-item>
@@ -42,6 +125,7 @@ import { defineComponent, defineAsyncComponent } from "vue";
42
125
 
43
126
  export default defineComponent({
44
127
  props: {
128
+ // active user info
45
129
  user: {
46
130
  type: Object,
47
131
  default: () => ({}),
@@ -50,7 +134,25 @@ export default defineComponent({
50
134
  type: String,
51
135
  default: "",
52
136
  },
137
+ accountsDomain: {
138
+ type: String,
139
+ default: "",
140
+ },
141
+ showAccountSwitcher: {
142
+ type: Boolean,
143
+ default: false,
144
+ },
145
+ // all available organization list including personal account
146
+ organizations: {
147
+ type: Array,
148
+ default: () => [],
149
+ },
150
+ showThemeMode: {
151
+ type: Boolean,
152
+ default: false
153
+ }
53
154
  },
155
+ emits: ["set:theme"],
54
156
 
55
157
  components: {
56
158
  NavbarItem: defineAsyncComponent(() =>
@@ -59,6 +161,78 @@ export default defineComponent({
59
161
  NavbarItemContent: defineAsyncComponent(() =>
60
162
  import("../../v2/navbar/NavbarItemContent.vue").then((module) => module.default)
61
163
  ),
164
+ ThemeMode: defineAsyncComponent(() =>
165
+ import("../../v3/navbar/ThemeMode.vue").then((module) => module.default)
166
+ ),
167
+ },
168
+
169
+ computed: {
170
+ formattedOrganizations() {
171
+ let activeUser;
172
+ const filteredList = this.organizations.filter((item) => {
173
+ if (item.username === this.user.username) {
174
+ activeUser = item;
175
+ } else {
176
+ return true;
177
+ }
178
+ return false;
179
+ });
180
+
181
+ filteredList.unshift(activeUser);
182
+
183
+ return filteredList || [];
184
+ },
185
+ },
186
+
187
+ data() {
188
+ return {
189
+ dropDownStatus: "close",
190
+ dropDownSectionHeight: null,
191
+ };
192
+ },
193
+
194
+ methods: {
195
+ toggleList() {
196
+ this.dropDownStatus = this.dropDownStatus === "open" ? "close" : "open";
197
+ this.$nextTick(() => {
198
+ this.$refs["dropdownItems"].$el.scrollTo(0, 0);
199
+ });
200
+ },
201
+ onOrganizationClick(orgName) {
202
+ this.$refs["dropdownItems"].$el.scrollTo(0, 0);
203
+ this.$emit("activeorg$set", orgName);
204
+ },
205
+ onMouseLeave() {
206
+ this.dropDownStatus = "close";
207
+ },
208
+ setTheme(val){
209
+ this.$emit("set:theme", val);
210
+ }
211
+ },
212
+
213
+ watch: {
214
+ dropDownStatus: {
215
+ immediate: true,
216
+ handler(n) {
217
+ if (n === "open") {
218
+ this.$nextTick(() => {
219
+ const dropDownUl = this.$refs["dropdownItems"];
220
+ if (dropDownUl)
221
+ this.dropDownSectionHeight = `${dropDownUl.scrollHeight}px`;
222
+ });
223
+ } else {
224
+ this.dropDownSectionHeight = null;
225
+ }
226
+ },
227
+ },
62
228
  },
63
229
  });
64
- </script>
230
+ </script>
231
+ <style lang="scss" scoped>
232
+ .ac-vscrollbar {
233
+ overflow: auto !important;
234
+ }
235
+ .line-break-anywhere {
236
+ line-break: anywhere;
237
+ }
238
+ </style>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <div
3
+ @mouseenter="notificationPanelOpen = true"
4
+ @mouseleave="notificationPanelOpen = false"
5
+ >
6
+ <button class="button ac-nav-button">
7
+ <span v-if="unreadCount">{{ unreadCount }}</span>
8
+ <i
9
+ class="fa fa-bell"
10
+ :class="{ 'ac-shake': unreadCount }"
11
+ aria-hidden="true"
12
+ ></i>
13
+ </button>
14
+ <div class="ac-menu-content is-notification">
15
+ <div class="notification-header">
16
+ <div class="left-content">
17
+ <p>
18
+ Notifications <span>({{ notifications.length }})</span>
19
+ </p>
20
+ </div>
21
+ <div class="right-content"></div>
22
+ </div>
23
+ <div :key="notificationPanelOpen ? 1 : 0" class="notification-body">
24
+ <transition-group v-if="notifications.length" name="slide-right">
25
+ <notification-item
26
+ v-for="notification in notifications"
27
+ :key="`${notification.id}${unreadCount}`"
28
+ :notification="notification"
29
+ />
30
+ </transition-group>
31
+ <span v-else class="single-notification-item"
32
+ >No new notifications</span
33
+ >
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { NatsConnection, StringCodec, Subscription } from "nats.ws";
41
+ import {
42
+ computed,
43
+ defineAsyncComponent,
44
+ getCurrentInstance,
45
+ ref,
46
+ Ref,
47
+ watch,
48
+ } from "vue";
49
+ import { TaskLog } from "../../../typings/long-running-tasks";
50
+ import { Notification } from "../../../typings/notification";
51
+
52
+ const NotificationItem = defineAsyncComponent(
53
+ () => import("./NotificationItem.vue")
54
+ );
55
+
56
+ const notifications: Ref<Notification[]> = ref([]);
57
+ const notificationsRead = ref(0);
58
+ const unreadCount = computed(
59
+ () => notifications.value.length - notificationsRead.value
60
+ );
61
+
62
+ const notificationPanelOpen = ref(false);
63
+ watch(notificationPanelOpen, (n) => {
64
+ if (n) notificationsRead.value = notifications.value.length;
65
+ });
66
+
67
+ function addNewNotification(notification: Notification) {
68
+ notifications.value.unshift(notification);
69
+ if (notificationPanelOpen.value) {
70
+ notificationsRead.value = notifications.value.length;
71
+ }
72
+ }
73
+
74
+ const app = getCurrentInstance();
75
+ const $nats: NatsConnection = app?.appContext.config.globalProperties.$nc;
76
+ let subscription: Subscription;
77
+
78
+ async function subscribeToNotifcations() {
79
+ subscription = $nats?.subscribe("notifications");
80
+ console.log("Started listening to Notifications");
81
+
82
+ if (subscription) {
83
+ // listen to channel events
84
+ for await (const msg of subscription) {
85
+ console.log("notifications ===>");
86
+ console.log({ data: StringCodec().decode(msg.data) });
87
+ const log: TaskLog = JSON.parse(StringCodec().decode(msg.data));
88
+ console.log({ log });
89
+ const currentTime = new Date().getTime();
90
+ addNewNotification({ ...log, id: currentTime, time: currentTime });
91
+ msg.respond();
92
+ }
93
+ console.log("Stopped listening to Notifications");
94
+ console.log("Closed Channel Notifications");
95
+ }
96
+ }
97
+ subscribeToNotifcations();
98
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <a class="single-notification-item is-complete">
3
+ <p>
4
+ {{ notification.msg }}
5
+ </p>
6
+ <div class="notification-status">
7
+ <p
8
+ :class="{
9
+ 'is-success': notification.status === 'Success',
10
+ 'is-danger': notification.status === 'Failed',
11
+ 'is-info':
12
+ notification.status === 'Started' ||
13
+ notification.status === 'Running',
14
+ 'is-warning': notification.status === 'Pending',
15
+ }"
16
+ >
17
+ <i
18
+ class="fa mr-5"
19
+ :class="{
20
+ 'fa-check': notification.status === 'Success',
21
+ 'fa-exclamation-triangle': notification.status === 'Failed',
22
+ 'fa-info-circle':
23
+ notification.status === 'Started' ||
24
+ notification.status === 'Pending' ||
25
+ notification.status === 'Running',
26
+ }"
27
+ />
28
+ {{ notification.status }}
29
+ </p>
30
+ <p>{{ notificationTime }} ago</p>
31
+ </div>
32
+ </a>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { computed, toRefs } from 'vue'
37
+ import { Notification } from '../../../typings/notification'
38
+ import TimeConvert from '../../../plugins/time-convert'
39
+
40
+ const props = withDefaults(defineProps<{ notification: Notification }>(), {
41
+ notification: () => ({
42
+ id: 0,
43
+ status: 'Pending',
44
+ time: 0,
45
+ }),
46
+ })
47
+
48
+ const { notification } = toRefs(props)
49
+ const notificationTime = computed(() =>
50
+ TimeConvert.getDayDifferences({ past: notification.value.time })
51
+ )
52
+ </script>
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <div v-if="sidebarCollapsed" class="is-cluster-logo">
3
+ <img
4
+ width="40"
5
+ :src="getProviderIcon(selectedCluster && selectedCluster.provider)"
6
+ onerror="this.onerror=null;this.src='https://cdn.appscode.com/images/cloud-provider-icons/Generic.png';"
7
+ alt="provider-icon"
8
+ />
9
+ </div>
10
+ <multiselect
11
+ v-else
12
+ v-model="selectedCluster"
13
+ placeholder="Selected Cluster"
14
+ label="name"
15
+ track-by="name"
16
+ :options="clusterOptions"
17
+ :allow-empty="false"
18
+ deselect-label=""
19
+ select-label=""
20
+ selected-label=""
21
+ >
22
+ <template #singleLabel="props">
23
+ <div class="is-flex is-align-items-center">
24
+ <img
25
+ :src="getProviderIcon(props.option.provider)"
26
+ onerror="this.onerror=null;this.src='https://cdn.appscode.com/images/cloud-provider-icons/Generic.png';"
27
+ alt="No cluster selected"
28
+ /><span
29
+ ><span>{{ props.option.displayName }}</span></span
30
+ >
31
+ </div>
32
+ </template>
33
+ <template #option="props">
34
+ <div class="is-flex is-align-items-center">
35
+ <img
36
+ class="mr-15"
37
+ :src="getProviderIcon(props.option.provider)"
38
+ onerror="this.onerror=null;this.src='https://cdn.appscode.com/images/cloud-provider-icons/Generic.png';"
39
+ alt="No cluster selected"
40
+ />
41
+ <div>
42
+ <p>{{ props.option.displayName }}</p>
43
+ <p class="location">{{ props.option.location }}</p>
44
+ </div>
45
+ </div>
46
+ </template>
47
+ </multiselect>
48
+ </template>
49
+
50
+ <script>
51
+ import { defineComponent, defineAsyncComponent } from 'vue'
52
+ export default defineComponent({
53
+ components: {
54
+ Multiselect: defineAsyncComponent(() =>
55
+ import('vue-multiselect').then((module) => module.default)
56
+ ),
57
+ },
58
+ props: {
59
+ sidebarCollapsed: {
60
+ type: Boolean,
61
+ default: false,
62
+ },
63
+ mouseHover: {
64
+ type: Boolean,
65
+ default: false,
66
+ },
67
+ clusterOptions: {
68
+ type: Array,
69
+ default: () => [],
70
+ },
71
+ modelValue: {
72
+ type: String,
73
+ default: '',
74
+ },
75
+ },
76
+
77
+ emits: ['update:modelValue'],
78
+
79
+ data() {
80
+ return {
81
+ selectedCluster: null,
82
+ selectedClusterName: null,
83
+ }
84
+ },
85
+
86
+ watch: {
87
+ modelValue: {
88
+ immediate: true,
89
+ handler(n) {
90
+ this.selectedClusterName = n
91
+ },
92
+ },
93
+ selectedCluster: {
94
+ deep: true,
95
+ handler(n) {
96
+ if (this.selectedClusterName !== n.name) {
97
+ this.selectedClusterName = n.name
98
+ }
99
+ },
100
+ },
101
+ selectedClusterName(n) {
102
+ if (n !== this.selectedCluster.name) {
103
+ this.clusterOptions.forEach((item) => {
104
+ if (this.selectedClusterName === item.name) {
105
+ this.selectedCluster = item
106
+ }
107
+ })
108
+ }
109
+
110
+ this.$emit('update:modelValue', n)
111
+ },
112
+ clusterOptions: {
113
+ deep: true,
114
+ immediate: true,
115
+ async handler(list) {
116
+ if (list) {
117
+ list.forEach((item) => {
118
+ if (this.selectedClusterName === item.name) {
119
+ this.selectedCluster = item
120
+ }
121
+ })
122
+ }
123
+ },
124
+ },
125
+ },
126
+
127
+ methods: {
128
+ getProviderIcon(provider) {
129
+ return `https://cdn.appscode.com/images/cloud-provider-icons/${provider}.png`
130
+ },
131
+ },
132
+ })
133
+ </script>