From fb6b9b5af7c153caf4b036b20eb56db75e3d9d24 Mon Sep 17 00:00:00 2001 From: Danny Skubak Date: Wed, 16 Oct 2019 23:43:20 -0400 Subject: [PATCH] Proposal Lifecycle & Crowdfunding (#23) * add proposal versioning * remove deadlines * update proposal lifecycle for admin * update proposal lifecycle for backend * update proposal lifecycle for frontend * fix tests * remove acceptedWithFunding * fix lint, remove commented code * remove commented code * refactor backend to provide isVersionTwo * refactor backend to provide isVersionTwo * Revert "refactor backend to provide isVersionTwo" This reverts commit e3b9bc661081e482326f83fa6aa517cf6bdebe6c. * use isVersionTwo in admin * add acceptedWithFunding * trigger ci * remove "version" * remove "version" * remove rejected from campaign block --- admin/src/components/ArbiterControl/index.tsx | 4 +- .../src/components/ProposalDetail/index.less | 5 +- admin/src/components/ProposalDetail/index.tsx | 164 +++++------------- admin/src/components/RFPForm/index.tsx | 12 -- admin/src/store.ts | 16 +- admin/src/types.ts | 1 + backend/grant/admin/views.py | 36 +--- backend/grant/proposal/commands.py | 2 +- backend/grant/proposal/models.py | 54 ++---- backend/grant/proposal/views.py | 3 - backend/grant/utils/enums.py | 1 - backend/tests/admin/test_admin_api.py | 54 +++--- .../client/components/CreateFlow/index.tsx | 4 +- .../Proposal/CampaignBlock/index.tsx | 163 ++++++++--------- frontend/client/components/RFP/index.tsx | 2 +- frontend/client/modules/create/utils.ts | 1 + frontend/stories/props.tsx | 3 +- frontend/types/proposal.ts | 1 + 18 files changed, 195 insertions(+), 331 deletions(-) diff --git a/admin/src/components/ArbiterControl/index.tsx b/admin/src/components/ArbiterControl/index.tsx index 4c85d617..fd71ae5f 100644 --- a/admin/src/components/ArbiterControl/index.tsx +++ b/admin/src/components/ArbiterControl/index.tsx @@ -30,10 +30,11 @@ class ArbiterControlNaked extends React.Component { }, 1000); render() { - const { arbiter } = this.props; + const { arbiter, isVersionTwo, acceptedWithFunding } = this.props; const { showSearch, searching } = this.state; const { results, search, error } = store.arbitersSearch; const showEmpty = !results.length && !searching; + const buttonDisabled = isVersionTwo && acceptedWithFunding === false const disp = { [PROPOSAL_ARBITER_STATUS.MISSING]: 'Nominate arbiter', @@ -51,6 +52,7 @@ class ArbiterControlNaked extends React.Component { type="primary" onClick={this.handleShowSearch} {...this.props.buttonProps} + disabled={buttonDisabled} > {disp[arbiter.status]} diff --git a/admin/src/components/ProposalDetail/index.less b/admin/src/components/ProposalDetail/index.less index b36ac488..17b9009e 100644 --- a/admin/src/components/ProposalDetail/index.less +++ b/admin/src/components/ProposalDetail/index.less @@ -27,8 +27,9 @@ .ant-collapse { margin-bottom: 16px; - button + button { - margin-left: 0.5rem; + button { + margin-right: 0.5rem; + margin-bottom: 0.25rem; } } diff --git a/admin/src/components/ProposalDetail/index.tsx b/admin/src/components/ProposalDetail/index.tsx index 977ad24b..747144bd 100644 --- a/admin/src/components/ProposalDetail/index.tsx +++ b/admin/src/components/ProposalDetail/index.tsx @@ -11,7 +11,6 @@ import { Collapse, Popconfirm, Input, - Switch, Tag, message, } from 'antd'; @@ -26,7 +25,6 @@ import { } from 'src/types'; import { Link } from 'react-router-dom'; import Back from 'components/Back'; -import Info from 'components/Info'; import Markdown from 'components/Markdown'; import ArbiterControl from 'components/ArbiterControl'; import { toZat, fromZat } from 'src/util/units'; @@ -65,6 +63,12 @@ class ProposalDetailNaked extends React.Component { return m.datePaid ? prev - parseFloat(m.payoutPercent) : prev; }, 100); + const { isVersionTwo } = p + const shouldShowArbiter = + !isVersionTwo || + (isVersionTwo && p.acceptedWithFunding === true); + const cancelButtonText = isVersionTwo ? 'Cancel' : 'Cancel & refund' + const renderCancelControl = () => { const disabled = this.getCancelAndRefundDisabled(); @@ -95,7 +99,7 @@ class ProposalDetailNaked extends React.Component { disabled={disabled} block > - Cancel & refund + { cancelButtonText } ); @@ -116,68 +120,6 @@ class ProposalDetailNaked extends React.Component { /> ); - const renderMatchingControl = () => ( -
- -
- Turn {p.contributionMatching ? 'off' : 'on'} contribution matching? -
- {p.status === PROPOSAL_STATUS.LIVE && ( -
- This is a LIVE proposal, this will alter the funding state of the - proposal! -
- )} - - } - okText="ok" - cancelText="cancel" - > - {' '} -
- - matching{' '} - - Contribution matching -
Funded amount will be multiplied by 2. -
Disabled after proposal is fully-funded. -
- } - /> - -
- ); - - const renderBountyControl = () => ( -
- -
- ); const renderApproved = () => p.status === PROPOSAL_STATUS.APPROVED && ( @@ -205,9 +147,17 @@ class ProposalDetailNaked extends React.Component { loading={store.proposalDetailApproving} icon="check" type="primary" - onClick={this.handleApprove} + onClick={() => this.handleApprove(true)} > - Approve + Approve With Funding + + - + */} )} - + {proposal.stage === PROPOSAL_STAGE.CANCELED ? ( + <> + + Proposal was canceled + + ) : ( + <> + + Proposal has been accepted + + )} + + )} + + {/* TODO: adapt below for tipjar? */} + {/* + /> */} ); } else { @@ -244,38 +279,6 @@ export class ProposalCampaignBlock extends React.Component { ); } - - private handleAmountChange = ( - event: React.ChangeEvent, - ) => { - const { value } = event.currentTarget; - if (!value) { - this.setState({ amountToRaise: '', amountError: null }); - return; - } - - const { target, funded } = this.props.proposal; - const remainingTarget = target.sub(funded); - const amount = parseFloat(value); - let amountError = null; - - if (Number.isNaN(amount)) { - // They're entering some garbage, they’ll work it out - } else { - const remainingTargetNum = parseFloat(fromZat(remainingTarget)); - amountError = getAmountError(amount, remainingTargetNum); - } - - this.setState({ amountToRaise: value, amountError }); - }; - - private handleChangePrivate = (ev: RadioChangeEvent) => { - const isPrivate = ev.target.value === 'isPrivate'; - this.setState({ isPrivate }); - }; - - private openContributionModal = () => this.setState({ isContributing: true }); - private closeContributionModal = () => this.setState({ isContributing: false }); } function mapStateToProps(state: AppState) { diff --git a/frontend/client/components/RFP/index.tsx b/frontend/client/components/RFP/index.tsx index fd81c57d..245baffa 100644 --- a/frontend/client/components/RFP/index.tsx +++ b/frontend/client/components/RFP/index.tsx @@ -72,7 +72,7 @@ class RFPDetail extends React.Component { tags.push( Closed - + , ); } diff --git a/frontend/client/modules/create/utils.ts b/frontend/client/modules/create/utils.ts index 48e3cdf6..b3c7db7b 100644 --- a/frontend/client/modules/create/utils.ts +++ b/frontend/client/modules/create/utils.ts @@ -248,6 +248,7 @@ export function makeProposalPreviewFromDraft(draft: ProposalDraft): ProposalDeta arbiter: { status: PROPOSAL_ARBITER_STATUS.ACCEPTED, }, + acceptedWithFunding: false, isVersionTwo: true, milestones: draft.milestones.map((m, idx) => ({ id: idx, diff --git a/frontend/stories/props.tsx b/frontend/stories/props.tsx index 9d49c9f0..17c0447a 100644 --- a/frontend/stories/props.tsx +++ b/frontend/stories/props.tsx @@ -173,7 +173,8 @@ export function generateProposal({ socialMedias: [], }, }, - isVersionTwo: true, + acceptedWithFunding: null, + isVersionTwo: false, team: [ { userid: 123, diff --git a/frontend/types/proposal.ts b/frontend/types/proposal.ts index 49464d05..e491fa7a 100644 --- a/frontend/types/proposal.ts +++ b/frontend/types/proposal.ts @@ -62,6 +62,7 @@ export interface Proposal extends Omit { datePublished: number | null; dateApproved: number | null; arbiter: ProposalProposalArbiter; + acceptedWithFunding: boolean | null; isVersionTwo: boolean; isTeamMember?: boolean; // FE derived isArbiter?: boolean; // FE derived