1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 17:33:14 +01:00

Refactor contract agreement to be a self contained component that can be used in forms

This commit is contained in:
Brett Sun 2015-12-03 15:22:17 +01:00
parent e442e6bcec
commit 3c516c1598
5 changed files with 217 additions and 156 deletions

View File

@ -22,8 +22,7 @@ class ContractAgreementListActions {
if (contractAgreementList.count > 0) { if (contractAgreementList.count > 0) {
this.actions.updateContractAgreementList(contractAgreementList.results); this.actions.updateContractAgreementList(contractAgreementList.results);
resolve(contractAgreementList.results); resolve(contractAgreementList.results);
} } else {
else{
resolve(null); resolve(null);
} }
}) })
@ -35,13 +34,13 @@ class ContractAgreementListActions {
); );
} }
fetchAvailableContractAgreementList(issuer, createContractAgreement) { fetchAvailableContractAgreementList(issuer, createPublicContractAgreement) {
return Q.Promise((resolve, reject) => { return Q.Promise((resolve, reject) => {
OwnershipFetcher.fetchContractAgreementList(issuer, true, null) OwnershipFetcher.fetchContractAgreementList(issuer, true, null)
.then((acceptedContractAgreementList) => { .then((acceptedContractAgreementList) => {
// if there is at least an accepted contract agreement, we're going to // if there is at least an accepted contract agreement, we're going to
// use it // use it
if(acceptedContractAgreementList.count > 0) { if (acceptedContractAgreementList.count > 0) {
this.actions.updateContractAgreementList(acceptedContractAgreementList.results); this.actions.updateContractAgreementList(acceptedContractAgreementList.results);
} else { } else {
// otherwise, we're looking for contract agreements that are still pending // otherwise, we're looking for contract agreements that are still pending
@ -50,16 +49,14 @@ class ContractAgreementListActions {
// overcomplicate the method // overcomplicate the method
OwnershipFetcher.fetchContractAgreementList(issuer, null, true) OwnershipFetcher.fetchContractAgreementList(issuer, null, true)
.then((pendingContractAgreementList) => { .then((pendingContractAgreementList) => {
if(pendingContractAgreementList.count > 0) { if (pendingContractAgreementList.count > 0) {
this.actions.updateContractAgreementList(pendingContractAgreementList.results); this.actions.updateContractAgreementList(pendingContractAgreementList.results);
} else { } else if (createPublicContractAgreement) {
// if there was neither a pending nor an active contractAgreement // if there was neither a pending nor an active contractAgreement
// found and createContractAgreement is set to true, we create a // found and createPublicContractAgreement is set to true, we create a
// new contract agreement // new public contract agreement
if(createContractAgreement) {
this.actions.createContractAgreementFromPublicContract(issuer); this.actions.createContractAgreementFromPublicContract(issuer);
} }
}
}) })
.catch((err) => { .catch((err) => {
console.logGlobal(err); console.logGlobal(err);
@ -81,8 +78,7 @@ class ContractAgreementListActions {
// create an agreement with the public contract if there is one // create an agreement with the public contract if there is one
if (publicContract && publicContract.length > 0) { if (publicContract && publicContract.length > 0) {
return this.actions.createContractAgreement(null, publicContract[0]); return this.actions.createContractAgreement(null, publicContract[0]);
} } else {
else {
/* /*
contractAgreementList in the store is already set to null; contractAgreementList in the store is already set to null;
*/ */
@ -91,18 +87,14 @@ class ContractAgreementListActions {
if (publicContracAgreement) { if (publicContracAgreement) {
this.actions.updateContractAgreementList([publicContracAgreement]); this.actions.updateContractAgreementList([publicContracAgreement]);
} }
}).catch((err) => { }).catch(console.logGlobal);
console.logGlobal(err);
});
} }
createContractAgreement(issuer, contract){ createContractAgreement(issuer, contract){
return Q.Promise((resolve, reject) => { return Q.Promise((resolve, reject) => {
OwnershipFetcher.createContractAgreement(issuer, contract).then( OwnershipFetcher
(contractAgreement) => { .createContractAgreement(issuer, contract).then(resolve)
resolve(contractAgreement); .catch((err) => {
}
).catch((err) => {
console.logGlobal(err); console.logGlobal(err);
reject(err); reject(err);
}); });

View File

@ -4,6 +4,7 @@ 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 InputTextAreaToggable from './input_textarea_toggable';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
@ -22,6 +23,7 @@ let ConsignForm = React.createClass({
email: React.PropTypes.string, email: React.PropTypes.string,
message: React.PropTypes.string, message: React.PropTypes.string,
labels: React.PropTypes.object, labels: React.PropTypes.object,
createPublicContractAgreement: React.PropTypes.bool,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
@ -52,6 +54,7 @@ let ConsignForm = React.createClass({
const { email } = this.state; const { email } = this.state;
const { const {
autoFocusProperty, autoFocusProperty,
createPublicContractAgreement,
email: defaultEmail, email: defaultEmail,
handleSuccess, handleSuccess,
id, id,
@ -107,6 +110,12 @@ let ConsignForm = React.createClass({
placeholder={getLangText('Enter a message...')} placeholder={getLangText('Enter a message...')}
required /> required />
</Property> </Property>
<ContractAgreementProperty
ref='contractAgreement'
createPublicContractAgreement={createPublicContractAgreement}
email={email}
embedClassName={'consign-form'}
label={getLangText('Consign Contract')} />
<Property <Property
name='password' name='password'
label={getLangText('Password')}> label={getLangText('Password')}>

View File

@ -6,20 +6,18 @@ 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 Form from './form'; import Form from './form';
import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable'; import InputTextAreaToggable from './input_textarea_toggable';
import InputDate from './input_date'; import InputDate from './input_date';
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 AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import AclInformation from '../ascribe_buttons/acl_information';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import AclInformation from '../ascribe_buttons/acl_information';
let LoanForm = React.createClass({ let LoanForm = React.createClass({
propTypes: { propTypes: {
@ -49,55 +47,20 @@ let LoanForm = React.createClass({
showPersonalMessage: true, showPersonalMessage: true,
showEndDate: true, showEndDate: true,
showStartDate: true, showStartDate: true,
showPassword: true, showPassword: true
createPublicContractAgreement: true
}; };
}, },
getInitialState() { getInitialState() {
return ContractAgreementListStore.getState();
},
componentDidMount() {
ContractAgreementListStore.listen(this.onChange);
this.getContractAgreementsOrCreatePublic(this.props.email);
},
/**
* This method needs to be in form_loan as some whitelabel pages (Cyland) load
* the loanee's email async!
*
* SO LEAVE IT IN!
*/
componentWillReceiveProps(nextProps) {
if(nextProps && nextProps.email && this.props.email !== nextProps.email) {
this.getContractAgreementsOrCreatePublic(nextProps.email);
}
},
componentWillUnmount() {
ContractAgreementListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
getContractAgreementsOrCreatePublic(email){
ContractAgreementListActions.flushContractAgreementList.defer();
if (email) {
// fetch the available contractagreements (pending/accepted)
ContractAgreementListActions.fetchAvailableContractAgreementList(email, true);
}
return { return {
email: this.props.email || '' email: this.props.email || ''
}; };
}, },
getFormData(){ getFormData() {
return mergeOptions( return mergeOptions(
this.props.id, this.props.id,
this.getContractAgreementId() this.refs.contractAgreement.getFormDataForProperty()
); );
}, },
@ -108,90 +71,8 @@ let LoanForm = React.createClass({
}); });
}, },
getContractAgreementId() { handleReset(event) {
if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) { this.handleEmailOnChange();
return {'contract_agreement_id': this.state.contractAgreementList[0].id};
}
return {};
},
getContractCheckbox() {
if(this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
// 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)
let contractAgreement = this.state.contractAgreementList[0];
let contract = contractAgreement.contract;
if(contractAgreement.datetime_accepted) {
return (
<Property
name="terms"
label={getLangText('Loan Contract')}
hidden={false}
className="notification-contract-pdf">
<embed
className="loan-form"
src={contract.blob.url_safe}
alt="pdf"
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
<a href={contract.blob.url_safe} target="_blank">
<span className="glyphicon glyphicon-download" aria-hidden="true"></span> {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
name="terms"
className="ascribe-property-collapsible-toggle"
style={{paddingBottom: 0}}>
<InputCheckbox
key="terms_explicitly"
defaultChecked={false}>
<span>
{getLangText('I agree to the')}&nbsp;
<a href={contract.blob.url_safe} target="_blank">
{getLangText('terms of ')} {contract.issuer}
</a>
</span>
</InputCheckbox>
</Property>
);
}
} else {
return (
<Property
name="terms"
style={{paddingBottom: 0}}
hidden={true}>
<InputCheckbox
key="terms_implicitly"
defaultChecked={true} />
</Property>
);
}
},
getAppendix() {
if(this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
let appendix = this.state.contractAgreementList[0].appendix;
if (appendix && appendix.default) {
return (
<Property
name='appendix'
label={getLangText('Appendix')}>
<pre className="ascribe-pre">{appendix.default}</pre>
</Property>
);
}
}
return null;
}, },
getButtons() { getButtons() {
@ -222,7 +103,7 @@ let LoanForm = React.createClass({
const { email } = this.state; const { email } = this.state;
const { const {
children, children,
email, createPublicContractAgreement,
email: defaultEmail, email: defaultEmail,
handleSuccess, handleSuccess,
gallery, gallery,
@ -242,7 +123,7 @@ let LoanForm = React.createClass({
ref='form' ref='form'
url={url} url={url}
getFormData={this.getFormData} getFormData={this.getFormData}
onReset={this.handleOnChange} onReset={this.handleReset}
handleSuccess={handleSuccess} handleSuccess={handleSuccess}
buttons={this.getButtons()} buttons={this.getButtons()}
spinner={ spinner={
@ -309,8 +190,12 @@ let LoanForm = React.createClass({
placeholder={getLangText('Enter a message...')} placeholder={getLangText('Enter a message...')}
required={showPersonalMessage}/> required={showPersonalMessage}/>
</Property> </Property>
{this.getContractCheckbox()} <ContractAgreementProperty
{this.getAppendix()} ref='contractAgreement'
createPublicContractAgreement={createPublicContractAgreement}
email={email}
embedClassName={'loan-form'}
label={getLangText('Loan Contract')} />
<Property <Property
name='password' name='password'
label={getLangText('Password')} label={getLangText('Password')}

View File

@ -0,0 +1,168 @@
'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.string,
email: React.PropTypes.string,
embedClassName: React.PropTypes.string,
label: React.PropTypes.string
},
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 (nextEmail && this.props.email !== nextEmail && isEmail(nextEmail)) {
this.getContractAgreementsOrCreatePublic(nextEmail);
} else if (contractAgreementList && contractAgreementList.length > 0) {
ContractAgreementListActions.flushContractAgreementList();
}
},
componentWillUnmount() {
ContractAgreementListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
getFormDataForProperty() {
return this.getContractAgreementId();
},
getContractAgreementId() {
if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
return { 'contract_agreement_id': this.state.contractAgreementList[0].id };
} else {
return {};
}
},
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')}>
<pre className="ascribe-pre">{appendix.default}</pre>
</Property>
);
}
}
},
getContractCheckbox() {
const { contractAgreementList } = this.state;
if (contractAgreementList && contractAgreementList.length > 0) {
// 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)
const contractAgreement = contractAgreementList[0];
const { issuer: contractIssuer, blob: { url_safe: contractUrl } } = contractAgreement.contract;
if (contractAgreement.datetime_accepted) {
return (
<Property
name="terms"
label={this.props.label}
hidden={false}
className="notification-contract-pdf">
<embed
className={this.props.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
name="terms"
className="ascribe-property-collapsible-toggle"
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
name="terms"
style={{paddingBottom: 0}}
hidden={true}>
<InputCheckbox
key="terms_implicitly"
defaultChecked={true} />
</Property>
);
}
},
render() {
return (
<div>
{this.getContractCheckbox()}
{this.getAppendix()}
</div>
);
}
});
export default ContractAgreementProperty;

7
js/utils/regex_utils.js Normal file
View File

@ -0,0 +1,7 @@
'use strict'
export function isEmail(string) {
// This is a bit of a weak test for an email, but you really can't win them all
// http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address
return !!string && string.match(/.*@.*\..*/);
}