LPXXX volcopy misc
authorBill Erickson <berickxx@gmail.com>
Wed, 22 Jul 2020 20:35:53 +0000 (16:35 -0400)
committerBill Erickson <berickxx@gmail.com>
Wed, 22 Jul 2020 20:35:53 +0000 (16:35 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html
Open-ILS/src/eg2/src/app/staff/share/holdings/batch-item-attr.component.html
Open-ILS/src/eg2/src/app/staff/share/holdings/batch-item-attr.component.ts

index 5e13cff..af6c51e 100644 (file)
 </ng-template>
 
 <!-- this one is also repeated a lot -->
-<ng-template #batchAttr let-field="field" 
+<ng-template #batchAttr let-field="field" let-required="required"
   let-label="label" let-template="template" let-displayAs="displayAs">
   <eg-batch-item-attr 
     [name]="field" 
     [label]="label || copyFieldLabel(field)"
+    [valueRequired]="required"
     [displayAs]="displayAs"
     [editInputDomId]="field + '-input'"
-    [emptyIsUnset]="true"
     [editTemplate]="template"
     [labelCounts]="itemAttrCounts(field)"
     (valueCleared)="applyCopyValue(field, null)"
   <div class="col-lg-7 d-flex">
     <button class="btn btn-outline-dark mr-2" (click)="applyTemplate()" i18n>Apply</button>
     <button class="btn btn-outline-dark mr-2" (click)="saveTemplate()" i18n>Save</button>
-    <!--
-    <button class="btn btn-outline-dark mr-2" (click)="importTemplate()" i18n>Import</button>
-    -->
 
     <!-- 
-      The type typical approach of wrapping a file input in a <label>
-      results in button-ish things that have slightly different dimensions
+      The typical approach of wrapping a file input in a <label> results
+      in button-ish things that have slightly different dimensions.
+      Instead have a button activate a hidden file input.
     -->
     <button class="btn btn-outline-dark mr-2" (click)="templateFile.click()">
       <input type="file" class="d-none" #templateFile
@@ -99,8 +97,7 @@
 
     <div class="mb-1" *ngIf="displayAttr('barcode')">
       <eg-batch-item-attr label="Barcode" i18n-label
-        [readOnly]="true" [emptyIsUnset]="true"
-        [labelCounts]="itemAttrCounts('barcode')">
+        [readOnly]="true" [labelCounts]="itemAttrCounts('barcode')">
       </eg-batch-item-attr>
     </div>
 
         </eg-item-location-select>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'location',template:locationTemplate}">
+        context:{field:'location',required:true,template:locationTemplate}">
       </ng-container>
     </div>
 
         </eg-org-select>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'circ_lib',template:circLibTemplate}">
+        context:{field:'circ_lib',required:true,template:circLibTemplate}">
       </ng-container>
     </div>
 
         </eg-org-select>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'owning_lib',template:owningLibTemplate,label:olLabel.text}">
+        context:{field:'owning_lib',required:true,template:owningLibTemplate,label:olLabel.text}">
       </ng-container>
     </div>
 
         </ng-container>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'circulate',template:circulateTemplate,displayAs:'bool'}">
+        context:{field:'circulate',required:true,template:circulateTemplate,displayAs:'bool'}">
       </ng-container>
     </div>
 
         </ng-container>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'holdable',template:holdableTemplate,displayAs:'bool'}">
+        context:{field:'holdable',required:true,template:holdableTemplate,displayAs:'bool'}">
       </ng-container>
     </div>
 
 
       <ng-template #loanDurationTemplate>
         <select class="form-control" 
-          id="loan-duration-input" [(ngModel)]="values['loan_duration']">
+          id="loan_duration-input" [(ngModel)]="values['loan_duration']">
           <option value="1" i18n>{{loanDurationShort.text}}</option>
           <option value="2" i18n>{{loanDurationNormal.text}}</option>
           <option value="3" i18n>{{loanDurationLong.text}}</option>
         </select>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'loan_duration',template:loanDurationTemplate}">
+        context:{field:'loan_duration',required:true,template:loanDurationTemplate}">
       </ng-container>
     </div>
 
 
       <ng-template #fineLevelTemplate>
         <select class="form-control" 
