diff --git a/modules/opportunities/client/views/cwu-opportunity-view.html b/modules/opportunities/client/views/cwu-opportunity-view.html index e2ef6da43..81fdd9f80 100644 --- a/modules/opportunities/client/views/cwu-opportunity-view.html +++ b/modules/opportunities/client/views/cwu-opportunity-view.html @@ -129,9 +129,9 @@ -
+
- +
@@ -318,13 +318,6 @@

Got Questions?

Proposal Evaluation Criteria

Your Proposal will be scored by this criteria:

- - -
-
- -
-
diff --git a/modules/proposals/client/controllers/ProposalEditCWUController.ts b/modules/proposals/client/controllers/ProposalEditCWUController.ts index b0f35cc25..ad70368f6 100644 --- a/modules/proposals/client/controllers/ProposalEditCWUController.ts +++ b/modules/proposals/client/controllers/ProposalEditCWUController.ts @@ -1,7 +1,7 @@ 'use strict'; import { StateService } from '@uirouter/core'; -import angular, { angularFileUpload, IFormController, IRootScopeService, uiNotification } from 'angular'; +import angular, { angularFileUpload, IFormController, IRootScopeService, ui, uiNotification } from 'angular'; import moment from 'moment-timezone'; import { Settings } from 'tinymce'; import { IOpportunitiesService, IOpportunityResource } from '../../../opportunities/client/services/OpportunitiesService'; @@ -9,15 +9,14 @@ import { IOrgResource } from '../../../orgs/client/services/OrgService'; import { IAuthenticationService } from '../../../users/client/services/AuthenticationService'; import { IUserService } from '../../../users/client/services/UsersService'; import { IUser } from '../../../users/shared/IUserDTO'; +import { ProposalModalActions } from '../directives/ProposalApplyDirective'; import { IProposalResource, IProposalService } from '../services/ProposalService'; export default class ProposalEditCWUController { public static $inject = [ - 'editing', '$scope', 'ask', 'Upload', - '$state', 'proposal', 'opportunity', 'AuthenticationService', @@ -26,7 +25,8 @@ export default class ProposalEditCWUController { 'UsersService', 'Notification', 'org', - 'TinyMceConfiguration' + 'TinyMceConfiguration', + '$uibModalInstance' ]; public members: any[]; @@ -34,13 +34,13 @@ export default class ProposalEditCWUController { public proposalForm: IFormController; private user: IUser; + private isclosed: boolean; + private hasAttachments: boolean; constructor( - public editing: boolean, private $scope: IRootScopeService, private ask, private Upload: angularFileUpload.IUploadService, - private $state: StateService, public proposal: IProposalResource, public opportunity: IOpportunityResource, private AuthenticationService: IAuthenticationService, @@ -49,17 +49,20 @@ export default class ProposalEditCWUController { private UsersService: IUserService, private Notification: uiNotification.INotificationService, public org: IOrgResource, - public TinyMceConfiguration: Settings + public TinyMceConfiguration: Settings, + private $uibModalInstance: ui.bootstrap.IModalServiceInstance ) { - // if not editing (i.e. creating), ensure that the current user doesn't already have a proposal started for this opp - // if they do, transition to edit view for that proposal - this.checkForExisting(); - // refresh the view based on passed in proposal this.refreshProposal(this.proposal); // set the user this.user = this.AuthenticationService.user; + + this.init(); + } + + private async init(){ + this.isclosed = await this.isClosed(); } // Format dates to always be in PST (America/Vancouver timezone) @@ -92,31 +95,39 @@ export default class ProposalEditCWUController { // Save the proposal this.copyUserInfo(); - let updatedProposal: IProposalResource; - if (this.editing) { - updatedProposal = await this.ProposalService.update(this.proposal).$promise; - } else { - updatedProposal = await this.ProposalService.create(this.proposal).$promise; + if (this.proposal.status === 'New') { + this.proposal.status = 'Draft'; } + const updatedProposal = await this.ProposalService.update(this.proposal).$promise; - this.refreshProposal(updatedProposal); this.Notification.success({ message: ` ${successMessage}` }); - this.proposalForm.$setPristine(); - // if this is a newly created proposal, transition to edit view - if (!this.editing) { - this.$state.go('proposaladmin.editcwu', { proposalId: this.proposal._id, opportunityId: this.opportunity.code }); - } + // close the modal and include the proposal in the response + this.$uibModalInstance.close({ + action: ProposalModalActions.SAVED, + proposal: updatedProposal + }); } catch (error) { this.handleError(error); } } - // Leave the edit view - public close(): void { - this.$state.go('opportunities.viewcwu', { opportunityId: this.opportunity.code }); + // Leave the edit view and save any changes made if needed + public async close(): Promise { + + // If looking at a proposal for a closed opportunity, simply close the modal + if(this.isclosed) { + this.$uibModalInstance.close({ + action: ProposalModalActions.SAVED, + proposal: this.proposal + }); + + // If looking at a proposal for an open opportunity, save any changes made + } else { + this.save(true); + } } // Delete a proposal @@ -128,10 +139,12 @@ export default class ProposalEditCWUController { try { await this.proposal.$remove(); this.proposalForm.$setPristine(); - this.$state.go('opportunities.viewcwu', { opportunityId: this.opportunity.code }); this.Notification.success({ message: ' Proposal Deleted' }); + this.$uibModalInstance.close({ + action: ProposalModalActions.DELETED + }); } catch (error) { this.handleError(error); } @@ -162,11 +175,15 @@ export default class ProposalEditCWUController { // Submit the proposal this.copyUserInfo(); - await this.ProposalService.submit(this.proposal).$promise; + const submittedProposal = await this.ProposalService.submit(this.proposal).$promise; this.Notification.success({ message: ' Your proposal has been submitted' }); - this.close(); + + this.$uibModalInstance.close({ + action: ProposalModalActions.SAVED, + proposal: submittedProposal + }); } catch (error) { this.handleError(error); } @@ -202,7 +219,7 @@ export default class ProposalEditCWUController { message: ' Attachment Uploaded' }); - const updatedProposal = response.data as IProposalResource; + const updatedProposal = new this.ProposalService(response.data); this.refreshProposal(updatedProposal); } catch (error) { this.handleError(error); @@ -249,6 +266,24 @@ export default class ProposalEditCWUController { } } + // Set the terms to accepted and update the proposal + public async acceptTerms(): Promise{ + try{ + this.proposal.isAcceptedTerms = true; + const updatedProposal = await this.ProposalService.update(this.proposal).$promise; + this.refreshProposal(updatedProposal); + }catch(error){ + this.handleError(error); + } + } + + // Determine whether the deadline for the opportunity has passed + public async isClosed(){ + const response = await this.OpportunitiesService.getDeadlineStatus({ opportunityId: this.opportunity._id }).$promise; + return response.deadlineStatus === 'CLOSED'; + } + + // Determine whether the deadline for the opportunity has passed and send an error if it has private async checkDeadline(): Promise { // Check with server to ensure deadline hasn't passed const response = await this.OpportunitiesService.getDeadlineStatus({ opportunityId: this.opportunity._id }).$promise; @@ -263,16 +298,6 @@ export default class ProposalEditCWUController { } } - private async checkForExisting(): Promise { - if (!this.editing) { - const myProposal = await this.ProposalService.getMyProposal({ opportunityId: this.opportunity.code }).$promise; - - if (myProposal && myProposal._id) { - this.$state.go('proposaladmin.editcwu', { proposalId: myProposal._id, opportunityId: this.opportunity.code }); - } - } - } - private refreshProposal(newProposal: IProposalResource): void { this.proposal = newProposal; @@ -282,15 +307,12 @@ export default class ProposalEditCWUController { this.members = this.org.members.concat(this.org.admins); } - this.title = this.editing ? 'Edit' : 'Create'; + this.title = 'Edit'; if (!this.proposal.team) { this.proposal.team = []; } - // ensure status set accordingly - if (!this.editing) { - this.proposal.status = 'New'; - } + this.hasAttachments = this.proposal.attachments.length>0; } // Copy over user and org information to the proposal diff --git a/modules/proposals/client/directives/ProposalApplyDirective.ts b/modules/proposals/client/directives/ProposalApplyDirective.ts index 3b3b453de..606b8a5b6 100644 --- a/modules/proposals/client/directives/ProposalApplyDirective.ts +++ b/modules/proposals/client/directives/ProposalApplyDirective.ts @@ -1,17 +1,21 @@ 'use strict'; +import { StateParams } from '@uirouter/core'; import angular, { IController, IScope } from 'angular'; import moment from 'moment-timezone'; -import { IOpportunityResource } from '../../../opportunities/client/services/OpportunitiesService'; +import { IOpportunitiesService, IOpportunityResource } from '../../../opportunities/client/services/OpportunitiesService'; import { IOrgCommonService } from '../../../orgs/client/services/OrgCommonService'; -import { IOrgResource } from '../../../orgs/client/services/OrgService'; +import { IOrgResource, IOrgService } from '../../../orgs/client/services/OrgService'; import { IAuthenticationService } from '../../../users/client/services/AuthenticationService'; -import { IProposalResource } from '../services/ProposalService'; +import { IUser } from '../../../users/shared/IUserDTO'; +import ProposalEditCWUController from '../controllers/ProposalEditCWUController'; +import { IProposalResource, IProposalService } from '../services/ProposalService'; interface IProposalApplyScope extends IScope { opportunity: IOpportunityResource; proposal?: IProposalResource; org: IOrgResource; + isclosed: boolean; } enum UserStates { @@ -22,23 +26,115 @@ enum UserStates { NOTHING } +export enum ProposalModalActions { + CANCELLED = 0, + SAVED, + DELETED +} + export class ProposalApplyDirectiveController implements IController { - public static $inject = ['$scope', 'AuthenticationService', 'OrgCommonService']; + public static $inject = ['$scope', 'AuthenticationService', 'OrgCommonService', 'modalService']; public opportunity: IOpportunityResource; + public isclosed: boolean; public proposal: IProposalResource; public org: IOrgResource; + public user: IUser; public userState: UserStates; public userStates = UserStates; - constructor($scope: IProposalApplyScope, AuthenticationService: IAuthenticationService, private OrgCommonService: IOrgCommonService) { + constructor(private $scope: IProposalApplyScope, private AuthenticationService: IAuthenticationService, private OrgCommonService: IOrgCommonService, private modalService: any) { this.opportunity = $scope.opportunity; + this.isclosed = $scope.isclosed; this.proposal = $scope.proposal; this.org = $scope.org; this.userState = this.userStates.NOTHING; + this.user = this.AuthenticationService.user; + this.refreshDirective(); + } + + public async openProposalApplicationDialog(proposalId?: string): Promise { + + const modalResponse = await this.modalService.showModal({ + size: 'md', + templateUrl: '/modules/proposals/client/views/cwu-proposal-edit.html', + controller: ProposalEditCWUController, + controllerAs: 'ppp', + resolve: { + proposal: [ + 'ProposalService', + async (ProposalService: IProposalService) => { + if (!proposalId) { + // create a new proposal, and immediately save, the proposal edit controller will handle cleaning up if the user opts not to save their draft + const proposal = new ProposalService(); + proposal.opportunity = this.opportunity; + + proposal.businessName = this.user.businessName; + proposal.businessAddress = this.user.businessAddress; + proposal.businessContactName = this.user.businessContactName; + proposal.businessContactEmail = this.user.businessContactEmail; + proposal.businessContactPhone = this.user.businessContactPhone; + const newProposal = await ProposalService.create(proposal).$promise; + return newProposal; + } else { + return ProposalService.get({ proposalId }).$promise; + } + } + ], + opportunity: [ + '$stateParams', + 'OpportunitiesService', + ($stateParams: StateParams, OpportunitiesService: IOpportunitiesService) => { + return OpportunitiesService.get({ + opportunityId: $stateParams.opportunityId + }).$promise; + } + ], + org: [ + 'AuthenticationService', + 'OrgService', + (AuthenticationService: IAuthenticationService, OrgService: IOrgService) => { + if (!AuthenticationService.user) { + return {}; + } + return OrgService.myadmin().$promise.then(orgs => { + if (orgs && orgs.length > 0) { + return orgs[0]; + } else { + return null; + } + }); + } + ] + } + }); + + let action: ProposalModalActions = ProposalModalActions.CANCELLED; + if (modalResponse && modalResponse.action) { + action = modalResponse.action; + } + + if (action === ProposalModalActions.SAVED) { + this.$scope.proposal = modalResponse.proposal; + } else { + this.$scope.proposal = null; + } + + this.refreshDirective(); + } + + // Format dates to always be in PST (America/Vancouver timezone) + public formatDate(date: string, includeTime: boolean): string { + const momentDate = moment(date); + const dateFormat = includeTime ? 'MMMM Do YYYY, HH:mm z' : 'MMMM Do YYYY'; + return momentDate.tz('America/Vancouver').format(dateFormat); + } + + private refreshDirective(): void { + this.proposal = this.$scope.proposal; - const isUser = !!AuthenticationService.user; - const isAdmin = isUser && AuthenticationService.user.roles.includes('admin'); - const isGov = isUser && AuthenticationService.user.roles.includes('gov'); + const isUser = !!this.AuthenticationService.user; + const isAdmin = isUser && this.AuthenticationService.user.roles.includes('admin'); + const isGov = isUser && this.AuthenticationService.user.roles.includes('gov'); const isProposal = this.proposal && this.proposal._id; const canEdit = !isAdmin && !isGov; @@ -54,13 +150,6 @@ export class ProposalApplyDirectiveController implements IController { } } } - - // Format dates to always be in PST (America/Vancouver timezone) - public formatDate(date: string, includeTime: boolean): string { - const momentDate = moment(date); - const dateFormat = includeTime ? 'MMMM Do YYYY, HH:mm z' : 'MMMM Do YYYY'; - return momentDate.tz('America/Vancouver').format(dateFormat); - } } angular.module('proposals').directive('proposalApply', () => { @@ -70,6 +159,7 @@ angular.module('proposals').directive('proposalApply', () => { templateUrl: '/modules/proposals/client/views/proposal-apply.directive.html', scope: { opportunity: '=', + isclosed: '=', proposal: '=', org: '=' }, diff --git a/modules/proposals/client/views/cwu-proposal-edit-attachments.html b/modules/proposals/client/views/cwu-proposal-edit-attachments.html index d78181472..3f6b73d62 100644 --- a/modules/proposals/client/views/cwu-proposal-edit-attachments.html +++ b/modules/proposals/client/views/cwu-proposal-edit-attachments.html @@ -1,22 +1,26 @@ -
-
Upload any supporting material here.
-
- Upload a - file   Max 3 MB per file +