]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
lp1857911 follow-up tweaks
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / org-family-select / org-family-select.component.ts
1 import {Component, EventEmitter, OnInit, Input, Output, ViewChildren, QueryList, 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 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
7 import {ServerStoreService} from '@eg/core/server-store.service';
8
9 export interface OrgFamily {
10   primaryOrgId: number;
11   includeAncestors?: boolean;
12   includeDescendants?: boolean;
13   orgIds?: number[];
14 }
15
16 @Component({
17     selector: 'eg-org-family-select',
18     templateUrl: 'org-family-select.component.html',
19     providers: [
20     {
21       provide: NG_VALUE_ACCESSOR,
22       useExisting: forwardRef(() => OrgFamilySelectComponent),
23       multi: true
24     }
25   ]
26 })
27 export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
28
29     // The label for this input
30     @Input() labelText = 'Library';
31
32     // Should the Ancestors checkbox be hidden?
33     @Input() hideAncestorSelector = false;
34
35     // Should the Descendants checkbox be hidden?
36     @Input() hideDescendantSelector = false;
37
38     // Should the Ancestors checkbox be checked by default?
39     //
40     // Ignored if [hideAncestorSelector]="true"
41     @Input() ancestorSelectorChecked = false;
42
43     // Should the Descendants checkbox be checked by default?
44     //
45     // Ignored if [hideDescendantSelector]="true"
46     @Input() descendantSelectorChecked = false;
47
48     // Default org unit
49     @Input() selectedOrgId: number;
50
51     // Only show the OUs that the user has certain permissions at
52     @Input() limitPerms: string[];
53
54     @Input() domId: string;
55
56     @Input() persistKey: string;
57
58     @Output() onChange = new EventEmitter<any>();
59
60     @ViewChildren(OrgSelectComponent)  orgSelects: QueryList<OrgSelectComponent>;
61
62     // this is the most up-to-date value used for ngModel and reactive form
63     // subscriptions
64     options: OrgFamily;
65
66     orgOnChange: ($event: IdlObject) => void;
67     emitArray: () => void;
68
69     familySelectors: FormGroup;
70
71     propagateChange = (_: OrgFamily) => {};
72     propagateTouch = () => {};
73
74     constructor(
75         private auth: AuthService,
76         private org: OrgService,
77         private serverStore: ServerStoreService
78     ) {
79     }
80
81     ngOnInit() {
82         if (this.selectedOrgId) {
83             this.options = {primaryOrgId: this.selectedOrgId};
84         } else if (this.auth.user()) {
85             this.options = {primaryOrgId: this.auth.user().ws_ou()};
86         }
87
88         this.familySelectors = new FormGroup({
89             'includeAncestors': new FormControl({
90                 value: this.ancestorSelectorChecked,
91                 disabled: this.disableAncestorSelector()}),
92             'includeDescendants': new FormControl({
93                 value: this.descendantSelectorChecked,
94                 disabled: this.disableDescendantSelector()}),
95         });
96
97         if (!this.domId) {
98             this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
99         }
100
101         this.familySelectors.valueChanges.subscribe(val => {
102             this.emitArray();
103         });
104
105         this.orgOnChange = ($event: IdlObject) => {
106             this.options.primaryOrgId = $event.id();
107             this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
108             this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
109             this.emitArray();
110         };
111
112         this.emitArray = () => {
113             // Prepare and emit an array containing the primary org id and
114             // optionally ancestor and descendant org units, and flags that select those.
115
116             this.options.orgIds = [this.options.primaryOrgId];
117             this.options.includeAncestors = this.includeAncestors.value;
118             this.options.includeDescendants = this.includeDescendants.value;
119
120             if (this.includeAncestors.value) {
121                 this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
122             }
123
124             if (this.includeDescendants.value) {
125                 this.options.orgIds = this.options.orgIds.concat(
126                     this.org.descendants(this.options.primaryOrgId, true));
127             }
128
129             // Using ancestors() and descendants() can result in
130             // duplicate org ID's.  Be nice and uniqify.
131             const hash: any = {};
132             this.options.orgIds.forEach(id => hash[id] = true);
133             this.options.orgIds = Object.keys(hash).map(id => Number(id));
134
135             this.propagateChange(this.options);
136             this.onChange.emit(this.options);
137
138             if (this.persistKey) {
139                 const key = `eg.orgfamilyselect.${this.persistKey}`;
140                 this.serverStore.setItem(key, this.options);
141             }
142         };
143
144         this.loadPersistedValues();
145     }
146
147     private loadPersistedValues() {
148         if (!this.persistKey) return;
149
150         const key = `eg.orgfamilyselect.${this.persistKey}`;
151
152         this.serverStore.getItem(key).then(persistedOptions => {
153             if (persistedOptions) {
154                 this.writeValue(persistedOptions);
155             }
156         });
157     }
158
159     writeValue(value: OrgFamily) {
160         if (value) {
161             this.selectedOrgId = value['primaryOrgId'];
162             if (this.orgSelects) {
163                 this.orgSelects.toArray()[0].applyOrgId = this.selectedOrgId;
164                 this.options = {primaryOrgId: this.selectedOrgId};
165             }
166             this.familySelectors.patchValue({
167                 'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
168                 'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
169             });
170         }
171     }
172
173     registerOnChange(fn) {
174         this.propagateChange = fn;
175     }
176
177     registerOnTouched(fn) {
178         this.propagateTouch = fn;
179     }
180
181     disableAncestorSelector(): boolean {
182         return this.options.primaryOrgId === this.org.root().id();
183     }
184
185     disableDescendantSelector(): boolean {
186         const contextOrg = this.org.get(this.options.primaryOrgId);
187         return contextOrg.children().length === 0;
188     }
189
190     get includeAncestors() {
191         return this.familySelectors.get('includeAncestors');
192     }
193     get includeDescendants() {
194         return this.familySelectors.get('includeDescendants');
195     }
196
197 }
198