ExecutionLogsTable.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template>
  2. <Table
  3. :data="logs"
  4. :headers="headers"
  5. :table-id="tableId"
  6. :show-pagination="false"
  7. :filterable="false"
  8. :loading="loading"
  9. row-key="executionTrackingId"
  10. >
  11. <template #cell-timestamp="{ row }">
  12. <span class="timestamp">{{ formatTimestamp(row.datetimeStarted) }}</span>
  13. </template>
  14. <template
  15. v-if="variant === 'action-history'"
  16. #cell-duration="{ row }"
  17. >
  18. <span class="duration">
  19. <slot
  20. name="duration"
  21. :row="row"
  22. >
  23. {{ row.duration }}
  24. </slot>
  25. </span>
  26. </template>
  27. <template #cell-executionId="{ row }">
  28. <router-link
  29. :to="`/logs/${row.executionTrackingId}`"
  30. class="execution-id-link"
  31. >
  32. <LogActionTitle
  33. :justification="variant === 'action-history' ? row.justification : ''"
  34. >
  35. {{ row.executionTrackingId }}
  36. </LogActionTitle>
  37. </router-link>
  38. </template>
  39. <template
  40. v-if="variant === 'standard'"
  41. #cell-action="{ row }"
  42. >
  43. <ActionIconGlyph
  44. class="icon"
  45. :glyph="row.actionIcon"
  46. />
  47. <router-link
  48. v-if="row.bindingId"
  49. :to="{ name: 'ActionDetails', params: { actionId: row.bindingId } }"
  50. >
  51. <LogActionTitle
  52. :action-title="row.actionTitle"
  53. :justification="row.justification"
  54. />
  55. </router-link>
  56. <LogActionTitle
  57. v-else
  58. :action-title="row.actionTitle"
  59. :justification="row.justification"
  60. />
  61. <span
  62. v-if="row.entityPrefix"
  63. class="queue-entity annotation"
  64. >
  65. {{ row.entityPrefix }}
  66. </span>
  67. </template>
  68. <template #cell-metadata="{ row }">
  69. <span class="tags">
  70. <span class="annotation">
  71. <span class="annotation-key">User:</span>
  72. <span class="annotation-val">{{ row.user }}</span>
  73. </span>
  74. <span
  75. v-if="row.tags && row.tags.length > 0"
  76. class="tag-list"
  77. >
  78. <span
  79. v-for="tag in row.tags"
  80. :key="tag"
  81. class="tag"
  82. >{{ tag }}</span>
  83. </span>
  84. </span>
  85. </template>
  86. <template #cell-status="{ row }">
  87. <span class="exit-code">
  88. <span
  89. v-if="variant === 'standard' && row.queuePosition != null && !row.executionFinished && !row.executionStarted"
  90. class="queue-position"
  91. >
  92. {{ t('logs.queue-position', { position: row.queuePosition }) }}
  93. </span>
  94. <ActionStatusDisplay
  95. :log-entry="row"
  96. :link-queued-status="true"
  97. />
  98. </span>
  99. </template>
  100. </Table>
  101. </template>
  102. <script setup>
  103. import { computed } from 'vue'
  104. import { useI18n } from 'vue-i18n'
  105. import Table from 'picocrank/vue/components/Table.vue'
  106. import ActionIconGlyph from './ActionIconGlyph.vue'
  107. import ActionStatusDisplay from './ActionStatusDisplay.vue'
  108. import LogActionTitle from './LogActionTitle.vue'
  109. const props = defineProps({
  110. logs: {
  111. type: Array,
  112. required: true
  113. },
  114. variant: {
  115. type: String,
  116. default: 'standard',
  117. validator: value => ['standard', 'action-history'].includes(value)
  118. },
  119. loading: {
  120. type: Boolean,
  121. default: false
  122. }
  123. })
  124. const { t } = useI18n()
  125. const tableId = computed(() =>
  126. props.variant === 'action-history'
  127. ? 'olivetin-execution-logs-action-history'
  128. : 'olivetin-execution-logs-standard'
  129. )
  130. const headers = computed(() => {
  131. if (props.variant === 'action-history') {
  132. return [
  133. { key: 'timestamp', label: 'Timestamp', sortable: false },
  134. { key: 'duration', label: 'Duration', sortable: false },
  135. { key: 'executionId', label: 'Execution ID', sortable: false },
  136. { key: 'metadata', label: 'Metadata', sortable: false },
  137. { key: 'status', label: 'Status', sortable: false }
  138. ]
  139. }
  140. return [
  141. { key: 'timestamp', label: t('logs.timestamp'), sortable: false },
  142. { key: 'action', label: t('logs.action'), sortable: false },
  143. { key: 'executionId', label: t('logs.execution-id'), sortable: false },
  144. { key: 'metadata', label: t('logs.metadata'), sortable: false },
  145. { key: 'status', label: t('logs.status'), sortable: false }
  146. ]
  147. })
  148. function formatTimestamp (timestamp) {
  149. if (!timestamp) {
  150. return 'Unknown'
  151. }
  152. try {
  153. return new Date(timestamp).toLocaleString()
  154. } catch (err) {
  155. return timestamp
  156. }
  157. }
  158. </script>
  159. <style scoped>
  160. .timestamp {
  161. font-family: monospace;
  162. font-size: 0.875rem;
  163. color: var(--muted-text-color, #666);
  164. }
  165. .duration {
  166. font-size: 0.9rem;
  167. color: var(--muted-text-color, #666);
  168. white-space: nowrap;
  169. }
  170. .icon {
  171. margin-right: 0.5rem;
  172. font-size: 1.2em;
  173. }
  174. .annotation {
  175. font-weight: 500;
  176. font-size: smaller;
  177. }
  178. .queue-entity {
  179. display: block;
  180. margin-top: 0.25rem;
  181. color: var(--muted-text-color, #666);
  182. }
  183. .tags {
  184. display: flex;
  185. flex-wrap: wrap;
  186. gap: 0.5rem;
  187. }
  188. .exit-code {
  189. display: flex;
  190. align-items: center;
  191. gap: 0.5rem;
  192. }
  193. .queue-position {
  194. white-space: nowrap;
  195. }
  196. .execution-id-link {
  197. font-family: monospace;
  198. font-size: 0.875rem;
  199. text-decoration: none;
  200. }
  201. .execution-id-link:hover {
  202. text-decoration: underline;
  203. }
  204. </style>