-
-<eg-staff-banner bannerText="Hold Details (#{{hold.id}})" i18n-bannerText>
-</eg-staff-banner>
+<eg-hold-note-dialog #noteDialog [holdId]="holdId"></eg-hold-note-dialog>
+<eg-hold-notify-dialog #notifyDialog [holdId]="holdId"></eg-hold-notify-dialog>
<div class="row">
- <div class="col-lg-3">
+ <div class="col-lg-2">
<button (click)="showListView()" class="btn btn-info" i18n>List View</button>
</div>
+ <div class="col-lg-3 font-weight-bold" i18n><h4>Hold #{{holdId}}</h4></div>
</div>
<div class="well-table">
</div>
</div>
+<div class="row mt-2">
+ <div class="col-lg-12">
+ <ul ngbNav #detailNav="ngbNav" class="nav-tabs" [activeId]="detailTab">
+ <li ngbNavItem="notes">
+ <a ngbNavLink i18n>Notes</a>
+ <ng-template ngbNavContent>
+ <button class="btn btn-outline-dark mt-3" (click)="newNote()" i18n>New Note</button>
+ <div class="mt-3" *ngFor="let note of notes">
+ <div class="d-flex">
+ <div class="font-weight-bold">{{note.title()}}</div>
+ <div class="flex-1"></div>
+ <div>
+ <span *ngIf="note.slip() == 't'"
+ class="ml-2 badge badge-info p-1">Print on Slip</span>
+ <span *ngIf="note.pub() == 't'"
+ class="ml-2 badge badge-warning p-1">Patron Visible</span>
+ <span *ngIf="note.staff() == 't'"
+ class="ml-2 badge badge-info p-1">Staff Create</span>
+ </div>
+ </div>
+ <div class="well-table">
+ <div class="well-row">
+ <div class="well-value">{{note.body()}}</div>
+ <div class="well-label-no-flex">
+ <button class="btn btn-warning"
+ (click)="deleteNote(note)" i18n>Delete</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-template>
+ </li>
+ <li ngbNavItem="notifications">
+ <a ngbNavLink i18n>Staff Notifications</a>
+ <ng-template ngbNavContent>
+ <button class="btn btn-outline-dark mt-3"
+ (click)="newNotify()" i18n>Add Record of Notification</button>
+ <div class="mt-3" *ngFor="let notify of notifies">
+ <div class="d-flex">
+ <div class="font-weight-bold">{{notify.method()}}</div>
+ <div class="flex-1"></div>
+ <div>
+ <span>{{notify.notify_time() | date:'short'}}</span>
+ <span class="ml-2" i18n>
+ Created by {{notify.notify_staff().usrname()}}</span>
+ </div>
+ <div>
+ </div>
+ </div>
+ <div class="well-table">
+ <div class="well-row">
+ <div class="well-value">{{notify.note()}}</div>
+ </div>
+ </div>
+ </div>
+ </ng-template>
+ </li>
+ </ul>
+ <div [ngbNavOutlet]="detailNav"></div>
+ </div>
+</div>
import {Component, OnInit, Input, Output, ViewChild, EventEmitter} from '@angular/core';
import {Observable, Observer, of} from 'rxjs';
+import {tap} from 'rxjs/operators';
+import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
import {OrgService} from '@eg/core/org.service';
import {AuthService} from '@eg/core/auth.service';
+import {HoldNoteDialogComponent} from './note-dialog.component';
+import {HoldNotifyDialogComponent} from './notify-dialog.component';
/** Hold details read-only view */
templateUrl: 'detail.component.html'
})
export class HoldDetailComponent implements OnInit {
+ detailTab = 'notes';
+ notes: IdlObject[] = [];
+ notifies: IdlObject[] = [];
- _holdId: number;
+ private _holdId: number;
@Input() set holdId(id: number) {
- this._holdId = id;
- if (this.initDone) {
- this.fetchHold();
+ if (this._holdId !== id) {
+ this._holdId = id;
+ if (this.initDone) {
+ this.fetchHold();
+ }
}
}
+ get holdId(): number {
+ return this._holdId;
+ }
+
hold: any; // wide hold reference
@Input() set wideHold(wh: any) {
this.hold = wh;
}
+ get wideHold(): any {
+ return this.hold;
+ }
+
initDone: boolean;
@Output() onShowList: EventEmitter<any>;
+ @ViewChild('noteDialog') noteDialog: HoldNoteDialogComponent;
+ @ViewChild('notifyDialog') notifyDialog: HoldNotifyDialogComponent;
+
constructor(
private net: NetService,
+ private pcrud: PcrudService,
private org: OrgService,
private auth: AuthService,
) {
}
fetchHold() {
- if (!this._holdId) { return; }
+ if (!this.holdId && !this.hold) { return; }
- this.net.request(
- 'open-ils.circ',
- 'open-ils.circ.hold.wide_hash.stream',
- this.auth.token(), {id: this._holdId}
- ).subscribe(wideHold => {
+ const promise = this.hold ? Promise.resolve(this.hold) :
+ this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.hold.wide_hash.stream',
+ this.auth.token(), {id: this.holdId}
+ ).toPromise();
+
+ return promise.then(wideHold => {
this.hold = wideHold;
- });
+ // avoid this.holdId = since it re-fires this fetch.
+ this._holdId = wideHold.id;
+ })
+ .then(_ => this.getNotes())
+ .then(_ => this.getNotifies());
+ }
+
+ getNotes(): Promise<any> {
+ this.notes = [];
+ return this.pcrud.search('ahrn', {hold: this.holdId})
+ .pipe(tap(note => this.notes.push(note))).toPromise();
+ }
+
+ getNotifies(): Promise<any> {
+ this.notifies = [];
+
+ return this.pcrud.search('ahn', {hold: this.holdId}, {
+ flesh: 1,
+ flesh_fields: {ahn: ['notify_staff']},
+ order_by: {ahn: 'notify_time DESC'}
+ }).pipe(tap(notify => this.notifies.push(notify))).toPromise();
}
getOrgName(id: number) {
showListView() {
this.onShowList.emit();
}
+
+ deleteNote(note: IdlObject) {
+ this.pcrud.remove(note).toPromise()
+ .then(ok => { if (ok) { this.getNotes(); } });
+ }
+
+ newNote() {
+ this.noteDialog.open().subscribe(note => this.notes.unshift(note));
+ }
+
+ newNotify() {
+ this.notifyDialog.open().subscribe(notify => this.getNotifies()); // fleshing
+ }
}
import {HoldTransferDialogComponent} from './transfer-dialog.component';
import {HoldCancelDialogComponent} from './cancel-dialog.component';
import {HoldManageDialogComponent} from './manage-dialog.component';
+import {HoldNoteDialogComponent} from './note-dialog.component';
+import {HoldNotifyDialogComponent} from './notify-dialog.component';
@NgModule({
declarations: [
HoldRetargetDialogComponent,
HoldTransferDialogComponent,
HoldCancelDialogComponent,
- HoldManageDialogComponent
+ HoldManageDialogComponent,
+ HoldNoteDialogComponent,
+ HoldNotifyDialogComponent
],
imports: [
StaffCommonModule,
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title">Add Hold Note for Hold #{{holdId}}</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body form-validated">
+ <div class="form-group form-check">
+ <input type="checkbox" class="form-check-input"
+ id="patron-visible-cbox" [(ngModel)]="pub">
+ <label class="form-check-label"
+ for="patron-visible-cbox" i18n>Patron Visible?</label>
+ </div>
+ <div class="form-group form-check">
+ <input type="checkbox" class="form-check-input"
+ id="hold-slip-cbox" [(ngModel)]="slip">
+ <label class="form-check-label"
+ for="hold-slip-cbox" i18n>Print on Slip?</label>
+ </div>
+ <div class="form-group">
+ <label for="title-input" i18n>Note Title</label>
+ <input type="text" class="form-control" id="title-input" required [(ngModel)]="title"/>
+ </div>
+ <div class="form-group">
+ <label for="body-input" i18n>Note Body</label>
+ <textarea class="form-control" id="body-input" required [(ngModel)]="body">
+ </textarea>
+ </div>
+ <div class="w-100">
+ <button class="btn btn-success ml-auto" (click)="createNote()"
+ [disabled]="!title || !body" i18n>Create Note</button>
+ <button class="btn btn-warning ml-2" (click)="close()" i18n>Cancel</button>
+ </div>
+ </div>
+</ng-template>
--- /dev/null
+import {Component, OnInit, Input, Output, ViewChild, EventEmitter} from '@angular/core';
+import {Observable, Observer, of} from 'rxjs';
+import {tap} from 'rxjs/operators';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+
+/** New hold note dialog */
+
+@Component({
+ selector: 'eg-hold-note-dialog',
+ templateUrl: 'note-dialog.component.html'
+})
+export class HoldNoteDialogComponent extends DialogComponent {
+ pub = false;
+ slip = false;
+ title: string;
+ body: string;
+
+ @Input() holdId: number;
+
+ constructor(
+ private modal: NgbModal,
+ private idl: IdlService,
+ private pcrud: PcrudService
+ ) { super(modal); }
+
+ createNote() {
+ const note = this.idl.create('ahrn');
+ note.staff('t');
+ note.hold(this.holdId);
+ note.title(this.title);
+ note.body(this.body);
+ note.slip(this.slip ? 't' : 'f');
+ note.pub(this.pub ? 't' : 'f');
+
+ this.pcrud.create(note).toPromise().then(
+ resp => this.close(resp), // new note object
+ err => console.error('Could not create note', err)
+ );
+ }
+}
+
+
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title">Create Record of Hold Notification</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body form-validated">
+ <div class="form-group">
+ <label for="method-input" i18n>Notification Method</label>
+ <input type="text" class="form-control" id="method-input" required [(ngModel)]="method"/>
+ </div>
+ <div class="form-group">
+ <label for="note-input" i18n>Note</label>
+ <textarea class="form-control" id="note-input" required [(ngModel)]="note">
+ </textarea>
+ </div>
+ <div class="w-100">
+ <button class="btn btn-success ml-auto" (click)="createNotify()"
+ [disabled]="!method || !note" i18n>Create</button>
+ <button class="btn btn-warning ml-2" (click)="close()" i18n>Cancel</button>
+ </div>
+ </div>
+</ng-template>
--- /dev/null
+import {Component, OnInit, Input, Output, ViewChild, EventEmitter} from '@angular/core';
+import {Observable, Observer, of} from 'rxjs';
+import {tap} from 'rxjs/operators';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+
+/** New hold notify dialog */
+
+@Component({
+ selector: 'eg-hold-notify-dialog',
+ templateUrl: 'notify-dialog.component.html'
+})
+export class HoldNotifyDialogComponent extends DialogComponent {
+ method: string;
+ note: string;
+
+ @Input() holdId: number;
+
+ constructor(
+ private modal: NgbModal,
+ private idl: IdlService,
+ private auth: AuthService,
+ private pcrud: PcrudService
+ ) { super(modal); }
+
+ createNotify() {
+ const notify = this.idl.create('ahn');
+ notify.hold(this.holdId);
+ notify.notify_staff(this.auth.user().id());
+ notify.method(this.method);
+ notify.note(this.note);
+
+ this.pcrud.create(notify).toPromise().then(
+ resp => this.close(resp), // new notify object
+ err => console.error('Could not create notify', err)
+ );
+ }
+}
+
+
min-height: 40px;
}
+.well-table .well-label-no-flex {
+ display: flex;
+ align-items: center;
+ margin: 4px;
+ padding: 4px;
+ min-height: 40px;
+}
+
.well-table .well-value {
flex: 1;
display: flex;