1
0

LongJobs.vue 4.7 KB

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