lp1852321 Angular Shelving Location Groups UI Port
authorMike Risher <mrisher@catalyte.io>
Mon, 2 Dec 2019 20:38:15 +0000 (20:38 +0000)
committerGalen Charlton <gmc@equinoxinitiative.org>
Mon, 8 Mar 2021 19:32:39 +0000 (14:32 -0500)
Port Shelving Location Groups interface from DOJO to Angular. Features
ported include  creating and editing Location Groups, dragging and dropping
Location Groups to change their saved order, adding and removing
Group Entgries, and sorting Shelving Locations in the same order as the old
interface.

Signed-off-by: Mike Risher <mrisher@catalyte.io>
Signed-off-by: Jennifer Weston <jennifer.weston@equinoxinitiative.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups_routing.module.ts [new file with mode: 0644]

index 42cad63..36dc6d5 100644 (file)
@@ -5632,7 +5632,7 @@ SELECT  usr,
                        <field reporter:label="ID" name="id" reporter:selector="name" reporter:datatype="id"/>
                        <field reporter:label="Name" name="name"  reporter:datatype="text" oils_persist:i18n="true"/>
                        <field reporter:label="Is OPAC Visible?" name="opac_visible" reporter:datatype="bool"/>
-                       <field reporter:label="Owning Org Unit" name="owner"  reporter:datatype="org_unit"/>
+                       <field reporter:label="Owning Library" name="owner" reporter:datatype="org_unit" />
             <field reporter:label="Position" name="pos" reporter:datatype="int"/>
             <field reporter:label="Display Above Orgs" name="top" reporter:datatype="bool"/>
             <field reporter:label="Copy Location Mappings" name="location_maps" oils_persist:virtual="true" reporter:datatype="link"/>
index ff241f7..d9e34ea 100644 (file)
@@ -59,7 +59,7 @@
     <eg-link-table-link i18n-label label="Search Filter Groups" 
       url="/eg/staff/admin/local/actor/search_filter_group"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Shelving Location Groups" 
-      url="/eg/staff/admin/local/asset/copy_location_group"></eg-link-table-link>
+      routerLink="/staff/admin/local/asset/shelving_location_groups"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Shelving Location Order" 
       url="/eg/staff/admin/local/asset/copy_location_order"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Shelving Locations Editor" 
