From 1be2c5c9455287962640be4dd6a5ec346b9b992e Mon Sep 17 00:00:00 2001 From: William O'Beirne Date: Sat, 16 Feb 2019 12:32:26 -0500 Subject: [PATCH] Add bounty banner to campaign block. Reflect bounty in funded amount. Fix some of the cases that opens up. (#213) --- backend/grant/proposal/models.py | 5 +++++ .../components/Proposal/CampaignBlock/index.tsx | 14 ++++++++++++++ .../Proposal/CampaignBlock/style.less | 17 +++++++++++++++-- frontend/client/utils/api.ts | 7 ++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/backend/grant/proposal/models.py b/backend/grant/proposal/models.py index 2504a8d1..8072f06e 100644 --- a/backend/grant/proposal/models.py +++ b/backend/grant/proposal/models.py @@ -419,6 +419,8 @@ class Proposal(db.Model): self.date_published = datetime.datetime.now() self.status = ProposalStatus.LIVE self.stage = ProposalStage.FUNDING_REQUIRED + # If we had a bounty that pushed us into funding, skip straight into WIP + self.set_funded_when_ready() def set_funded_when_ready(self): if self.status == ProposalStatus.LIVE and self.is_funded: @@ -464,6 +466,9 @@ class Proposal(db.Model): target = Decimal(self.target) # apply matching multiplier funded = Decimal(self.contributed) * Decimal(1 + self.contribution_matching) + # apply bounty, if available + if self.rfp: + funded = funded + Decimal(self.rfp.bounty) # if funded > target, just set as target if funded > target: return str(target) diff --git a/frontend/client/components/Proposal/CampaignBlock/index.tsx b/frontend/client/components/Proposal/CampaignBlock/index.tsx index d935f62b..ef2f65fd 100644 --- a/frontend/client/components/Proposal/CampaignBlock/index.tsx +++ b/frontend/client/components/Proposal/CampaignBlock/index.tsx @@ -61,6 +61,14 @@ export class ProposalCampaignBlock extends React.Component { const isDisabled = isFundingOver || !!amountError || !amountFloat || isPreview; const remainingTargetNum = parseFloat(fromZat(target.sub(funded))); + // Get bounty from RFP. If it exceeds proposal target, show bounty as full amount + let bounty; + if (proposal.rfp && proposal.rfp.bounty) { + bounty = proposal.rfp.bounty.gt(proposal.target) + ? proposal.target + : proposal.rfp.bounty; + } + content = ( {isLive && ( @@ -96,6 +104,12 @@ export class ProposalCampaignBlock extends React.Component { + {bounty && ( +
+ Awarded with bounty +
+ )} + {proposal.contributionMatching > 0 && (
Funds are being matched x{proposal.contributionMatching + 1} diff --git a/frontend/client/components/Proposal/CampaignBlock/style.less b/frontend/client/components/Proposal/CampaignBlock/style.less index 847086e8..031870cb 100644 --- a/frontend/client/components/Proposal/CampaignBlock/style.less +++ b/frontend/client/components/Proposal/CampaignBlock/style.less @@ -32,11 +32,11 @@ } } + &-bounty, &-matching { margin: 0.5rem -1.5rem; - padding: 0.75rem 1.5rem; + padding: 0.75rem 1rem; text-align: center; - background: @info-color; color: #FFF; font-size: 1rem; @@ -45,6 +45,19 @@ } } + &-bounty { + background: @primary-color; + } + + &-matching { + background: @info-color; + } + + &-bounty + &-matching { + margin-top: 0; + } + + &-popover { &-overlay { max-width: 400px; diff --git a/frontend/client/utils/api.ts b/frontend/client/utils/api.ts index 8a186704..03d695bc 100644 --- a/frontend/client/utils/api.ts +++ b/frontend/client/utils/api.ts @@ -97,6 +97,9 @@ export function formatProposalFromGet(p: any): Proposal { proposal.milestones = proposal.milestones.map(msToFe); proposal.currentMilestone = msToFe(proposal.currentMilestone); } + if (proposal.rfp) { + proposal.rfp = formatRFPFromGet(proposal.rfp); + } return proposal; } @@ -104,7 +107,9 @@ export function formatRFPFromGet(rfp: RFP): RFP { if (rfp.bounty) { rfp.bounty = toZat(rfp.bounty as any); } - rfp.acceptedProposals = rfp.acceptedProposals.map(formatProposalFromGet); + if (rfp.acceptedProposals) { + rfp.acceptedProposals = rfp.acceptedProposals.map(formatProposalFromGet); + } return rfp; }