]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Tweakhancement: display document id, with copy (#11896)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Mon, 26 Jan 2026 16:30:43 +0000 (08:30 -0800)
committerGitHub <noreply@github.com>
Mon, 26 Jan 2026 16:30:43 +0000 (08:30 -0800)
src-ui/src/app/components/common/page-header/page-header.component.html
src-ui/src/app/components/common/page-header/page-header.component.scss
src-ui/src/app/components/common/page-header/page-header.component.spec.ts
src-ui/src/app/components/common/page-header/page-header.component.ts
src-ui/src/app/components/document-detail/document-detail.component.html

index 2832182199763aa69bc365578ff499f34e844c93..488fff59d4e0dd2af2951b8d36586bc14315e8c4 100644 (file)
@@ -1,9 +1,18 @@
 <div class="row pt-3 pb-3 pb-md-2 align-items-center">
   <div class="col-md text-truncate">
-    <h3 class="text-truncate" style="line-height: 1.4">
-      {{title}}
+    <h3 class="d-flex align-items-center mb-1" style="line-height: 1.4">
+      <span class="text-truncate">{{title}}</span>
+      @if (id) {
+        <span class="badge bg-primary text-primary-text-contrast ms-3 small fs-normal cursor-pointer" (click)="copyID()">
+          @if (copied) {
+            <i-bs width="1em" height="1em" name="clipboard-check"></i-bs>&nbsp;<ng-container i18n>Copied!</ng-container>
+          } @else {
+            ID: {{id}}
+          }
+        </span>
+      }
       @if (subTitle) {
-        <span class="h6 mb-0 d-block d-md-inline fw-normal ms-md-3 text-truncate" style="line-height: 1.4">{{subTitle}}</span>
+        <span class="h6 mb-0 mt-1 d-block d-md-inline fw-normal ms-md-3 text-truncate" style="line-height: 1.4">{{subTitle}}</span>
       }
       @if (info) {
         <button class="btn btn-sm btn-link text-muted me-auto p-0 p-md-2" title="What's this?" i18n-title type="button" [ngbPopover]="infoPopover" [autoClose]="true">
index ee373a8e276e1e3ad1fedd7c9f92fa8f595346a9..d8d3311bbb5e5495ebc097192c76f4524e5f4e0d 100644 (file)
@@ -1,5 +1,10 @@
 h3 {
     min-height: calc(1.325rem + 0.9vw);
+
+    .badge {
+        font-size: 0.65rem;
+        line-height: 1;
+    }
 }
 
 @media (min-width: 1200px) {
index 8b307b10dfa16723009cf5cb2eae27ba978e7d36..8fa9ad3695b20022e3c3dfe937e60bff23dc2a82 100644 (file)
@@ -1,3 +1,4 @@
+import { Clipboard } from '@angular/cdk/clipboard'
 import { ComponentFixture, TestBed } from '@angular/core/testing'
 import { Title } from '@angular/platform-browser'
 import { environment } from 'src/environments/environment'
@@ -7,6 +8,7 @@ describe('PageHeaderComponent', () => {
   let component: PageHeaderComponent
   let fixture: ComponentFixture<PageHeaderComponent>
   let titleService: Title
+  let clipboard: Clipboard
 
   beforeEach(async () => {
     TestBed.configureTestingModule({
@@ -15,6 +17,7 @@ describe('PageHeaderComponent', () => {
     }).compileComponents()
 
     titleService = TestBed.inject(Title)
+    clipboard = TestBed.inject(Clipboard)
     fixture = TestBed.createComponent(PageHeaderComponent)
     component = fixture.componentInstance
     fixture.detectChanges()
@@ -24,7 +27,8 @@ describe('PageHeaderComponent', () => {
     component.title = 'Foo'
     component.subTitle = 'Bar'
     fixture.detectChanges()
-    expect(fixture.nativeElement.textContent).toContain('Foo Bar')
+    expect(fixture.nativeElement.textContent).toContain('Foo')
+    expect(fixture.nativeElement.textContent).toContain('Bar')
   })
 
   it('should set html title', () => {
@@ -32,4 +36,16 @@ describe('PageHeaderComponent', () => {
     component.title = 'Foo Bar'
     expect(titleSpy).toHaveBeenCalledWith(`Foo Bar - ${environment.appTitle}`)
   })
+
+  it('should copy id to clipboard, reset after 3 seconds', () => {
+    jest.useFakeTimers()
+    component.id = 42 as any
+    jest.spyOn(clipboard, 'copy').mockReturnValue(true)
+    component.copyID()
+    expect(clipboard.copy).toHaveBeenCalledWith('42')
+    expect(component.copied).toBe(true)
+
+    jest.advanceTimersByTime(3000)
+    expect(component.copied).toBe(false)
+  })
 })
index d5d397641439c6cb1bccefcefd6fee97ee812d1c..2e0bc1c39bc7d307e28c4170397e5569ec8cca23 100644 (file)
@@ -1,3 +1,4 @@
+import { Clipboard } from '@angular/cdk/clipboard'
 import { Component, Input, inject } from '@angular/core'
 import { Title } from '@angular/platform-browser'
 import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
@@ -13,8 +14,11 @@ import { environment } from 'src/environments/environment'
 })
 export class PageHeaderComponent {
   private titleService = inject(Title)
+  private clipboard = inject(Clipboard)
 
-  _title = ''
+  private _title = ''
+  public copied: boolean = false
+  private copyTimeout: any
 
   @Input()
   set title(title: string) {
@@ -26,6 +30,9 @@ export class PageHeaderComponent {
     return this._title
   }
 
+  @Input()
+  id: number
+
   @Input()
   subTitle: string = ''
 
@@ -34,4 +41,12 @@ export class PageHeaderComponent {
 
   @Input()
   infoLink: string
+
+  public copyID() {
+    this.copied = this.clipboard.copy(this.id.toString())
+    clearTimeout(this.copyTimeout)
+    this.copyTimeout = setTimeout(() => {
+      this.copied = false
+    }, 3000)
+  }
 }
index 44304c9424202e1b6f593929837358c078b21c90..34e1f79804e4b32f4897af51d6f7da254b7d320f 100644 (file)
@@ -1,4 +1,4 @@
-<pngx-page-header [(title)]="title">
+<pngx-page-header [(title)]="title" [id]="documentId">
   @if (archiveContentRenderType === ContentRenderType.PDF && !useNativePdfViewer) {
     @if (previewNumPages) {
       <div class="input-group input-group-sm d-none d-md-flex">