From 4e9a12bcb08f58a85a803160590da9012bd89d5a Mon Sep 17 00:00:00 2001 From: Zavier Banks Date: Fri, 11 Oct 2019 21:17:40 +0000 Subject: [PATCH] Lp#1846552: Port Local Admin Shelving Location Order Editor to Angular I ported the admin shelving location from dojo, into Angular, with all the same bells and whistles. Using the Angular framework, the user can drag and drop through a list of different org units, and save said list to a database. Signed-off-by: Zavier Banks --- .../app/staff/admin/local/admin-local.module.ts | 8 +- .../admin/local/copy-location-order.component.html | 23 +++ .../admin/local/copy-location-order.component.ts | 209 +++++++++++++++++++++ .../src/app/staff/admin/local/routing.module.ts | 8 +- .../staff/admin/local/share/card.component.html | 14 ++ .../app/staff/admin/local/share/card.component.ts | 50 +++++ .../admin/local/share/table-list.component.html | 17 ++ .../admin/local/share/table-list.component.ts | 174 +++++++++++++++++ 8 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.ts diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local.module.ts index 9f70ab78d6..8beccea873 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local.module.ts @@ -7,13 +7,19 @@ import {AdminLocalSplashComponent} from './admin-local-splash.component'; import {AddressAlertComponent} from './address-alert.component'; import {AdminCarouselComponent} from './admin-carousel.component'; import {StandingPenaltyComponent} from './standing-penalty.component'; +import {CopyLocationOrderComponent} from './copy-location-order.component'; +import {TableListComponent} from './share/table-list.component'; +import {CardComponent} from './share/card.component'; @NgModule({ declarations: [ AdminLocalSplashComponent, AddressAlertComponent, AdminCarouselComponent, - StandingPenaltyComponent + StandingPenaltyComponent, + CopyLocationOrderComponent, + TableListComponent, + CardComponent ], imports: [ AdminCommonModule, diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.html new file mode 100644 index 0000000000..47631cf97e --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.html @@ -0,0 +1,23 @@ +
+ + +
+

Context Org Unit

+
+ +
+
+

To move an item, drag it up or down with the mouse.

