import { Component, Input, Output, EventEmitter, Inject, AfterViewInit } from '@angular/core'; import { downgradeComponent } from '@angular/upgrade/static'; import { TranslateService } from '@ngx-translate/core'; import { NiceDatePipe } from '@fb/common/pipes/niceDate.pipe'; import * as _ from 'underscore'; import * as moment from 'moment'; declare const angular: angular.IAngularStatic; const styles: any[] = [require('./nytt-bud.component.less')]; @Component({ selector: 'fb-nytt-bud', templateUrl: './nytt-bud.component.html', styles: styles }) export class NyttBudComponent implements AfterViewInit { @Input() visningar: fb.List; @Input() serverfel: boolean; @Input() taBortHelaVisningen: (visningsSlot: fb.ISlot, intressent: fb.Objektintressent) => boolean; @Input() intressent: fb.Objektintressent; @Input() intressenter: fb.List; @Input() isUnderkontakt: boolean; @Input() personIntressegrad: fb.Intressegrad; @Input() maeklarObjekt: fb.MaeklarObjektDO; @Input() nyObjektIntressent: fb.NyObjektIntressent; @Input() digitalBudgivning: { Aktiv: fb.ChangeTrack; IsDisabled(): boolean; Tooltip(): string; }; @Output() dismissServerFel: EventEmitter = new EventEmitter(); @Output() onIntressegradChange: EventEmitter = new EventEmitter(); @Output() onError: EventEmitter<{ data: any, origin: string }> = new EventEmitter<{ data: any, origin: string }>(); @Output() refreshVisningar: EventEmitter = new EventEmitter(); intressegrader: fb.List; digitalBudgivningAktiveradPaaKontoret: any; digitalBudgivningAlts: { value: boolean, name: string }[]; showPopupRemoveVisningsId: number; showAddVisningSection: boolean; showAddNewVisning: boolean; valdVisning: fb.Visning; valdSlot: fb.ISlot; newVisning: fb.Visning; newVisningAnsvarigPersonId: fb.ChangeTrack; pendingVisningSave: fb.Visning; private updateIntressentPromise: Promise; private defferedUpdateIntressentTimeout: any; constructor( @Inject('InstaellningarService') private readonly instaellningarService: fb.IInstaellningarService, @Inject('DomainService') private readonly domainService: fb.IDomainService, @Inject('CommonService') private readonly commonService: fb.ICommonService, @Inject('VisningService') private readonly visningService: fb.IVisningService, @Inject('IntressentService') private readonly intressentService: fb.IIntressentService, private readonly niceDatePipe: NiceDatePipe, private readonly translate: TranslateService ) { this.digitalBudgivningAlts = [ { name: this.translate.instant('SIDEDRAWERINTRESSENTBUD.AKTIVERAD'), value: true }, { name: this.translate.instant('SIDEDRAWERINTRESSENTBUD.EJ_AKTIVERAD'), value: false } ]; } ngAfterViewInit(): void { this.initiateNewVisning(); this.intressegrader = this.domainService.Intressegrad.query(this.maeklarObjekt.isProjekt()); this.instaellningarService .init() .then( () => this.digitalBudgivningAktiveradPaaKontoret = this.instaellningarService .instaellningar .instaellningar[fb.InstaellningTyp.DigitalBudgivningAktiverad.id] .InstaellningVaerde .value ); } onDismissServerFel(): void { this.dismissServerFel.emit(); } updateIntressegrad(intressengrad: fb.Intressegrad): void { this.onIntressegradChange.emit(intressengrad); } getEjBookadeVisningar(visningar: fb.IVisningSlot[]): fb.IVisningSlot[] { const ejBookadeVisningar: fb.IVisningSlot[] = []; if (visningar) { visningar.forEach(visning => { const harVisning: fb.IVisningSlot = _.find(this.intressent.Visningar, (slot: fb.IVisningSlot) => { return slot.VisningId.value === visning.AktivitetId.value; }); if (!harVisning) { ejBookadeVisningar.push(visning); } }); } return ejBookadeVisningar; } setValdVisning(visning: fb.Visning): void { this.valdVisning = visning; } setSlotInValdVisning(slot: fb.ISlot): void { this.valdSlot = slot; } getTextForSlot(slot: fb.ISlot): string { let slotText: string = ''; let emptySlots: number; if (!slot) { return slotText; } if (slot.AktivitetStart) { slotText = slotText + moment(slot.AktivitetStart).format('HH:mm'); } if (_.isNumber(slot.currentlyRegistered)) { emptySlots = slot.maxBookings - slot.currentlyRegistered; } else if (!slot.currentlyRegistered && slot.maxBookings) { emptySlots = slot.maxBookings; } if (emptySlots <= 0) { slotText = slotText + ' (' + this.translate.instant('SIDEDRAWERINTRESSENT.FULLBOKAD').toLowerCase() + ')'; } else if (_.isNumber(emptySlots)) { slotText = slotText + ' (' + this.translate.instant('SIDEDRAWERINTRESSENT.LEDIGA_TIDER').toLowerCase() + ' ' + emptySlots + this.translate.instant('GLOBALS.STYCKEN_INITIALS').toLowerCase() + ')'; } return slotText; } avbrytLaeggTillVisningFoerPerson(): void { this.valdVisning = null; this.valdSlot = null; this.showAddVisningSection = false; } getShortTextForSlot(slot: fb.ISlot): string { if (!slot) { return ''; } const start: Moment = moment(slot.AktivitetStart, 'YYYY-MM-DD[T]HH:mm:ss', true); const slut: Moment = moment(slot.AktivitetSlut, 'YYYY-MM-DD[T]HH:mm:ss', true); if (!start.isValid() || !slut.isValid()) { return ''; } return start.format('HH:mm') + ' - ' + slut.format('HH:mm'); } getVisningDateAndTime(slot: fb.ISlot): string { if (!slot) { return null; } const isVisningInstance: boolean = slot instanceof fb.Visning; // Unik design som progagerar i hela NyttBud.html // ändrar inte pga storlekens ingrepp. const slotAsVisning: fb.Visning = isVisningInstance ? slot as any as fb.Visning : null; let displayString: string = (isVisningInstance ? this.visningsNummer(slot as any as fb.Visning) : this.visningsNummerFromSlot(slot)) + '. '; const aktivitetStart: string = isVisningInstance ? slotAsVisning.AktivitetStart.value : slot.AktivitetStart; const aktivitetSlut: string = isVisningInstance ? slotAsVisning.AktivitetSlut.value : slot.AktivitetSlut; const hideTime: boolean = this.commonService.date.timePart(aktivitetStart) === ('00:00:00') && this.commonService.date.timePart(aktivitetSlut) === ('00:00:00'); displayString += this.niceDatePipe.transform(aktivitetStart, true, !hideTime); if (_.isString(aktivitetSlut) && !hideTime) { displayString += ' - ' + moment(this.commonService.date.timePart(aktivitetSlut), 'HH:mm:ss').format('HH:mm'); } return displayString; } deleteVisningFromIntressentAndRemoveVisning(visningsSlot: fb.ISlot): void { // Ta bort kopplingen mellan intressent och visning this.deleteVisningFromIntressentDontRemoveVisning( visningsSlot, (updateIntressentPromise: Promise) => this.deleteVisningCallback(visningsSlot, updateIntressentPromise) ); } deleteVisningFromIntressentDontRemoveVisning(visningsSlot: fb.ISlot, callback?: (updateIntressentPromise: Promise) => void): void { // Dölj ta bort visnings popover this.showPopupRemoveVisningsId = undefined; const indexOfSlot: number = this.nyObjektIntressent.Visningar.indexOf(visningsSlot); if (this.intressent) { // Ta bort från intressentlistan const vs: fb.IVisningSlot = _.find( this.intressent.Visningar, (slot: fb.IVisningSlot) => slot.VisningId.value === visningsSlot.VisningId ); this.intressent.Visningar.splice(this.intressent.Visningar.indexOf(vs), 1); } const actualVisning: fb.Visning = _.find( this.visningar, (v: fb.Visning) => v.AktivitetId.value === visningsSlot.VisningId ); // Ta bort uppbokad slot på visning om visning har slots //Om vi inte hittar visningar (de tar bort en visningsdeltagare från en borttagen visning) så behöver vi inte frigöra slottar if (actualVisning && _.isArray(actualVisning.Slots)) { const slotForVisning: fb.ISlot = _.find( actualVisning.Slots, (s: fb.ISlot) => s.VisningId === visningsSlot.VisningId ); if (!_.isUndefined(slotForVisning) && _.isNumber(slotForVisning.currentlyRegistered)) { slotForVisning.currentlyRegistered = Math.max(0, slotForVisning.currentlyRegistered - 1); } } // Ta bort från Nyttbud-panel this.nyObjektIntressent.Visningar.splice(indexOfSlot, 1); // Uppdatera intressent, se till att inga parallella uppdateringar sker samtidigt mot webapi clearTimeout(this.defferedUpdateIntressentTimeout); this.defferedUpdateIntressentTimeout = setTimeout(() => { const old: Promise = this.updateIntressentPromise; if (!old) { this.updateIntressentPromise = this.intressentService.save(this.nyObjektIntressent).$promise as any as Promise; } else { old.then( () => { if (old === this.updateIntressentPromise) { this.updateIntressentPromise = this.intressentService.save(this.nyObjektIntressent).$promise as any as Promise; } }) .catch((data) => { this.onError.emit({ data, origin: 'serverfel' }); }); } if (callback) { callback(this.updateIntressentPromise); } }, 200); } getSorteradVisningar(visningar: fb.ISlot[]): fb.ISlot[] { let list: fb.ISlot[] = []; if (visningar) { visningar.forEach(v => list[this.visningsNummerFromSlot(v) - 1] = v); list = list.filter(item => !!item); } return list; } deleteVisningFromIntressent(visningsSlot: fb.ISlot): void { if (this.nyObjektIntressent && _.isArray(this.nyObjektIntressent.Visningar)) { const indexOfSlot: number = this.nyObjektIntressent.Visningar.indexOf(visningsSlot); if (indexOfSlot >= 0) { // Visa popup för att ta bort hela visningen om inga fler intressenter finns kopplad till denna visning const showPopupRemoveVisning: boolean = this.taBortHelaVisningen(visningsSlot, this.intressent); // Om det finns fler intressenter som är kopplad till visningen tar vi bort kopplingen direkt // Om det är bara denna intressent som har en koppling till visningen visar vi popovern som ger möjlighet att // ta bort hela visningen och kopplingen mellan visning och intressent. if (showPopupRemoveVisning) { this.showPopupRemoveVisningsId = visningsSlot.VisningId; } else { this.deleteVisningFromIntressentDontRemoveVisning(visningsSlot); } } } } enableddVisningSection(): void { this.showAddVisningSection = true; } sortIntressentensVisningarByNr(visningar: fb.ISlot[]): fb.ISlot[] { visningar.sort((a: fb.ISlot, b: fb.ISlot) => { const visA: fb.Visning = _.find(this.visningar, (v: fb.Visning) => v.AktivitetId.value === a.VisningId); const visB: fb.Visning = _.find(this.visningar, (v: fb.Visning) => v.AktivitetId.value === b.VisningId); return (_.isUndefined(visA) && _.isUndefined(visB)) ? 0 : _.isUndefined(visA) ? -1 : _.isUndefined(visB) ? 1 : (this.visningar.indexOf(visA) - this.visningar.indexOf(visB)); }); return visningar; } laeggTillVisningFoerPerson(): void { let startTid: string; let slutTid: string; if (!this.valdVisning || !this.valdVisning.AktivitetId || !this.valdVisning.AktivitetId.value) { return null; } if (this.valdSlot) { startTid = this.valdSlot.AktivitetStart; slutTid = this.valdSlot.AktivitetSlut; } else { startTid = this.valdVisning.AktivitetStart.value; slutTid = this.valdVisning.AktivitetSlut.value; } const slotForRegistration: fb.ISlot = { VisningId: this.valdVisning.AktivitetId.value, AktivitetStart: startTid, AktivitetSlut: slutTid, AnsvarigPersonId: this.maeklarObjekt.isUtland ? this.newVisningAnsvarigPersonId.value : null }; this.showAddVisningSection = false; // Hide the new visning if (this.intressent) { if (!this.intressent.Visningar) { this.intressent.Visningar = []; } if (this.nyObjektIntressent) { // We use nyObjektintressent instead of intressent because this is the data format returned by a get on intressent if (!this.nyObjektIntressent.Visningar) { this.nyObjektIntressent.Visningar = []; } this.nyObjektIntressent.Visningar.push(slotForRegistration); this.nyObjektIntressent.$resolved = false; this.intressentService.save(this.nyObjektIntressent).$promise .then(() => { this.intressent.Visningar.push(this.commonService.changeTrack.createChangeTrackObject(slotForRegistration)); this.nyObjektIntressent.Visningar = this.sortIntressentensVisningarByNr(this.nyObjektIntressent.Visningar); this.nyObjektIntressent.$resolved = true; this.refreshVisningar.emit(); if (this.personIntressegrad.Intressegrad > 90) { //Sätt intressegrad till varit/kommer på visning om den är lägre än det //Detta sätts egentligen på serversidan också men vi måste uppdatera GUI. //Bokad på visning som inte ägt rum eller vi inte vet när den äger rum och tidigare intressegraden är högre if ( (slotForRegistration.AktivitetStart === null || moment(slotForRegistration.AktivitetStart) >= moment()) && (this.personIntressegrad.Intressegrad === null || this.personIntressegrad.Intressegrad > fb.FasITDomain.Intressegrad.BOKAD_PAA_VISNING) ) { this.personIntressegrad = _.findWhere(this.intressegrader, { Intressegrad: fb.FasITDomain.Intressegrad.BOKAD_PAA_VISNING }); } else if ( //Bokad på visning som har ägt rum och tidigare intressegraden är högre (slotForRegistration.AktivitetStart !== null && moment(slotForRegistration.AktivitetStart) < moment()) && (this.personIntressegrad.Intressegrad === null || this.personIntressegrad.Intressegrad > fb.FasITDomain.Intressegrad.VARIT_PAA_VISNING) ) { this.personIntressegrad = _.findWhere(this.intressegrader, { Intressegrad: fb.FasITDomain.Intressegrad.VARIT_PAA_VISNING }); } } }).catch(data => { this.onError.emit({ data, origin: 'serverfel' }); // Ta bort slot som misslyckades att sparas ned const failedSlotIndex: number = this.nyObjektIntressent.Visningar.lastIndexOf(slotForRegistration); this.nyObjektIntressent.Visningar.splice(failedSlotIndex, 1); this.nyObjektIntressent.$resolved = true; }); this.valdVisning = null; this.valdSlot = null; } } } addVisningToMaeklarObjekt(): void { let clonedVisning: fb.Visning; this.newVisning.AktivitetSlut.value = this.commonService.date.setDatePart( this.newVisning.AktivitetSlut.value, this.commonService.date.datePart(this.newVisning.AktivitetStart.value) ); if (!this.maeklarObjekt.isUtland) { this.newVisning.PublikStarttid.setValue(this.newVisning.AktivitetStart.value); this.newVisning.PublikSlutTidpunkt.setValue(this.newVisning.AktivitetSlut.value); } this.pendingVisningSave = this.visningService.add(this.newVisning); const newVisning: fb.Visning = this.pendingVisningSave; newVisning.$promise.then(() => { clonedVisning = _.clone(newVisning); this.visningar.push(clonedVisning); this.valdSlot = { VisningId: clonedVisning.AktivitetId.value, AktivitetStart: clonedVisning.AktivitetStart.value, AktivitetSlut: clonedVisning.AktivitetSlut.value }; this.valdVisning = clonedVisning; this.laeggTillVisningFoerPerson(); this.initiateNewVisning(); }).catch(data => { this.onError.emit({ data, origin: 'serverfel' }); }); this.showAddNewVisning = false; } private initiateNewVisning(): void { this.newVisning = fb.Visning.createNew(); this.newVisning.AktivitetStart.setValue(moment().format('YYYY-MM-DDTHH:mm:ss')); this.newVisning.AktivitetSlut.setValue(moment().format('YYYY-MM-DDTHH:mm:ss')); this.newVisning.PublikStarttid.setValue(null); this.newVisning.SkallVisasPaaInternet.setValue(false); this.newVisning.MaeklarObjektId.setValue(this.maeklarObjekt.MaeklarObjektId.value); if (this.maeklarObjekt.isUtland) { this.newVisningAnsvarigPersonId = new fb.ChangeTrack(this.maeklarObjekt.AnsvarigMaeklareId.value); } } private deleteVisningCallback(visningsSlot: fb.ISlot, updateIntressentPromise: Promise): void { // Ta bort visningen updateIntressentPromise .then(() => { const i: number = this.visningar .indexOf( _.find( this.visningar, (visning: fb.Visning) => visning.AktivitetId.value === visningsSlot.VisningId ) ); const removed: fb.Visning = this.visningar.splice(i, 1)[0]; if (removed.AktivitetId) { removed.dequeue(); // Tar bort ev. förändingar från save-kön innan borttag this.visningService.remove(removed.AktivitetId.value).$promise .catch(() => { // Lägg tillbaka visning, gick inte att ta bort den this.visningar.splice(i, 0, removed); removed.enqueue(); }); } }) .catch(); } private visningsNummer(visning: fb.Visning): number { const index: number = this.visningar.indexOf(visning); return index === -1 ? this.visningar.length : (index + 1); } private visningsNummerFromSlot(slot: fb.ISlot): number { const visning: fb.Visning = _.find(this.visningar, (vis: fb.Visning) => vis.AktivitetId.value === slot.VisningId); const index: number = this.visningar.indexOf(visning); return index === -1 ? this.visningar.length : (index + 1); } } angular.module('fasit').directive( 'fbNyttBud', downgradeComponent({ component: NyttBudComponent, inputs: [ 'serverfel', 'baraEnIntressent', 'intressent', 'isUnderkontakt', 'personIntressegrad', 'maeklarObjekt', 'visningar', 'nyObjektIntressent', 'digitalBudgivning', 'taBortHelaVisningen' ], outputs: [ 'dismissServerFel', 'onIntressegradChange', 'onError', 'refreshVisningar' ] }) );