Angular Course Page Associate Users Tab
authorKyle Huckins <khuckins@catalyte.io>
Tue, 10 Dec 2019 19:03:17 +0000 (19:03 +0000)
committerJane Sandberg <sandbej@linnbenton.edu>
Wed, 26 Aug 2020 22:53:48 +0000 (15:53 -0700)
- Apply Course Users functionality to Angular Course Page
Admin UI.

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>
 Changes to be committed:
modified:   Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html
modified:   Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts
modified:   Open-ILS/src/eg2/src/app/staff/share/course.service.ts

Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts
Open-ILS/src/eg2/src/app/staff/share/course.service.ts

index 0361526..1b0f634 100644 (file)
         <!-- End Input Sidebar -->
         <div class="col-lg-8 mt-3">
           <eg-grid #materialsGrid [dataSource]="materialsDataSource">
-            <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
+            <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelectedMaterials($event)">
             </eg-grid-toolbar-action>
 
             <eg-grid-column path="id" [index]=true [hidden]="true" label="ID" i18n-label></eg-grid-column>
-            <eg-grid-column label="Barcode" i18n-label name="barcode" [cellTemplate]="barcodeCellTemplate"></eg-grid-column>
+            <eg-grid-column label="Barcode" i18n-label name="card" [cellTemplate]="barcodeCellTemplate"></eg-grid-column>
             <eg-grid-column label="Title" i18n-label name="title" [cellTemplate]="titleCellTemplate"></eg-grid-column>
             <eg-grid-column path="call_number.label" label="Call Number" i18n-label></eg-grid-column>
             <eg-grid-column path="call_number.prefix.label" [hidden]="true" label="Call Number Prefix" i18n-label hidden></eg-grid-column>
       <div class="row mt-3">
         <div class="col-lg-4 mt-3">
           <div class="row mt-3">
-            <div class="col-lg-12 d-flex">
+            <div class="col-lg-12 col-md-6 mt-3 d-flex">
               <div class="input-group">
                 <div class="input-group-prepend">
                   <span class="input-group-text" i18n>Patron Barcode</span>
                 </div>
                 <input type="text" class="flex-grow-1" [(ngModel)]="userBarcode"
                   (click)="$event.target.select()" 
-                  [disabled]="currentCourse && currentCourse.is_archived() == 't'" />
-                  <!--(keyup.enter)="associateUser(userBarcode)"-->
+                  [disabled]="currentCourse && currentCourse.is_archived() == 't'"
+                  (keyup.enter)="associateUser(userBarcode)" />
               </div>
             </div>
-          </div>
-          <div class="row mt-3">
-            <div class="col-lg-12 d-flex">
+            <div class="col-lg-12 col-md-6 mt-3 d-flex">
               <div class="input-group">
                 <div class="input-group-prepend">
                   <span class="input-group-text" i18n>Role</span>
             </div>
           </div>
           <div class="row mt-3">
-            <div class="col-lg-12 text-right">
-              <!--(click)="associateUser(userBarcode)"-->
+            <div class="col-sm-6">
+              <div class="input-group">
+                <div class="input-group-prepend">
+                  <div class="input-group-text">
+                    <span i18n>Is Public Role?</span>
+                  </div>
+                </div>
+                <div class="input-group-append">
+                  <div class="input-group-text">
+                    <input type="checkbox" [(ngModel)]="isPublicRole"
+                      [disabled]="currentCourse && currentCourse.is_archived() == 't'"
+                      aria-label="Checkbox for allowing user to display on the OPAC Course Page" />
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="col-sm-6 text-right">
               <button class="btn btn-primary"
                 [disabled]="currentCourse && currentCourse.is_archived() == 't'"
-                i18n [disabled]="!userBarcode">
+                i18n [disabled]="!userBarcode" (click)="associateUser(userBarcode)">
                 Add User
               </button>
             </div>
           </div>
         </div>
         <div class="col-lg-8 mt-3">
