import { Component, OnInit } from '@angular/core'; import { Validators } from '@angular/forms'; import { SpinnerService } from '@core/services/spinner.service'; import { CharityBucket } from '@core/typings/charity-buckets.typings'; import { ProgramApplicantType, ProgramDeadline } from '@core/typings/program.typing'; import { CharityBucketsService } from '@features/charity-buckets/charity-buckets.service'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { CharityBucketsModalComponent } from '@features/programs/charity-buckets-modal/charity-buckets-modal.component'; import { EmailService } from '@features/system-emails/email.service'; import { EmailNotificationType } from '@features/system-emails/email.typing'; import { PanelTypes, SelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { ModalFactory } from '@yourcause/common/modals'; import { AutoDeclineModalComponent } from '../auto-decline-modal/auto-decline-modal.component'; import { ProgramDeadlineModalComponent } from '../program-deadline-modal/program-deadline-modal.component'; import { ProgramService } from '../program.service'; const DEFAULT_FREQUENCY = 5; interface ProgramSettingsFormGroup { allowCollaboration: boolean; reserveFunds: boolean; autoDeclineBudgetAssignment: boolean; autoDeclineBudgetAssignmentDeclinationComment: string; autoDeclineBudgetAssignmentSendEmail: boolean; charityBucket: CharityBucket; restrictOrgSearch: boolean; charityBucketId: string; applicantType: ProgramApplicantType; allowAddOrg: boolean; charityBucketDescriptions: string; maskApplicantInfo: boolean; useProgramLogo: boolean; senderDisplayName: string; draftReminder: number; inviteOnly: boolean; applicantFormDueReminder: boolean; reviewerFormDueReminder: boolean; formDueReminderFrequencyForApplicant: number; formOverDueReminderFrequencyForApplicant: number; formDueReminderFrequencyForReviewer: number; formOverDueReminderFrequencyForReviewer: number; hidePaymentStatus: boolean; alternatePaymentStatusText: string; } @Component({ selector: 'gc-program-settings', templateUrl: './program-settings.component.html', styleUrls: ['./program-settings.component.scss'] }) export class ProgramSettingsComponent implements OnInit { formGroup: TypeSafeFormGroup; ProgramApplicantType = ProgramApplicantType; noneSelected = this.i18n.translate('PROGRAM:textNoneSelected', {}, 'None selected'); charityBucket = this.initializeCharityBucketId(); charityBuckets = this.charityBucketsService.charityBuckets; PanelTypes = PanelTypes; recipientOptions: SelectOption[] = this.charityBuckets ? [{ display: this.i18n.translate( 'GLOBAL:textAnIndividualApplicant', {}, 'An individual applicant' ), value: ProgramApplicantType.INDIVIDUAL }, { display: this.i18n.translate( 'GLOBAL:textAnyOrganization', {}, 'Any organization' ), value: ProgramApplicantType.ORGS }, { display: this.i18n.translate( 'GLOBAL:textOrganizationsBasedOnCharityBucket', {}, 'Organizations based on charity bucket' ), value: ProgramApplicantType.ORGS_WITH_BUCKET }] : [{ display: this.i18n.translate( 'GLOBAL:textAnIndividualApplicant', {}, 'An individual applicant' ), value: ProgramApplicantType.INDIVIDUAL }, { display: this.i18n.translate( 'GLOBAL:textAnOrganization', {}, 'An organization' ), value: ProgramApplicantType.ORGS }]; canChangeRecipient = true; senderHelpText = this.i18n.translate( 'PROGRAM:textSenderDisplayNameHelp2', {}, 'Applies to email body (if applicable), email signature, and in "From" fields for all outgoing emails' ); draftReminderEmailActive = false; formDueReminderFrequencyForApplicantLabel = this.i18n.translate( this.isNomination ? 'common:textEmailIsSetAtFrequencyNomHelp' : 'common:textEmailIsSetAtFrequencyAppHelp', {}, this.isNomination ? 'Email is sent at a frequency set here after a nomination enters a workflow level and form is sent' : 'Email is sent at a frequency set here after an application enters a workflow level and form is sent' ); formOverDueReminderFrequencyForApplicantLabel = this.i18n.translate( 'common:textEmailIsSetAtFrequencyOverdueHelp', {}, 'Email is sent at a frequency set here after the form due date if form has not been submitted' ); formDueReminderFrequencyForReviewerLabel = this.i18n.translate( this.isNomination ? 'common:textEmailIsSetAtFrequencyNomManagerHelp' : 'common:textEmailIsSetAtFrequencyAppManagerHelp', {}, this.isNomination ? 'Email is sent at a frequency set here after a nomination enters a workflow level and required number of submissions have not been met' : 'Email is sent at a frequency set here after an application enters a workflow level and required number of submissions have not been met' ); formOverDueReminderFrequencyForReviewerLabel = this.i18n.translate( 'common:textEmailIsSetAtFrequencyOverdueManagerHelp', {}, 'Email is sent at a frequency set here after the form due date if required number of submissions have not been met' ); constructor ( private programService: ProgramService, private formBuilder: TypeSafeFormBuilder, private modalFactory: ModalFactory, private i18n: I18nService, private charityBucketsService: CharityBucketsService, private clientSettingsService: ClientSettingsService, private emailService: EmailService, private spinnerService: SpinnerService ) { } 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]; } get branding () { return this.clientSettingsService.get('clientBranding'); } get canReserveFunds () { return this.clientSettingsService.get('clientSettings').reserveFunds; } async ngOnInit () { this.spinnerService.startSpinner(); await this.setDraftReminderEmailActive(); this.canChangeRecipient = this.getCanChangeRecipient(); const formDueReminderFrequencyForApplicant = this.program.formDueReminderFrequencyForApplicant; const formDueReminderFrequencyForReviewer = this.program.formDueReminderFrequencyForReviewer; const formOverDueReminderFrequencyForApplicant = this.program.formOverDueReminderFrequencyForApplicant; const formOverDueReminderFrequencyForReviewer = this.program.formOverDueReminderFrequencyForReviewer; this.formGroup = this.formBuilder.group({ allowCollaboration: this.program.allowCollaboration, reserveFunds: this.program.reserveFunds, autoDeclineBudgetAssignment: this.program.autoDeclineBudgetAssignment, autoDeclineBudgetAssignmentDeclinationComment: this.program.autoDeclineBudgetAssignmentDeclinationComment, autoDeclineBudgetAssignmentSendEmail: this.program.autoDeclineBudgetAssignmentSendEmail, charityBucket: this.charityBucket, restrictOrgSearch: !!this.program.charityBucketId, charityBucketId: this.program.charityBucketId || '', applicantType: this.setApplicantType(), allowAddOrg: this.program.allowAddOrg, charityBucketDescriptions: this.program.charityBucketDescriptions || '', maskApplicantInfo: this.program.maskApplicantInfo || false, useProgramLogo: this.program.useProgramLogo || false, senderDisplayName: [ this.program.senderDisplayName || this.branding.name, [ Validators.required, Validators.maxLength(50) ] ], draftReminder: this.program.daysBeforeEndDateReminders || 7, inviteOnly: this.program.inviteOnly, applicantFormDueReminder: !!(formDueReminderFrequencyForApplicant || formOverDueReminderFrequencyForApplicant), reviewerFormDueReminder: !!(formDueReminderFrequencyForReviewer || formOverDueReminderFrequencyForReviewer), formDueReminderFrequencyForApplicant: [formDueReminderFrequencyForApplicant, Validators.min(1)], formOverDueReminderFrequencyForApplicant: [formOverDueReminderFrequencyForApplicant, Validators.min(1)], formDueReminderFrequencyForReviewer: [formDueReminderFrequencyForReviewer, Validators.min(1)], formOverDueReminderFrequencyForReviewer: [formOverDueReminderFrequencyForReviewer, Validators.min(1)], hidePaymentStatus: this.program.hidePaymentStatus || false, alternatePaymentStatusText: this.program.alternatePaymentStatusText || '' }); this.spinnerService.stopSpinner(); } async setDraftReminderEmailActive () { const emailType = this.isNomination ? EmailNotificationType.ProgramClosingReminderForNominator : EmailNotificationType.ProgramClosingReminderForApplicant; const activeForProgram = this.program.id ? await this.emailService.isProgramEmailActive(emailType, this.program.id) : true; this.draftReminderEmailActive = ( this.emailService.isEmailActive(emailType) && activeForProgram ); } getCanChangeRecipient () { if (!this.isNomination) { const found = this.programService.allPublishedPrograms.find((prog) => { return +prog.grantProgramId === +this.activeProgramId; }); return found ? !found.isTiedToNominationPrograms : true; } return true; } setApplicantType () { if (this.program.charityBucketId) { return ProgramApplicantType.ORGS_WITH_BUCKET; } else if (this.program.applicantType) { return this.program.applicantType; } else { return ProgramApplicantType.ORGS; } } setCharityBucketId () { this.programService.setMapProperty( this.activeProgramId, 'charityBucketId', this.formGroup.value.charityBucketId ); if (this.formGroup.value.charityBucketId) { this.programService.setMapProperty( this.activeProgramId, 'allowAddOrg', false ); } } setUseProgramLogo () { this.programService.setMapProperty( this.activeProgramId, 'useProgramLogo', this.formGroup.value.useProgramLogo ); } allowCollabChange () { this.programService.setMapProperty( this.activeProgramId, 'allowCollaboration', this.formGroup.value.allowCollaboration ); } allowReserveFunds () { this.programService.setMapProperty( this.activeProgramId, 'reserveFunds', this.formGroup.value.reserveFunds ); } allowAutoDecline () { this.programService.setMapProperty( this.activeProgramId, 'autoDeclineBudgetAssignment', this.formGroup.value.autoDeclineBudgetAssignment ); } inviteOnlyChange () { this.programService.setMapProperty( this.activeProgramId, 'inviteOnly', this.formGroup.value.inviteOnly ); } draftReminderChange () { this.programService.setMapProperty( this.activeProgramId, 'daysBeforeEndDateReminders', +this.formGroup.value.draftReminder ); } hidePaymentStatusChange () { this.programService.setMapProperty( this.activeProgramId, 'hidePaymentStatus', this.formGroup.value.hidePaymentStatus ); } alternatePaymentStatusTextChange () { this.programService.setMapProperty( this.activeProgramId, 'alternatePaymentStatusText', this.formGroup.value.alternatePaymentStatusText ); } async openCharityBucketsModal () { const deps = { charityBucket: this.charityBucket, charityBucketDescriptions: this.program.charityBucketDescriptions, allowAddOrg: this.program.allowAddOrg }; const response = await this.modalFactory.open( CharityBucketsModalComponent, deps, { class: 'modal-xl', keyboard: false } ); if (response) { this.formGroup.get('charityBucketId').setValue(response.charityBucket.bucketId); this.formGroup.get('restrictOrgSearch').setValue(true); this.formGroup.get('charityBucketDescriptions').setValue( response.orgSearchGuidelines ); const updatedBucket = this.charityBucketsService.charityBuckets .find((bucket) => { return bucket.bucketId === response.charityBucket.bucketId; }); this.formGroup.get('charityBucket').setValue(updatedBucket); this.charityBucket = updatedBucket; this.setCharityBucketId(); this.setOrgSearchGuidelines(); } } async openDeclinationCommentModal () { const deps = { comment: this.formGroup.value.autoDeclineBudgetAssignmentDeclinationComment, notifyApplicant: this.formGroup.value.autoDeclineBudgetAssignmentSendEmail }; const declinationModal = await this.modalFactory.open( AutoDeclineModalComponent, deps ); if (declinationModal) { this.formGroup.get('autoDeclineBudgetAssignmentDeclinationComment').setValue(declinationModal.comment); this.formGroup.get('autoDeclineBudgetAssignmentSendEmail').setValue(declinationModal.notifyApplicant); this.programService.setMapProperty( this.activeProgramId, 'autoDeclineBudgetAssignmentDeclinationComment', this.formGroup.value.autoDeclineBudgetAssignmentDeclinationComment ); this.programService.setMapProperty( this.activeProgramId, 'autoDeclineBudgetAssignmentSendEmail', this.formGroup.value.autoDeclineBudgetAssignmentSendEmail ); } } recipientChange () { const type = this.formGroup.value.applicantType; if ( this.isNomination && [ ProgramApplicantType.ORGS, ProgramApplicantType.ORGS_WITH_BUCKET ].includes(type) ? this.program.applicantType === ProgramApplicantType.INDIVIDUAL : this.program.applicantType !== ProgramApplicantType.INDIVIDUAL ) { this.programService.setMapProperty( this.activeProgramId, 'grantPrograms', [] ); } if (type === ProgramApplicantType.INDIVIDUAL) { this.programService.setMapProperty( this.activeProgramId, 'allowAddOrg', false ); } this.programService.setMapProperty( this.activeProgramId, 'applicantType', type ); if ( (type === ProgramApplicantType.ORGS) || (type === ProgramApplicantType.INDIVIDUAL) ) { this.formGroup.get('charityBucketId').setValue(null); this.formGroup.get('charityBucketDescriptions').setValue(''); this.setCharityBucketId(); this.setOrgSearchGuidelines(); this.maskApplicantInfoChange(); } } allowAddOrgChange () { this.programService.setMapProperty( this.activeProgramId, 'allowAddOrg', this.formGroup.value.allowAddOrg ); } maskApplicantInfoChange () { this.programService.setMapProperty( this.activeProgramId, 'maskApplicantInfo', this.formGroup.value.maskApplicantInfo ); } setOrgSearchGuidelines () { this.programService.setMapProperty( this.activeProgramId, 'charityBucketDescriptions', this.formGroup.value.charityBucketDescriptions || '' ); } setSenderDisplayName () { this.programService.setMapProperty( this.activeProgramId, 'senderDisplayName', this.formGroup.value.senderDisplayName ); } async addOrEditDeadline (row?: ProgramDeadline, index?: number) { const deadline = await this.modalFactory.open( ProgramDeadlineModalComponent, { deadline: row } ); if (deadline) { if (row) { this.programService.setMapProperty( this.activeProgramId, 'deadlines', [ ...this.program.deadlines.slice(0, index), { ...row, name: deadline.name, date: deadline.date.format(), description: deadline.description }, ...this.program.deadlines.slice(index + 1) ] ); } else { this.programService.setMapProperty( this.activeProgramId, 'deadlines', [ ...this.program.deadlines || [], { name: deadline.name, date: deadline.date.format(), description: deadline.description } ] ); } } } initializeCharityBucketId () { const noneSelected = { name: this.noneSelected, bucketId: '', charityCount: { numberOfCharities: 0, asOfDate: '1/1/2018' } }; if (this.program.charityBucketId) { const found = this.charityBucketsService.charityBuckets.find((bucket) => { return bucket.bucketId === this.program.charityBucketId; }); return found || noneSelected; } return noneSelected; } removeDeadline (index: number) { this.programService.setMapProperty( this.activeProgramId, 'deadlines', [ ...this.program.deadlines.slice(0, index), ...this.program.deadlines.slice(index + 1) ] ); } toggleDueDateFrequency (on = false, forReviewers = false) { const reminderName = forReviewers ? 'formDueReminderFrequencyForReviewer' : 'formDueReminderFrequencyForApplicant'; const overdueName = forReviewers ? 'formOverDueReminderFrequencyForReviewer' : 'formOverDueReminderFrequencyForApplicant'; if (on) { this.formGroup.get(reminderName).setValue( DEFAULT_FREQUENCY ); this.formGroup.get(overdueName).setValue( DEFAULT_FREQUENCY ); } else { this.formGroup.get(reminderName).setValue( null ); this.formGroup.get(overdueName).setValue( null ); } } setApplicantFrequency () { const formValue = this.formGroup.value; this.programService.setMapProperty( this.activeProgramId, 'formDueReminderFrequencyForApplicant', formValue.applicantFormDueReminder ? formValue.formDueReminderFrequencyForApplicant : null ); this.programService.setMapProperty( this.activeProgramId, 'formOverDueReminderFrequencyForApplicant', formValue.applicantFormDueReminder ? formValue.formOverDueReminderFrequencyForApplicant : null ); } setReviewerFrequency () { const formValue = this.formGroup.value; this.programService.setMapProperty( this.activeProgramId, 'formDueReminderFrequencyForReviewer', formValue.reviewerFormDueReminder ? formValue.formDueReminderFrequencyForReviewer : null ); this.programService.setMapProperty( this.activeProgramId, 'formOverDueReminderFrequencyForReviewer', formValue.reviewerFormDueReminder ? formValue.formOverDueReminderFrequencyForReviewer : null ); } }