From 5b63c9a0ffbf8d535a92a9644d1d3a031a08fff4 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 1 Nov 2026 05:39:52 -0800 Subject: [PATCH] Fixhancement: add tag names to document history list (#14625) --- .../document-history.component.html | 2 ++ .../document-history.component.spec.ts | 34 +++++++++++++++++++ .../document-history.component.ts | 28 ++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-detail/document-history/document-history.component.html b/src-ui/src/app/components/document-detail/document-history/document-history.component.html index e23ca6c07e..2c2ed0a7fb 100644 --- a/src-ui/src/app/components/document-detail/document-history/document-history.component.html +++ b/src-ui/src/app/components/document-detail/document-history/document-history.component.html @@ -47,6 +47,8 @@ {{ change.key | titlecase }}:  @if (change.key === 'content') { {{ change.value[1]?.substring(0,100) }}... + } @else if (change.key === 'tags') { + {{ getTagNames($any(change.value[1])) | async }} } @else { {{ getPrettyName(change.key, change.value[1]) | async }} } diff --git a/src-ui/src/app/components/document-detail/document-history/document-history.component.spec.ts b/src-ui/src/app/components/document-detail/document-history/document-history.component.spec.ts index c1845c8e47..1b00482101 100644 --- a/src-ui/src/app/components/document-detail/document-history/document-history.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-history/document-history.component.spec.ts @@ -12,6 +12,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic import { DocumentTypeService } from 'src/app/services/rest/document-type.service' import { DocumentService } from 'src/app/services/rest/document.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service' +import { TagService } from 'src/app/services/rest/tag.service' import { UserService } from 'src/app/services/rest/user.service' import { DocumentHistoryComponent } from './document-history.component' @@ -174,6 +175,39 @@ describe('DocumentHistoryComponent', () => { }) }) + it('should render bulk tag history with names and preserve missing tag IDs', () => { + const getCachedSpy = jest + .spyOn(TestBed.inject(TagService), 'getCached') + .mockImplementation((id) => + of(id === 1 ? { id: 1, name: 'Invoice' } : null) + ) + component.entries.set([ + { + id: 1, + action: AuditLogAction.Update, + timestamp: '2021-01-01T00:00:00Z', + remote_addr: null, + changes: JSON.parse('{"tags":[[],[1,2345]]}'), + }, + ]) + component.loading.set(false) + fixture.detectChanges() + + expect(fixture.nativeElement.querySelector('code').textContent).toBe( + 'Invoice, 2345' + ) + fixture.detectChanges() + expect(getCachedSpy).toHaveBeenCalledTimes(2) + }) + + it('should handle removal of all tags without a lookup', () => { + const getCachedSpy = jest.spyOn(TestBed.inject(TagService), 'getCached') + component.getTagNames([]).subscribe((result) => { + expect(result).toBe('') + }) + expect(getCachedSpy).not.toHaveBeenCalled() + }) + it('getPrettyName should return the value as is for other types', () => { const id = '123' component.getPrettyName('other', id).subscribe((result) => { diff --git a/src-ui/src/app/components/document-detail/document-history/document-history.component.ts b/src-ui/src/app/components/document-detail/document-history/document-history.component.ts index d1cb069f47..37d999139c 100644 --- a/src-ui/src/app/components/document-detail/document-history/document-history.component.ts +++ b/src-ui/src/app/components/document-detail/document-history/document-history.component.ts @@ -2,7 +2,7 @@ import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common' import { Component, Input, OnInit, inject, signal } from '@angular/core' import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Observable, first, map, of, shareReplay } from 'rxjs' +import { Observable, first, forkJoin, map, of, shareReplay } from 'rxjs' import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry' import { DataType } from 'src/app/data/datatype' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' @@ -10,6 +10,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic import { DocumentTypeService } from 'src/app/services/rest/document-type.service' import { DocumentService } from 'src/app/services/rest/document.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service' +import { TagService } from 'src/app/services/rest/tag.service' import { UserService } from 'src/app/services/rest/user.service' @Component({ @@ -30,6 +31,7 @@ export class DocumentHistoryComponent implements OnInit { private readonly correspondentService = inject(CorrespondentService) private readonly storagePathService = inject(StoragePathService) private readonly documentTypeService = inject(DocumentTypeService) + private readonly tagService = inject(TagService) private readonly userService = inject(UserService) public AuditLogAction = AuditLogAction @@ -63,6 +65,30 @@ export class DocumentHistoryComponent implements OnInit { } } + getTagNames(tagIds: number[]): Observable { + const cacheKey = `tags:${JSON.stringify(tagIds)}` + const cachedNames = this.prettyNameCache.get(cacheKey) + if (cachedNames) { + return cachedNames + } + + const tagNames$ = tagIds.length + ? forkJoin( + tagIds.map((tagId) => + this.tagService.getCached(tagId).pipe( + first(), + map((tag) => tag?.name ?? String(tagId)) + ) + ) + ).pipe(map((names) => names.join(', '))) + : of('') + const names$ = tagNames$.pipe( + shareReplay({ bufferSize: 1, refCount: true }) + ) + this.prettyNameCache.set(cacheKey, names$) + return names$ + } + getPrettyName(type: DataType | string, id: string): Observable { const cacheKey = `${type}:${id}` const cached = this.prettyNameCache.get(cacheKey)