Frontend for new RFP fields.

This commit is contained in:
Will O'Beirne 2019-02-08 14:02:34 -05:00
parent 64da535650
commit 263764255b
No known key found for this signature in database
GPG Key ID: 44C190DB5DEAF9F6
10 changed files with 92 additions and 17 deletions

View File

@ -253,7 +253,7 @@ class RFPForm extends React.Component<Props, State> {
const rfpId = this.getRFPId();
const values = {
...rawValues,
dateCloses: rawValues.dateCloses.unix(),
dateCloses: rawValues.dateCloses && rawValues.dateCloses.unix(),
};
let msg;
if (rfpId) {

View File

@ -286,12 +286,15 @@ def get_rfps():
parameter('content', type=str),
parameter('category', type=str),
parameter('bounty', type=str),
parameter('matching', type=bool),
parameter('matching', type=bool, default=False),
parameter('dateCloses', type=int),
)
@admin_auth_required
def create_rfp(**kwargs):
rfp = RFP(**kwargs)
def create_rfp(date_closes, **kwargs):
rfp = RFP(
**kwargs,
date_closes=datetime.fromtimestamp(date_closes) if date_closes else None,
)
db.session.add(rfp)
db.session.commit()
return admin_rfp_schema.dump(rfp), 201
@ -336,7 +339,7 @@ def update_rfp(rfp_id, title, brief, content, category, bounty, matching, date_c
# Update timestamps if status changed
if rfp.status != status:
if status == RFPStatus.LIVE:
if status == RFPStatus.LIVE and not rfp.date_opened:
rfp.date_opened = datetime.now()
if status == RFPStatus.CLOSED:
rfp.date_closed = datetime.now()

View File

@ -161,9 +161,9 @@ def make_proposal_draft(rfp_id):
rfp = RFP.query.filter_by(id=rfp_id).first()
if not rfp:
return {"message": "The request this proposal was made for doesnt exist"}, 400
proposal.title = rfp.title
proposal.brief = rfp.brief
proposal.category = rfp.category
if rfp.matching:
proposal.matching = 1.0
rfp.proposals.append(proposal)
db.session.add(rfp)

View File

@ -68,15 +68,35 @@ class RFPSchema(ma.Schema):
"content",
"category",
"status",
"matching",
"bounty",
"date_created",
"date_closes",
"date_opened",
"date_closed",
"accepted_proposals",
)
date_created = ma.Method("get_date_created")
status = ma.Method("get_status")
date_closes = ma.Method("get_date_closes")
date_opened = ma.Method("get_date_opened")
date_closed = ma.Method("get_date_closed")
accepted_proposals = ma.Nested("ProposalSchema", many=True, exclude=["rfp"])
def get_date_created(self, obj):
return dt_to_unix(obj.date_created)
def get_status(self, obj):
# Force it into closed state if date_closes is in the past
if obj.date_closes and obj.date_closes <= datetime.today():
return RFPStatus.CLOSED
return obj.status
def get_date_closes(self, obj):
return dt_to_unix(obj.date_closes) if obj.date_closes else None
def get_date_opened(self, obj):
return dt_to_unix(obj.date_opened) if obj.date_opened else None
def get_date_closed(self, obj):
return dt_to_unix(obj.date_closed) if obj.date_closed else None
rfp_schema = RFPSchema()
rfps_schema = RFPSchema(many=True)

View File

@ -8,7 +8,7 @@ Create Date: 2019-02-07 15:09:11.548655
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import expression
from datetime import datetime
from datetime import datetime, timedelta
# revision identifiers, used by Alembic.
@ -29,10 +29,9 @@ def upgrade():
# ### end Alembic commands ###
# Set columns for times based on status.
now = datetime.now()
connection = op.get_bind()
connection.execute("UPDATE rfp SET date_opened = now() - INTERVAL '1 DAY' WHERE status = 'LIVE' OR status = 'CLOSED'")
connection.execute("UPDATE rfp SET date_closed = now() WHERE status = 'CLOSED'")
connection.execute("UPDATE rfp SET date_opened = now() WHERE status = 'LIVE'")

View File

@ -32,9 +32,23 @@
&-content {
font-size: 1.15rem;
margin-bottom: 2rem;
}
&-rules {
padding-bottom: 2rem;
margin-bottom: 2rem;
border-bottom: 1px solid rgba(#000, 0.08);
ul {
margin: 0;
padding: 0 0 0 1rem;
li {
font-size: 0.9rem;
line-height: 2rem;
}
}
}
&-proposals {

View File

@ -11,6 +11,7 @@ import { AppState } from 'store/reducers';
import Loader from 'components/Loader';
import Markdown from 'components/Markdown';
import ProposalCard from 'components/Proposals/ProposalCard';
import UnitDisplay from 'components/UnitDisplay';
import './index.less';
interface OwnProps {
@ -53,11 +54,31 @@ class RFPDetail extends React.Component<Props> {
<Icon type="arrow-left" /> Back to Requests
</Link>
<div className="RFPDetail-top-date">
Opened {moment(rfp.dateCreated * 1000).format('LL')}
Opened {moment(rfp.dateOpened * 1000).format('LL')}
</div>
</div>
<h1 className="RFPDetail-title">{rfp.title}</h1>
<Markdown className="RFPDetail-content" source={rfp.content} />
<div className="RFPDetail-rules">
<ul>
{rfp.bounty && (
<li>
Accepted proposals will be funded up to{' '}
<UnitDisplay value={rfp.bounty} symbol="ZEC" />
</li>
)}
{rfp.matching && (
<li>
Contributions will have their funding matched by the Zcash Foundation
</li>
)}
{rfp.dateCloses && (
<li>
Proposal submissions end {moment(rfp.dateCloses * 1000).format('LL')}
</li>
)}
</ul>
</div>
{!!rfp.acceptedProposals.length && (
<div className="RFPDetail-proposals">

View File

@ -13,7 +13,16 @@ interface Props {
export default class RFPItem extends React.Component<Props> {
render() {
const { rfp, isSmall } = this.props;
const { id, title, brief, acceptedProposals, dateCreated } = rfp;
const {
id,
title,
brief,
acceptedProposals,
dateOpened,
dateCloses,
dateClosed,
} = rfp;
const closeDate = dateCloses || dateClosed;
return (
<Link
@ -25,7 +34,8 @@ export default class RFPItem extends React.Component<Props> {
<div className="RFPItem-details">
<div className="RFPItem-details-detail">
{moment(dateCreated * 1000).format('LL')}
{moment(dateOpened * 1000).format('LL')}
{closeDate && <> {moment(closeDate * 1000).format('LL')}</>}
</div>
<div className="RFPItem-details-detail">
{acceptedProposals.length} proposals approved

View File

@ -103,6 +103,9 @@ export function formatProposalFromGet(p: any): Proposal {
}
export function formatRFPFromGet(rfp: RFP): RFP {
if (rfp.bounty) {
rfp.bounty = toZat(rfp.bounty as any);
}
rfp.acceptedProposals = rfp.acceptedProposals.map(formatProposalFromGet);
return rfp;
}

View File

@ -1,13 +1,18 @@
import { Proposal } from './proposal';
import { PROPOSAL_CATEGORY, RFP_STATUS } from 'api/constants';
import { Zat } from 'utils/units';
export interface RFP {
id: number;
dateCreated: number;
title: string;
brief: string;
content: string;
category: PROPOSAL_CATEGORY;
status: RFP_STATUS;
acceptedProposals: Proposal[];
bounty: Zat | null;
matching: boolean;
dateOpened: number;
dateClosed?: number;
dateCloses?: number;
}