commit
b34ee977d8
|
@ -32,7 +32,7 @@ class Home extends React.Component {
|
||||||
<div>
|
<div>
|
||||||
<Icon type="exclamation-circle" /> There are <b>{proposalNoArbiterCount}</b>{' '}
|
<Icon type="exclamation-circle" /> There are <b>{proposalNoArbiterCount}</b>{' '}
|
||||||
live proposals <b>without an arbiter</b>.{' '}
|
live proposals <b>without an arbiter</b>.{' '}
|
||||||
<Link to="/proposals?filters[]=STATUS_LIVE&filters[]=ARBITER_MISSING">
|
<Link to="/proposals?filters[]=STATUS_LIVE&filters[]=ARBITER_MISSING&filters[]=STAGE_NOT_CANCELED">
|
||||||
Click here
|
Click here
|
||||||
</Link>{' '}
|
</Link>{' '}
|
||||||
to view them.
|
to view them.
|
||||||
|
@ -42,14 +42,16 @@ class Home extends React.Component {
|
||||||
<div>
|
<div>
|
||||||
<Icon type="exclamation-circle" /> There are{' '}
|
<Icon type="exclamation-circle" /> There are{' '}
|
||||||
<b>{proposalMilestonePayoutsCount}</b> proposals <b>with approved payouts</b>.{' '}
|
<b>{proposalMilestonePayoutsCount}</b> proposals <b>with approved payouts</b>.{' '}
|
||||||
<Link to="/proposals?filters[]=MILESTONE_ACCEPTED">Click here</Link> to view
|
<Link to="/proposals?filters[]=MILESTONE_ACCEPTED&filters[]=STAGE_NOT_CANCELED">
|
||||||
them.
|
Click here
|
||||||
|
</Link>{' '}
|
||||||
|
to view them.
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
!!contributionRefundableCount && (
|
!!contributionRefundableCount && (
|
||||||
<div>
|
<div>
|
||||||
<Icon type="exclamation-circle" /> There are <b>{contributionRefundableCount}</b>{' '}
|
<Icon type="exclamation-circle" /> There are{' '}
|
||||||
contributions <b>ready to be refunded</b>.{' '}
|
<b>{contributionRefundableCount}</b> contributions <b>ready to be refunded</b>.{' '}
|
||||||
<Link to="/contributions?filters[]=REFUNDABLE">Click here</Link> to view them.
|
<Link to="/contributions?filters[]=REFUNDABLE">Click here</Link> to view them.
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Input,
|
Input,
|
||||||
Switch,
|
Switch,
|
||||||
|
Tag,
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import TextArea from 'antd/lib/input/TextArea';
|
import TextArea from 'antd/lib/input/TextArea';
|
||||||
|
@ -64,19 +65,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
return m.datePaid ? prev - parseFloat(m.payoutPercent) : prev;
|
return m.datePaid ? prev - parseFloat(m.payoutPercent) : prev;
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
const renderDeleteControl = () => (
|
|
||||||
<Popconfirm
|
|
||||||
onConfirm={this.handleDelete}
|
|
||||||
title="Delete proposal?"
|
|
||||||
okText="delete"
|
|
||||||
cancelText="cancel"
|
|
||||||
>
|
|
||||||
<Button icon="delete" className="ProposalDetail-controls-control" block>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderCancelControl = () => {
|
const renderCancelControl = () => {
|
||||||
const disabled = this.getCancelAndRefundDisabled();
|
const disabled = this.getCancelAndRefundDisabled();
|
||||||
|
|
||||||
|
@ -392,7 +380,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
{renderNominatedArbiter()}
|
{renderNominatedArbiter()}
|
||||||
{renderMilestoneAccepted()}
|
{renderMilestoneAccepted()}
|
||||||
{renderFailed()}
|
{renderFailed()}
|
||||||
<Collapse defaultActiveKey={['brief', 'content']}>
|
<Collapse defaultActiveKey={['brief', 'content', 'milestones']}>
|
||||||
|
|
||||||
<Collapse.Panel key="brief" header="brief">
|
<Collapse.Panel key="brief" header="brief">
|
||||||
{p.brief}
|
{p.brief}
|
||||||
</Collapse.Panel>
|
</Collapse.Panel>
|
||||||
|
@ -401,6 +390,27 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
<Markdown source={p.content} />
|
<Markdown source={p.content} />
|
||||||
</Collapse.Panel>
|
</Collapse.Panel>
|
||||||
|
|
||||||
|
<Collapse.Panel key="milestones" header="milestones">
|
||||||
|
{
|
||||||
|
p.milestones.map((milestone, i) =>
|
||||||
|
|
||||||
|
<Card title={
|
||||||
|
<>
|
||||||
|
{milestone.title + ' '}
|
||||||
|
{milestone.immediatePayout && <Tag color="magenta">Immediate Payout</Tag>}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
extra={`${milestone.payoutPercent}% Payout`}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
<p><b>Estimated Date:</b> {formatDateSeconds(milestone.dateEstimated )} </p>
|
||||||
|
<p>{milestone.content}</p>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Collapse.Panel>
|
||||||
|
|
||||||
<Collapse.Panel key="json" header="json">
|
<Collapse.Panel key="json" header="json">
|
||||||
<pre>{JSON.stringify(p, null, 4)}</pre>
|
<pre>{JSON.stringify(p, null, 4)}</pre>
|
||||||
</Collapse.Panel>
|
</Collapse.Panel>
|
||||||
|
@ -411,7 +421,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
{/* ACTIONS */}
|
{/* ACTIONS */}
|
||||||
<Card size="small" className="ProposalDetail-controls">
|
<Card size="small" className="ProposalDetail-controls">
|
||||||
{renderDeleteControl()}
|
|
||||||
{renderCancelControl()}
|
{renderCancelControl()}
|
||||||
{renderArbiterControl()}
|
{renderArbiterControl()}
|
||||||
{renderBountyControl()}
|
{renderBountyControl()}
|
||||||
|
@ -507,11 +516,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
store.fetchProposalDetail(this.getIdFromQuery());
|
store.fetchProposalDetail(this.getIdFromQuery());
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleDelete = () => {
|
|
||||||
if (!store.proposalDetail) return;
|
|
||||||
store.deleteProposal(store.proposalDetail.proposalId);
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleCancelCancel = () => {
|
private handleCancelCancel = () => {
|
||||||
this.setState({ showCancelAndRefundPopover: false });
|
this.setState({ showCancelAndRefundPopover: false });
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,6 +87,7 @@ export enum PROPOSAL_STAGE {
|
||||||
COMPLETED = 'COMPLETED',
|
COMPLETED = 'COMPLETED',
|
||||||
FAILED = 'FAILED',
|
FAILED = 'FAILED',
|
||||||
CANCELED = 'CANCELED',
|
CANCELED = 'CANCELED',
|
||||||
|
NOT_CANCELED = 'NOT_CANCELED',
|
||||||
}
|
}
|
||||||
export interface Proposal {
|
export interface Proposal {
|
||||||
proposalId: number;
|
proposalId: number;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export interface StatusSoT<E> {
|
||||||
tagDisplay: string;
|
tagDisplay: string;
|
||||||
tagColor: string;
|
tagColor: string;
|
||||||
hint: string;
|
hint: string;
|
||||||
|
not?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MILESTONE_STAGES: Array<StatusSoT<MILESTONE_STAGE>> = [
|
export const MILESTONE_STAGES: Array<StatusSoT<MILESTONE_STAGE>> = [
|
||||||
|
@ -131,6 +132,13 @@ export const PROPOSAL_STAGES: Array<StatusSoT<PROPOSAL_STAGE>> = [
|
||||||
hint:
|
hint:
|
||||||
'Proposal was canceled by an admin and is currently refunding all contributors.',
|
'Proposal was canceled by an admin and is currently refunding all contributors.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: PROPOSAL_STAGE.NOT_CANCELED,
|
||||||
|
tagDisplay: 'NOT Canceled',
|
||||||
|
tagColor: '#eb4118',
|
||||||
|
hint: 'Proposal has NOT been canceled.',
|
||||||
|
not: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PROPOSAL_ARBITER_STATUSES: Array<StatusSoT<PROPOSAL_ARBITER_STATUS>> = [
|
export const PROPOSAL_ARBITER_STATUSES: Array<StatusSoT<PROPOSAL_ARBITER_STATUS>> = [
|
||||||
|
|
|
@ -18,7 +18,7 @@ module.exports = {
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
|
chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
|
||||||
},
|
},
|
||||||
devtool: 'inline-source-map',
|
devtool: isDev ? 'inline-source-map' : 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
port: 3500,
|
port: 3500,
|
||||||
contentBase: './build',
|
contentBase: './build',
|
||||||
|
|
|
@ -149,6 +149,7 @@ def stats():
|
||||||
proposal_milestone_payouts_count = db.session.query(func.count(Proposal.id)) \
|
proposal_milestone_payouts_count = db.session.query(func.count(Proposal.id)) \
|
||||||
.join(Proposal.milestones) \
|
.join(Proposal.milestones) \
|
||||||
.filter(Proposal.status == ProposalStatus.LIVE) \
|
.filter(Proposal.status == ProposalStatus.LIVE) \
|
||||||
|
.filter(Proposal.stage != ProposalStage.CANCELED) \
|
||||||
.filter(Milestone.stage == MilestoneStage.ACCEPTED) \
|
.filter(Milestone.stage == MilestoneStage.ACCEPTED) \
|
||||||
.scalar()
|
.scalar()
|
||||||
# Count contributions on proposals that didn't get funded for users who have specified a refund address
|
# Count contributions on proposals that didn't get funded for users who have specified a refund address
|
||||||
|
|
|
@ -10,7 +10,9 @@ from .enums import ProposalStatus, ProposalStage, Category, ContributionStatus,
|
||||||
|
|
||||||
|
|
||||||
def extract_filters(sw, strings):
|
def extract_filters(sw, strings):
|
||||||
return [f[len(sw):] for f in strings if f.startswith(sw)]
|
filters = [f[len(sw):] for f in strings if f.startswith(sw)]
|
||||||
|
filters = [f for f in filters if not f.startswith('NOT_')]
|
||||||
|
return filters
|
||||||
|
|
||||||
|
|
||||||
class PaginationException(Exception):
|
class PaginationException(Exception):
|
||||||
|
@ -52,6 +54,7 @@ class ProposalPagination(Pagination):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.FILTERS = [f'STATUS_{s}' for s in ProposalStatus.list()]
|
self.FILTERS = [f'STATUS_{s}' for s in ProposalStatus.list()]
|
||||||
self.FILTERS.extend([f'STAGE_{s}' for s in ProposalStage.list()])
|
self.FILTERS.extend([f'STAGE_{s}' for s in ProposalStage.list()])
|
||||||
|
self.FILTERS.extend([f'STAGE_NOT_{s}' for s in ProposalStage.list()])
|
||||||
self.FILTERS.extend([f'CAT_{c}' for c in Category.list()])
|
self.FILTERS.extend([f'CAT_{c}' for c in Category.list()])
|
||||||
self.FILTERS.extend([f'ARBITER_{c}' for c in ProposalArbiterStatus.list()])
|
self.FILTERS.extend([f'ARBITER_{c}' for c in ProposalArbiterStatus.list()])
|
||||||
self.FILTERS.extend([f'MILESTONE_{c}' for c in MilestoneStage.list()])
|
self.FILTERS.extend([f'MILESTONE_{c}' for c in MilestoneStage.list()])
|
||||||
|
@ -80,6 +83,7 @@ class ProposalPagination(Pagination):
|
||||||
self.validate_filters(filters)
|
self.validate_filters(filters)
|
||||||
status_filters = extract_filters('STATUS_', filters)
|
status_filters = extract_filters('STATUS_', filters)
|
||||||
stage_filters = extract_filters('STAGE_', filters)
|
stage_filters = extract_filters('STAGE_', filters)
|
||||||
|
stage_not_filters = extract_filters('STAGE_NOT_', filters, )
|
||||||
cat_filters = extract_filters('CAT_', filters)
|
cat_filters = extract_filters('CAT_', filters)
|
||||||
arbiter_filters = extract_filters('ARBITER_', filters)
|
arbiter_filters = extract_filters('ARBITER_', filters)
|
||||||
milestone_filters = extract_filters('MILESTONE_', filters)
|
milestone_filters = extract_filters('MILESTONE_', filters)
|
||||||
|
@ -88,6 +92,8 @@ class ProposalPagination(Pagination):
|
||||||
query = query.filter(Proposal.status.in_(status_filters))
|
query = query.filter(Proposal.status.in_(status_filters))
|
||||||
if stage_filters:
|
if stage_filters:
|
||||||
query = query.filter(Proposal.stage.in_(stage_filters))
|
query = query.filter(Proposal.stage.in_(stage_filters))
|
||||||
|
if stage_not_filters:
|
||||||
|
query = query.filter(Proposal.stage.notin_(stage_not_filters))
|
||||||
if cat_filters:
|
if cat_filters:
|
||||||
query = query.filter(Proposal.category.in_(cat_filters))
|
query = query.filter(Proposal.category.in_(cat_filters))
|
||||||
if arbiter_filters:
|
if arbiter_filters:
|
||||||
|
@ -177,7 +183,6 @@ class ContributionPagination(Pagination):
|
||||||
Proposal.stage == ProposalStage.CANCELED,
|
Proposal.stage == ProposalStage.CANCELED,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
# SORT (see self.SORT_MAP)
|
# SORT (see self.SORT_MAP)
|
||||||
if sort:
|
if sort:
|
||||||
self.validate_sort(sort)
|
self.validate_sort(sort)
|
||||||
|
|
Loading…
Reference in New Issue