From 4b3b1a4b0f931b7ee959e66f1f73ed6070e2cea6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 12:35:42 -0600 Subject: [PATCH 1/8] chore: remove max budget hint from twu proposal view --- .../pages/proposal/team-with-us/lib/components/form.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/front-end/typescript/lib/pages/proposal/team-with-us/lib/components/form.tsx b/src/front-end/typescript/lib/pages/proposal/team-with-us/lib/components/form.tsx index b69b83dc9..39c274101 100644 --- a/src/front-end/typescript/lib/pages/proposal/team-with-us/lib/components/form.tsx +++ b/src/front-end/typescript/lib/pages/proposal/team-with-us/lib/components/form.tsx @@ -24,7 +24,7 @@ import Markdown, { ProposalMarkdown } from "front-end/lib/views/markdown"; import { find } from "lodash"; import React from "react"; import { Alert, Col, Row } from "reactstrap"; -import { formatAmount, formatDate } from "shared/lib"; +import { formatDate } from "shared/lib"; import { isTWUOpportunityAcceptingProposals, TWUOpportunity @@ -683,7 +683,6 @@ const PricingView: component_.base.View = ({ dispatch, disabled }) => { - const { maxBudget } = state.opportunity; return (
@@ -751,10 +750,6 @@ const PricingView: component_.base.View = ({ extraChildProps={{ prefix: "$" }} label="Hourly Rate" placeholder="Hourly Rate" - hint={`Maximum opportunity budget is ${formatAmount( - maxBudget, - "$" - )}`} state={state.hourlyRate} dispatch={component_.base.mapDispatch(dispatch, (value) => adt("hourlyRate" as const, value) From 52b17f8e3cad72f3c274b2972d630e017011ad44 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 16:01:47 -0600 Subject: [PATCH 2/8] fix: remove unused/incorrect join for proposal retrieval --- src/back-end/lib/db/proposal/sprint-with-us.ts | 6 ------ src/back-end/lib/db/proposal/team-with-us.ts | 6 ------ 2 files changed, 12 deletions(-) diff --git a/src/back-end/lib/db/proposal/sprint-with-us.ts b/src/back-end/lib/db/proposal/sprint-with-us.ts index 8f786f02d..650105fe5 100644 --- a/src/back-end/lib/db/proposal/sprint-with-us.ts +++ b/src/back-end/lib/db/proposal/sprint-with-us.ts @@ -1892,12 +1892,6 @@ async function calculateScores( const proposalScorings = await generateSWUProposalQuery(connection) .where({ "proposals.opportunity": opportunityId }) - .join( - "swuTeamQuestionResponses as responses", - "responses.proposal", - "=", - "proposals.id" - ) .select( "proposals.id", "statuses.status", diff --git a/src/back-end/lib/db/proposal/team-with-us.ts b/src/back-end/lib/db/proposal/team-with-us.ts index ff4c03596..366f32bf4 100644 --- a/src/back-end/lib/db/proposal/team-with-us.ts +++ b/src/back-end/lib/db/proposal/team-with-us.ts @@ -1547,12 +1547,6 @@ async function calculateScores( const proposalScorings = await generateTWUProposalQuery(connection) .where({ "proposals.opportunity": opportunityId }) - .join( - "twuResourceQuestionResponses as responses", - "responses.proposal", - "=", - "proposals.id" - ) .select( "proposals.id", "statuses.status", From 66a5397b3a31845f1ac04cce6556c3a491ca2b14 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 16:10:43 -0600 Subject: [PATCH 3/8] fix: adjust ordinal suffix for number amounts in 'teens' --- src/shared/lib/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shared/lib/index.ts b/src/shared/lib/index.ts index 8e6042a46..179965f12 100644 --- a/src/shared/lib/index.ts +++ b/src/shared/lib/index.ts @@ -92,6 +92,8 @@ export function flipCurried( } export function getOrdinalSuffix(position: number): string { + if (position > 10 && position < 20) return "th"; // teens are all "th" + switch (position % 10) { case 1: return "st"; From def38316c80a7f695f37fc21abdc1aab97d2ac03 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 16:44:15 -0600 Subject: [PATCH 4/8] chore: disallow proposal withdrawals after opportunity deadline --- .../lib/resources/proposal/code-with-us.ts | 23 ++++------ .../lib/resources/proposal/sprint-with-us.ts | 42 +++++++------------ .../lib/resources/proposal/team-with-us.ts | 30 +++++-------- 3 files changed, 32 insertions(+), 63 deletions(-) diff --git a/src/shared/lib/resources/proposal/code-with-us.ts b/src/shared/lib/resources/proposal/code-with-us.ts index a179c23a9..2da5ed3cf 100644 --- a/src/shared/lib/resources/proposal/code-with-us.ts +++ b/src/shared/lib/resources/proposal/code-with-us.ts @@ -319,36 +319,29 @@ export function isValidStatusChange( case CWUProposalStatus.UnderReview: return ( - (([ - CWUProposalStatus.Evaluated, - CWUProposalStatus.Disqualified - ].includes(to) && - userType !== UserType.Vendor) || - (to === CWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + [CWUProposalStatus.Evaluated, CWUProposalStatus.Disqualified].includes( + to + ) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case CWUProposalStatus.Evaluated: return ( - (([ + [ CWUProposalStatus.Evaluated, CWUProposalStatus.Awarded, CWUProposalStatus.NotAwarded, CWUProposalStatus.Disqualified ].includes(to) && - userType !== UserType.Vendor) || - (to === CWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case CWUProposalStatus.Awarded: return ( - ((to === CWUProposalStatus.Disqualified && - userType !== UserType.Vendor) || - (to === CWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + to === CWUProposalStatus.Disqualified && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); diff --git a/src/shared/lib/resources/proposal/sprint-with-us.ts b/src/shared/lib/resources/proposal/sprint-with-us.ts index ff3a65c30..d99bf257b 100644 --- a/src/shared/lib/resources/proposal/sprint-with-us.ts +++ b/src/shared/lib/resources/proposal/sprint-with-us.ts @@ -425,77 +425,63 @@ export function isValidStatusChange( case SWUProposalStatus.UnderReviewTeamQuestions: return ( - (([ + [ SWUProposalStatus.EvaluatedTeamQuestions, SWUProposalStatus.Disqualified ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case SWUProposalStatus.EvaluatedTeamQuestions: return ( - ([ + [ SWUProposalStatus.UnderReviewCodeChallenge, SWUProposalStatus.Disqualified - ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case SWUProposalStatus.UnderReviewCodeChallenge: return ( - ([ + [ SWUProposalStatus.EvaluatedCodeChallenge, SWUProposalStatus.Disqualified, SWUProposalStatus.EvaluatedTeamQuestions - ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case SWUProposalStatus.EvaluatedCodeChallenge: return ( - ([ + [ SWUProposalStatus.UnderReviewTeamScenario, SWUProposalStatus.Disqualified - ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case SWUProposalStatus.UnderReviewTeamScenario: return ( - ([ + [ SWUProposalStatus.EvaluatedTeamScenario, SWUProposalStatus.Disqualified, SWUProposalStatus.EvaluatedCodeChallenge - ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case SWUProposalStatus.EvaluatedTeamScenario: return ( - (([ + [ SWUProposalStatus.Awarded, SWUProposalStatus.NotAwarded, SWUProposalStatus.Disqualified ].includes(to) && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case SWUProposalStatus.Awarded: return ( - ((to === SWUProposalStatus.Disqualified && - userType !== UserType.Vendor) || - (to === SWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + to === SWUProposalStatus.Disqualified && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); diff --git a/src/shared/lib/resources/proposal/team-with-us.ts b/src/shared/lib/resources/proposal/team-with-us.ts index 228489453..4a1fe2c0e 100644 --- a/src/shared/lib/resources/proposal/team-with-us.ts +++ b/src/shared/lib/resources/proposal/team-with-us.ts @@ -317,56 +317,46 @@ export function isValidStatusChange( case TWUProposalStatus.UnderReviewResourceQuestions: return ( - (([ + [ TWUProposalStatus.EvaluatedResourceQuestions, TWUProposalStatus.Disqualified ].includes(to) && - userType !== UserType.Vendor) || - (to === TWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case TWUProposalStatus.EvaluatedResourceQuestions: return ( - ([ + [ TWUProposalStatus.UnderReviewChallenge, TWUProposalStatus.Disqualified - ].includes(to) && - userType !== UserType.Vendor) || - (to === TWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case TWUProposalStatus.UnderReviewChallenge: return ( - ([ + [ TWUProposalStatus.EvaluatedChallenge, TWUProposalStatus.Disqualified, TWUProposalStatus.EvaluatedResourceQuestions - ].includes(to) && - userType !== UserType.Vendor) || - (to === TWUProposalStatus.Withdrawn && userType === UserType.Vendor) + ].includes(to) && userType !== UserType.Vendor ); case TWUProposalStatus.EvaluatedChallenge: return ( - (([ + [ TWUProposalStatus.Awarded, TWUProposalStatus.NotAwarded, TWUProposalStatus.Disqualified ].includes(to) && - userType !== UserType.Vendor) || - (to === TWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); case TWUProposalStatus.Awarded: return ( - ((to === TWUProposalStatus.Disqualified && - userType !== UserType.Vendor) || - (to === TWUProposalStatus.Withdrawn && - userType === UserType.Vendor)) && + to === TWUProposalStatus.Disqualified && + userType !== UserType.Vendor && hasProposalDeadlinePassed ); From 1f3063984a59bc36d41b411af1ae4b9880307ba5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 17:02:25 -0600 Subject: [PATCH 5/8] chore: remove withdraw button from proposal view where applicable --- .../proposal/code-with-us/edit/tab/proposal.tsx | 14 +------------- .../proposal/sprint-with-us/edit/tab/proposal.tsx | 14 +------------- .../proposal/team-with-us/edit/tab/proposal.tsx | 14 +------------- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/front-end/typescript/lib/pages/proposal/code-with-us/edit/tab/proposal.tsx b/src/front-end/typescript/lib/pages/proposal/code-with-us/edit/tab/proposal.tsx index 0f03c8297..4c6da1539 100644 --- a/src/front-end/typescript/lib/pages/proposal/code-with-us/edit/tab/proposal.tsx +++ b/src/front-end/typescript/lib/pages/proposal/code-with-us/edit/tab/proposal.tsx @@ -1169,19 +1169,7 @@ export const component: Tab.Component = { case CWUProposalStatus.UnderReview: case CWUProposalStatus.Evaluated: case CWUProposalStatus.Awarded: - return component_.page.actions.links([ - { - children: "Withdraw", - symbol_: leftPlacement(iconLinkSymbol("ban")), - button: true, - outline: true, - color: "c-nav-fg-alt", - disabled, - loading: isWithdrawLoading, - onClick: () => - dispatch(adt("showModal", "withdrawAfterDeadline" as const)) - } - ]); + return component_.page.actions.none(); case CWUProposalStatus.Withdrawn: if (isAcceptingProposals) { return component_.page.actions.links([ diff --git a/src/front-end/typescript/lib/pages/proposal/sprint-with-us/edit/tab/proposal.tsx b/src/front-end/typescript/lib/pages/proposal/sprint-with-us/edit/tab/proposal.tsx index 2ad5c87b4..2138a78b0 100644 --- a/src/front-end/typescript/lib/pages/proposal/sprint-with-us/edit/tab/proposal.tsx +++ b/src/front-end/typescript/lib/pages/proposal/sprint-with-us/edit/tab/proposal.tsx @@ -1128,19 +1128,7 @@ export const component: Tab.Component = { case SWUProposalStatus.EvaluatedCodeChallenge: case SWUProposalStatus.EvaluatedTeamScenario: case SWUProposalStatus.Awarded: - return component_.page.actions.links([ - { - children: "Withdraw", - symbol_: leftPlacement(iconLinkSymbol("ban")), - button: true, - outline: true, - color: "c-nav-fg-alt", - disabled, - loading: isWithdrawLoading, - onClick: () => - dispatch(adt("showModal", "withdrawAfterDeadline" as const)) - } - ]); + return component_.page.actions.none(); case SWUProposalStatus.Withdrawn: if (isAcceptingProposals) { return component_.page.actions.links([ diff --git a/src/front-end/typescript/lib/pages/proposal/team-with-us/edit/tab/proposal.tsx b/src/front-end/typescript/lib/pages/proposal/team-with-us/edit/tab/proposal.tsx index 4c0522269..d6febbb49 100644 --- a/src/front-end/typescript/lib/pages/proposal/team-with-us/edit/tab/proposal.tsx +++ b/src/front-end/typescript/lib/pages/proposal/team-with-us/edit/tab/proposal.tsx @@ -1146,19 +1146,7 @@ export const component: Tab.Component = { case TWUProposalStatus.EvaluatedResourceQuestions: case TWUProposalStatus.EvaluatedChallenge: case TWUProposalStatus.Awarded: - return component_.page.actions.links([ - { - children: "Withdraw", - symbol_: leftPlacement(iconLinkSymbol("ban")), - button: true, - outline: true, - color: "c-nav-fg-alt", - disabled, - loading: isWithdrawLoading, - onClick: () => - dispatch(adt("showModal", "withdrawAfterDeadline" as const)) - } - ]); + return component_.page.actions.none(); case TWUProposalStatus.Withdrawn: if (isAcceptingProposals) { return component_.page.actions.links([ From 9f6f13a80e800426d80d8688253e7d1f3b3955c8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 17:32:42 -0600 Subject: [PATCH 6/8] chore: adjust audience for withdrawal notifications --- .../notifications/proposal/code-with-us.tsx | 43 ++++++++---------- .../notifications/proposal/sprint-with-us.tsx | 45 +++++++++---------- .../notifications/proposal/team-with-us.tsx | 43 ++++++++---------- 3 files changed, 58 insertions(+), 73 deletions(-) diff --git a/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx index 6d9e6c8f7..53297d2bc 100644 --- a/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates"; import { makeSend } from "back-end/lib/mailer/transport"; import React from "react"; import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config"; -import { - CWUOpportunity, - isCWUOpportunityClosed -} from "shared/lib/resources/opportunity/code-with-us"; +import { CWUOpportunity } from "shared/lib/resources/opportunity/code-with-us"; import { CWUProposal, CWUProposalSlim } from "shared/lib/resources/proposal/code-with-us"; import { AuthenticatedSession } from "shared/lib/resources/session"; -import { User } from "shared/lib/resources/user"; +import { User, UserType } from "shared/lib/resources/user"; import { Id } from "shared/lib/types"; import { getValidValue } from "shared/lib/validation"; @@ -155,13 +152,6 @@ export async function handleCWUProposalWithdrawn( ), null ); - // Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself - const opportunityAuthor = - proposal && - getValidValue( - await db.readOneCWUOpportunityAuthor(connection, proposal.opportunity.id), - null - ); if (proposal && opportunity) { const withdrawnProponent = @@ -170,24 +160,29 @@ export async function handleCWUProposalWithdrawn( await db.readOneUser(connection, proposal.createdBy.id), null ); - // Notify opportunity author if opportunity is closed - if ( - opportunityAuthor && - withdrawnProponent && - isCWUOpportunityClosed(opportunity) - ) { - await withdrawnCWUProposalSubmission( - opportunityAuthor, - withdrawnProponent, - opportunity - ); - } // Notify proposal author if (withdrawnProponent) { await withdrawnCWUProposalSubmissionProposalAuthor( withdrawnProponent, opportunity ); + + // Notify admins that the proposal has been withdrawn + const adminUsers = + getValidValue( + await db.readManyUsersByRole(connection, UserType.Admin), + null + ) || []; + await Promise.all( + adminUsers.map( + async (admin) => + await withdrawnCWUProposalSubmission( + admin, + withdrawnProponent, + opportunity + ) + ) + ); } } } diff --git a/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx index 3c4bd80d3..7838441c4 100644 --- a/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates"; import { makeSend } from "back-end/lib/mailer/transport"; import React from "react"; import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config"; -import { - isSWUOpportunityClosed, - SWUOpportunity -} from "shared/lib/resources/opportunity/sprint-with-us"; +import { SWUOpportunity } from "shared/lib/resources/opportunity/sprint-with-us"; import { SWUProposal, SWUProposalSlim } from "shared/lib/resources/proposal/sprint-with-us"; import { AuthenticatedSession } from "shared/lib/resources/session"; -import { User } from "shared/lib/resources/user"; +import { User, UserType } from "shared/lib/resources/user"; import { Id } from "shared/lib/types"; import { getValidValue } from "shared/lib/validation"; @@ -146,7 +143,7 @@ export async function handleSWUProposalWithdrawn( proposalId: Id, session: AuthenticatedSession ): Promise { - //Notify the opportunity author if the opportunity is in an awardable state + //Notify the opportunity author that the proposal has been withdrawn const proposal = getValidValue( await db.readOneSWUProposal(connection, proposalId, session), null @@ -161,13 +158,6 @@ export async function handleSWUProposalWithdrawn( ), null ); - // Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself - const opportunityAuthor = - proposal && - getValidValue( - await db.readOneSWUOpportunityAuthor(connection, proposal.opportunity.id), - null - ); if (proposal && opportunity) { const withdrawnProponent = @@ -176,24 +166,29 @@ export async function handleSWUProposalWithdrawn( await db.readOneUser(connection, proposal.createdBy.id), null ); - // Notify opportunity author if opportunity is closed - if ( - opportunityAuthor && - withdrawnProponent && - isSWUOpportunityClosed(opportunity) - ) { - await withdrawnSWUProposalSubmission( - opportunityAuthor, - withdrawnProponent, - opportunity - ); - } // Notify proposal author if (withdrawnProponent) { await withdrawnSWUProposalSubmissionProposalAuthor( withdrawnProponent, opportunity ); + + // Notify admins that the proposal has been withdrawn + const adminUsers = + getValidValue( + await db.readManyUsersByRole(connection, UserType.Admin), + null + ) || []; + await Promise.all( + adminUsers.map( + async (admin) => + await withdrawnSWUProposalSubmission( + admin, + withdrawnProponent, + opportunity + ) + ) + ); } } } diff --git a/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx index c4736869a..0d2de278c 100644 --- a/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates"; import { makeSend } from "back-end/lib/mailer/transport"; import React from "react"; import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config"; -import { - isTWUOpportunityClosed, - TWUOpportunity -} from "shared/lib/resources/opportunity/team-with-us"; +import { TWUOpportunity } from "shared/lib/resources/opportunity/team-with-us"; import { TWUProposal, TWUProposalSlim } from "shared/lib/resources/proposal/team-with-us"; import { AuthenticatedSession } from "shared/lib/resources/session"; -import { User } from "shared/lib/resources/user"; +import { User, UserType } from "shared/lib/resources/user"; import { Id } from "shared/lib/types"; import { getValidValue } from "shared/lib/validation"; @@ -161,13 +158,6 @@ export async function handleTWUProposalWithdrawn( ), null ); - // Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself - const opportunityAuthor = - proposal && - getValidValue( - await db.readOneTWUOpportunityAuthor(connection, proposal.opportunity.id), - null - ); if (proposal && opportunity) { const withdrawnProponent = @@ -176,24 +166,29 @@ export async function handleTWUProposalWithdrawn( await db.readOneUser(connection, proposal.createdBy.id), null ); - // Notify opportunity author if opportunity is closed - if ( - opportunityAuthor && - withdrawnProponent && - isTWUOpportunityClosed(opportunity) - ) { - await withdrawnTWUProposalSubmission( - opportunityAuthor, - withdrawnProponent, - opportunity - ); - } // Notify proposal author if (withdrawnProponent) { await withdrawnTWUProposalSubmissionProposalAuthor( withdrawnProponent, opportunity ); + + // Notify admins that the proposal has been withdrawn + const adminUsers = + getValidValue( + await db.readManyUsersByRole(connection, UserType.Admin), + null + ) || []; + await Promise.all( + adminUsers.map( + async (admin) => + await withdrawnTWUProposalSubmission( + admin, + withdrawnProponent, + opportunity + ) + ) + ); } } } From e3b6ce6706b8ad4c6184c599428946336bea5c08 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 11 Feb 2024 17:39:37 -0600 Subject: [PATCH 7/8] chore: remove incorrect comments --- src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx | 1 - .../lib/mailer/notifications/proposal/sprint-with-us.tsx | 1 - src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx index 53297d2bc..9d10d4100 100644 --- a/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx @@ -137,7 +137,6 @@ export async function handleCWUProposalWithdrawn( proposalId: Id, session: AuthenticatedSession ): Promise { - //Notify the opportunity author if the opportunity is in an awardable state const proposal = getValidValue( await db.readOneCWUProposal(connection, proposalId, session), null diff --git a/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx index 7838441c4..abf9646fd 100644 --- a/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx @@ -143,7 +143,6 @@ export async function handleSWUProposalWithdrawn( proposalId: Id, session: AuthenticatedSession ): Promise { - //Notify the opportunity author that the proposal has been withdrawn const proposal = getValidValue( await db.readOneSWUProposal(connection, proposalId, session), null diff --git a/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx b/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx index 0d2de278c..c5b315d0d 100644 --- a/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx +++ b/src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx @@ -143,7 +143,6 @@ export async function handleTWUProposalWithdrawn( proposalId: Id, session: AuthenticatedSession ): Promise { - //Notify the opportunity author if the opportunity is in an award-able state const proposal = getValidValue( await db.readOneTWUProposal(connection, proposalId, session), null From c9f1ad4be276056451ee726e81e4673455a1a781 Mon Sep 17 00:00:00 2001 From: IanFonzie Date: Thu, 1 Feb 2024 16:52:04 -0800 Subject: [PATCH 8/8] fix: allow admins to delete opps under review (cherry picked from commit 13dc4e128df93cde527001f606eb9a4f6c13f025) --- src/back-end/lib/permissions.ts | 10 +++++++-- .../lib/resources/opportunity/code-with-us.ts | 22 +++++++++---------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/back-end/lib/permissions.ts b/src/back-end/lib/permissions.ts index a640eb212..045ed03cd 100644 --- a/src/back-end/lib/permissions.ts +++ b/src/back-end/lib/permissions.ts @@ -20,6 +20,7 @@ import { import { Affiliation } from "shared/lib/resources/affiliation"; import { CWUOpportunity, + CWUOpportunityStatus, doesCWUOpportunityStatusAllowGovToViewProposals } from "shared/lib/resources/opportunity/code-with-us"; import { @@ -351,12 +352,17 @@ export async function editCWUOpportunity( export async function deleteCWUOpportunity( connection: Connection, session: Session, - opportunityId: string + opportunityId: string, + status: CWUOpportunityStatus ): Promise { return ( - isAdmin(session) || + (isAdmin(session) && + [CWUOpportunityStatus.Draft, CWUOpportunityStatus.UnderReview].includes( + status + )) || (session && isGovernment(session) && + status === CWUOpportunityStatus.Draft && (await isCWUOpportunityAuthor( connection, session.user, diff --git a/src/back-end/lib/resources/opportunity/code-with-us.ts b/src/back-end/lib/resources/opportunity/code-with-us.ts index d64c5ae5a..43a29456c 100644 --- a/src/back-end/lib/resources/opportunity/code-with-us.ts +++ b/src/back-end/lib/resources/opportunity/code-with-us.ts @@ -1089,28 +1089,26 @@ const delete_: crud.Delete< > = (connection: db.Connection) => { return { async validateRequestBody(request) { + const validatedCWUOpportunity = await validateCWUOpportunityId( + connection, + request.params.id, + request.session + ); + if (isInvalid(validatedCWUOpportunity)) { + return invalid({ notFound: ["Opportunity not found."] }); + } if ( !(await permissions.deleteCWUOpportunity( connection, request.session, - request.params.id + request.params.id, + validatedCWUOpportunity.value.status )) ) { return invalid({ permissions: [permissions.ERROR_MESSAGE] }); } - const validatedCWUOpportunity = await validateCWUOpportunityId( - connection, - request.params.id, - request.session - ); - if (isInvalid(validatedCWUOpportunity)) { - return invalid({ notFound: ["Opportunity not found."] }); - } - if (validatedCWUOpportunity.value.status !== CWUOpportunityStatus.Draft) { - return invalid({ permissions: [permissions.ERROR_MESSAGE] }); - } return valid(validatedCWUOpportunity.value.id); }, respond: wrapRespond({