-          <!-- eg-grid -->
+          <eg-grid #usersGrid [dataSource]="usersDataSource">
+            <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelectedUsers($event)">
+            </eg-grid-toolbar-action>
+            <eg-grid-column label="id" [index]=true [hidden]="true" i18n-label></eg-grid-column>
+            <eg-grid-column label="User Role" name="_role" i18n-label></eg-grid-column>
+            <eg-grid-column label="Viewable on OPAC" name="_is_public" i18n-label datatype="bool"></eg-grid-column>
+            <eg-grid-column label="First Name" name="first_given_name" i18n-label></eg-grid-column>
+            <eg-grid-column label="Second Name" name="second_given_name" i18n-label></eg-grid-column>
+            <eg-grid-column label="Last Name" name="family_name" i18n-label></eg-grid-column>
+            <eg-grid-column label="Prefix" name="pref_prefix" [hidden]="true" i18n-label></eg-grid-column>
+            <eg-grid-column label="Preferred First Name" name="pref_first_given_name"[hidden]="true"  i18n-label></eg-grid-column>
+            <eg-grid-column label="Preferred Second Name" name="pref_second_given_name"[hidden]="true"  i18n-label></eg-grid-column>
+            <eg-grid-column label="Preferred Family Name" name="pref_family_name"[hidden]="true"  i18n-label></eg-grid-column>
+            <eg-grid-column label="Preferred Suffix" name="pref_suffix" [hidden]="true" i18n-label></eg-grid-column>
+          </eg-grid>
         </div>
       </div>
     </ng-template>
 <eg-string #materialDeleteSuccessString i18n-text text="Disassociation of Course Material succeeded"></eg-string>
 <eg-string #materialAddSuccessString i18n-text text="Association of Course Material succeeded"></eg-string>
 <eg-string #materialAddFailedString i18n-text text="Association of Course Material failed or was not allowed"></eg-string>