+ + + +
{{selected_node.name}} ({{selected_node.shortname}})
+
\ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.ts new file mode 100644 index 0000000000..756600e9b8 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/copy-location-order.component.ts @@ -0,0 +1,209 @@ +import {Component, OnInit, HostListener} from '@angular/core'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {AuthService} from '@eg/core/auth.service'; +import {ToastService} from '@eg/share/toast/toast.service'; +import { IdlService } from '@eg/core/idl.service'; + + + +class Category { + name:string; + owning_lib:string; + shortname:string; + unhashed_element:any; +} + +@Component({ + templateUrl: './copy-location-order.component.html' +}) + +export class CopyLocationOrderComponent implements OnInit { + orders:any; + locations:any; + id_log=[]; + lib_tree=[]; + active_libraries=[]; + mouse_x_position:number; + mouse_y_position:number; + detect_mouse_down=false; + detect_mouse = false; + selected_node:any; + + position_styling={ + backgroundColor:"white", + boxShadow:"5px 4px 10px 2px #5B5A5A", + textAlign:"center", + padding:"3px", + width:'80px', + position:'fixed', + } + + constructor( + private pcrud: PcrudService, + private idl: IdlService, + private toast: ToastService, + private auth: AuthService, + ) { + } + + ngOnInit() { + //this.createTree(); + //this.filterGrid(this.auth.user().ws_ou()); + }; + + detectMouseLeave(){ + this.detect_mouse=false; + } + + toggleDragAndDrop(toggle){ + + this.detect_mouse=toggle; + } + + disableMouseUp(){ + this.detect_mouse_down=false; + } + onMouseDown(is_mouse_down){ + this.detect_mouse_down=is_mouse_down; + } + + + displaySelectedNode(node){ + this.selected_node=node; + } + + /** + * Initializes the search for the library tree and the array of the library locations. + * @param org The authorized user passed in during initialization. + */ + filterGrid(org) { + // fetch the locations and order entries + if(!this.orders) { + this.pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}}) + .subscribe(elements => console.log("Order",elements)); + this.mappingLocation(); + } + } + + + /** + * Maps the values of the specified category to an array of objects, whose keys hold the necessary values. + */ + mappingLocation(){ + this.locations=[]; + this.pcrud.search('acpl', + {owning_lib:this.id_log, deleted : 'f'} + ).subscribe(element => { + var categ = new Category(); + var unhashed_element=element; + var hashed_element = this.idl.toHash(element, false); + var found_library = this.findLibrary(hashed_element.owning_lib) + categ.name=hashed_element.name; + categ.owning_lib= found_library.name; + categ.shortname=found_library.shortname; + categ.unhashed_element=unhashed_element; + this.locations.push(categ); + }); + this.id_log=[]; + } + + /** + * Finds the Library name, using the library id. + * @param lib_id The id that is to be used to find the name. + */ + findLibrary(lib_id){ + var lib_name; + this.active_libraries.forEach(library=>{ + if(library.id==lib_id){ + lib_name= { + name:library.name, + shortname:library.shortname + }; + } + }) + return lib_name; + } + + /** + * Starts from parent, logs id, looks for a specific key, checks to see if it has any children. If so + * rinse repeat. + * @param parent_node The uppermost parent node. + */ + nodeTrailing(parent_node){ + this.findParent(parent_node); + } + + /** + * Creates a tree of the different library branches + * @param node The parent node, of which, the branches 'branch out' + */ + createBranch(node){ + this.lib_tree.push(node); + node.children.forEach(child => { + this.createBranch(child); + }); + } + + /** + * Checks if the node passed in has a parent node. If so, follow that branch, while saving the child nodes, + * until the root node is found. + * @param node The node that is to be checked for a parent. + */ + findParent(node){ + this.id_log.push(node.id); + this.active_libraries.push(node); + var filteredTree= this.lib_tree.filter(branch=> branch.ou_type.depth<=(node.ou_type.depth-1)); //Creates a filtered branch + filteredTree.forEach(branch =>{ + if + ( + ( branch.id==node.parent_ou) + && this.isChild(branch.children, node) + ) + { + this.findParent(branch); + } + }) + } + + /** + * Using the given parameters, checks if the 'node' passed is the parent of the 'children' parameter passed in. + * @param children The array of children nodes that is to be checked. + * @param node The assumed parent node. + */ + isChild(children, node){ + var names_are_equal = false; + children.forEach(child => { + if(child.shortname==node.shortname){ + names_are_equal= true; + } + }) + return names_are_equal; + } + + onChange(object){ + if(this.lib_tree.length==0){ + this.createBranch(this.idl.toHash(object, false)); + } + this.nodeTrailing(this.idl.toHash(object, false)) + this.mappingLocation(); + } + + applyChanges(new_locations){ + var library_locations=[]; + new_locations.forEach(element => { + library_locations.push(element.unhashed_element) + }); + var library =this.pcrud.update(library_locations); + + console.log(library.subscribe(object=> console.log(object))) + } + + @HostListener('mousemove', ['$event']) + mouseMove($event: MouseEvent) { + this.mouse_y_position=$event.clientY; + this.mouse_x_position=$event.clientX; + } + + +} + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts index 39c6be7179..865fd7e5d0 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts @@ -5,6 +5,7 @@ import {BasicAdminPageComponent} from '@eg/staff/admin/basic-admin-page.componen import {AddressAlertComponent} from './address-alert.component'; import {AdminCarouselComponent} from './admin-carousel.component'; import {StandingPenaltyComponent} from './standing-penalty.component'; +import {CopyLocationOrderComponent} from './copy-location-order.component'; const routes: Routes = [{ path: 'splash', @@ -23,9 +24,14 @@ const routes: Routes = [{ path: 'config/standing_penalty', component: StandingPenaltyComponent }, { + path: 'asset/copy_location_order', + component: CopyLocationOrderComponent +}, { path: ':schema/:table', component: BasicAdminPageComponent -}]; +} + +]; @NgModule({ imports: [RouterModule.forChild(routes)], diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.html new file mode 100644 index 0000000000..33eb983e94 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.html @@ -0,0 +1,14 @@ + + +
+ keyboard_arrow_right + +
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.ts new file mode 100644 index 0000000000..7f3310cab3 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/share/card.component.ts @@ -0,0 +1,50 @@ + +import {Component, OnInit, Input,Output, EventEmitter, ViewChild} from '@angular/core'; + +@Component({ + selector:'card', + templateUrl: './card.component.html' +}) + +export class CardComponent implements OnInit { + @Input() content:any; + @Input() index:number; + @Input() content_length:number; + @Output() detectMouseUpEvents= new EventEmitter(); + @Output() detectMouseDownEvents= new EventEmitter(); + detect_drag=false; + @Input() is_mouse_down:boolean; + + card_style={ + display:"flex", + flexDirection:"column" + } + + divider_style={ + marginTop:"4px", + width:"2px" + } + + constructor() { + } + + + ngOnInit() {}; + + onMouseEnter(){ + this.detect_drag=true; + } + onMouseLeave(){ + this.detect_drag=false; + } + + onMouseUpEvent(){ + this.is_mouse_down=false; + this.detectMouseUpEvents.emit(this.index) + } + + onMouseDownEvent(){ + this.is_mouse_down=true; + this.detectMouseDownEvents.emit(this.index); + } +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.html new file mode 100644 index 0000000000..440bae97b4 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.html @@ -0,0 +1,17 @@ + + +
+
+ +
+ {{content.name}} ({{content.shortname}}) +
+
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.ts new file mode 100644 index 0000000000..de92637602 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/share/table-list.component.ts @@ -0,0 +1,174 @@ + +import {Component, OnInit, Input,Output,EventEmitter} from '@angular/core'; + +/** + * Creates a class for the pairs. + */ +class Pair { + 1:number; + 2:number; +} + +@Component({ + selector:'table-list', + templateUrl: './table-list.component.html' +}) + +export class TableListComponent implements OnInit { + @Input() content_list:any; + @Output() saveContent= new EventEmitter(); + @Output() detectMousePosition = new EventEmitter(); + @Output() nodeIsSelected = new EventEmitter(); + @Output() toggleDragAndDrop = new EventEmitter(); + @Input() is_mouse_down=false; + detect_mouse_leave=false; + mutable_content_list:any; + mutable_content_dictionary={}; + card_pairs:Pair; + + /** + * The Following are style variables to style to tabs and cards in the html file. + */ + + table_container={ + display:"flex", + flexDirection:"row", + justifyContent:"flex-start" + } + + card_container={ + display:"flex", + flexDirection:"column", + justifyContent:"center", + width:"300px", + marginRight:"10px" + } + + button_style={ + margin:"auto" + } + + constructor() { + }; + + ngOnInit() { + this.card_pairs= new Pair(); + this.convertListToDictionary(this.content_list); + this.mutable_content_list= this.content_list.slice(0, this.content_list.length);//Retains immutability when saving string to another variable. + + }; + + ngAfterViewInit(){ + } + applyChanges(){ + this.saveContent.emit(this.mutable_content_list); + } + + onMouseLeave(){ + //this.is_mouse_down=false; + this.toggleDragAndDrop.emit(false); + + } + onMouseEnter(){ + this.toggleDragAndDrop.emit(true); + } + + onMouseDown(index){ + this.detect_mouse_leave=true; + this.is_mouse_down=true; + this.card_pairs[1]=index; + this.detectMousePosition.emit(true); + this.toggleDragAndDrop.emit(true); + this.nodeIsSelected.emit(this.mutable_content_list[index]); + } + onMouseUp(index){ + this.toggleDragAndDrop.emit(false); + this.detectMousePosition.emit(false); + if(this.card_pairs[1]!=undefined && this.card_pairs[1]!=null){ //Checks if the tab pair is a number + this.card_pairs[2]=index; + this.is_mouse_down=false; + if((this.card_pairs[1]!= this.card_pairs[2]) && this.card_pairs[2]!=undefined && this.card_pairs[2]!=null){ //Checks if the tab pair is a number + this.reorderContentListFromCards(this.card_pairs, this.mutable_content_list); + } + }else{ + this.is_mouse_down=false; + } + } + + /** + * Converts a list into a dictionary to decrease the time complexity of multiple rearrangements. + * @param list The list that is to be converted + */ + convertListToDictionary(list){ + var dictionary ={} + list.forEach(function (element, index) { + dictionary[index]=element; + }); + this.mutable_content_dictionary=dictionary; + } + + + convertDictionaryToList(dictionary){ + var new_list=[]; + for(var x =0; xpaired_values[1]){ + + for(var y=0; y paired_values[2]; x--){ + new_dictionary[x]=dictionary[x]; + } + }else{ + + for(var y=0; y paired_values[1]; x--){ + new_dictionary[x]=dictionary[x]; + } + } + return Object.assign({...new_dictionary}); + } +} + -- 2.11.0