{{log}}
- } -+ {{log.message}} +
+ diff --git a/src-ui/src/app/components/admin/logs/logs.component.scss b/src-ui/src/app/components/admin/logs/logs.component.scss index 834c8c1cb..56fd2e8f3 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.scss +++ b/src-ui/src/app/components/admin/logs/logs.component.scss @@ -18,7 +18,7 @@ .log-container { overflow-y: scroll; height: calc(100vh - 200px); - top: 70px; + top: 0; p { white-space: pre-wrap; diff --git a/src-ui/src/app/components/admin/logs/logs.component.spec.ts b/src-ui/src/app/components/admin/logs/logs.component.spec.ts index 6e4adacfe..728916830 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.spec.ts +++ b/src-ui/src/app/components/admin/logs/logs.component.spec.ts @@ -1,3 +1,8 @@ +import { + CdkVirtualScrollViewport, + ScrollingModule, +} from '@angular/cdk/scrolling' +import { CommonModule } from '@angular/common' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' import { ComponentFixture, TestBed } from '@angular/core/testing' @@ -38,6 +43,9 @@ describe('LogsComponent', () => { NgxBootstrapIconsModule.pick(allIcons), LogsComponent, PageHeaderComponent, + CommonModule, + CdkVirtualScrollViewport, + ScrollingModule, ], providers: [ provideHttpClient(withInterceptorsFromDi()), @@ -54,13 +62,12 @@ describe('LogsComponent', () => { fixture = TestBed.createComponent(LogsComponent) component = fixture.componentInstance reloadSpy = jest.spyOn(component, 'reloadLogs') - window.HTMLElement.prototype.scroll = function () {} // mock scroll jest.useFakeTimers() fixture.detectChanges() }) it('should display logs with first log initially', () => { - expect(logSpy).toHaveBeenCalledWith('paperless') + expect(logSpy).toHaveBeenCalledWith('paperless', 5000) fixture.detectChanges() expect(fixture.debugElement.nativeElement.textContent).toContain( paperless_logs[0] @@ -71,7 +78,7 @@ describe('LogsComponent', () => { fixture.debugElement .queryAll(By.directive(NgbNavLink))[1] .nativeElement.dispatchEvent(new MouseEvent('click')) - expect(logSpy).toHaveBeenCalledWith('mail') + expect(logSpy).toHaveBeenCalledWith('mail', 5000) }) it('should handle error with no logs', () => { @@ -83,6 +90,10 @@ describe('LogsComponent', () => { }) it('should auto refresh, allow toggle', () => { + jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToIndex') + .mockImplementation(() => undefined) + jest.advanceTimersByTime(6000) expect(reloadSpy).toHaveBeenCalledTimes(2) @@ -90,4 +101,13 @@ describe('LogsComponent', () => { jest.advanceTimersByTime(6000) expect(reloadSpy).toHaveBeenCalledTimes(2) }) + + it('should debounce limit changes before reloading logs', () => { + const initialCalls = reloadSpy.mock.calls.length + component.onLimitChange(6000) + jest.advanceTimersByTime(299) + expect(reloadSpy).toHaveBeenCalledTimes(initialCalls) + jest.advanceTimersByTime(1) + expect(reloadSpy).toHaveBeenCalledTimes(initialCalls + 1) + }) }) diff --git a/src-ui/src/app/components/admin/logs/logs.component.ts b/src-ui/src/app/components/admin/logs/logs.component.ts index 4799b6125..68b88265d 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.ts +++ b/src-ui/src/app/components/admin/logs/logs.component.ts @@ -1,7 +1,11 @@ +import { + CdkVirtualScrollViewport, + ScrollingModule, +} from '@angular/cdk/scrolling' +import { CommonModule } from '@angular/common' import { ChangeDetectorRef, Component, - ElementRef, OnDestroy, OnInit, ViewChild, @@ -9,7 +13,7 @@ import { } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap' -import { filter, takeUntil, timer } from 'rxjs' +import { Subject, debounceTime, filter, takeUntil, timer } from 'rxjs' import { LogService } from 'src/app/services/rest/log.service' import { PageHeaderComponent } from '../../common/page-header/page-header.component' import { LoadingComponentWithPermissions } from '../../loading-component/loading.component' @@ -21,8 +25,11 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading imports: [ PageHeaderComponent, NgbNavModule, + CommonModule, FormsModule, ReactiveFormsModule, + CdkVirtualScrollViewport, + ScrollingModule, ], }) export class LogsComponent @@ -32,7 +39,7 @@ export class LogsComponent private logService = inject(LogService) private changedetectorRef = inject(ChangeDetectorRef) - public logs: string[] = [] + public logs: Array<{ message: string; level: number }> = [] public logFiles: string[] = [] @@ -40,9 +47,17 @@ export class LogsComponent public autoRefreshEnabled: boolean = true - @ViewChild('logContainer') logContainer: ElementRef + public limit: number = 5000 + + private readonly limitChange$ = new Subject