11<!DOCTYPE html>
22< html lang ="en ">
3+
34< head >
45 < meta charset ="UTF-8 " />
56 < title > LabReportAPI Logs</ title >
67 < style >
7- body { font-family : sans-serif; padding : 2rem ; }
8- table { width : 100% ; border-collapse : collapse; }
9- th , td { border : 1px solid # ddd ; padding : 8px ; }
10- th { background-color : # f2f2f2 ; }
11- tr : nth-child (even) { background-color : # f9f9f9 ; }
8+ body {
9+ font-family : sans-serif;
10+ padding : 2rem ;
11+ }
12+
13+ table {
14+ width : 100% ;
15+ border-collapse : collapse;
16+ }
17+
18+ th ,
19+ td {
20+ border : 1px solid # ddd ;
21+ padding : 8px ;
22+ }
23+
24+ th {
25+ background-color : # f2f2f2 ;
26+ }
27+
28+ tr : nth-child (even) {
29+ background-color : # f9f9f9 ;
30+ }
31+
32+ button {
33+ margin-right : 10px ;
34+ padding : 0.5rem 1rem ;
35+ }
36+
37+ pre {
38+ background : # f4f4f4 ;
39+ padding : 1rem ;
40+ font-family : monospace;
41+ border : 1px solid # ccc ;
42+ }
43+
44+ /* Dark mode base styles (inactive by default) */
45+ body .dark {
46+ background-color : # 1e1e1e ;
47+ color : # e0e0e0 ;
48+ }
49+
50+ body .dark table ,
51+ body .dark th ,
52+ body .dark td {
53+ border-color : # 444 ;
54+ }
55+
56+ /*
57+ label {
58+ color: #000;
59+ }
60+
61+ body.dark label {
62+ color: #e0e0e0;
63+ } */
64+
65+ body .dark th {
66+ background-color : # 2d2d2d ;
67+ }
68+
69+ body .dark tr : nth-child (even) {
70+ background-color : # 2a2a2a ;
71+ }
72+
73+ body .dark pre {
74+ background : # 2d2d2d ;
75+ border-color : # 444 ;
76+ }
77+
78+ /* Filter and dark mode toggle styles */
79+ .filter-container {
80+ display : flex;
81+ align-items : center;
82+ justify-content : space-between;
83+ margin : 1rem 0 1.5rem ;
84+ padding : 0.5rem 1rem ;
85+ background-color : # f2f2f2 ;
86+ border-radius : 8px ;
87+ }
88+
89+ .filter-group {
90+ display : flex;
91+ align-items : center;
92+ gap : 0.75rem ;
93+ }
94+
95+ select {
96+ padding : 0.4rem 1rem ;
97+ border-radius : 6px ;
98+ border : 1px solid # ccc ;
99+ font-size : 1rem ;
100+ background-color : # fff ;
101+ }
102+
103+ body .dark select {
104+ background-color : # 2d2d2d ;
105+ color : # e0e0e0 ;
106+ border : 1px solid # 666 ;
107+ }
108+
109+ # themeToggleBtn {
110+ padding : 0.5rem 1rem ;
111+ font-size : 1rem ;
112+ border : none;
113+ border-radius : 6px ;
114+ cursor : pointer;
115+ background-color : # 333 ;
116+ color : # fff ;
117+ transition : background 0.3s ;
118+ }
119+
120+ body .dark # themeToggleBtn {
121+ background-color : # eee ;
122+ color : # 111 ;
123+ }
12124 </ style >
13125</ head >
126+
14127< body >
15128 < h1 > TCP Listener Logs</ h1 >
129+
130+ <!-- Buttons -->
131+ < button onclick ="manualSave() "> Manual Save</ button >
132+ < button onclick ="fetchSystemStatus() "> Refresh System Status</ button >
133+ < button id ="themeToggleBtn " onclick ="toggleTheme() "> Toggle Dark Mode</ button >
134+
135+ <!-- System Status Display -->
136+ < h2 > System Status</ h2 >
137+ < pre id ="logStatus "> Loading...</ pre >
138+
139+ <!-- Log Entries Table -->
140+ < h2 > Log Entries</ h2 >
141+ < div class ="filter-container ">
142+ < div class ="filter-group ">
143+ < label for ="logLevelFilter " style ="color: #2a2a2a; "> Filter by Level:</ label >
144+ < select id ="logLevelFilter " onchange ="fetchLogs() ">
145+ < option value =""> All</ option >
146+ < option value ="INFO "> INFO</ option >
147+ < option value ="WARNING "> WARN</ option >
148+ < option value ="ERROR "> ERROR</ option >
149+ </ select >
150+ </ div >
151+ </ div >
152+
153+
16154 < table >
17155 < thead >
18156 < tr >
@@ -26,27 +164,109 @@ <h1>TCP Listener Logs</h1>
26164 </ table >
27165
28166 < script >
167+ // Manual save
168+ async function manualSave ( ) {
169+ try {
170+ const response = await fetch ( 'api/labdata/save' , { method : 'POST' } ) ;
171+ const text = await response . text ( ) ;
172+ alert ( 'Manual save triggered: ' + response . status + ' - ' + text ) ;
173+ } catch ( error ) {
174+ alert ( 'Failed to send POST request: ' + error ) ;
175+ }
176+ }
177+
178+ // Fetch system status
179+ async function fetchSystemStatus ( ) {
180+ try {
181+ const response = await fetch ( 'api/labdata/status' ) ;
182+ if ( ! response . ok ) throw new Error ( "Server error: " + response . status ) ;
183+
184+ const data = await response . json ( ) ;
185+
186+ const statusText = `
187+ Status: ${ data . status }
188+ Protocol: ${ data . protocol }
189+ Port: ${ data . port }
190+ Server IP: ${ data . serverIp }
191+ Last Message Time: ${ new Date ( data . lastMessageReceivedAt ) . toLocaleString ( ) }
192+ Last Write Status: ${ data . lastWriteStatus }
193+ Last Write Time: ${ data . lastWriteTime === "0001-01-01T00:00:00" ? "N/A" : new Date ( data . lastWriteTime ) . toLocaleString ( ) }
194+ ` ;
195+ document . getElementById ( "logStatus" ) . textContent = statusText . trim ( ) ;
196+
197+ } catch ( error ) {
198+ document . getElementById ( "logStatus" ) . textContent = "Failed to fetch system status: " + error . message ;
199+ }
200+ }
201+
202+ // Fetch logs
29203 async function fetchLogs ( ) {
30- const response = await fetch ( '/api/logs' ) ;
31- const logs = await response . json ( ) ;
204+ try {
205+ const response = await fetch ( 'api/logs' ) ;
206+ if ( ! response . ok ) throw new Error ( "Server error: " + response . status ) ;
32207
208+ const logs = await response . json ( ) ;
209+ displayLogs ( logs ?? [ ] ) ;
210+ } catch ( error ) {
211+ console . error ( "Failed to fetch logs: " + error . message ) ;
212+ }
213+ }
214+
215+ function displayLogs ( logs ) {
33216 const table = document . getElementById ( 'logTable' ) ;
34- table . innerHTML = '' ; // clear previous
217+ table . innerHTML = '' ; // Clear previous
218+
219+ const selectedLevel = document . getElementById ( 'logLevelFilter' ) . value ;
35220
36- logs . reverse ( ) . forEach ( log => {
221+ // Apply level filter if selected
222+ let filtered = logs ;
223+ if ( selectedLevel ) {
224+ filtered = logs . filter ( log => log . level === selectedLevel ) ;
225+ }
226+
227+ // Limit to latest 100 logs
228+ const recentLogs = filtered . slice ( - 100 ) . reverse ( ) ;
229+
230+ if ( recentLogs . length === 0 ) {
231+ const row = document . createElement ( 'tr' ) ;
232+ row . innerHTML = `<td colspan="4" style="text-align:center;">No log entries available</td>` ;
233+ table . appendChild ( row ) ;
234+ return ;
235+ }
236+
237+ recentLogs . forEach ( log => {
37238 const row = document . createElement ( 'tr' ) ;
38239 row . innerHTML = `
39- <td>${ new Date ( log . timestamp ) . toLocaleString ( ) } </td>
40- <td>${ log . level } </td>
41- <td>${ log . message } </td>
42- <td>${ log . context ?? '' } </td>
43- ` ;
240+ <td>${ new Date ( log . timestamp ) . toLocaleString ( ) } </td>
241+ <td>${ log . level } </td>
242+ <td>${ log . message } </td>
243+ <td>${ log . context ?? '' } </td>
244+ ` ;
44245 table . appendChild ( row ) ;
45246 } ) ;
46247 }
47248
249+ function toggleTheme ( ) {
250+ const isDark = document . body . classList . toggle ( 'dark' ) ;
251+ document . getElementById ( 'themeToggleBtn' ) . textContent = isDark
252+ ? "Toggle Light Mode"
253+ : "Toggle Dark Mode" ;
254+ localStorage . setItem ( 'theme' , isDark ? 'dark' : 'light' ) ;
255+ }
256+
257+
258+ // Load everything on page load
259+ fetchSystemStatus ( ) ;
48260 fetchLogs ( ) ;
49- setInterval ( fetchLogs , 5000 ) ; // refresh every 5 seconds
261+ setInterval ( fetchLogs , 5000 ) ; // Refresh logs every 5 seconds
262+ // Load saved theme
263+ const savedTheme = localStorage . getItem ( 'theme' ) ;
264+ if ( savedTheme === 'dark' ) {
265+ document . body . classList . add ( 'dark' ) ;
266+ document . getElementById ( 'themeToggleBtn' ) . textContent = "Toggle Light Mode" ;
267+ }
268+
50269 </ script >
51270</ body >
52- </ html >
271+
272+ </ html >
0 commit comments