@@ -14,6 +14,12 @@ window.LogsModule = {
1414 autoScrollWasEnabled : false ,
1515 userTimezone : null , // Cache for user's timezone setting
1616
17+ // Pagination state
18+ currentPage : 1 ,
19+ totalPages : 1 ,
20+ pageSize : 20 ,
21+ totalLogs : 0 ,
22+
1723 // Element references
1824 elements : { } ,
1925
@@ -23,6 +29,11 @@ window.LogsModule = {
2329 this . cacheElements ( ) ;
2430 this . loadUserTimezone ( ) ;
2531 this . setupEventListeners ( ) ;
32+
33+ // Load initial logs for the default app without resetting pagination
34+ console . log ( '[LogsModule] Loading initial logs...' ) ;
35+ this . loadLogsFromAPI ( this . currentLogApp ) ;
36+ this . setupLogPolling ( this . currentLogApp ) ;
2637 } ,
2738
2839 // Load user's timezone setting from the backend
@@ -141,6 +152,13 @@ window.LogsModule = {
141152 this . elements . currentLogApp = document . getElementById ( 'current-log-app' ) ;
142153 this . elements . logDropdownBtn = document . querySelector ( '.log-dropdown-btn' ) ;
143154 this . elements . logDropdownContent = document . querySelector ( '.log-dropdown-content' ) ;
155+
156+ // Pagination elements
157+ this . elements . logsPrevPage = document . getElementById ( 'logsPrevPage' ) ;
158+ this . elements . logsNextPage = document . getElementById ( 'logsNextPage' ) ;
159+ this . elements . logsCurrentPage = document . getElementById ( 'logsCurrentPage' ) ;
160+ this . elements . logsTotalPages = document . getElementById ( 'logsTotalPages' ) ;
161+ this . elements . logsPageSize = document . getElementById ( 'logsPageSize' ) ;
144162 } ,
145163
146164 // Set up event listeners for logging functionality
@@ -219,6 +237,19 @@ window.LogsModule = {
219237 this . handleLogOptionChange ( app ) ;
220238 } ) ;
221239 }
240+
241+ // Pagination event listeners
242+ if ( this . elements . logsPrevPage ) {
243+ this . elements . logsPrevPage . addEventListener ( 'click' , ( ) => this . handlePagination ( 'prev' ) ) ;
244+ }
245+
246+ if ( this . elements . logsNextPage ) {
247+ this . elements . logsNextPage . addEventListener ( 'click' , ( ) => this . handlePagination ( 'next' ) ) ;
248+ }
249+
250+ if ( this . elements . logsPageSize ) {
251+ this . elements . logsPageSize . addEventListener ( 'change' , ( ) => this . handlePageSizeChange ( ) ) ;
252+ }
222253 } ,
223254
224255 // Handle log option dropdown changes
@@ -228,7 +259,12 @@ window.LogsModule = {
228259 } else if ( app && app . target && typeof app . target . getAttribute === 'function' ) {
229260 app = app . target . getAttribute ( 'data-app' ) ;
230261 }
231- if ( ! app || app === this . currentLogApp ) return ;
262+ if ( ! app || app === this . currentLogApp ) {
263+ console . log ( `[LogsModule] handleLogOptionChange - no change needed (${ app } === ${ this . currentLogApp } )` ) ;
264+ return ;
265+ }
266+
267+ console . log ( `[LogsModule] handleLogOptionChange - switching from ${ this . currentLogApp } to ${ app } ` ) ;
232268
233269 // Update the select value
234270 const logAppSelect = document . getElementById ( 'logAppSelect' ) ;
@@ -243,12 +279,43 @@ window.LogsModule = {
243279
244280 // Switch to the selected app logs
245281 this . currentLogApp = app ;
282+ this . currentPage = 1 ; // Reset to first page when switching apps
283+ this . clearLogs ( ) ;
284+ this . connectToLogs ( ) ;
285+ } ,
286+
287+ // Handle app changes from external sources (like huntarrUI tab switching)
288+ handleAppChange : function ( app ) {
289+ if ( ! app || app === this . currentLogApp ) {
290+ console . log ( `[LogsModule] handleAppChange - no change needed (${ app } === ${ this . currentLogApp } )` ) ;
291+ return ;
292+ }
293+
294+ console . log ( `[LogsModule] handleAppChange - switching from ${ this . currentLogApp } to ${ app } ` ) ;
295+
296+ // Update the select value
297+ const logAppSelect = document . getElementById ( 'logAppSelect' ) ;
298+ if ( logAppSelect ) logAppSelect . value = app ;
299+
300+ // Update the current log app text with proper capitalization
301+ let displayName = app . charAt ( 0 ) . toUpperCase ( ) + app . slice ( 1 ) ;
302+ if ( app === 'whisparr' ) displayName = 'Whisparr V2' ;
303+ else if ( app === 'eros' ) displayName = 'Whisparr V3' ;
304+
305+ if ( this . elements . currentLogApp ) this . elements . currentLogApp . textContent = displayName ;
306+
307+ // Switch to the selected app logs
308+ this . currentLogApp = app ;
309+ this . currentPage = 1 ; // Reset to first page when switching apps
246310 this . clearLogs ( ) ;
247311 this . connectToLogs ( ) ;
248312 } ,
249313
250314 // Connect to logs stream
251315 connectToLogs : function ( ) {
316+ console . log ( `[LogsModule] connectToLogs() called - currentLogApp: ${ this . currentLogApp } , currentPage: ${ this . currentPage } ` ) ;
317+ console . trace ( '[LogsModule] connectToLogs call stack' ) ;
318+
252319 // Disconnect any existing event sources
253320 this . disconnectAllEventSources ( ) ;
254321
@@ -273,7 +340,8 @@ window.LogsModule = {
273340 this . elements . logConnectionStatus . className = '' ;
274341 }
275342
276- // Load initial logs
343+ // Load logs for the current page (don't always reset to page 1)
344+ console . log ( `[LogsModule] connectEventSource - loading page ${ this . currentPage } for app ${ appType } ` ) ;
277345 this . loadLogsFromAPI ( appType ) ;
278346
279347 // Set up polling with user's configured interval
@@ -298,14 +366,20 @@ window.LogsModule = {
298366
299367 // Set up polling for new logs using the configured interval
300368 this . logPollingInterval = setInterval ( ( ) => {
301- this . loadLogsFromAPI ( appType , true ) ;
369+ // Only poll for new logs when on page 1 (latest logs)
370+ if ( this . currentPage === 1 ) {
371+ this . loadLogsFromAPI ( appType , true ) ;
372+ }
302373 } , intervalMs ) ;
303374 } )
304375 . catch ( error => {
305376 console . error ( '[LogsModule] Error fetching log refresh interval, using default 30 seconds:' , error ) ;
306377 // Fallback to 30 seconds if settings fetch fails
307378 this . logPollingInterval = setInterval ( ( ) => {
308- this . loadLogsFromAPI ( appType , true ) ;
379+ // Only poll for new logs when on page 1 (latest logs)
380+ if ( this . currentPage === 1 ) {
381+ this . loadLogsFromAPI ( appType , true ) ;
382+ }
309383 } , 30000 ) ;
310384 } ) ;
311385 } ,
@@ -315,11 +389,20 @@ window.LogsModule = {
315389 // Use the correct API endpoint - the backend now supports 'all' as an app_type
316390 const apiUrl = `/api/logs/${ appType } ` ;
317391
318- const limit = isPolling ? 20 : 100 ; // Load fewer logs when polling for updates
392+ // For polling, always get latest logs (offset=0, small limit)
393+ // For pagination, use current page and page size
394+ let limit , offset ;
395+ if ( isPolling ) {
396+ limit = 20 ;
397+ offset = 0 ;
398+ } else {
399+ limit = this . pageSize ;
400+ offset = ( this . currentPage - 1 ) * this . pageSize ;
401+ }
319402
320403 // Include level filter in API call if a specific level is selected
321404 const currentLogLevel = this . elements . logLevelSelect ? this . elements . logLevelSelect . value : 'all' ;
322- let apiParams = `limit=${ limit } &offset=0 ` ;
405+ let apiParams = `limit=${ limit } &offset=${ offset } ` ;
323406 if ( currentLogLevel !== 'all' ) {
324407 apiParams += `&level=${ currentLogLevel . toUpperCase ( ) } ` ;
325408 }
@@ -331,6 +414,17 @@ window.LogsModule = {
331414 . then ( data => {
332415 if ( data . success && data . logs ) {
333416 this . processLogsFromAPI ( data . logs , appType , isPolling ) ;
417+
418+ // Update pagination info (only on non-polling requests)
419+ if ( ! isPolling && data . total !== undefined ) {
420+ this . totalLogs = data . total ;
421+ this . totalPages = Math . max ( 1 , Math . ceil ( this . totalLogs / this . pageSize ) ) ;
422+ console . log ( `[LogsModule] Updated pagination: totalLogs=${ this . totalLogs } , totalPages=${ this . totalPages } , currentPage=${ this . currentPage } ` ) ;
423+ this . updatePaginationUI ( ) ;
424+ } else if ( isPolling ) {
425+ console . log ( `[LogsModule] Polling request - not updating pagination. Current: totalLogs=${ this . totalLogs } , totalPages=${ this . totalPages } , currentPage=${ this . currentPage } ` ) ;
426+ }
427+
334428 // Update connection status on successful API call (only on initial load, not polling)
335429 if ( this . elements . logConnectionStatus && ! isPolling ) {
336430 this . elements . logConnectionStatus . textContent = 'Connected' ;
@@ -727,86 +821,13 @@ window.LogsModule = {
727821
728822 // Filter logs by level
729823 filterLogsByLevel : function ( selectedLevel ) {
730- if ( ! this . elements . logsContainer ) return ;
731-
732- const allLogEntries = this . elements . logsContainer . querySelectorAll ( '.log-entry' ) ;
733- let visibleCount = 0 ;
734- let totalCount = allLogEntries . length ;
735-
736- console . log ( `[LogsModule] Filtering logs by level: ${ selectedLevel } , total entries: ${ totalCount } ` ) ;
824+ console . log ( `[LogsModule] Filtering logs by level: ${ selectedLevel } ` ) ;
737825
738- allLogEntries . forEach ( entry => {
739- entry . removeAttribute ( 'data-hidden-by-filter' ) ;
740- } ) ;
826+ // Reset to first page when changing filter
827+ this . currentPage = 1 ;
741828
742- allLogEntries . forEach ( entry => {
743- if ( selectedLevel === 'all' ) {
744- entry . style . display = '' ;
745- visibleCount ++ ;
746- } else {
747- const levelBadge = entry . querySelector ( '.log-level-badge, .log-level, .log-level-error, .log-level-warning, .log-level-info, .log-level-debug' ) ;
748-
749- if ( levelBadge ) {
750- let entryLevel = '' ;
751- const badgeText = levelBadge . textContent . toLowerCase ( ) . trim ( ) ;
752-
753- switch ( badgeText ) {
754- case 'information' :
755- case 'info' :
756- entryLevel = 'info' ;
757- break ;
758- case 'warning' :
759- case 'warn' :
760- entryLevel = 'warning' ;
761- break ;
762- case 'error' :
763- entryLevel = 'error' ;
764- break ;
765- case 'debug' :
766- entryLevel = 'debug' ;
767- break ;
768- case 'fatal' :
769- case 'critical' :
770- entryLevel = 'error' ;
771- break ;
772- default :
773- if ( levelBadge . classList . contains ( 'log-level-error' ) ) {
774- entryLevel = 'error' ;
775- } else if ( levelBadge . classList . contains ( 'log-level-warning' ) ) {
776- entryLevel = 'warning' ;
777- } else if ( levelBadge . classList . contains ( 'log-level-info' ) ) {
778- entryLevel = 'info' ;
779- } else if ( levelBadge . classList . contains ( 'log-level-debug' ) ) {
780- entryLevel = 'debug' ;
781- } else {
782- entryLevel = null ;
783- }
784- }
785-
786- if ( entryLevel && entryLevel === selectedLevel ) {
787- entry . style . display = '' ;
788- visibleCount ++ ;
789- } else {
790- entry . style . display = 'none' ;
791- entry . setAttribute ( 'data-hidden-by-filter' , 'true' ) ;
792- }
793- } else {
794- entry . style . display = 'none' ;
795- entry . setAttribute ( 'data-hidden-by-filter' , 'true' ) ;
796- }
797- }
798- } ) ;
799-
800- if ( this . autoScroll && this . elements . autoScrollCheckbox && this . elements . autoScrollCheckbox . checked && visibleCount > 0 ) {
801- setTimeout ( ( ) => {
802- window . scrollTo ( {
803- top : 0 ,
804- behavior : 'smooth'
805- } ) ;
806- } , 100 ) ;
807- }
808-
809- console . log ( `[LogsModule] Filtered logs by level '${ selectedLevel } ': showing ${ visibleCount } /${ totalCount } entries` ) ;
829+ // Reload logs from API with new filter
830+ this . loadLogsFromAPI ( this . currentLogApp , false ) ;
810831 } ,
811832
812833 // Apply filter to single entry
@@ -918,9 +939,64 @@ window.LogsModule = {
918939 return false ;
919940 } ,
920941
942+ // Handle pagination navigation
943+ handlePagination : function ( direction ) {
944+ if ( direction === 'prev' && this . currentPage > 1 ) {
945+ this . currentPage -- ;
946+ this . loadLogsFromAPI ( this . currentLogApp , false ) ;
947+ } else if ( direction === 'next' && this . currentPage < this . totalPages ) {
948+ this . currentPage ++ ;
949+ this . loadLogsFromAPI ( this . currentLogApp , false ) ;
950+ }
951+ } ,
952+
953+ // Handle page size change
954+ handlePageSizeChange : function ( ) {
955+ const newPageSize = parseInt ( this . elements . logsPageSize . value ) ;
956+ if ( newPageSize !== this . pageSize ) {
957+ this . pageSize = newPageSize ;
958+ this . currentPage = 1 ; // Reset to first page
959+ this . loadLogsFromAPI ( this . currentLogApp , false ) ;
960+ }
961+ } ,
962+
963+ // Update pagination UI elements
964+ updatePaginationUI : function ( ) {
965+ console . log ( `[LogsModule] updatePaginationUI called - currentPage: ${ this . currentPage } , totalPages: ${ this . totalPages } ` ) ;
966+ console . log ( `[LogsModule] DOM elements found:` , {
967+ logsCurrentPage : ! ! this . elements . logsCurrentPage ,
968+ logsTotalPages : ! ! this . elements . logsTotalPages ,
969+ logsPrevPage : ! ! this . elements . logsPrevPage ,
970+ logsNextPage : ! ! this . elements . logsNextPage
971+ } ) ;
972+
973+ if ( this . elements . logsCurrentPage ) {
974+ this . elements . logsCurrentPage . textContent = this . currentPage ;
975+ console . log ( `[LogsModule] Updated logsCurrentPage to: ${ this . currentPage } ` ) ;
976+ } else {
977+ console . warn ( '[LogsModule] logsCurrentPage element not found!' ) ;
978+ }
979+
980+ if ( this . elements . logsTotalPages ) {
981+ this . elements . logsTotalPages . textContent = this . totalPages ;
982+ console . log ( `[LogsModule] Updated logsTotalPages to: ${ this . totalPages } ` ) ;
983+ } else {
984+ console . warn ( '[LogsModule] logsTotalPages element not found!' ) ;
985+ }
986+
987+ if ( this . elements . logsPrevPage ) {
988+ this . elements . logsPrevPage . disabled = this . currentPage <= 1 ;
989+ }
990+
991+ if ( this . elements . logsNextPage ) {
992+ this . elements . logsNextPage . disabled = this . currentPage >= this . totalPages ;
993+ }
994+ } ,
995+
921996 // Reset logs to default state
922997 resetToDefaults : function ( ) {
923998 this . currentLogApp = 'all' ;
999+ this . currentPage = 1 ; // Reset pagination
9241000
9251001 const logAppSelect = document . getElementById ( 'logAppSelect' ) ;
9261002 if ( logAppSelect && logAppSelect . value !== 'all' ) {
0 commit comments