-          id="fine-level-input" [(ngModel)]="values['fine_level']">
+          id="fine_level-input" [(ngModel)]="values['fine_level']">
           <option value="1" i18n>{{fineLevelLow.text}}</option>
           <option value="2" i18n>{{fineLevelNormal.text}}</option>
           <option value="3" i18n>{{fineLevelHigh.text}}</option>
         </select>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'fine_level',template:fineLevelTemplate}">
+        context:{field:'fine_level',required:true,template:fineLevelTemplate}">
       </ng-container>
     </div>
 
     <div *ngIf="displayAttr('circ_as_type')">
       <ng-template #circAsTypeTemplate>
-        <eg-combobox domId="circ-as-type-input"
+        <eg-combobox domId="circ_as_type-input"
           (ngModelChange)="values['circ_as_type'] = $event ? $event.id : null"
           [ngModel]="values['circ_as_type']">
           <eg-combobox-entry *ngFor="let map of volcopy.commonData.acp_item_type_map"
 
     <div *ngIf="displayAttr('circ_modifier')">
       <ng-template #circModifierTemplate>
-        <select class="form-control" [(ngModel)]="values['circ_modifier']">
+        <select class="form-control" id='circ_modifier-input' 
+          [(ngModel)]="values['circ_modifier']">
           <option [value]="null" i18n>&lt;Unset&gt;</option>
           <option *ngFor="let mod of volcopy.commonData.acp_circ_modifier"
             value="{{mod.code()}}">{{mod.name()}}</option>
       </ng-template>
       <eg-batch-item-attr label="Alert Message" i18n-label
         editInputDomId="alert-message-input"
-        [emptyIsUnset]="true"
         [editTemplate]="alertMessageTemplate"
         [labelCounts]="itemAttrCounts('alert_message')"
         (changesSaved)="applyCopyValue('alert_message')">
         </ng-container>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'deposit',template:depositTemplate,displayAs:'bool'}">
+        context:{field:'deposit',required:true,template:depositTemplate,displayAs:'bool'}">
       </ng-container>
     </div>
 
     <div *ngIf="displayAttr('deposit_amount')">
       <ng-template #depositAmountTemplate>
         <input type="number" class="form-control" 
-          id="deposit-amount-input" [(ngModel)]="values['deposit_amount']"/>
+          id="deposit_amount-input" [(ngModel)]="values['deposit_amount']"/>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'deposit_amount',template:depositAmountTemplate,displayAs:'currency'}">
+        context:{field:'deposit_amount',required:true,template:depositAmountTemplate,displayAs:'currency'}">
       </ng-container>
     </div>
 
         </ng-container>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'opac_visible',template:opacVisibleTemplate,displayAs:'bool'}">
+        context:{field:'opac_visible',required:true,template:opacVisibleTemplate,displayAs:'bool'}">
       </ng-container>
     </div>
 
         </ng-container>
       </ng-template>
       <ng-container *ngTemplateOutlet="batchAttr;
-        context:{field:'ref',template:refTemplate,displayAs:'bool'}">
+        context:{field:'ref',required:true,template:refTemplate,displayAs:'bool'}">
       </ng-container>
     </div>
 
 
       <ng-template #mintConditionTemplate>
         <select class="form-control" 
-          id="mint-condition-input" [(ngModel)]="values['mint_condition']">
+          id="mint_condition-input" [(ngModel)]="values['mint_condition']">
           <option value="t" i18n>{{mintConditionYes.text}}</option>
           <option value="f" i18n>{{mintConditionNo.text}}</option>
         </select>
         </ng-template>
         <eg-batch-item-attr label="{{cat.name()}} ({{orgSn(cat.owner())}})" i18n-label
           name="stat_cat_{{cat.id()}}" editInputDomId="stat-cat-input-{{cat.id()}}"
-          [emptyIsUnset]="true"
           [editTemplate]="statCatTemplate"
           [labelCounts]="statCatCounts(cat.id())"
           (valueCleared)="statCatChanged(cat.id(), true)"
index cc660d6..ac2426a 100644 (file)
@@ -1,5 +1,5 @@
 
-<div class="border rounded m-1">
+<div class="border rounded m-1" [ngClass]="{'border-danger': warnOnRequired()}">
   <div class="font-weight-bold header p-2 d-flex" i18n>
     {{label}} <span *ngIf="hasChanged" class="text-danger">*</span>
     <ng-container *ngIf="bulky()">
