Moar bugs (#379)

* #376 fix milestone passed to milestone_paid email

* #374 fix admin contribution PUT/POST status validation

* #373 fix updates & contributors tabs of Proposal
This commit is contained in:
AMStrix 2019-03-14 22:26:28 -05:00 committed by Daniel Ternyak
parent 0e979dc77c
commit 1d07997a7a
9 changed files with 42 additions and 33 deletions

View File

@ -37,6 +37,7 @@ class FakeUpdate(object):
user = FakeUser() user = FakeUser()
proposal = FakeProposal() proposal = FakeProposal()
milestone = FakeMilestone()
contribution = FakeContribution() contribution = FakeContribution()
update = FakeUpdate() update = FakeUpdate()
@ -160,6 +161,7 @@ example_email_args = {
}, },
'milestone_paid': { 'milestone_paid': {
'proposal': proposal, 'proposal': proposal,
'milestone': milestone,
'amount': '33', 'amount': '33',
'tx_explorer_url': 'http://someblockexplorer.com/tx/271857129857192579125', 'tx_explorer_url': 'http://someblockexplorer.com/tx/271857129857192579125',
'proposal_milestones_url': 'http://zfnd.org/proposals/999-my-proposal?tab=milestones', 'proposal_milestones_url': 'http://zfnd.org/proposals/999-my-proposal?tab=milestones',

View File

@ -432,6 +432,7 @@ def paid_milestone_payout_request(id, mid, tx_id):
for member in proposal.team: for member in proposal.team:
send_email(member.email_address, 'milestone_paid', { send_email(member.email_address, 'milestone_paid', {
'proposal': proposal, 'proposal': proposal,
'milestone': ms,
'amount': amount, 'amount': amount,
'tx_explorer_url': f'{EXPLORER_URL}transactions/{tx_id}', 'tx_explorer_url': f'{EXPLORER_URL}transactions/{tx_id}',
'proposal_milestones_url': make_url(f'/proposals/{proposal.id}?tab=milestones'), 'proposal_milestones_url': make_url(f'/proposals/{proposal.id}?tab=milestones'),
@ -568,7 +569,7 @@ def get_contributions(page, filters, search, sort):
@body({ @body({
"proposalId": fields.Int(required=True), "proposalId": fields.Int(required=True),
"userId": fields.Int(required=True), "userId": fields.Int(required=True),
"status": fields.Str(required=True, validate=validate.OneOf(choices=RFPStatus.list())), "status": fields.Str(required=True, validate=validate.OneOf(choices=ContributionStatus.list())),
"amount": fields.Str(required=True), "amount": fields.Str(required=True),
"txId": fields.Str(required=False, missing=None) "txId": fields.Str(required=False, missing=None)
}) })
@ -607,7 +608,7 @@ def get_contribution(contribution_id):
@body({ @body({
"proposalId": fields.Int(required=False, missing=None), "proposalId": fields.Int(required=False, missing=None),
"userId": fields.Int(required=False, missing=None), "userId": fields.Int(required=False, missing=None),
"status": fields.Str(required=True, validate=validate.OneOf(choices=RFPStatus.list())), "status": fields.Str(required=True, validate=validate.OneOf(choices=ContributionStatus.list())),
"amount": fields.Str(required=False, missing=None), "amount": fields.Str(required=False, missing=None),
"txId": fields.Str(required=False, missing=None), "txId": fields.Str(required=False, missing=None),
"refundTxId": fields.Str(required=False, allow_none=True, missing=None), "refundTxId": fields.Str(required=False, allow_none=True, missing=None),

View File

@ -269,7 +269,7 @@ def milestone_accept(email_args):
def milestone_paid(email_args): def milestone_paid(email_args):
p = email_args['proposal'] p = email_args['proposal']
a = email_args['amount'] a = email_args['amount']
ms = p.current_milestone ms = email_args['milestone']
return { return {
'subject': f'{p.title} - {ms.title} has been paid!', 'subject': f'{p.title} - {ms.title} has been paid!',
'title': f'Milestone paid', 'title': f'Milestone paid',

View File

@ -1,7 +1,7 @@
<p style="margin: 0 0 20px;"> <p style="margin: 0 0 20px;">
Hooray! <b>{{ args.amount }} ZEC</b> has been paid out for Hooray! <b>{{ args.amount }} ZEC</b> has been paid out for
<a href="{{ args.proposal_milestones_url }}" target="_blank"> <a href="{{ args.proposal_milestones_url }}" target="_blank">
{{ args.proposal.title }} - {{ args.proposal.current_milestone.title }}</a {{ args.proposal.title }} - {{ args.milestone.title }}</a
>! You can view the transaction below: >! You can view the transaction below:
</p> </p>

View File

@ -1,4 +1,4 @@
Hooray! {{args.amount}} ZEC has been paid out for "{{ args.proposal.title }} - {{args.proposal.current_milestone.title }}"! Hooray! {{args.amount}} ZEC has been paid out for "{{ args.proposal.title }} - {{args.milestone.title }}"!
You can view the transaction below: You can view the transaction below:
{{ args.tx_explorer_url }} {{ args.tx_explorer_url }}

View File

@ -10,7 +10,7 @@ interface Props {
} }
const Placeholder: React.SFC<Props> = ({ style = {}, title, subtitle, loading }) => ( const Placeholder: React.SFC<Props> = ({ style = {}, title, subtitle, loading }) => (
<div className={`Placeholder ${!!loading && 'is-loading'}`} style={style}> <div className={`Placeholder${(!!loading && ' is-loading') || ''}`} style={style}>
{(loading && <Loader inline={true} size="small" />) || ( {(loading && <Loader inline={true} size="small" />) || (
<> <>
{title && <h3 className="Placeholder-title">{title}</h3>} {title && <h3 className="Placeholder-title">{title}</h3>}

View File

@ -2,23 +2,27 @@
.ProposalContributors { .ProposalContributors {
display: flex; display: flex;
max-width: 880px;
@media @mobile-query { @media @mobile-query {
flex-direction: column; flex-direction: column;
} }
.Placeholder {
flex-grow: 1;
}
&-block { &-block {
flex: 1; flex: 1;
padding: 1.5rem; padding: 1.5rem;
margin: 1rem; margin: 1rem;
background: #FFF; background: #fff;
border-radius: 4px; border-radius: 4px;
border: 1px solid #ddd; border: 1px solid #ddd;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(0, 0, 0, 0.1);
0 1px 1px rgba(0, 0, 0, 0.1);
&:first-child { &:first-child {
margin-left: 0; margin-left: 0;
} }
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;

View File

@ -3,7 +3,6 @@ import { connect } from 'react-redux';
import UserRow from 'components/UserRow'; import UserRow from 'components/UserRow';
import Placeholder from 'components/Placeholder'; import Placeholder from 'components/Placeholder';
import UnitDisplay from 'components/UnitDisplay'; import UnitDisplay from 'components/UnitDisplay';
import Loader from 'components/Loader';
import { toZat } from 'utils/units'; import { toZat } from 'utils/units';
import { fetchProposalContributions } from 'modules/proposals/actions'; import { fetchProposalContributions } from 'modules/proposals/actions';
import { import {
@ -54,20 +53,26 @@ class ProposalContributors extends React.Component<Props> {
<div className="ProposalContributors-block-contributor" key={c.id}> <div className="ProposalContributors-block-contributor" key={c.id}>
<UserRow <UserRow
user={c.user} user={c.user}
extra={<>+<UnitDisplay value={toZat(c.amount)} symbol="ZEC" /></>} extra={
<>
+<UnitDisplay value={toZat(c.amount)} symbol="ZEC" />
</>
}
/> />
</div> </div>
) );
content = <> content = (
<div className="ProposalContributors-block"> <>
<h3 className="ProposalContributors-block-title">Latest contributors</h3> <div className="ProposalContributors-block">
{contributions.latest.map(makeContributionRow)} <h3 className="ProposalContributors-block-title">Latest contributors</h3>
</div> {contributions.latest.map(makeContributionRow)}
<div className="ProposalContributors-block"> </div>
<h3 className="ProposalContributors-block-title">Top contributors</h3> <div className="ProposalContributors-block">
{contributions.top.map(makeContributionRow)} <h3 className="ProposalContributors-block-title">Top contributors</h3>
</div> {contributions.top.map(makeContributionRow)}
</>; </div>
</>
);
} else { } else {
content = ( content = (
<Placeholder <Placeholder
@ -81,18 +86,16 @@ class ProposalContributors extends React.Component<Props> {
); );
} }
} else if (fetchContributionsError) { } else if (fetchContributionsError) {
content = <Placeholder title="Something went wrong" subtitle={fetchContributionsError} />; content = (
<Placeholder title="Something went wrong" subtitle={fetchContributionsError} />
);
} else { } else {
content = <Loader />; content = <Placeholder loading={true} />;
} }
return ( return <div className="ProposalContributors">{content}</div>;
<div className="ProposalContributors">
{content}
</div>
);
} }
}; }
export default connect( export default connect(
(state: AppState, ownProps: OwnProps) => ({ (state: AppState, ownProps: OwnProps) => ({

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Loader from 'components/Loader';
import Markdown from 'components/Markdown'; import Markdown from 'components/Markdown';
import moment from 'moment'; import moment from 'moment';
import Placeholder from 'components/Placeholder'; import Placeholder from 'components/Placeholder';
@ -58,7 +57,7 @@ class ProposalUpdates extends React.Component<Props, State> {
let content = null; let content = null;
if (isFetchingUpdates) { if (isFetchingUpdates) {
content = <Loader />; content = <Placeholder loading={true} />;
} else if (updatesError) { } else if (updatesError) {
content = <Placeholder title="Something went wrong" subtitle={updatesError} />; content = <Placeholder title="Something went wrong" subtitle={updatesError} />;
} else if (updates) { } else if (updates) {