@appscode/design-system 1.1.0-beta.42 → 1.1.0-beta.46

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": "@appscode/design-system",
3
- "version": "1.1.0-beta.42",
3
+ "version": "1.1.0-beta.46",
4
4
  "description": "A design system for Appscode websites and dashboards made using Bulma",
5
5
  "main": "main.scss",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
- import type { TaskLog } from "./longRunningTasks";
2
-
3
- export interface Notification extends TaskLog {
1
+ export interface Notification {
4
2
  id: string;
5
3
  time: number;
4
+ msg?: string;
5
+ status: string;
6
6
  }
@@ -9,7 +9,7 @@ import {
9
9
  watch,
10
10
  } from "vue";
11
11
  import type { Ref } from "vue";
12
- import type { TaskLog } from "../../types/longRunningTasks";
12
+ // import type { TaskLog } from "../../types/longRunningTasks";
13
13
  import type { Notification } from "../../types/notification";
14
14
 
15
15
  const NotificationItem = defineAsyncComponent(
@@ -48,7 +48,7 @@ async function subscribeToNotifcations() {
48
48
  for await (const msg of subscription) {
49
49
  console.log("notifications ===>");
50
50
  console.log({ data: StringCodec().decode(msg.data) });
51
- const log: TaskLog = JSON.parse(StringCodec().decode(msg.data));
51
+ const log = JSON.parse(StringCodec().decode(msg.data));
52
52
  console.log({ log });
53
53
  const currentTime = new Date().getTime();
54
54
  addNewNotification({
@@ -1,20 +0,0 @@
1
- export type TaskStatus =
2
- | "Pending"
3
- | "Running"
4
- | "Success"
5
- | "Failed"
6
- | "Started";
7
- export interface TaskLog {
8
- status?: TaskStatus;
9
- msg?: string;
10
- step?: string;
11
- error?: string;
12
- id?: string;
13
- }
14
- export interface Task extends Omit<TaskLog, "message"> {
15
- logs: Array<string>;
16
- }
17
- export interface LongRunningTasksCtx {
18
- natsSubject: string;
19
- tasks: Array<Task>;
20
- }
@@ -1,94 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, toRefs } from "vue";
3
- import type { TaskStatus } from "./../../types/longRunningTasks";
4
-
5
- interface Props {
6
- title?: string;
7
- status?: TaskStatus;
8
- }
9
-
10
- const props = withDefaults(defineProps<Props>(), {
11
- title: "no title",
12
- status: "Pending",
13
- });
14
-
15
- const { status } = toRefs(props);
16
- const statusClass = computed(() => `is-${status.value.toLowerCase()}`);
17
- const statusIcon = computed(() => {
18
- if (status.value === "Running" || status.value === "Started")
19
- return "circle-o-notch";
20
- else if (status.value === "Success") return "check-circle";
21
- else if (status.value === "Failed") return "times-circle";
22
- else return "spinner";
23
- });
24
- </script>
25
-
26
- <template>
27
- <span href="" class="task-item" :class="[statusClass]">
28
- <i class="fa" :class="`fa-${statusIcon}`" />
29
- {{ title }}
30
- </span>
31
- </template>
32
-
33
- <style scoped lang="scss">
34
- .task-item {
35
- font-size: 14px;
36
- display: block;
37
- padding: 5px;
38
- border-radius: 5px;
39
- transition: all 0.3s ease-in-out;
40
- &:hover {
41
- background-color: $primary-97;
42
- cursor: pointer;
43
- }
44
- &.is-active {
45
- background-color: $primary-97;
46
- }
47
- &.is-pending {
48
- color: $black-50;
49
- i {
50
- visibility: hidden;
51
- }
52
- &:hover {
53
- background-color: transparent;
54
- cursor: not-allowed;
55
- }
56
- }
57
- &.is-aborted {
58
- color: $black-50;
59
- &:hover {
60
- background-color: transparent;
61
- cursor: not-allowed;
62
- }
63
- }
64
- &.is-running,
65
- &.is-started {
66
- i {
67
- color: $primary;
68
- animation-name: spin;
69
- animation-duration: 1000ms;
70
- animation-iteration-count: infinite;
71
- animation-timing-function: linear;
72
- }
73
-
74
- @keyframes spin {
75
- from {
76
- transform: rotate(0deg);
77
- }
78
- to {
79
- transform: rotate(360deg);
80
- }
81
- }
82
- }
83
- &.is-success {
84
- i {
85
- color: $success;
86
- }
87
- }
88
- &.is-failed {
89
- i {
90
- color: $danger;
91
- }
92
- }
93
- }
94
- </style>
@@ -1,402 +0,0 @@
1
- <script setup lang="ts">
2
- import {
3
- computed,
4
- getCurrentInstance,
5
- onBeforeUnmount,
6
- toRefs,
7
- watch,
8
- } from "vue";
9
- import type { Ref } from "vue";
10
- import { defineAsyncComponent, ref } from "vue";
11
- import type { Task, TaskLog } from "../../types/longRunningTasks";
12
- import { StringCodec } from "nats.ws";
13
- import type { Subscription } from "nats.ws";
14
-
15
- const Modal = defineAsyncComponent(() => import("../modal/Modal.vue"));
16
- const LongRunningTaskItem = defineAsyncComponent(
17
- () => import("../long-running-tasks/LongRunningTaskItem.vue")
18
- );
19
- const LongRunningTaskTerminal = defineAsyncComponent(
20
- () => import("../terminal/LongRunningTaskTerminal.vue")
21
- );
22
- const Preloader = defineAsyncComponent(
23
- () => import("../../v2/preloader/Preloader.vue")
24
- );
25
- const AcButton = defineAsyncComponent(() => import("../button/Button.vue"));
26
-
27
- defineEmits(["close"]);
28
-
29
- interface Props {
30
- open?: boolean;
31
- theme?: string;
32
- title?: string;
33
- simple?: boolean;
34
- natsSubject?: string;
35
- isNatsConnectionLoading?: boolean;
36
- errorCtx?: {
37
- connectionError: string;
38
- onError: (msg: string) => void;
39
- };
40
- successCtx?: {
41
- btnTitle?: string;
42
- isLoaderActive?: boolean;
43
- onSuccess: () => void;
44
- onSuccessBtnClick?: () => void;
45
- };
46
- }
47
-
48
- const props = withDefaults(defineProps<Props>(), {
49
- open: true,
50
- theme: "light",
51
- simple: true,
52
- title: "Sample title",
53
- natsSubject: "",
54
- isNatsConnectionLoading: false,
55
- errorCtx: undefined,
56
- successCtx: undefined,
57
- });
58
-
59
- const { natsSubject, open, errorCtx, successCtx } = toRefs(props);
60
- const connectionError = computed(() => errorCtx.value?.connectionError);
61
- const currentInstance = getCurrentInstance();
62
- const $nats = currentInstance?.appContext.config.globalProperties.$nc;
63
- let subscription: Subscription;
64
-
65
- const tasks: Ref<Array<Task>> = ref([]);
66
- const activeStepId: Ref<string> = ref("");
67
- // to maintain stepId to stepIndex map
68
- // to find active task faster
69
- const idToStepIndex: Ref<Record<string, number>> = ref({});
70
- const activeTask = computed(() => {
71
- const task = tasks.value[idToStepIndex.value[activeStepId.value]];
72
- return task;
73
- });
74
-
75
- function handleTaskLog(log: TaskLog) {
76
- if (log.step) {
77
- // log has a step
78
- // so add new task
79
- tasks.value.push({
80
- ...log,
81
- logs: [(log.msg && log.msg) || ""],
82
- });
83
-
84
- // recent pushed task index
85
- const latestStepIndex = tasks.value.length - 1;
86
-
87
- // map taskid to stepIndex
88
- idToStepIndex.value[log.id as string] = latestStepIndex;
89
-
90
- // update active step index for first task
91
- // or if current active step is in latest step
92
- if (
93
- latestStepIndex === 0 ||
94
- latestStepIndex === idToStepIndex.value[activeStepId.value] + 1
95
- ) {
96
- activeStepId.value = log.id as string;
97
- }
98
- } else {
99
- // get active task
100
- const task = tasks.value[idToStepIndex.value[log.id as string]];
101
- if (task) {
102
- task.status = log.status;
103
- if (log.status === "Failed") {
104
- task.logs.push(log.error || "");
105
- errorCtx.value?.onError(log?.error || "");
106
- } else {
107
- task.logs.push(log.msg || "");
108
- }
109
- }
110
- }
111
- }
112
-
113
- async function subscribeToChannel(channelId: string) {
114
- subscription = $nats?.subscribe(channelId);
115
-
116
- console.log("Started listening", channelId);
117
-
118
- if (subscription) {
119
- // listen to channel events
120
- //@ts-ignore
121
- for await (const msg of subscription) {
122
- console.log("Long Running Tasks Modal=>");
123
- console.log({ data: StringCodec().decode(msg.data) });
124
- const log: TaskLog = JSON.parse(StringCodec().decode(msg.data));
125
- console.log({ log });
126
- handleTaskLog(log);
127
- }
128
- console.log("Stopped listening", channelId);
129
- console.log("Closed Channel", channelId);
130
- }
131
- }
132
-
133
- watch(
134
- natsSubject,
135
- (n) => {
136
- if (n) {
137
- subscribeToChannel(n);
138
- }
139
- },
140
- { immediate: true }
141
- );
142
-
143
- watch(open, (n) => {
144
- if (!n) {
145
- subscription && subscription.unsubscribe();
146
- }
147
- });
148
- onBeforeUnmount(() => {
149
- subscription && subscription.unsubscribe();
150
- });
151
-
152
- const longRunningTaskStatus = computed(() => {
153
- let successTaskCount = 0;
154
- let failedTaskCount = 0;
155
-
156
- // get count of success and failed task
157
- tasks.value.forEach((task) => {
158
- if (task?.status === "Success") successTaskCount++;
159
- else if (task?.status === "Failed") failedTaskCount++;
160
- });
161
-
162
- if (tasks.value.length === 0) return "NotStarted";
163
- // if all the task has been successful
164
- else if (successTaskCount === tasks.value.length) {
165
- return "Success";
166
- }
167
- // if all the task has been completed and some tasks are failed
168
- else if (
169
- failedTaskCount &&
170
- successTaskCount + failedTaskCount === tasks.value.length
171
- ) {
172
- return "Failed";
173
- } else return "Pending";
174
- });
175
-
176
- // modal close / footer feature
177
- const enableModalClose = computed(() => {
178
- return (
179
- connectionError.value ||
180
- longRunningTaskStatus.value === "Failed" ||
181
- longRunningTaskStatus.value === "Success"
182
- );
183
- });
184
- const enableModalFooter = computed(() => {
185
- return showReportButton.value || showSuccessButton.value;
186
- });
187
-
188
- // generate report issue title with error step title
189
- const getReportIssueInfo = (): { title: string; body: string } => {
190
- const stepTitlesFromErrorTasks: Array<string> = [];
191
- const stepLogsFromErrorTasks: Array<string> = [];
192
- tasks.value.forEach((task) => {
193
- // if this taskLog is error task, push it to array
194
- if (task.error) {
195
- stepTitlesFromErrorTasks.push(task?.step || "");
196
- stepLogsFromErrorTasks.push(task?.logs[task?.logs?.length - 1] || "");
197
- }
198
- });
199
- // return final object
200
- return {
201
- title: stepTitlesFromErrorTasks.join(", "),
202
- body: stepLogsFromErrorTasks.join(", "),
203
- };
204
- };
205
-
206
- // report button
207
- const showReportButton = computed(
208
- () => longRunningTaskStatus.value === "Failed"
209
- );
210
- function onReportIssueClick() {
211
- const url = `https://github.com/bytebuilders/community/issues/new?title=Chart Install: ${
212
- getReportIssueInfo().title
213
- }&labels[]=bug&body=${window.location.href} %0A%0A %60%60%60 %0A ${
214
- getReportIssueInfo().body
215
- } %0A %60%60%60`;
216
- window.open(url, "_blank");
217
- }
218
-
219
- // success button
220
- const showSuccessButton = computed(
221
- () =>
222
- longRunningTaskStatus.value === "Success" && !!successCtx.value?.btnTitle
223
- );
224
-
225
- // execute on success and on error functions
226
- watch(longRunningTaskStatus, (n) => {
227
- if (n === "Success") {
228
- successCtx.value?.onSuccess();
229
- } else if (n === "Failed") {
230
- errorCtx.value?.onError("Operation Failed");
231
- }
232
- });
233
- </script>
234
-
235
- <template>
236
- <modal
237
- :open="open"
238
- :title="title"
239
- :is-close-option-disabled="!enableModalClose"
240
- :ignore-outside-click="true"
241
- :hide-action-footer="!enableModalFooter"
242
- @closemodal="$emit('close')"
243
- >
244
- <div v-if="connectionError" class="task-simple-wrapper">
245
- <div class="task-cogs-icon">
246
- <i class="fa fa-times-circle has-text-danger fa-5x fa-fw"></i>
247
- </div>
248
- <div class="task-log">
249
- <span class="task-title">
250
- <i class="fa fa-times-circle mr-5 is-failed" />
251
- <span> Connection error </span>
252
- </span>
253
- <span>{{ connectionError }}</span>
254
- </div>
255
- </div>
256
- <div
257
- v-else-if="isNatsConnectionLoading || !activeStepId"
258
- class="is-justify-content-center"
259
- :class="simple ? 'task-simple-wrapper' : 'task-complex-wrapper'"
260
- >
261
- <preloader
262
- :style="{ height: '100%' }"
263
- class="is-fullheight"
264
- message="Connecting..."
265
- />
266
- </div>
267
- <div v-else-if="simple" class="task-simple-wrapper">
268
- <div class="task-cogs-icon">
269
- <i class="fa fa-cog fa-spin fa-5x fa-fw"></i>
270
- <span class="is-flex is-flex-direction-column">
271
- <i class="fa fa-cog fa-spin fa-3x fa-bw"></i>
272
- <i class="fa fa-cog fa-spin fa-3x fa-bw"></i>
273
- </span>
274
- </div>
275
- <div class="task-log">
276
- <span class="task-title">
277
- <i
278
- v-if="activeTask?.status === 'Running'"
279
- class="fa fa-circle-o-notch fa-spin mr-5"
280
- />
281
- <i
282
- v-else-if="activeTask?.status === 'Success'"
283
- class="fa fa-check-circle mr-5 is-success"
284
- />
285
- <i
286
- v-else-if="activeTask?.status === 'Failed'"
287
- class="fa fa-times-circle mr-5 is-failed"
288
- />
289
- <span>
290
- {{ activeTask?.step }}
291
- </span>
292
- </span>
293
- <span>{{ activeTask?.logs[activeTask?.logs.length - 1] }}</span>
294
- </div>
295
- </div>
296
- <div v-else class="task-complex-wrapper">
297
- <ul class="task-list">
298
- <li v-for="task in tasks" :key="task.step">
299
- <long-running-task-item
300
- :title="task.step"
301
- :status="task.status"
302
- :class="{ 'is-active': activeStepId === task.id }"
303
- @click="activeStepId = task.id as string"
304
- />
305
- </li>
306
- </ul>
307
- <long-running-task-terminal
308
- :key="activeTask?.id"
309
- :theme="theme"
310
- :logs="activeTask?.logs"
311
- class="task-log"
312
- />
313
- </div>
314
-
315
- <template #modal-footer-controls>
316
- <ac-button
317
- title="Close"
318
- modifier-classes="is-outlined"
319
- @click.stop="$emit('close')"
320
- />
321
- <ac-button
322
- v-if="showSuccessButton"
323
- :title="successCtx?.btnTitle"
324
- :is-loader-active="successCtx?.isLoaderActive"
325
- modifier-classes="is-primary"
326
- icon-class="step-forward"
327
- @click="successCtx?.onSuccessBtnClick"
328
- />
329
- <ac-button
330
- v-if="showReportButton"
331
- title="Report Issue"
332
- modifier-classes="is-danger"
333
- icon-class="external-link"
334
- @click="onReportIssueClick"
335
- />
336
- </template>
337
- </modal>
338
- </template>
339
-
340
- <style scoped lang="scss">
341
- .task-simple-wrapper {
342
- display: flex;
343
- flex-direction: column;
344
- justify-content: space-between;
345
- width: 40vw;
346
- height: 40vh;
347
- .task-cogs-icon {
348
- width: 100%;
349
- height: 70%;
350
- display: flex;
351
- align-items: center;
352
- justify-content: center;
353
- font-size: 20px;
354
- color: $primary;
355
- }
356
- .task-log {
357
- width: 100%;
358
- height: 30%;
359
- display: flex;
360
- flex-direction: column;
361
- align-items: center;
362
- justify-content: center;
363
- .task-title {
364
- span,
365
- i {
366
- font-size: 16px;
367
- }
368
- i {
369
- color: $primary;
370
- &.is-success {
371
- color: $success;
372
- }
373
- &.is-failed {
374
- color: $danger;
375
- }
376
- }
377
- font-weight: 500;
378
- }
379
- span {
380
- font-size: 14px;
381
- }
382
- }
383
- }
384
- .task-complex-wrapper {
385
- display: flex;
386
- flex-direction: row;
387
- justify-content: space-between;
388
- width: 60vw;
389
- height: 60vh;
390
-
391
- .task-list {
392
- width: 25%;
393
- height: 100%;
394
- }
395
- .task-log {
396
- width: 70%;
397
- height: 100%;
398
- border-radius: 4px;
399
- font-size: 13px;
400
- }
401
- }
402
- </style>
@@ -1,151 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, nextTick, ref, toRefs, watch, watchPostEffect } from "vue";
3
- import { Terminal } from "xterm";
4
- import { FitAddon } from "xterm-addon-fit";
5
- import { WebglAddon } from "xterm-addon-webgl";
6
- import { Material, MaterialDark } from "xterm-theme"; //https://github.com/ysk2014/xterm-theme/tree/master/src/iterm
7
-
8
- interface Props {
9
- theme?: string;
10
- logs?: string[];
11
- }
12
-
13
- const props = withDefaults(defineProps<Props>(), {
14
- theme: "light",
15
- logs: () => [],
16
- });
17
- // terminal print logic
18
- const { logs, theme } = toRefs(props);
19
- const lastPrintIdx = ref(0);
20
-
21
- //theme
22
- const bodyBgc = computed(() =>
23
- theme.value === "light" ? "#eaeaea" : "#232322"
24
- );
25
-
26
- // xterm component logic
27
- const terminalRef = ref<HTMLElement>();
28
- const terminal = new Terminal({
29
- windowsMode: false,
30
- theme: theme.value === "light" ? Material : MaterialDark,
31
- });
32
- const fitAddon = new FitAddon();
33
- terminal.loadAddon(fitAddon);
34
- const webGlAddon = new WebglAddon();
35
- webGlAddon.onContextLoss(() => {
36
- webGlAddon.dispose();
37
- });
38
- watchPostEffect(() => {
39
- if (terminalRef.value) {
40
- terminal.open(terminalRef.value);
41
- fitAddon.fit();
42
- terminal.focus();
43
- terminal.loadAddon(webGlAddon);
44
- }
45
- });
46
- function writeOnTerminal(msg: string) {
47
- const lines = msg.split("\n");
48
- lines.forEach((line, index) => {
49
- if (lines.length === 1 || index < lines.length - 1) terminal.writeln(line);
50
- else terminal.write(line);
51
- });
52
- }
53
-
54
- watch(
55
- logs,
56
- (n) => {
57
- if (n.length > lastPrintIdx.value) {
58
- nextTick(() => {
59
- writeOnTerminal(n.slice(lastPrintIdx.value).join("\n"));
60
- lastPrintIdx.value = n.length;
61
- });
62
- }
63
- },
64
- { immediate: true, deep: true }
65
- );
66
- </script>
67
-
68
- <template>
69
- <div ref="terminalRef" class="terminal-body"></div>
70
- </template>
71
-
72
- <style lang="scss">
73
- .terminal-body {
74
- width: 100%;
75
- height: 100%;
76
- background-color: v-bind(bodyBgc);
77
- padding: 5px 0px 0px 10px;
78
-
79
- // for terminal scroll bar style
80
- .xterm .xterm-viewport {
81
- &::-webkit-scrollbar {
82
- border-radius: 50px;
83
- width: 3px;
84
- }
85
-
86
- &::-moz-scrollbar {
87
- border-radius: 50px;
88
- width: 3px;
89
- }
90
-
91
- &::-ms-scrollbar {
92
- border-radius: 50px;
93
- width: 3px;
94
- }
95
-
96
- &::-webkit-scrollbar:hover {
97
- width: 7px;
98
- }
99
-
100
- &::-moz-scrollbar:hover {
101
- width: 7px;
102
- }
103
-
104
- &::-ms-scrollbar:hover {
105
- width: 7px;
106
- }
107
-
108
- &::-webkit-scrollbar-thumb {
109
- background-color: #929292;
110
- border-radius: 50px;
111
- height: 2px !important;
112
- }
113
-
114
- &::-moz-scrollbar-thumb {
115
- background-color: #929292;
116
- border-radius: 50px;
117
- height: 2px !important;
118
- }
119
-
120
- &::-ms-scrollbar-thumb {
121
- background-color: #929292;
122
- border-radius: 50px;
123
- height: 2px !important;
124
- }
125
-
126
- &::-webkit-scrollbar-thumb:hover {
127
- background-color: #929292;
128
- }
129
-
130
- &::-moz-scrollbar-thumb:hover {
131
- background-color: #929292;
132
- }
133
-
134
- &::-ms-scrollbar-thumb:hover {
135
- background-color: #929292;
136
- }
137
-
138
- &:hover::-webkit-scrollbar-corner {
139
- height: 40px;
140
- }
141
-
142
- &:hover::-moz-scrollbar-corner {
143
- height: 40px;
144
- }
145
-
146
- &:hover::-ms-scrollbar-corner {
147
- height: 40px;
148
- }
149
- }
150
- }
151
- </style>