LP#1626157 op-change, toast; strings cont.
authorBill Erickson <berickxx@gmail.com>
Wed, 25 Apr 2018 16:57:11 +0000 (12:57 -0400)
committerBill Erickson <berickxx@gmail.com>
Wed, 25 Apr 2018 16:57:11 +0000 (12:57 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
12 files changed:
Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts
Open-ILS/src/eg2/src/app/share/string/string.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/string/string.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/toast/toast.component.css [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/toast/toast.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/toast/toast.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/toast/toast.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/common.module.ts
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.ts [new file with mode: 0644]

index 5e17f6f..82f39be 100644 (file)
@@ -194,15 +194,6 @@ export class FmRecordEditorComponent
                     rec[field.name](org.id());
                 }
             }
-
-            /* TODO
-            // retrieve values from any fields controlled
-            // by custom templates, which for the moment all
-            // expect to be passed an ordinary flat value
-            if (field.name in $scope.rec_flat) {
-                rec[field.name]($scope.rec_flat[field.name]);
-            }
-            */
         });
     }
 
diff --git a/Open-ILS/src/eg2/src/app/share/string/string.component.ts b/Open-ILS/src/eg2/src/app/share/string/string.component.ts
new file mode 100644 (file)
index 0000000..13e442c
--- /dev/null
@@ -0,0 +1,50 @@
+/*j
+ * <eg-string #helloStr text="Hello, {{name}}" i18n-text></eg-string>
+ *
+ * import {EgStringComponent} from '@eg/share/string.component';
+ * @ViewChild('helloStr') private helloStr: EgStringComponent;
+ * ...
+ * this.helloStr.currrent().then(s => console.log(s));
+ *
+ */
+import {Component, Input, OnInit, ElementRef, TemplateRef} from '@angular/core';
+import {EgStringService} from '@eg/share/string/string.service';
+
+@Component({
+  selector: 'eg-string',
+  template: `
+    <span style='display:none'>
+    <ng-container *ngTemplateOutlet="template; context:ctx"></ng-container>
+    </span>
+  `
+})
+
+export class EgStringComponent implements OnInit {
+
+    @Input() key: string;
+    @Input() ctx: any;
+    @Input() template: TemplateRef<any>;
+
+    constructor(private elm: ElementRef, private strings: EgStringService) {
+        this.elm = elm;
+        this.strings = strings;
+    }
+
+    ngOnInit() {
+        this.strings.register({
+            key: this.key, 
+            resolver: (ctx:any) => this.current(ctx)
+        });
+    }
+
+
+    // Apply the new context if provided, give our container a 
+    // chance to update, then resolve with the current string.
+    current(ctx?: any): Promise<string> {
+        if (ctx) this.ctx = ctx;
+        return new Promise(resolve => {
+            setTimeout(() => resolve(this.elm.nativeElement.textContent));
+        });
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/share/string/string.service.ts b/Open-ILS/src/eg2/src/app/share/string/string.service.ts
new file mode 100644 (file)
index 0000000..0e3e706
--- /dev/null
@@ -0,0 +1,27 @@
+import {Injectable} from '@angular/core';
+
+interface EgStringAssignment {
+    key: string,     // keyboard command
+    resolver: (ctx:any) => Promise<string>
+};
+
+@Injectable()
+export class EgStringService {
+
+    strings: {[key:string] : EgStringAssignment} = {};
+
+    constructor() {}
+
+    register(assn: EgStringAssignment) {
+        this.strings[assn.key] = assn;
+    }
+
+    interpolate(key: string, ctx: any): Promise<string> {
+        if (!this.strings[key]) 
+            return Promise.reject('No Such String');
+        return this.strings[key].resolver(ctx);
+    }
+
+}
+
diff --git a/Open-ILS/src/eg2/src/app/share/toast/toast.component.css b/Open-ILS/src/eg2/src/app/share/toast/toast.component.css
new file mode 100644 (file)
index 0000000..1f70349
--- /dev/null
@@ -0,0 +1,11 @@
+#eg-toast-container {
+    min-width: 250px;
+    text-align: center;
+    border-radius: 2px;
+    padding: 10px;
+    position: fixed;
+    z-index: 1;
+    right: 15px;
+    bottom: 5px;
+}
+
diff --git a/Open-ILS/src/eg2/src/app/share/toast/toast.component.html b/Open-ILS/src/eg2/src/app/share/toast/toast.component.html
new file mode 100644 (file)
index 0000000..6aa1545
--- /dev/null
@@ -0,0 +1,3 @@
+<div id="eg-toast-container" *ngIf="message">
+  <ngb-alert [type]="message.style" (close)="dismiss(message)">{{message.text}}</ngb-alert>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/share/toast/toast.component.ts b/Open-ILS/src/eg2/src/app/share/toast/toast.component.ts
new file mode 100644 (file)
index 0000000..eebe625
--- /dev/null
@@ -0,0 +1,43 @@
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {EgToastService, EgToastMessage} from '@eg/share/toast/toast.service';
+
+const EG_TOAST_TIMEOUT = 3000;
+
+@Component({
+  selector: 'eg-toast',
+  templateUrl: './toast.component.html',
+  styleUrls: ['./toast.component.css']
+})
+export class EgToastComponent implements OnInit {
+
+    message: EgToastMessage;
+
+    // track the most recent timeout event
+    timeout: any;
+
+       constructor(private toast: EgToastService) {
+    }
+
+    ngOnInit() {
+        this.toast.messages$.subscribe(msg => this.show(msg));
+    }
+
+    show(msg: EgToastMessage) {
+        this.dismiss(this.message);
+        this.message = msg;
+        this.timeout = setTimeout(
+            () => this.dismiss(this.message), 
+            EG_TOAST_TIMEOUT
+        );
+    }
+
+    dismiss(msg: EgToastMessage) {
+        this.message = null;
+        if (this.timeout) {
+            clearTimeout(this.timeout);
+            this.timeout = null;
+        }
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/share/toast/toast.service.ts b/Open-ILS/src/eg2/src/app/share/toast/toast.service.ts
new file mode 100644 (file)
index 0000000..9692c13
--- /dev/null
@@ -0,0 +1,39 @@
+import {Injectable, EventEmitter} from '@angular/core';
+
+export interface EgToastMessage {
+    text: string,
+    style: string 
+};
+
+@Injectable()
+export class EgToastService {
+
+    messages$: EventEmitter<EgToastMessage>;
+
+    constructor() {
+        this.messages$ = new EventEmitter<EgToastMessage>();
+    }
+
+    sendMessage(msg: EgToastMessage) {
+        this.messages$.emit(msg);
+    }
+
+    success(text: string) {
+        this.sendMessage({text: text, style: 'success'});
+    }
+
+    info(text: string) {
+        this.sendMessage({text: text, style: 'info'});
+    }
+
+    warning(text: string) {
+        this.sendMessage({text: text, style: 'warning'});
+    }
+
+    danger(text: string) {
+        this.sendMessage({text: text, style: 'danger'});
+    }
+
+    // Others?
+}
+
index 5f47962..61094e4 100644 (file)
@@ -12,6 +12,8 @@ import {EgAccessKeyInfoComponent} from '@eg/share/accesskey/accesskey-info.compo
 import {EgOpChangeComponent} from '@eg/staff/share/op-change/op-change.component';
 import {EgToastService} from '@eg/share/toast/toast.service';
 import {EgToastComponent} from '@eg/share/toast/toast.component';
+import {EgStringComponent} from '@eg/share/string/string.component';
+import {EgStringService} from '@eg/share/string/string.service';
 
 /**
  * Imports the EG common modules and adds modules common to all staff UI's.
@@ -28,6 +30,7 @@ import {EgToastComponent} from '@eg/share/toast/toast.component';
     EgAccessKeyDirective,
     EgAccessKeyInfoComponent,
     EgToastComponent,
+    EgStringComponent,
     EgOpChangeComponent
   ],
   imports: [
@@ -44,6 +47,7 @@ import {EgToastComponent} from '@eg/share/toast/toast.component';
     EgAccessKeyDirective,
     EgAccessKeyInfoComponent,
     EgToastComponent,
+    EgStringComponent,
     EgOpChangeComponent
   ]
 })
@@ -54,6 +58,7 @@ export class EgStaffCommonModule {
             ngModule: EgStaffCommonModule,
             providers: [ // Export staff-wide services
                 EgAccessKeyService,
+                EgStringService,
                 EgToastService
             ]
         };
index 1b55f39..e8f2626 100644 (file)
@@ -28,7 +28,6 @@
 </div>
 <!-- / FM Editor Experiments ----------------------------- -->
 
-
 <!-- Progress Dialog Experiments ----------------------------- -->
 <div class="row mb-3">
   <eg-progress-dialog #progressDialog>
 <!-- /Progress Dialog Experiments ----------------------------- -->
 
 <!-- eg toast -->
-<button class="btn btn-info" (click)="testToast()">Test Toast Message</button>
+<div class="row mb-3">
+   <button class="btn btn-info" (click)="testToast()">Test Toast Message</button>
+</div>
+
+<!-- eg strings -->
+<!--
+<div class="row mb-3">
+    <eg-string #helloString text="Hello, {{name}}" i18n-text></eg-string>
+    <button class="btn btn-success" (click)="testStrings()">Test Strings</button>
+</div>
+-->
+
+<div class="row mb-3">
+    <ng-template #helloStrTmpl let-name="name" i18n>Hello, {{name}}</ng-template>
+    <!--
+    <eg-string #helloStr key="helloKey" [template]="helloStrTmpl"></eg-string>
+    -->
+    <eg-string key="staff.sandbox.test" [template]="helloStrTmpl"></eg-string>
+    <button class="btn btn-success" (click)="testStrings()">Test Strings</button>
+</div>
+
 
index dae9e12..90ebe15 100644 (file)
@@ -1,6 +1,8 @@
-import {Component, OnInit, ViewChild} from '@angular/core';
+import {Component, OnInit, ViewChild, Input, TemplateRef} from '@angular/core';
 import {EgProgressDialogComponent} from '@eg/share/dialog/progress.component';
 import {EgToastService} from '@eg/share/toast/toast.service';
+import {EgStringService} from '@eg/share/string/string.service';
+import {Observable} from 'rxjs/Rx';
 
 @Component({
   templateUrl: 'sandbox.component.html'
@@ -10,25 +12,49 @@ export class EgSandboxComponent implements OnInit {
     @ViewChild('progressDialog')
     private progressDialog: EgProgressDialogComponent;
 
+    //@ViewChild('helloStr') private helloStr: EgStringComponent;
+
+
+    testStr: string;
+    @Input() set testString(str: string) {
+        this.testStr = str;
+    }
+
+    name: string = 'Jane';
+
     constructor(
+        private strings: EgStringService,
         private toast: EgToastService
     ) {}
 
     ngOnInit() {
+        
     }
 
     showProgress() {
         this.progressDialog.open();
-        this.progressDialog.update({value: 1, max: 100})
-        setTimeout(() => this.progressDialog.update({value: 5}), 1000);
-        setTimeout(() => this.progressDialog.update({value: 10}), 2000);
-        setTimeout(() => this.progressDialog.update({value: 50}), 3000);
-        setTimeout(() => this.progressDialog.update({value: 95}), 4000);
-        setTimeout(() => this.progressDialog.close(), 5000);
+
+        // every 250ms emit x*10 for 0-10
+        Observable.timer(0, 250).map(x => x * 10).take(11).subscribe(
+            val => this.progressDialog.update({value: val, max: 100}),
+            err => {},
+            ()  => this.progressDialog.close()
+        );
     }
 
     testToast() {
         this.toast.success('HELLO TOAST TEST');
+        setTimeout(() => this.toast.danger('DANGER TEST AHHH!'), 4000);
+    }
+
+    testStrings() {
+        this.strings.interpolate('staff.sandbox.test', {name : 'janey'})
+            .then(txt => this.toast.success(txt));
+
+        setTimeout(() => {
+            this.strings.interpolate('staff.sandbox.test', {name : 'johnny'})
+                .then(txt => this.toast.success(txt));
+        }, 4000);
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.html b/Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.html
new file mode 100644 (file)
index 0000000..3befa3e
--- /dev/null
@@ -0,0 +1,65 @@
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title" i18n>Change Operator</h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" 
+      (click)="dismiss('cross_click')">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <form class="form-validated">
+      <div class="form-group row">
+        <label class="col-lg-4 text-right font-weight-bold" for="username" i18n>Username</label>
+        <input 
+          type="text" 
+          class="form-control col-lg-7"
+          id="username" 
+          name="username"
+          required
+          (keyup)="checkEnter($event)"
+          autocomplete="username"
+          i18n-placeholder
+          placeholder="Username..." 
+          [(ngModel)]="username"/>
+      </div>
+
+      <div class="form-group row">
+        <label class="col-lg-4 text-right font-weight-bold" 
+            for="password" i18n>Password</label>
+        <input 
+          type="password" 
+          class="form-control col-lg-7"
+          id="password" 
+          name="password"
+          required
+          (keyup)="checkEnter($event)"
+          autocomplete="current-password"
+          i18n-placeholder
+          placeholder="Password..." 
+          [(ngModel)]="password"/>
+      </div>
+
+      <div class="form-group row">
+        <label class="col-lg-4 text-right font-weight-bold" 
+            for="loginType" i18n>Login Type</label>
+        <select 
+          class="form-control col-lg-7" 
+          id="loginType" 
+          name="loginType"
+          placeholder="Login Type..."
+          i18n-placeholder
+          required
+          [(ngModel)]="loginType">
+          <option value="temp" selected i18n>Temporary</option>                   
+          <option value="staff" i18n>Staff</option>             
+          <option value="persist" i18n>Persistent</option>      
+        </select>
+      </div>
+    </form>
+  </div>
+  <div class="modal-footer">
+    <button (click)="login()" class="btn btn-info" i18n>OK/Continue</button>
+    <button (click)="dismiss('canceled')" class="btn btn-warning ml-2" i18n>Cancel</button>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.ts b/Open-ILS/src/eg2/src/app/staff/share/op-change/op-change.component.ts
new file mode 100644 (file)
index 0000000..2451f21
--- /dev/null
@@ -0,0 +1,59 @@
+import {Component, OnInit, Input, Renderer} from '@angular/core';
+import {EgAuthService} from '@eg/core/auth.service';
+import {EgDialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+  selector: 'eg-op-change',
+  templateUrl: 'op-change.component.html'
+})
+
+export class EgOpChangeComponent 
+    extends EgDialogComponent implements OnInit {
+
+    @Input() username: string;
+    @Input() password: string;
+    @Input() loginType: string = 'temp';
+
+       constructor(
+        private modal: NgbModal, // required for passing to parent
+        private renderer: Renderer,
+        private auth: EgAuthService) {
+        super(modal);
+    }
+
+    ngOnInit() {
+
+        // Focus the username any time the dialog is opened.
+        this.onOpen$.subscribe(
+            val => this.renderer.selectRootElement('#username').focus()
+        );
+    }
+
+    checkEnter($event: any): void {
+        if ($event.keyCode == 13)
+            this.login();
+    }
+
+    login() {
+        if (!(this.username && this.password)) return;
+        
+        this.auth.login(
+            {   username    : this.username, 
+                password    : this.password, 
+                workstation : this.auth.workstation(),
+                type        : this.loginType
+            },  true        // isOpChange
+        ).then(
+            ok => {
+                console.log('OP change succeeded'),
+                this.close('login OK');
+            },
+            notOk => {
+                console.warn('OP change failed')
+            }
+        );
+    }
+}
+
+