From 9b075fb86b129d7bf70d854d07da1e8f6329cc45 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 17 Apr 2024 20:24:31 -0700 Subject: [PATCH] Updated UI for editing saved views with custom fields --- .../admin/settings/settings.component.html | 77 ++++++++++--------- .../admin/settings/settings.component.spec.ts | 46 ++++++++++- .../admin/settings/settings.component.ts | 28 ++++++- .../drag-drop-select.component.html | 3 +- .../drag-drop-select.component.scss | 4 + .../drag-drop-select.component.ts | 7 +- .../saved-view-widget.component.spec.ts | 68 ++++++++-------- .../saved-view-widget.component.ts | 26 +++---- src-ui/src/app/data/saved-view.ts | 20 ++--- 9 files changed, 174 insertions(+), 105 deletions(-) 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 6f3ccec54..6f4b90d59 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -325,48 +325,49 @@ @for (view of savedViews; track view) {
  • -
    - -
    - - +
    +
    +
    -
    - - -
    -
    -
    - @if (savedViewGroup.get(view.id.toString()).get('show_on_dashboard').value) { -
    -
    - -
    -
    - - -
    +
    +
    + +
    - @if (savedViewGroup.get(view.id.toString()).get('dashboard_view_mode').value === DashboardViewMode.TABLE) { - +
    + + +
    +
    +
    + + + +
    +
    + @if (savedViewGroup.get(view.id.toString()).get('show_on_dashboard').value) { +
    +
    + +
    +
    + + +
    + @if (savedViewGroup.get(view.id.toString()).get('dashboard_view_mode').value === DashboardViewMode.TABLE && documentDisplayFields) { + } +
    } -
    -
    - - - - -
  • } 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 2f76ec361..ca4d336bc 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 @@ -15,7 +15,11 @@ import { import { NgSelectModule } from '@ng-select/ng-select' import { of, throwError } from 'rxjs' import { routes } from 'src/app/app-routing.module' -import { SavedView } from 'src/app/data/saved-view' +import { + DOCUMENT_DISPLAY_FIELDS, + DocumentDisplayField, + SavedView, +} from 'src/app/data/saved-view' import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' @@ -48,6 +52,8 @@ import { InstallType, SystemStatusItemStatus, } from 'src/app/data/system-status' +import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' +import { CustomFieldDataType } from 'src/app/data/custom-field' const savedViews = [ { id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true }, @@ -61,6 +67,20 @@ const groups = [ { id: 1, name: 'group1' }, { id: 2, name: 'group2' }, ] +const customFields = [ + { + id: 1, + name: 'Field 1', + created: new Date(), + data_type: CustomFieldDataType.Monetary, + }, + { + id: 2, + name: 'Field 2', + created: new Date(), + data_type: CustomFieldDataType.String, + }, +] describe('SettingsComponent', () => { let component: SettingsComponent @@ -76,6 +96,7 @@ describe('SettingsComponent', () => { let groupService: GroupService let modalService: NgbModal let systemStatusService: SystemStatusService + let customFieldsService: CustomFieldsService beforeEach(async () => { TestBed.configureTestingModule({ @@ -121,6 +142,7 @@ describe('SettingsComponent', () => { permissionsService = TestBed.inject(PermissionsService) modalService = TestBed.inject(NgbModal) systemStatusService = TestBed.inject(SystemStatusService) + customFieldsService = TestBed.inject(CustomFieldsService) jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) jest .spyOn(permissionsService, 'currentUserHasObjectPermissions') @@ -160,6 +182,15 @@ describe('SettingsComponent', () => { }) ) } + if (excludeService !== customFieldsService) { + jest.spyOn(customFieldsService, 'listAll').mockReturnValue( + of({ + all: customFields.map((f) => f.id), + count: customFields.length, + results: customFields.concat([]), + }) + ) + } fixture = TestBed.createComponent(SettingsComponent) component = fixture.componentInstance @@ -444,4 +475,17 @@ describe('SettingsComponent', () => { component.reset() expect(component.settingsForm.get('themeColor').value).toEqual('') }) + + it('should dynamically create display fields options including custom fields', () => { + completeSetup() + expect( + component.documentDisplayFields.includes(DOCUMENT_DISPLAY_FIELDS[0]) + ).toBeTruthy() + expect( + component.documentDisplayFields.find( + (f) => + f.id === `${DocumentDisplayField.CUSTOM_FIELD}${customFields[0].id}` + ).name + ).toEqual(customFields[0].name) + }) }) 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 c896dd5ac..be7629088 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -27,9 +27,9 @@ import { } from 'rxjs' import { Group } from 'src/app/data/group' import { - document_display_fields, + DOCUMENT_DISPLAY_FIELDS, DashboardViewMode, - DashboardViewTableColumn, + DocumentDisplayField, SavedView, } from 'src/app/data/saved-view' import { SETTINGS_KEYS } from 'src/app/data/ui-settings' @@ -55,6 +55,7 @@ import { SystemStatusItemStatus, SystemStatus, } from 'src/app/data/system-status' +import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' enum SettingsNavIDs { General = 1, @@ -116,7 +117,7 @@ export class SettingsComponent savedViews: SavedView[] SettingsNavIDs = SettingsNavIDs - document_display_fields = document_display_fields + documentDisplayFields: any[] store: BehaviorSubject storeSub: Subscription @@ -162,7 +163,8 @@ export class SettingsComponent private router: Router, public permissionsService: PermissionsService, private modalService: NgbModal, - private systemStatusService: SystemStatusService + private systemStatusService: SystemStatusService, + private customFieldsService: CustomFieldsService ) { super() this.settings.settingsSaved.subscribe(() => { @@ -223,6 +225,24 @@ export class SettingsComponent }) } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.CustomField + ) + ) { + this.customFieldsService.listAll().subscribe((r) => { + this.documentDisplayFields = DOCUMENT_DISPLAY_FIELDS.concat( + r.results.map((field) => { + return { + id: `${DocumentDisplayField.CUSTOM_FIELD}${field.id}` as any, + name: field.name, + } + }) + ) + }) + } + this.activatedRoute.paramMap.subscribe((paramMap) => { const section = paramMap.get('section') if (section) { diff --git a/src-ui/src/app/components/common/input/drag-drop-select/drag-drop-select.component.html b/src-ui/src/app/components/common/input/drag-drop-select/drag-drop-select.component.html index e0ef64e71..e8da3559c 100644 --- a/src-ui/src/app/components/common/input/drag-drop-select/drag-drop-select.component.html +++ b/src-ui/src/app/components/common/input/drag-drop-select/drag-drop-select.component.html @@ -1,5 +1,5 @@
    - Selected: + {{title}}:
    -
    { + @Input() title: string = $localize`Selected items` + @Input() items: { id: string; name: string }[] = [] public selectedItems: { id: string; name: string }[] = [] @@ -53,7 +55,10 @@ export class DragDropSelectComponent extends AbstractInputComponent { 0, this.unselectedItems[event.previousIndex] ) - } else { + } else if ( + event.container === this.unselectedList && + event.previousContainer === this.selectedList + ) { this.selectedItems.splice(event.previousIndex, 1) } this.onChange(this.selectedItems.map((i) => i.id)) diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts index 754a90f69..63f90ccf9 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts @@ -19,7 +19,7 @@ import { } from 'src/app/data/filter-rule-type' import { DashboardViewMode, - DashboardViewTableColumn, + DocumentDisplayField, SavedView, } from 'src/app/data/saved-view' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' @@ -59,14 +59,14 @@ const savedView: SavedView = { dashboard_view_limit: 20, dashboard_view_mode: DashboardViewMode.TABLE, document_display_fields: [ - DashboardViewTableColumn.CREATED, - DashboardViewTableColumn.TITLE, - DashboardViewTableColumn.TAGS, - DashboardViewTableColumn.CORRESPONDENT, - DashboardViewTableColumn.DOCUMENT_TYPE, - DashboardViewTableColumn.STORAGE_PATH, - `${DashboardViewTableColumn.CUSTOM_FIELD}11` as any, - `${DashboardViewTableColumn.CUSTOM_FIELD}15` as any, + DocumentDisplayField.CREATED, + DocumentDisplayField.TITLE, + DocumentDisplayField.TAGS, + DocumentDisplayField.CORRESPONDENT, + DocumentDisplayField.DOCUMENT_TYPE, + DocumentDisplayField.STORAGE_PATH, + `${DocumentDisplayField.CUSTOM_FIELD}11` as any, + `${DocumentDisplayField.CUSTOM_FIELD}15` as any, ], } @@ -321,54 +321,52 @@ describe('SavedViewWidgetComponent', () => { }) it('should get correct column title', () => { - expect(component.getColumnTitle(DashboardViewTableColumn.TITLE)).toEqual( + expect(component.getColumnTitle(DocumentDisplayField.TITLE)).toEqual( 'Title' ) - expect(component.getColumnTitle(DashboardViewTableColumn.CREATED)).toEqual( + expect(component.getColumnTitle(DocumentDisplayField.CREATED)).toEqual( 'Created' ) - expect(component.getColumnTitle(DashboardViewTableColumn.ADDED)).toEqual( + expect(component.getColumnTitle(DocumentDisplayField.ADDED)).toEqual( 'Added' ) - expect(component.getColumnTitle(DashboardViewTableColumn.TAGS)).toEqual( - 'Tags' - ) + expect(component.getColumnTitle(DocumentDisplayField.TAGS)).toEqual('Tags') expect( - component.getColumnTitle(DashboardViewTableColumn.CORRESPONDENT) + component.getColumnTitle(DocumentDisplayField.CORRESPONDENT) ).toEqual('Correspondent') expect( - component.getColumnTitle(DashboardViewTableColumn.DOCUMENT_TYPE) + component.getColumnTitle(DocumentDisplayField.DOCUMENT_TYPE) ).toEqual('Document type') - expect( - component.getColumnTitle(DashboardViewTableColumn.STORAGE_PATH) - ).toEqual('Storage path') + expect(component.getColumnTitle(DocumentDisplayField.STORAGE_PATH)).toEqual( + 'Storage path' + ) }) it('should check if column is visible including permissions', () => { expect( - component.visibleColumns.includes(DashboardViewTableColumn.TITLE) + component.visibleColumns.includes(DocumentDisplayField.TITLE) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.CREATED) + component.visibleColumns.includes(DocumentDisplayField.CREATED) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.ADDED) + component.visibleColumns.includes(DocumentDisplayField.ADDED) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.TAGS) + component.visibleColumns.includes(DocumentDisplayField.TAGS) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.CORRESPONDENT) + component.visibleColumns.includes(DocumentDisplayField.CORRESPONDENT) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.DOCUMENT_TYPE) + component.visibleColumns.includes(DocumentDisplayField.DOCUMENT_TYPE) ).toBeTruthy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.STORAGE_PATH) + component.visibleColumns.includes(DocumentDisplayField.STORAGE_PATH) ).toBeTruthy() expect( component.visibleColumns.includes( - `${DashboardViewTableColumn.CUSTOM_FIELD}11` as any + `${DocumentDisplayField.CUSTOM_FIELD}11` as any ) ).toBeTruthy() @@ -378,20 +376,20 @@ describe('SavedViewWidgetComponent', () => { .mockReturnValue(false) component.ngOnInit() expect( - component.visibleColumns.includes(DashboardViewTableColumn.TAGS) + component.visibleColumns.includes(DocumentDisplayField.TAGS) ).toBeFalsy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.CORRESPONDENT) + component.visibleColumns.includes(DocumentDisplayField.CORRESPONDENT) ).toBeFalsy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.DOCUMENT_TYPE) + component.visibleColumns.includes(DocumentDisplayField.DOCUMENT_TYPE) ).toBeFalsy() expect( - component.visibleColumns.includes(DashboardViewTableColumn.STORAGE_PATH) + component.visibleColumns.includes(DocumentDisplayField.STORAGE_PATH) ).toBeFalsy() expect( component.visibleColumns.includes( - `${DashboardViewTableColumn.CUSTOM_FIELD}11` as any + `${DocumentDisplayField.CUSTOM_FIELD}11` as any ) ).toBeFalsy() }) @@ -400,13 +398,13 @@ describe('SavedViewWidgetComponent', () => { expect( component.getMonetaryCustomFieldValue( documentResults[2], - `${DashboardViewTableColumn.CUSTOM_FIELD}3` + `${DocumentDisplayField.CUSTOM_FIELD}3` ) ).toEqual([123, 'EUR']) expect( component.getMonetaryCustomFieldValue( documentResults[0], - `${DashboardViewTableColumn.CUSTOM_FIELD}999` + `${DocumentDisplayField.CUSTOM_FIELD}999` ) ).toEqual([null, null]) }) diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index f771136c2..b2792f49d 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -10,10 +10,10 @@ import { Router } from '@angular/router' import { Subject, takeUntil } from 'rxjs' import { Document } from 'src/app/data/document' import { - DashboardViewTableColumn, + DocumentDisplayField, DashboardViewMode, SavedView, - document_display_fields, + DOCUMENT_DISPLAY_FIELDS, } from 'src/app/data/saved-view' import { ConsumerStatusService } from 'src/app/services/consumer-status.service' import { DocumentService } from 'src/app/services/rest/document.service' @@ -46,7 +46,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy { public DashboardViewMode = DashboardViewMode - public DashboardViewTableColumn = DashboardViewTableColumn + public DashboardViewTableColumn = DocumentDisplayField public CustomFieldDataType = CustomFieldDataType loading: boolean = true @@ -79,10 +79,10 @@ export class SavedViewWidgetComponent mouseOnPreview = false popoverHidden = true - visibleColumns: DashboardViewTableColumn[] = [ - DashboardViewTableColumn.TITLE, - DashboardViewTableColumn.CREATED, - DashboardViewTableColumn.ADDED, + visibleColumns: DocumentDisplayField[] = [ + DocumentDisplayField.TITLE, + DocumentDisplayField.CREATED, + DocumentDisplayField.ADDED, ] docLinkDocuments: Document[] = [] @@ -115,7 +115,7 @@ export class SavedViewWidgetComponent let type: PermissionType = Object.values(PermissionType).find((t) => t.includes(column) ) - if (column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)) { + if (column.startsWith(DocumentDisplayField.CUSTOM_FIELD)) { type = PermissionType.CustomField } if ( @@ -249,12 +249,12 @@ export class SavedViewWidgetComponent }, 300) } - public getColumnTitle(column: DashboardViewTableColumn): string { - if (column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)) { + public getColumnTitle(column: DocumentDisplayField): string { + if (column.startsWith(DocumentDisplayField.CUSTOM_FIELD)) { const id = column.split('_')[2] return this.customFields.find((c) => c.id === parseInt(id))?.name } - return document_display_fields.find((c) => c.id === column)?.name + return DOCUMENT_DISPLAY_FIELDS.find((c) => c.id === column)?.name } public getCustomFieldDataType(column_id: string): string { @@ -285,9 +285,7 @@ export class SavedViewWidgetComponent let docIds = [] let docLinkColumns = [] this.savedView.document_display_fields - ?.filter((column) => - column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD) - ) + ?.filter((column) => column.startsWith(DocumentDisplayField.CUSTOM_FIELD)) .forEach((column) => { if ( this.getCustomFieldDataType(column) === diff --git a/src-ui/src/app/data/saved-view.ts b/src-ui/src/app/data/saved-view.ts index f0564ca59..bfd8b42f6 100644 --- a/src-ui/src/app/data/saved-view.ts +++ b/src-ui/src/app/data/saved-view.ts @@ -6,7 +6,7 @@ export enum DashboardViewMode { SMALL_CARDS = 'small_cards', } -export enum DashboardViewTableColumn { +export enum DocumentDisplayField { TITLE = 'title', CREATED = 'created', ADDED = 'added', @@ -17,33 +17,33 @@ export enum DashboardViewTableColumn { CUSTOM_FIELD = 'custom_field_', } -export const document_display_fields = [ +export const DOCUMENT_DISPLAY_FIELDS = [ { - id: DashboardViewTableColumn.TITLE, + id: DocumentDisplayField.TITLE, name: $localize`Title`, }, { - id: DashboardViewTableColumn.CREATED, + id: DocumentDisplayField.CREATED, name: $localize`Created`, }, { - id: DashboardViewTableColumn.ADDED, + id: DocumentDisplayField.ADDED, name: $localize`Added`, }, { - id: DashboardViewTableColumn.TAGS, + id: DocumentDisplayField.TAGS, name: $localize`Tags`, }, { - id: DashboardViewTableColumn.CORRESPONDENT, + id: DocumentDisplayField.CORRESPONDENT, name: $localize`Correspondent`, }, { - id: DashboardViewTableColumn.DOCUMENT_TYPE, + id: DocumentDisplayField.DOCUMENT_TYPE, name: $localize`Document type`, }, { - id: DashboardViewTableColumn.STORAGE_PATH, + id: DocumentDisplayField.STORAGE_PATH, name: $localize`Storage path`, }, ] @@ -65,5 +65,5 @@ export interface SavedView extends ObjectWithPermissions { dashboard_view_mode?: DashboardViewMode - document_display_fields?: DashboardViewTableColumn[] + document_display_fields?: DocumentDisplayField[] }