From dac15ea6bec32605f95d9cc59f987fcdd0e61f5f Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 8 Sep 2025 08:13:55 -0700 Subject: [PATCH] Super basic UI stuff [ci skip] --- .../document-detail.component.html | 27 ++++++++ .../document-detail.component.ts | 69 ++++++++++++++++++- src-ui/src/app/data/document.ts | 4 ++ .../src/app/services/rest/document.service.ts | 10 +++ 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index c926c82d9..80a89f180 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -44,6 +44,27 @@ } + @if (document?.versions?.length > 0) { +
+ + +
+ } +
+ + + +
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index d139550c0..1c7386ea5 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -220,6 +220,8 @@ export class DocumentDetailComponent titleSubject: Subject = new Subject() previewUrl: string thumbUrl: string + // Versioning: which document ID to use for file preview/download + selectedVersionId: number previewText: string previewLoaded: boolean = false tiffURL: string @@ -268,6 +270,7 @@ export class DocumentDetailComponent public readonly DataType = DataType @ViewChild('nav') nav: NgbNav + @ViewChild('versionFileInput') versionFileInput @ViewChild('pdfPreview') set pdfPreview(element) { // this gets called when component added or removed from DOM if ( @@ -396,7 +399,10 @@ export class DocumentDetailComponent } private loadDocument(documentId: number): void { - this.previewUrl = this.documentsService.getPreviewUrl(documentId) + this.selectedVersionId = documentId + this.previewUrl = this.documentsService.getPreviewUrl( + this.selectedVersionId + ) this.http .get(this.previewUrl, { responseType: 'text' }) .pipe( @@ -411,7 +417,7 @@ export class DocumentDetailComponent err.message ?? err.toString() }`), }) - this.thumbUrl = this.documentsService.getThumbUrl(documentId) + this.thumbUrl = this.documentsService.getThumbUrl(this.selectedVersionId) this.documentsService .get(documentId) .pipe( @@ -632,6 +638,8 @@ export class DocumentDetailComponent updateComponent(doc: Document) { this.document = doc + // Default selected version is the head document + this.selectedVersionId = doc.id this.requiresPassword = false this.updateFormForCustomFields() if (this.archiveContentRenderType === ContentRenderType.TIFF) { @@ -696,6 +704,30 @@ export class DocumentDetailComponent this.prepareForm(doc) } + // Update file preview and download target to a specific version (by document id) + selectVersion(versionId: number) { + this.selectedVersionId = versionId + this.previewUrl = this.documentsService.getPreviewUrl( + this.selectedVersionId + ) + this.thumbUrl = this.documentsService.getThumbUrl(this.selectedVersionId) + // For text previews, refresh content + this.http + .get(this.previewUrl, { responseType: 'text' }) + .pipe( + first(), + takeUntil(this.unsubscribeNotifier), + takeUntil(this.docChangeNotifier) + ) + .subscribe({ + next: (res) => (this.previewText = res.toString()), + error: (err) => + (this.previewText = $localize`An error occurred loading content: ${ + err.message ?? err.toString() + }`), + }) + } + get customFieldFormFields(): FormArray { return this.documentForm.get('custom_fields') as FormArray } @@ -1043,10 +1075,41 @@ export class DocumentDetailComponent }) } + // Upload a new file version for this document + triggerUploadVersion() { + this.versionFileInput?.nativeElement?.click() + } + + onVersionFileSelected(event: Event) { + const input = event.target as HTMLInputElement + if (!input?.files || input.files.length === 0) return + const file = input.files[0] + // Reset input to allow re-selection of the same file later + input.value = '' + this.documentsService + .uploadVersion(this.documentId, file) + .pipe(first()) + .subscribe({ + next: () => { + this.toastService.showInfo( + $localize`Uploading new version. Processing will happen in the background.` + ) + // Refresh metadata to reflect that versions changed (when ready) + this.openDocumentService.refreshDocument(this.documentId) + }, + error: (error) => { + this.toastService.showError( + $localize`Error uploading new version`, + error + ) + }, + }) + } + download(original: boolean = false) { this.downloading = true const downloadUrl = this.documentsService.getDownloadUrl( - this.documentId, + this.selectedVersionId || this.documentId, original ) this.http diff --git a/src-ui/src/app/data/document.ts b/src-ui/src/app/data/document.ts index 8aae31945..8b7d32876 100644 --- a/src-ui/src/app/data/document.ts +++ b/src-ui/src/app/data/document.ts @@ -159,6 +159,10 @@ export interface Document extends ObjectWithPermissions { page_count?: number + // Versioning + head_version?: number + versions?: number[] + // Frontend only __changedFields?: string[] } diff --git a/src-ui/src/app/services/rest/document.service.ts b/src-ui/src/app/services/rest/document.service.ts index 4f52633ea..b70e953b9 100644 --- a/src-ui/src/app/services/rest/document.service.ts +++ b/src-ui/src/app/services/rest/document.service.ts @@ -184,6 +184,16 @@ export class DocumentService extends AbstractPaperlessService { return url } + uploadVersion(documentId: number, file: File) { + const formData = new FormData() + formData.append('document', file, file.name) + return this.http.post( + this.getResourceUrl(documentId, 'update_version'), + formData, + { reportProgress: true, observe: 'events' } + ) + } + getNextAsn(): Observable { return this.http.get(this.getResourceUrl(null, 'next_asn')) }