LongJobs.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <script setup lang="ts">
  2. import { useStore } from "vuex";
  3. import { defineAsyncComponent, ref, computed, onMounted } from "vue";
  4. const FloatingBox = defineAsyncComponent(
  5. () => import("@/components/FloatingBox.vue")
  6. );
  7. const store = useStore();
  8. const body = ref(document.body);
  9. const loggedIn = computed(() => store.state.user.auth.loggedIn);
  10. const activeJobs = computed(() => store.state.longJobs.activeJobs);
  11. const { socket } = store.state.websockets;
  12. const setJob = payload => store.dispatch("longJobs/setJob", payload);
  13. const setJobs = payload => store.dispatch("longJobs/setJobs", payload);
  14. const removeJob = payload => store.dispatch("longJobs/removeJob", payload);
  15. const remove = job => {
  16. if (job.status === "success" || job.status === "error") {
  17. socket.dispatch("users.removeLongJob", job.id, res => {
  18. if (res.status === "success") {
  19. removeJob(job.id);
  20. } else console.log(res.message);
  21. });
  22. }
  23. };
  24. onMounted(() => {
  25. if (loggedIn.value) {
  26. socket.dispatch("users.getLongJobs", {
  27. cb: res => {
  28. if (res.status === "success") {
  29. setJobs(res.data.longJobs);
  30. } else console.log(res.message);
  31. },
  32. onProgress: res => {
  33. setJob(res);
  34. }
  35. });
  36. socket.on("keep.event:longJob.removed", ({ data }) => {
  37. removeJob(data.jobId);
  38. });
  39. socket.on("keep.event:longJob.added", ({ data }) => {
  40. if (
  41. !activeJobs.value.find(activeJob => activeJob.id === data.jobId)
  42. )
  43. socket.dispatch("users.getLongJob", data.jobId, {
  44. cb: res => {
  45. if (res.status === "success") {
  46. setJob(res.data.longJob);
  47. } else console.log(res.message);
  48. },
  49. onProgress: res => {
  50. setJob(res);
  51. }
  52. });
  53. });
  54. }
  55. });
  56. </script>
  57. <template>
  58. <floating-box
  59. v-if="activeJobs.length > 0"
  60. title="Jobs"
  61. id="longJobs"
  62. ref="longJobs"
  63. :persist="true"
  64. initial="align-bottom"
  65. :min-width="200"
  66. :max-width="400"
  67. :min-height="200"
  68. >
  69. <template #body>
  70. <div class="active-jobs">
  71. <div
  72. v-for="job in activeJobs"
  73. :key="`activeJob-${job.id}`"
  74. class="active-job"
  75. >
  76. <i
  77. v-if="
  78. job.status === 'started' || job.status === 'update'
  79. "
  80. class="material-icons"
  81. content="In Progress"
  82. v-tippy="{ theme: 'info', placement: 'right' }"
  83. >
  84. pending
  85. </i>
  86. <i
  87. v-else-if="job.status === 'success'"
  88. class="material-icons success"
  89. content="Complete"
  90. v-tippy="{ theme: 'info', placement: 'right' }"
  91. >
  92. check_circle
  93. </i>
  94. <i
  95. v-else
  96. class="material-icons error"
  97. content="Failed"
  98. v-tippy="{ theme: 'info', placement: 'right' }"
  99. >
  100. error
  101. </i>
  102. <div class="name" :title="job.name">{{ job.name }}</div>
  103. <div class="actions">
  104. <i
  105. class="material-icons clear"
  106. :class="{
  107. disabled: !(
  108. job.status === 'success' ||
  109. job.status === 'error'
  110. )
  111. }"
  112. content="Clear"
  113. v-tippy="{ placement: 'left' }"
  114. @click="remove(job)"
  115. >
  116. remove_circle
  117. </i>
  118. <tippy
  119. :touch="true"
  120. :interactive="true"
  121. placement="left"
  122. ref="longJobMessage"
  123. :append-to="body"
  124. >
  125. <i class="material-icons">chat</i>
  126. <template #content>
  127. <div class="long-job-message">
  128. <strong>Latest Update:</strong>
  129. <span :title="job.message">{{
  130. job.message
  131. }}</span>
  132. </div>
  133. </template>
  134. </tippy>
  135. </div>
  136. </div>
  137. </div>
  138. </template>
  139. </floating-box>
  140. </template>
  141. <style lang="less" scoped>
  142. .night-mode {
  143. #longJobs {
  144. .active-jobs {
  145. .active-job {
  146. background-color: var(--dark-grey);
  147. border: 0;
  148. }
  149. }
  150. }
  151. .long-job-message {
  152. color: var(--black);
  153. }
  154. }
  155. #longJobs {
  156. z-index: 5000 !important;
  157. .active-jobs {
  158. .active-job {
  159. display: flex;
  160. padding: 5px;
  161. margin: 5px 0;
  162. border: 1px solid var(--light-grey-3);
  163. border-radius: @border-radius;
  164. &:first-child {
  165. margin-top: 0;
  166. }
  167. &:last-child {
  168. margin-bottom: 0;
  169. }
  170. .name {
  171. line-height: 24px;
  172. font-weight: 600;
  173. text-transform: capitalize;
  174. text-overflow: ellipsis;
  175. white-space: nowrap;
  176. overflow: hidden;
  177. margin-right: auto;
  178. }
  179. .material-icons {
  180. font-size: 20px;
  181. color: var(--primary-color);
  182. margin: auto 5px auto 0;
  183. cursor: pointer;
  184. &.success {
  185. color: var(--green);
  186. }
  187. &.error,
  188. &.clear {
  189. color: var(--red);
  190. }
  191. &.disabled {
  192. color: var(--light-grey-3);
  193. cursor: not-allowed;
  194. }
  195. }
  196. .actions {
  197. display: flex;
  198. .material-icons {
  199. margin: auto 0 auto 5px;
  200. }
  201. & > span {
  202. display: flex;
  203. padding: 0;
  204. }
  205. }
  206. }
  207. }
  208. }
  209. .long-job-message {
  210. display: flex;
  211. flex-direction: column;
  212. strong {
  213. font-size: 12px;
  214. }
  215. span {
  216. display: -webkit-inline-box;
  217. text-overflow: ellipsis;
  218. white-space: normal;
  219. -webkit-box-orient: vertical;
  220. -webkit-line-clamp: 3;
  221. overflow: hidden;
  222. max-width: 200px;
  223. font-size: 12px;
  224. }
  225. }
  226. </style>