import React from 'react'; import { view } from 'react-easy-state'; import { Icon, Button, Popover, InputNumber, Checkbox, Alert, Input } from 'antd'; import { RouteComponentProps, withRouter } from 'react-router'; import Showdown from 'showdown'; import moment from 'moment'; import store from 'src/store'; import { Proposal, Contract, ContractMethod as TContractMethod, ContractMilestone, ContractContributor, } from 'src/types'; import './index.less'; import Field from 'components/Field'; import { Link } from 'react-router-dom'; const showdownConverter = new Showdown.Converter({ simplifiedAutoLink: true, tables: true, strikethrough: true, disableForced4SpacesIndentedSublists: true, openLinksInNewWindow: true, excludeTrailingPunctuationFromURLs: true, }); type Props = RouteComponentProps; class ProposalsNaked extends React.Component { componentDidMount() { store.fetchProposals(); } render() { const id = Number(this.props.match.params.id); const { proposals, proposalsFetched } = store; if (!proposalsFetched) { return 'loading proposals...'; } if (id) { const singleProposal = proposals.find(p => p.proposalId === id); if (singleProposal) { return (
proposals {id}{' '}
); } else { return `could not find proposal: ${id}`; } } return (
{proposals.length === 0 &&
no proposals
} {proposals.length > 0 && proposals.map(p => )}
); } } // tslint:disable-next-line:max-classes-per-file class ProposalItemNaked extends React.Component { state = { showDelete: false, }; render() { const p = this.props; const body = showdownConverter.makeHtml(p.body); return (
{' '}
} title="Permanently delete proposal?" trigger="click" visible={this.state.showDelete} onVisibleChange={showDelete => this.setState({ showDelete })} >
{p.title} [{p.proposalId}]{p.proposalAddress}{' '} {p.team.map(u => (
{u.displayName} ( {u.accountAddress})
))}
} /> TODO: comments} /> } /> {p.milestones.map((ms, idx) => (
{idx}. {ms.title} (title)
{moment(ms.dateCreated).format('YYYY/MM/DD h:mm a')} (dateCreated)
{moment(ms.dateEstimated).format('YYYY/MM/DD h:mm a')} (dateEstimated)
{ms.stage} (stage)
{JSON.stringify(ms.immediatePayout)} (immediatePayout)
{ms.payoutPercent} (payoutPercent)
{ms.body} (body)
{/* content
{ms.content}
*/}
))} } /> {!store.web3Enabled && } {store.web3Enabled && ( {Object.keys(p.contract) .map(k => k as keyof Contract) .map(k => ( ))} } /> )} ); } private handleDelete = () => { store.deleteProposal(this.props.proposalId); }; } const ProposalItem = view(ProposalItemNaked); // tslint:disable-next-line:max-classes-per-file class ContractMethodNaked extends React.Component< TContractMethod & { proposalId: number; name: string } > { state = {}; render() { const { name, value, status, type, format } = this.props; const isObj = typeof value === 'object' && value !== null; const isArray = Array.isArray(value); const fmt = (val: any) => { if (val && format === 'time') { const asNumber = Number(val) * 1000; return `${moment(asNumber).format()} (${moment(asNumber).fromNow()})`; } else if (val && format === 'duration') { const asNumber = Number(val) * 1000; return `${asNumber} (${moment.duration(asNumber).humanize()})`; } return value; }; if (type === 'send') { return ; } return (
{name}: {(!isObj && {fmt(value)}) || (
{isArray && name !== 'milestones' && name !== 'contributors' && (value as string[]).map((x, i) => (
{i}: {x}
))} {isArray && name === 'milestones' && (
{(value as ContractMilestone[]).map((cm, idx) => (
paid: {JSON.stringify(cm.paid)}
amount: {cm.amount}
payoutRequestVoteDeadline:{' '} {Number(cm.payoutRequestVoteDeadline) < 2 ? cm.payoutRequestVoteDeadline : moment(Number(cm.payoutRequestVoteDeadline) * 1000).fromNow()}
amountVotingAgainstPayout:{' '} {cm.amountVotingAgainstPayout}
))}
)} {isArray && name === 'contributors' && (
{(value as ContractContributor[]).map(c => (
address: {c.address}
milestoneNoVotes:{' '} {JSON.stringify(c.milestoneNoVotes)}
contributionAmount: {c.contributionAmount}
refundVote: {JSON.stringify(c.refundVote)}
refunded: {JSON.stringify(c.refunded)}
))}
)}
)}
); } } const ContractMethod = view(ContractMethodNaked); // tslint:disable-next-line:max-classes-per-file class ContractMethodSendNaked extends React.Component< TContractMethod & { proposalId: number; name: string } > { state = { args: this.props.input.map(i => (i.type === 'boolean' ? false : '')) as any[], }; render() { const { name, status, input, proposalId, error } = this.props; return (
{input.length === 0 && 'no input'} {input.map( (x, idx) => ((x.type === 'wei' || x.type === 'integer') && ( { const args = [...this.state.args]; args[idx] = val; this.setState({ args }); }} value={this.state.args[idx]} className={`Proposals-proposal-contract-input is-${x.type || ''}`} /> )) || (x.type === 'string' && ( { const args = [...this.state.args]; args[idx] = evt.currentTarget.value; this.setState({ args }); }} value={this.state.args[idx]} className={`Proposals-proposal-contract-input is-${x.type || ''}`} /> )) || ( { const args = [...this.state.args]; args[idx] = evt.target.checked; this.setState({ args }); }} value={this.state.args[idx]} className={`Proposals-proposal-contract-input is-${x.type || ''}`} > {x.name} ), )}
{error && }
); } } const ContractMethodSend = view(ContractMethodSendNaked); const Proposals = withRouter(view(ProposalsNaked)); export default Proposals;