From 36a39ad2c720c930139f07214094496593be78ff Mon Sep 17 00:00:00 2001 From: Mike Risher Date: Tue, 20 Aug 2019 20:29:23 +0000 Subject: [PATCH] LP#1840327: port standing penalty admin interface to Angular Convert standing penalty types admin UI from DOJO to Angular. Name field is read only if the ID is below 100. Doing this involved: - creating a new standing penalty component - using rowFlairCallback functionality in the grid, so that an icon and tooltip is shown for fields where the name cannot be edited - making the ID show up in red text when it is below 100 - adding "readonly Override" functionality to fm-editor, so that some fields of a given type are read only, but others are not Signed-off-by: Mike Risher Signed-off-by: Galen Charlton --- .../share/fm-editor/fm-editor.component.ts | 19 ++- .../local/admin-local-splash.component.html | 2 +- .../staff/admin/local/admin-local.module.ts | 4 +- .../app/staff/admin/local/routing.module.ts | 4 + .../local/standing-penalty.component.html | 28 +++ .../admin/local/standing-penalty.component.ts | 160 ++++++++++++++++++ 6 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/standing-penalty.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/standing-penalty.component.ts diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts index a4fc62ba73..e5ed4006ba 100644 --- a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts @@ -67,6 +67,11 @@ export interface FmFieldOptions { // This only has an affect if the value is true. isReadonly?: boolean; + // If this function is defined, the function will be called + // at render time to see if the field should be marked readonly. + // This supersedes all other isReadonly specifiers. + isReadonlyOverride?: (field: string, record: IdlObject) => boolean; + // Render the field using this custom template instead of chosing // from the default set of form inputs. customTemplate?: CustomFieldTemplate; @@ -455,10 +460,16 @@ export class FmRecordEditorComponent let promise = null; const fieldOptions = this.fieldOptions[field.name] || {}; - - field.readOnly = this.mode === 'view' - || fieldOptions.isReadonly === true - || this.readonlyFieldsList.includes(field.name); + + if (this.mode === 'view') { + field.readOnly = true; + } else if (fieldOptions.isReadonlyOverride) { + field.readOnly = + !fieldOptions.isReadonlyOverride(field.name, this.record); + } else { + field.readOnly = fieldOptions.isReadonly === true + || this.readonlyFieldsList.includes(field.name); + } if (fieldOptions.isRequiredOverride) { field.isRequired = () => { diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html index 870deeb8dc..02565dfafa 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html @@ -55,7 +55,7 @@ + routerLink="/staff/admin/local/config/standing_penalty"> + + + + +
+ + + + + + + + +
+ + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/standing-penalty.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/standing-penalty.component.ts new file mode 100644 index 0000000000..f1c4bd582f --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/standing-penalty.component.ts @@ -0,0 +1,160 @@ +import {Pager} from '@eg/share/util/pager'; +import {Component, OnInit, Input, ViewChild} from '@angular/core'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {GridDataSource, GridColumn, GridRowFlairEntry} from '@eg/share/grid/grid'; +import {IdlObject} from '@eg/core/idl.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; +import {StringComponent} from '@eg/share/string/string.component'; +import {ToastService} from '@eg/share/toast/toast.service'; + +@Component({ + templateUrl: './standing-penalty.component.html' +}) + +export class StandingPenaltyComponent implements OnInit { + recId: number; + gridDataSource: GridDataSource; + initDone = false; + cspSource: GridDataSource = new GridDataSource(); + @ViewChild('partsGrid') partsGrid: GridComponent; + @ViewChild('editDialog') editDialog: FmRecordEditorComponent; + @ViewChild('grid') grid: GridComponent; + @ViewChild('successString') successString: StringComponent; + @ViewChild('createString') createString: StringComponent; + @ViewChild('createErrString') createErrString: StringComponent; + @ViewChild('updateFailedString') updateFailedString: StringComponent; + @ViewChild('cspFlairTooltip') private cspFlairTooltip: StringComponent; + + cspRowFlairCallback: (row: any) => GridRowFlairEntry; + + canCreate: boolean; + canDelete: boolean; + deleteSelected: (rows: IdlObject[]) => void; + + permissions: {[name: string]: boolean}; + + // Default sort field, used when no grid sorting is applied. + @Input() sortField: string; + + @Input() idlClass: string = "csp"; + // Size of create/edito dialog. Uses large by default. + @Input() dialogSize: 'sm' | 'lg' = 'lg'; + // Optional comma-separated list of read-only fields + // @Input() readonlyFields: string; + + @Input() set recordId(id: number) { + this.recId = id; + // Only force new data collection when recordId() + // is invoked after ngInit() has already run. + if (this.initDone) { + this.partsGrid.reload(); + } + } + + constructor( + private pcrud: PcrudService, + private toast: ToastService + ) { + this.gridDataSource = new GridDataSource(); + } + + ngOnInit() { + this.initDone = true; + this.cspSource.getRows = (pager: Pager, sort: any[]) => { + const orderBy: any = {}; + if (sort.length) { + // Sort specified from grid + orderBy[this.idlClass] = sort[0].name + ' ' + sort[0].dir; + } else if (this.sortField) { + // Default sort field + orderBy[this.idlClass] = this.sortField; + } + + const searchOps = { + offset: pager.offset, + limit: pager.limit, + order_by: orderBy + }; + return this.pcrud.retrieveAll('csp', searchOps, {fleshSelectors: true}); + } + + this.cspRowFlairCallback = (row: any): GridRowFlairEntry => { + const flair = {icon: null, title: null}; + if (row.id() < 100) { + flair.icon = 'not_interested'; + flair.title = this.cspFlairTooltip.text; + } + return flair; + } + } + + cspReadonlyOverride = (field: string, copy: IdlObject): boolean => { + if (copy.id() >= 100) { + return true; + } + return false; + } + + cspGridCellClassCallback = (row: any, col: GridColumn): string => { + if (col.name === "id" && row.a[0] < 100) { + return "text-danger"; + } + return ""; + }; + + showEditDialog(standingPenalty: IdlObject): Promise { + this.editDialog.mode = 'update'; + this.editDialog.recId = standingPenalty["id"](); + return new Promise((resolve, reject) => { + this.editDialog.open({size: this.dialogSize}).subscribe( + result => { + this.successString.current() + .then(str => this.toast.success(str)); + this.grid.reload(); + resolve(result); + }, + error => { + this.updateFailedString.current() + .then(str => this.toast.danger(str)); + reject(error); + } + ); + }); + } + + editSelected(standingPenaltyFields: IdlObject[]) { + // Edit each IDL thing one at a time + const editOneThing = (standingPenalty: IdlObject) => { + if (!standingPenalty) { return; } + + this.showEditDialog(standingPenalty).then( + () => editOneThing(standingPenaltyFields.shift())); + }; + + editOneThing(standingPenaltyFields.shift()); + } + + createNew() { + this.editDialog.mode = 'create'; + // We reuse the same editor for all actions. Be sure + // create action does not try to modify an existing record. + this.editDialog.recId = null; + this.editDialog.record = null; + this.editDialog.open({size: this.dialogSize}).subscribe( + ok => { + this.createString.current() + .then(str => this.toast.success(str)); + this.grid.reload(); + }, + rejection => { + if (!rejection.dismissed) { + this.createErrString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } + +} + -- 2.43.2