import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Validators } from '@angular/forms'; import { SpinnerService } from '@core/services/spinner.service'; import { ApplicationAttachmentService } from '@features/application-view/application-attachments/application-attachments.service'; import { AttachmentType } from '@features/application-view/application-attachments/application-attachments.typing'; import { CommunicationsService } from '@features/communications/communications.service'; import { TypeaheadSelectOption, TypeSafeFormBuilder, TypeSafeFormGroup, validMomentValidator, YcFile } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { YCModalComponent } from '@yourcause/common/modals'; import moment from 'moment'; import { Subscription } from 'rxjs'; import { Communication, CommunicationTypes, CommunicationVisibility } from '../communications.typing'; export interface AddEditCommunicationFormGroup { type: CommunicationTypes; subject: string; files: YcFile[]; date: string; content: string; visibility: CommunicationVisibility; publishToNonprofit: boolean; } @Component({ selector: 'yc-add-edit-communication-modal', templateUrl: 'add-edit-communication-modal.component.html' }) export class AddEditCommunicationComponent extends YCModalComponent implements OnInit, OnDestroy { @Input() isForNonprofit = false; @Input() communication: Communication; @Input() applicationId: number; @Input() isNomination = false; @Input() updateViewPermissionsOnly = false; characterLimit = 3000; contentRows = 6; saving = false; sub = new Subscription(); communicationTypeOptions: TypeaheadSelectOption[] = [{ label: this.i18n.translate( 'GLOBAL:textExternalCommunication', {}, 'External communication' ), value: CommunicationTypes.EXTERNAL_COMMUNICATION }, { label: this.i18n.translate( 'GLOBAL:textDocumentation', {}, 'Documentation' ), value: CommunicationTypes.DOCUMENTATION }, { label: this.i18n.translate( 'GLOBAL:textMeetingAndInteraction', {}, 'Meeting and interaction' ), value: CommunicationTypes.MEETING_AND_INTERACTION }]; communicationVisibilityOptions: TypeaheadSelectOption[] = []; formGroup: TypeSafeFormGroup; constructor ( private formBuilder: TypeSafeFormBuilder, private i18n: I18nService, private spinnerService: SpinnerService, private applicationAttachmentService: ApplicationAttachmentService, private communicationService: CommunicationsService ) { super(); } async ngOnInit () { this.setCommunicationVisibilityOptions(); const files = await this.getFile(); this.formGroup = this.setAndGetFormGroup(files); } setCommunicationVisibilityOptions () { this.communicationVisibilityOptions = [{ label: this.i18n.translate( this.isNomination ? 'nonprofits:textAllManagersOnNomination' : 'nonprofits:textAllManagersOnApplication', {}, `All grant managers on ${ this.isNomination ? 'nomination' : 'application' }` ), value: CommunicationVisibility.ALL_GRANT_MANAGERS }, { label: this.i18n.translate( 'nonprofits:textManagersInWorkflow', {}, 'Grant managers in current workflow level' ), value: CommunicationVisibility.MANAGERS_IN_LEVEL }, { label: this.i18n.translate( 'nonprofits:textOnlyMe', {}, 'Only me' ), value: CommunicationVisibility.ONLY_ME }]; } async getFile () { const files: YcFile[] = []; if (this.communication?.files.length) { this.spinnerService.startSpinner(); await Promise.all(this.communication.files.map(async (file) => { let accessUrl = ''; if (this.isForNonprofit) { accessUrl = await this.communicationService.getAccessUrlForNonprofitCommunication( this.communication.joinCommunicationId, file.fileUploadId ); } else { accessUrl = await this.applicationAttachmentService.getAccessUrlForManager( file.fileUploadId, this.applicationId, AttachmentType.COMMUNICATION ); } files.push( new YcFile( file.fileName, null, accessUrl, file.fileUploadId )); })); this.spinnerService.stopSpinner(); } return files; } async openFile ( file: YcFile, download: boolean ) { this.spinnerService.startSpinner(); if (download) { await this.applicationAttachmentService.downloadCommunicationFile({ file, isForNonprofit: this.isForNonprofit, applicationId: this.applicationId }); } else { await this.applicationAttachmentService.openCommunicationFile({ file, isForNonprofit: this.isForNonprofit, applicationId: this.applicationId }); } this.spinnerService.stopSpinner(); } private setAndGetFormGroup ( files: YcFile[] ): TypeSafeFormGroup { const publishToNonprofit = this.isForNonprofit ? true : this.communication?.publishToNonprofit; const dateValue = this.communication?.date ?? moment().toString(); return this.formBuilder.group({ type: [this.communication?.type || CommunicationTypes.EXTERNAL_COMMUNICATION, Validators.required], subject: [this.communication?.subject || '', Validators.required], date: [ dateValue, [Validators.required, validMomentValidator()] ], content: [this.communication?.content || '', Validators.required], files: [files || []], visibility: [this.communication?.visibility || CommunicationVisibility.ALL_GRANT_MANAGERS, Validators.required], publishToNonprofit: [publishToNonprofit] }); } handleUploadRequest (input: YcFile) { input.setStatusText( this.i18n.translate( 'common:textFileWillUploadOnSave', {}, 'File will upload on save' ) ); this.formGroup.get('files').setValue([ input, ...this.formGroup.get('files').value ]); } handleRemoveFile (file: YcFile) { const newData = (this.formGroup.get('files').value) .filter(previousFile => { return previousFile !== file; }); this.formGroup.get('files').setValue(newData); } async save () { this.saving = true; this.spinnerService.startSpinner(); const formGroupValue = this.formGroup.get('files').value; const oldFiles = formGroupValue.filter((file) => !!file.fileUploadId).map((file) => { return { fileName: file.fileName, fileUploadId: file.fileUploadId }; }); const newFiles = formGroupValue.filter((file) => !file.fileUploadId); const newFileUploads = await this.communicationService.handleMultipleCommFileUploads( newFiles ); this.spinnerService.stopSpinner(); if (newFileUploads) { this.closeModal.emit({ ...(this.communication || {}), ...this.formGroup.value, files: [ ...oldFiles, ...newFileUploads ] }); } this.saving = false; } ngOnDestroy () { this.sub.unsubscribe(); } }