mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge branch 'dev' into quick-filters
This commit is contained in:
		
							
								
								
									
										63
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										63
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -2215,6 +2215,11 @@ | ||||
|       "integrity": "sha512-UV1/ZJMC+HcP902wWdpC43cAcGu0IQk/I5bXjP2aSuCjsk3cE74mDvFrLKga7oDC170ugOAYBwfT4DSQW3akDA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "@types/pdfjs-dist": { | ||||
|       "version": "2.1.7", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.1.7.tgz", | ||||
|       "integrity": "sha512-nQIwcPUhkAIyn7x9NS0lR/qxYfd5unRtfGkMjvpgF4Sh28IXftRymaNmFKTTdejDNY25NDGSIyjwj/BRwAPexg==" | ||||
|     }, | ||||
|     "@types/q": { | ||||
|       "version": "1.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", | ||||
| @@ -3023,6 +3028,16 @@ | ||||
|       "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "bindings": { | ||||
|       "version": "1.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", | ||||
|       "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", | ||||
|       "dev": true, | ||||
|       "optional": true, | ||||
|       "requires": { | ||||
|         "file-uri-to-path": "1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "blob": { | ||||
|       "version": "0.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", | ||||
| @@ -5508,6 +5523,13 @@ | ||||
|         "schema-utils": "^2.6.5" | ||||
|       } | ||||
|     }, | ||||
|     "file-uri-to-path": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", | ||||
|       "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", | ||||
|       "dev": true, | ||||
|       "optional": true | ||||
|     }, | ||||
|     "fill-range": { | ||||
|       "version": "7.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", | ||||
| @@ -8208,6 +8230,13 @@ | ||||
|       "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "nan": { | ||||
|       "version": "2.14.2", | ||||
|       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", | ||||
|       "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", | ||||
|       "dev": true, | ||||
|       "optional": true | ||||
|     }, | ||||
|     "nanomatch": { | ||||
|       "version": "1.2.13", | ||||
|       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", | ||||
| @@ -8260,6 +8289,23 @@ | ||||
|         "moment": "2.18.1" | ||||
|       } | ||||
|     }, | ||||
|     "ng2-pdf-viewer": { | ||||
|       "version": "6.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/ng2-pdf-viewer/-/ng2-pdf-viewer-6.3.2.tgz", | ||||
|       "integrity": "sha512-H2tBhDd+Lq6CUzK2g54HsCcZDR2wTn1sDjYqKY3yF0Ydasl2R5ppCKynZBU/zge4EKvmHglJI120FbQMpJKDYQ==", | ||||
|       "requires": { | ||||
|         "@types/pdfjs-dist": "^2.1.4", | ||||
|         "pdfjs-dist": "^2.4.456", | ||||
|         "tslib": "^1.10.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "tslib": { | ||||
|           "version": "1.14.1", | ||||
|           "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||
|           "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "ngx-cookie-service": { | ||||
|       "version": "10.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-10.1.1.tgz", | ||||
| @@ -9270,6 +9316,11 @@ | ||||
|         "sha.js": "^2.4.8" | ||||
|       } | ||||
|     }, | ||||
|     "pdfjs-dist": { | ||||
|       "version": "2.5.207", | ||||
|       "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.5.207.tgz", | ||||
|       "integrity": "sha512-xGDUhnCYPfHy+unMXCLCJtlpZaaZ17Ew3WIL0tnSgKFUZXHAPD49GO9xScyszSsQMoutNDgRb+rfBXIaX/lJbw==" | ||||
|     }, | ||||
|     "performance-now": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", | ||||
| @@ -13228,7 +13279,11 @@ | ||||
|           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", | ||||
|           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", | ||||
|           "dev": true, | ||||
|           "optional": true | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "bindings": "^1.5.0", | ||||
|             "nan": "^2.12.1" | ||||
|           } | ||||
|         }, | ||||
|         "glob-parent": { | ||||
|           "version": "3.1.0", | ||||
| @@ -13832,7 +13887,11 @@ | ||||
|           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", | ||||
|           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", | ||||
|           "dev": true, | ||||
|           "optional": true | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "bindings": "^1.5.0", | ||||
|             "nan": "^2.12.1" | ||||
|           } | ||||
|         }, | ||||
|         "glob-parent": { | ||||
|           "version": "3.1.0", | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|     "@ng-bootstrap/ng-bootstrap": "^8.0.0", | ||||
|     "bootstrap": "^4.5.0", | ||||
|     "ng-bootstrap": "^1.6.3", | ||||
|     "ng2-pdf-viewer": "^6.3.2", | ||||
|     "ngx-cookie-service": "^10.1.1", | ||||
|     "ngx-file-drop": "^10.0.0", | ||||
|     "ngx-infinite-scroll": "^9.1.0", | ||||
|   | ||||
| @@ -14,10 +14,9 @@ import { LogsComponent } from './components/manage/logs/logs.component'; | ||||
| import { SettingsComponent } from './components/manage/settings/settings.component'; | ||||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||||
| import { DatePipe } from '@angular/common'; | ||||
| import { SafePipe } from './pipes/safe.pipe'; | ||||
| import { NotFoundComponent } from './components/not-found/not-found.component'; | ||||
| import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'; | ||||
| import { DeleteDialogComponent } from './components/common/delete-dialog/delete-dialog.component'; | ||||
| import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'; | ||||
| import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'; | ||||
| import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component'; | ||||
| import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'; | ||||
| @@ -48,10 +47,13 @@ import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-v | ||||
| import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'; | ||||
| import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'; | ||||
| import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; | ||||
| import { PdfViewerModule } from 'ng2-pdf-viewer'; | ||||
| import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; | ||||
| import { YesNoPipe } from './pipes/yes-no.pipe'; | ||||
| import { FileSizePipe } from './pipes/file-size.pipe'; | ||||
| import { FilterPipe } from './pipes/filter.pipe'; | ||||
| import { DocumentTitlePipe } from './pipes/document-title.pipe'; | ||||
| import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'; | ||||
|  | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
| @@ -64,10 +66,9 @@ import { FilterPipe } from './pipes/filter.pipe'; | ||||
|     DocumentTypeListComponent, | ||||
|     LogsComponent, | ||||
|     SettingsComponent, | ||||
|     SafePipe, | ||||
|     NotFoundComponent, | ||||
|     CorrespondentEditDialogComponent, | ||||
|     DeleteDialogComponent, | ||||
|     ConfirmDialogComponent, | ||||
|     TagEditDialogComponent, | ||||
|     DocumentTypeEditDialogComponent, | ||||
|     TagComponent, | ||||
| @@ -97,6 +98,8 @@ import { FilterPipe } from './pipes/filter.pipe'; | ||||
|     YesNoPipe, | ||||
|     FileSizePipe, | ||||
|     FilterPipe | ||||
|     DocumentTitlePipe, | ||||
|     MetadataCollapseComponent | ||||
|   ], | ||||
|   imports: [ | ||||
|     BrowserModule, | ||||
| @@ -106,7 +109,8 @@ import { FilterPipe } from './pipes/filter.pipe'; | ||||
|     FormsModule, | ||||
|     ReactiveFormsModule, | ||||
|     NgxFileDropModule, | ||||
|     InfiniteScrollModule | ||||
|     InfiniteScrollModule, | ||||
|     PdfViewerModule | ||||
|   ], | ||||
|   providers: [ | ||||
|     DatePipe, | ||||
|   | ||||
| @@ -132,7 +132,7 @@ | ||||
|         </h6> | ||||
|         <ul class="nav flex-column mb-2"> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link" href="https://paperless-ng.readthedocs.io/en/latest/"> | ||||
|             <a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://paperless-ng.readthedocs.io/en/latest/"> | ||||
|               <svg class="sidebaricon" fill="currentColor"> | ||||
|                 <use xlink:href="assets/bootstrap-icons.svg#question-circle"/> | ||||
|               </svg> | ||||
| @@ -140,7 +140,7 @@ | ||||
|             </a> | ||||
|           </li> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link" href="https://github.com/jonaswinkler/paperless-ng"> | ||||
|             <a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng"> | ||||
|               <svg class="sidebaricon" fill="currentColor"> | ||||
|                 <use xlink:href="assets/bootstrap-icons.svg#link"/> | ||||
|               </svg> | ||||
|   | ||||
| @@ -5,10 +5,10 @@ | ||||
|       </button> | ||||
|     </div> | ||||
|     <div class="modal-body"> | ||||
|       <p><b>{{message}}</b></p> | ||||
|       <p *ngIf="message2">{{message2}}</p> | ||||
|       <p *ngIf="messageBold"><b>{{messageBold}}</b></p> | ||||
|       <p *ngIf="message">{{message}}</p> | ||||
|     </div> | ||||
|     <div class="modal-footer"> | ||||
|       <button type="button" class="btn btn-outline-dark" (click)="cancelClicked()">Cancel</button> | ||||
|       <button type="button" class="btn btn-danger" (click)="deleteClicked.emit()">Delete</button> | ||||
|       <button type="button" class="btn" [class]="btnClass" (click)="confirmClicked.emit()">{{btnCaption}}</button> | ||||
|     </div> | ||||
| @@ -1,20 +1,20 @@ | ||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
| 
 | ||||
| import { DeleteDialogComponent } from './delete-dialog.component'; | ||||
| import { ConfirmDialogComponent } from './confirm-dialog.component'; | ||||
| 
 | ||||
| describe('DeleteDialogComponent', () => { | ||||
|   let component: DeleteDialogComponent; | ||||
|   let fixture: ComponentFixture<DeleteDialogComponent>; | ||||
| describe('ConfirmDialogComponent', () => { | ||||
|   let component: ConfirmDialogComponent; | ||||
|   let fixture: ComponentFixture<ConfirmDialogComponent>; | ||||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       declarations: [ DeleteDialogComponent ] | ||||
|       declarations: [ ConfirmDialogComponent ] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
|   }); | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     fixture = TestBed.createComponent(DeleteDialogComponent); | ||||
|     fixture = TestBed.createComponent(ConfirmDialogComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
| @@ -0,0 +1,37 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-confirm-dialog', | ||||
|   templateUrl: './confirm-dialog.component.html', | ||||
|   styleUrls: ['./confirm-dialog.component.scss'] | ||||
| }) | ||||
| export class ConfirmDialogComponent implements OnInit { | ||||
|  | ||||
|   constructor(public activeModal: NgbActiveModal) { } | ||||
|  | ||||
|   @Output() | ||||
|   public confirmClicked = new EventEmitter() | ||||
|  | ||||
|   @Input() | ||||
|   title = "Confirmation" | ||||
|  | ||||
|   @Input() | ||||
|   messageBold | ||||
|  | ||||
|   @Input() | ||||
|   message | ||||
|  | ||||
|   @Input() | ||||
|   btnClass = "btn-primary" | ||||
|  | ||||
|   @Input() | ||||
|   btnCaption = "Confirm" | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|   } | ||||
|  | ||||
|   cancelClicked() { | ||||
|     this.activeModal.close() | ||||
|   } | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-delete-dialog', | ||||
|   templateUrl: './delete-dialog.component.html', | ||||
|   styleUrls: ['./delete-dialog.component.scss'] | ||||
| }) | ||||
| export class DeleteDialogComponent implements OnInit { | ||||
|  | ||||
|   constructor(public activeModal: NgbActiveModal) { } | ||||
|  | ||||
|   @Output() | ||||
|   public deleteClicked = new EventEmitter() | ||||
|  | ||||
|   @Input() | ||||
|   title = "Delete confirmation" | ||||
|  | ||||
|   @Input() | ||||
|   message = "Do you really want to delete this?" | ||||
|  | ||||
|   @Input() | ||||
|   message2 | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|   } | ||||
|  | ||||
|   cancelClicked() { | ||||
|     this.activeModal.close() | ||||
|   } | ||||
| } | ||||
| @@ -8,7 +8,7 @@ | ||||
|  | ||||
|     <div class="input-group-append" ngbDropdown placement="top-right"> | ||||
|       <button class="btn btn-outline-secondary" type="button" ngbDropdownToggle></button> | ||||
|       <div ngbDropdownMenu class="scrollable-menu"> | ||||
|       <div ngbDropdownMenu class="scrollable-menu shadow"> | ||||
|         <button type="button" *ngFor="let tag of tags" ngbDropdownItem (click)="addTag(tag.id)"> | ||||
|           <app-tag [tag]="tag"></app-tag> | ||||
|         </button> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <div class="card mb-3 shadow"> | ||||
| <div class="card mb-3 shadow-sm"> | ||||
|   <div class="card-header"> | ||||
|     <div class="d-flex justify-content-between align-items-center"> | ||||
|       <h5 class="card-title mb-0">{{title}}</h5> | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
|  | ||||
|         <div class="btn-group" ngbDropdown role="group" *ngIf="metadata?.has_archive_version"> | ||||
|             <button class="btn btn-sm btn-outline-primary dropdown-toggle-split" ngbDropdownToggle></button> | ||||
|             <div class="dropdown-menu" ngbDropdownMenu> | ||||
|             <div class="dropdown-menu shadow" ngbDropdownMenu> | ||||
|                 <a ngbDropdownItem [href]="downloadOriginalUrl">Download original</a> | ||||
|             </div> | ||||
|         </div> | ||||
| @@ -35,7 +35,7 @@ | ||||
|  | ||||
|  | ||||
| <div class="row"> | ||||
|     <div class="col-xl"> | ||||
|     <div class="col mb-4"> | ||||
|  | ||||
|         <form [formGroup]='documentForm' (ngSubmit)="save()"> | ||||
|  | ||||
| @@ -110,53 +110,8 @@ | ||||
|                             </tbody> | ||||
|                         </table> | ||||
|  | ||||
|                         <h6 *ngIf="metadata?.original_metadata.length > 0"> | ||||
|                             <button type="button" class="btn btn-outline-secondary btn-sm mr-2" | ||||
|                                 (click)="expandOriginalMetadata = !expandOriginalMetadata" aria-controls="collapseExample"> | ||||
|                                 <svg class="buttonicon" fill="currentColor" *ngIf="!expandOriginalMetadata"> | ||||
|                                     <use xlink:href="assets/bootstrap-icons.svg#caret-down" /> | ||||
|                                 </svg> | ||||
|                                 <svg class="buttonicon" fill="currentColor" *ngIf="expandOriginalMetadata"> | ||||
|                                     <use xlink:href="assets/bootstrap-icons.svg#caret-up" /> | ||||
|                                 </svg> | ||||
|                             </button> | ||||
|                             Original document metadata | ||||
|                         </h6> | ||||
|  | ||||
|                         <div #collapse="ngbCollapse" [(ngbCollapse)]="!expandOriginalMetadata"> | ||||
|                             <table class="table table-borderless"> | ||||
|                                 <tbody> | ||||
|                                     <tr *ngFor="let m of metadata?.original_metadata"> | ||||
|                                         <td>{{m.prefix}}:{{m.key}}</td> | ||||
|                                         <td>{{m.value}}</td> | ||||
|                                     </tr> | ||||
|                                 </tbody> | ||||
|                             </table> | ||||
|                         </div> | ||||
|  | ||||
|                         <h6 *ngIf="metadata?.has_archive_version && metadata?.archive_metadata.length > 0"> | ||||
|                             <button type="button" class="btn btn-outline-secondary btn-sm mr-2" | ||||
|                                 (click)="expandArchivedMetadata = !expandArchivedMetadata" aria-controls="collapseExample"> | ||||
|                                 <svg class="buttonicon" fill="currentColor" *ngIf="!expandArchivedMetadata"> | ||||
|                                     <use xlink:href="assets/bootstrap-icons.svg#caret-down" /> | ||||
|                                 </svg> | ||||
|                                 <svg class="buttonicon" fill="currentColor" *ngIf="expandArchivedMetadata"> | ||||
|                                     <use xlink:href="assets/bootstrap-icons.svg#caret-up" /> | ||||
|                                 </svg> | ||||
|                             </button> | ||||
|                             Archived document metadata | ||||
|                         </h6> | ||||
|  | ||||
|                         <div #collapse="ngbCollapse" [(ngbCollapse)]="!expandArchivedMetadata"> | ||||
|                             <table class="table table-borderless"> | ||||
|                                 <tbody> | ||||
|                                     <tr *ngFor="let m of metadata?.archive_metadata"> | ||||
|                                         <td>{{m.prefix}}:{{m.key}}</td> | ||||
|                                         <td>{{m.value}}</td> | ||||
|                                     </tr> | ||||
|                                 </tbody> | ||||
|                             </table> | ||||
|                         </div> | ||||
|                         <app-metadata-collapse title="Original document metadata" [metadata]="metadata.original_metadata" *ngIf="metadata?.original_metadata.length > 0"></app-metadata-collapse> | ||||
|                         <app-metadata-collapse title="Archived document metadata" [metadata]="metadata.archive_metadata" *ngIf="metadata?.archive_metadata.length > 0"></app-metadata-collapse> | ||||
|  | ||||
|                     </ng-template> | ||||
|                 </li> | ||||
| @@ -171,11 +126,9 @@ | ||||
|         </form> | ||||
|     </div> | ||||
|  | ||||
|     <div class="col-xl d-none d-xl-block document-preview"> | ||||
|         <object [data]="previewUrl | safe" type="application/pdf" width="100%" height="100%"> | ||||
|             <p>Your browser does not support PDFs. | ||||
|                 <a href="previewUrl">Download the PDF</a>.</p> | ||||
|         </object> | ||||
|  | ||||
|     <div class="col-md-6 col-xl-8 mb-3"> | ||||
|       <div class="pdf-viewer-container" *ngIf="getContentType() == 'application/pdf'"> | ||||
|         <pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true"></pdf-viewer> | ||||
|       </div> | ||||
|     </div> | ||||
| </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| .document-preview { | ||||
|   height: calc(100vh - 180px); | ||||
| .pdf-viewer-container { | ||||
|   height: calc(100vh - 160px); | ||||
|   top: 70px; | ||||
|   position: sticky; | ||||
| } | ||||
|   background-color: gray; | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,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 { environment } from 'src/environments/environment'; | ||||
| import { DeleteDialogComponent } from '../common/delete-dialog/delete-dialog.component'; | ||||
| import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'; | ||||
| import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component'; | ||||
| import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component'; | ||||
|  | ||||
| @@ -59,6 +59,10 @@ export class DocumentDetailComponent implements OnInit { | ||||
|     private documentListViewService: DocumentListViewService, | ||||
|     private titleService: Title) { } | ||||
|  | ||||
|   getContentType() { | ||||
|     return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     this.documentForm.valueChanges.subscribe(wow => { | ||||
|       Object.assign(this.document, this.documentForm.value) | ||||
| @@ -151,10 +155,13 @@ export class DocumentDetailComponent implements OnInit { | ||||
|   } | ||||
|  | ||||
|   delete() { | ||||
|     let modal = this.modalService.open(DeleteDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.message = `Do you really want to delete document '${this.document.title}'?` | ||||
|     modal.componentInstance.message2 = `The files for this document will be deleted permanently. This operation cannot be undone.` | ||||
|     modal.componentInstance.deleteClicked.subscribe(() => { | ||||
|     let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.title = "Confirm delete" | ||||
|     modal.componentInstance.messageBold = `Do you really want to delete document '${this.document.title}'?` | ||||
|     modal.componentInstance.message = `The files for this document will be deleted permanently. This operation cannot be undone.` | ||||
|     modal.componentInstance.btnClass = "btn-danger" | ||||
|     modal.componentInstance.btnCaption = "Delete document" | ||||
|     modal.componentInstance.confirmClicked.subscribe(() => { | ||||
|       this.documentsService.delete(this.document).subscribe(() => { | ||||
|         modal.close()   | ||||
|         this.close() | ||||
|   | ||||
| @@ -0,0 +1,23 @@ | ||||
| <h6> | ||||
|   <button type="button" class="btn btn-outline-secondary btn-sm mr-2" | ||||
|       (click)="expand = !expand"> | ||||
|       <svg class="buttonicon" fill="currentColor" *ngIf="!expand"> | ||||
|           <use xlink:href="assets/bootstrap-icons.svg#caret-down" /> | ||||
|       </svg> | ||||
|       <svg class="buttonicon" fill="currentColor" *ngIf="expand"> | ||||
|           <use xlink:href="assets/bootstrap-icons.svg#caret-up" /> | ||||
|       </svg> | ||||
|   </button> | ||||
|   {{title}} | ||||
| </h6> | ||||
|  | ||||
| <div #collapse="ngbCollapse" [(ngbCollapse)]="!expand"> | ||||
|   <table class="table table-borderless"> | ||||
|       <tbody> | ||||
|           <tr *ngFor="let m of metadata"> | ||||
|               <td>{{m.prefix}}:{{m.key}}</td> | ||||
|               <td>{{m.value}}</td> | ||||
|           </tr> | ||||
|       </tbody> | ||||
|   </table> | ||||
| </div> | ||||
| @@ -0,0 +1,25 @@ | ||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
|  | ||||
| import { MetadataCollapseComponent } from './metadata-collapse.component'; | ||||
|  | ||||
| describe('MetadataCollapseComponent', () => { | ||||
|   let component: MetadataCollapseComponent; | ||||
|   let fixture: ComponentFixture<MetadataCollapseComponent>; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       declarations: [ MetadataCollapseComponent ] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
|   }); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     fixture = TestBed.createComponent(MetadataCollapseComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
|  | ||||
|   it('should create', () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,23 @@ | ||||
| import { Component, Input, OnInit } from '@angular/core'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-metadata-collapse', | ||||
|   templateUrl: './metadata-collapse.component.html', | ||||
|   styleUrls: ['./metadata-collapse.component.scss'] | ||||
| }) | ||||
| export class MetadataCollapseComponent implements OnInit { | ||||
|  | ||||
|   constructor() { } | ||||
|  | ||||
|   expand = false | ||||
|  | ||||
|   @Input() | ||||
|   metadata | ||||
|  | ||||
|   @Input() | ||||
|   title = "Metadata" | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -12,7 +12,7 @@ | ||||
|               <a *ngIf="clickCorrespondent.observers.length ; else nolink" [routerLink]="" title="Filter by correspondent" (click)="clickCorrespondent.emit(document.correspondent)" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a> | ||||
|               <ng-template #nolink>{{(document.correspondent$ | async)?.name}}</ng-template>: | ||||
|             </ng-container> | ||||
|             {{document.title}} | ||||
|             {{document.title | documentTitle}} | ||||
|             <app-tag [tag]="t" linkTitle="Filter by tag" *ngFor="let t of document.tags$ | async" class="ml-1" (click)="clickTag.emit(t.id)" [clickable]="clickTag.observers.length"></app-tag> | ||||
|           </h5> | ||||
|           <h5 class="card-title" *ngIf="document.archive_serial_number">#{{document.archive_serial_number}}</h5> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <div class="col p-2 h-100" style="width: 16rem;"> | ||||
|   <div class="card h-100 shadow-sm"> | ||||
|     <div class=" border-bottom pr-1"> | ||||
|     <div class="border-bottom"> | ||||
|       <img class="card-img doc-img" [src]="getThumbUrl()"> | ||||
|       <div style="top: 0; right: 0; font-size: large" class="text-right position-absolute mr-1"> | ||||
|         <div *ngFor="let t of getTagsLimited$() | async"> | ||||
| @@ -17,7 +17,7 @@ | ||||
|         <ng-container *ngIf="document.correspondent"> | ||||
|           <a [routerLink]="" title="Filter by correspondent" (click)="clickCorrespondent.emit(document.correspondent)" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>: | ||||
|         </ng-container> | ||||
|         {{document.title}} | ||||
|         {{document.title | documentTitle}} | ||||
|       </p> | ||||
|     </div> | ||||
|     <div class="card-footer"> | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|   <div class="btn-group btn-group-toggle ml-2" ngbRadioGroup [(ngModel)]="list.sortDirection"> | ||||
|     <div ngbDropdown class="btn-group"> | ||||
|       <button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle>Sort by</button> | ||||
|       <div ngbDropdownMenu aria-labelledby="dropdownBasic1"> | ||||
|       <div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow"> | ||||
|         <button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="list.sortField = f.field" | ||||
|           [class.active]="list.sortField == f.field">{{f.name}}</button> | ||||
|       </div> | ||||
| @@ -79,7 +79,7 @@ | ||||
|   </app-document-card-large> | ||||
| </div> | ||||
|  | ||||
| <table class="table table-sm border shadow" *ngIf="displayMode == 'details'"> | ||||
| <table class="table table-sm border shadow-sm" *ngIf="displayMode == 'details'"> | ||||
|   <thead> | ||||
|     <th class="d-none d-lg-table-cell">ASN</th> | ||||
|     <th class="d-none d-md-table-cell">Correspondent</th> | ||||
| @@ -99,7 +99,7 @@ | ||||
|         </ng-container> | ||||
|       </td> | ||||
|       <td> | ||||
|         <a routerLink="/documents/{{d.id}}" title="Edit document" style="overflow-wrap: anywhere;">{{d.title}}</a> | ||||
|         <a routerLink="/documents/{{d.id}}" title="Edit document" style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a> | ||||
|         <app-tag [tag]="t" *ngFor="let t of d.tags$ | async" class="ml-1" clickable="true" linkTitle="Filter by tag" (click)="clickTag(t)"></app-tag> | ||||
|       </td> | ||||
|       <td class="d-none d-xl-table-cell"> | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/mat | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id'; | ||||
| import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive'; | ||||
| import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'; | ||||
| import { DeleteDialogComponent } from '../../common/delete-dialog/delete-dialog.component'; | ||||
| import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'; | ||||
|  | ||||
| @Directive() | ||||
| export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit { | ||||
| @@ -88,10 +88,13 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On | ||||
|   } | ||||
|  | ||||
|   openDeleteDialog(object: T) { | ||||
|     var activeModal = this.modalService.open(DeleteDialogComponent, {backdrop: 'static'}) | ||||
|     activeModal.componentInstance.message = `Do you really want to delete ${this.getObjectName(object)}?` | ||||
|     activeModal.componentInstance.message2 = "Associated documents will not be deleted." | ||||
|     activeModal.componentInstance.deleteClicked.subscribe(() => { | ||||
|     var activeModal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) | ||||
|     activeModal.componentInstance.title = "Confirm delete" | ||||
|     activeModal.componentInstance.messageBold = `Do you really want to delete ${this.getObjectName(object)}?` | ||||
|     activeModal.componentInstance.message = "Associated documents will not be deleted." | ||||
|     activeModal.componentInstance.btnClass = "btn-danger" | ||||
|     activeModal.componentInstance.btnCaption = "Delete" | ||||
|     activeModal.componentInstance.confirmPressed.subscribe(() => { | ||||
|       this.service.delete(object).subscribe(_ => { | ||||
|         activeModal.close() | ||||
|         this.reloadData() | ||||
|   | ||||
							
								
								
									
										8
									
								
								src-ui/src/app/pipes/document-title.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src-ui/src/app/pipes/document-title.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import { DocumentTitlePipe } from './document-title.pipe'; | ||||
|  | ||||
| describe('DocumentTitlePipe', () => { | ||||
|   it('create an instance', () => { | ||||
|     const pipe = new DocumentTitlePipe(); | ||||
|     expect(pipe).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										16
									
								
								src-ui/src/app/pipes/document-title.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src-ui/src/app/pipes/document-title.pipe.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import { Pipe, PipeTransform } from '@angular/core'; | ||||
|  | ||||
| @Pipe({ | ||||
|   name: 'documentTitle' | ||||
| }) | ||||
| export class DocumentTitlePipe implements PipeTransform { | ||||
|  | ||||
|   transform(value: string): unknown { | ||||
|     if (value) { | ||||
|       return value | ||||
|     } else { | ||||
|       return "(no title)" | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| import { SafePipe } from './safe.pipe'; | ||||
|  | ||||
| describe('SafePipe', () => { | ||||
|   it('create an instance', () => { | ||||
|     const pipe = new SafePipe(); | ||||
|     expect(pipe).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,19 +0,0 @@ | ||||
| import { Pipe, PipeTransform } from '@angular/core'; | ||||
| import { DomSanitizer } from '@angular/platform-browser'; | ||||
|  | ||||
| @Pipe({ | ||||
|   name: 'safe' | ||||
| }) | ||||
| export class SafePipe implements PipeTransform { | ||||
|  | ||||
|   constructor(private sanitizer: DomSanitizer) { } | ||||
|  | ||||
|   transform(url) { | ||||
|     if (url == null) { | ||||
|       return this.sanitizer.bypassSecurityTrustResourceUrl("") | ||||
|     } else { | ||||
|       return this.sanitizer.bypassSecurityTrustResourceUrl(url); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon