diff --git a/js/actions/contract_agreement_list_actions.js b/js/actions/contract_agreement_list_actions.js
index 4993b129..1eedf5b0 100644
--- a/js/actions/contract_agreement_list_actions.js
+++ b/js/actions/contract_agreement_list_actions.js
@@ -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);
+ });
});
}
}
diff --git a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js
index 5d3e033f..8033f239 100644
--- a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js
+++ b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js
@@ -78,7 +78,6 @@ let AccordionListItemEditionWidget = React.createClass({
return (
);
} else {
@@ -137,4 +136,4 @@ let AccordionListItemEditionWidget = React.createClass({
}
});
-export default AccordionListItemEditionWidget;
\ No newline at end of file
+export default AccordionListItemEditionWidget;
diff --git a/js/components/ascribe_accordion_list/accordion_list_item_piece.js b/js/components/ascribe_accordion_list/accordion_list_item_piece.js
index 4547ce3b..006479c5 100644
--- a/js/components/ascribe_accordion_list/accordion_list_item_piece.js
+++ b/js/components/ascribe_accordion_list/accordion_list_item_piece.js
@@ -4,6 +4,7 @@ import React from 'react';
import { Link } from 'react-router';
import AccordionListItem from './accordion_list_item';
+import AccordionListItemThumbnailPlacholder from './accordion_list_item_thumbnail_placeholder';
import { getLangText } from '../../utils/lang_utils';
@@ -19,7 +20,14 @@ let AccordionListItemPiece = React.createClass({
]),
subsubheading: React.PropTypes.object,
buttons: React.PropTypes.object,
- badge: React.PropTypes.object
+ badge: React.PropTypes.object,
+ thumbnailPlaceholder: React.PropTypes.func
+ },
+
+ getDefaultProps() {
+ return {
+ thumbnailPlaceholder: AccordionListItemThumbnailPlacholder
+ };
},
getLinkData() {
@@ -34,19 +42,23 @@ let AccordionListItemPiece = React.createClass({
},
render() {
- const { className, piece, artistName, buttons, badge, children, subsubheading } = this.props;
+ const {
+ artistName,
+ badge,
+ buttons,
+ children,
+ className,
+ piece,
+ subsubheading,
+ thumbnailPlaceholder: ThumbnailPlaceholder } = this.props;
const { url, url_safe } = piece.thumbnail;
let thumbnail;
// Since we're going to refactor the thumbnail generation anyway at one point,
// for not use the annoying ascribe_spiral.png, we're matching the url against
// this name and replace it with a CSS version of the new logo.
- if(url.match(/https:\/\/.*\/media\/thumbnails\/ascribe_spiral.png/)) {
- thumbnail = (
-
- A
-
- );
+ if (url.match(/https:\/\/.*\/media\/thumbnails\/ascribe_spiral.png/)) {
+ thumbnail = ();
} else {
thumbnail = (
diff --git a/js/components/ascribe_accordion_list/accordion_list_item_thumbnail_placeholder.js b/js/components/ascribe_accordion_list/accordion_list_item_thumbnail_placeholder.js
new file mode 100644
index 00000000..37c98371
--- /dev/null
+++ b/js/components/ascribe_accordion_list/accordion_list_item_thumbnail_placeholder.js
@@ -0,0 +1,15 @@
+'use strict'
+
+import React from 'react';
+
+let AccordionListItemThumbnailPlaceholder = React.createClass({
+ render() {
+ return (
+
+ A
+
+ );
+ }
+});
+
+export default AccordionListItemThumbnailPlaceholder;
diff --git a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
index da45d1e8..a8cab166 100644
--- a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
+++ b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
@@ -31,6 +31,7 @@ let AccordionListItemWallet = React.createClass({
propTypes: {
className: React.PropTypes.string,
content: React.PropTypes.object,
+ thumbnailPlaceholder: React.PropTypes.func,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
@@ -123,32 +124,36 @@ let AccordionListItemWallet = React.createClass({
},
render() {
+ const { children, className, content, thumbnailPlaceholder } = this.props;
return (
- {Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}
+ {Moment(content.date_created, 'YYYY-MM-DD').year()}
{this.getLicences()}
- }
+
+ }
buttons={
}
- badge={this.getGlyphicon()}>
+
+ }
+ badge={this.getGlyphicon()}
+ thumbnailPlaceholder={thumbnailPlaceholder}>
{this.getCreateEditionsDialog()}
{/* this.props.children is AccordionListItemTableEditions */}
- {this.props.children}
+ {children}
);
}
diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js
index 6a3df7b2..97f2e173 100644
--- a/js/components/ascribe_buttons/acls/acl_button.js
+++ b/js/components/ascribe_buttons/acls/acl_button.js
@@ -26,7 +26,7 @@ export default function ({ action, displayName, title, tooltip }) {
availableAcls: React.PropTypes.object.isRequired,
buttonAcceptName: React.PropTypes.string,
buttonAcceptClassName: React.PropTypes.string,
- currentUser: React.PropTypes.object.isRequired,
+ currentUser: React.PropTypes.object,
email: React.PropTypes.string,
pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object,
diff --git a/js/components/ascribe_detail/detail_property.js b/js/components/ascribe_detail/detail_property.js
index 9ea37285..8b0f50b5 100644
--- a/js/components/ascribe_detail/detail_property.js
+++ b/js/components/ascribe_detail/detail_property.js
@@ -7,6 +7,7 @@ let DetailProperty = React.createClass({
propTypes: {
label: React.PropTypes.string,
value: React.PropTypes.oneOfType([
+ React.PropTypes.number,
React.PropTypes.string,
React.PropTypes.element
]),
diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js
index 254746a6..2de5eacf 100644
--- a/js/components/ascribe_detail/edition.js
+++ b/js/components/ascribe_detail/edition.js
@@ -41,12 +41,20 @@ import { getLangText } from '../../utils/lang_utils';
*/
let Edition = React.createClass({
propTypes: {
+ actionPanelButtonListType: React.PropTypes.func,
+ furtherDetailsType: React.PropTypes.func,
edition: React.PropTypes.object,
loadEdition: React.PropTypes.func
},
mixins: [History],
+ getDefaultProps() {
+ return {
+ furtherDetailsType: FurtherDetails
+ };
+ },
+
getInitialState() {
return UserStore.getState();
},
@@ -74,6 +82,8 @@ let Edition = React.createClass({
},
render() {
+ let FurtherDetailsType = this.props.furtherDetailsType;
+
return (
@@ -89,6 +99,7 @@ let Edition = React.createClass({
@@ -150,7 +161,7 @@ let Edition = React.createClass({
show={this.props.edition.acl.acl_edit
|| Object.keys(this.props.edition.extra_data).length > 0
|| this.props.edition.other_data.length > 0}>
- 0){
- let statusStr = this.props.edition.status.join().replace(/_/, ' ');
+ let statusStr = this.props.edition.status.join(', ').replace(/_/g, ' ');
status = ;
if (this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer){
status = (
@@ -195,7 +207,7 @@ let EditionSummary = React.createClass({
},
render() {
- let { edition, currentUser } = this.props;
+ let { actionPanelButtonListType, edition, currentUser } = this.props;
return (
diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js
index 2ef753fa..36a79e7c 100644
--- a/js/components/ascribe_detail/edition_action_panel.js
+++ b/js/components/ascribe_detail/edition_action_panel.js
@@ -36,6 +36,7 @@ import { getLangText } from '../../utils/lang_utils';
*/
let EditionActionPanel = React.createClass({
propTypes: {
+ actionPanelButtonListType: React.PropTypes.func,
edition: React.PropTypes.object,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func
@@ -43,6 +44,12 @@ let EditionActionPanel = React.createClass({
mixins: [History],
+ getDefaultProps() {
+ return {
+ actionPanelButtonListType: AclButtonList
+ };
+ },
+
getInitialState() {
return PieceListStore.getState();
},
@@ -87,7 +94,10 @@ let EditionActionPanel = React.createClass({
},
render(){
- let {edition, currentUser} = this.props;
+ const {
+ actionPanelButtonListType: ActionPanelButtonListType,
+ edition,
+ currentUser } = this.props;
if (edition &&
edition.notifications &&
@@ -104,7 +114,7 @@ let EditionActionPanel = React.createClass({
return (
-
+ expanded={false}>
+ type="text"
+ value={edition.bitcoin_id}
+ readOnly />
+
);
diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js
index febe652d..107cf1ee 100644
--- a/js/components/ascribe_detail/edition_container.js
+++ b/js/components/ascribe_detail/edition_container.js
@@ -17,6 +17,8 @@ import { setDocumentTitle } from '../../utils/dom_utils';
*/
let EditionContainer = React.createClass({
propTypes: {
+ actionPanelButtonListType: React.PropTypes.func,
+ furtherDetailsType: React.PropTypes.func,
params: React.PropTypes.object
},
@@ -71,6 +73,8 @@ let EditionContainer = React.createClass({
return (
);
diff --git a/js/components/ascribe_detail/further_details_fileuploader.js b/js/components/ascribe_detail/further_details_fileuploader.js
index 9a1f091c..dc638816 100644
--- a/js/components/ascribe_detail/further_details_fileuploader.js
+++ b/js/components/ascribe_detail/further_details_fileuploader.js
@@ -10,9 +10,12 @@ import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
import { getCookie } from '../../utils/fetch_api_utils';
+import { getLangText } from '../../utils/lang_utils';
+
let FurtherDetailsFileuploader = React.createClass({
propTypes: {
+ label: React.PropTypes.string,
uploadStarted: React.PropTypes.func,
pieceId: React.PropTypes.number,
otherData: React.PropTypes.arrayOf(React.PropTypes.object),
@@ -25,6 +28,7 @@ let FurtherDetailsFileuploader = React.createClass({
getDefaultProps() {
return {
+ label: getLangText('Additional files'),
multiple: false
};
},
@@ -44,7 +48,7 @@ let FurtherDetailsFileuploader = React.createClass({
return (
+ label={this.props.label}>
0
|| this.state.piece.other_data.length > 0}
defaultExpanded={true}>
-
diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js
index 0e3517a2..98b7cfe5 100644
--- a/js/components/ascribe_forms/form.js
+++ b/js/components/ascribe_forms/form.js
@@ -124,8 +124,18 @@ let Form = React.createClass({
getFormData() {
let data = {};
- for (let ref in this.refs) {
- data[this.refs[ref].props.name] = this.refs[ref].state.value;
+ for (let refName in this.refs) {
+ const ref = this.refs[refName];
+
+ if (ref.state && 'value' in ref.state) {
+ // 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) {
+ Object.assign(data, ref.state.value);
+ } else {
+ data[ref.props.name] = ref.state.value;
+ }
+ }
}
if (typeof this.props.getFormData === 'function') {
@@ -238,7 +248,15 @@ let Form = React.createClass({
renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) {
- return ReactAddons.addons.cloneWithProps(child, {
+ // Since refs will be overwritten by this functions return statement,
+ // we still want to be able to define refs for nested `Form` or `Property`
+ // children, which is why we're upfront simply invoking the callback-ref-
+ // function before overwriting it.
+ if(typeof child.ref === 'function' && this.refs[child.props.name]) {
+ child.ref(this.refs[child.props.name]);
+ }
+
+ return React.cloneElement(child, {
handleChange: this.handleChangeChild,
ref: child.props.name,
key: i,
diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js
index da40f009..2f0ebf05 100644
--- a/js/components/ascribe_forms/form_consign.js
+++ b/js/components/ascribe_forms/form_consign.js
@@ -6,31 +6,70 @@ import Button from 'react-bootstrap/lib/Button';
import Form from './form';
import Property from './property';
+
+import InputContractAgreementCheckbox from './input_contract_agreement_checkbox';
import InputTextAreaToggable from './input_textarea_toggable';
+
import AscribeSpinner from '../ascribe_spinner';
-import { getLangText } from '../../utils/lang_utils.js';
+
import AclInformation from '../ascribe_buttons/acl_information';
+import { getLangText } from '../../utils/lang_utils.js';
+
let ConsignForm = React.createClass({
propTypes: {
url: React.PropTypes.string,
id: React.PropTypes.object,
+ autoFocusProperty: React.PropTypes.string,
+ email: React.PropTypes.string,
message: React.PropTypes.string,
+ labels: React.PropTypes.object,
+ createPublicContractAgreement: React.PropTypes.bool,
handleSuccess: React.PropTypes.func
},
- getFormData(){
+ getDefaultProps() {
+ return {
+ labels: {}
+ };
+ },
+
+ getInitialState() {
+ return {
+ email: this.props.email || ''
+ };
+ },
+
+ getFormData() {
return this.props.id;
},
+ handleEmailOnChange(event) {
+ // event.target.value is the submitted email of the consignee
+ this.setState({
+ email: event && event.target && event.target.value || ''
+ });
+ },
+
render() {
+ const { email } = this.state;
+ const {
+ autoFocusProperty,
+ createPublicContractAgreement,
+ email: defaultEmail,
+ handleSuccess,
+ id,
+ message,
+ labels,
+ url } = this.props;
+
return (
}>
+ label={labels.email || getLangText('Email')}
+ editable={!defaultEmail}
+ onChange={this.handleEmailOnChange}
+ overrideForm={!!defaultEmail}>
+ label={labels.message || getLangText('Personal Message')}
+ editable
+ overrideForm>
+
+
+
diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js
index aac4c5ea..a392fb03 100644
--- a/js/components/ascribe_forms/form_create_contract.js
+++ b/js/components/ascribe_forms/form_create_contract.js
@@ -91,14 +91,14 @@ let CreateContractForm = React.createClass({
+ expanded={false}>
+ expanded={false}>
diff --git a/js/components/ascribe_forms/form_loan.js b/js/components/ascribe_forms/form_loan.js
index 7a0af7fd..a204fb87 100644
--- a/js/components/ascribe_forms/form_loan.js
+++ b/js/components/ascribe_forms/form_loan.js
@@ -1,33 +1,34 @@
'use strict';
import React from 'react';
-
import classnames from 'classnames';
import Button from 'react-bootstrap/lib/Button';
+import ContractAgreementListStore from '../../stores/contract_agreement_list_store';
+
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 InputDate from './input_date';
+import InputTextAreaToggable from './input_textarea_toggable';
+import InputContractAgreementCheckbox from './input_contract_agreement_checkbox';
import AscribeSpinner from '../ascribe_spinner';
-import { mergeOptions } from '../../utils/general_utils';
-import { getLangText } from '../../utils/lang_utils';
import AclInformation from '../ascribe_buttons/acl_information';
+import { getLangText } from '../../utils/lang_utils';
+import { mergeOptions } from '../../utils/general_utils';
+
+
let LoanForm = React.createClass({
propTypes: {
loanHeading: React.PropTypes.string,
email: React.PropTypes.string,
gallery: React.PropTypes.string,
- startdate: React.PropTypes.object,
- enddate: React.PropTypes.object,
+ startDate: React.PropTypes.object,
+ endDate: React.PropTypes.object,
showPersonalMessage: React.PropTypes.bool,
showEndDate: React.PropTypes.bool,
showStartDate: React.PropTypes.bool,
@@ -36,7 +37,11 @@ let LoanForm = React.createClass({
id: React.PropTypes.object,
message: React.PropTypes.string,
createPublicContractAgreement: React.PropTypes.bool,
- handleSuccess: React.PropTypes.func
+ handleSuccess: React.PropTypes.func,
+ children: React.PropTypes.oneOfType([
+ React.PropTypes.object,
+ React.PropTypes.array
+ ])
},
getDefaultProps() {
@@ -45,148 +50,33 @@ 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);
+ return {
+ email: this.props.email || ''
+ };
},
onChange(state) {
this.setState(state);
},
- getContractAgreementsOrCreatePublic(email){
- ContractAgreementListActions.flushContractAgreementList.defer();
- if (email) {
- // fetch the available contractagreements (pending/accepted)
- ContractAgreementListActions.fetchAvailableContractAgreementList(email, true);
- }
- },
-
- getFormData(){
- return mergeOptions(
- this.props.id,
- this.getContractAgreementId()
- );
- },
-
- handleOnChange(event) {
+ handleEmailOnChange(event) {
// event.target.value is the submitted email of the loanee
- if(event && event.target && event.target.value && event.target.value.match(/.*@.*\..*/)) {
- this.getContractAgreementsOrCreatePublic(event.target.value);
- } else {
- ContractAgreementListActions.flushContractAgreementList();
- }
+ this.setState({
+ email: event && event.target && event.target.value || ''
+ });
},
- getContractAgreementId() {
- if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
- return {'contract_agreement_id': this.state.contractAgreementList[0].id};
- }
- return {};
+ handleReset() {
+ this.handleEmailOnChange();
},
- 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 (
-
-
-
- {getLangText('Download contract')}
-
- {/* We still need to send the server information that we're accepting */}
-
-
- );
- } else {
- return (
-
-
-
- {getLangText('I agree to the')}
-
- {getLangText('terms of ')} {contract.issuer}
-
-
-
-
- );
- }
- } else {
- return (
-
-
-
- );
- }
- },
-
- getAppendix() {
- if(this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
- let appendix = this.state.contractAgreementList[0].appendix;
- if (appendix && appendix.default) {
- return (
-
- {appendix.default}
-
- );
- }
- }
- return null;
+ getFormData() {
+ return this.props.id;
},
getButtons() {
@@ -214,14 +104,31 @@ let LoanForm = React.createClass({
},
render() {
+ const { email } = this.state;
+ const {
+ children,
+ createPublicContractAgreement,
+ email: defaultEmail,
+ handleSuccess,
+ gallery,
+ loanHeading,
+ message,
+ showPersonalMessage,
+ endDate,
+ startDate,
+ showEndDate,
+ showStartDate,
+ showPassword,
+ url } = this.props;
+
return (