Feature: Implement custom fields for documents (#4502)

Adds custom fields of certain data types, attachable to documents and searchable

Co-Authored-By: Trenton H <797416+stumpylog@users.noreply.github.com>
This commit is contained in:
shamoon
2023-11-05 17:26:51 -08:00
parent 800f54f263
commit 10729f0362
67 changed files with 3199 additions and 421 deletions

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { FormArray, FormControl, FormGroup } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
import {
NgbDateStruct,
@@ -63,7 +63,12 @@ import { EditDialogMode } from '../common/edit-dialog/edit-dialog.component'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { FilterRule } from 'src/app/data/filter-rule'
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
import { ShareLinksDropdownComponent } from '../common/share-links-dropdown/share-links-dropdown.component'
import {
PaperlessCustomField,
PaperlessCustomFieldDataType,
} from 'src/app/data/paperless-custom-field'
import { PaperlessCustomFieldInstance } from 'src/app/data/paperless-custom-field-instance'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
enum DocumentDetailNavIDs {
Details = 1,
@@ -120,6 +125,7 @@ export class DocumentDetailComponent
archive_serial_number: new FormControl(),
tags: new FormControl([]),
permissions_form: new FormControl(null),
custom_fields: new FormArray([]),
})
previewCurrentPage: number = 1
@@ -135,6 +141,9 @@ export class DocumentDetailComponent
ogDate: Date
customFields: PaperlessCustomField[]
public readonly PaperlessCustomFieldDataType = PaperlessCustomFieldDataType
@ViewChild('nav') nav: NgbNav
@ViewChild('pdfPreview') set pdfPreview(element) {
// this gets called when compontent added or removed from DOM
@@ -166,6 +175,7 @@ export class DocumentDetailComponent
private storagePathService: StoragePathService,
private permissionsService: PermissionsService,
private userService: UserService,
private customFieldsService: CustomFieldsService,
private http: HttpClient
) {
super()
@@ -232,6 +242,8 @@ export class DocumentDetailComponent
.pipe(first(), takeUntil(this.unsubscribeNotifier))
.subscribe((result) => (this.users = result.results))
this.getCustomFields()
this.route.paramMap
.pipe(
takeUntil(this.unsubscribeNotifier),
@@ -324,6 +336,7 @@ export class DocumentDetailComponent
owner: doc.owner,
set_permissions: doc.permissions,
},
custom_fields: doc.custom_fields,
})
this.isDirty$ = dirtyCheck(
@@ -385,6 +398,8 @@ export class DocumentDetailComponent
updateComponent(doc: PaperlessDocument) {
this.document = doc
this.requiresPassword = false
// this.customFields = doc.custom_fields.concat([])
this.updateFormForCustomFields()
this.documentsService
.getMetadata(doc.id)
.pipe(first())
@@ -433,6 +448,10 @@ export class DocumentDetailComponent
if (!this.userCanEdit) this.documentForm.disable()
}
get customFieldFormFields(): FormArray {
return this.documentForm.get('custom_fields') as FormArray
}
createDocumentType(newName: string) {
var modal = this.modalService.open(DocumentTypeEditDialogComponent, {
backdrop: 'static',
@@ -510,6 +529,7 @@ export class DocumentDetailComponent
set_permissions: doc.permissions,
}
this.title = doc.title
this.updateFormForCustomFields()
this.documentForm.patchValue(doc)
this.openDocumentService.setDirty(doc, false)
},
@@ -533,6 +553,7 @@ export class DocumentDetailComponent
close && this.close()
this.networkActive = false
this.error = null
this.openDocumentService.refreshDocument(this.documentId)
},
error: (error) => {
this.networkActive = false
@@ -819,4 +840,61 @@ export class DocumentDetailComponent
this.documentListViewService.quickFilter(filterRules)
}
private getCustomFields() {
this.customFieldsService
.listAll()
.pipe(first(), takeUntil(this.unsubscribeNotifier))
.subscribe((result) => (this.customFields = result.results))
}
public refreshCustomFields() {
this.customFieldsService.clearCache()
this.getCustomFields()
}
public getCustomFieldFromInstance(
instance: PaperlessCustomFieldInstance
): PaperlessCustomField {
return this.customFields?.find((f) => f.id === instance.field)
}
public getCustomFieldError(index: number) {
const fieldError = this.error?.custom_fields?.[index]
return fieldError?.['non_field_errors'] ?? fieldError?.['value']
}
private updateFormForCustomFields(emitEvent: boolean = false) {
this.customFieldFormFields.clear({ emitEvent: false })
this.document.custom_fields?.forEach((fieldInstance) => {
this.customFieldFormFields.push(
new FormGroup({
field: new FormControl(
this.getCustomFieldFromInstance(fieldInstance)?.id
),
value: new FormControl(fieldInstance.value),
}),
{ emitEvent }
)
})
}
public addField(field: PaperlessCustomField) {
this.document.custom_fields.push({
field: field.id,
value: null,
document: this.documentId,
created: new Date(),
})
this.updateFormForCustomFields(true)
}
public removeField(fieldInstance: PaperlessCustomFieldInstance) {
this.document.custom_fields.splice(
this.document.custom_fields.indexOf(fieldInstance),
1
)
this.updateFormForCustomFields(true)
this.documentForm.updateValueAndValidity()
}
}