import { Injectable } from '@angular/core'; import { TranslationService } from '@core/services/translation.service'; import { CyclesAPI } from '@core/typings/api/cycles.typing'; import { ArrayHelpersService, SimpleStringMap } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { LogService } from '@yourcause/common/logging'; import { NotifierService } from '@yourcause/common/notifier'; import { AttachYCState, BaseYCService } from '@yourcause/common/state'; import moment from 'moment'; import { CyclesResources } from './cycles.resources'; import { CyclesState } from './cycles.state'; @AttachYCState(CyclesState) @Injectable({ providedIn: 'root' }) export class CyclesService extends BaseYCService { constructor ( private logger: LogService, private cyclesResources: CyclesResources, private notifier: NotifierService, private i18n: I18nService, private arrayHelper: ArrayHelpersService, private translationService: TranslationService ) { super(); } get programCycleMap () { return this.get('programCycleMap'); } get allMyCycleSelectOptions () { return this.get('allMyCycleSelectOptions'); } get allMyNonFutureCycleSelectOptions () { return this.get('allMyNonFutureCycleSelectOptions'); } resetAllMyCycleSelectOptions () { this.set('allMyCycleSelectOptions', undefined); } setMyCycleAttrs (myCycles: CyclesAPI.BaseProgramCycle[]) { this.set( 'allMyCycleSelectOptions', this.arrayHelper.sort( myCycles.map((cycle) => { return this.getCycleSelectObj(cycle); }), 'label' ) ); this.set( 'allMyNonFutureCycleSelectOptions', this.arrayHelper.sort( myCycles.filter((cycle) => { return moment(cycle.startDate).isBefore(moment()); }).map((cycle) => { return this.getCycleSelectObj(cycle); }), 'label' ) ); } getCycleSelectObj (cycle: CyclesAPI.SimpleCycle|CyclesAPI.BaseProgramCycle) { const translateMap = this.translationService.viewTranslations.Grant_Program_Cycle; const record = translateMap[cycle.id]; return { label: record && record.Name ? record.Name : (cycle as CyclesAPI.SimpleCycle).name || '', value: cycle.id }; } getCycleStatus (startDate: string, endDate: string): CyclesAPI.CycleStatuses { if (moment().isBefore(moment(startDate))) { return CyclesAPI.CycleStatuses.Upcoming; } else if (moment().isAfter(moment(endDate))) { return CyclesAPI.CycleStatuses.Past; } return CyclesAPI.CycleStatuses.Open; } getArchiveCycleStats (cycleId: number) { return this.cyclesResources.getArchiveCycleStats(cycleId); } handleDeletedCycles (cycleIds: number[] = []) { return Promise.all(cycleIds.map((cycleId) => { return this.cyclesResources.deleteCycle(cycleId); })); } async handleArchivedCycles ( cycles: CyclesAPI.ConfigureProgramCycle[] = [], originalCycles: CyclesAPI.ConfigureProgramCycle[] = [] ) { const cyclesToArchive: CyclesAPI.ConfigureProgramCycle[] = []; const cyclesToUnarchive: CyclesAPI.ConfigureProgramCycle[] = []; originalCycles.forEach((original) => { const found = cycles.find((cycle) => { return original.id === cycle.id; }); if (found) { if (original.isArchived && !found.isArchived) { cyclesToUnarchive.push(found); } if (!original.isArchived && found.isArchived) { cyclesToArchive.push(found); } } }); await Promise.all(cyclesToArchive.map((cycle) => { return this.cyclesResources.archiveCycle(cycle.id, cycle.archiveComment); })); await Promise.all(cyclesToUnarchive.map((cycle) => { return this.cyclesResources.unarchiveCycle(cycle.id); })); } resetCycleMap () { this.set('programCycleMap', {}); } setCycleMap (cycleMap: SimpleStringMap) { this.set('programCycleMap', cycleMap); } getAllCycles () { return this.cyclesResources.getAllCycles(); } getTranslatedCycleStatus (status: CyclesAPI.CycleStatuses) { switch (status) { case CyclesAPI.CycleStatuses.Open: return this.i18n.translate( 'GLOBAL:textOpen', {}, 'Open' ); case CyclesAPI.CycleStatuses.Upcoming: return this.i18n.translate( 'GLOBAL:textUpcoming', {}, 'Upcoming' ); case CyclesAPI.CycleStatuses.Past: return this.i18n.translate( 'GLOBAL:textClosed', {}, 'Closed' ); default: return ''; } } async handleBulkCycleUpdate (payload: CyclesAPI.BulkCycleUpdatePayload) { let errorMessage = ''; try { await this.cyclesResources.bulkCycleUpdate(payload); this.notifier.success( this.i18n.translate( 'PROGRAM:textSuccessfullyUpdatedCycle2', {}, 'Successfully updated cycle' ) ); } catch (e: any) { this.logger.error(e); this.notifier.error( this.i18n.translate( 'PROGRAM:textErrorUpdatingCycle', {}, 'There was an error updating the cycle' ) ); if ( e.error.message === 'The specified destination cycle is not valid or doesn\'t exist' ) { errorMessage = this.i18n.translate( 'PROGRAM:textNewCycleMustContainTheSameBudgetUsed', {}, 'New cycle must contain the budget previously used' ); } } return errorMessage; } getCycleDateHelpers ( cycles: CyclesAPI.BaseProgramCycle[] ): CyclesAPI.CycleHelpers { let currentCycle: CyclesAPI.BaseProgramCycle; cycles.forEach((cycle) => { if (moment().isBetween(cycle.startDate, cycle.endDate)) { currentCycle = cycle; } }); const upcomingCycles = cycles.filter((a) => moment(a.startDate).isAfter()); const nextCycle = upcomingCycles.length ? upcomingCycles.reduce((next, curr) => { return moment(curr.startDate).isBefore(next.startDate) ? curr : next; }) : undefined; const previousCycles = cycles.filter((a) => moment(a.endDate).isBefore()); const lastCycle = previousCycles.length ? previousCycles.reduce((next, curr) => { return (moment(curr.endDate).isAfter(next.endDate)) ? curr : next; }) : undefined; return { currentCycle, nextCycle, lastCycle }; } returnCycleIdsForProgramList (programIds: number[]) { const cycleMap = this.get('programCycleMap'); const cycles: CyclesAPI.SimpleCycle[] = []; programIds.forEach((programId) => { const cyclesForProg = cycleMap[programId]; cycles.push(...cyclesForProg); }); const cycleIds: number[] = []; cycles.forEach((cycle) => { cycleIds.push(cycle.id); }); return cycleIds; } }