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:
Danny Skubak 2019-11-13 18:23:36 -05:00 committed by Daniel Ternyak
parent ed6d98ceec
commit 8cfec5de5d
23 changed files with 87 additions and 201 deletions

View File

@ -445,7 +445,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
{renderDeetItem('isFailed', JSON.stringify(p.isFailed))}
{renderDeetItem('status', p.status)}
{renderDeetItem('stage', p.stage)}
{renderDeetItem('category', p.category)}
{renderDeetItem('target', p.target)}
{renderDeetItem('contributed', p.contributed)}
{renderDeetItem('funded (inc. matching)', p.funded)}

View File

@ -37,9 +37,11 @@ class RFPDetail extends React.Component<Props> {
</div>
);
const pendingProposals = rfp.proposals.filter(p => p.status === PROPOSAL_STATUS.PENDING);
const acceptedProposals = rfp.proposals.filter(p =>
p.status === PROPOSAL_STATUS.LIVE || p.status === PROPOSAL_STATUS.APPROVED
const pendingProposals = rfp.proposals.filter(
p => p.status === PROPOSAL_STATUS.PENDING,
);
const acceptedProposals = rfp.proposals.filter(
p => p.status === PROPOSAL_STATUS.LIVE || p.status === PROPOSAL_STATUS.APPROVED,
);
return (
@ -90,10 +92,12 @@ class RFPDetail extends React.Component<Props> {
{renderDeetItem('id', rfp.id)}
{renderDeetItem('created', formatDateSeconds(rfp.dateCreated))}
{renderDeetItem('status', rfp.status)}
{renderDeetItem('category', rfp.category)}
{renderDeetItem('matching', String(rfp.matching))}
{renderDeetItem('bounty', `${rfp.bounty} ZEC`)}
{renderDeetItem('dateCloses', rfp.dateCloses && formatDateSeconds(rfp.dateCloses))}
{renderDeetItem(
'dateCloses',
rfp.dateCloses && formatDateSeconds(rfp.dateCloses),
)}
</Card>
{/* PROPOSALS */}

View File

@ -3,22 +3,10 @@ import moment from 'moment';
import { view } from 'react-easy-state';
import { RouteComponentProps, withRouter } from 'react-router';
import { Link } from 'react-router-dom';
import {
Form,
Input,
Select,
Icon,
Button,
message,
Spin,
Row,
Col,
DatePicker,
} from 'antd';
import { Form, Input, Select, Button, message, Spin, Row, Col, DatePicker } from 'antd';
import Exception from 'ant-design-pro/lib/Exception';
import { FormComponentProps } from 'antd/lib/form';
import { PROPOSAL_CATEGORY, RFP_STATUS, RFPArgs } from 'src/types';
import { CATEGORY_UI } from 'util/ui';
import { RFP_STATUS, RFPArgs } from 'src/types';
import { typedKeys } from 'util/ts';
import { RFP_STATUSES, getStatusById } from 'util/statuses';
import Markdown from 'components/Markdown';
@ -53,7 +41,6 @@ class RFPForm extends React.Component<Props, State> {
title: '',
brief: '',
content: '',
category: '',
status: '',
matching: false,
bounty: undefined,
@ -71,7 +58,6 @@ class RFPForm extends React.Component<Props, State> {
title: rfp.title,
brief: rfp.brief,
content: rfp.content,
category: rfp.category,
status: rfp.status,
matching: rfp.matching,
bounty: rfp.bounty,
@ -130,28 +116,6 @@ class RFPForm extends React.Component<Props, State> {
</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">
{getFieldDecorator('brief', {
initialValue: defaults.brief,

View File

@ -42,7 +42,6 @@ export interface RFP {
title: string;
brief: string;
content: string;
category: string;
status: string;
proposals: Proposal[];
matching: boolean;
@ -53,7 +52,6 @@ export interface RFPArgs {
title: string;
brief: string;
content: string;
category: string;
matching: boolean;
dateCloses: number | null | undefined;
bounty: string | null | undefined;
@ -103,7 +101,6 @@ export interface Proposal {
title: string;
content: string;
stage: PROPOSAL_STAGE;
category: string;
milestones: Milestone[];
currentMilestone?: Milestone;
team: User[];

View File

@ -487,7 +487,6 @@ def get_rfps():
"title": fields.Str(required=True),
"brief": 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),
"matching": fields.Bool(required=False, missing=False),
"dateCloses": fields.Int(required=False, missing=None)
@ -519,13 +518,12 @@ def get_rfp(rfp_id):
"brief": fields.Str(required=True),
"status": fields.Str(required=True, validate=validate.OneOf(choices=RFPStatus.list())),
"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),
"matching": fields.Bool(required=False, default=False, missing=False),
"dateCloses": fields.Int(required=False, missing=None),
})
@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()
if not rfp:
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.brief = brief
rfp.content = content
rfp.category = category
rfp.matching = matching
rfp.bounty = bounty
rfp.date_closes = datetime.fromtimestamp(date_closes) if date_closes else None

View File

@ -241,7 +241,7 @@ class Proposal(db.Model):
brief = db.Column(db.String(255), nullable=False)
stage = db.Column(db.String(255), 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_published = db.Column(db.DateTime)
reject_reason = db.Column(db.String())
@ -312,12 +312,9 @@ class Proposal(db.Model):
# Validate fields to be database save-able.
# Stricter validation is done in validate_publishable.
stage = proposal.get('stage')
category = proposal.get('category')
if stage and not ProposalStage.includes(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):
payout_total = 0.0
@ -350,7 +347,7 @@ class Proposal(db.Model):
self.validate_publishable_milestones()
# 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:
if not hasattr(self, field):
raise ValidationException("Proposal must have a {}".format(field))
@ -773,7 +770,6 @@ class ProposalSchema(ma.Schema):
"updates",
"milestones",
"current_milestone",
"category",
"team",
"payout_address",
"deadline_duration",

View File

@ -192,7 +192,6 @@ def make_proposal_draft(rfp_id):
return {"message": "The request this proposal was made for has expired"}, 400
if rfp.status == RFPStatus.CLOSED:
return {"message": "The request this proposal was made for has been closed"}, 400
proposal.category = rfp.category
rfp.proposals.append(proposal)
db.session.add(rfp)
@ -227,7 +226,6 @@ def get_proposal_drafts():
# Length checks are to prevent database errors, not actual user limits imposed
"title": 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),
"target": fields.Str(required=True),
"payoutAddress": fields.Str(required=True),

View File

@ -25,7 +25,7 @@ class RFP(db.Model):
title = db.Column(db.String(255), nullable=False)
brief = db.Column(db.String(255), 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)
matching = db.Column(db.Boolean, default=False, nullable=False)
_bounty = db.Column("bounty", db.String(255), nullable=True)
@ -96,20 +96,17 @@ class RFP(db.Model):
title: str,
brief: str,
content: str,
category: str,
bounty: str,
date_closes: datetime,
matching: bool = False,
status: str = RFPStatus.DRAFT,
):
assert RFPStatus.includes(status)
assert Category.includes(category)
self.id = gen_random_id(RFP)
self.date_created = datetime.now()
self.title = title[:255]
self.brief = brief[:255]
self.content = content
self.category = category
self.bounty = bounty
self.date_closes = date_closes
self.matching = matching
@ -125,7 +122,6 @@ class RFPSchema(ma.Schema):
"title",
"brief",
"content",
"category",
"status",
"matching",
"bounty",
@ -173,7 +169,6 @@ class AdminRFPSchema(ma.Schema):
"title",
"brief",
"content",
"category",
"status",
"matching",
"bounty",

View File

@ -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 ###

View File

@ -386,19 +386,3 @@ class TestAdminAPI(BaseProposalCreatorConfig):
})
)
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)

View File

@ -11,7 +11,6 @@ class TestRfpApi(BaseProposalCreatorConfig):
title="title",
brief="brief",
content="content",
category=Category.DEV_TOOL,
date_closes=datetime.datetime(2030, 1, 1),
bounty="10",
status=RFPStatus.DRAFT,

View File

@ -1,23 +1,22 @@
import React from 'react';
import moment from 'moment';
import classnames from 'classnames';
import { Icon } from 'antd';
import { PROPOSAL_CATEGORY, CATEGORY_UI } from 'api/constants';
import './index.less';
import { Link } from 'react-router-dom';
import { Proposal } from 'types';
import Like from 'components/Like'
interface CardInfoProps {
category: PROPOSAL_CATEGORY;
proposal: Proposal;
time: number;
}
export const CardInfo: React.SFC<CardInfoProps> = ({ category, time }) => (
export const CardInfo: React.SFC<CardInfoProps> = ({ proposal, time }) => (
<div className="Card-info">
<div
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 className="ProposalCard-info-created">
{moment(time).fromNow()}

View File

@ -1,12 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { Input, Form, Icon, Select, Alert, Popconfirm, message, Radio } from 'antd';
import { SelectValue } from 'antd/lib/select';
import { Input, Form, Alert, Popconfirm, message, Radio } from 'antd';
import { RadioChangeEvent } from 'antd/lib/radio';
import { PROPOSAL_CATEGORY, CATEGORY_UI } from 'api/constants';
import { ProposalDraft, RFP } from 'types';
import { getCreateErrors } from 'modules/create/utils';
import { typedKeys } from 'utils/ts';
import { Link } from 'react-router-dom';
import { unlinkProposalRFP } from 'modules/create/actions';
import { AppState } from 'store/reducers';
@ -31,7 +28,6 @@ type Props = OwnProps & StateProps & DispatchProps;
interface State extends Partial<ProposalDraft> {
title: string;
brief: string;
category?: PROPOSAL_CATEGORY;
target: string;
rfp?: RFP;
}
@ -42,7 +38,6 @@ class CreateFlowBasics extends React.Component<Props, State> {
this.state = {
title: '',
brief: '',
category: undefined,
target: '',
...(props.initialState || {}),
};
@ -64,7 +59,7 @@ class CreateFlowBasics extends React.Component<Props, State> {
render() {
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);
// 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 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
label="Target amount"
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) => {
this.setState({ rfpOptIn: e.target.value }, () => {
this.props.updateForm(this.state);

View File

@ -1,12 +1,11 @@
import React from 'react';
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 Markdown from 'components/Markdown';
import UserAvatar from 'components/UserAvatar';
import { AppState } from 'store/reducers';
import { CREATE_STEP } from './index';
import { CATEGORY_UI, PROPOSAL_CATEGORY } from 'api/constants';
import { ProposalDraft } from 'types';
import './Review.less';
@ -37,7 +36,6 @@ class CreateReview extends React.Component<Props> {
render() {
const { form } = this.props;
const errors = getCreateErrors(this.props.form);
const catUI = CATEGORY_UI[form.category as PROPOSAL_CATEGORY] || ({} as any);
const sections: Section[] = [
{
step: CREATE_STEP.BASICS,
@ -59,15 +57,6 @@ class CreateReview extends React.Component<Props> {
content: form.brief,
error: errors.brief,
},
{
key: 'category',
content: (
<div style={{ color: catUI.color }}>
<Icon type={catUI.icon} /> {catUI.label}
</div>
),
error: errors.category,
},
{
key: 'target',
content: <div style={{ fontSize: '1.2rem' }}>{form.target} ZEC</div>,

View File

@ -1,13 +1,9 @@
import { PROPOSAL_CATEGORY } from 'api/constants';
import { ProposalDraft } from 'types';
const createExampleProposal = (): Partial<ProposalDraft> => {
const cats = Object.keys(PROPOSAL_CATEGORY);
const category = cats[Math.floor(Math.random() * cats.length)] as PROPOSAL_CATEGORY;
return {
title: 'Grant.io T-Shirts',
brief: "The most stylish wear, sporting your favorite brand's logo",
category,
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',
target: '5',

View File

@ -4,14 +4,15 @@ import { Icon, Button, Input, message } from 'antd';
import { AppState } from 'store/reducers';
import { proposalActions } from 'modules/proposals';
import { rfpActions } from 'modules/rfps';
import { ProposalDetail } from 'modules/proposals/reducers';
import { Proposal } from 'types';
import { Comment, RFP } from 'types';
import { likeProposal, likeComment, likeRfp } from 'api/api';
import AuthButton from 'components/AuthButton';
import './index.less';
interface OwnProps {
proposal?: ProposalDetail | null;
proposal?: Proposal;
proposal_card?: boolean;
comment?: Comment;
rfp?: RFP;
style?: React.CSSProperties;
@ -34,19 +35,27 @@ const STATE = {
};
type State = typeof STATE;
class Follow extends React.Component<Props, State> {
class Like extends React.Component<Props, State> {
state: State = { ...STATE };
render() {
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 zoom = comment ? 0.8 : 1;
const shouldShowLikeText = !!proposal || !!rfp;
const zoom = comment || proposal_card ? 0.8 : 1;
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 (
<Input.Group className="Like" compact style={{ zoom, ...style }}>
<AuthButton onClick={this.handleLike}>
<Input.Group className="Like" compact style={{ zoom, pointerEvents, ...style }}>
<IconButton onClick={handleIconButtonClick}>
<Icon
theme={authedLiked ? 'filled' : 'outlined'}
type={loading ? 'loading' : 'like'}
@ -54,7 +63,7 @@ class Follow extends React.Component<Props, State> {
{shouldShowLikeText && (
<span className="Like-label">{authedLiked ? ' Unlike' : ' Like'}</span>
)}
</AuthButton>
</IconButton>
<Button className="Like-count" disabled>
<span>{likesCount}</span>
</Button>
@ -175,4 +184,4 @@ const withConnect = connect<StateProps, DispatchProps, OwnProps, AppState>(
},
);
export default withConnect(Follow);
export default withConnect(Like);

View File

@ -9,7 +9,7 @@ import { AppState } from 'store/reducers';
import { withRouter } from 'react-router';
import UnitDisplay from 'components/UnitDisplay';
import Loader from 'components/Loader';
import { CATEGORY_UI, PROPOSAL_STAGE } from 'api/constants';
import { PROPOSAL_STAGE } from 'api/constants';
import './style.less';
interface OwnProps {
@ -87,16 +87,6 @@ export class ProposalCampaignBlock extends React.Component<Props, State> {
</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 &&
!isFundingOver && (
<div className="ProposalCampaignBlock-info">

View File

@ -1,12 +1,10 @@
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 { SelectValue } from 'antd/lib/select';
import {
PROPOSAL_SORT,
SORT_LABELS,
PROPOSAL_CATEGORY,
CATEGORY_UI,
PROPOSAL_STAGE,
STAGE_UI,
} from 'api/constants';
@ -39,21 +37,6 @@ export default class ProposalFilters extends React.Component<Props> {
<div style={{ marginBottom: '1rem' }} />
<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>
<div style={{ marginBottom: '0.25rem' }}>
<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) => {
let stage = [] as PROPOSAL_STAGE[];
if (ev.target.value !== 'ALL') {

View File

@ -18,7 +18,6 @@ export class ProposalCard extends React.Component<Proposal> {
title,
proposalAddress,
proposalUrlId,
category,
datePublished,
dateCreated,
team,
@ -74,7 +73,7 @@ export class ProposalCard extends React.Component<Proposal> {
</div>
</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>
);
}

View File

@ -7,7 +7,7 @@ import {
isValidSproutAddress,
} from 'utils/validators';
import { Zat, toZat } from 'utils/units';
import { PROPOSAL_CATEGORY, PROPOSAL_STAGE } from 'api/constants';
import { PROPOSAL_STAGE } from 'api/constants';
import {
ProposalDetail,
PROPOSAL_DETAIL_INITIAL_STATE,
@ -17,7 +17,6 @@ interface CreateFormErrors {
rfpOptIn?: string;
title?: string;
brief?: string;
category?: string;
target?: string;
team?: string[];
content?: string;
@ -30,7 +29,6 @@ export const FIELD_NAME_MAP: { [key in KeyOfForm]: string } = {
rfpOptIn: 'RFP KYC',
title: 'Title',
brief: 'Brief',
category: 'Category',
target: 'Target amount',
team: 'Team',
content: 'Details',
@ -41,7 +39,6 @@ export const FIELD_NAME_MAP: { [key in KeyOfForm]: string } = {
const requiredFields = [
'title',
'brief',
'category',
'target',
'content',
'payoutAddress',
@ -231,7 +228,6 @@ export function makeProposalPreviewFromDraft(draft: ProposalDraft): ProposalDeta
contributionBounty: Zat('0'),
percentFunded: 0,
stage: PROPOSAL_STAGE.PREVIEW,
category: draft.category || PROPOSAL_CATEGORY.CORE_DEV,
isStaked: true,
arbiter: {
status: PROPOSAL_ARBITER_STATUS.ACCEPTED,

View File

@ -6,7 +6,7 @@ import {
STATUS,
PROPOSAL_ARBITER_STATUS,
} from 'types';
import { PROPOSAL_CATEGORY, PROPOSAL_STAGE } from 'api/constants';
import { PROPOSAL_STAGE } from 'api/constants';
import BN from 'bn.js';
import moment from 'moment';
@ -160,7 +160,6 @@ export function generateProposal({
brief: 'A cool test crowdfund',
content: 'body',
stage: PROPOSAL_STAGE.WIP,
category: PROPOSAL_CATEGORY.COMMUNITY,
isStaked: true,
authedFollows: false,
followersCount: 0,

View File

@ -1,5 +1,5 @@
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 { ProposalMilestone } from './milestone';
import { RFP } from './rfp';
@ -34,7 +34,6 @@ export interface ProposalDraft {
dateCreated: number;
title: string;
brief: string;
category: PROPOSAL_CATEGORY;
content: string;
stage: PROPOSAL_STAGE;
target: string;

View File

@ -1,5 +1,5 @@
import { Proposal } from './proposal';
import { PROPOSAL_CATEGORY, RFP_STATUS } from 'api/constants';
import { RFP_STATUS } from 'api/constants';
import { Zat } from 'utils/units';
export interface RFP {
@ -8,7 +8,6 @@ export interface RFP {
title: string;
brief: string;
content: string;
category: PROPOSAL_CATEGORY;
status: RFP_STATUS;
acceptedProposals: Proposal[];
bounty: Zat | null;