index ccb3f18..0819945 100644 (file)
@@ -25,6 +25,9 @@ const routes: Routes = [{
         table: 'copy_location',
         fieldOrder: 'owning_lib,name,opac_visible,circulate,holdable,hold_verify,checkin_alert,deleted,label_prefix,label_suffix,url,id'}]
 }, {
+    path: 'asset/shelving_location_groups',
+    loadChildren: '@eg/staff/admin/local/shelving_location_groups/shelving_location_groups.module#ShelvingLocationGroupsModule'
+}, {
     path: 'container/carousel',
     component: AdminCarouselComponent
 }, {
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.html
new file mode 100644 (file)
index 0000000..8dcc5b6
--- /dev/null
@@ -0,0 +1,177 @@
+<div class="row">
+    <div style="font-size: 24px;" class="mr-3">Shelving Location Groups</div>
+    <eg-org-select (onChange)="orgOnChange($event)" [initialOrgId]="1"></eg-org-select>     
+</div>
+<div class="row">
+    <p>Location groups can be re-ordered by dragging and dropping.</p>
+</div>
+<div style="height: 450px;">
+    <div class="row h-100">
+        <div class="col-lg-6 h-100">
+            <eg-staff-banner bannerText="Location Groups" i18n-bannerText>
+            </eg-staff-banner>
+            <div class="text-center">          
+                <div class="btn btn-outline-dark mb-3 ml-2" (click)="createLocationGroup()" 
+                    *ngIf="hasPermission">
+                    New Location Group                     
+                </div>
+                <div class="btn btn-outline-dark mb-3 ml-2 disabled"
+                    *ngIf="!hasPermission">
+                    New Location Group                     
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-lg-6 h-100 p-2 text-center">
+                    <span class="align-middle font-weight-bold">Name</span>
+                </div>
+                <div class="col-lg-1 h-100 p-2 text-center">
+                    <span class="align-middle font-weight-bold">Pos</span>
+                </div>
+                <div class="col-lg-2 h-100 p-2 text-center">
+                    <span class="align-middle font-weight-bold">Visible?</span>
+                </div>
+                <div class="col-lg-3">    
+                </div>
+            </div>
+            
+            <div *ngFor="let group of locationGroups; let dragIndex = index" 
+                class="d-flex flex-row locationGroup"
+                onmouseover="this.style.cursor='move'" 
+                draggable="true"
+                (dragstart)="onDragStart($event, group)"
+                (dragenter)="onDragEnter($event, group)" 
+                (dragover)="onDragOver($event)" 
+                (drop)="onDragDrop($event, dragIndex)">
+                <div *ngIf="group.selected"
+                    class="col-lg-6 h-100 p-2 bg-warning border border-dark">
+                    <span class="align-middle">{{group.name}}</span>
+                </div>
+                <div *ngIf="!group.selected" class="col-lg-6 h-100 p-2" 
+                    (click)='changeSelectedLocationGroup(group)'>
+                    <span class="align-middle">{{group.name}}</span>
+                </div>
+                <div class="col-lg-1 h-100 p-2 text-center">
+                    <span class="align-middle">{{group.posit}}</span>
+                </div>
+                <div class="col-lg-2 h-100 p-2 text-center">
+                    <span *ngIf="group.isVisible" class="align-middle text-success">
+                        Visible
+                    </span>
+                    <span *ngIf="!group.isVisible" class="align-middle text-danger">
+                        Not Visible
+                    </span>
+                </div>
+                <div class="col-lg-3 h-100 btn-group p-2">
+                    <span *ngIf="hasPermission"
+                        class="btn btn-sm btn-outline-dark mr-1" 
+                        (click)="editLocationGroup(group)">
+                        Edit
+                    </span>
+                    <span *ngIf="!hasPermission" 
+                        class="btn btn-sm btn-outline-dark mr-1 disabled">
+                        Edit
+                    </span>
+                    <span *ngIf="hasPermission"
+                        class="btn btn-sm btn-danger mr-2" 
+                        (click)="deleteLocationGroup(group)">
+                        Delete
+                    </span>
+                    <span *ngIf="!hasPermission"
+                        class="btn btn-sm btn-danger mr-2 disabled">
+                        Delete
+                    </span>
+                    <span class="align-middle">
+                        <svg class="pt-2" xmlns="http://www.w3.org/2000/svg" width="24" 
+                            height="24" viewBox="0 0 24 24">
+                            <path fill="none" d="M0 0h24v24H0V0z"/><path d="M11 18c0 
+                            1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 
+                            .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9
+                            2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 
+                            .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 
+                            2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 
+                            2-2-.9-2-2-2z"/>
+                        </svg>
+                        <!-- As of Nov 2019 drag_indicator not included in library -->
+                        <!-- <span class="material-icons pt-2">drag_indicator</span> -->
+                    </span>
+                </div>
+            </div>
+        </div>
+        <div class="col-lg-3 h-100">
+            <eg-staff-banner bannerText="Group Entries" i18n-bannerText></eg-staff-banner>
+            <div class="text-center">
+                <div class="btn btn-outline-dark mb-2" (click)="removeEntries()">
+                    Remove &rarr;
+                </div>
+            </div>
+            <div class="row overflow-auto h-100">
+                <table class="table table-striped col-lg-10 offset-lg-1">
+                    <thead></thead>
+                    <tbody>
+                        <tr *ngFor="let group of groupEntries" class="row">
+                            <td>
+                                <input
+                                type="checkbox" 
+                                class="mr-2"
+                                [(ngModel)]="group.checked"
+                                [checked]="group.checked"
+                                id="{{group.label}}" /> 
+                                <label for="{{group.label}}">
+                                    {{group.shortname}} &nbsp; {{group.name}}
+                                </label>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <div class="col-lg-3 h-100">
+            <eg-staff-banner bannerText="Shelving Locations" i18n-bannerText>
+            </eg-staff-banner>
+            <div class="text-center">
+                <div class="btn btn-outline-dark mb-2" (click)="addEntries()">
+                    &larr; Add
+                </div>
+            </div>
+            <div  class="row overflow-auto h-100">
+                <table class="table table-striped col-lg-10 offset-lg-1">
+                    <thead></thead>
+                    <tbody>
+                        <ng-container *ngFor="let location of shelvingLocations" >
+                            <tr class="row" *ngIf="!location.hidden">
+                                <td>
+                                    <input
+                                    type="checkbox" 
+                                    class="mr-2"
+                                    [(ngModel)]="location.checked"
+                                    [checked]="location.checked"
+                                    id="{{location.label}}" /> 
+                                    <label for="{{location.label}}">
+                                        {{location.shortname}} &nbsp; {{location.name}}
+                                    </label>
+                                </td>
+                            </tr>
+                        </ng-container>               
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
+
+<eg-fm-record-editor #editDialog hiddenFieldsList="id" idlClass="acplg" 
+    readonlyFields="owner"
+    requiredFields="name,pos" [defaultNewRecord]="defaultNewRecord"></eg-fm-record-editor>
+<eg-string #editLocGroupSuccess i18n-text text="Edited Location Group"></eg-string>
+<eg-string #editLocGroupFailure i18n-text 
+    text="Error when trying to edit Location Group"></eg-string>
+<eg-string #addedGroupEntriesSuccess i18n-text text="Added to Group Entries"></eg-string>
+<eg-string #addedGroupEntriesFailure i18n-text 
+    text="Error when trying to add to Group Entries"></eg-string>
+<eg-string #removedGroupEntriesSuccess i18n-text 
+    text="Removed from Group Entries"></eg-string>
+<eg-string #removedGroupEntriesFailure i18n-text 
+    text="Error when trying to remove from Group Entries"></eg-string>
+<eg-string #changeOrderSuccess i18n-text text="Changed Location Group order"></eg-string>
+<eg-string #changeOrderFailure i18n-text 
+    text="Error when trying to change Location Group Order"></eg-string>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.ts
new file mode 100644 (file)
index 0000000..ce5a1da
--- /dev/null
@@ -0,0 +1,375 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.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';
+import {PermService} from '@eg/core/perm.service';
+
+@Component({
+    templateUrl: './shelving_location_groups.component.html'
+})
+
+export class ShelvingLocationGroupsComponent implements OnInit {
+
+    selectedOrg: IdlObject;
+    selectedOrgId = 1;
+    locationGroups: IdlObject[];
+    shelvingLocations: IdlObject[];
+    groupEntries: IdlObject[];
+    selectedLocationGroupId: number;
+    selectedLocationGroup: IdlObject;
+    permissions: number[];
+    hasPermission = false;
+    draggedElement: IdlObject;
+    dragTarget: IdlObject;
+    defaultNewRecord: IdlObject;
+
+    @ViewChild ('editDialog', { static: true }) private editDialog: FmRecordEditorComponent;
+    @ViewChild ('editLocGroupSuccess', {static: true}) editLocGroupSuccess: StringComponent;
+    @ViewChild ('editLocGroupFailure', {static: true}) editLocGroupFailure: StringComponent;
+    @ViewChild ('addedGroupEntriesSuccess', {static: true})
+        addedGroupEntriesSuccess: StringComponent;
+    @ViewChild ('addedGroupEntriesFailure', {static: true})
+        addedGroupEntriesFailure: StringComponent;
+    @ViewChild ('removedGroupEntriesSuccess', {static: true})
+        removedGroupEntriesSuccess: StringComponent;
+    @ViewChild ('removedGroupEntriesFailure', {static: true})
+        removedGroupEntriesFailure: StringComponent;
+    @ViewChild ('changeOrderSuccess', {static: true}) changeOrderSuccess: StringComponent;
+    @ViewChild ('changeOrderFailure', {static: true}) changeOrderFailure: StringComponent;
+
+    constructor(
+        private org: OrgService,
+        private pcrud: PcrudService,
+        private toast: ToastService,
+        private idl: IdlService,
+        private perm: PermService
+    ) {
+       this.permissions = [];
+    }
+
+    ngOnInit() {
+        this.loadLocationGroups();
+        this.perm.hasWorkPermAt(['ADMIN_COPY_LOCATION_GROUP'], true).then((perm) => {
+            this.permissions = perm['ADMIN_COPY_LOCATION_GROUP'];
+            this.checkCurrentPermissions();
+        });
+    }
+
+    checkCurrentPermissions = () => {
+        this.hasPermission =
+            (this.permissions.indexOf(this.selectedOrgId) !== -1);
+    }
+
+    createLocationGroup = () => {
+        this.editDialog.mode = 'create';
+        this.defaultNewRecord = this.idl.create('acplg');
+        this.defaultNewRecord.owner(this.selectedOrgId);
+        let highestPosition = 0;
+        if (this.locationGroups.length) {
+            highestPosition = this.locationGroups[0].posit;
+            this.locationGroups.forEach(grp => {
+                if (grp.posit > highestPosition) {
+                    highestPosition = grp.posit;
+                }
+            });
+        }
+        // make the new record the last one on the list
+        this.defaultNewRecord.pos(highestPosition + 1);
+        this.editDialog.record = this.defaultNewRecord;
+        this.editDialog.recordId = null;
+        this.editDialog.open({size: 'lg'}).subscribe(
+            newLocationGroup => {
+                this.processLocationGroup(newLocationGroup);
+                this.locationGroups.push(newLocationGroup);
+                // select it by default if it's the only location group
+                if (this.locationGroups.length === 1) {
+                    this.markAsSelected(newLocationGroup);
+                } else {
+                    this.sortLocationGroups();
+                }
+                console.debug('Record editor performed action');
+            }, err => {
+                console.debug(err);
+            }
+        );
+    }
+
+    processLocationGroup = (locationGroup) => {
+        locationGroup.isVisible = (locationGroup.opac_visible() === 't');
+        locationGroup.posit = locationGroup.pos();
+        locationGroup.name = locationGroup.name();
+    }
+
+    editLocationGroup = (group) => {
+        this.editDialog.mode = 'update';
+        this.editDialog.recordId = group.id();
+        this.editDialog.open({size: 'lg'}).subscribe(
+            id => {
+                console.debug('Record editor performed action');
+                this.loadLocationGroups();
+            },
+            err => {
+                console.debug(err);
+            },
+            () => console.debug('Dialog closed')
+        );
+    }
+
+    deleteLocationGroup = (locationGroupToDelete) => {
+        const idToDelete = locationGroupToDelete.id();
+        this.pcrud.remove(locationGroupToDelete).subscribe(
+            ok => {
+                this.locationGroups.forEach((locationGroup, index) => {
+                    if (locationGroup.id() === idToDelete) {
+                        this.locationGroups.splice(index, 1);
+                    }
+                });
+            },
+            err => console.debug(err)
+        );
+    }
+
+    sortLocationGroups = () => {
+        this.locationGroups.sort((a, b) => (a.posit > b.posit) ? 1 : -1);
+    }
+
+    loadLocationGroups = () => {
+        this.locationGroups = [];
+        this.pcrud.search('acplg', {owner: this.selectedOrgId}, {
+            flesh: 1,
+            flesh_fields: {acplg: ['opac_visible', 'pos', 'name']},
+            order_by: {acplg: 'owner'}
+        }).subscribe(data => {
+            this.processLocationGroup(data);
+            this.locationGroups.push(data);
+        }, (error) => {
+            console.debug(error);
+        }, () => {
+            this.sortLocationGroups();
+            if (this.locationGroups.length) {
+                this.markAsSelected(this.locationGroups[0]);
+            }
+            this.loadGroupEntries();
+        });
+    }
+
+    changeSelectedLocationGroup = (group) => {
+        this.selectedLocationGroup.selected = false;
+        this.markAsSelected(group);
+        this.loadGroupEntries();
+    }
+
+    markAsSelected = (locationGroup) => {
+        this.selectedLocationGroup = locationGroup;
+        this.selectedLocationGroup.selected = true;
+        this.selectedLocationGroupId = locationGroup.id();
+    }
+
+    loadGroupEntries = () => {
+        this.groupEntries = [];
+        this.pcrud.search('acplgm', {lgroup: this.selectedLocationGroupId}, {
+            flesh: 1,
+            flesh_fields: {acplgm: ['location']},
+            order_by: {acplgm: ['location']}
+        }).subscribe(data => {
+            data.name = data.location().name();
+            data.shortname = this.org.get(data.location().owning_lib()).shortname();
+            // remove all non-alphanumeric chars to make label a valid id
+            data.label = (data.shortname + data.name).replace(/\W/g, '');
+            data.checked = false;
+            this.groupEntries.push(data);
+        }, (error) => {
+            console.debug(error);
+        }, () => {
+            this.loadShelvingLocations();
+        });
+    }
+
+    loadShelvingLocations = () => {
+        let orgList = this.org.fullPath(this.selectedOrgId, false);
+        orgList.sort(function(a, b) {
+            return a.ou_type().depth() < b.ou_type().depth() ? -1 : 1;
+        });
+        orgList = orgList.map((member) => {
+            return member.id();
+        });
+        const groupEntryIds = this.groupEntries.map(
+            (group) => group.location().id());
+        this.shelvingLocations = [];
+        this.pcrud.search('acpl', {owning_lib : orgList, deleted: 'f'})
+        .subscribe(data => {
+            data.name = data.name();
+            data.shortname = this.org.get(data.owning_lib()).shortname();
+            // remove all non-alphanumeric chars to make label a valid id
+            data.label = (data.shortname + data.name).replace(/\W/g, '');
+            data.checked = false;
+            if (groupEntryIds.indexOf(data.id()) === -1) {
+                data.hidden = false;
+            } else {
+                data.hidden = true;
+            }
+            this.shelvingLocations.push(data);
+        }, (error) => {
+            console.debug(error);
+        }, () => {
+            this.shelvingLocations.sort(function(a, b) {
+                return a.name < b.name ? -1 : 1;
+            });
+            const sortedShelvingLocations = [];
+            // order our array primarily by location
+            orgList.forEach(member => {
+                const currentLocationArray = this.shelvingLocations.filter((loc) => {
+                    return (member === loc.owning_lib());
+                });
+                Array.prototype.push.apply(sortedShelvingLocations, currentLocationArray);
+            });
+            this.shelvingLocations = sortedShelvingLocations;
+        });
+    }
+
+    addEntries = () => {
+        const checkedEntries = this.shelvingLocations.filter((entry) => {
+            return entry.checked;
+        });
+        checkedEntries.forEach((entry) => {
+            const newGroupEntry = this.idl.create('acplgm');
+            newGroupEntry.location(entry);
+            newGroupEntry.lgroup(this.selectedLocationGroup.id());
+            this.pcrud.create(newGroupEntry).subscribe(
+                newEntry => {
+                    // hide item so it won't show on on list of shelving locations
+                    entry.hidden = true;
+                    entry.checked = false;
+                    newEntry.checked = false;
+                    newEntry.location(entry);
+                    newEntry.name = entry.name;
+                    newEntry.shortname = entry.shortname;
+                    this.groupEntries.push(newEntry);
+                    this.addedGroupEntriesSuccess.current().then(msg =>
+                        this.toast.success(msg));
+                },
+                err => {
+                    console.debug(err);
+                    this.addedGroupEntriesFailure.current().then(msg => this.toast.warning(msg));
+                }
+            );
+        });
+    }
+
+    removeEntries = () => {
+        const checkedEntries = this.groupEntries.filter((entry) => {
+            return entry.checked;
+        });
+        this.pcrud.remove(checkedEntries).subscribe(
+            idRemoved => {
+                idRemoved = parseInt(idRemoved, 10);
+                let deletedName;
+                let deletedShortName;
+                // on pcrud success, remove from local group entries array
+                this.groupEntries = this.groupEntries.filter((entry) => {
+                    if (entry.id() === idRemoved) {
+                        deletedName = entry.name;
+                        deletedShortName = entry.shortname;
+                    }
+                    return (entry.id() !== idRemoved);
+                });
+                // show the entry on list of shelving locations
+                this.shelvingLocations.forEach((location) => {
+                    if ((location.name === deletedName) && (location.shortname ===
+                        deletedShortName)) {
+                        location.hidden = false;
+                    }
+                });
+                this.removedGroupEntriesSuccess.current().then(msg =>
+                    this.toast.success(msg));
+            }, (error) => {
+                console.debug(error);
+                this.removedGroupEntriesFailure.current().then(msg =>
+                    this.toast.warning(msg));
+            }
+        );
+    }
+
+    orgOnChange = (org: IdlObject): void => {
+        this.selectedOrg = org;
+        this.selectedOrgId = org.id();
+        this.loadLocationGroups();
+        this.checkCurrentPermissions();
+    }
+
+    onDragStart = (event, locationGroup) => {
+        this.draggedElement = locationGroup;
+    }
+
+    onDragOver = (event) => {
+        event.preventDefault();
+    }
+
+    onDragEnter = (event, locationGroup) => {
+        this.dragTarget = locationGroup;
+        // remove border where we previously were dragging
+        if (event.relatedTarget) {
+            event.relatedTarget.parentElement.style.borderTop = 'none';
+        }
+        // add border above target location group
+        if (event.target.parentElement !== null) {
+            if (event.target.parentElement.classList.contains('locationGroup')) {
+                event.target.parentElement.style.borderTop = '1px solid black';
+            }
+        }
+    }
+
+    onDragDrop = (event, index) => {
+        // do nothing if element is dragged onto itself
+        if (this.draggedElement !== this.dragTarget) {
+            this.assignNewPositions(index);
+        }
+        event.target.parentElement.style.borderTop = 'none';
+        this.draggedElement = null;
+        this.dragTarget = null;
+    }
+
+    assignNewPositions (index) {
+        const endingPos = this.dragTarget.posit;
+        const locationGroupsToUpdate = [];
+        this.draggedElement.pos(endingPos);
+        this.draggedElement.posit = endingPos;
+        locationGroupsToUpdate.push(this.draggedElement);
+        // add 1 to the position of all groups after the one we inserted
+        for (let i = index; i < this.locationGroups.length; i++) {
+            // we already processed the item being dragged; skip it
+            if (this.locationGroups[i] === this.draggedElement) { continue; }
+            const newPosition = this.locationGroups[i].posit + 1;
+            this.locationGroups[i].pos(newPosition);
+            this.locationGroups[i].posit = newPosition;
+            locationGroupsToUpdate.push(this.locationGroups[i]);
+        }
+        this.saveNewPositions(locationGroupsToUpdate);
+    }
+
+    saveNewPositions (locationGroupsToUpdate) {
+        let errorHappened = false;
+        this.pcrud.update(locationGroupsToUpdate).subscribe(
+            ok => {
+                console.debug('Record editor performed action');
+            },
+            err => {
+                console.debug(err);
+                errorHappened = true;
+            },
+            () => {
+                this.sortLocationGroups();
+                if (errorHappened) {
+                    this.changeOrderFailure.current().then(msg =>
+                        this.toast.warning(msg));
+                } else {
+                    this.changeOrderSuccess.current().then(msg =>
+                        this.toast.success(msg));
+                }
+            }
+        );
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.module.ts
new file mode 100644 (file)
index 0000000..f92c00e
--- /dev/null
@@ -0,0 +1,18 @@
+import {NgModule} from '@angular/core';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {ShelvingLocationGroupsComponent} from './shelving_location_groups.component';
+import {ShelvingLocationGroupsRoutingModule} from './shelving_location_groups_routing.module';
+
+@NgModule({
+  declarations: [
+    ShelvingLocationGroupsComponent,
+  ],
+  imports: [
+    AdminCommonModule,
+    ShelvingLocationGroupsRoutingModule,
+  ],
+  exports: [],
+  providers: []
+})
+
+export class ShelvingLocationGroupsModule {}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups_routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups_routing.module.ts
new file mode 100644 (file)
index 0000000..b1db98c
--- /dev/null
@@ -0,0 +1,15 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {ShelvingLocationGroupsComponent} from './shelving_location_groups.component';
+
+const routes: Routes = [{
+    path: '',
+    component: ShelvingLocationGroupsComponent
+}];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class ShelvingLocationGroupsRoutingModule {}