DashboardComponentMostRecentExecution.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <template>
  2. <div class="mre-container" :class="component.cssClass">
  3. <router-link
  4. v-if="executionTrackingId"
  5. :to="`/logs/${executionTrackingId}`"
  6. class="mre-link"
  7. >
  8. <pre class="mre-output">{{ output }}</pre>
  9. </router-link>
  10. <pre v-else class="mre-output fg-important">{{ output }}</pre>
  11. </div>
  12. </template>
  13. <script setup>
  14. import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
  15. import { buttonResults } from '../stores/buttonResults'
  16. const props = defineProps({
  17. component: {
  18. type: Object,
  19. required: true
  20. }
  21. })
  22. const output = ref('Waiting...')
  23. const executionTrackingId = ref(null)
  24. let unwatchButtonResults = null
  25. function updateFromLogEntry(logEntry) {
  26. if (logEntry) {
  27. if (logEntry.output !== undefined) {
  28. output.value = logEntry.output
  29. } else {
  30. output.value = 'No output available'
  31. }
  32. if (logEntry.executionTrackingId) {
  33. executionTrackingId.value = logEntry.executionTrackingId
  34. }
  35. }
  36. }
  37. async function fetchMostRecentExecution() {
  38. if (!props.component.title) {
  39. output.value = 'Error: No action ID specified'
  40. executionTrackingId.value = null
  41. return
  42. }
  43. if (!window.client) {
  44. output.value = 'Error: Client not initialized'
  45. executionTrackingId.value = null
  46. return
  47. }
  48. try {
  49. const executionStatusArgs = {
  50. actionId: props.component.title
  51. }
  52. const result = await window.client.executionStatus(executionStatusArgs)
  53. if (result.logEntry) {
  54. updateFromLogEntry(result.logEntry)
  55. } else {
  56. output.value = 'No output available'
  57. executionTrackingId.value = null
  58. }
  59. } catch (err) {
  60. if (err.code === 'NotFound' || err.status === 404) {
  61. output.value = 'No execution found'
  62. executionTrackingId.value = null
  63. } else {
  64. output.value = 'Error: ' + (err.message || 'Failed to fetch execution')
  65. console.error('Failed to fetch most recent execution:', err)
  66. executionTrackingId.value = null
  67. }
  68. }
  69. }
  70. onMounted(() => {
  71. fetchMostRecentExecution()
  72. unwatchButtonResults = watch(
  73. buttonResults,
  74. () => {
  75. // Find the most recent finished execution for this bindingId
  76. const bindingId = props.component.title
  77. let mostRecent = null
  78. let mostRecentTime = null
  79. for (const trackingId in buttonResults) {
  80. const logEntry = buttonResults[trackingId]
  81. if (logEntry && logEntry.bindingId === bindingId && logEntry.executionFinished) {
  82. const finishedTime = new Date(logEntry.datetimeFinished)
  83. if (!mostRecent || finishedTime > mostRecentTime) {
  84. mostRecent = logEntry
  85. mostRecentTime = finishedTime
  86. }
  87. }
  88. }
  89. if (mostRecent) {
  90. updateFromLogEntry(mostRecent)
  91. }
  92. },
  93. { deep: true }
  94. )
  95. })
  96. onBeforeUnmount(() => {
  97. if (unwatchButtonResults) {
  98. unwatchButtonResults()
  99. }
  100. })
  101. </script>
  102. <style scoped>
  103. .mre-container {
  104. display: grid;
  105. grid-column: span 2;
  106. }
  107. .mre-link {
  108. text-decoration: none;
  109. color: inherit;
  110. display: grid;
  111. cursor: pointer;
  112. grid-column: span 2;
  113. }
  114. .mre-link:hover .mre-output {
  115. border-color: #999;
  116. }
  117. .mre-output {
  118. box-shadow: 0 0 .6em #aaa;
  119. border: 1px dashed #ccc;
  120. border-radius: .7em;
  121. padding: 1em;
  122. margin: 0;
  123. min-height: 0;
  124. white-space: pre-wrap;
  125. word-wrap: break-word;
  126. font-family: monospace;
  127. font-size: 0.9em;
  128. overflow-x: auto;
  129. overflow-y: auto;
  130. transition: border-color 0.2s ease;
  131. max-height: 20em;
  132. }
  133. @media (prefers-color-scheme: dark) {
  134. .mre-output {
  135. border: 1px dashed #444;
  136. box-shadow: 0 0 .6em #444;
  137. }
  138. .mre-link:hover .mre-output {
  139. border-color: #666;
  140. }
  141. }
  142. </style>