Various fixes (#260)
* remove linkedin social media option * fix ProfileEdit social media button white-space * fix default profile tab for non-authed user * filter out non live proposal contributions for GET user/<id> * admin: remove 255 char limit on reject_reason
This commit is contained in:
parent
4c3a2127a8
commit
77e25b1c7c
|
@ -10,7 +10,6 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Collapse,
|
Collapse,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Modal,
|
|
||||||
Input,
|
Input,
|
||||||
Switch,
|
Switch,
|
||||||
message,
|
message,
|
||||||
|
@ -29,14 +28,13 @@ import Back from 'components/Back';
|
||||||
import Info from 'components/Info';
|
import Info from 'components/Info';
|
||||||
import Markdown from 'components/Markdown';
|
import Markdown from 'components/Markdown';
|
||||||
import ArbiterControl from 'components/ArbiterControl';
|
import ArbiterControl from 'components/ArbiterControl';
|
||||||
import './index.less';
|
|
||||||
import { toZat, fromZat } from 'src/util/units';
|
import { toZat, fromZat } from 'src/util/units';
|
||||||
|
import FeedbackModal from '../FeedbackModal';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
type Props = RouteComponentProps<any>;
|
type Props = RouteComponentProps<any>;
|
||||||
|
|
||||||
const STATE = {
|
const STATE = {
|
||||||
showRejectModal: false,
|
|
||||||
rejectReason: '',
|
|
||||||
paidTxId: '',
|
paidTxId: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,8 +48,7 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const id = this.getIdFromQuery();
|
const id = this.getIdFromQuery();
|
||||||
const { proposalDetail: p, proposalDetailFetching, proposalDetailApproving } = store;
|
const { proposalDetail: p, proposalDetailFetching } = store;
|
||||||
const { rejectReason, showRejectModal } = this.state;
|
|
||||||
|
|
||||||
if (!p || (p && p.proposalId !== id) || proposalDetailFetching) {
|
if (!p || (p && p.proposalId !== id) || proposalDetailFetching) {
|
||||||
return 'loading proposal...';
|
return 'loading proposal...';
|
||||||
|
@ -65,7 +62,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 = () => (
|
const renderDeleteControl = () => (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
onConfirm={this.handleDelete}
|
onConfirm={this.handleDelete}
|
||||||
|
@ -81,13 +77,13 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
|
|
||||||
const renderCancelControl = () => (
|
const renderCancelControl = () => (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={(
|
title={
|
||||||
<p>
|
<p>
|
||||||
Are you sure you want to cancel proposal and begin
|
Are you sure you want to cancel proposal and begin
|
||||||
<br />
|
<br />
|
||||||
the refund process? This cannot be undone.
|
the refund process? This cannot be undone.
|
||||||
</p>
|
</p>
|
||||||
)}
|
}
|
||||||
placement="left"
|
placement="left"
|
||||||
cancelText="cancel"
|
cancelText="cancel"
|
||||||
okText="confirm"
|
okText="confirm"
|
||||||
|
@ -181,35 +177,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const rejectModal = (
|
|
||||||
<Modal
|
|
||||||
visible={showRejectModal}
|
|
||||||
title="Reject this proposal"
|
|
||||||
onOk={this.handleReject}
|
|
||||||
onCancel={() => this.setState({ showRejectModal: false })}
|
|
||||||
okButtonProps={{
|
|
||||||
disabled: rejectReason.length === 0,
|
|
||||||
loading: proposalDetailApproving,
|
|
||||||
}}
|
|
||||||
cancelButtonProps={{
|
|
||||||
loading: proposalDetailApproving,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Please provide a reason ({!!rejectReason.length && `${rejectReason.length}/`}
|
|
||||||
250 chars max):
|
|
||||||
<Input.TextArea
|
|
||||||
ref={ta => (this.rejectInput = ta)}
|
|
||||||
rows={4}
|
|
||||||
maxLength={250}
|
|
||||||
required={true}
|
|
||||||
value={rejectReason}
|
|
||||||
onChange={e => {
|
|
||||||
this.setState({ rejectReason: e.target.value });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderReview = () =>
|
const renderReview = () =>
|
||||||
p.status === PROPOSAL_STATUS.PENDING && (
|
p.status === PROPOSAL_STATUS.PENDING && (
|
||||||
<Alert
|
<Alert
|
||||||
|
@ -232,16 +199,16 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
icon="close"
|
icon="close"
|
||||||
type="danger"
|
type="danger"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({ showRejectModal: true });
|
FeedbackModal.open({
|
||||||
// hacky way of waiting for modal to render in before focus
|
title: 'Reject this proposal?',
|
||||||
setTimeout(() => {
|
label: 'Please provide a reason:',
|
||||||
if (this.rejectInput) this.rejectInput.focus();
|
okText: 'Reject',
|
||||||
}, 200);
|
onOk: this.handleReject,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Reject
|
Reject
|
||||||
</Button>
|
</Button>
|
||||||
{rejectModal}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -357,21 +324,22 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
<Alert
|
<Alert
|
||||||
showIcon
|
showIcon
|
||||||
type="error"
|
type="error"
|
||||||
message={p.stage === PROPOSAL_STAGE.FAILED ? 'Proposal failed' : 'Proposal canceled'}
|
message={
|
||||||
|
p.stage === PROPOSAL_STAGE.FAILED ? 'Proposal failed' : 'Proposal canceled'
|
||||||
|
}
|
||||||
description={
|
description={
|
||||||
p.stage === PROPOSAL_STAGE.FAILED ? (
|
p.stage === PROPOSAL_STAGE.FAILED ? (
|
||||||
<>
|
<>
|
||||||
This proposal failed to reach its funding goal of <b>{p.target} ZEC</b> by{' '}
|
This proposal failed to reach its funding goal of <b>{p.target} ZEC</b> by{' '}
|
||||||
<b>{formatDateSeconds(p.datePublished + p.deadlineDuration)}</b>. All contributors
|
<b>{formatDateSeconds(p.datePublished + p.deadlineDuration)}</b>. All
|
||||||
will need to be refunded.
|
contributors will need to be refunded.
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
This proposal was canceled by an admin, and will be refunding contributors
|
This proposal was canceled by an admin, and will be refunding contributors{' '}
|
||||||
{' '}<b>{refundablePct}%</b> of their contributions.
|
<b>{refundablePct}%</b> of their contributions.
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -421,7 +389,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
{renderCancelControl()}
|
{renderCancelControl()}
|
||||||
{renderArbiterControl()}
|
{renderArbiterControl()}
|
||||||
{renderMatchingControl()}
|
{renderMatchingControl()}
|
||||||
{/* TODO - other actions */}
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* DETAILS */}
|
{/* DETAILS */}
|
||||||
|
@ -502,9 +469,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
||||||
store.approveProposal(true);
|
store.approveProposal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleReject = async () => {
|
private handleReject = async (reason: string) => {
|
||||||
await store.approveProposal(false, this.state.rejectReason);
|
await store.approveProposal(false, reason);
|
||||||
this.setState({ showRejectModal: false });
|
message.info('Proposal rejected');
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleToggleMatching = async () => {
|
private handleToggleMatching = async () => {
|
||||||
|
|
|
@ -160,7 +160,7 @@ class ProposalContribution(db.Model):
|
||||||
self.status = ContributionStatus.CONFIRMED
|
self.status = ContributionStatus.CONFIRMED
|
||||||
self.tx_id = tx_id
|
self.tx_id = tx_id
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
|
|
||||||
@hybrid_property
|
@hybrid_property
|
||||||
def refund_address(self):
|
def refund_address(self):
|
||||||
return self.user.settings.refund_address
|
return self.user.settings.refund_address
|
||||||
|
@ -216,7 +216,7 @@ class Proposal(db.Model):
|
||||||
category = db.Column(db.String(255), nullable=False)
|
category = db.Column(db.String(255), nullable=False)
|
||||||
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(255))
|
reject_reason = db.Column(db.String())
|
||||||
|
|
||||||
# Payment info
|
# Payment info
|
||||||
target = db.Column(db.String(255), nullable=False)
|
target = db.Column(db.String(255), nullable=False)
|
||||||
|
|
|
@ -83,6 +83,7 @@ def get_user(user_id, with_proposals, with_comments, with_funded, with_pending,
|
||||||
contributions = ProposalContribution.get_by_userid(user_id)
|
contributions = ProposalContribution.get_by_userid(user_id)
|
||||||
if not authed_user or user.id != authed_user.id:
|
if not authed_user or user.id != authed_user.id:
|
||||||
contributions = [c for c in contributions if c.status == ContributionStatus.CONFIRMED]
|
contributions = [c for c in contributions if c.status == ContributionStatus.CONFIRMED]
|
||||||
|
contributions = [c for c in contributions if c.proposal.status == ProposalStatus.LIVE]
|
||||||
contributions_dump = user_proposal_contributions_schema.dump(contributions)
|
contributions_dump = user_proposal_contributions_schema.dump(contributions)
|
||||||
result["contributions"] = contributions_dump
|
result["contributions"] = contributions_dump
|
||||||
if with_comments:
|
if with_comments:
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
"""remove linkedin social_media items
|
||||||
|
|
||||||
|
Revision ID: 332a15eba9d8
|
||||||
|
Revises: 7c7cecfe5e6c
|
||||||
|
Create Date: 2019-02-23 19:51:16.284007
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '332a15eba9d8'
|
||||||
|
down_revision = '7c7cecfe5e6c'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
connection = op.get_bind()
|
||||||
|
connection.execute("DELETE FROM social_media WHERE service = 'LINKEDIN'")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# there is no going back, all your precious linkedin profiles are gone now
|
||||||
|
pass
|
|
@ -28,7 +28,7 @@ def upgrade():
|
||||||
sa.Column('category', sa.String(length=255), nullable=False),
|
sa.Column('category', sa.String(length=255), nullable=False),
|
||||||
sa.Column('date_approved', sa.DateTime(), nullable=True),
|
sa.Column('date_approved', sa.DateTime(), nullable=True),
|
||||||
sa.Column('date_published', sa.DateTime(), nullable=True),
|
sa.Column('date_published', sa.DateTime(), nullable=True),
|
||||||
sa.Column('reject_reason', sa.String(length=255), nullable=True),
|
sa.Column('reject_reason', sa.String(), nullable=True),
|
||||||
sa.Column('target', sa.String(length=255), nullable=False),
|
sa.Column('target', sa.String(length=255), nullable=False),
|
||||||
sa.Column('payout_address', sa.String(length=255), nullable=False),
|
sa.Column('payout_address', sa.String(length=255), nullable=False),
|
||||||
sa.Column('deadline_duration', sa.Integer(), nullable=False),
|
sa.Column('deadline_duration', sa.Integer(), nullable=False),
|
||||||
|
|
|
@ -164,8 +164,7 @@ class ProfileEdit extends React.PureComponent<Props, State> {
|
||||||
loading={loading}
|
loading={loading}
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
{!loading && s.icon}
|
{!loading && s.icon} <>Connect to {s.name}</>
|
||||||
Connect to {s.name}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
|
@ -125,7 +125,7 @@ class Profile extends React.Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
<div className="Profile-tabs">
|
<div className="Profile-tabs">
|
||||||
<LinkableTabs defaultActiveKey="pending">
|
<LinkableTabs defaultActiveKey={(isAuthedUser && 'pending') || 'created'}>
|
||||||
{isAuthedUser && (
|
{isAuthedUser && (
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
tab={TabTitle('Pending', pendingProposals.length)}
|
tab={TabTitle('Pending', pendingProposals.length)}
|
||||||
|
|
|
@ -16,12 +16,6 @@ export const SOCIAL_INFO: { [key in SOCIAL_SERVICE]: SocialInfo } = {
|
||||||
format: `https://twitter.com/${accountNameRegex}`,
|
format: `https://twitter.com/${accountNameRegex}`,
|
||||||
icon: <Icon type="twitter" />,
|
icon: <Icon type="twitter" />,
|
||||||
},
|
},
|
||||||
[SOCIAL_SERVICE.LINKEDIN]: {
|
|
||||||
service: SOCIAL_SERVICE.LINKEDIN,
|
|
||||||
name: 'LinkedIn',
|
|
||||||
format: `https://linkedin.com/in/${accountNameRegex}`,
|
|
||||||
icon: <Icon type="linkedin" />,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function socialMediaToUrl(service: SOCIAL_SERVICE, username: string): string {
|
export function socialMediaToUrl(service: SOCIAL_SERVICE, username: string): string {
|
||||||
|
|
|
@ -18,5 +18,4 @@ export interface SocialInfo {
|
||||||
export enum SOCIAL_SERVICE {
|
export enum SOCIAL_SERVICE {
|
||||||
GITHUB = 'GITHUB',
|
GITHUB = 'GITHUB',
|
||||||
TWITTER = 'TWITTER',
|
TWITTER = 'TWITTER',
|
||||||
LINKEDIN = 'LINKEDIN',
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue