Frontend for new RFP fields.
This commit is contained in:
parent
64da535650
commit
263764255b
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 doesn’t 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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'")
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue