import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { APIAdminClient, CreateClientResponse } from '@core/typings/api/admin-client.typing'; import { AdaptedAdminUser } from '@core/typings/user.typing'; import { CustomDataTablesService } from '@features/custom-data-tables/custom-data-table.service'; import { ReferenceFieldsService } from '@features/reference-fields/services/reference-fields.service'; import { ArrayHelpersService, PaginationOptions, SimpleStringMap, TypeaheadSelectOption } 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 { AtRiskModalResponse } from '../customer-insights/at-risk-activity-tracking-email-modal/at-risk-activity-tracking-email-modal.component'; import { AdminClientResources } from './admin-client.resources'; import { AdminClientState } from './admin-client.state'; @AttachYCState(AdminClientState) @Injectable({ providedIn: 'root' }) export class AdminClientService extends BaseYCService { constructor ( private logger: LogService, private adminClientResources: AdminClientResources, private arrayHelper: ArrayHelpersService, private referenceFieldsService: ReferenceFieldsService, private dataTableService: CustomDataTablesService, private notifier: NotifierService, private i18n: I18nService ) { super(); this.loadProgramsForClientId(this.activeClientId); this.changesTo$('activeClientId') .subscribe(async (id: number) => { await this.loadProgramsForClientId(id); }); } get clients () { return this.get('clients'); } get activeClient () { return this.get('activeClient'); } get activeClientId () { return this.get('activeClientId'); } get clientPrograms () { return this.get('clientPrograms'); } get clientProgramForms () { return this.get('clientProgramForms'); } get clientsMap () { return this.get('clientsMap'); } get clientBudgetsMap () { return this.get('clientBudgetsMap'); } getApplicantTypeOptions () { return [{ label: this.i18n.translate( 'ADMIN:textEmployeeOnly', {}, 'Employee only' ), value: APIAdminClient.SupportedApplicantTypes.EmployeeOnly }, { label: this.i18n.translate( 'ADMIN:textEmployeeAndNonprofits', {}, 'Employees and nonprofits' ), value: APIAdminClient.SupportedApplicantTypes.EmployeesAndNonprofits }, { label: this.i18n.translate( 'ADMIN:textNonprofitOnly', {}, 'Nonprofit only' ), value: APIAdminClient.SupportedApplicantTypes.NonprofitOnly }]; } async fetchClientExtras (clientId: number) { const [ refFieldResponse, dataTables, budgetsAndSources ] = await Promise.all([ this.adminClientResources.getClientReferenceFields(clientId, []), this.adminClientResources.getClientDataTables(clientId), this.adminClientResources.getClientBudgetsAndFundingSources(clientId) ]); this.set('clientBudgetsMap', { ...this.clientBudgetsMap, [clientId]: budgetsAndSources }); this.referenceFieldsService.setAllReferenceFields(refFieldResponse.records); this.dataTableService.setCustomDataTableOnState(dataTables); dataTables.forEach(table => { const options = table.picklistOptions; this.dataTableService.setCustomDataTableOptionsOnState(table.id, options); }); } async loadProgramsForClientId (id: number) { if (id && !this.clientPrograms[id]) { const programs = await this.adminClientResources.getClientPrograms(id); this.set('clientPrograms', { ...this.clientPrograms, [id]: programs }); } } async fetchClientProgramDetails (programId: number) { if (!this.clientProgramForms[programId]) { const forms = await this.adminClientResources.getClientProgramForms(this.activeClientId, programId); this.set('clientProgramForms', { ...this.clientProgramForms, [programId]: forms }); } return this.clientProgramForms[programId]; } async getAllClients () { const clients = await this.adminClientResources.getClients({ rowsPerPage: 10, pageNumber: 1, sortColumns: [], filterColumns: [], retrieveTotalRecordCount: true, returnAll: true }); this.appendClients(clients.records); return clients.records; } getClients (options: PaginationOptions) { return this.adminClientResources.getClients(options); } async resetClients () { const clients = await this.adminClientResources.getClients({ rowsPerPage: 10, pageNumber: 1, sortColumns: [{ columnName: 'name', sortAscending: true } ], filterColumns: [], retrieveTotalRecordCount: true, returnAll: true }); this.set('clients', clients.records); this.alignActiveClient(); } appendClients (clients: APIAdminClient.Client[]) { const sorted = this.arrayHelper.sort(clients, 'name'); const adminClientMap: SimpleStringMap = {}; clients.forEach((client) => { adminClientMap[client.clientId] = client; }); this.set('clientsMap', adminClientMap); this.set('clients', sorted); this.alignActiveClient(); } setActiveClient (clientId: number) { this.set('activeClientId', clientId); this.alignActiveClient(); } alignActiveClient () { const foundClient = this.clients.find(client => client.clientId === this.activeClientId); if (foundClient && (foundClient !== this.activeClient)) { this.set('clientPrograms', { ...this.clientPrograms, [foundClient.clientId]: null }); } this.set('activeClient', foundClient); } async getClientObjects (clientId: number) { const objects = await this.adminClientResources.getClientObjects(clientId); return objects.map((object) => { return { ...object, parentId: object.parentId || '' }; }); } clearClientData (clientId: number) { return this.adminClientResources.clearClientData(clientId); } getCSRClientsNotInGC (): Promise { return this.adminClientResources.getCSRClientsNotInGC(); } getClientOptionsForAdmin (adminDetail: AdaptedAdminUser) { let allowedClients; let clientOptions; if (!adminDetail.canAccessAllClientZones) { allowedClients = this.clients.filter((client) => { return adminDetail.clientZoneIds.includes(client.clientId); }); } else { allowedClients = this.clients; } clientOptions = allowedClients.map(client => { return { label: client.name, value: client.clientId }; }); clientOptions = this.arrayHelper.sort(clientOptions, 'label'); return clientOptions; } /** * * @param client: the client to create or update * @returns success / fail info */ async createOrUpdateClient ( client: APIAdminClient.SaveClient ): Promise { try { await this.adminClientResources.createOrUpdateClient(client); await this.getAllClients(); this.notifier.success(this.i18n.translate( 'ADMIN:notificationSuccesfulClientSubmission', {}, 'Successfully submitted client information.' )); return CreateClientResponse.SUCCESS; } catch (err) { const e = err as HttpErrorResponse; this.logger.error(e); if (e?.error?.message === 'Email already exists.') { this.notifier.error(this.i18n.translate( 'ADMIN:textEmailAlreadyExistsUseDifferent', {}, 'This email already exists. Use a different email to continue.' )); return CreateClientResponse.EMAIL_EXISTS; } else { this.notifier.error(this.i18n.translate( 'ADMIN:notificationErrorSendingClient', {}, 'There was an error sending the client information.' )); return CreateClientResponse.FAIL; } } } /** * * @param clientId: the clientId we may be snoozing alert activity * @param modalResponse: response from at risk modal */ async handleSnoozeSetting ( clientId: number, modalResponse: AtRiskModalResponse ) { try { await this.adminClientResources.snoozeActivityAlerts( clientId, this.getSnoozedTilDate(modalResponse.snoozedDateOption, '') ); await this.getAllClients(); this.notifier.success(this.i18n.translate( 'common:textSuccessfullyUpdatedAtRiskNotifications', {}, 'Succesfully updated at risk notifications' )); } catch (e) { this.logger.error(e); this.notifier.error(this.i18n.translate( 'common:textThereWasAnErrorUpdatingAtRiskNotifications', {}, 'There was an error updating at risk notifications' )); } } /** * * @param status selected alert status in dropdown * @returns the snoozed til date based on that status */ getSnoozedTilDate ( status: APIAdminClient.ActivityAlertStatusOption, format = 'll' ) { const today = moment(); switch (status) { case APIAdminClient.ActivityAlertStatusOption.Snooze1Month: return today.add(1, 'month').format(format); case APIAdminClient.ActivityAlertStatusOption.Snooze2Months: return today.add(2, 'month').format(format); case APIAdminClient.ActivityAlertStatusOption.Snooze3Months: return today.add(3, 'month').format(format); case APIAdminClient.ActivityAlertStatusOption.Active: return ''; } } /** * * @returns options for the activity alert status */ getSnoozeDateOptions (): TypeaheadSelectOption[] { return [{ label: this.getSnoozeUntilText( APIAdminClient.ActivityAlertStatusOption.Snooze1Month ), value: APIAdminClient.ActivityAlertStatusOption.Snooze1Month }, { label: this.getSnoozeUntilText( APIAdminClient.ActivityAlertStatusOption.Snooze2Months ), value: APIAdminClient.ActivityAlertStatusOption.Snooze2Months }, { label: this.getSnoozeUntilText( APIAdminClient.ActivityAlertStatusOption.Snooze3Months ), value: APIAdminClient.ActivityAlertStatusOption.Snooze3Months }]; } /** * * @param option: option for snoozing * @returns the snoozed until text */ getSnoozeUntilText (option: APIAdminClient.ActivityAlertStatusOption) { return this.i18n.translate( 'common:textSnoozeUntilDate', { date: this.getSnoozedTilDate(option) }, 'Snooze until __date__' ); } /** * * @param clientId: client ID to snooze * @returns if unsnooze was successful */ async handleUnsnooze (clientId: number) { try { await this.adminClientResources.unsnoozeActivityAlerts(clientId); await this.getAllClients(); this.notifier.success(this.i18n.translate( 'common:textSuccessfullyUpdatedAtRiskNotifications', {}, 'Succesfully updated at risk notifications' )); return true; } catch (e) { this.logger.error(e); this.notifier.error(this.i18n.translate( 'common:textThereWasAnErrorUpdatingAtRiskNotifications', {}, 'There was an error updating at risk notifications' )); return false; } } }