import { Component, OnInit } from '@angular/core'; import { CyclesAPI } from '@core/typings/api/cycles.typing'; import { ProgramService } from '@features/programs/program.service'; import { ALL_SKIP_FILTER, ArrayHelpersService, TopLevelFilter, TopLevelFilterOptionsConfig } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { ConfirmationModalComponent, ModalFactory } from '@yourcause/common/modals'; import { isEqual } from 'lodash'; import { ArchiveCycleModalComponent } from '../archive-cycle-modal/archive-cycle-modal.component'; import { ProgramCycleModalComponent } from '../program-cycle-modal/program-cycle-modal.component'; @Component({ selector: 'gc-program-cycles', templateUrl: './program-cycles.component.html', styleUrls: ['./program-cycles.component.scss'] }) export class ProgramCyclesComponent implements OnInit { topLevelFilters: TopLevelFilter[] = []; statusOptions: TopLevelFilterOptionsConfig = { selectOptions: [{ value: ALL_SKIP_FILTER, display: this.i18n.translate( 'PROGRAM:textAllCycles', {}, 'All cycles' ) }, { value: CyclesAPI.CycleStatuses.Open, display: this.i18n.translate( 'PROGRAM:textOpenCycles', {}, 'Open cycles' ) }, { value: CyclesAPI.CycleStatuses.Upcoming, display: this.i18n.translate( 'PROGRAM:textUpcomingCycles', {}, 'Upcoming cycles' ) }, { value: CyclesAPI.CycleStatuses.Past, display: this.i18n.translate( 'PROGRAM:textCloseCycles', {}, 'Closed cycles' ) }] }; zeroStateDescription = this.i18n.translate( 'PROGRAM:textAddCyclesToYourProgramDesc', {}, 'Add cycles to your program by clicing "Add cycle" above. At least one cycle is required for your program.' ); constructor ( private programService: ProgramService, private i18n: I18nService, private modalFactory: ModalFactory, private arrayHelper: ArrayHelpersService ) { } get isNomination () { return location.pathname.includes('nomination'); } get programMap () { return this.programService.get('configureProgramMap'); } get activeProgramId () { return this.programService.get('activeProgramId'); } get program () { return this.programMap[this.activeProgramId]; } ngOnInit () { this.setTopLevelFilters(); } setTopLevelFilters () { this.topLevelFilters = [ new TopLevelFilter( 'text', 'name', '', this.i18n.translate( 'PROGRAM:textSearchByCycleName', {}, 'Search by cycle name' ) ), new TopLevelFilter( 'typeaheadSingleEquals', 'status', ALL_SKIP_FILTER, undefined, this.statusOptions, this.i18n.translate( 'GLOBAL:textCycleStatus', {}, 'Cycle status' ) ) ]; } getIndex (compareCycle: CyclesAPI.ConfigureProgramCycle) { return this.program.cycles.findIndex((cycle) => { return isEqual(cycle, compareCycle); }); } updateCycle (newCycle: CyclesAPI.ConfigureProgramCycle, index: number) { const cycles = [ ...this.program.cycles.slice(0, index), newCycle, ...this.program.cycles.slice(index + 1) ]; this.programService.setMapProperty( this.activeProgramId, 'cycles', this.arrayHelper.sort(cycles, 'name') ); } deleteCycle (index: number) { const cycles = [ ...this.program.cycles.slice(0, index), ...this.program.cycles.slice(index + 1) ]; this.programService.setMapProperty( this.activeProgramId, 'cycles', this.arrayHelper.sort(cycles, 'name') ); } async edit (row: CyclesAPI.ConfigureProgramCycle) { const cycle = await this.modalFactory.open( ProgramCycleModalComponent, { cycle: { ...row }, otherCycles: this.program.cycles.filter((otherCycle) => { // We may not have ID here, so use dates return (otherCycle.startDate !== row.startDate) && (otherCycle.endDate !== row.endDate); }) } ); if (cycle) { this.updateCycle(cycle, this.getIndex(row)); } } async copy (row: CyclesAPI.ConfigureProgramCycle) { const cycle = await this.modalFactory.open( ProgramCycleModalComponent, { isCopy: true, cycle: { ...row, id: undefined, name: row.name + ' - Copy' }, otherCycles: this.program.cycles } ); if (cycle) { this.programService.setMapProperty( this.activeProgramId, 'cycles', this.arrayHelper.sort([ ...this.program.cycles, cycle ], 'name') ); } } async archive (row: CyclesAPI.ConfigureProgramCycle) { const response = await this.modalFactory.open( ArchiveCycleModalComponent, { isNomination: this.isNomination, cycleId: row.id, cycleName: row.name } ); if (response) { row.isArchived = true; row.archiveComment = response.comment; this.updateCycle(row, this.getIndex(row)); } } async unarchive (row: CyclesAPI.ConfigureProgramCycle) { const deps = { modalHeader: this.i18n.translate( 'PROGRAM:hdrUnarchiveCycle', {}, 'Unarchive Cycle' ), modalSubHeader: row.name, confirmButtonText: this.i18n.translate( 'GLOBAL:textUnarchive', {}, 'Unarchive' ), confirmText: this.i18n.translate( 'PROGRAM:textUnarchiveCycleConfirmText', {}, 'Are you sure you want to unarchive the cycle?' ) }; const response = await this.modalFactory.open( ConfirmationModalComponent, deps ); if (response) { row.isArchived = false; this.updateCycle(row, this.getIndex(row)); } } async delete (row: CyclesAPI.ConfigureProgramCycle) { const deps = { modalHeader: this.i18n.translate( 'PROGRAM:hdrDeleteCycle', {}, 'Delete Cycle' ), modalSubHeader: row.name, confirmButtonText: this.i18n.translate( 'PROGRAM:textDeleteCycle', {}, 'Delete cycle' ), confirmText: this.i18n.translate( this.isNomination ? 'PROGRAM:textDeleteCycleConfirmTextNom' : 'PROGRAM:textDeleteCycleConfirmTextApp', {}, this.isNomination ? 'Deleting the cycle will prevent nominators from submitting nominations for the cycle time period. Are you sure you want to delete the cycle?' : 'Deleting the cycle will prevent applicants from submitting applications for the cycle time period. Are you sure you want to delete the cycle?' ) }; const response = await this.modalFactory.open( ConfirmationModalComponent, deps ); if (response) { this.deleteCycle(this.getIndex(row)); if (row.id) { this.programService.setMapProperty( this.activeProgramId, 'cyclesToDelete', [ ...this.program.cyclesToDelete || [], row.id ] ); } } } }