1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

Refactor ContractAgreementProperty to InputContractAgreementCheckbox. Thx to @Brett

This commit is contained in:
Tim Daubenschütz 2015-12-03 21:29:56 +01:00
parent 8ec0634d2e
commit 36041d3b51
6 changed files with 245 additions and 219 deletions

View File

@ -128,7 +128,14 @@ let Form = React.createClass({
const ref = this.refs[refName]; const ref = this.refs[refName];
if (ref.state && 'value' in ref.state) { if (ref.state && 'value' in ref.state) {
data[ref.props.name] = ref.state.value; // An input can also provide an `Object` as a value
// which we're going to merge with `data` (overwrites)
if(ref.state.value.constructor === Object) {
console.log(ref.state.value);
Object.assign(data, ref.state.value);
} else {
data[ref.props.name] = ref.state.value;
}
} }
} }

View File

@ -4,11 +4,13 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import ContractAgreementProperty from './property_contract_agreement';
import InputTextAreaToggable from './input_textarea_toggable';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import InputContractAgreementCheckbox from './input_contract_agreement_checkbox';
import InputTextAreaToggable from './input_textarea_toggable';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import AclInformation from '../ascribe_buttons/acl_information'; import AclInformation from '../ascribe_buttons/acl_information';
@ -110,12 +112,14 @@ let ConsignForm = React.createClass({
placeholder={getLangText('Enter a message...')} placeholder={getLangText('Enter a message...')}
required /> required />
</Property> </Property>
<ContractAgreementProperty <Property
ref='contractAgreement' name='contract_agreement'
createPublicContractAgreement={createPublicContractAgreement} label={getLangText('Consign Contract')}
email={email} className="ascribe-property-collapsible-toggle">
embedClassName={'consign-form'} <InputContractAgreementCheckbox
label={getLangText('Consign Contract')} /> createPublicContractAgreement={createPublicContractAgreement}
email={email} />
</Property>
<Property <Property
name='password' name='password'
label={getLangText('Password')}> label={getLangText('Password')}>

View File

@ -1,23 +1,26 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import ContractAgreementProperty from './property_contract_agreement'; import ContractAgreementListStore from '../../stores/contract_agreement_list_store';
import Form from './form'; import Form from './form';
import InputTextAreaToggable from './input_textarea_toggable';
import InputDate from './input_date';
import Property from './property'; import Property from './property';
import InputDate from './input_date';
import InputTextAreaToggable from './input_textarea_toggable';
import InputContractAgreementCheckbox from './input_contract_agreement_checkbox';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import AclInformation from '../ascribe_buttons/acl_information'; import AclInformation from '../ascribe_buttons/acl_information';
import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
let LoanForm = React.createClass({ let LoanForm = React.createClass({
propTypes: { propTypes: {
@ -57,6 +60,10 @@ let LoanForm = React.createClass({
}; };
}, },
onChange(state) {
this.setState(state);
},
handleEmailOnChange(event) { handleEmailOnChange(event) {
// event.target.value is the submitted email of the loanee // event.target.value is the submitted email of the loanee
this.setState({ this.setState({
@ -69,10 +76,7 @@ let LoanForm = React.createClass({
}, },
getFormData() { getFormData() {
return mergeOptions( return this.props.id;
this.props.id,
this.refs.contractAgreement.getFormDataForProperty()
);
}, },
getButtons() { getButtons() {
@ -190,13 +194,14 @@ let LoanForm = React.createClass({
placeholder={getLangText('Enter a message...')} placeholder={getLangText('Enter a message...')}
required={showPersonalMessage}/> required={showPersonalMessage}/>
</Property> </Property>
<ContractAgreementProperty <Property
ref={ref => this.refs.contractAgreement = ref}
createPublicContractAgreement={createPublicContractAgreement}
email={email}
embedClassName={'loan-form'}
name='contract_agreement' name='contract_agreement'
label={getLangText('Loan Contract')} /> label={getLangText('Loan Contract')}
className="ascribe-property-collapsible-toggle">
<InputContractAgreementCheckbox
createPublicContractAgreement={createPublicContractAgreement}
email={email} />
</Property>
<Property <Property
name='password' name='password'
label={getLangText('Password')} label={getLangText('Password')}

View File

@ -0,0 +1,194 @@
'use strict';
import React from 'react/addons';
import InputCheckbox from './input_checkbox';
import ContractAgreementListStore from '../../stores/contract_agreement_list_store';
import ContractAgreementListActions from '../../actions/contract_agreement_list_actions';
import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
import { isEmail } from '../../utils/regex_utils';
const InputContractAgreementCheckbox = React.createClass({
propTypes: {
createPublicContractAgreement: React.PropTypes.bool,
email: React.PropTypes.string,
required: React.PropTypes.bool,
// provided by Property
disabled: React.PropTypes.bool,
onChange: React.PropTypes.func,
name: React.PropTypes.string,
setExpanded: React.PropTypes.func,
// can be used to style the component from the outside
style: React.PropTypes.object
},
getDefaultProps() {
return {
createPublicContractAgreement: true
};
},
getInitialState() {
return mergeOptions(
ContractAgreementListStore.getState(),
{
value: {
terms: null,
contract_agreement_id: null
}
}
);
},
componentDidMount() {
ContractAgreementListStore.listen(this.onStoreChange);
this.getContractAgreementsOrCreatePublic(this.props.email);
},
componentWillReceiveProps({ email: nextEmail }) {
const { contractAgreementList } = this.state;
if (this.props.email !== nextEmail) {
if (isEmail(nextEmail)) {
this.getContractAgreementsOrCreatePublic(nextEmail);
} else if (contractAgreementList && contractAgreementList.length > 0) {
ContractAgreementListActions.flushContractAgreementList();
}
}
},
componentWillUnmount() {
ContractAgreementListStore.unlisten(this.onStoreChange);
},
onStoreChange(state) {
const contractAgreement = this.getContractAgreement(state.contractAgreementList);
this.props.setExpanded(!!contractAgreement);
state = mergeOptions(state, {
value: {
contract_agreement_id: contractAgreement ? contractAgreement.id : null,
terms: !contractAgreement || !!contractAgreement.datetime_accepted
}
});
this.setState(state);
},
onChange(event) {
this.setState({
value: React.addons.update(this.state.value, {
terms: { $set: event.target.value }
})
});
this.props.onChange(event);
},
getContractAgreement(contractAgreementList) {
if (contractAgreementList && contractAgreementList.length > 0) {
return contractAgreementList[0];
}
},
getContractAgreementsOrCreatePublic(email) {
ContractAgreementListActions.flushContractAgreementList.defer();
if (email) {
// fetch the available contractagreements (pending/accepted)
ContractAgreementListActions.fetchAvailableContractAgreementList(email, this.props.createPublicContractAgreement);
}
},
getAppendix() {
const { contractAgreementList } = this.state;
if (contractAgreementList && contractAgreementList.length > 0) {
const appendix = contractAgreementList[0].appendix;
if (appendix && appendix.default) {
return (
<pre className="ascribe-pre">{appendix.default}</pre>
);
}
}
},
getContractCheckbox() {
const { name,
disabled,
style } = this.props;
const { contractAgreementList } = this.state;
const inputCheckboxProps = {
name,
disabled,
style,
onChange: this.onChange
};
if (contractAgreementList && contractAgreementList.length > 0) {
const contractAgreement = contractAgreementList[0];
const { issuer: contractIssuer, blob: { url_safe: contractUrl } } = contractAgreement.contract;
if (contractAgreement.datetime_accepted) {
// For `InputCheckbox` we want to override style in this case
Object.assign(inputCheckboxProps, { style: { 'display': 'none' } });
return (
<div className="notification-contract-pdf">
<embed
className="loan-form"
src={contractUrl}
alt="pdf"
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
<a href={contractUrl} target="_blank">
<span className="glyphicon glyphicon-download" aria-hidden="true" /> {getLangText('Download contract')}
</a>
<InputCheckbox
{...inputCheckboxProps}
key="terms_implicitly"
defaultChecked={true} />
</div>
);
} else {
return (
<InputCheckbox
{...inputCheckboxProps}
key="terms_explicitly"
defaultChecked={false}>
<span>
{getLangText('I agree to the')}&nbsp;
<a href={contractUrl} target="_blank">
{getLangText('terms of ')} {contractIssuer}
</a>
</span>
</InputCheckbox>
);
}
} else {
return (
<InputCheckbox
{...inputCheckboxProps}
key="terms_implicitly"
defaultChecked={true} />
);
}
},
render() {
return (
<div>
{this.getContractCheckbox()}
{this.getAppendix()}
</div>
);
}
});
export default InputContractAgreementCheckbox;

View File

@ -78,13 +78,15 @@ const Property = React.createClass({
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
let childInput = this.refs.input; let childInput = this.refs.input;
// For expanded there are actually two use cases: // For expanded there are actually three use cases:
// //
// 1. Control its value from the outside completely (do not define `checkboxLabel`) // 1. Control its value from the outside completely (do not define `checkboxLabel`)
// 2. Let it be controlled from the inside (default value can be set though via `expanded`) // 2. Let it be controlled from the inside (default value can be set though via `expanded`)
// 3. Let it be controlled from a child by using `setExpanded` (`expanded` must not be
// set from the outside as a prop then(!!!))
// //
// This handles case 1. // This handles case 1. and 3.
if(nextProps.expanded !== this.state.expanded && !this.props.checkboxLabel) { if(typeof nextProps.expanded === this.props.expanded && nextProps.expanded !== this.state.expanded && !this.props.checkboxLabel) {
this.setState({ expanded: nextProps.expanded }); this.setState({ expanded: nextProps.expanded });
} }
@ -226,6 +228,10 @@ const Property = React.createClass({
} }
}, },
setExpanded(expanded) {
this.setState({ expanded });
},
renderChildren(style) { renderChildren(style) {
// Input's props should only be cloned and propagated down the tree, // Input's props should only be cloned and propagated down the tree,
// if the component is actually being shown (!== 'expanded === false') // if the component is actually being shown (!== 'expanded === false')
@ -247,7 +253,8 @@ const Property = React.createClass({
onBlur: this.handleBlur, onBlur: this.handleBlur,
disabled: !this.props.editable, disabled: !this.props.editable,
ref: 'input', ref: 'input',
name: this.props.name name: this.props.name,
setExpanded: this.setExpanded
}); });
}); });
} }

View File

@ -1,191 +0,0 @@
'use strict'
import React from 'react'
import InputCheckbox from './input_checkbox';
import Property from './property';
import ContractAgreementListStore from '../../stores/contract_agreement_list_store';
import ContractAgreementListActions from '../../actions/contract_agreement_list_actions';
import { getLangText } from '../../utils/lang_utils';
import { isEmail } from '../../utils/regex_utils';
let ContractAgreementProperty = React.createClass({
propTypes: {
createPublicContractAgreement: React.PropTypes.bool,
email: React.PropTypes.string,
embedClassName: React.PropTypes.string,
label: React.PropTypes.string,
// Necessary for Form to pick this element up as a ref
name: React.PropTypes.string,
// Passed down from Form element
handleChange: React.PropTypes.func
},
getDefaultProps() {
return {
createPublicContractAgreement: true
};
},
getInitialState() {
return ContractAgreementListStore.getState();
},
componentDidMount() {
ContractAgreementListStore.listen(this.onChange);
this.getContractAgreementsOrCreatePublic(this.props.email);
},
componentWillReceiveProps({ email: nextEmail }) {
const { contractAgreementList } = this.state;
if (this.props.email !== nextEmail) {
if (isEmail(nextEmail)) {
this.getContractAgreementsOrCreatePublic(nextEmail);
} else if (contractAgreementList && contractAgreementList.length > 0) {
ContractAgreementListActions.flushContractAgreementList();
}
}
},
componentWillUnmount() {
ContractAgreementListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
getFormDataForProperty() {
const contractAgreementId = this.getContractAgreementId();
const formData = {
'terms': this.refs.terms.state.value
};
if (contractAgreementId) {
formData.contract_agreement_id = contractAgreementId;
}
return formData;
},
getContractAgreementId() {
if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
return this.state.contractAgreementList[0].id;
}
},
getContractAgreementsOrCreatePublic(email) {
ContractAgreementListActions.flushContractAgreementList.defer();
if (email) {
// fetch the available contractagreements (pending/accepted)
ContractAgreementListActions.fetchAvailableContractAgreementList(email, this.props.createPublicContractAgreement);
}
},
getAppendix() {
const { contractAgreementList } = this.state;
if (contractAgreementList && contractAgreementList.length > 0) {
const appendix = contractAgreementList[0].appendix;
if (appendix && appendix.default) {
return (
<Property
name='appendix'
label={getLangText('Appendix')}
handleChange={this.props.handleChange}>
<pre className="ascribe-pre">{appendix.default}</pre>
</Property>
);
}
}
},
getContractCheckbox() {
const { embedClassName, handleChange, label } = this.props;
const { contractAgreementList } = this.state;
if (contractAgreementList && contractAgreementList.length > 0) {
const contractAgreement = contractAgreementList[0];
const { issuer: contractIssuer, blob: { url_safe: contractUrl } } = contractAgreement.contract;
// we need to define a key on the InputCheckboxes as otherwise
// react is not rerendering them on a store switch and is keeping
// the default value of the component (which is in that case true)
if (contractAgreement.datetime_accepted) {
return (
<Property
ref="terms"
name="terms"
label={label}
hidden={false}
handleChange={handleChange}
className="notification-contract-pdf">
<embed
className={embedClassName}
src={contractUrl}
alt="pdf"
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
<a href={contractUrl} target="_blank">
<span className="glyphicon glyphicon-download" aria-hidden="true" /> {getLangText('Download contract')}
</a>
{/* We still need to send the server information that we're accepting */}
<InputCheckbox
style={{'display': 'none'}}
key="terms_implicitly"
defaultChecked={true} />
</Property>
);
} else {
return (
<Property
ref="terms"
name="terms"
className="ascribe-property-collapsible-toggle"
handleChange={handleChange}
style={{paddingBottom: 0}}>
<InputCheckbox
key="terms_explicitly"
defaultChecked={false}>
<span>
{getLangText('I agree to the')}&nbsp;
<a href={contractUrl} target="_blank">
{getLangText('terms of ')} {contractIssuer}
</a>
</span>
</InputCheckbox>
</Property>
);
}
} else {
return (
<Property
ref="terms"
name="terms"
handleChange={handleChange}
style={{paddingBottom: 0}}
hidden={true}>
<InputCheckbox
key="terms_implicitly"
defaultChecked={true} />
</Property>
);
}
},
render() {
return (
<div>
{this.getContractCheckbox()}
{this.getAppendix()}
</div>
);
}
});
export default ContractAgreementProperty;