1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-25 18:56:28 +02: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) {
this.actions.updateContractAgreementList(contractAgreementList.results);
resolve(contractAgreementList.results);
}
else{
} else {
resolve(null);
}
})
@ -35,13 +34,13 @@ class ContractAgreementListActions {
);
}
fetchAvailableContractAgreementList(issuer, createContractAgreement) {
fetchAvailableContractAgreementList(issuer, createPublicContractAgreement) {
return Q.Promise((resolve, reject) => {
OwnershipFetcher.fetchContractAgreementList(issuer, true, null)
.then((acceptedContractAgreementList) => {
// if there is at least an accepted contract agreement, we're going to
// use it
if(acceptedContractAgreementList.count > 0) {
if (acceptedContractAgreementList.count > 0) {
this.actions.updateContractAgreementList(acceptedContractAgreementList.results);
} else {
// otherwise, we're looking for contract agreements that are still pending
@ -50,15 +49,13 @@ class ContractAgreementListActions {
// overcomplicate the method
OwnershipFetcher.fetchContractAgreementList(issuer, null, true)
.then((pendingContractAgreementList) => {
if(pendingContractAgreementList.count > 0) {
if (pendingContractAgreementList.count > 0) {
this.actions.updateContractAgreementList(pendingContractAgreementList.results);
} else {
} else if (createPublicContractAgreement) {
// if there was neither a pending nor an active contractAgreement
// found and createContractAgreement is set to true, we create a
// new contract agreement
if(createContractAgreement) {
this.actions.createContractAgreementFromPublicContract(issuer);
}
// found and createPublicContractAgreement is set to true, we create a
// new public contract agreement
this.actions.createContractAgreementFromPublicContract(issuer);
}
})
.catch((err) => {
@ -81,8 +78,7 @@ class ContractAgreementListActions {
// create an agreement with the public contract if there is one
if (publicContract && publicContract.length > 0) {
return this.actions.createContractAgreement(null, publicContract[0]);
}
else {
} else {
/*
contractAgreementList in the store is already set to null;
*/
@ -91,21 +87,17 @@ class ContractAgreementListActions {
if (publicContracAgreement) {
this.actions.updateContractAgreementList([publicContracAgreement]);
}
}).catch((err) => {
console.logGlobal(err);
});
}).catch(console.logGlobal);
}
createContractAgreement(issuer, contract){
return Q.Promise((resolve, reject) => {
OwnershipFetcher.createContractAgreement(issuer, contract).then(
(contractAgreement) => {
resolve(contractAgreement);
}
).catch((err) => {
console.logGlobal(err);
reject(err);
});
OwnershipFetcher
.createContractAgreement(issuer, contract).then(resolve)
.catch((err) => {
console.logGlobal(err);
reject(err);
});
});
}
}

View File

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

View File

@ -6,20 +6,18 @@ import classnames from 'classnames';
import Button from 'react-bootstrap/lib/Button';
import ContractAgreementProperty from './property_contract_agreement';
import Form from './form';
import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable';
import InputDate from './input_date';
import InputCheckbox from './input_checkbox';
import ContractAgreementListStore from '../../stores/contract_agreement_list_store';
import ContractAgreementListActions from '../../actions/contract_agreement_list_actions';
import Property from './property';
import AscribeSpinner from '../ascribe_spinner';
import AclInformation from '../ascribe_buttons/acl_information';
import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
import AclInformation from '../ascribe_buttons/acl_information';
let LoanForm = React.createClass({
propTypes: {
@ -49,55 +47,20 @@ let LoanForm = React.createClass({
showPersonalMessage: true,
showEndDate: true,
showStartDate: true,
showPassword: true,
createPublicContractAgreement: true
showPassword: true
};
},
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 {
email: this.props.email || ''
};
},
getFormData(){
getFormData() {
return mergeOptions(
this.props.id,
this.getContractAgreementId()
this.refs.contractAgreement.getFormDataForProperty()
);
},
@ -108,90 +71,8 @@ let LoanForm = React.createClass({
});
},
getContractAgreementId() {
if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
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;
handleReset(event) {
this.handleEmailOnChange();
},
getButtons() {
@ -222,7 +103,7 @@ let LoanForm = React.createClass({
const { email } = this.state;
const {
children,
email,
createPublicContractAgreement,
email: defaultEmail,
handleSuccess,
gallery,
@ -242,7 +123,7 @@ let LoanForm = React.createClass({
ref='form'
url={url}
getFormData={this.getFormData}
onReset={this.handleOnChange}
onReset={this.handleReset}
handleSuccess={handleSuccess}
buttons={this.getButtons()}
spinner={
@ -309,8 +190,12 @@ let LoanForm = React.createClass({
placeholder={getLangText('Enter a message...')}
required={showPersonalMessage}/>
</Property>
{this.getContractCheckbox()}
{this.getAppendix()}
<ContractAgreementProperty
ref='contractAgreement'
createPublicContractAgreement={createPublicContractAgreement}
email={email}
embedClassName={'loan-form'}
label={getLangText('Loan Contract')} />
<Property
name='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(/.*@.*\..*/);
}