--- /dev/null
+<ng-template #addSuccessStrTmpl i18n>Added tag</ng-template>
+<eg-string #addSuccessString [template]="removeSuccessStrTmpl"></eg-string>
+<ng-template #addErrorStrTmpl i18n>Failed to add tag</ng-template>
+<eg-string #addErrorString [template]="removeErrorStrTmpl"></eg-string>
+<ng-template #removeSuccessStrTmpl i18n>Removed tag</ng-template>
+<eg-string #removeSuccessString [template]="removeSuccessStrTmpl"></eg-string>
+<ng-template #removeErrorStrTmpl i18n>Failed to remove tag</ng-template>
+<eg-string #removeErrorString [template]="removeErrorStrTmpl"></eg-string>
+
+<div class="row">
+ <div class="col-sm-2" *ngFor="let ftm of tagMaps">
+ <button class="btn btn-sm material-icon-button" type="button"
+ (click)="removeTagMap(ftm)"
+ i18n-title title="Remove Tag"><span class="sr-only">Remove Tag</span>
+ <span class="material-icons" aria-hidden="true">delete</span>
+ </button>
+ {{ftm.tag().name()}} ({{ftm.tag().owner().shortname()}})
+ </div>
+</div>
+<div class="row mt-3">
+ <div class="col-sm-2">
+ <eg-combobox #tagSelector [asyncSupportsEmptyTermClick]="true"
+ [(ngModel)]="newTag" [asyncDataSource]="tagSelectorDataSource"
+ i18n-placeholder placeholder="Select tag"></eg-combobox>
+ </div>
+ <div class="col-sm-1">
+ <button class="btn btn-success" [disabled]="!newTag || checkNewTagAlreadyMapped()"
+ (click)="addTagMap()" i18n>Add Tag
+ </button>
+ </div>
+ <div class="col-sm-2" *ngIf="newTag && checkNewTagAlreadyMapped()">
+ <span class="alert-warning" i18n>(tag is already assigned to this fund)</span>
+ </div>
+</div>
--- /dev/null
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {ComboboxComponent} from '@eg/share/combobox/combobox.component';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {Observable} from 'rxjs';
+import {map} from 'rxjs/operators';
+
+@Component({
+ selector: 'eg-fund-tags',
+ templateUrl: './fund-tags.component.html'
+})
+export class FundTagsComponent implements OnInit {
+
+ @Input() fundId: number;
+ @Input() fundOwner: number;
+
+ @ViewChild('addSuccessString', { static: true }) addSuccessString: StringComponent;
+ @ViewChild('addErrorString', { static: true }) addErrorString: StringComponent;
+ @ViewChild('removeSuccessString', { static: true }) removeSuccessString: StringComponent;
+ @ViewChild('removeErrorString', { static: true }) removeErrorString: StringComponent;
+ @ViewChild('tagSelector', { static: false }) tagSelector: ComboboxComponent;
+
+ tagMaps: IdlObject[];
+ newTag: ComboboxEntry = null;
+ tagSelectorDataSource: (term: string) => Observable<ComboboxEntry>
+
+ constructor(
+ private idl: IdlService,
+ private evt: EventService,
+ private net: NetService,
+ private auth: AuthService,
+ private pcrud: PcrudService,
+ private org: OrgService,
+ private toast: ToastService
+ ) {}
+
+ ngOnInit() {
+ this._loadTagMaps();
+ this.tagSelectorDataSource = term => {
+ const field = 'name';
+ const args = {};
+ const extra_args = { order_by : {} };
+ args[field] = {'ilike': `%${term}%`}; // could -or search on label
+ args['owner'] = this.org.ancestors(this.fundOwner, true)
+ extra_args['order_by']['acqft'] = field;
+ extra_args['limit'] = 100;
+ extra_args['flesh'] = 2;
+ const flesh_fields: Object = {};
+ flesh_fields['acqft'] = ['owner'];
+ extra_args['flesh_fields'] = flesh_fields;
+ return this.pcrud.search('acqft', args, extra_args).pipe(map(data => {
+ return {
+ id: data.id(),
+ label: data.name() + ' (' + data.owner().shortname() + ')',
+ fm: data
+ };
+ }));
+ };
+ }
+
+ _loadTagMaps() {
+ this.tagMaps = [];
+ this.pcrud.search('acqftm', { fund: this.fundId }, {
+ flesh: 2,
+ flesh_fields: {
+ acqftm: ['tag'],
+ acqft: ['owner']
+ }
+ }).subscribe(
+ res => this.tagMaps.push(res),
+ err => {},
+ () => this.tagMaps.sort((a, b) => {
+ return a.tag().name() < b.tag().name() ? -1 : 1
+ })
+ );
+ }
+
+ checkNewTagAlreadyMapped(): boolean {
+ if ( this.newTag == null) { return false; }
+ const matches: IdlObject[] = this.tagMaps.filter(tm => tm.tag().id() === this.newTag.id);
+ return matches.length > 0 ? true : false;
+ }
+
+ addTagMap() {
+ const ftm = this.idl.create('acqftm');
+ ftm.tag(this.newTag.id);
+ ftm.fund(this.fundId);
+ this.pcrud.create(ftm).subscribe(
+ ok => {
+ this.addSuccessString.current()
+ .then(str => this.toast.success(str));
+ },
+ err => {
+ this.addErrorString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ this.newTag = null;
+ this.tagSelector.selectedId = null;
+ this._loadTagMaps();
+ }
+ );
+ }
+ removeTagMap(ftm: IdlObject) {
+ this.pcrud.remove(ftm).subscribe(
+ ok => {
+ this.removeSuccessString.current()
+ .then(str => this.toast.success(str));
+ },
+ err => {
+ this.removeErrorString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => this._loadTagMaps()
+ )
+ }
+}