@@ -13,6 +13,7 @@
     </ng-container>
   </div>
   <div tabindex="0" class="p-2" *ngIf="!editing || multiValue()"
+    (click)="enterEditMode()" (keyup.enter)="enterEditMode()"
     [ngClass]="{'has-changes': hasChanged}">
     <div class="d-flex" 
       *ngFor="let count of labelCounts | keyvalue; let idx = index">
               [(ngModel)]="editValues[count.key]"/>
           </div>
         </ng-container>
-        <div class="flex-1"
-          (click)="toggleEditMode()" (keyup.enter)="toggleEditMode()">
+        <div class="flex-1">
           <ng-container *ngIf="displayAs == 'bool'">
             <span *ngIf="count.key == 't'" i18n>Yes</span>
             <span *ngIf="count.key == 'f'" i18n>No</span>
           </ng-container>
           <ng-container *ngIf="displayAs == 'currency'">
             <ng-container 
-              *ngIf="emptyIsUnset && !count.key && count.key !== 0; else defaultCurrency">
+              *ngIf="valueIsUnset(count.key); else defaultCurrency">
               <span i18n>&lt;Unset&gt;</span>
             </ng-container>
             <ng-template #defaultCurrency>{{count.key | currency}}</ng-template>
           </ng-container>
           <ng-container *ngIf="displayAs == null">
             <ng-container 
-              *ngIf="emptyIsUnset && !count.key && count.key !== 0; else default">
+              *ngIf="valueIsUnset(count.key); else default">
               <span i18n>&lt;Unset&gt;</span>
             </ng-container>
             <ng-template #default>{{count.key}}</ng-template>
           </ng-container>
         </div>
-        <div (click)="toggleEditMode()" (keyup.enter)="toggleEditMode()" i18n>
-          {{count.value}} copies
-        </div>
+        <div i18n>{{count.value}} copies</div>
       </ng-container>
     </div>
   </div>
index e970e2d..7a33d0d 100644 (file)
@@ -50,8 +50,12 @@ export class BatchItemAttrComponent {
     // Display only
     @Input() readOnly = false;
 
-    // If true, null/undefined/empty-string display as <Unset>
-    @Input() emptyIsUnset: boolean;
+    // Warn the user when a required field has an empty value
+    @Input() valueRequired = false;
+
+    // If true, a value of '' is considered unset for display and
+    // valueRequired purposes.
+    @Input() emptyStringIsUnset = true;
 
     // Lists larger than this will be partially hidden behind
     // and expandy.
@@ -101,24 +105,37 @@ export class BatchItemAttrComponent {
         return Object.keys(this.labelCounts).length > 1;
     }
 
-    toggleEditMode() {
-        if (this.readOnly) { return; }
-
-        this.editing = !this.editing;
+    // True if a value is required and any value exists that's unset.
+    warnOnRequired(): boolean {
+        if (!this.valueRequired) { return false; }
 
-        // Avoid using selectRootElement to focus.
-        // https://stackoverflow.com/a/36059595
-        if (this.editing) {
+        return Object.keys(this.labelCounts)
+            .filter(key => this.valueIsUnset(key)).length > 0;
+    }
 
-            Object.keys(this.labelCounts).forEach(
-                key => this.editValues[key] = true);
+    valueIsUnset(value: any): boolean {
+        return (
+            value === null ||
+            value === undefined ||
+            (this.emptyStringIsUnset && value === '')
+        );
+    }
 
-            if (this.editInputDomId) {
-                setTimeout(() => {
-                    const node = document.getElementById(this.editInputDomId);
-                    if (node) { node.focus(); }
-                });
-            }
+    enterEditMode() {
+        if (this.readOnly || this.editing) { return; }
+        this.editing = true;
+
+        // Assume all values should be edited by default
+        Object.keys(this.labelCounts).forEach(
+            key => this.editValues[key] = true);
+
+        if (this.editInputDomId) {
+            setTimeout(() => {
+                // Avoid using selectRootElement to focus.
+                // https://stackoverflow.com/a/36059595
+                const node = document.getElementById(this.editInputDomId);
+                if (node) { node.focus(); }
+            });
         }
     }
 }