-<eg-string #MaterialAddDifferentLibraryString i18n-text text="Material exists at a different library"></eg-string>
\ No newline at end of file
+<eg-string #MaterialAddDifferentLibraryString i18n-text text="Material exists at a different library"></eg-string>
+<eg-string #userDeleteFailedString i18n-text text="Removal of User failed or was not allowed"></eg-string>
+<eg-string #userDeleteSuccessString i18n-text text="Removal of User succeeded"></eg-string>
+<eg-string #userAddSuccessString i18n-text text="Addition of User succeeded"></eg-string>
+<eg-string #userAddFailedString i18n-text text="Addition of User failed or was not allowed"></eg-string>
index 3765496..0d25c22 100644 (file)
@@ -59,19 +59,33 @@ export class CoursePageComponent implements OnInit {
     @Input() isModifyingLocation: Boolean;
 
     // Users Tab
+    users: any[] = [];
+    @ViewChild('usersGrid', {static: true}) usersGrid: GridComponent;
+    @ViewChild('userDeleteFailedString', { static: true })
+        userDeleteFailedString: StringComponent;
+    @ViewChild('userDeleteSuccessString', { static: true })
+        userDeleteSuccessString: StringComponent;
+    @ViewChild('userAddSuccessString', { static: true })
+        userAddSuccessString: StringComponent;
+    @ViewChild('userAddFailedString', { static: true })
+        userAddFailedString: StringComponent;
+    usersDataSource: GridDataSource;
     @Input() userBarcode: String;
     @Input() userRoleInput: String;
+    @Input() isPublicRole: Boolean;
     constructor(
         private auth: AuthService,
         private course: CourseService,
         private event: EventService,
         private idl: IdlService,
+        private net: NetService,
         private org: OrgService,
         private pcrud: PcrudService,
         private route: ActivatedRoute,
         private toast: ToastService
     ) {
         this.materialsDataSource = new GridDataSource();
+        this.usersDataSource = new GridDataSource();
     }
 
     ngOnInit() {
@@ -79,6 +93,9 @@ export class CoursePageComponent implements OnInit {
         this.course.getCourses([this.courseId]).then(course => {
             this.currentCourse = course[0];
         });
+        this.usersDataSource.getRows = (pager: Pager, sort: any[]) => {
+            return this.loadUsersGrid(pager);
+        }
         this.materialsDataSource.getRows = (pager: Pager, sort: any[]) => {
             return this.loadMaterialsGrid(pager);
         }
@@ -159,7 +176,7 @@ export class CoursePageComponent implements OnInit {
         }
     }
 
-    deleteSelected(items) {
+    deleteSelectedMaterials(items) {
         let item_ids = [];
         items.forEach(item => {
             this.materialsDataSource.data.splice(this.materialsDataSource.data.indexOf(item, 0), 1);
@@ -180,4 +197,70 @@ export class CoursePageComponent implements OnInit {
             );
         });
     }
+   
+   // Users Tab
+    loadUsersGrid(pager: Pager): Observable<any> {
+        return new Observable<any>(observer => {
+            this.course.getUsers(this.courseId).then(users => {
+                users.forEach(user => {
+                    console.log(user);
+                    this.course.fleshUser(user.usr(), user.usr_role(), user.is_public()).then(fleshed_user => {
+                        this.usersDataSource.data.push(fleshed_user);
+                    });
+                });
+            });
+            observer.complete();
+        });
+    }
+
+    associateUser(barcode) {
+        if (barcode) {
+            let args = {
+                currentCourse: this.currentCourse,
+                barcode: barcode,
+                role: this.userRoleInput,
+                is_public: this.isPublicRole
+            }
+
+            this.userBarcode = null;
+
+            this.net.request(
+                'open-ils.actor',
+                'open-ils.actor.user.retrieve_id_by_barcode_or_username',
+                this.auth.token(), barcode
+            ).subscribe(patron => {
+                let associatedUser = this.course.associateUsers(patron, args).then(res => {
+                    this.course.fleshUser(
+                        patron, args.role, args.is_public
+                    ).then(fleshed_user => {
+                        this.usersDataSource.data.push(fleshed_user);
+                        this.userAddSuccessString.current().then(str => this.toast.success(str));
+                    });
+                }, err => {
+                    this.userAddFailedString.current().then(str => this.toast.danger(str));
+                });
+            });
+        }
+    }
+
+    deleteSelectedUsers(users) {
+        let user_ids = [];
+        users.forEach(user => {
+            this.usersDataSource.data.splice(this.usersDataSource.data.indexOf(user, 0), 1);
+            user_ids.push(user.id())
+        });
+        this.pcrud.search('acmcu', {course: this.courseId, usr: user_ids}).subscribe(user => {
+            user.isdeleted(true);
+            this.pcrud.autoApply(user).subscribe(
+                val => {
+                    console.debug('deleted: ' + val);
+                    this.userDeleteSuccessString.current().then(str => this.toast.success(str));
+                },
+                err => {
+                    this.userDeleteFailedString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            );
+        });
+    }
 }
\ No newline at end of file
index de946c4..798ea9b 100644 (file)
@@ -45,6 +45,16 @@ export class CourseService {
         }
     }
 
+    getUsers(course_ids?: Number[]): Promise<IdlObject[]> {
+        if (!course_ids) {
+            return this.pcrud.retrieveAll('acmcu',
+                {}, {atomic: true}).toPromise();
+        } else {
+            return this.pcrud.search('acmcu', {course: course_ids},
+                {}, {atomic: true}).toPromise();
+        }
+    }
+
     fleshMaterial(itemId, relationship?): Promise<any> {
         return new Promise((resolve, reject) => {
             let item = this.idl.create('acp');
@@ -66,6 +76,23 @@ export class CourseService {
         });
     }
 
+    fleshUser(patron_id, role?, is_public?): Promise<any> {
+        return new Promise((resolve, reject) => {
+            let user = this.idl.create('au');
+            this.net.request(
+                'open-ils.actor',
+                'open-ils.actor.user.fleshed.retrieve',
+                this.auth.token(), patron_id
+            ).subscribe(patron => {
+                user = patron;
+                if (role) user._role = role
+                if (is_public) user._is_public = is_public
+            }, err => {
+                reject(err);
+            }, () => resolve(user));
+        });
+    }
+
     getCoursesFromMaterial(copy_id): Promise<any> {
         let id_list = [];
         return new Promise((resolve, reject) => {
@@ -145,6 +172,16 @@ export class CourseService {
         return response;
     }
 
+    associateUsers(patron_id, args) {
+        console.log(args);
+        let new_user = this.idl.create('acmcu');
+        if (args.is_public) new_user.is_public(args.is_public);
+        if (args.role) new_user.usr_role(args.role);
+        new_user.course(args.currentCourse.id());
+        new_user.usr(patron_id);
+        return this.pcrud.create(new_user).toPromise()
+    }
+
     disassociateMaterials(courses) {
         return new Promise((resolve, reject) => {
             let course_ids = [];