From 4a0e23e9c7effeba198e5e00cba070096e1e4d11 Mon Sep 17 00:00:00 2001
From: Danny Skubak
Date: Tue, 3 Dec 2019 19:02:39 -0500
Subject: [PATCH] Price in Usd (#91)
* init profile tipjar backend
* init profile tipjar frontend
* fix lint
* implement tip jar block
* fix wrapping, hide tip block on self
* init backend proposal tipjar
* init frontend proposal tipjar
* add hide title, fix bug
* uncomment rate limit
* rename vars, use null check
* allow address and view key to be unset
* add api tests
* fix tsc errors
* fix lint
* fix CopyInput styling
* fix migrations
* hide tipping in proposal if address not set
* add tip address to create flow
* redesign campaign block
* fix typo
* init backend changes
* init admin changes
* init frontend changes
* fix backend tests
* update campaign block
* be - init rfp usd changes
* admin - init rfp usd changes
* fe - fully adapt api util functions to usd
* fe - init rfp usd changes
* adapt profile created to usd
* misc usd changes
* add tip jar to dedicated card
* fix tipjar bug
* use zf light logo
* switch to zf grants logo
* hide profile tip jar if address not set
* add comment, run prettier
* conditionally add info icon and tooltip to funding line
* admin - disallow decimals in RFPs
* fe - cover usd string edge case
* add Usd as rfp bounty type
---
admin/src/components/ProposalDetail/index.tsx | 32 ++++--
admin/src/components/RFPDetail/index.tsx | 6 +-
admin/src/components/RFPForm/index.tsx | 16 ++-
admin/src/types.ts | 1 +
admin/src/util/formatters.ts | 72 ++++++++++++
backend/.env.example | 2 +-
backend/grant/proposal/models.py | 9 +-
backend/grant/rfp/models.py | 14 ++-
backend/migrations/versions/1e1460456ce4_.py | 28 +++++
.../tests/milestone/test_milestone_methods.py | 2 +-
backend/tests/test_data.py | 2 +-
frontend/.env.example | 2 +-
.../client/components/CreateFlow/Basics.tsx | 7 +-
.../client/components/CreateFlow/Review.tsx | 3 +-
.../components/Profile/ProfileProposal.tsx | 44 ++++++--
.../Proposal/CampaignBlock/index.tsx | 15 ++-
.../components/Proposal/Milestones/index.tsx | 12 +-
.../Proposals/ProposalCard/index.tsx | 3 +-
frontend/client/components/RFP/index.tsx | 43 +++++---
frontend/client/components/RFPs/RFPItem.tsx | 20 +++-
frontend/client/modules/create/utils.ts | 6 +-
frontend/client/utils/api.ts | 103 ++++++++++++------
frontend/client/utils/formatters.ts | 11 ++
frontend/client/utils/units.ts | 11 ++
frontend/client/utils/validators.ts | 32 +++++-
frontend/types/milestone.ts | 2 +-
frontend/types/proposal.ts | 12 +-
frontend/types/rfp.ts | 5 +-
28 files changed, 417 insertions(+), 98 deletions(-)
create mode 100644 admin/src/util/formatters.ts
create mode 100644 backend/migrations/versions/1e1460456ce4_.py
diff --git a/admin/src/components/ProposalDetail/index.tsx b/admin/src/components/ProposalDetail/index.tsx
index 193b0810..ed83a145 100644
--- a/admin/src/components/ProposalDetail/index.tsx
+++ b/admin/src/components/ProposalDetail/index.tsx
@@ -29,6 +29,7 @@ import Markdown from 'components/Markdown';
import ArbiterControl from 'components/ArbiterControl';
import { toZat, fromZat } from 'src/util/units';
import FeedbackModal from '../FeedbackModal';
+import { formatUsd } from 'util/formatters';
import './index.less';
type Props = RouteComponentProps;
@@ -285,11 +286,23 @@ class ProposalDetailNaked extends React.Component {
return;
}
const ms = p.currentMilestone;
- const amount = fromZat(
- toZat(p.target)
- .mul(new BN(ms.payoutPercent))
- .divn(100),
- );
+
+ let paymentMsg;
+ if (p.isVersionTwo) {
+ const target = parseFloat(p.target.toString());
+ const payoutPercent = parseFloat(ms.payoutPercent);
+ const amountNum = (target * payoutPercent) / 100;
+ const amount = formatUsd(amountNum, true, 2);
+ paymentMsg = `${amount} in ZEC`;
+ } else {
+ const amount = fromZat(
+ toZat(p.target)
+ .mul(new BN(ms.payoutPercent))
+ .divn(100),
+ );
+ paymentMsg = `${amount} ZEC`;
+ }
+
return (
{
{' '}
- Please make a payment of {amount.toString()} ZEC to:
+ Please make a payment of {paymentMsg} to:
{' '}
{p.payoutAddress}
{
{renderDeetItem('isFailed', JSON.stringify(p.isFailed))}
{renderDeetItem('status', p.status)}
{renderDeetItem('stage', p.stage)}
- {renderDeetItem('target', p.target)}
+ {renderDeetItem('target', p.isVersionTwo ? formatUsd(p.target) : p.target)}
{renderDeetItem('contributed', p.contributed)}
- {renderDeetItem('funded (inc. matching)', p.funded)}
+ {renderDeetItem(
+ 'funded (inc. matching)',
+ p.isVersionTwo ? formatUsd(p.funded) : p.funded,
+ )}
{renderDeetItem('matching', p.contributionMatching)}
{renderDeetItem('bounty', p.contributionBounty)}
{renderDeetItem('rfpOptIn', JSON.stringify(p.rfpOptIn))}
diff --git a/admin/src/components/RFPDetail/index.tsx b/admin/src/components/RFPDetail/index.tsx
index fc270c5c..e9e34982 100644
--- a/admin/src/components/RFPDetail/index.tsx
+++ b/admin/src/components/RFPDetail/index.tsx
@@ -9,6 +9,7 @@ import Markdown from 'components/Markdown';
import { formatDateSeconds } from 'util/time';
import store from 'src/store';
import { PROPOSAL_STATUS } from 'src/types';
+import { formatUsd } from 'src/util/formatters';
import './index.less';
type Props = RouteComponentProps<{ id?: string }>;
@@ -93,7 +94,10 @@ class RFPDetail extends React.Component {
{renderDeetItem('created', formatDateSeconds(rfp.dateCreated))}
{renderDeetItem('status', rfp.status)}
{renderDeetItem('matching', String(rfp.matching))}
- {renderDeetItem('bounty', `${rfp.bounty} ZEC`)}
+ {renderDeetItem(
+ 'bounty',
+ rfp.isVersionTwo ? formatUsd(rfp.bounty) : `${rfp.bounty} ZEC`,
+ )}
{renderDeetItem(
'dateCloses',
rfp.dateCloses && formatDateSeconds(rfp.dateCloses),
diff --git a/admin/src/components/RFPForm/index.tsx b/admin/src/components/RFPForm/index.tsx
index a9ebe121..a82789f8 100644
--- a/admin/src/components/RFPForm/index.tsx
+++ b/admin/src/components/RFPForm/index.tsx
@@ -47,6 +47,8 @@ class RFPForm extends React.Component {
dateCloses: undefined,
};
const rfpId = this.getRFPId();
+ let isVersionTwo = true;
+
if (rfpId) {
if (!store.rfpsFetched) {
return ;
@@ -63,6 +65,7 @@ class RFPForm extends React.Component {
bounty: rfp.bounty,
dateCloses: rfp.dateCloses || undefined,
};
+ isVersionTwo = rfp.isVersionTwo;
} else {
return ;
}
@@ -73,6 +76,10 @@ class RFPForm extends React.Component {
: defaults.dateCloses && moment(defaults.dateCloses * 1000);
const forceClosed = dateCloses && dateCloses.isBefore(moment.now());
+ const bountyMatchRule = isVersionTwo
+ ? { pattern: /^[^.]*$/, message: 'Cannot contain a decimal' }
+ : {};
+
return (