2018-09-20 11:58:47 -07:00
|
|
|
import React from 'react';
|
2019-11-24 07:07:03 -08:00
|
|
|
import { Form, Input, Card, Icon, Alert, Checkbox, Button, Tooltip } from 'antd';
|
2018-11-14 08:43:00 -08:00
|
|
|
import { ProposalDraft, CreateMilestone } from 'types';
|
2018-09-20 11:58:47 -07:00
|
|
|
import { getCreateErrors } from 'modules/create/utils';
|
|
|
|
|
|
|
|
interface State {
|
2018-11-14 08:43:00 -08:00
|
|
|
milestones: ProposalDraft['milestones'];
|
2018-09-20 11:58:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
initialState: Partial<State>;
|
2018-11-14 08:43:00 -08:00
|
|
|
updateForm(form: Partial<ProposalDraft>): void;
|
2018-09-20 11:58:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_STATE: State = {
|
|
|
|
milestones: [
|
|
|
|
{
|
|
|
|
title: '',
|
2018-11-14 08:43:00 -08:00
|
|
|
content: '',
|
2019-11-13 14:38:17 -08:00
|
|
|
daysEstimated: '',
|
2019-02-19 13:42:40 -08:00
|
|
|
payoutPercent: '',
|
2018-09-20 11:58:47 -07:00
|
|
|
immediatePayout: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
export default class CreateFlowMilestones extends React.Component<Props, State> {
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
...DEFAULT_STATE,
|
|
|
|
...(props.initialState || {}),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Don't allow for empty milestones array
|
|
|
|
if (!this.state.milestones.length) {
|
|
|
|
this.state = {
|
|
|
|
...this.state,
|
|
|
|
milestones: [...DEFAULT_STATE.milestones],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-04 21:27:02 -07:00
|
|
|
handleMilestoneChange = (index: number, milestone: CreateMilestone) => {
|
2018-09-20 11:58:47 -07:00
|
|
|
const milestones = [...this.state.milestones];
|
|
|
|
milestones[index] = milestone;
|
|
|
|
this.setState({ milestones }, () => {
|
|
|
|
this.props.updateForm(this.state);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
addMilestone = () => {
|
|
|
|
const { milestones: oldMilestones } = this.state;
|
2019-02-19 13:42:40 -08:00
|
|
|
const milestones = [...oldMilestones, { ...DEFAULT_STATE.milestones[0] }];
|
2018-09-20 11:58:47 -07:00
|
|
|
this.setState({ milestones });
|
|
|
|
};
|
|
|
|
|
|
|
|
removeMilestone = (index: number) => {
|
|
|
|
let milestones = this.state.milestones.filter((_, i) => i !== index);
|
|
|
|
if (milestones.length === 0) {
|
|
|
|
milestones = [...DEFAULT_STATE.milestones];
|
|
|
|
}
|
|
|
|
this.setState({ milestones }, () => {
|
|
|
|
this.props.updateForm(this.state);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { milestones } = this.state;
|
|
|
|
const errors = getCreateErrors(this.state, true);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Form layout="vertical" style={{ maxWidth: 720, margin: '0 auto' }}>
|
|
|
|
{milestones.map((milestone, idx) => (
|
|
|
|
<MilestoneFields
|
|
|
|
key={idx}
|
|
|
|
milestone={milestone}
|
|
|
|
index={idx}
|
|
|
|
error={errors.milestones && errors.milestones[idx]}
|
|
|
|
onChange={this.handleMilestoneChange}
|
|
|
|
onRemove={this.removeMilestone}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
|
|
|
|
{milestones.length < 10 && (
|
|
|
|
<Button type="dashed" onClick={this.addMilestone}>
|
|
|
|
<Icon type="plus" /> Add another milestone
|
|
|
|
</Button>
|
|
|
|
)}
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interface MilestoneFieldsProps {
|
|
|
|
index: number;
|
2018-10-04 21:27:02 -07:00
|
|
|
milestone: CreateMilestone;
|
2019-11-13 14:38:17 -08:00
|
|
|
// previousMilestoneDateEstimate: moment.Moment | undefined;
|
2018-10-19 15:03:37 -07:00
|
|
|
error: Falsy | string;
|
2018-10-04 21:27:02 -07:00
|
|
|
onChange(index: number, milestone: CreateMilestone): void;
|
2018-09-20 11:58:47 -07:00
|
|
|
onRemove(index: number): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
const MilestoneFields = ({
|
|
|
|
index,
|
|
|
|
milestone,
|
|
|
|
error,
|
|
|
|
onChange,
|
|
|
|
onRemove,
|
|
|
|
}: MilestoneFieldsProps) => (
|
|
|
|
<Card style={{ marginBottom: '2rem' }}>
|
|
|
|
<div style={{ display: 'flex', marginBottom: '0.5rem', alignItems: 'center' }}>
|
|
|
|
<Input
|
|
|
|
size="large"
|
2019-02-19 13:42:40 -08:00
|
|
|
placeholder="Milestone title"
|
2018-09-20 11:58:47 -07:00
|
|
|
type="text"
|
|
|
|
name="title"
|
|
|
|
value={milestone.title}
|
|
|
|
onChange={ev => onChange(index, { ...milestone, title: ev.currentTarget.value })}
|
2019-03-18 11:47:46 -07:00
|
|
|
maxLength={80}
|
2018-09-20 11:58:47 -07:00
|
|
|
/>
|
|
|
|
<button
|
|
|
|
onClick={() => onRemove(index)}
|
|
|
|
style={{
|
|
|
|
paddingLeft: '0.5rem',
|
|
|
|
fontSize: '1.5rem',
|
|
|
|
cursor: 'pointer',
|
|
|
|
opacity: 0.8,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Icon type="close-circle-o" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div style={{ marginBottom: '0.5rem' }}>
|
|
|
|
<Input.TextArea
|
|
|
|
rows={3}
|
2018-11-14 09:59:48 -08:00
|
|
|
name="content"
|
2019-02-19 13:42:40 -08:00
|
|
|
placeholder="Description of what will be delivered"
|
2018-11-14 08:43:00 -08:00
|
|
|
value={milestone.content}
|
2018-09-20 11:58:47 -07:00
|
|
|
onChange={ev =>
|
2018-11-14 08:43:00 -08:00
|
|
|
onChange(index, { ...milestone, content: ev.currentTarget.value })
|
2018-09-20 11:58:47 -07:00
|
|
|
}
|
2019-03-18 11:47:46 -07:00
|
|
|
maxLength={255}
|
2018-09-20 11:58:47 -07:00
|
|
|
/>
|
|
|
|
</div>
|
CCRs (#86)
* CCRs API / Models boilerplate
* start on frontend
* backendy things
* Create CCR redux module, integrate API endpoints, create types
* Fix/Cleanup API
* Wire up CreateRequestDraftList
* bounty->target
* Add 'Create Request Flow' MVP
* cleanup
* Tweak filenames
* Simplify migrations
* fix migrations
* CCR Staking MVP
* tslint
* Get Pending Requests into Profile
* Remove staking requirement
* more staking related removals
* MVP Admin integration
* Make RFP when CCR is accepted
* Add pagination to CCRs in Admin
Improve styles for Proposals
* Hookup notifications
Adjust copy
* Simplify ccr->rfp relationship
Add admin approval email
Fixup copy
* Show Message on RFP Detail
Make Header CTAs change based on draft status
Adjust proposal card style
* Bugfix: Show header for non signed in users
* Add 'create a request' to intro
* Profile Created CCRs
RFP CCR attribution
* ignore
* CCR Price in USD (#85)
* init profile tipjar backend
* init profile tipjar frontend
* fix lint
* implement tip jar block
* fix wrapping, hide tip block on self
* init backend proposal tipjar
* init frontend proposal tipjar
* add hide title, fix bug
* uncomment rate limit
* rename vars, use null check
* allow address and view key to be unset
* add api tests
* fix tsc errors
* fix lint
* fix CopyInput styling
* fix migrations
* hide tipping in proposal if address not set
* add tip address to create flow
* redesign campaign block
* fix typo
* init backend changes
* init admin changes
* init frontend changes
* fix backend tests
* update campaign block
* be - init rfp usd changes
* admin - init rfp usd changes
* fe - fully adapt api util functions to usd
* fe - init rfp usd changes
* adapt profile created to usd
* misc usd changes
* add tip jar to dedicated card
* fix tipjar bug
* use zf light logo
* switch to zf grants logo
* hide profile tip jar if address not set
* add comment, run prettier
* conditionally add info icon and tooltip to funding line
* admin - disallow decimals in RFPs
* fe - cover usd string edge case
* add Usd as rfp bounty type
* fix migration order
* fix email bug
* adapt CCRs to USD
* implement CCR preview
* fix tsc
* Copy Updates and UX Tweaks (#87)
* Add default structure to proposal content
* Landing page copy
* Hide contributors tab for v2 proposals
* Minor UX tweaks for Liking/Following/Tipping
* Copy for Tipping Tooltip, proposal explainer for review, and milestone day estimate notice.
* Fix header styles bug and remove commented out styles.
* Revert "like" / "unfollow" hyphenication
* Comment out unused tests related to staking
Increase PROPOSAL_TARGET_MAX in .env.example
* Comment out ccr approval email send until ready
* Adjust styles, copy.
* fix proposal prune test (#88)
* fix USD display in preview, fix non-unique key (#90)
* Pre-stepper explainer for CCRs.
* Tweak styles
* Default content for CCRs
* fix tsc
* CCR approval and rejection emails
* add back admin_approval_ccr email templates
* Link ccr author name to profile in RFPs
* copy tweaks
* copy tweak
* hookup mangle user command
* Fix/add endif in jinja
* fix tests
* review
* fix review
2019-12-05 17:01:02 -08:00
|
|
|
{index > 0 && (
|
|
|
|
<div style={{ marginBottom: '8px', opacity: 0.7, fontSize: '13px' }}>
|
|
|
|
(Note: This number represents the number of days past the previous milestone day
|
|
|
|
estimate)
|
|
|
|
</div>
|
|
|
|
)}
|
2018-09-20 11:58:47 -07:00
|
|
|
|
|
|
|
<div style={{ display: 'flex' }}>
|
2019-11-13 14:38:17 -08:00
|
|
|
<Input
|
|
|
|
value={milestone.daysEstimated}
|
|
|
|
disabled={milestone.immediatePayout}
|
|
|
|
placeholder="Estimated days to complete"
|
2019-11-24 07:07:03 -08:00
|
|
|
onChange={ev => {
|
2019-11-13 14:38:17 -08:00
|
|
|
return onChange(index, {
|
|
|
|
...milestone,
|
2019-11-24 07:07:03 -08:00
|
|
|
daysEstimated: ev.currentTarget.value,
|
|
|
|
});
|
|
|
|
}}
|
2019-11-13 14:38:17 -08:00
|
|
|
addonAfter="days"
|
|
|
|
style={{ flex: 1, marginRight: '0.5rem' }}
|
|
|
|
maxLength={6}
|
2018-09-20 11:58:47 -07:00
|
|
|
/>
|
|
|
|
<Input
|
|
|
|
value={milestone.payoutPercent}
|
2019-02-19 13:42:40 -08:00
|
|
|
placeholder="Payout"
|
2018-09-20 11:58:47 -07:00
|
|
|
onChange={ev =>
|
|
|
|
onChange(index, {
|
|
|
|
...milestone,
|
2019-02-19 13:42:40 -08:00
|
|
|
payoutPercent: ev.currentTarget.value,
|
2018-09-20 11:58:47 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
addonAfter="%"
|
|
|
|
style={{ maxWidth: '120px', width: '100%' }}
|
2019-03-18 11:47:46 -07:00
|
|
|
maxLength={6}
|
2018-09-20 11:58:47 -07:00
|
|
|
/>
|
|
|
|
{index === 0 && (
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', marginLeft: '0.5rem' }}>
|
|
|
|
<Checkbox
|
|
|
|
checked={milestone.immediatePayout}
|
|
|
|
onChange={ev =>
|
|
|
|
onChange(index, {
|
|
|
|
...milestone,
|
|
|
|
immediatePayout: ev.target.checked,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<span style={{ opacity: 0.7 }}>Payout Immediately</span>
|
|
|
|
</Checkbox>
|
2019-12-10 13:32:12 -08:00
|
|
|
<Tooltip title="Allows the milestone to be paid out immediately if the proposal is accepted with funding.">
|
CCRs (#86)
* CCRs API / Models boilerplate
* start on frontend
* backendy things
* Create CCR redux module, integrate API endpoints, create types
* Fix/Cleanup API
* Wire up CreateRequestDraftList
* bounty->target
* Add 'Create Request Flow' MVP
* cleanup
* Tweak filenames
* Simplify migrations
* fix migrations
* CCR Staking MVP
* tslint
* Get Pending Requests into Profile
* Remove staking requirement
* more staking related removals
* MVP Admin integration
* Make RFP when CCR is accepted
* Add pagination to CCRs in Admin
Improve styles for Proposals
* Hookup notifications
Adjust copy
* Simplify ccr->rfp relationship
Add admin approval email
Fixup copy
* Show Message on RFP Detail
Make Header CTAs change based on draft status
Adjust proposal card style
* Bugfix: Show header for non signed in users
* Add 'create a request' to intro
* Profile Created CCRs
RFP CCR attribution
* ignore
* CCR Price in USD (#85)
* init profile tipjar backend
* init profile tipjar frontend
* fix lint
* implement tip jar block
* fix wrapping, hide tip block on self
* init backend proposal tipjar
* init frontend proposal tipjar
* add hide title, fix bug
* uncomment rate limit
* rename vars, use null check
* allow address and view key to be unset
* add api tests
* fix tsc errors
* fix lint
* fix CopyInput styling
* fix migrations
* hide tipping in proposal if address not set
* add tip address to create flow
* redesign campaign block
* fix typo
* init backend changes
* init admin changes
* init frontend changes
* fix backend tests
* update campaign block
* be - init rfp usd changes
* admin - init rfp usd changes
* fe - fully adapt api util functions to usd
* fe - init rfp usd changes
* adapt profile created to usd
* misc usd changes
* add tip jar to dedicated card
* fix tipjar bug
* use zf light logo
* switch to zf grants logo
* hide profile tip jar if address not set
* add comment, run prettier
* conditionally add info icon and tooltip to funding line
* admin - disallow decimals in RFPs
* fe - cover usd string edge case
* add Usd as rfp bounty type
* fix migration order
* fix email bug
* adapt CCRs to USD
* implement CCR preview
* fix tsc
* Copy Updates and UX Tweaks (#87)
* Add default structure to proposal content
* Landing page copy
* Hide contributors tab for v2 proposals
* Minor UX tweaks for Liking/Following/Tipping
* Copy for Tipping Tooltip, proposal explainer for review, and milestone day estimate notice.
* Fix header styles bug and remove commented out styles.
* Revert "like" / "unfollow" hyphenication
* Comment out unused tests related to staking
Increase PROPOSAL_TARGET_MAX in .env.example
* Comment out ccr approval email send until ready
* Adjust styles, copy.
* fix proposal prune test (#88)
* fix USD display in preview, fix non-unique key (#90)
* Pre-stepper explainer for CCRs.
* Tweak styles
* Default content for CCRs
* fix tsc
* CCR approval and rejection emails
* add back admin_approval_ccr email templates
* Link ccr author name to profile in RFPs
* copy tweaks
* copy tweak
* hookup mangle user command
* Fix/add endif in jinja
* fix tests
* review
* fix review
2019-12-05 17:01:02 -08:00
|
|
|
<Icon type="info-circle" style={{ fontSize: '16px' }} />
|
2019-11-24 07:07:03 -08:00
|
|
|
</Tooltip>
|
2018-09-20 11:58:47 -07:00
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{error && (
|
|
|
|
<Alert style={{ marginTop: '1rem' }} type="error" message={error} showIcon />
|
|
|
|
)}
|
|
|
|
</Card>
|
|
|
|
);
|