LP1830432: Make the org-family-select reusable
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / share / org-family-select / org-family-select.component.ts
1 import {Component, EventEmitter, OnInit, Input, Output, ViewChild, forwardRef} from '@angular/core';
2 import {ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
3 import {AuthService} from '@eg/core/auth.service';
4 import {IdlObject} from '@eg/core/idl.service';
5 import {OrgService} from '@eg/core/org.service';
6
7 export interface OrgFamily {
8   primaryOrgId: number;
9   includeAncestors?: boolean;
10   includeDescendants?: boolean;
11   orgIds?: number[];
12 }
13
14 @Component({
15     selector: 'eg-org-family-select',
16     templateUrl: 'org-family-select.component.html',
17     providers: [
18     {
19       provide: NG_VALUE_ACCESSOR,
20       useExisting: forwardRef(() => OrgFamilySelectComponent),
21       multi: true
22     }
23   ]
24 })
25 export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
26
27     // The label for this input
28     @Input() labelText = 'Library';
29
30     // Should the Ancestors checkbox be hidden?
31     @Input() hideAncestorSelector = false;
32
33     // Should the Descendants checkbox be hidden?
34     @Input() hideDescendantSelector = false;
35
36     // Should the Ancestors checkbox be checked by default?
37     //
38     // Ignored if [hideAncestorSelector]="true"
39     @Input() ancestorSelectorChecked = false;
40
41     // Should the Descendants checkbox be checked by default?
42     //
43     // Ignored if [hideDescendantSelector]="true"
44     @Input() descendantSelectorChecked = false;
45
46     // Default org unit
47     @Input() selectedOrgId: number;
48
49     // Only show the OUs that the user has certain permissions at
50     @Input() limitPerms: string[];
51
52     @Input() domId: string;
53
54     // this is the most up-to-date value used for ngModel and reactive form
55     // subscriptions
56     options: OrgFamily;
57
58     orgOnChange: ($event: IdlObject) => void;
59     emitArray: () => void;
60
61     familySelectors: FormGroup;
62
63     propagateChange = (_: OrgFamily) => {};
64
65     constructor(
66         private auth: AuthService,
67         private org: OrgService
68     ) {
69     }
70
71     ngOnInit() {
72         if (this.selectedOrgId) {
73             this.options = {primaryOrgId: this.selectedOrgId};
74         } else if (this.auth.user()) {
75             this.options = {primaryOrgId: this.auth.user().ws_ou()};
76         }
77
78         this.familySelectors = new FormGroup({
79             'includeAncestors': new FormControl({
80                 value: this.ancestorSelectorChecked,
81                 disabled: this.disableAncestorSelector()}),
82             'includeDescendants': new FormControl({
83                 value: this.descendantSelectorChecked,
84                 disabled: this.disableDescendantSelector()}),
85         });
86
87         if (!this.domId) {
88             this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
89         }
90
91         this.familySelectors.valueChanges.subscribe(val => {
92             this.emitArray();
93         });
94
95         this.orgOnChange = ($event: IdlObject) => {
96             this.options.primaryOrgId = $event.id();
97             this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
98             this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
99             this.emitArray();
100         };
101
102         this.emitArray = () => {
103             // Prepare and emit an array containing the primary org id and
104             // optionally ancestor and descendant org units.
105
106             this.options.orgIds = [this.options.primaryOrgId];
107
108             if (this.includeAncestors.value) {
109                 this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
110             }
111
112             if (this.includeDescendants.value) {
113                 // can result in duplicate workstation org IDs... meh
114                 this.options.orgIds = this.options.orgIds.concat(
115                     this.org.descendants(this.options.primaryOrgId, true));
116             }
117
118             this.propagateChange(this.options);
119         };
120
121     }
122
123     writeValue(value: OrgFamily) {
124         if (value) {
125             this.selectedOrgId = value['primaryOrgId'];
126             this.familySelectors.patchValue({
127                 'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
128                 'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
129             });
130         }
131     }
132
133     registerOnChange(fn) {
134         this.propagateChange = fn;
135     }
136
137     registerOnTouched() {}
138
139     disableAncestorSelector(): boolean {
140         return this.options.primaryOrgId === this.org.root().id();
141     }
142
143     disableDescendantSelector(): boolean {
144         const contextOrg = this.org.get(this.options.primaryOrgId);
145         return contextOrg.children().length === 0;
146     }
147
148     get includeAncestors() {
149         return this.familySelectors.get('includeAncestors');
150     }
151     get includeDescendants() {
152         return this.familySelectors.get('includeDescendants');
153     }
154
155 }
156