mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-01-28 22:59:03 -06:00
Enhancement: user control of doc details fields (#11906)
This commit is contained in:
@@ -103,22 +103,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Items per page</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
|
|
||||||
<select class="form-select" formControlName="documentListItemPerPage">
|
|
||||||
<option [ngValue]="10">10</option>
|
|
||||||
<option [ngValue]="25">25</option>
|
|
||||||
<option [ngValue]="50">50</option>
|
|
||||||
<option [ngValue]="100">100</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Sidebar</span>
|
<span i18n>Sidebar</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,8 +137,28 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 ps-xl-5">
|
||||||
|
<h5 class="mt-3 mt-md-0" i18n>Global search</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" id="update-checking" i18n>Update checking</h5>
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Full search links to</span>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-3">
|
||||||
|
<select class="form-select" formControlName="searchLink">
|
||||||
|
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
||||||
|
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="mt-3 mt-md-0" id="update-checking" i18n>Update checking</h5>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col d-flex flex-row align-items-start">
|
<div class="col d-flex flex-row align-items-start">
|
||||||
<pngx-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled"></pngx-input-check>
|
<pngx-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled"></pngx-input-check>
|
||||||
@@ -179,11 +183,33 @@
|
|||||||
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-6 ps-xl-5">
|
</div>
|
||||||
<h5 class="mt-3 mt-md-0" i18n>Document editing</h5>
|
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li [ngbNavItem]="SettingsNavIDs.Documents">
|
||||||
|
<a ngbNavLink i18n>Documents</a>
|
||||||
|
<ng-template ngbNavContent>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6 pe-xl-5">
|
||||||
|
<h5 i18n>Documents</h5>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Items per page</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<select class="form-select" formControlName="documentListItemPerPage">
|
||||||
|
<option [ngValue]="10">10</option>
|
||||||
|
<option [ngValue]="25">25</option>
|
||||||
|
<option [ngValue]="50">50</option>
|
||||||
|
<option [ngValue]="100">100</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="mt-3" i18n>Document editing</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
||||||
@@ -209,31 +235,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show document thumbnail during loading" formControlName="documentEditingOverlayThumbnail"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show document thumbnail during loading" formControlName="documentEditingOverlayThumbnail"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Global search</h5>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col">
|
||||||
<span i18n>Full search links to</span>
|
<p class="mb-2" i18n>Built-in fields to show:</p>
|
||||||
</div>
|
@for (option of documentDetailFieldOptions; track option.id) {
|
||||||
<div class="col mb-3">
|
<div class="form-check ms-3">
|
||||||
<select class="form-select" formControlName="searchLink">
|
<input class="form-check-input" type="checkbox"
|
||||||
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
[id]="'documentDetailField-' + option.id"
|
||||||
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
[checked]="isDocumentDetailFieldShown(option.id)"
|
||||||
</select>
|
(change)="toggleDocumentDetailField(option.id, $event.target.checked)" />
|
||||||
|
<label class="form-check-label" [for]="'documentDetailField-' + option.id">
|
||||||
|
{{ option.label }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p class="small text-muted mt-1" i18n>Uncheck fields to hide them on the document details page.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-6 ps-xl-5">
|
||||||
<h5 class="mt-3" i18n>Bulk editing</h5>
|
<h5 class="mt-3" i18n>Bulk editing</h5>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@@ -248,10 +274,8 @@
|
|||||||
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|||||||
@@ -201,9 +201,9 @@ describe('SettingsComponent', () => {
|
|||||||
const navigateSpy = jest.spyOn(router, 'navigate')
|
const navigateSpy = jest.spyOn(router, 'navigate')
|
||||||
const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavLink))
|
const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavLink))
|
||||||
tabButtons[1].nativeElement.dispatchEvent(new MouseEvent('click'))
|
tabButtons[1].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions'])
|
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'documents'])
|
||||||
tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click'))
|
tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'notifications'])
|
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions'])
|
||||||
|
|
||||||
const initSpy = jest.spyOn(component, 'initialize')
|
const initSpy = jest.spyOn(component, 'initialize')
|
||||||
component.isDirty = true // mock dirty
|
component.isDirty = true // mock dirty
|
||||||
@@ -213,8 +213,8 @@ describe('SettingsComponent', () => {
|
|||||||
expect(initSpy).not.toHaveBeenCalled()
|
expect(initSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
navigateSpy.mockResolvedValueOnce(true) // nav accepted even though dirty
|
navigateSpy.mockResolvedValueOnce(true) // nav accepted even though dirty
|
||||||
tabButtons[1].nativeElement.dispatchEvent(new MouseEvent('click'))
|
tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'notifications'])
|
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions'])
|
||||||
expect(initSpy).toHaveBeenCalled()
|
expect(initSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ describe('SettingsComponent', () => {
|
|||||||
activatedRoute.snapshot.fragment = '#notifications'
|
activatedRoute.snapshot.fragment = '#notifications'
|
||||||
const scrollSpy = jest.spyOn(viewportScroller, 'scrollToAnchor')
|
const scrollSpy = jest.spyOn(viewportScroller, 'scrollToAnchor')
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
expect(component.activeNavID).toEqual(3) // Notifications
|
expect(component.activeNavID).toEqual(4) // Notifications
|
||||||
component.ngAfterViewInit()
|
component.ngAfterViewInit()
|
||||||
expect(scrollSpy).toHaveBeenCalledWith('#notifications')
|
expect(scrollSpy).toHaveBeenCalledWith('#notifications')
|
||||||
})
|
})
|
||||||
@@ -251,7 +251,7 @@ describe('SettingsComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
expect(storeSpy).toHaveBeenCalled()
|
expect(storeSpy).toHaveBeenCalled()
|
||||||
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
||||||
expect(setSpy).toHaveBeenCalledTimes(30)
|
expect(setSpy).toHaveBeenCalledTimes(31)
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
storeSpy.mockReturnValueOnce(of(true))
|
storeSpy.mockReturnValueOnce(of(true))
|
||||||
@@ -366,4 +366,22 @@ describe('SettingsComponent', () => {
|
|||||||
settingsService.settingsSaved.emit(true)
|
settingsService.settingsSaved.emit(true)
|
||||||
expect(maybeRefreshSpy).toHaveBeenCalled()
|
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should support toggling document detail fields', () => {
|
||||||
|
completeSetup()
|
||||||
|
const field = 'storage_path'
|
||||||
|
expect(
|
||||||
|
component.settingsForm.get('documentDetailsHiddenFields').value.length
|
||||||
|
).toEqual(0)
|
||||||
|
component.toggleDocumentDetailField(field, false)
|
||||||
|
expect(
|
||||||
|
component.settingsForm.get('documentDetailsHiddenFields').value.length
|
||||||
|
).toEqual(1)
|
||||||
|
expect(component.isDocumentDetailFieldShown(field)).toBeFalsy()
|
||||||
|
component.toggleDocumentDetailField(field, true)
|
||||||
|
expect(
|
||||||
|
component.settingsForm.get('documentDetailsHiddenFields').value.length
|
||||||
|
).toEqual(0)
|
||||||
|
expect(component.isDocumentDetailFieldShown(field)).toBeTruthy()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission
|
|||||||
|
|
||||||
enum SettingsNavIDs {
|
enum SettingsNavIDs {
|
||||||
General = 1,
|
General = 1,
|
||||||
Permissions = 2,
|
Documents = 2,
|
||||||
Notifications = 3,
|
Permissions = 3,
|
||||||
SavedViews = 4,
|
Notifications = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
const systemLanguage = { code: '', name: $localize`Use system language` }
|
const systemLanguage = { code: '', name: $localize`Use system language` }
|
||||||
@@ -81,6 +81,25 @@ const systemDateFormat = {
|
|||||||
name: $localize`Use date format of display language`,
|
name: $localize`Use date format of display language`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DocumentDetailFieldID {
|
||||||
|
ArchiveSerialNumber = 'archive_serial_number',
|
||||||
|
Correspondent = 'correspondent',
|
||||||
|
DocumentType = 'document_type',
|
||||||
|
StoragePath = 'storage_path',
|
||||||
|
Tags = 'tags',
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentDetailFieldOptions = [
|
||||||
|
{
|
||||||
|
id: DocumentDetailFieldID.ArchiveSerialNumber,
|
||||||
|
label: $localize`Archive serial number`,
|
||||||
|
},
|
||||||
|
{ id: DocumentDetailFieldID.Correspondent, label: $localize`Correspondent` },
|
||||||
|
{ id: DocumentDetailFieldID.DocumentType, label: $localize`Document type` },
|
||||||
|
{ id: DocumentDetailFieldID.StoragePath, label: $localize`Storage path` },
|
||||||
|
{ id: DocumentDetailFieldID.Tags, label: $localize`Tags` },
|
||||||
|
]
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-settings',
|
selector: 'pngx-settings',
|
||||||
templateUrl: './settings.component.html',
|
templateUrl: './settings.component.html',
|
||||||
@@ -146,6 +165,7 @@ export class SettingsComponent
|
|||||||
pdfViewerDefaultZoom: new FormControl(null),
|
pdfViewerDefaultZoom: new FormControl(null),
|
||||||
documentEditingRemoveInboxTags: new FormControl(null),
|
documentEditingRemoveInboxTags: new FormControl(null),
|
||||||
documentEditingOverlayThumbnail: new FormControl(null),
|
documentEditingOverlayThumbnail: new FormControl(null),
|
||||||
|
documentDetailsHiddenFields: new FormControl([]),
|
||||||
searchDbOnly: new FormControl(null),
|
searchDbOnly: new FormControl(null),
|
||||||
searchLink: new FormControl(null),
|
searchLink: new FormControl(null),
|
||||||
|
|
||||||
@@ -176,6 +196,8 @@ export class SettingsComponent
|
|||||||
|
|
||||||
public readonly ZoomSetting = ZoomSetting
|
public readonly ZoomSetting = ZoomSetting
|
||||||
|
|
||||||
|
public readonly documentDetailFieldOptions = documentDetailFieldOptions
|
||||||
|
|
||||||
get systemStatusHasErrors(): boolean {
|
get systemStatusHasErrors(): boolean {
|
||||||
return (
|
return (
|
||||||
this.systemStatus.database.status === SystemStatusItemStatus.ERROR ||
|
this.systemStatus.database.status === SystemStatusItemStatus.ERROR ||
|
||||||
@@ -336,6 +358,9 @@ export class SettingsComponent
|
|||||||
documentEditingOverlayThumbnail: this.settings.get(
|
documentEditingOverlayThumbnail: this.settings.get(
|
||||||
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
||||||
),
|
),
|
||||||
|
documentDetailsHiddenFields: this.settings.get(
|
||||||
|
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS
|
||||||
|
),
|
||||||
searchDbOnly: this.settings.get(SETTINGS_KEYS.SEARCH_DB_ONLY),
|
searchDbOnly: this.settings.get(SETTINGS_KEYS.SEARCH_DB_ONLY),
|
||||||
searchLink: this.settings.get(SETTINGS_KEYS.SEARCH_FULL_TYPE),
|
searchLink: this.settings.get(SETTINGS_KEYS.SEARCH_FULL_TYPE),
|
||||||
}
|
}
|
||||||
@@ -526,6 +551,10 @@ export class SettingsComponent
|
|||||||
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL,
|
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL,
|
||||||
this.settingsForm.value.documentEditingOverlayThumbnail
|
this.settingsForm.value.documentEditingOverlayThumbnail
|
||||||
)
|
)
|
||||||
|
this.settings.set(
|
||||||
|
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
||||||
|
this.settingsForm.value.documentDetailsHiddenFields
|
||||||
|
)
|
||||||
this.settings.set(
|
this.settings.set(
|
||||||
SETTINGS_KEYS.SEARCH_DB_ONLY,
|
SETTINGS_KEYS.SEARCH_DB_ONLY,
|
||||||
this.settingsForm.value.searchDbOnly
|
this.settingsForm.value.searchDbOnly
|
||||||
@@ -587,6 +616,26 @@ export class SettingsComponent
|
|||||||
this.settingsForm.get('themeColor').patchValue('')
|
this.settingsForm.get('themeColor').patchValue('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDocumentDetailFieldShown(fieldId: string): boolean {
|
||||||
|
const hiddenFields =
|
||||||
|
this.settingsForm.value.documentDetailsHiddenFields || []
|
||||||
|
return !hiddenFields.includes(fieldId)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDocumentDetailField(fieldId: string, checked: boolean) {
|
||||||
|
const hiddenFields = new Set(
|
||||||
|
this.settingsForm.value.documentDetailsHiddenFields || []
|
||||||
|
)
|
||||||
|
if (checked) {
|
||||||
|
hiddenFields.delete(fieldId)
|
||||||
|
} else {
|
||||||
|
hiddenFields.add(fieldId)
|
||||||
|
}
|
||||||
|
this.settingsForm
|
||||||
|
.get('documentDetailsHiddenFields')
|
||||||
|
.setValue(Array.from(hiddenFields))
|
||||||
|
}
|
||||||
|
|
||||||
showSystemStatus() {
|
showSystemStatus() {
|
||||||
const modal: NgbModalRef = this.modalService.open(
|
const modal: NgbModalRef = this.modalService.open(
|
||||||
SystemStatusDialogComponent,
|
SystemStatusDialogComponent,
|
||||||
|
|||||||
@@ -146,16 +146,26 @@
|
|||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div>
|
<div>
|
||||||
<pngx-input-text #inputTitle i18n-title title="Title" formControlName="title" [horizontal]="true" [suggestion]="suggestions?.title" (keyup)="titleKeyUp($event)" [error]="error?.title"></pngx-input-text>
|
<pngx-input-text #inputTitle i18n-title title="Title" formControlName="title" [horizontal]="true" [suggestion]="suggestions?.title" (keyup)="titleKeyUp($event)" [error]="error?.title"></pngx-input-text>
|
||||||
<pngx-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" [horizontal]="true" formControlName='archive_serial_number'></pngx-input-number>
|
@if (!isFieldHidden(DocumentDetailFieldID.ArchiveSerialNumber)) {
|
||||||
|
<pngx-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" [horizontal]="true" formControlName='archive_serial_number'></pngx-input-number>
|
||||||
|
}
|
||||||
<pngx-input-date i18n-title title="Date created" formControlName="created" [suggestions]="suggestions?.dates" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
<pngx-input-date i18n-title title="Date created" formControlName="created" [suggestions]="suggestions?.dates" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
||||||
[error]="error?.created"></pngx-input-date>
|
[error]="error?.created"></pngx-input-date>
|
||||||
<pngx-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.Correspondent)"
|
@if (!isFieldHidden(DocumentDetailFieldID.Correspondent)) {
|
||||||
(createNew)="createCorrespondent($event)" [hideAddButton]="createDisabled(DataType.Correspondent)" [suggestions]="suggestions?.correspondents" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }"></pngx-input-select>
|
<pngx-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.Correspondent)"
|
||||||
<pngx-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.DocumentType)"
|
(createNew)="createCorrespondent($event)" [hideAddButton]="createDisabled(DataType.Correspondent)" [suggestions]="suggestions?.correspondents" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }"></pngx-input-select>
|
||||||
(createNew)="createDocumentType($event)" [hideAddButton]="createDisabled(DataType.DocumentType)" [suggestions]="suggestions?.document_types" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }"></pngx-input-select>
|
}
|
||||||
<pngx-input-select [items]="storagePaths" i18n-title title="Storage path" formControlName="storage_path" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.StoragePath)"
|
@if (!isFieldHidden(DocumentDetailFieldID.DocumentType)) {
|
||||||
(createNew)="createStoragePath($event)" [hideAddButton]="createDisabled(DataType.StoragePath)" [suggestions]="suggestions?.storage_paths" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }"></pngx-input-select>
|
<pngx-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.DocumentType)"
|
||||||
<pngx-input-tags #tagsInput formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.Tag)" [hideAddButton]="createDisabled(DataType.Tag)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
(createNew)="createDocumentType($event)" [hideAddButton]="createDisabled(DataType.DocumentType)" [suggestions]="suggestions?.document_types" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }"></pngx-input-select>
|
||||||
|
}
|
||||||
|
@if (!isFieldHidden(DocumentDetailFieldID.StoragePath)) {
|
||||||
|
<pngx-input-select [items]="storagePaths" i18n-title title="Storage path" formControlName="storage_path" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.StoragePath)"
|
||||||
|
(createNew)="createStoragePath($event)" [hideAddButton]="createDisabled(DataType.StoragePath)" [suggestions]="suggestions?.storage_paths" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }"></pngx-input-select>
|
||||||
|
}
|
||||||
|
@if (!isFieldHidden(DocumentDetailFieldID.Tags)) {
|
||||||
|
<pngx-input-tags #tagsInput formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event, DataType.Tag)" [hideAddButton]="createDisabled(DataType.Tag)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
||||||
|
}
|
||||||
@for (fieldInstance of document?.custom_fields; track fieldInstance.field; let i = $index) {
|
@for (fieldInstance of document?.custom_fields; track fieldInstance.field; let i = $index) {
|
||||||
<div [formGroup]="customFieldFormFields.controls[i]">
|
<div [formGroup]="customFieldFormFields.controls[i]">
|
||||||
@switch (getCustomFieldFromInstance(fieldInstance)?.data_type) {
|
@switch (getCustomFieldFromInstance(fieldInstance)?.data_type) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import {
|
|||||||
} from 'src/app/data/filter-rule-type'
|
} from 'src/app/data/filter-rule-type'
|
||||||
import { StoragePath } from 'src/app/data/storage-path'
|
import { StoragePath } from 'src/app/data/storage-path'
|
||||||
import { Tag } from 'src/app/data/tag'
|
import { Tag } from 'src/app/data/tag'
|
||||||
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||||
@@ -1015,7 +1016,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
it('should display built-in pdf viewer if not disabled', () => {
|
it('should display built-in pdf viewer if not disabled', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
component.document.archived_file_name = 'file.pdf'
|
component.document.archived_file_name = 'file.pdf'
|
||||||
jest.spyOn(settingsService, 'get').mockReturnValue(false)
|
settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false)
|
||||||
expect(component.useNativePdfViewer).toBeFalsy()
|
expect(component.useNativePdfViewer).toBeFalsy()
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(fixture.debugElement.query(By.css('pdf-viewer'))).not.toBeNull()
|
expect(fixture.debugElement.query(By.css('pdf-viewer'))).not.toBeNull()
|
||||||
@@ -1024,7 +1025,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
it('should display native pdf viewer if enabled', () => {
|
it('should display native pdf viewer if enabled', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
component.document.archived_file_name = 'file.pdf'
|
component.document.archived_file_name = 'file.pdf'
|
||||||
jest.spyOn(settingsService, 'get').mockReturnValue(true)
|
settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, true)
|
||||||
expect(component.useNativePdfViewer).toBeTruthy()
|
expect(component.useNativePdfViewer).toBeTruthy()
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(fixture.debugElement.query(By.css('object'))).not.toBeNull()
|
expect(fixture.debugElement.query(By.css('object'))).not.toBeNull()
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ import { ToastService } from 'src/app/services/toast.service'
|
|||||||
import { getFilenameFromContentDisposition } from 'src/app/utils/http'
|
import { getFilenameFromContentDisposition } from 'src/app/utils/http'
|
||||||
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
|
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
|
||||||
import * as UTIF from 'utif'
|
import * as UTIF from 'utif'
|
||||||
|
import { DocumentDetailFieldID } from '../admin/settings/settings.component'
|
||||||
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { PasswordRemovalConfirmDialogComponent } from '../common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component'
|
import { PasswordRemovalConfirmDialogComponent } from '../common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component'
|
||||||
import { CustomFieldsDropdownComponent } from '../common/custom-fields-dropdown/custom-fields-dropdown.component'
|
import { CustomFieldsDropdownComponent } from '../common/custom-fields-dropdown/custom-fields-dropdown.component'
|
||||||
@@ -281,6 +282,8 @@ export class DocumentDetailComponent
|
|||||||
|
|
||||||
public readonly DataType = DataType
|
public readonly DataType = DataType
|
||||||
|
|
||||||
|
public readonly DocumentDetailFieldID = DocumentDetailFieldID
|
||||||
|
|
||||||
@ViewChild('nav') nav: NgbNav
|
@ViewChild('nav') nav: NgbNav
|
||||||
@ViewChild('pdfPreview') set pdfPreview(element) {
|
@ViewChild('pdfPreview') set pdfPreview(element) {
|
||||||
// this gets called when component added or removed from DOM
|
// this gets called when component added or removed from DOM
|
||||||
@@ -327,6 +330,12 @@ export class DocumentDetailComponent
|
|||||||
return this.settings.get(SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL)
|
return this.settings.get(SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFieldHidden(fieldId: DocumentDetailFieldID): boolean {
|
||||||
|
return this.settings
|
||||||
|
.get(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS)
|
||||||
|
.includes(fieldId)
|
||||||
|
}
|
||||||
|
|
||||||
private getRenderType(mimeType: string): ContentRenderType {
|
private getRenderType(mimeType: string): ContentRenderType {
|
||||||
if (!mimeType) return ContentRenderType.Unknown
|
if (!mimeType) return ContentRenderType.Unknown
|
||||||
if (mimeType === 'application/pdf') {
|
if (mimeType === 'application/pdf') {
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ export const SETTINGS_KEYS = {
|
|||||||
'general-settings:document-editing:remove-inbox-tags',
|
'general-settings:document-editing:remove-inbox-tags',
|
||||||
DOCUMENT_EDITING_OVERLAY_THUMBNAIL:
|
DOCUMENT_EDITING_OVERLAY_THUMBNAIL:
|
||||||
'general-settings:document-editing:overlay-thumbnail',
|
'general-settings:document-editing:overlay-thumbnail',
|
||||||
|
DOCUMENT_DETAILS_HIDDEN_FIELDS:
|
||||||
|
'general-settings:document-details:hidden-fields',
|
||||||
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
||||||
SEARCH_FULL_TYPE: 'general-settings:search:more-link',
|
SEARCH_FULL_TYPE: 'general-settings:search:more-link',
|
||||||
EMPTY_TRASH_DELAY: 'trash_delay',
|
EMPTY_TRASH_DELAY: 'trash_delay',
|
||||||
@@ -255,6 +257,11 @@ export const SETTINGS: UiSetting[] = [
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: SETTINGS_KEYS.SEARCH_DB_ONLY,
|
key: SETTINGS_KEYS.SEARCH_DB_ONLY,
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|||||||
Reference in New Issue
Block a user