DashboardComponentMostRecentExecution.vue 3.6 KB

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