186 lines
6.0 KiB
TypeScript
186 lines
6.0 KiB
TypeScript
|
import React from 'react';
|
||
|
import { Link } from 'react-router-dom';
|
||
|
import { connect } from 'react-redux';
|
||
|
import moment from 'moment';
|
||
|
import Placeholder from 'components/Placeholder';
|
||
|
import { AppState } from 'store/reducers';
|
||
|
import { Proposal, Revision, RevisionChange, REVISION_CHANGE_TYPES } from 'types';
|
||
|
import { fetchProposalRevisions } from 'modules/proposals/actions';
|
||
|
import {
|
||
|
getProposalRevisions,
|
||
|
getIsFetchingRevisions,
|
||
|
getRevisionsError,
|
||
|
} from 'modules/proposals/selectors';
|
||
|
import { Icon } from 'antd';
|
||
|
import './index.less';
|
||
|
|
||
|
interface OwnProps {
|
||
|
proposalId: Proposal['proposalId'];
|
||
|
}
|
||
|
|
||
|
interface StateProps {
|
||
|
revisions: ReturnType<typeof getProposalRevisions>;
|
||
|
isFetchingRevisions: ReturnType<typeof getIsFetchingRevisions>;
|
||
|
revisionsError: ReturnType<typeof getRevisionsError>;
|
||
|
}
|
||
|
|
||
|
interface DispatchProps {
|
||
|
fetchProposalRevisions: typeof fetchProposalRevisions;
|
||
|
}
|
||
|
|
||
|
type Props = DispatchProps & OwnProps & StateProps;
|
||
|
|
||
|
export class ProposalRevision extends React.Component<Props> {
|
||
|
componentDidMount() {
|
||
|
if (this.props.proposalId) {
|
||
|
this.props.fetchProposalRevisions(this.props.proposalId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
componentWillReceiveProps(nextProps: Props) {
|
||
|
if (nextProps.proposalId && nextProps.proposalId !== this.props.proposalId) {
|
||
|
this.props.fetchProposalRevisions(nextProps.proposalId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
const { revisions, isFetchingRevisions, revisionsError } = this.props;
|
||
|
let content = null;
|
||
|
|
||
|
if (isFetchingRevisions) {
|
||
|
content = <Placeholder loading={true} />;
|
||
|
} else if (revisionsError) {
|
||
|
content = <Placeholder title="Something went wrong" subtitle={revisionsError} />;
|
||
|
} else if (revisions) {
|
||
|
if (revisions.length >= 2) {
|
||
|
revisions.reverse();
|
||
|
content = revisions.map((revision, index) => (
|
||
|
<div key={revision.revisionId} className="ProposalRevision-revision">
|
||
|
{index === revisions.length - 1 ? (
|
||
|
<>
|
||
|
<h3 className="ProposalRevision-revision-title">Original Proposal</h3>
|
||
|
<div className="ProposalRevision-revision-controls">
|
||
|
<Link
|
||
|
to={`/proposals/${revision.proposalArchiveId}/archive`}
|
||
|
className="ProposalRevision-revision-controls-button"
|
||
|
>
|
||
|
View archived
|
||
|
</Link>
|
||
|
</div>
|
||
|
</>
|
||
|
) : (
|
||
|
<>
|
||
|
<h3 className="ProposalRevision-revision-title">
|
||
|
{moment(revision.dateCreated * 1000).format('MMMM Do, YYYY')}
|
||
|
</h3>
|
||
|
<div className="ProposalRevision-revision-date">
|
||
|
{`Revision ${revision.revisionIndex}`}
|
||
|
</div>
|
||
|
<div className="ProposalRevision-revision-body">
|
||
|
{this.renderRevisionBody(revision)}
|
||
|
</div>
|
||
|
<div className="ProposalRevision-revision-controls">
|
||
|
{index !== 0 && (
|
||
|
<Link
|
||
|
to={`/proposals/${revision.proposalArchiveId}/archive`}
|
||
|
className="ProposalRevision-revision-controls-button"
|
||
|
>
|
||
|
View archived
|
||
|
</Link>
|
||
|
)}
|
||
|
</div>
|
||
|
</>
|
||
|
)}
|
||
|
</div>
|
||
|
));
|
||
|
} else {
|
||
|
content = (
|
||
|
<Placeholder
|
||
|
title="No revisions have been made"
|
||
|
subtitle="Edits to this proposal will be tracked here"
|
||
|
/>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return <div className="ProposalRevision">{content}</div>;
|
||
|
}
|
||
|
|
||
|
renderRevisionBody = (revision: Revision) => {
|
||
|
return (
|
||
|
<div>
|
||
|
{revision.changes.map((change, index) => (
|
||
|
<div
|
||
|
key={`${revision.revisionId}-${index}`}
|
||
|
className="ProposalRevision-revision-body-list"
|
||
|
>
|
||
|
<div className="ProposalRevision-revision-body-list-icon">
|
||
|
{this.renderChangeIcon(change)}
|
||
|
</div>
|
||
|
<div>{this.renderChangeMsg(change)}</div>
|
||
|
</div>
|
||
|
))}
|
||
|
</div>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
renderChangeIcon = (change: RevisionChange) => {
|
||
|
if (change.type === 'MILESTONE_ADD') {
|
||
|
return <Icon type="plus" />;
|
||
|
}
|
||
|
|
||
|
if (change.type === 'MILESTONE_REMOVE') {
|
||
|
return <Icon type="minus" />;
|
||
|
}
|
||
|
|
||
|
return <Icon type="edit" />;
|
||
|
};
|
||
|
|
||
|
renderChangeMsg = (change: RevisionChange) => {
|
||
|
const msMsg =
|
||
|
change.milestoneIndex !== undefined
|
||
|
? `Milestone ${change.milestoneIndex + 1}`
|
||
|
: 'Milestone';
|
||
|
|
||
|
switch (change.type) {
|
||
|
case REVISION_CHANGE_TYPES.PROPOSAL_EDIT_BRIEF:
|
||
|
return 'Proposal brief edited';
|
||
|
case REVISION_CHANGE_TYPES.PROPOSAL_EDIT_CONTENT:
|
||
|
return 'Proposal content edited';
|
||
|
case REVISION_CHANGE_TYPES.PROPOSAL_EDIT_TARGET:
|
||
|
return 'Proposal target edited';
|
||
|
case REVISION_CHANGE_TYPES.PROPOSAL_EDIT_TITLE:
|
||
|
return 'Proposal title edited';
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_ADD:
|
||
|
return `${msMsg} added`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_REMOVE:
|
||
|
return `${msMsg} removed`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_AMOUNT:
|
||
|
return `${msMsg} amount edited`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_DAYS:
|
||
|
return `${msMsg} estimated days edited`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_IMMEDIATE_PAYOUT:
|
||
|
return `${msMsg} immediate payout edited`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_PERCENT:
|
||
|
return `${msMsg} payout percent edited`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_CONTENT:
|
||
|
return `${msMsg} content edited`;
|
||
|
case REVISION_CHANGE_TYPES.MILESTONE_EDIT_TITLE:
|
||
|
return `${msMsg} title edited`;
|
||
|
default:
|
||
|
return '';
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
export default connect(
|
||
|
(state: AppState, ownProps: OwnProps) => ({
|
||
|
revisions: getProposalRevisions(state, ownProps.proposalId),
|
||
|
isFetchingRevisions: getIsFetchingRevisions(state),
|
||
|
revisionsError: getRevisionsError(state),
|
||
|
}),
|
||
|
{
|
||
|
fetchProposalRevisions,
|
||
|
},
|
||
|
)(ProposalRevision);
|