Remove Categories (#63)
* remove category from admin * remove category from frontend, add likes to proposal card view * make category nullable in backend, remove from views * add db migration * remove category from frontend rfp * update tests * remove category from admin proposal * remove category from rfp put * remove moment * remove moment
This commit is contained in:
parent
ed6d98ceec
commit
8cfec5de5d
|
@ -445,7 +445,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
{renderDeetItem('isFailed', JSON.stringify(p.isFailed))}
|
{renderDeetItem('isFailed', JSON.stringify(p.isFailed))}
|
||||||
{renderDeetItem('status', p.status)}
|
{renderDeetItem('status', p.status)}
|
||||||
{renderDeetItem('stage', p.stage)}
|
{renderDeetItem('stage', p.stage)}
|
||||||
{renderDeetItem('category', p.category)}
|
|
||||||
{renderDeetItem('target', p.target)}
|
{renderDeetItem('target', p.target)}
|
||||||
{renderDeetItem('contributed', p.contributed)}
|
{renderDeetItem('contributed', p.contributed)}
|
||||||
{renderDeetItem('funded (inc. matching)', p.funded)}
|
{renderDeetItem('funded (inc. matching)', p.funded)}
|
||||||
|
|
|
@ -37,9 +37,11 @@ class RFPDetail extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const pendingProposals = rfp.proposals.filter(p => p.status === PROPOSAL_STATUS.PENDING);
|
const pendingProposals = rfp.proposals.filter(
|
||||||
const acceptedProposals = rfp.proposals.filter(p =>
|
p => p.status === PROPOSAL_STATUS.PENDING,
|
||||||
p.status === PROPOSAL_STATUS.LIVE || p.status === PROPOSAL_STATUS.APPROVED
|
);
|
||||||
|
const acceptedProposals = rfp.proposals.filter(
|
||||||
|
p => p.status === PROPOSAL_STATUS.LIVE || p.status === PROPOSAL_STATUS.APPROVED,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -90,10 +92,12 @@ class RFPDetail extends React.Component<Props> {
|
||||||
{renderDeetItem('id', rfp.id)}
|
{renderDeetItem('id', rfp.id)}
|
||||||
{renderDeetItem('created', formatDateSeconds(rfp.dateCreated))}
|
{renderDeetItem('created', formatDateSeconds(rfp.dateCreated))}
|
||||||
{renderDeetItem('status', rfp.status)}
|
{renderDeetItem('status', rfp.status)}
|
||||||
{renderDeetItem('category', rfp.category)}
|
|
||||||
{renderDeetItem('matching', String(rfp.matching))}
|
{renderDeetItem('matching', String(rfp.matching))}
|
||||||
{renderDeetItem('bounty', `${rfp.bounty} ZEC`)}
|
{renderDeetItem('bounty', `${rfp.bounty} ZEC`)}
|
||||||
{renderDeetItem('dateCloses', rfp.dateCloses && formatDateSeconds(rfp.dateCloses))}
|
{renderDeetItem(
|
||||||
|
'dateCloses',
|
||||||
|
rfp.dateCloses && formatDateSeconds(rfp.dateCloses),
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* PROPOSALS */}
|
{/* PROPOSALS */}
|
||||||
|
|
|
@ -3,22 +3,10 @@ import moment from 'moment';
|
||||||
import { view } from 'react-easy-state';
|
import { view } from 'react-easy-state';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router';
|
import { RouteComponentProps, withRouter } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import { Form, Input, Select, Button, message, Spin, Row, Col, DatePicker } from 'antd';
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Icon,
|
|
||||||
Button,
|
|
||||||
message,
|
|
||||||
Spin,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
DatePicker,
|
|
||||||
} from 'antd';
|
|
||||||
import Exception from 'ant-design-pro/lib/Exception';
|
import Exception from 'ant-design-pro/lib/Exception';
|
||||||
import { FormComponentProps } from 'antd/lib/form';
|
import { FormComponentProps } from 'antd/lib/form';
|
||||||
import { PROPOSAL_CATEGORY, RFP_STATUS, RFPArgs } from 'src/types';
|
import { RFP_STATUS, RFPArgs } from 'src/types';
|
||||||
import { CATEGORY_UI } from 'util/ui';
|
|
||||||
import { typedKeys } from 'util/ts';
|
import { typedKeys } from 'util/ts';
|
||||||
import { RFP_STATUSES, getStatusById } from 'util/statuses';
|
import { RFP_STATUSES, getStatusById } from 'util/statuses';
|
||||||
import Markdown from 'components/Markdown';
|
import Markdown from 'components/Markdown';
|
||||||
|
@ -53,7 +41,6 @@ class RFPForm extends React.Component<Props, State> {
|
||||||
title: '',
|
title: '',
|
||||||
brief: '',
|
brief: '',
|
||||||
content: '',
|
content: '',
|
||||||
category: '',
|
|
||||||
status: '',
|
status: '',
|
||||||
matching: false,
|
matching: false,
|
||||||
bounty: undefined,
|
bounty: undefined,
|
||||||
|
@ -71,7 +58,6 @@ class RFPForm extends React.Component<Props, State> {
|
||||||
title: rfp.title,
|
title: rfp.title,
|
||||||
brief: rfp.brief,
|
brief: rfp.brief,
|
||||||
content: rfp.content,
|
content: rfp.content,
|
||||||
category: rfp.category,
|
|
||||||
status: rfp.status,
|
status: rfp.status,
|
||||||
matching: rfp.matching,
|
matching: rfp.matching,
|
||||||
bounty: rfp.bounty,
|
bounty: rfp.bounty,
|
||||||
|
@ -130,28 +116,6 @@ class RFPForm extends React.Component<Props, State> {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Form.Item label="Category">
|
|
||||||
{getFieldDecorator('category', {
|
|
||||||
initialValue: defaults.category,
|
|
||||||
rules: [
|
|
||||||
{ required: true, message: 'Category is required' },
|
|
||||||
{ max: 60, message: 'Max 60 chars' },
|
|
||||||
],
|
|
||||||
})(
|
|
||||||
<Select size="large" placeholder="Select a category">
|
|
||||||
{typedKeys(PROPOSAL_CATEGORY).map(c => (
|
|
||||||
<Select.Option value={c} key={c}>
|
|
||||||
<Icon
|
|
||||||
type={CATEGORY_UI[c].icon}
|
|
||||||
style={{ color: CATEGORY_UI[c].color }}
|
|
||||||
/>{' '}
|
|
||||||
{CATEGORY_UI[c].label}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>,
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="Brief description">
|
<Form.Item label="Brief description">
|
||||||
{getFieldDecorator('brief', {
|
{getFieldDecorator('brief', {
|
||||||
initialValue: defaults.brief,
|
initialValue: defaults.brief,
|
||||||
|
|
|
@ -42,7 +42,6 @@ export interface RFP {
|
||||||
title: string;
|
title: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
content: string;
|
content: string;
|
||||||
category: string;
|
|
||||||
status: string;
|
status: string;
|
||||||
proposals: Proposal[];
|
proposals: Proposal[];
|
||||||
matching: boolean;
|
matching: boolean;
|
||||||
|
@ -53,7 +52,6 @@ export interface RFPArgs {
|
||||||
title: string;
|
title: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
content: string;
|
content: string;
|
||||||
category: string;
|
|
||||||
matching: boolean;
|
matching: boolean;
|
||||||
dateCloses: number | null | undefined;
|
dateCloses: number | null | undefined;
|
||||||
bounty: string | null | undefined;
|
bounty: string | null | undefined;
|
||||||
|
@ -103,7 +101,6 @@ export interface Proposal {
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
stage: PROPOSAL_STAGE;
|
stage: PROPOSAL_STAGE;
|
||||||
category: string;
|
|
||||||
milestones: Milestone[];
|
milestones: Milestone[];
|
||||||
currentMilestone?: Milestone;
|
currentMilestone?: Milestone;
|
||||||
team: User[];
|
team: User[];
|
||||||
|
|
|
@ -487,7 +487,6 @@ def get_rfps():
|
||||||
"title": fields.Str(required=True),
|
"title": fields.Str(required=True),
|
||||||
"brief": fields.Str(required=True),
|
"brief": fields.Str(required=True),
|
||||||
"content": fields.Str(required=True),
|
"content": fields.Str(required=True),
|
||||||
"category": fields.Str(required=True, validate=validate.OneOf(choices=Category.list())),
|
|
||||||
"bounty": fields.Str(required=False, missing=0),
|
"bounty": fields.Str(required=False, missing=0),
|
||||||
"matching": fields.Bool(required=False, missing=False),
|
"matching": fields.Bool(required=False, missing=False),
|
||||||
"dateCloses": fields.Int(required=False, missing=None)
|
"dateCloses": fields.Int(required=False, missing=None)
|
||||||
|
@ -519,13 +518,12 @@ def get_rfp(rfp_id):
|
||||||
"brief": fields.Str(required=True),
|
"brief": fields.Str(required=True),
|
||||||
"status": fields.Str(required=True, validate=validate.OneOf(choices=RFPStatus.list())),
|
"status": fields.Str(required=True, validate=validate.OneOf(choices=RFPStatus.list())),
|
||||||
"content": fields.Str(required=True),
|
"content": fields.Str(required=True),
|
||||||
"category": fields.Str(required=True, validate=validate.OneOf(choices=Category.list())),
|
|
||||||
"bounty": fields.Str(required=False, allow_none=True, missing=None),
|
"bounty": fields.Str(required=False, allow_none=True, missing=None),
|
||||||
"matching": fields.Bool(required=False, default=False, missing=False),
|
"matching": fields.Bool(required=False, default=False, missing=False),
|
||||||
"dateCloses": fields.Int(required=False, missing=None),
|
"dateCloses": fields.Int(required=False, missing=None),
|
||||||
})
|
})
|
||||||
@admin.admin_auth_required
|
@admin.admin_auth_required
|
||||||
def update_rfp(rfp_id, title, brief, content, category, bounty, matching, date_closes, status):
|
def update_rfp(rfp_id, title, brief, content, bounty, matching, date_closes, status):
|
||||||
rfp = RFP.query.filter(RFP.id == rfp_id).first()
|
rfp = RFP.query.filter(RFP.id == rfp_id).first()
|
||||||
if not rfp:
|
if not rfp:
|
||||||
return {"message": "No RFP matching that id"}, 404
|
return {"message": "No RFP matching that id"}, 404
|
||||||
|
@ -534,7 +532,6 @@ def update_rfp(rfp_id, title, brief, content, category, bounty, matching, date_c
|
||||||
rfp.title = title
|
rfp.title = title
|
||||||
rfp.brief = brief
|
rfp.brief = brief
|
||||||
rfp.content = content
|
rfp.content = content
|
||||||
rfp.category = category
|
|
||||||
rfp.matching = matching
|
rfp.matching = matching
|
||||||
rfp.bounty = bounty
|
rfp.bounty = bounty
|
||||||
rfp.date_closes = datetime.fromtimestamp(date_closes) if date_closes else None
|
rfp.date_closes = datetime.fromtimestamp(date_closes) if date_closes else None
|
||||||
|
|
|
@ -241,7 +241,7 @@ class Proposal(db.Model):
|
||||||
brief = db.Column(db.String(255), nullable=False)
|
brief = db.Column(db.String(255), nullable=False)
|
||||||
stage = db.Column(db.String(255), nullable=False)
|
stage = db.Column(db.String(255), nullable=False)
|
||||||
content = db.Column(db.Text, nullable=False)
|
content = db.Column(db.Text, nullable=False)
|
||||||
category = db.Column(db.String(255), nullable=False)
|
category = db.Column(db.String(255), nullable=True)
|
||||||
date_approved = db.Column(db.DateTime)
|
date_approved = db.Column(db.DateTime)
|
||||||
date_published = db.Column(db.DateTime)
|
date_published = db.Column(db.DateTime)
|
||||||
reject_reason = db.Column(db.String())
|
reject_reason = db.Column(db.String())
|
||||||
|
@ -312,12 +312,9 @@ class Proposal(db.Model):
|
||||||
# Validate fields to be database save-able.
|
# Validate fields to be database save-able.
|
||||||
# Stricter validation is done in validate_publishable.
|
# Stricter validation is done in validate_publishable.
|
||||||
stage = proposal.get('stage')
|
stage = proposal.get('stage')
|
||||||
category = proposal.get('category')
|
|
||||||
|
|
||||||
if stage and not ProposalStage.includes(stage):
|
if stage and not ProposalStage.includes(stage):
|
||||||
raise ValidationException("Proposal stage {} is not a valid stage".format(stage))
|
raise ValidationException("Proposal stage {} is not a valid stage".format(stage))
|
||||||
if category and not Category.includes(category):
|
|
||||||
raise ValidationException("Category {} not a valid category".format(category))
|
|
||||||
|
|
||||||
def validate_publishable_milestones(self):
|
def validate_publishable_milestones(self):
|
||||||
payout_total = 0.0
|
payout_total = 0.0
|
||||||
|
@ -350,7 +347,7 @@ class Proposal(db.Model):
|
||||||
self.validate_publishable_milestones()
|
self.validate_publishable_milestones()
|
||||||
|
|
||||||
# Require certain fields
|
# Require certain fields
|
||||||
required_fields = ['title', 'content', 'brief', 'category', 'target', 'payout_address']
|
required_fields = ['title', 'content', 'brief', 'target', 'payout_address']
|
||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
if not hasattr(self, field):
|
if not hasattr(self, field):
|
||||||
raise ValidationException("Proposal must have a {}".format(field))
|
raise ValidationException("Proposal must have a {}".format(field))
|
||||||
|
@ -773,7 +770,6 @@ class ProposalSchema(ma.Schema):
|
||||||
"updates",
|
"updates",
|
||||||
"milestones",
|
"milestones",
|
||||||
"current_milestone",
|
"current_milestone",
|
||||||
"category",
|
|
||||||
"team",
|
"team",
|
||||||
"payout_address",
|
"payout_address",
|
||||||
"deadline_duration",
|
"deadline_duration",
|
||||||
|
|
|
@ -192,7 +192,6 @@ def make_proposal_draft(rfp_id):
|
||||||
return {"message": "The request this proposal was made for has expired"}, 400
|
return {"message": "The request this proposal was made for has expired"}, 400
|
||||||
if rfp.status == RFPStatus.CLOSED:
|
if rfp.status == RFPStatus.CLOSED:
|
||||||
return {"message": "The request this proposal was made for has been closed"}, 400
|
return {"message": "The request this proposal was made for has been closed"}, 400
|
||||||
proposal.category = rfp.category
|
|
||||||
rfp.proposals.append(proposal)
|
rfp.proposals.append(proposal)
|
||||||
db.session.add(rfp)
|
db.session.add(rfp)
|
||||||
|
|
||||||
|
@ -227,7 +226,6 @@ def get_proposal_drafts():
|
||||||
# Length checks are to prevent database errors, not actual user limits imposed
|
# Length checks are to prevent database errors, not actual user limits imposed
|
||||||
"title": fields.Str(required=True),
|
"title": fields.Str(required=True),
|
||||||
"brief": fields.Str(required=True),
|
"brief": fields.Str(required=True),
|
||||||
"category": fields.Str(required=True, validate=validate.OneOf(choices=Category.list() + [''])),
|
|
||||||
"content": fields.Str(required=True),
|
"content": fields.Str(required=True),
|
||||||
"target": fields.Str(required=True),
|
"target": fields.Str(required=True),
|
||||||
"payoutAddress": fields.Str(required=True),
|
"payoutAddress": fields.Str(required=True),
|
||||||
|
|
|
@ -25,7 +25,7 @@ class RFP(db.Model):
|
||||||
title = db.Column(db.String(255), nullable=False)
|
title = db.Column(db.String(255), nullable=False)
|
||||||
brief = db.Column(db.String(255), nullable=False)
|
brief = db.Column(db.String(255), nullable=False)
|
||||||
content = db.Column(db.Text, nullable=False)
|
content = db.Column(db.Text, nullable=False)
|
||||||
category = db.Column(db.String(255), nullable=False)
|
category = db.Column(db.String(255), nullable=True)
|
||||||
status = db.Column(db.String(255), nullable=False)
|
status = db.Column(db.String(255), nullable=False)
|
||||||
matching = db.Column(db.Boolean, default=False, nullable=False)
|
matching = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
_bounty = db.Column("bounty", db.String(255), nullable=True)
|
_bounty = db.Column("bounty", db.String(255), nullable=True)
|
||||||
|
@ -96,20 +96,17 @@ class RFP(db.Model):
|
||||||
title: str,
|
title: str,
|
||||||
brief: str,
|
brief: str,
|
||||||
content: str,
|
content: str,
|
||||||
category: str,
|
|
||||||
bounty: str,
|
bounty: str,
|
||||||
date_closes: datetime,
|
date_closes: datetime,
|
||||||
matching: bool = False,
|
matching: bool = False,
|
||||||
status: str = RFPStatus.DRAFT,
|
status: str = RFPStatus.DRAFT,
|
||||||
):
|
):
|
||||||
assert RFPStatus.includes(status)
|
assert RFPStatus.includes(status)
|
||||||
assert Category.includes(category)
|
|
||||||
self.id = gen_random_id(RFP)
|
self.id = gen_random_id(RFP)
|
||||||
self.date_created = datetime.now()
|
self.date_created = datetime.now()
|
||||||
self.title = title[:255]
|
self.title = title[:255]
|
||||||
self.brief = brief[:255]
|
self.brief = brief[:255]
|
||||||
self.content = content
|
self.content = content
|
||||||
self.category = category
|
|
||||||
self.bounty = bounty
|
self.bounty = bounty
|
||||||
self.date_closes = date_closes
|
self.date_closes = date_closes
|
||||||
self.matching = matching
|
self.matching = matching
|
||||||
|
@ -125,7 +122,6 @@ class RFPSchema(ma.Schema):
|
||||||
"title",
|
"title",
|
||||||
"brief",
|
"brief",
|
||||||
"content",
|
"content",
|
||||||
"category",
|
|
||||||
"status",
|
"status",
|
||||||
"matching",
|
"matching",
|
||||||
"bounty",
|
"bounty",
|
||||||
|
@ -173,7 +169,6 @@ class AdminRFPSchema(ma.Schema):
|
||||||
"title",
|
"title",
|
||||||
"brief",
|
"brief",
|
||||||
"content",
|
"content",
|
||||||
"category",
|
|
||||||
"status",
|
"status",
|
||||||
"matching",
|
"matching",
|
||||||
"bounty",
|
"bounty",
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 2013e180c438
|
||||||
|
Revises: 7fea7427e9d6
|
||||||
|
Create Date: 2019-11-05 15:53:00.533347
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '2013e180c438'
|
||||||
|
down_revision = '7fea7427e9d6'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('proposal', 'category',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('rfp', 'category',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=True)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('rfp', 'category',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('proposal', 'category',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
|
@ -386,19 +386,3 @@ class TestAdminAPI(BaseProposalCreatorConfig):
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
self.assert200(resp)
|
self.assert200(resp)
|
||||||
|
|
||||||
def test_create_rfp_fails_with_bad_category(self):
|
|
||||||
self.login_admin()
|
|
||||||
|
|
||||||
resp = self.app.post(
|
|
||||||
"/api/v1/admin/rfps",
|
|
||||||
data=json.dumps({
|
|
||||||
"brief": "Some brief",
|
|
||||||
"category": "NOT_CORE_DEV",
|
|
||||||
"content": "CONTENT",
|
|
||||||
"dateCloses": 1553980004,
|
|
||||||
"status": "DRAFT",
|
|
||||||
"title": "TITLE"
|
|
||||||
})
|
|
||||||
)
|
|
||||||
self.assert400(resp)
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ class TestRfpApi(BaseProposalCreatorConfig):
|
||||||
title="title",
|
title="title",
|
||||||
brief="brief",
|
brief="brief",
|
||||||
content="content",
|
content="content",
|
||||||
category=Category.DEV_TOOL,
|
|
||||||
date_closes=datetime.datetime(2030, 1, 1),
|
date_closes=datetime.datetime(2030, 1, 1),
|
||||||
bounty="10",
|
bounty="10",
|
||||||
status=RFPStatus.DRAFT,
|
status=RFPStatus.DRAFT,
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Icon } from 'antd';
|
|
||||||
import { PROPOSAL_CATEGORY, CATEGORY_UI } from 'api/constants';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Proposal } from 'types';
|
||||||
|
import Like from 'components/Like'
|
||||||
|
|
||||||
interface CardInfoProps {
|
interface CardInfoProps {
|
||||||
category: PROPOSAL_CATEGORY;
|
proposal: Proposal;
|
||||||
time: number;
|
time: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardInfo: React.SFC<CardInfoProps> = ({ category, time }) => (
|
export const CardInfo: React.SFC<CardInfoProps> = ({ proposal, time }) => (
|
||||||
<div className="Card-info">
|
<div className="Card-info">
|
||||||
<div
|
<div
|
||||||
className="ProposalCard-info-category"
|
className="ProposalCard-info-category"
|
||||||
style={{ color: CATEGORY_UI[category].color }}
|
|
||||||
>
|
>
|
||||||
<Icon type={CATEGORY_UI[category].icon} /> {CATEGORY_UI[category].label}
|
<Like proposal={proposal} proposal_card/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ProposalCard-info-created">
|
<div className="ProposalCard-info-created">
|
||||||
{moment(time).fromNow()}
|
{moment(time).fromNow()}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Input, Form, Icon, Select, Alert, Popconfirm, message, Radio } from 'antd';
|
import { Input, Form, Alert, Popconfirm, message, Radio } from 'antd';
|
||||||
import { SelectValue } from 'antd/lib/select';
|
|
||||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||||
import { PROPOSAL_CATEGORY, CATEGORY_UI } from 'api/constants';
|
|
||||||
import { ProposalDraft, RFP } from 'types';
|
import { ProposalDraft, RFP } from 'types';
|
||||||
import { getCreateErrors } from 'modules/create/utils';
|
import { getCreateErrors } from 'modules/create/utils';
|
||||||
import { typedKeys } from 'utils/ts';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { unlinkProposalRFP } from 'modules/create/actions';
|
import { unlinkProposalRFP } from 'modules/create/actions';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
@ -31,7 +28,6 @@ type Props = OwnProps & StateProps & DispatchProps;
|
||||||
interface State extends Partial<ProposalDraft> {
|
interface State extends Partial<ProposalDraft> {
|
||||||
title: string;
|
title: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
category?: PROPOSAL_CATEGORY;
|
|
||||||
target: string;
|
target: string;
|
||||||
rfp?: RFP;
|
rfp?: RFP;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +38,6 @@ class CreateFlowBasics extends React.Component<Props, State> {
|
||||||
this.state = {
|
this.state = {
|
||||||
title: '',
|
title: '',
|
||||||
brief: '',
|
brief: '',
|
||||||
category: undefined,
|
|
||||||
target: '',
|
target: '',
|
||||||
...(props.initialState || {}),
|
...(props.initialState || {}),
|
||||||
};
|
};
|
||||||
|
@ -64,7 +59,7 @@ class CreateFlowBasics extends React.Component<Props, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isUnlinkingProposalRFP } = this.props;
|
const { isUnlinkingProposalRFP } = this.props;
|
||||||
const { title, brief, category, target, rfp, rfpOptIn } = this.state;
|
const { title, brief, target, rfp, rfpOptIn } = this.state;
|
||||||
const errors = getCreateErrors(this.state, true);
|
const errors = getCreateErrors(this.state, true);
|
||||||
|
|
||||||
// Don't show target error at zero since it defaults to that
|
// Don't show target error at zero since it defaults to that
|
||||||
|
@ -171,25 +166,6 @@ class CreateFlowBasics extends React.Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label="Category">
|
|
||||||
<Select
|
|
||||||
size="large"
|
|
||||||
placeholder="Select a category"
|
|
||||||
value={category || undefined}
|
|
||||||
onChange={this.handleCategoryChange}
|
|
||||||
>
|
|
||||||
{typedKeys(PROPOSAL_CATEGORY).map(c => (
|
|
||||||
<Select.Option value={c} key={c}>
|
|
||||||
<Icon
|
|
||||||
type={CATEGORY_UI[c].icon}
|
|
||||||
style={{ color: CATEGORY_UI[c].color }}
|
|
||||||
/>{' '}
|
|
||||||
{CATEGORY_UI[c].label}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Target amount"
|
label="Target amount"
|
||||||
validateStatus={errors.target ? 'error' : undefined}
|
validateStatus={errors.target ? 'error' : undefined}
|
||||||
|
@ -219,12 +195,6 @@ class CreateFlowBasics extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleCategoryChange = (value: SelectValue) => {
|
|
||||||
this.setState({ category: value as PROPOSAL_CATEGORY }, () => {
|
|
||||||
this.props.updateForm(this.state);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleRfpOptIn = (e: RadioChangeEvent) => {
|
private handleRfpOptIn = (e: RadioChangeEvent) => {
|
||||||
this.setState({ rfpOptIn: e.target.value }, () => {
|
this.setState({ rfpOptIn: e.target.value }, () => {
|
||||||
this.props.updateForm(this.state);
|
this.props.updateForm(this.state);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Icon, Timeline } from 'antd';
|
import { Timeline } from 'antd';
|
||||||
import { getCreateErrors, KeyOfForm, FIELD_NAME_MAP } from 'modules/create/utils';
|
import { getCreateErrors, KeyOfForm, FIELD_NAME_MAP } from 'modules/create/utils';
|
||||||
import Markdown from 'components/Markdown';
|
import Markdown from 'components/Markdown';
|
||||||
import UserAvatar from 'components/UserAvatar';
|
import UserAvatar from 'components/UserAvatar';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { CREATE_STEP } from './index';
|
import { CREATE_STEP } from './index';
|
||||||
import { CATEGORY_UI, PROPOSAL_CATEGORY } from 'api/constants';
|
|
||||||
import { ProposalDraft } from 'types';
|
import { ProposalDraft } from 'types';
|
||||||
import './Review.less';
|
import './Review.less';
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ class CreateReview extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { form } = this.props;
|
const { form } = this.props;
|
||||||
const errors = getCreateErrors(this.props.form);
|
const errors = getCreateErrors(this.props.form);
|
||||||
const catUI = CATEGORY_UI[form.category as PROPOSAL_CATEGORY] || ({} as any);
|
|
||||||
const sections: Section[] = [
|
const sections: Section[] = [
|
||||||
{
|
{
|
||||||
step: CREATE_STEP.BASICS,
|
step: CREATE_STEP.BASICS,
|
||||||
|
@ -59,15 +57,6 @@ class CreateReview extends React.Component<Props> {
|
||||||
content: form.brief,
|
content: form.brief,
|
||||||
error: errors.brief,
|
error: errors.brief,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'category',
|
|
||||||
content: (
|
|
||||||
<div style={{ color: catUI.color }}>
|
|
||||||
<Icon type={catUI.icon} /> {catUI.label}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
error: errors.category,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: 'target',
|
key: 'target',
|
||||||
content: <div style={{ fontSize: '1.2rem' }}>{form.target} ZEC</div>,
|
content: <div style={{ fontSize: '1.2rem' }}>{form.target} ZEC</div>,
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import { PROPOSAL_CATEGORY } from 'api/constants';
|
|
||||||
import { ProposalDraft } from 'types';
|
import { ProposalDraft } from 'types';
|
||||||
|
|
||||||
const createExampleProposal = (): Partial<ProposalDraft> => {
|
const createExampleProposal = (): Partial<ProposalDraft> => {
|
||||||
const cats = Object.keys(PROPOSAL_CATEGORY);
|
|
||||||
const category = cats[Math.floor(Math.random() * cats.length)] as PROPOSAL_CATEGORY;
|
|
||||||
return {
|
return {
|
||||||
title: 'Grant.io T-Shirts',
|
title: 'Grant.io T-Shirts',
|
||||||
brief: "The most stylish wear, sporting your favorite brand's logo",
|
brief: "The most stylish wear, sporting your favorite brand's logo",
|
||||||
category,
|
|
||||||
content:
|
content:
|
||||||
'![](https://i.imgur.com/aQagS0D.png)\n\nWe all know it, Grant.io is the bee\'s knees. But wouldn\'t it be great if you could show all your friends and family how much you love it? Well that\'s what we\'re here to offer today.\n\n# What We\'re Building\n\nWhy, T-Shirts of course! These beautiful shirts made out of 100% cotton and laser printed for long lasting goodness come from American Apparel. We\'ll be offering them in 4 styles:\n\n* Crew neck (wrinkled)\n* Crew neck (straight)\n* Scoop neck (fitted)\n* V neck (fitted)\n\nShirt sizings will be as follows:\n\n| Size | S | M | L | XL |\n|--------|-----|-----|-----|------|\n| **Width** | 18" | 20" | 22" | 24" |\n| **Length** | 28" | 29" | 30" | 31" |\n\n# Who We Are\n\nWe are the team behind grant.io. In addition to our software engineering experience, we have over 78 years of T-Shirt printing expertise combined. Sometimes I wake up at night and realize I was printing shirts in my dreams. Weird, man.\n\n# Expense Breakdown\n\n* $1,000 - A professional designer will hand-craft each letter on the shirt.\n* $500 - We\'ll get the shirt printed from 5 different factories and choose the best quality one.\n* $3,000 - The full run of prints, with 20 smalls, 20 mediums, and 20 larges.\n* $500 - Pizza. Lots of pizza.\n\n**Total**: $5,000',
|
'![](https://i.imgur.com/aQagS0D.png)\n\nWe all know it, Grant.io is the bee\'s knees. But wouldn\'t it be great if you could show all your friends and family how much you love it? Well that\'s what we\'re here to offer today.\n\n# What We\'re Building\n\nWhy, T-Shirts of course! These beautiful shirts made out of 100% cotton and laser printed for long lasting goodness come from American Apparel. We\'ll be offering them in 4 styles:\n\n* Crew neck (wrinkled)\n* Crew neck (straight)\n* Scoop neck (fitted)\n* V neck (fitted)\n\nShirt sizings will be as follows:\n\n| Size | S | M | L | XL |\n|--------|-----|-----|-----|------|\n| **Width** | 18" | 20" | 22" | 24" |\n| **Length** | 28" | 29" | 30" | 31" |\n\n# Who We Are\n\nWe are the team behind grant.io. In addition to our software engineering experience, we have over 78 years of T-Shirt printing expertise combined. Sometimes I wake up at night and realize I was printing shirts in my dreams. Weird, man.\n\n# Expense Breakdown\n\n* $1,000 - A professional designer will hand-craft each letter on the shirt.\n* $500 - We\'ll get the shirt printed from 5 different factories and choose the best quality one.\n* $3,000 - The full run of prints, with 20 smalls, 20 mediums, and 20 larges.\n* $500 - Pizza. Lots of pizza.\n\n**Total**: $5,000',
|
||||||
target: '5',
|
target: '5',
|
||||||
|
|
|
@ -4,14 +4,15 @@ import { Icon, Button, Input, message } from 'antd';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { proposalActions } from 'modules/proposals';
|
import { proposalActions } from 'modules/proposals';
|
||||||
import { rfpActions } from 'modules/rfps';
|
import { rfpActions } from 'modules/rfps';
|
||||||
import { ProposalDetail } from 'modules/proposals/reducers';
|
import { Proposal } from 'types';
|
||||||
import { Comment, RFP } from 'types';
|
import { Comment, RFP } from 'types';
|
||||||
import { likeProposal, likeComment, likeRfp } from 'api/api';
|
import { likeProposal, likeComment, likeRfp } from 'api/api';
|
||||||
import AuthButton from 'components/AuthButton';
|
import AuthButton from 'components/AuthButton';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
proposal?: ProposalDetail | null;
|
proposal?: Proposal;
|
||||||
|
proposal_card?: boolean;
|
||||||
comment?: Comment;
|
comment?: Comment;
|
||||||
rfp?: RFP;
|
rfp?: RFP;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
@ -34,19 +35,27 @@ const STATE = {
|
||||||
};
|
};
|
||||||
type State = typeof STATE;
|
type State = typeof STATE;
|
||||||
|
|
||||||
class Follow extends React.Component<Props, State> {
|
class Like extends React.Component<Props, State> {
|
||||||
state: State = { ...STATE };
|
state: State = { ...STATE };
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { likesCount, authedLiked } = this.deriveInfo();
|
const { likesCount, authedLiked } = this.deriveInfo();
|
||||||
const { proposal, rfp, comment, style } = this.props;
|
const { proposal, rfp, comment, style, proposal_card } = this.props;
|
||||||
const { loading } = this.state;
|
const { loading } = this.state;
|
||||||
const zoom = comment ? 0.8 : 1;
|
const zoom = comment || proposal_card ? 0.8 : 1;
|
||||||
const shouldShowLikeText = !!proposal || !!rfp;
|
const shouldShowLikeText = (!!proposal && !proposal_card) || !!rfp;
|
||||||
|
|
||||||
|
// if like button is on a proposal card...
|
||||||
|
// 1) use regular button to prevent login redirect
|
||||||
|
const IconButton = proposal_card ? Button : AuthButton;
|
||||||
|
// 2) prevent mouseover effects
|
||||||
|
const pointerEvents = proposal_card ? 'none' : undefined;
|
||||||
|
// 3) make button click a noop
|
||||||
|
const handleIconButtonClick = proposal_card ? undefined : this.handleLike;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input.Group className="Like" compact style={{ zoom, ...style }}>
|
<Input.Group className="Like" compact style={{ zoom, pointerEvents, ...style }}>
|
||||||
<AuthButton onClick={this.handleLike}>
|
<IconButton onClick={handleIconButtonClick}>
|
||||||
<Icon
|
<Icon
|
||||||
theme={authedLiked ? 'filled' : 'outlined'}
|
theme={authedLiked ? 'filled' : 'outlined'}
|
||||||
type={loading ? 'loading' : 'like'}
|
type={loading ? 'loading' : 'like'}
|
||||||
|
@ -54,7 +63,7 @@ class Follow extends React.Component<Props, State> {
|
||||||
{shouldShowLikeText && (
|
{shouldShowLikeText && (
|
||||||
<span className="Like-label">{authedLiked ? ' Unlike' : ' Like'}</span>
|
<span className="Like-label">{authedLiked ? ' Unlike' : ' Like'}</span>
|
||||||
)}
|
)}
|
||||||
</AuthButton>
|
</IconButton>
|
||||||
<Button className="Like-count" disabled>
|
<Button className="Like-count" disabled>
|
||||||
<span>{likesCount}</span>
|
<span>{likesCount}</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -175,4 +184,4 @@ const withConnect = connect<StateProps, DispatchProps, OwnProps, AppState>(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withConnect(Follow);
|
export default withConnect(Like);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { AppState } from 'store/reducers';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import UnitDisplay from 'components/UnitDisplay';
|
import UnitDisplay from 'components/UnitDisplay';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import { CATEGORY_UI, PROPOSAL_STAGE } from 'api/constants';
|
import { PROPOSAL_STAGE } from 'api/constants';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
|
@ -87,16 +87,6 @@ export class ProposalCampaignBlock extends React.Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="ProposalCampaignBlock-info">
|
|
||||||
<div className="ProposalCampaignBlock-info-label">Category</div>
|
|
||||||
<div className="ProposalCampaignBlock-info-value">
|
|
||||||
<Icon
|
|
||||||
type={CATEGORY_UI[proposal.category].icon}
|
|
||||||
style={{ color: CATEGORY_UI[proposal.category].color }}
|
|
||||||
/>{' '}
|
|
||||||
{CATEGORY_UI[proposal.category].label}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{!isVersionTwo &&
|
{!isVersionTwo &&
|
||||||
!isFundingOver && (
|
!isFundingOver && (
|
||||||
<div className="ProposalCampaignBlock-info">
|
<div className="ProposalCampaignBlock-info">
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Select, Checkbox, Radio, Card, Divider } from 'antd';
|
import { Select, Radio, Card } from 'antd';
|
||||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||||
import { SelectValue } from 'antd/lib/select';
|
import { SelectValue } from 'antd/lib/select';
|
||||||
import {
|
import {
|
||||||
PROPOSAL_SORT,
|
PROPOSAL_SORT,
|
||||||
SORT_LABELS,
|
SORT_LABELS,
|
||||||
PROPOSAL_CATEGORY,
|
|
||||||
CATEGORY_UI,
|
|
||||||
PROPOSAL_STAGE,
|
PROPOSAL_STAGE,
|
||||||
STAGE_UI,
|
STAGE_UI,
|
||||||
} from 'api/constants';
|
} from 'api/constants';
|
||||||
|
@ -39,21 +37,6 @@ export default class ProposalFilters extends React.Component<Props> {
|
||||||
<div style={{ marginBottom: '1rem' }} />
|
<div style={{ marginBottom: '1rem' }} />
|
||||||
|
|
||||||
<Card title="Filter" extra={<a onClick={this.resetFilters}>Reset</a>}>
|
<Card title="Filter" extra={<a onClick={this.resetFilters}>Reset</a>}>
|
||||||
<h3>Category</h3>
|
|
||||||
{typedKeys(PROPOSAL_CATEGORY).map(c => (
|
|
||||||
<div key={c} style={{ marginBottom: '0.25rem' }}>
|
|
||||||
<Checkbox
|
|
||||||
checked={filters.category.includes(c as PROPOSAL_CATEGORY)}
|
|
||||||
value={c}
|
|
||||||
onChange={this.handleCategoryChange}
|
|
||||||
>
|
|
||||||
{CATEGORY_UI[c].label}
|
|
||||||
</Checkbox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<h3>Proposal stage</h3>
|
<h3>Proposal stage</h3>
|
||||||
<div style={{ marginBottom: '0.25rem' }}>
|
<div style={{ marginBottom: '0.25rem' }}>
|
||||||
<Radio
|
<Radio
|
||||||
|
@ -92,19 +75,6 @@ export default class ProposalFilters extends React.Component<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleCategoryChange = (ev: RadioChangeEvent) => {
|
|
||||||
const { filters } = this.props;
|
|
||||||
const cat = ev.target.value as PROPOSAL_CATEGORY;
|
|
||||||
const category = ev.target.checked
|
|
||||||
? [...filters.category, cat]
|
|
||||||
: filters.category.filter(c => c !== cat);
|
|
||||||
|
|
||||||
this.props.handleChangeFilters({
|
|
||||||
...filters,
|
|
||||||
category,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleStageChange = (ev: RadioChangeEvent) => {
|
private handleStageChange = (ev: RadioChangeEvent) => {
|
||||||
let stage = [] as PROPOSAL_STAGE[];
|
let stage = [] as PROPOSAL_STAGE[];
|
||||||
if (ev.target.value !== 'ALL') {
|
if (ev.target.value !== 'ALL') {
|
||||||
|
|
|
@ -18,7 +18,6 @@ export class ProposalCard extends React.Component<Proposal> {
|
||||||
title,
|
title,
|
||||||
proposalAddress,
|
proposalAddress,
|
||||||
proposalUrlId,
|
proposalUrlId,
|
||||||
category,
|
|
||||||
datePublished,
|
datePublished,
|
||||||
dateCreated,
|
dateCreated,
|
||||||
team,
|
team,
|
||||||
|
@ -74,7 +73,7 @@ export class ProposalCard extends React.Component<Proposal> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ProposalCard-address">{proposalAddress}</div>
|
<div className="ProposalCard-address">{proposalAddress}</div>
|
||||||
<Card.Info category={category} time={(datePublished || dateCreated) * 1000} />
|
<Card.Info proposal={this.props} time={(datePublished || dateCreated) * 1000} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
isValidSproutAddress,
|
isValidSproutAddress,
|
||||||
} from 'utils/validators';
|
} from 'utils/validators';
|
||||||
import { Zat, toZat } from 'utils/units';
|
import { Zat, toZat } from 'utils/units';
|
||||||
import { PROPOSAL_CATEGORY, PROPOSAL_STAGE } from 'api/constants';
|
import { PROPOSAL_STAGE } from 'api/constants';
|
||||||
import {
|
import {
|
||||||
ProposalDetail,
|
ProposalDetail,
|
||||||
PROPOSAL_DETAIL_INITIAL_STATE,
|
PROPOSAL_DETAIL_INITIAL_STATE,
|
||||||
|
@ -17,7 +17,6 @@ interface CreateFormErrors {
|
||||||
rfpOptIn?: string;
|
rfpOptIn?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
brief?: string;
|
brief?: string;
|
||||||
category?: string;
|
|
||||||
target?: string;
|
target?: string;
|
||||||
team?: string[];
|
team?: string[];
|
||||||
content?: string;
|
content?: string;
|
||||||
|
@ -30,7 +29,6 @@ export const FIELD_NAME_MAP: { [key in KeyOfForm]: string } = {
|
||||||
rfpOptIn: 'RFP KYC',
|
rfpOptIn: 'RFP KYC',
|
||||||
title: 'Title',
|
title: 'Title',
|
||||||
brief: 'Brief',
|
brief: 'Brief',
|
||||||
category: 'Category',
|
|
||||||
target: 'Target amount',
|
target: 'Target amount',
|
||||||
team: 'Team',
|
team: 'Team',
|
||||||
content: 'Details',
|
content: 'Details',
|
||||||
|
@ -41,7 +39,6 @@ export const FIELD_NAME_MAP: { [key in KeyOfForm]: string } = {
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
'title',
|
'title',
|
||||||
'brief',
|
'brief',
|
||||||
'category',
|
|
||||||
'target',
|
'target',
|
||||||
'content',
|
'content',
|
||||||
'payoutAddress',
|
'payoutAddress',
|
||||||
|
@ -231,7 +228,6 @@ export function makeProposalPreviewFromDraft(draft: ProposalDraft): ProposalDeta
|
||||||
contributionBounty: Zat('0'),
|
contributionBounty: Zat('0'),
|
||||||
percentFunded: 0,
|
percentFunded: 0,
|
||||||
stage: PROPOSAL_STAGE.PREVIEW,
|
stage: PROPOSAL_STAGE.PREVIEW,
|
||||||
category: draft.category || PROPOSAL_CATEGORY.CORE_DEV,
|
|
||||||
isStaked: true,
|
isStaked: true,
|
||||||
arbiter: {
|
arbiter: {
|
||||||
status: PROPOSAL_ARBITER_STATUS.ACCEPTED,
|
status: PROPOSAL_ARBITER_STATUS.ACCEPTED,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
STATUS,
|
STATUS,
|
||||||
PROPOSAL_ARBITER_STATUS,
|
PROPOSAL_ARBITER_STATUS,
|
||||||
} from 'types';
|
} from 'types';
|
||||||
import { PROPOSAL_CATEGORY, PROPOSAL_STAGE } from 'api/constants';
|
import { PROPOSAL_STAGE } from 'api/constants';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
@ -160,7 +160,6 @@ export function generateProposal({
|
||||||
brief: 'A cool test crowdfund',
|
brief: 'A cool test crowdfund',
|
||||||
content: 'body',
|
content: 'body',
|
||||||
stage: PROPOSAL_STAGE.WIP,
|
stage: PROPOSAL_STAGE.WIP,
|
||||||
category: PROPOSAL_CATEGORY.COMMUNITY,
|
|
||||||
isStaked: true,
|
isStaked: true,
|
||||||
authedFollows: false,
|
authedFollows: false,
|
||||||
followersCount: 0,
|
followersCount: 0,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Zat } from 'utils/units';
|
import { Zat } from 'utils/units';
|
||||||
import { PROPOSAL_CATEGORY, PROPOSAL_STAGE } from 'api/constants';
|
import { PROPOSAL_STAGE } from 'api/constants';
|
||||||
import { CreateMilestone, Update, User, Comment, ContributionWithUser } from 'types';
|
import { CreateMilestone, Update, User, Comment, ContributionWithUser } from 'types';
|
||||||
import { ProposalMilestone } from './milestone';
|
import { ProposalMilestone } from './milestone';
|
||||||
import { RFP } from './rfp';
|
import { RFP } from './rfp';
|
||||||
|
@ -34,7 +34,6 @@ export interface ProposalDraft {
|
||||||
dateCreated: number;
|
dateCreated: number;
|
||||||
title: string;
|
title: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
category: PROPOSAL_CATEGORY;
|
|
||||||
content: string;
|
content: string;
|
||||||
stage: PROPOSAL_STAGE;
|
stage: PROPOSAL_STAGE;
|
||||||
target: string;
|
target: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Proposal } from './proposal';
|
import { Proposal } from './proposal';
|
||||||
import { PROPOSAL_CATEGORY, RFP_STATUS } from 'api/constants';
|
import { RFP_STATUS } from 'api/constants';
|
||||||
import { Zat } from 'utils/units';
|
import { Zat } from 'utils/units';
|
||||||
|
|
||||||
export interface RFP {
|
export interface RFP {
|
||||||
|
@ -8,7 +8,6 @@ export interface RFP {
|
||||||
title: string;
|
title: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
content: string;
|
content: string;
|
||||||
category: PROPOSAL_CATEGORY;
|
|
||||||
status: RFP_STATUS;
|
status: RFP_STATUS;
|
||||||
acceptedProposals: Proposal[];
|
acceptedProposals: Proposal[];
|
||||||
bounty: Zat | null;
|
bounty: Zat | null;
|
||||||
|
|
Loading…
Reference in New Issue