zcash-grant-system/frontend/client/components/CreateFlow/Basics.tsx

216 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import { connect } from 'react-redux';
import { Input, Form, Alert, Popconfirm, message, Radio } from 'antd';
import { RadioChangeEvent } from 'antd/lib/radio';
import { ProposalDraft, RFP } from 'types';
import { getCreateErrors } from 'modules/create/utils';
import { Link } from 'react-router-dom';
import { unlinkProposalRFP } from 'modules/create/actions';
import { AppState } from 'store/reducers';
interface OwnProps {
proposalId: number;
initialState?: Partial<State>;
updateForm(form: Partial<ProposalDraft>): void;
}
interface StateProps {
isUnlinkingProposalRFP: AppState['create']['isUnlinkingProposalRFP'];
unlinkProposalRFPError: AppState['create']['unlinkProposalRFPError'];
}
interface DispatchProps {
unlinkProposalRFP: typeof unlinkProposalRFP;
}
type Props = OwnProps & StateProps & DispatchProps;
interface State extends Partial<ProposalDraft> {
title: string;
brief: string;
target: string;
rfp?: RFP;
}
class CreateFlowBasics extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
title: '',
brief: '',
target: '',
...(props.initialState || {}),
};
}
componentDidUpdate(prevProps: Props) {
const { unlinkProposalRFPError, isUnlinkingProposalRFP } = this.props;
if (
unlinkProposalRFPError &&
unlinkProposalRFPError !== prevProps.unlinkProposalRFPError
) {
console.error('Failed to unlink request:', unlinkProposalRFPError);
message.error('Failed to unlink request');
} else if (!isUnlinkingProposalRFP && prevProps.isUnlinkingProposalRFP) {
this.setState({ rfp: undefined });
message.success('Unlinked proposal from request');
}
}
render() {
const { isUnlinkingProposalRFP } = this.props;
const { title, brief, target, rfp, rfpOptIn } = this.state;
const errors = getCreateErrors(this.state, true);
// Don't show target error at zero since it defaults to that
// Error just shows up at the end to prevent submission
if (target === '0') {
errors.target = undefined;
}
const rfpOptInRequired =
rfp && (rfp.matching || (rfp.bounty && parseFloat(rfp.bounty.toString()) > 0));
return (
<Form layout="vertical" style={{ maxWidth: 600, margin: '0 auto' }}>
{rfp && (
<Alert
className="CreateFlow-rfpAlert"
type="info"
message="This proposal is linked to a request"
description={
<>
This proposal is for the open request{' '}
<Link to={`/requests/${rfp.id}`} target="_blank">
{rfp.title}
</Link>
. If you didnt mean to do this, or want to unlink it,{' '}
<Popconfirm
title="Are you sure? This cannot be undone."
onConfirm={this.unlinkRfp}
okButtonProps={{ loading: isUnlinkingProposalRFP }}
>
<a>click here</a>
</Popconfirm>{' '}
to do so.
</>
}
showIcon
/>
)}
{rfpOptInRequired && (
<Alert
className="CreateFlow-rfpAlert"
type="warning"
message="KYC (know your customer)"
description={
<>
<div>
This RFP offers either a bounty or matching. This will require ZFGrants
to fulfill{' '}
<a
target="_blank"
href="https://en.wikipedia.org/wiki/Know_your_customer"
>
KYC
</a>{' '}
due dilligence. In the event your proposal is successful, you will need
to provide identifying information to ZFGrants.
<Radio.Group onChange={this.handleRfpOptIn}>
<Radio value={true} checked={rfpOptIn && rfpOptIn === true}>
<b>Yes</b>, I am willing to provide KYC information
</Radio>
<Radio
value={false}
checked={rfpOptIn !== null && rfpOptIn === false}
>
<b>No</b>, I do not wish to provide KYC information and understand I
will not receive any matching or bounty funds from ZFGrants
</Radio>
</Radio.Group>
</div>
</>
}
/>
)}
<Form.Item
label="Title"
validateStatus={errors.title ? 'error' : undefined}
help={errors.title}
>
<Input
size="large"
name="title"
placeholder="Short and sweet"
type="text"
value={title}
onChange={this.handleInputChange}
maxLength={200}
/>
</Form.Item>
<Form.Item
label="Brief"
validateStatus={errors.brief ? 'error' : undefined}
help={errors.brief}
>
<Input.TextArea
name="brief"
placeholder="An elevator-pitch version of your proposal, max 140 chars"
value={brief}
onChange={this.handleInputChange}
rows={3}
maxLength={200}
/>
</Form.Item>
<Form.Item
label="Target amount"
validateStatus={errors.target ? 'error' : undefined}
help={errors.target || 'This cannot be changed once your proposal starts'}
>
<Input
size="large"
name="target"
placeholder="1.5"
type="number"
value={target}
onChange={this.handleInputChange}
addonAfter="ZEC"
maxLength={16}
/>
</Form.Item>
</Form>
);
}
private handleInputChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { value, name } = event.currentTarget;
this.setState({ [name]: value } as any, () => {
this.props.updateForm(this.state);
});
};
private handleRfpOptIn = (e: RadioChangeEvent) => {
this.setState({ rfpOptIn: e.target.value }, () => {
this.props.updateForm(this.state);
});
};
private unlinkRfp = () => {
this.props.unlinkProposalRFP(this.props.proposalId);
};
}
export default connect<StateProps, DispatchProps, OwnProps, AppState>(
state => ({
isUnlinkingProposalRFP: state.create.isUnlinkingProposalRFP,
unlinkProposalRFPError: state.create.unlinkProposalRFPError,
}),
{ unlinkProposalRFP },
)(CreateFlowBasics);