From f3edc1ac36198f98d020159fc3aa29daa0df9d15 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 4 Jul 2026 09:19:37 -0700 Subject: [PATCH] Ok first real chunk of zoneless pngx document list dashboard / widget frame stats --- .../storage-path-edit-dialog.component.ts | 2 +- .../components/common/tag/tag.component.ts | 8 ++-- .../dashboard/dashboard.component.ts | 12 ++++- .../statistics-widget.component.ts | 21 ++++++-- .../widget-frame/widget-frame.component.ts | 9 +++- .../loading-component/loading.component.ts | 22 +++++++-- .../services/document-list-view.service.ts | 48 ++++++++++++++++++- 7 files changed, 107 insertions(+), 15 deletions(-) diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts index 68ce40f5e..1ccf6a47a 100644 --- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts @@ -67,11 +67,11 @@ export class StoragePathEditDialogComponent private testDocument: Document public testResult: string public testFailed: boolean = false - public loading = false public testLoading = false constructor() { super() + this.loading = false this.service = inject(StoragePathService) this.userService = inject(UserService) this.settingsService = inject(SettingsService) diff --git a/src-ui/src/app/components/common/tag/tag.component.ts b/src-ui/src/app/components/common/tag/tag.component.ts index 35fd80a00..41c51e468 100644 --- a/src-ui/src/app/components/common/tag/tag.component.ts +++ b/src-ui/src/app/components/common/tag/tag.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, Input } from '@angular/core' +import { Component, inject, Input, signal } from '@angular/core' import { Tag } from 'src/app/data/tag' import { PermissionAction, @@ -16,16 +16,16 @@ export class TagComponent { private permissionsService = inject(PermissionsService) private tagService = inject(TagService) - private _tag: Tag + private tagSignal = signal(null) private _tagID: number @Input() public set tag(tag: Tag) { - this._tag = tag + this.tagSignal.set(tag) } public get tag(): Tag { - return this._tag + return this.tagSignal() } @Input() diff --git a/src-ui/src/app/components/dashboard/dashboard.component.ts b/src-ui/src/app/components/dashboard/dashboard.component.ts index 2d7e9445e..d9734c5c3 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.ts +++ b/src-ui/src/app/components/dashboard/dashboard.component.ts @@ -5,7 +5,7 @@ import { DragDropModule, moveItemInArray, } from '@angular/cdk/drag-drop' -import { Component, inject } from '@angular/core' +import { Component, inject, signal } from '@angular/core' import { RouterModule } from '@angular/router' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { TourNgBootstrap, TourService } from 'ngx-ui-tour-ng-bootstrap' @@ -47,7 +47,15 @@ export class DashboardComponent extends ComponentWithPermissions { private tourService = inject(TourService) private toastService = inject(ToastService) - public dashboardViews: SavedView[] = [] + private dashboardViewsSignal = signal([]) + + get dashboardViews(): SavedView[] { + return this.dashboardViewsSignal() + } + + set dashboardViews(value: SavedView[]) { + this.dashboardViewsSignal.set(value) + } constructor() { super() diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts index 8b26690db..b74ad1aec 100644 --- a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts @@ -1,6 +1,6 @@ import { DecimalPipe } from '@angular/common' import { HttpClient } from '@angular/common/http' -import { Component, inject, OnDestroy, OnInit } from '@angular/core' +import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core' import { RouterModule } from '@angular/router' import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' import * as mimeTypeNames from 'mime-names' @@ -55,9 +55,24 @@ export class StatisticsWidgetComponent private websocketConnectionService = inject(WebsocketStatusService) private documentListViewService = inject(DocumentListViewService) - loading: boolean = false + private loadingSignal = signal(false) + private statisticsSignal = signal({}) - statistics: Statistics = {} + get loading(): boolean { + return this.loadingSignal() + } + + set loading(value: boolean) { + this.loadingSignal.set(value) + } + + get statistics(): Statistics { + return this.statisticsSignal() + } + + set statistics(value: Statistics) { + this.statisticsSignal.set(value) + } subscription: Subscription private unsubscribeNotifer: Subject = new Subject() diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts index a638cb52c..3b0b04529 100644 --- a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts @@ -16,13 +16,20 @@ export class WidgetFrameComponent { constructor() { super() + this.loading = false } @Input() title: string @Input() - loading: boolean = false + override set loading(value: boolean) { + super.loading = value + } + + override get loading(): boolean { + return super.loading + } @Input() draggable: any diff --git a/src-ui/src/app/components/loading-component/loading.component.ts b/src-ui/src/app/components/loading-component/loading.component.ts index 0cde537d3..1cf1d6dea 100644 --- a/src-ui/src/app/components/loading-component/loading.component.ts +++ b/src-ui/src/app/components/loading-component/loading.component.ts @@ -1,4 +1,4 @@ -import { Directive, OnDestroy } from '@angular/core' +import { Directive, OnDestroy, signal } from '@angular/core' import { Subject } from 'rxjs' import { ComponentWithPermissions } from '../with-permissions/with-permissions.component' @@ -7,8 +7,24 @@ export abstract class LoadingComponentWithPermissions extends ComponentWithPermissions implements OnDestroy { - public loading: boolean = true - public show: boolean = false + private loadingSignal = signal(true) + private showSignal = signal(false) + + public get loading(): boolean { + return this.loadingSignal() + } + + public set loading(value: boolean) { + this.loadingSignal.set(value) + } + + public get show(): boolean { + return this.showSignal() + } + + public set show(value: boolean) { + this.showSignal.set(value) + } protected unsubscribeNotifier: Subject = new Subject() diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index 1d37000bf..e7df204ab 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -1,4 +1,4 @@ -import { Injectable, inject } from '@angular/core' +import { Injectable, inject, signal } from '@angular/core' import { ParamMap, Router, UrlTree } from '@angular/router' import { Observable, Subject, takeUntil } from 'rxjs' import { @@ -118,6 +118,7 @@ export class DocumentListViewService { isReloading: boolean = false initialized: boolean = false error: string = null + private stateVersion = signal(0) rangeSelectionAnchorIndex: number lastRangeSelectionToIndex: number @@ -132,6 +133,14 @@ export class DocumentListViewService { private displayFieldsInitialized: boolean = false + private markChanged(): void { + this.stateVersion.update((version) => version + 1) + } + + private trackState(): void { + this.stateVersion() + } + private restoreListViewState(savedState: unknown): ListViewState { const newState = this.defaultListViewState() @@ -159,10 +168,12 @@ export class DocumentListViewService { } get activeSavedViewId() { + this.trackState() return this._activeSavedViewId } get activeSavedViewTitle() { + this.trackState() return this.activeListViewState.title } @@ -191,6 +202,7 @@ export class DocumentListViewService { ) this.saveDocumentListView() } + this.markChanged() }) } @@ -243,6 +255,7 @@ export class DocumentListViewService { } else { this._activeSavedViewId = null } + this.markChanged() } activateSavedViewWithQueryParams(view: SavedView, queryParams: ParamMap) { @@ -267,6 +280,7 @@ export class DocumentListViewService { this.activeListViewState.displayFields = view.display_fields this.reduceSelectionToFilter() + this.markChanged() if (!this.router.routerState.snapshot.url.includes('/view/')) { this.router.navigate(['view', view.id]) @@ -297,6 +311,7 @@ export class DocumentListViewService { this.activeListViewState.sortField = newState.sortField this.activeListViewState.sortReverse = newState.sortReverse this.activeListViewState.currentPage = newState.currentPage + this.markChanged() this.reload(null, paramsEmpty) // update the params if there aren't any } } @@ -305,6 +320,7 @@ export class DocumentListViewService { this.cancelPending() this.isReloading = true this.error = null + this.markChanged() let activeListViewState = this.activeListViewState this.documentService .listFiltered( @@ -325,6 +341,7 @@ export class DocumentListViewService { activeListViewState.documents = result.results this.selectionData = resultWithSelectionData.selection_data ?? null this.syncSelectedToCurrentPage() + this.markChanged() if (updateQueryParams && !this._activeSavedViewId) { let base = ['/documents'] @@ -343,12 +360,14 @@ export class DocumentListViewService { onFinish() } this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null + this.markChanged() }, error: (error) => { this.isReloading = false if (activeListViewState.currentPage != 1 && error.status == 404) { // this happens when applying a filter: the current page might not be available anymore due to the reduced result set. activeListViewState.currentPage = 1 + this.markChanged() this.reload() } else if ( activeListViewState.sortField.indexOf('custom_field') === 0 && @@ -381,6 +400,7 @@ export class DocumentListViewService { errorMessage = error.error } this.error = errorMessage + this.markChanged() } }, }) @@ -397,12 +417,14 @@ export class DocumentListViewService { if (resetPage) { this.activeListViewState.currentPage = 1 } + this.markChanged() this.reload() this.reduceSelectionToFilter() this.saveDocumentListView() } get filterRules(): FilterRule[] { + this.trackState() return this.activeListViewState.filterRules } @@ -416,48 +438,58 @@ export class DocumentListViewService { set sortField(field: string) { this.activeListViewState.sortField = field + this.markChanged() this.reload() this.saveDocumentListView() } get sortField(): string { + this.trackState() return this.activeListViewState.sortField } set sortReverse(reverse: boolean) { this.activeListViewState.sortReverse = reverse + this.markChanged() this.reload() this.saveDocumentListView() } get sortReverse(): boolean { + this.trackState() return this.activeListViewState.sortReverse } get collectionSize(): number { + this.trackState() return this.activeListViewState.collectionSize } get currentPage(): number { + this.trackState() return this.activeListViewState.currentPage } set currentPage(page: number) { if (this.activeListViewState.currentPage == page) return this.activeListViewState.currentPage = page + this.markChanged() this.reload() this.saveDocumentListView() } get documents(): Document[] { + this.trackState() return this.activeListViewState.documents } get selected(): Set { + this.trackState() return this.activeListViewState.selected } get allSelected(): boolean { + this.trackState() return this.activeListViewState.allSelected ?? false } @@ -474,16 +506,19 @@ export class DocumentListViewService { setSort(field: string, reverse: boolean) { this.activeListViewState.sortField = field this.activeListViewState.sortReverse = reverse + this.markChanged() this.reload() this.saveDocumentListView() } set displayMode(mode: DisplayMode) { this.activeListViewState.displayMode = mode + this.markChanged() this.saveDocumentListView() } get displayMode(): DisplayMode { + this.trackState() const mode = this.activeListViewState.displayMode ?? DisplayMode.SMALL_CARDS if (mode === ('details' as any)) { // legacy @@ -494,11 +529,13 @@ export class DocumentListViewService { set pageSize(size: number) { this.activeListViewState.pageSize = size + this.markChanged() this.reload() this.saveDocumentListView() } get pageSize(): number { + this.trackState() return ( this.activeListViewState.pageSize ?? this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) @@ -506,6 +543,7 @@ export class DocumentListViewService { } get displayFields(): DisplayField[] { + this.trackState() return this.activeListViewState.displayFields ?? LIST_DEFAULT_DISPLAY_FIELDS } @@ -517,6 +555,7 @@ export class DocumentListViewService { undefined ) : fields + this.markChanged() this.saveDocumentListView() } @@ -540,6 +579,7 @@ export class DocumentListViewService { quickFilter(filterRules: FilterRule[]) { this._activeSavedViewId = null + this.markChanged() this.setFilterRules(filterRules) this.router.navigate(['documents']) } @@ -628,6 +668,7 @@ export class DocumentListViewService { this.activeListViewState.allSelected = false this.selected.clear() this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null + this.markChanged() } reduceSelectionToFilter() { @@ -644,6 +685,7 @@ export class DocumentListViewService { this.selected.delete(id) } } + this.markChanged() }) } } @@ -651,6 +693,7 @@ export class DocumentListViewService { selectAll() { this.activeListViewState.allSelected = true this.syncSelectedToCurrentPage() + this.markChanged() } selectPage() { @@ -659,6 +702,7 @@ export class DocumentListViewService { this.documents.forEach((doc) => { this.selected.add(doc.id) }) + this.markChanged() } isSelected(d: Document) { @@ -673,6 +717,7 @@ export class DocumentListViewService { else this.selected.add(d.id) this.rangeSelectionAnchorIndex = this.documentIndexInCurrentView(d.id) this.lastRangeSelectionToIndex = null + this.markChanged() } selectRangeTo(d: Document) { @@ -710,6 +755,7 @@ export class DocumentListViewService { this.selected.add(d.id) }) this.lastRangeSelectionToIndex = documentToIndex + this.markChanged() } else { // e.g. shift key but was first click this.toggleSelected(d)