import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { SpinnerService } from '@core/services/spinner.service'; import { TimeZoneService } from '@core/services/time-zone.service'; import { TranslationService } from '@core/services/translation.service'; import { CyclesAPI } from '@core/typings/api/cycles.typing'; import { ProgramDropdownOptionsForInsights, ProgramForDashboard, ProgramTopLevelStats, ProgramTypes } from '@core/typings/program.typing'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { CyclesService } from '@features/cycles/cycles.service'; import { ProgramService } from '@features/programs/program.service'; import { ArrayHelpersService, AutoTableRepositoryFactory, DebounceFactory, FileService, Html2CanvasService, SelectOption, TableCSVFactory, TopLevelFilter, TypeaheadSelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; interface ProgramGroup { archiveStatusFilter: ProgramDropdownOptionsForInsights; programFilter: number[]; } @Component({ selector: 'gc-program-insights-page', templateUrl: './program-insights-page.component.html', styleUrls: ['./program-insights-page.component.scss'] }) export class ProgramInsightsPageComponent implements OnInit { key = 'PROGRAM_MANAGER'; allProgramsSelected = false; programs: ProgramForDashboard[] = []; programIds: number[] = []; cycleIds: number[] = []; isArchived: boolean = null; programTopLevelStats: ProgramTopLevelStats; formGroup: TypeSafeFormGroup; programCsvFactory: TableCSVFactory; programOptions: SelectOption[] = []; programListFilters: TopLevelFilter[] = []; rows: ProgramForDashboard[] = []; programTypes = ProgramTypes; viewTranslations = this.translationService.viewTranslations; programTranslationMap = this.viewTranslations.Grant_Program; clientDefaultTimezone = this.clientSettingsService.clientSettings.defaultTimezone; archiveOptions: TypeaheadSelectOption[] = [{ value: ProgramDropdownOptionsForInsights.ALL, label: this.i18n.translate( 'common:textAllPrograms', {}, 'All programs' ) }, { value: ProgramDropdownOptionsForInsights.ACTIVE, label: this.i18n.translate( 'common:lblActivePrograms', {}, 'Active programs' ) }, { value: ProgramDropdownOptionsForInsights.ARCHIVED, label: this.i18n.translate( 'common:lblArchivePrograms', {}, 'Archived programs' ) }]; downloadingChart = false; @ViewChild('chartWrapper') chartWrapperEl: ElementRef; constructor ( private formBuilder: TypeSafeFormBuilder, private programService: ProgramService, private arrayHelper: ArrayHelpersService, private i18n: I18nService, private translationService: TranslationService, private cyclesService: CyclesService, private clientSettingsService: ClientSettingsService, private fileService: FileService, private autoTableRepoFactory: AutoTableRepositoryFactory, private timezoneService: TimeZoneService, private spinnerService: SpinnerService, private html2CanvasService: Html2CanvasService ) { } get programsFromState () { return this.programService.get('dashboardPrograms').filter((prog) => { return !prog.isDraft; }); } async ngOnInit () { this.programOptions = this.arrayHelper.sort(this.programsFromState.map((prog) => { const map = this.programTranslationMap[prog.grantProgramId]; return { label: map && map.Name ? map.Name : prog.grantProgramName, value: prog.grantProgramId }; }), 'label'); this.setProgramCsvFactory(); this.formGroup = this.formBuilder.group({ archiveStatusFilter: ProgramDropdownOptionsForInsights.ACTIVE, programFilter: [[]] }); this.programListFilters = [ new TopLevelFilter( 'text', 'grantProgramName', '', this.i18n.translate( 'common:textSearchByProgramName', {}, 'Search by program name' ) ) ]; this.updateProgramFilter(ProgramDropdownOptionsForInsights.ACTIVE); } setProgramCsvFactory () { this.programCsvFactory = DebounceFactory.createSimple(() => { const currentRepo = this.autoTableRepoFactory.getRepository(this.key); const filteredRows = currentRepo.clientSideFilteredRows as ProgramForDashboard[]; const adapted = filteredRows.map((row) => { const programStart = this.timezoneService.displayFormattedTimeInTimezone( row.startDate, row.timezoneId || this.clientDefaultTimezone, false, 'll' ); const programEnd = this.timezoneService.displayFormattedTimeInTimezone( row.endDate, row.timezoneId || this.clientDefaultTimezone, false, 'll' ); return { 'Program Name': row.grantProgramName, 'Is Published': row.isDraft ? 'No' : 'Yes', 'Is Archived': row.isArchived ? 'Yes' : 'No', 'Number of Cycles': row.numberOfGrantProgramCycles, 'Program Start': programStart, 'Program End': programEnd, 'Number of Applications': row.numberOfApplications, 'Request Total': row.requestAmount, 'Number of Awards': row.numberOfAwards, 'Awards Total': row.awardTotal, 'Number of Payments': row.numberOfPayments, 'Payments Total': row.paymentsTotal }; }); return this.fileService.convertObjectArrayToCSVString(adapted); }); } setProgramIds () { const filterValue = this.formGroup.value.archiveStatusFilter; const programs = this.formGroup.value.programFilter; this.allProgramsSelected = programs.length === 0; this.programs = this.programService.filterByProgramStatus(filterValue) .filter((prog) => { return this.allProgramsSelected || programs.includes(prog.grantProgramId); }); this.programIds = this.programs.map((prog) => +prog.grantProgramId); } programFilterChanged () { const programStatus = this.formGroup.value.archiveStatusFilter; this.setProgramIds(); // check to see if all programs selected // if that's true send no program or cycle this.cycleIds = this.allProgramsSelected ? [] : this.cyclesService.returnCycleIdsForProgramList(this.programIds); // check to see which active/archive filter they've selected // cyclePayload should be empty array if they've selected all programs if (programStatus !== ProgramDropdownOptionsForInsights.ALL) { this.isArchived = programStatus === ProgramDropdownOptionsForInsights.ARCHIVED; } else { this.isArchived = null; } this.rows = this.programsFromState.filter((program) => { return this.programIds.includes(+program.grantProgramId); }); } updateProgramFilter (filterValue: ProgramDropdownOptionsForInsights) { this.programOptions = this.programService.getProgramCycleOptionsForInsights( filterValue, false, CyclesAPI.CycleStatuses.All ); this.programFilterChanged(); this.formGroup.get('programFilter').setValue([]); } async downloadCharts () { this.spinnerService.startSpinner(); this.downloadingChart = true; setTimeout(async () => { await this.html2CanvasService.downloadSnapshot(this.chartWrapperEl.nativeElement); this.downloadingChart = false; this.spinnerService.stopSpinner(); }); } }