Просмотр исходного кода

Feature search logs (#182)

* feature: search logs wip

* feature: Log search
James Read 2 лет назад
Родитель
Сommit
912fd8089e
4 измененных файлов с 55 добавлено и 5 удалено
  1. 5 1
      webui/index.html
  2. 2 1
      webui/js/marshaller.js
  3. 27 0
      webui/main.js
  4. 21 3
      webui/style.css

+ 5 - 1
webui/index.html

@@ -35,9 +35,13 @@
 			</div>
 
 			<section id = "contentLogs" title = "Logs" hidden>
+				<div class = "toolbar">
+					<input placeholder = "Search for action name" id = "logSearchBox" />
+					<button id = "searchLogsClear" title = "Clear search filter" disabled>X</button>
+				</div>
 				<table title = "Logs">
 					<thead>
-						<tr>
+						<tr title = "untitled">
 							<th>Timestamp</th>
 							<th>Log</th>
 							<th>Exit Code</th>

+ 2 - 1
webui/js/marshaller.js

@@ -30,7 +30,7 @@ export function marshalActionButtonsJsonToHtml (json) {
 export function marshalLogsJsonToHtml (json) {
   for (const logEntry of json.logs) {
     const tpl = document.getElementById('tplLogRow')
-    const row = tpl.content.cloneNode(true)
+    const row = tpl.content.querySelector('tr').cloneNode(true)
 
     if (logEntry.stdout.length === 0) {
       logEntry.stdout = '(empty)'
@@ -56,6 +56,7 @@ export function marshalLogsJsonToHtml (json) {
     row.querySelector('pre.stdout').innerText = logEntry.stdout
     row.querySelector('pre.stderr').innerText = logEntry.stderr
     row.querySelector('.exit-code').innerText = logTableExitCode
+    row.setAttribute('title', logEntry.actionTitle)
 
     for (const tag of logEntry.tags) {
       const domTag = document.createElement('span')

+ 27 - 0
webui/main.js

@@ -3,6 +3,27 @@
 import { marshalActionButtonsJsonToHtml, marshalLogsJsonToHtml } from './js/marshaller.js'
 import { checkWebsocketConnection } from './js/websocket.js'
 
+function searchLogs (e) {
+  document.getElementById('searchLogsClear').disabled = false
+
+  const searchText = e.target.value.toLowerCase()
+
+  for (const row of document.querySelectorAll('tr.log-row')) {
+    const actionTitle = row.getAttribute('title').toLowerCase()
+
+    row.hidden = !actionTitle.includes(searchText)
+  }
+}
+
+function searchLogsClear () {
+  for (const row of document.querySelectorAll('tr.log-row')) {
+    row.hidden = false
+  }
+
+  document.getElementById('searchLogsClear').disabled = true
+  document.getElementById('logSearchBox').value = ''
+}
+
 function showSection (name) {
   for (const otherName of ['Actions', 'Logs']) {
     document.getElementById('show' + otherName).classList.remove('activeSection')
@@ -22,6 +43,11 @@ function setupSections () {
   showSection('Actions')
 }
 
+function setupLogSearchBox () {
+  document.getElementById('logSearchBox').oninput = searchLogs
+  document.getElementById('searchLogsClear').onclick = searchLogsClear
+}
+
 function refreshLoop () {
   if (window.websocketAvailable) {
     // Websocket updates are streamed live, not updated on a loop.
@@ -117,6 +143,7 @@ function processWebuiSettingsJson (settings) {
 
 function main () {
   setupSections()
+  setupLogSearchBox()
 
   window.fetch('webUiSettings.json').then(res => {
     return res.json()

+ 21 - 3
webui/style.css

@@ -232,9 +232,9 @@ action-button details summary div span:first-child {
   padding: 0.2em;
   background-color: #efefef;
   border-top: 0;
-  border-left: 1px solid #999;
-  border-right: 1px solid #999;
-  border-bottom: 1px solid #999;
+  border-left: 1px solid #666;
+  border-right: 1px solid #666;
+  border-bottom: 1px solid #666;
 }
 
 execution-button button {
@@ -404,6 +404,20 @@ span.tag {
   padding: 0.2em;
 }
 
+div.toolbar {
+  padding: .4em;
+  text-align: left;
+  background-color: #efefef;
+  border: 1px solid #999;
+  border-bottom: 0;
+  display: flex;
+  flex-direction: row;
+}
+
+div.toolbar * {
+  margin-right: 1em;
+}
+
 @media (prefers-color-scheme: dark) {
   body {
     background-color: #333;
@@ -472,4 +486,8 @@ span.tag {
   tr:hover td {
     background-color: #666;
   }
+  
+  div.toolbar {
+    background-color: black;
+  }
 }