import { Injectable } from '@angular/core'; import { TimeZoneService } from '@core/services/time-zone.service'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { FileService } 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 { CustomerInsightsResources } from './customer-insights.resources'; import { CustomerInsightsState } from './customer-insights.state'; import { CustomerInsights } from './customer-insights.typing'; @AttachYCState(CustomerInsightsState) @Injectable({ providedIn: 'root' }) export class CustomerInsightsService extends BaseYCService { constructor ( private customerInsightsResources: CustomerInsightsResources, private logger: LogService, private i18n: I18nService, private notifier: NotifierService, private clientSettingsService: ClientSettingsService, private fileService: FileService, private timeZoneService: TimeZoneService ) { super(); } get customerInsightData () { return this.get('customerInsightData'); } setCustomerInsightsData ( clientDataKey: string, appData: CustomerInsights.DataSetForChart[], loginData: CustomerInsights.DataSetForChart[] ) { this.set('customerInsightData', { ...this.customerInsightData, [clientDataKey]: { appData, loginData } }); } resetCustomerInsightData () { this.set('customerInsightData', null); } getClientDataKey (payload: CustomerInsights.CustomerInsightsPayload): string { return '' + payload.clientId + payload.date; } async getAndSetCustomerInsightsData ( payload: CustomerInsights.CustomerInsightsPayload ) { const clientDataKey = this.getClientDataKey(payload); if (!this.customerInsightData || !this?.customerInsightData[clientDataKey]) { const appData = await this.getApplicationSubmittedDataSet(payload); const loginData = await this.getLoginDataSet(payload); this.setCustomerInsightsData(clientDataKey, appData, loginData); } return this.customerInsightData[clientDataKey]; } async getApplicationSubmittedDataSet ( payload: CustomerInsights.CustomerInsightsPayload ): Promise { const response = await this.customerInsightsResources.getApplicationSubmittedInfo(payload); const adapted = this.adaptDataForChart(response); return adapted; } async getLoginDataSet ( payload: CustomerInsights.CustomerInsightsPayload ): Promise { const response = await this.customerInsightsResources.getUserLoginInfo(payload); const adapted = this.adaptDataForChart(response); return adapted; } adaptDataForChart ( data: CustomerInsights.CustomerInsightsResponse ): CustomerInsights.DataSetForChart[] { const providedYear = '' + Object.keys(data.providedYearData)[0].split('/')[0]; const previousYear = '' + Object.keys(data.previousYearData)[0].split('/')[0]; const providedYearData = Object.keys(data.providedYearData).map((key) => data.providedYearData[key]); const previousYearData = Object.keys(data.previousYearData).map((key) => data.previousYearData[key]); return [{ label: providedYear, data: providedYearData, backgroundColor: this.clientSettingsService.defaultBranding.brandUtility, borderColor: this.clientSettingsService.defaultBranding.brandUtility, pointBackgroundColor: this.clientSettingsService.defaultBranding.brandUtility, pointHoverBackgroundColor: this.clientSettingsService.defaultBranding.brandUtility, pointHoverBorderColor: this.clientSettingsService.defaultBranding.brandUtility }, { label: previousYear, data: previousYearData, backgroundColor: this.clientSettingsService.defaultBranding.brandSecondary, borderColor: this.clientSettingsService.defaultBranding.brandSecondary, pointBackgroundColor: this.clientSettingsService.defaultBranding.brandSecondary, pointHoverBackgroundColor: this.clientSettingsService.defaultBranding.brandSecondary, pointHoverBorderColor: this.clientSettingsService.defaultBranding.brandSecondary }]; } async getApplicationSubmittedInfo ( payload: CustomerInsights.CustomerInsightsPayload ) { try { const response = await this.customerInsightsResources.getApplicationSubmittedInfo(payload); return response; } catch (e) { this.logger.error(e); this.notifier.error( this.i18n.translate( 'ADMIN:textErrorFetchingApplicationInfo2', {}, 'There was an error getting applications submitted data' ) ); return null; } } async getUserLoginInfo ( payload: CustomerInsights.CustomerInsightsPayload ) { try { const response = await this.customerInsightsResources.getUserLoginInfo(payload); return response; } catch (e) { this.logger.error(e); this.notifier.error( this.i18n.translate( 'admin:textErrorFetchingLoginInfo', {}, 'There was an error getting login data' ) ); return null; } } async getAdminInsightsFile ( payload: CustomerInsights.CustomerInsightsFilePayload, clientName: string ) { try { const response = await this.customerInsightsResources.getAdminInsightsFile(payload); const fileName = this.i18n.translate( 'ADMIN:textClientInsightFileName', { clientName, date: this.timeZoneService.returnDateForMgmtDisplay(payload.adminInsightsRequestModel.date, 0, 'YYYY') }, '__clientName__ Activity Since __date__' ); await this.fileService.downloadUrlAs(response, fileName); } catch (e) { this.logger.error(e); this.notifier.error( this.i18n.translate( 'ADMIN:textErrorFetchingLoginInfoExport2', {}, 'There was an error getting login data export' ) ); } } getUsageTrendData ( chartData: CustomerInsights.DataSetForChart[], comparingCurrentYear: boolean, currentMonthIndex: number ): { percentageChange: number; previousYearTotal: number; } { // when comparingCurrnetYear is true currentMonthIndex is used to get the total value for "this time last year" const yearSelectedTotal = chartData[0].data .reduce((sum, value) => sum + value, 0); const previousYearTotal = chartData[1].data .filter((_a, index) => comparingCurrentYear ? index <= currentMonthIndex : true) .reduce((sum, value) => sum + value, 0); let percentageChange; // if both are above 0, return actual percentage if (yearSelectedTotal > 0 && previousYearTotal > 0) { percentageChange = +((yearSelectedTotal/previousYearTotal) * 100).toFixed(); // if current year is greater than 0 but previous year is not, cap at 100 (to avoid dividing by zero) } else if (yearSelectedTotal > 0) { percentageChange = 100; // else, show zero since 0/anyNumber will equal zero } else { percentageChange = 0; } return { percentageChange, previousYearTotal }; } }