diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index b228dac32..807368aa6 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -103,22 +103,6 @@
-
- Items per page -
-
- - - -
-
- -
Sidebar
@@ -153,8 +137,28 @@
+ +
+
Global search
+
+
+ +
+
-
Update checking
+
+
+ Full search links to +
+
+ +
+
+ +
Update checking
@@ -179,11 +183,33 @@
-
-
-
Document editing
+
+ + + +
  • + Documents + +
    +
    +
    Documents
    +
    +
    + Items per page +
    +
    + +
    +
    + +
    Document editing
    @@ -209,31 +235,31 @@
    -
    +
    -
    Global search
    -
    -
    - -
    -
    -
    -
    - Full search links to -
    -
    - +
    +

    Built-in fields to show:

    + @for (option of documentDetailFieldOptions; track option.id) { +
    + + +
    + } +

    Uncheck fields to hide them on the document details page.

    - +
    +
    Bulk editing
    @@ -248,10 +274,8 @@
    -
    -
  • diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 650d6d8ea..62a5aa363 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -201,9 +201,9 @@ describe('SettingsComponent', () => { const navigateSpy = jest.spyOn(router, 'navigate') const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavLink)) tabButtons[1].nativeElement.dispatchEvent(new MouseEvent('click')) - expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions']) + expect(navigateSpy).toHaveBeenCalledWith(['settings', 'documents']) tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click')) - expect(navigateSpy).toHaveBeenCalledWith(['settings', 'notifications']) + expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions']) const initSpy = jest.spyOn(component, 'initialize') component.isDirty = true // mock dirty @@ -213,8 +213,8 @@ describe('SettingsComponent', () => { expect(initSpy).not.toHaveBeenCalled() navigateSpy.mockResolvedValueOnce(true) // nav accepted even though dirty - tabButtons[1].nativeElement.dispatchEvent(new MouseEvent('click')) - expect(navigateSpy).toHaveBeenCalledWith(['settings', 'notifications']) + tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click')) + expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions']) expect(initSpy).toHaveBeenCalled() }) @@ -226,7 +226,7 @@ describe('SettingsComponent', () => { activatedRoute.snapshot.fragment = '#notifications' const scrollSpy = jest.spyOn(viewportScroller, 'scrollToAnchor') component.ngOnInit() - expect(component.activeNavID).toEqual(3) // Notifications + expect(component.activeNavID).toEqual(4) // Notifications component.ngAfterViewInit() expect(scrollSpy).toHaveBeenCalledWith('#notifications') }) @@ -251,7 +251,7 @@ describe('SettingsComponent', () => { expect(toastErrorSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled() - expect(setSpy).toHaveBeenCalledTimes(30) + expect(setSpy).toHaveBeenCalledTimes(31) // succeed storeSpy.mockReturnValueOnce(of(true)) @@ -366,4 +366,22 @@ describe('SettingsComponent', () => { settingsService.settingsSaved.emit(true) 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() + }) }) diff --git a/src-ui/src/app/components/admin/settings/settings.component.ts b/src-ui/src/app/components/admin/settings/settings.component.ts index 614d2fcd0..990944ff6 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -70,9 +70,9 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission enum SettingsNavIDs { General = 1, - Permissions = 2, - Notifications = 3, - SavedViews = 4, + Documents = 2, + Permissions = 3, + Notifications = 4, } const systemLanguage = { code: '', name: $localize`Use system language` } @@ -81,6 +81,25 @@ const systemDateFormat = { 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({ selector: 'pngx-settings', templateUrl: './settings.component.html', @@ -146,6 +165,7 @@ export class SettingsComponent pdfViewerDefaultZoom: new FormControl(null), documentEditingRemoveInboxTags: new FormControl(null), documentEditingOverlayThumbnail: new FormControl(null), + documentDetailsHiddenFields: new FormControl([]), searchDbOnly: new FormControl(null), searchLink: new FormControl(null), @@ -176,6 +196,8 @@ export class SettingsComponent public readonly ZoomSetting = ZoomSetting + public readonly documentDetailFieldOptions = documentDetailFieldOptions + get systemStatusHasErrors(): boolean { return ( this.systemStatus.database.status === SystemStatusItemStatus.ERROR || @@ -336,6 +358,9 @@ export class SettingsComponent documentEditingOverlayThumbnail: this.settings.get( 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), searchLink: this.settings.get(SETTINGS_KEYS.SEARCH_FULL_TYPE), } @@ -526,6 +551,10 @@ export class SettingsComponent SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL, this.settingsForm.value.documentEditingOverlayThumbnail ) + this.settings.set( + SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS, + this.settingsForm.value.documentDetailsHiddenFields + ) this.settings.set( SETTINGS_KEYS.SEARCH_DB_ONLY, this.settingsForm.value.searchDbOnly @@ -587,6 +616,26 @@ export class SettingsComponent 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() { const modal: NgbModalRef = this.modalService.open( SystemStatusDialogComponent, 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 5ca002479..306152cc4 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 @@ -146,16 +146,26 @@
    - + @if (!isFieldHidden(DocumentDetailFieldID.ArchiveSerialNumber)) { + + } - - - - + @if (!isFieldHidden(DocumentDetailFieldID.Correspondent)) { + + } + @if (!isFieldHidden(DocumentDetailFieldID.DocumentType)) { + + } + @if (!isFieldHidden(DocumentDetailFieldID.StoragePath)) { + + } + @if (!isFieldHidden(DocumentDetailFieldID.Tags)) { + + } @for (fieldInstance of document?.custom_fields; track fieldInstance.field; let i = $index) {
    @switch (getCustomFieldFromInstance(fieldInstance)?.data_type) { diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index d1d10c985..809478816 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -48,6 +48,7 @@ import { } from 'src/app/data/filter-rule-type' import { StoragePath } from 'src/app/data/storage-path' 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 { CustomDatePipe } from 'src/app/pipes/custom-date.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', () => { initNormally() 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() fixture.detectChanges() expect(fixture.debugElement.query(By.css('pdf-viewer'))).not.toBeNull() @@ -1024,7 +1025,7 @@ describe('DocumentDetailComponent', () => { it('should display native pdf viewer if enabled', () => { initNormally() 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() fixture.detectChanges() expect(fixture.debugElement.query(By.css('object'))).not.toBeNull() 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 917597ef6..8c22f53c2 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 @@ -84,6 +84,7 @@ import { ToastService } from 'src/app/services/toast.service' import { getFilenameFromContentDisposition } from 'src/app/utils/http' import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter' import * as UTIF from 'utif' +import { DocumentDetailFieldID } from '../admin/settings/settings.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 { CustomFieldsDropdownComponent } from '../common/custom-fields-dropdown/custom-fields-dropdown.component' @@ -281,6 +282,8 @@ export class DocumentDetailComponent public readonly DataType = DataType + public readonly DocumentDetailFieldID = DocumentDetailFieldID + @ViewChild('nav') nav: NgbNav @ViewChild('pdfPreview') set pdfPreview(element) { // 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) } + isFieldHidden(fieldId: DocumentDetailFieldID): boolean { + return this.settings + .get(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS) + .includes(fieldId) + } + private getRenderType(mimeType: string): ContentRenderType { if (!mimeType) return ContentRenderType.Unknown if (mimeType === 'application/pdf') { diff --git a/src-ui/src/app/data/ui-settings.ts b/src-ui/src/app/data/ui-settings.ts index e797fe9b3..827a1b82d 100644 --- a/src-ui/src/app/data/ui-settings.ts +++ b/src-ui/src/app/data/ui-settings.ts @@ -70,6 +70,8 @@ export const SETTINGS_KEYS = { 'general-settings:document-editing:remove-inbox-tags', 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_FULL_TYPE: 'general-settings:search:more-link', EMPTY_TRASH_DELAY: 'trash_delay', @@ -255,6 +257,11 @@ export const SETTINGS: UiSetting[] = [ type: 'boolean', default: true, }, + { + key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS, + type: 'array', + default: [], + }, { key: SETTINGS_KEYS.SEARCH_DB_ONLY, type: 'boolean',