import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, Validators } from '@angular/forms'; import { ApplicantService } from '@core/services/auth-user/applicant.service'; import { SpinnerService } from '@core/services/spinner.service'; import { ApplicationApplicantService } from '@features/apply/application-applicant.service'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { CollaborationService } from '@features/collaboration/collaboration.service'; import { UserService } from '@features/users/user.service'; import { SelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { YCModalComponent } from '@yourcause/common/modals'; import { NotifierService } from '@yourcause/common/notifier'; import { PopoverDirective } from 'ngx-bootstrap/popover'; import { Subscription } from 'rxjs'; import { PortalDeterminationService } from '../../services/portal-determination.service'; import { ApplicantForApi, ApplicantFromSearch } from '../../typings/applicant.typing'; import { Collaborator, CollaboratorPermissions } from '../../typings/collaboration.typing'; interface PermissionsFormGroup { permissions: CollaboratorPermissions[]; } interface CreateApplicantFormGroup { firstName: string; lastName: string; isEmployeeOfClient: boolean; } @Component({ selector: 'gc-collaboration-modal', templateUrl: './collaboration-modal.component.html', styleUrls: ['./collaboration-modal.component.scss'], encapsulation: ViewEncapsulation.None }) export class CollaborationModalComponent extends YCModalComponent implements OnInit, OnDestroy { @Input() applicationId: number; @Input() sendNotificationsByDefault = false; @Input() updateApplicationViewMap = false; @Input() isNomination = false; @Input() clientId: number; @Input() clientName: string; sub = new Subscription(); loaded = false; currentPopover: { instance?: PopoverDirective; row?: Collaborator; isPermissions?: boolean; } = {}; createApplicantFormGroup: TypeSafeFormGroup = this.formBuilder.group({ firstName: ['', Validators.required], lastName: ['', Validators.required], isEmployeeOfClient: false }); showCreate = false; editPermissionsFormGroups = new WeakMap>(); permissions = CollaboratorPermissions; applicants: Collaborator[]; selectedApplicant: ApplicantFromSearch; newApplicantEmailAddress = ''; permissionsFormGroup: TypeSafeFormGroup; isManager = this.determinationService.isManager; currentUserId = this.userService.currentUser.id; checkboxOptions: SelectOption[] = []; editCheckboxOptions: SelectOption[] = this.checkboxOptions; showEmployeeCheckbox = this.clientSettingsService.showIsEmployeeOfClientCheckbox; constructor ( private collaborationService: CollaborationService, private determinationService: PortalDeterminationService, private i18n: I18nService, private notifier: NotifierService, private formBuilder: TypeSafeFormBuilder, private applicantService: ApplicantService, private spinnerService: SpinnerService, private userService: UserService, private clientSettingsService: ClientSettingsService, private applicationApplicantService: ApplicationApplicantService ) { super(); this.sub.add( this.collaborationService.changesTo$('collaboratorMap') .subscribe(() => { const map = this.collaborationService.collaboratorMap; if (map[this.applicationId]) { this.setUpEditCheckboxes(); } this.applicants = map[this.applicationId] || []; }) ); } get defaultPermissions () { return this.sendNotificationsByDefault ? [ CollaboratorPermissions.RECEIVES_NOTIFICATIONS ] : []; } get isValidEmail (): boolean { return !Validators.email(new FormControl(this.newApplicantEmailAddress)); } get myPermissions () { return this.collaborators.find((collab) => collab.id === this.currentUserId); } get collaborators () { return this.collaborationService.collaboratorMap[this.applicationId] || []; } get canRemoveOthers () { return this.determinationService.isManager || ( this.collaborators.some(collab => ( collab.id === this.currentUserId ) && collab.canManageApplicants) ); } async ngOnInit () { this.spinnerService.startSpinner(); if (!this.clientName && this.isManager) { this.clientName = this.clientSettingsService.clientBranding.name; } this.setCheckboxOptions(); await this.collaborationService.fetchCollaborators(this.applicationId); if (this.determinationService.isManager || ( this.myPermissions.canManageApplicants || this.myPermissions.isApplicationOwner )) { this.permissionsFormGroup = this.formBuilder.group({ permissions: [ this.defaultPermissions ] }); } this.loaded = true; this.spinnerService.stopSpinner(); } setCheckboxOptions () { this.checkboxOptions = [{ display: this.i18n.translate( this.isNomination ? 'APPLY:lblCanManageNominators' : 'APPLY:lblCanManageApplicants', {}, this.isNomination ? 'Can manage nominators' : 'Can manage applicants' ), value: CollaboratorPermissions.CAN_MANAGE }, { display: this.i18n.translate( this.isNomination ? 'APPLY:lblReceivesNominationNotifications' : 'APPLY:lblReceivesNotifications', {}, this.isNomination ? 'Receives email notifications for nomination status updates' : 'Receives email notifications for application status updates' ), value: CollaboratorPermissions.RECEIVES_NOTIFICATIONS }]; } onNoResults (searchTerm: string) { this.showCreate = true; if (!searchTerm) { this.showCreate = false; } } setCurrentPopover ( row: Collaborator, popover: PopoverDirective, isPermissions: boolean ) { if (this.currentPopover.instance) { this.currentPopover.instance.hide(); } if (this.currentPopover.isPermissions) { this.resetFormGroup(this.currentPopover.row); } this.currentPopover.instance = popover; this.currentPopover.isPermissions = isPermissions; this.currentPopover.row = row; setTimeout(() => { popover.show(); }); } onTermChange (searchTerm: string) { this.newApplicantEmailAddress = searchTerm; if (!searchTerm && !this.selectedApplicant) { this.resetAddForm(); } } async createApplicant () { this.spinnerService.startSpinner(); const formGroupData = this.createApplicantFormGroup.value; const applicant = { firstName: formGroupData.firstName, lastName: formGroupData.lastName, email: this.newApplicantEmailAddress, phoneNumber: '', sendEmail: true, clientId: this.isManager ? null : (formGroupData.isEmployeeOfClient ? this.clientId : null), isEmployeeOfClient: this.clientSettingsService.getIsEmployeeOfClientForPayload( formGroupData.isEmployeeOfClient ) } as ApplicantForApi; const response = await this.applicantService.createApplicant(applicant); this.showCreate = false; this.createApplicantFormGroup.setValue({ firstName: '', lastName: '', isEmployeeOfClient: false }); this.selectedApplicant = { id: response.applicantId, phoneNumber: applicant.phoneNumber, firstName: (applicant.firstName || '').trim(), lastName: (applicant.lastName || '').trim(), email: applicant.email }; await this.addApplicant(); this.spinnerService.stopSpinner(); } createFormGroup (collaborator: Collaborator): TypeSafeFormGroup { const permissions = []; if (collaborator.canManageApplicants) { permissions.push(CollaboratorPermissions.CAN_MANAGE); } if (collaborator.canReceiveEmails) { permissions.push(CollaboratorPermissions.RECEIVES_NOTIFICATIONS); } if (collaborator.isApplicationOwner) { permissions.push(CollaboratorPermissions.CURRENT_OWNER); } return this.formBuilder.group({ permissions: [permissions] }); } private setUpEditCheckboxes () { if ( (this.collaborators.length !== 1) && (this.isManager || this.myPermissions.isApplicationOwner) ) { this.editCheckboxOptions = this.checkboxOptions.concat([ { display: this.i18n.translate('APPLY:lblCurrentOwner', {}, 'Current owner'), value: CollaboratorPermissions.CURRENT_OWNER } ]); } else { this.editCheckboxOptions = this.checkboxOptions; } } resetFormGroup (collaborator: Collaborator) { this.editPermissionsFormGroups.set(collaborator, this.createFormGroup(collaborator)); this.currentPopover = {}; } getFormGroup (collaborator: Collaborator) { let existingFormGroup = this.editPermissionsFormGroups.get(collaborator); if (!existingFormGroup) { existingFormGroup = this.createFormGroup(collaborator); this.editPermissionsFormGroups.set(collaborator, existingFormGroup); } return existingFormGroup; } async updateCollaboratorPermissions (collaborator: Collaborator) { this.spinnerService.startSpinner(); const formGroup = this.getFormGroup(collaborator); const permissions = formGroup.value.permissions; const canManageApplicants = permissions.includes( CollaboratorPermissions.CAN_MANAGE ); const canReceiveEmails = permissions.includes( CollaboratorPermissions.RECEIVES_NOTIFICATIONS ); if (permissions.includes(CollaboratorPermissions.CURRENT_OWNER)) { await this.collaborationService.makeCollaboratorOwner( this.applicationId, collaborator.id, this.updateApplicationViewMap ); } await this.collaborationService.updateCollaboratorPermissions( this.applicationId, { applicantId: collaborator.id, canManageApplicants, canReceiveEmails }); if ( collaborator.id === this.currentUserId && !permissions.includes(CollaboratorPermissions.CAN_MANAGE) ) { this.closeModal.emit(); } this.spinnerService.stopSpinner(); } selectApplicant (selectedApplicant: ApplicantFromSearch) { if (!this.collaborators.some(collab => collab.email === selectedApplicant.email)) { this.selectedApplicant = selectedApplicant; this.showCreate = false; } else { this.notifier.error(this.i18n.translate( 'APPLY:notificationApplicantAlreadyAttached', {}, 'This applicant has already been attached to this application' )); } } resetAddForm () { this.selectedApplicant = null; this.showCreate = false; this.permissionsFormGroup.get('permissions').setValue(this.defaultPermissions); this.createApplicantFormGroup.setValue({ firstName: '', lastName: '', isEmployeeOfClient: false }); setTimeout(() => { this.selectedApplicant = null; }); } async addApplicant () { this.spinnerService.startSpinner(); const selectedPermissions = this.permissionsFormGroup.value.permissions; await this.collaborationService.addCollaborator(this.applicationId, { applicantId: this.selectedApplicant.id, canManageApplicants: selectedPermissions.includes( CollaboratorPermissions.CAN_MANAGE ), canReceiveEmails: selectedPermissions.includes( CollaboratorPermissions.RECEIVES_NOTIFICATIONS ) }); this.resetAddForm(); this.spinnerService.stopSpinner(); } async removeCollaborator (applicantId: number) { this.spinnerService.startSpinner(); const currentUserRemoved = !this.determinationService.isManager && (applicantId === this.currentUserId); await this.collaborationService.detachCollaborator( this.applicationId, applicantId, currentUserRemoved ); if (currentUserRemoved) { this.applicationApplicantService.removeFromMyApplications(this.applicationId); this.closeModal.emit(); } this.spinnerService.stopSpinner(); } ngOnDestroy () { this.sub.unsubscribe(); } }