mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 09:23:13 +01:00
Merged in AD-943-add-custom-additional-fields (pull request #60)
Ad 943 add custom additional fields
This commit is contained in:
commit
93e61cfff5
@ -15,8 +15,8 @@ class ContractAgreementListActions {
|
||||
}
|
||||
|
||||
fetchContractAgreementList(issuer, accepted, pending) {
|
||||
this.actions.updateContractAgreementList(null);
|
||||
return Q.Promise((resolve, reject) => {
|
||||
this.actions.updateContractAgreementList(null);
|
||||
OwnershipFetcher.fetchContractAgreementList(issuer, accepted, pending)
|
||||
.then((contractAgreementList) => {
|
||||
if (contractAgreementList.count > 0) {
|
||||
@ -35,27 +35,47 @@ class ContractAgreementListActions {
|
||||
);
|
||||
}
|
||||
|
||||
fetchAvailableContractAgreementList(issuer){
|
||||
fetchAvailableContractAgreementList(issuer, createContractAgreement) {
|
||||
return Q.Promise((resolve, reject) => {
|
||||
this.actions.fetchContractAgreementList(issuer, true, null)
|
||||
.then((contractAgreementListAccepted) => {
|
||||
if (!contractAgreementListAccepted) {
|
||||
// fetch pending agreements if no accepted ones
|
||||
return this.actions.fetchContractAgreementList(issuer, null, true);
|
||||
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) {
|
||||
this.actions.updateContractAgreementList(acceptedContractAgreementList.results);
|
||||
} else {
|
||||
// otherwise, we're looking for contract agreements that are still pending
|
||||
//
|
||||
// Normally nesting promises, but for this conditional one, it makes sense to not
|
||||
// overcomplicate the method
|
||||
OwnershipFetcher.fetchContractAgreementList(issuer, null, true)
|
||||
.then((pendingContractAgreementList) => {
|
||||
if(pendingContractAgreementList.count > 0) {
|
||||
this.actions.updateContractAgreementList(pendingContractAgreementList.results);
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.logGlobal(err);
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
resolve(contractAgreementListAccepted);
|
||||
}
|
||||
}).then((contractAgreementListPending) => {
|
||||
resolve(contractAgreementListPending);
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.logGlobal(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createContractAgreementFromPublicContract(issuer){
|
||||
createContractAgreementFromPublicContract(issuer) {
|
||||
ContractListActions.fetchContractList(null, null, issuer)
|
||||
.then((publicContract) => {
|
||||
// create an agreement with the public contract if there is one
|
||||
@ -88,7 +108,6 @@ class ContractAgreementListActions {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default alt.createActions(ContractAgreementListActions);
|
||||
|
@ -162,21 +162,24 @@ let AclButton = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let shouldDisplay = this.props.availableAcls[this.props.action];
|
||||
let aclProps = this.actionProperties();
|
||||
let buttonClassName = this.props.buttonAcceptClassName ? this.props.buttonAcceptClassName : '';
|
||||
return (
|
||||
<ModalWrapper
|
||||
trigger={
|
||||
<button className={shouldDisplay ? 'btn btn-default btn-sm ' + buttonClassName : 'hidden'}>
|
||||
{this.sanitizeAction()}
|
||||
</button>
|
||||
}
|
||||
handleSuccess={aclProps.handleSuccess}
|
||||
title={aclProps.title}>
|
||||
{aclProps.form}
|
||||
</ModalWrapper>
|
||||
);
|
||||
if (this.props.availableAcls){
|
||||
let shouldDisplay = this.props.availableAcls[this.props.action];
|
||||
let aclProps = this.actionProperties();
|
||||
let buttonClassName = this.props.buttonAcceptClassName ? this.props.buttonAcceptClassName : '';
|
||||
return (
|
||||
<ModalWrapper
|
||||
trigger={
|
||||
<button className={shouldDisplay ? 'btn btn-default btn-sm ' + buttonClassName : 'hidden'}>
|
||||
{this.sanitizeAction()}
|
||||
</button>
|
||||
}
|
||||
handleSuccess={aclProps.handleSuccess}
|
||||
title={aclProps.title}>
|
||||
{aclProps.form}
|
||||
</ModalWrapper>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -152,8 +152,9 @@ let Edition = React.createClass({
|
||||
|
||||
<CollapsibleParagraph
|
||||
title="Notes"
|
||||
show={(this.state.currentUser.username && true || false) ||
|
||||
(this.props.edition.acl.acl_edit || this.props.edition.public_note)}>
|
||||
show={!!(this.state.currentUser.username
|
||||
|| this.props.edition.acl.acl_edit
|
||||
|| this.props.edition.public_note)}>
|
||||
<Note
|
||||
id={() => {return {'bitcoin_id': this.props.edition.bitcoin_id}; }}
|
||||
label={getLangText('Personal note (private)')}
|
||||
|
@ -44,14 +44,13 @@ let Note = React.createClass({
|
||||
<Form
|
||||
url={this.props.url}
|
||||
getFormData={this.props.id}
|
||||
handleSuccess={this.showNotification}>
|
||||
handleSuccess={this.showNotification}
|
||||
disabled={!this.props.editable}>
|
||||
<Property
|
||||
name='note'
|
||||
label={this.props.label}
|
||||
editable={this.props.editable}>
|
||||
label={this.props.label}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={this.props.editable}
|
||||
defaultValue={this.props.defaultValue}
|
||||
placeholder={this.props.placeholder}/>
|
||||
</Property>
|
||||
@ -63,4 +62,4 @@ let Note = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
export default Note
|
||||
export default Note;
|
@ -237,12 +237,11 @@ let PieceContainer = React.createClass({
|
||||
</CollapsibleParagraph>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Notes')}
|
||||
show={(this.state.currentUser.username && true || false) ||
|
||||
(this.state.piece.public_note)}>
|
||||
show={!!(this.state.currentUser.username || this.state.piece.public_note)}>
|
||||
<Note
|
||||
id={this.getId}
|
||||
label={getLangText('Personal note (private)')}
|
||||
defaultValue={this.state.piece.private_note ? this.state.piece.private_note : null}
|
||||
defaultValue={this.state.piece.private_note || null}
|
||||
placeholder={getLangText('Enter your comments ...')}
|
||||
editable={true}
|
||||
successMessage={getLangText('Private note saved')}
|
||||
|
@ -203,7 +203,7 @@ let Form = React.createClass({
|
||||
}
|
||||
let buttons = null;
|
||||
|
||||
if (this.state.edited){
|
||||
if (this.state.edited && !this.props.disabled){
|
||||
buttons = (
|
||||
<div className="row" style={{margin: 0}}>
|
||||
<p className="pull-right">
|
||||
|
@ -56,10 +56,10 @@ let ConsignForm = React.createClass({
|
||||
<Property
|
||||
name='consign_message'
|
||||
label={getLangText('Personal Message')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required="required"/>
|
||||
|
@ -35,7 +35,7 @@ let ContractAgreementForm = React.createClass({
|
||||
|
||||
componentDidMount() {
|
||||
ContractListStore.listen(this.onChange);
|
||||
ContractListActions.fetchContractList(true);
|
||||
ContractListActions.fetchContractList(true, false);
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -68,15 +68,7 @@ let ContractAgreementForm = React.createClass({
|
||||
<Property
|
||||
name='contract'
|
||||
label={getLangText('Contract Type')}
|
||||
onChange={this.onContractChange}
|
||||
footer={
|
||||
<a
|
||||
className="pull-right"
|
||||
href={contractList[this.state.selectedContract].blob}
|
||||
target="_blank">
|
||||
{getLangText('Learn more')}
|
||||
</a>
|
||||
}>
|
||||
onChange={this.onContractChange}>
|
||||
<select name="contract">
|
||||
{contractList.map((contract, i) => {
|
||||
return (
|
||||
@ -95,45 +87,58 @@ let ContractAgreementForm = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.state.contractList && this.state.contractList.length > 0) {
|
||||
return (
|
||||
<Form
|
||||
className="ascribe-form-bordered ascribe-form-wrapper"
|
||||
ref='form'
|
||||
url={ApiUrls.ownership_contract_agreements}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
buttons={<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login">
|
||||
{getLangText('Send loan request')}
|
||||
</button>}
|
||||
spinner={
|
||||
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||
</span>
|
||||
}>
|
||||
<div className="ascribe-form-header">
|
||||
<h3>{getLangText('Contract form')}</h3>
|
||||
</div>
|
||||
<Property
|
||||
name='signee'
|
||||
label={getLangText('Artist Email')}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder={getLangText('(e.g. andy@warhol.co.uk)')}
|
||||
required/>
|
||||
</Property>
|
||||
{this.getContracts()}
|
||||
<PropertyCollapsible
|
||||
name='appendix'
|
||||
checkboxLabel={getLangText('Add appendix to the contract')}>
|
||||
<span>{getLangText('Appendix')}</span>
|
||||
{/* We're using disabled on a form here as PropertyCollapsible currently
|
||||
does not support the disabled + overrideForm functionality */}
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
disabled={false}
|
||||
placeholder={getLangText('This will be appended to the contract selected above')}/>
|
||||
</PropertyCollapsible>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Form
|
||||
className="ascribe-form-bordered ascribe-form-wrapper"
|
||||
ref='form'
|
||||
url={ApiUrls.ownership_contract_agreements}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
buttons={<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login">
|
||||
{getLangText('Send loan request')}
|
||||
</button>}
|
||||
spinner={
|
||||
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||
</span>
|
||||
}>
|
||||
<div className="ascribe-form-header">
|
||||
<h3>{getLangText('Contract form')}</h3>
|
||||
</div>
|
||||
<Property
|
||||
name='signee'
|
||||
label={getLangText('Artist Email')}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder={getLangText('(e.g. andy@warhol.co.uk)')}
|
||||
required/>
|
||||
</Property>
|
||||
{this.getContracts()}
|
||||
<PropertyCollapsible
|
||||
name='appendix'
|
||||
checkboxLabel={getLangText('Add appendix to the contract')}>
|
||||
<span>{getLangText('Appendix')}</span>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('This will be appended to the contract selected above')}/>
|
||||
</PropertyCollapsible>
|
||||
</Form>
|
||||
<div>
|
||||
<p className="text-center">
|
||||
{getLangText('No contracts uploaded yet, please go to the ')}
|
||||
<a href="settings">{getLangText('settings page')}</a>
|
||||
{getLangText(' and create them.')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
80
js/components/ascribe_forms/form_copyright_association.js
Normal file
80
js/components/ascribe_forms/form_copyright_association.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||
|
||||
import Form from './form';
|
||||
import Property from './property';
|
||||
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
|
||||
let CopyrightAssociationForm = React.createClass({
|
||||
propTypes: {
|
||||
currentUser: React.PropTypes.object
|
||||
},
|
||||
|
||||
handleSubmitSuccess(){
|
||||
let notification = getLangText('Copyright association updated');
|
||||
notification = new GlobalNotificationModel(notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
getProfileFormData(){
|
||||
return {email: this.props.currentUser.email};
|
||||
},
|
||||
|
||||
render() {
|
||||
let selectedState;
|
||||
let selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- ';
|
||||
|
||||
if (this.props.currentUser && this.props.currentUser.profile
|
||||
&& this.props.currentUser.profile.copyright_association) {
|
||||
selectedState = AppConstants.copyrightAssociations.indexOf(this.props.currentUser.profile.copyright_association);
|
||||
selectedState = selectedState !== -1 ? AppConstants.copyrightAssociations[selectedState] : selectDefaultValue;
|
||||
}
|
||||
|
||||
if (this.props.currentUser && this.props.currentUser.email){
|
||||
return (
|
||||
<Form
|
||||
ref='form'
|
||||
url={ApiUrls.users_profile}
|
||||
getFormData={this.getProfileFormData}
|
||||
handleSuccess={this.handleSubmitSuccess}>
|
||||
<Property
|
||||
name="copyright_association"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
label={getLangText('Copyright Association')}
|
||||
style={{paddingBottom: 0}}>
|
||||
<select defaultValue={selectedState} name="contract">
|
||||
<option
|
||||
name={0}
|
||||
key={0}
|
||||
value={selectDefaultValue}>
|
||||
{selectDefaultValue}
|
||||
</option>
|
||||
{AppConstants.copyrightAssociations.map((association, i) => {
|
||||
return (
|
||||
<option
|
||||
name={i + 1}
|
||||
key={i + 1}
|
||||
value={association}>
|
||||
{ association }
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export default CopyrightAssociationForm;
|
@ -35,6 +35,7 @@ let LoanForm = React.createClass({
|
||||
url: React.PropTypes.string,
|
||||
id: React.PropTypes.object,
|
||||
message: React.PropTypes.string,
|
||||
createPublicContractAgreement: React.PropTypes.bool,
|
||||
handleSuccess: React.PropTypes.func
|
||||
},
|
||||
|
||||
@ -44,7 +45,8 @@ let LoanForm = React.createClass({
|
||||
showPersonalMessage: true,
|
||||
showEndDate: true,
|
||||
showStartDate: true,
|
||||
showPassword: true
|
||||
showPassword: true,
|
||||
createPublicContractAgreement: true
|
||||
};
|
||||
},
|
||||
|
||||
@ -57,11 +59,14 @@ let LoanForm = React.createClass({
|
||||
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) {
|
||||
// however, it can also be that at the time the component is mounting,
|
||||
// the email is not defined (because it's asynchronously fetched from the server).
|
||||
// Then we need to update it as soon as it is included into LoanForm's props.
|
||||
if(nextProps && nextProps.email) {
|
||||
if(nextProps && nextProps.email && this.props.email !== nextProps.email) {
|
||||
this.getContractAgreementsOrCreatePublic(nextProps.email);
|
||||
}
|
||||
},
|
||||
@ -75,15 +80,10 @@ let LoanForm = React.createClass({
|
||||
},
|
||||
|
||||
getContractAgreementsOrCreatePublic(email){
|
||||
ContractAgreementListActions.flushContractAgreementList();
|
||||
ContractAgreementListActions.flushContractAgreementList.defer();
|
||||
if (email) {
|
||||
ContractAgreementListActions.fetchAvailableContractAgreementList(email).then(
|
||||
(contractAgreementList) => {
|
||||
if (!contractAgreementList) {
|
||||
ContractAgreementListActions.createContractAgreementFromPublicContract(email);
|
||||
}
|
||||
}
|
||||
);
|
||||
// fetch the available contractagreements (pending/accepted)
|
||||
ContractAgreementListActions.fetchAvailableContractAgreementList(email, true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -115,25 +115,46 @@ let LoanForm = React.createClass({
|
||||
// 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 contract = this.state.contractAgreementList[0].contract;
|
||||
let contractAgreement = this.state.contractAgreementList[0];
|
||||
let contract = contractAgreement.contract;
|
||||
|
||||
return (
|
||||
<Property
|
||||
name="terms"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox
|
||||
key="terms_explicitly"
|
||||
defaultChecked={false}>
|
||||
<span>
|
||||
{getLangText('I agree to the')}
|
||||
<a href={contract.blob.url_safe} target="_blank">
|
||||
{getLangText('terms of ')} {contract.issuer}
|
||||
</a>
|
||||
</span>
|
||||
</InputCheckbox>
|
||||
</Property>
|
||||
);
|
||||
if(contractAgreement.datetime_accepted) {
|
||||
return (
|
||||
<Property
|
||||
name="terms"
|
||||
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"/>
|
||||
{/* 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-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox
|
||||
key="terms_explicitly"
|
||||
defaultChecked={false}>
|
||||
<span>
|
||||
{getLangText('I agree to the')}
|
||||
<a href={contract.blob.url_safe} target="_blank">
|
||||
{getLangText('terms of ')} {contract.issuer}
|
||||
</a>
|
||||
</span>
|
||||
</InputCheckbox>
|
||||
</Property>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<Property
|
||||
@ -148,6 +169,22 @@ let LoanForm = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
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() {
|
||||
if(this.props.loanHeading) {
|
||||
return (
|
||||
@ -193,7 +230,7 @@ let LoanForm = React.createClass({
|
||||
name='loanee'
|
||||
label={getLangText('Loanee Email')}
|
||||
editable={!this.props.email}
|
||||
onBlur={this.handleOnChange}
|
||||
onChange={this.handleOnChange}
|
||||
overrideForm={!!this.props.email}>
|
||||
<input
|
||||
value={this.props.email}
|
||||
@ -235,14 +272,16 @@ let LoanForm = React.createClass({
|
||||
name='loan_message'
|
||||
label={getLangText('Personal Message')}
|
||||
editable={true}
|
||||
overrideForm={true}
|
||||
hidden={!this.props.showPersonalMessage}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required={this.props.showPersonalMessage ? 'required' : ''}/>
|
||||
</Property>
|
||||
{this.getContractCheckbox()}
|
||||
{this.getAppendix()}
|
||||
<Property
|
||||
name='password'
|
||||
label={getLangText('Password')}
|
||||
@ -252,7 +291,6 @@ let LoanForm = React.createClass({
|
||||
placeholder={getLangText('Enter your password')}
|
||||
required={this.props.showPassword ? 'required' : ''}/>
|
||||
</Property>
|
||||
{this.getContractCheckbox()}
|
||||
{this.props.children}
|
||||
</Form>
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ let LoanRequestAnswerForm = React.createClass({
|
||||
url: React.PropTypes.string,
|
||||
id: React.PropTypes.object,
|
||||
message: React.PropTypes.string,
|
||||
handleSuccess: React.PropTypes.func.required
|
||||
handleSuccess: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -27,7 +27,7 @@ let LoginForm = React.createClass({
|
||||
onLogin: React.PropTypes.func
|
||||
},
|
||||
|
||||
mixins: [Router.Navigation],
|
||||
mixins: [Router.Navigation, Router.State],
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
@ -95,6 +95,7 @@ let LoginForm = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let email = this.getQuery().email || null;
|
||||
return (
|
||||
<Form
|
||||
className="ascribe-form-bordered"
|
||||
@ -122,7 +123,8 @@ let LoginForm = React.createClass({
|
||||
<input
|
||||
type="email"
|
||||
placeholder={getLangText('Enter your email')}
|
||||
name="username"
|
||||
name="email"
|
||||
defaultValue={email}
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
|
@ -41,14 +41,13 @@ let PieceExtraDataForm = React.createClass({
|
||||
ref='form'
|
||||
url={url}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
getFormData={this.getFormData}>
|
||||
getFormData={this.getFormData}
|
||||
disabled={!this.props.editable}>
|
||||
<Property
|
||||
name={this.props.name}
|
||||
label={this.props.title}
|
||||
editable={this.props.editable}>
|
||||
label={this.props.title}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={this.props.editable}
|
||||
defaultValue={defaultValue}
|
||||
placeholder={getLangText('Fill in%s', ' ') + this.props.title}
|
||||
required="required"/>
|
||||
|
@ -112,7 +112,7 @@ let RegisterPieceForm = React.createClass({
|
||||
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||
isFineUploaderActive={this.props.isFineUploaderActive}
|
||||
onLoggedOut={this.props.onLoggedOut}
|
||||
editable={this.props.isFineUploaderEditable}
|
||||
disabled={!this.props.isFineUploaderEditable}
|
||||
enableLocalHashing={enableLocalHashing}/>
|
||||
</Property>
|
||||
<Property
|
||||
|
@ -60,10 +60,10 @@ let ShareForm = React.createClass({
|
||||
<Property
|
||||
name='share_message'
|
||||
label='Personal Message'
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required="required"/>
|
||||
|
@ -66,17 +66,24 @@ let SignupForm = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
getFormData() {
|
||||
if (this.getQuery().token){
|
||||
return {token: this.getQuery().token};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render() {
|
||||
let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' +
|
||||
getLangText('This password is securing your digital property like a bank account') + '.\n ' +
|
||||
getLangText('Store it in a safe place') + '!';
|
||||
let email = this.getQuery().email ? this.getQuery().email : null;
|
||||
let email = this.getQuery().email || null;
|
||||
return (
|
||||
<Form
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={ApiUrls.users_signup}
|
||||
getFormData={this.getQuery}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.handleSuccess}
|
||||
buttons={
|
||||
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||
|
@ -45,20 +45,20 @@ let PieceSubmitToPrizeForm = React.createClass({
|
||||
<Property
|
||||
name='artist_statement'
|
||||
label={getLangText('Artist statement')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('Enter your statement')}
|
||||
required="required"/>
|
||||
</Property>
|
||||
<Property
|
||||
name='work_description'
|
||||
label={getLangText('Work description')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('Enter the description for your work')}
|
||||
required="required"/>
|
||||
</Property>
|
||||
|
@ -61,10 +61,10 @@ let TransferForm = React.createClass({
|
||||
<Property
|
||||
name='transfer_message'
|
||||
label={getLangText('Personal Message')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required="required"/>
|
||||
|
@ -50,10 +50,10 @@ let UnConsignForm = React.createClass({
|
||||
<Property
|
||||
name='unconsign_message'
|
||||
label={getLangText('Personal Message')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required="required"/>
|
||||
|
@ -50,10 +50,10 @@ let UnConsignRequestForm = React.createClass({
|
||||
<Property
|
||||
name='unconsign_request_message'
|
||||
label={getLangText('Personal Message')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
defaultValue={this.props.message}
|
||||
placeholder={getLangText('Enter a message...')}
|
||||
required="required"/>
|
||||
|
@ -25,7 +25,10 @@ let InputCheckbox = React.createClass({
|
||||
|
||||
// provided by Property
|
||||
disabled: React.PropTypes.bool,
|
||||
onChange: React.PropTypes.func
|
||||
onChange: React.PropTypes.func,
|
||||
|
||||
// can be used to style the component from the outside
|
||||
style: React.PropTypes.object
|
||||
},
|
||||
|
||||
// As HTML inputs, we're setting the default value for an input to checked === false
|
||||
@ -98,6 +101,7 @@ let InputCheckbox = React.createClass({
|
||||
|
||||
return (
|
||||
<span
|
||||
style={this.props.style}
|
||||
onClick={this.onChange}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
@ -8,12 +8,14 @@ import AppConstants from '../../constants/application_constants';
|
||||
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
|
||||
let InputFileUploader = React.createClass({
|
||||
let InputFineUploader = React.createClass({
|
||||
propTypes: {
|
||||
setIsUploadReady: React.PropTypes.func,
|
||||
isReadyForFormSubmission: React.PropTypes.func,
|
||||
submitFileName: React.PropTypes.func,
|
||||
|
||||
areAssetsDownloadable: React.PropTypes.bool,
|
||||
|
||||
onClick: React.PropTypes.func,
|
||||
keyRoutine: React.PropTypes.shape({
|
||||
url: React.PropTypes.string,
|
||||
@ -33,7 +35,7 @@ let InputFileUploader = React.createClass({
|
||||
// before login in
|
||||
isFineUploaderActive: React.PropTypes.bool,
|
||||
onLoggedOut: React.PropTypes.func,
|
||||
editable: React.PropTypes.bool,
|
||||
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
|
||||
// provided by Property
|
||||
@ -86,7 +88,7 @@ let InputFileUploader = React.createClass({
|
||||
submitFile={this.submitFile}
|
||||
setIsUploadReady={this.props.setIsUploadReady}
|
||||
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
||||
areAssetsDownloadable={false}
|
||||
areAssetsDownloadable={this.props.areAssetsDownloadable}
|
||||
areAssetsEditable={editable}
|
||||
signature={{
|
||||
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||
@ -109,4 +111,4 @@ let InputFileUploader = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
export default InputFileUploader;
|
||||
export default InputFineUploader;
|
@ -7,7 +7,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
||||
|
||||
let InputTextAreaToggable = React.createClass({
|
||||
propTypes: {
|
||||
editable: React.PropTypes.bool.isRequired,
|
||||
disabled: React.PropTypes.bool,
|
||||
rows: React.PropTypes.number.isRequired,
|
||||
required: React.PropTypes.string,
|
||||
defaultValue: React.PropTypes.string
|
||||
@ -15,10 +15,26 @@ let InputTextAreaToggable = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
value: this.props.defaultValue
|
||||
value: null
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
value: this.props.defaultValue
|
||||
});
|
||||
},
|
||||
|
||||
componentDidUpdate() {
|
||||
// If the initial value of state.value is null, we want to set props.defaultValue
|
||||
// as a value. In all other cases TextareaAutosize.onChange is updating.handleChange already
|
||||
if(this.state.value === null && this.props.defaultValue) {
|
||||
this.setState({
|
||||
value: this.props.defaultValue
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handleChange(event) {
|
||||
this.setState({value: event.target.value});
|
||||
this.props.onChange(event);
|
||||
@ -28,7 +44,7 @@ let InputTextAreaToggable = React.createClass({
|
||||
let className = 'form-control ascribe-textarea';
|
||||
let textarea = null;
|
||||
|
||||
if(this.props.editable) {
|
||||
if(!this.props.disabled) {
|
||||
className = className + ' ascribe-textarea-editable';
|
||||
textarea = (
|
||||
<TextareaAutosize
|
||||
|
@ -10,6 +10,8 @@ import Property from '../ascribe_forms/property';
|
||||
import InputCheckbox from '../ascribe_forms/input_checkbox';
|
||||
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association';
|
||||
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
|
||||
@ -17,8 +19,8 @@ import { getLangText } from '../../utils/lang_utils';
|
||||
|
||||
let AccountSettings = React.createClass({
|
||||
propTypes: {
|
||||
currentUser: React.PropTypes.object.required,
|
||||
loadUser: React.PropTypes.func.required
|
||||
currentUser: React.PropTypes.object.isRequired,
|
||||
loadUser: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
handleSuccess(){
|
||||
@ -102,6 +104,7 @@ let AccountSettings = React.createClass({
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
{content}
|
||||
<CopyrightAssociationForm currentUser={this.props.currentUser}/>
|
||||
{profile}
|
||||
{/*<Form
|
||||
url={AppConstants.serverUrl + 'api/users/set_language/'}>
|
||||
|
@ -83,7 +83,7 @@ let ContractSettings = React.createClass({
|
||||
|
||||
return (
|
||||
<AclProxy
|
||||
aclName="acl_view_contract_settings"
|
||||
aclName="acl_view_settings_contract"
|
||||
aclObject={this.props.currentUser.acl}>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Contracts')}
|
||||
|
@ -43,15 +43,18 @@ let SettingsContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="settings-container">
|
||||
<AccountSettings currentUser={this.state.currentUser} loadUser={this.loadUser}/>
|
||||
{this.props.children}
|
||||
<APISettings />
|
||||
<BitcoinWalletSettings />
|
||||
<ContractSettings currentUser={this.state.currentUser} loadUser={this.loadUser}/>
|
||||
</div>
|
||||
);
|
||||
if (this.state.currentUser && this.state.currentUser.username) {
|
||||
return (
|
||||
<div className="settings-container">
|
||||
<AccountSettings currentUser={this.state.currentUser} loadUser={this.loadUser}/>
|
||||
{this.props.children}
|
||||
<APISettings />
|
||||
<BitcoinWalletSettings />
|
||||
<ContractSettings currentUser={this.state.currentUser} loadUser={this.loadUser}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -178,7 +178,7 @@ let SlidesContainer = React.createClass({
|
||||
let breadcrumbs = [];
|
||||
|
||||
ReactAddons.Children.map(this.props.children, (child, i) => {
|
||||
if(i >= this.state.startFrom && child.props['data-slide-title']) {
|
||||
if(child && i >= this.state.startFrom && child.props['data-slide-title']) {
|
||||
breadcrumbs.push(child.props['data-slide-title']);
|
||||
}
|
||||
});
|
||||
@ -229,7 +229,7 @@ let SlidesContainer = React.createClass({
|
||||
|
||||
// since the default parameter of startFrom is -1, we do not need to check
|
||||
// if its actually present in the url bar, as it will just not match
|
||||
if(i >= this.state.startFrom) {
|
||||
if(child && i >= this.state.startFrom) {
|
||||
return ReactAddons.addons.cloneWithProps(child, {
|
||||
className: 'ascribe-slide',
|
||||
style: {
|
||||
|
@ -84,10 +84,11 @@ let CoaVerifyForm = React.createClass({
|
||||
</Property>
|
||||
<Property
|
||||
name='signature'
|
||||
label="Signature">
|
||||
label="Signature"
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={3}
|
||||
editable={true}
|
||||
placeholder={getLangText('Copy paste the signature on the bottom of your Certificate of Authenticity')}
|
||||
required/>
|
||||
</Property>
|
||||
|
@ -42,6 +42,7 @@ let SignupContainer = React.createClass({
|
||||
{getLangText('Already an ascribe user')}? <Link to="login">{getLangText('Log in')}...</Link><br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -124,7 +124,7 @@ let AccordionListItemPrize = React.createClass({
|
||||
<div>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submit">
|
||||
aclName="acl_wallet_submit">
|
||||
<SubmitToPrizeButton
|
||||
className="pull-right"
|
||||
piece={this.props.content}
|
||||
|
@ -426,10 +426,10 @@ let PrizePieceDetails = React.createClass({
|
||||
<Property
|
||||
name={data}
|
||||
label={label}
|
||||
editable={false}>
|
||||
editable={false}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={false}
|
||||
defaultValue={this.props.piece.extra_data[data]}/>
|
||||
</Property>);
|
||||
}
|
||||
|
@ -41,20 +41,20 @@ let PrizeRegisterPiece = React.createClass({
|
||||
<Property
|
||||
name='artist_statement'
|
||||
label={getLangText('Artist statement')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('Enter your statement')}
|
||||
required="required"/>
|
||||
</Property>
|
||||
<Property
|
||||
name='work_description'
|
||||
label={getLangText('Work description')}
|
||||
editable={true}>
|
||||
editable={true}
|
||||
overrideForm={true}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('Enter the description for your work')}
|
||||
required="required"/>
|
||||
</Property>
|
||||
|
@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
|
||||
import ListRequestActions from '../../../../ascribe_forms/list_form_request_actions';
|
||||
import AclButtonList from '../../../../ascribe_buttons/acl_button_list';
|
||||
import DeleteButton from '../../../../ascribe_buttons/delete_button';
|
||||
|
||||
import AclProxy from '../../../../acl_proxy';
|
||||
|
||||
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
|
||||
|
||||
let WalletActionPanel = React.createClass({
|
||||
propTypes: {
|
||||
piece: React.PropTypes.object.isRequired,
|
||||
currentUser: React.PropTypes.object.isRequired,
|
||||
loadPiece: React.PropTypes.func.isRequired,
|
||||
submitButtonType: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
render(){
|
||||
if (this.props.piece &&
|
||||
this.props.piece.notifications &&
|
||||
this.props.piece.notifications.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.props.piece}
|
||||
currentUser={this.props.currentUser}
|
||||
handleSuccess={this.props.loadPiece}
|
||||
notifications={this.props.piece.notifications}/>);
|
||||
}
|
||||
else {
|
||||
|
||||
//We need to disable the normal acl_loan because we're inserting a custom acl_loan button
|
||||
let availableAcls;
|
||||
|
||||
if (this.props.piece && this.props.piece.acl && typeof this.props.piece.acl.acl_loan !== 'undefined') {
|
||||
// make a copy to not have side effects
|
||||
availableAcls = mergeOptions({}, this.props.piece.acl);
|
||||
availableAcls.acl_loan = false;
|
||||
}
|
||||
let SubmitButtonType = this.props.submitButtonType;
|
||||
|
||||
return (
|
||||
<AclButtonList
|
||||
className="text-center ascribe-button-list"
|
||||
availableAcls={availableAcls}
|
||||
editions={this.props.piece}
|
||||
handleSuccess={this.loadPiece}>
|
||||
<AclProxy
|
||||
aclObject={availableAcls}
|
||||
aclName="acl_wallet_submit">
|
||||
<SubmitButtonType
|
||||
className="btn-sm"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.props.piece}/>
|
||||
</AclProxy>
|
||||
<DeleteButton
|
||||
handleSuccess={this.handleDeleteSuccess}
|
||||
piece={this.props.piece}/>
|
||||
</AclButtonList>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default WalletActionPanel;
|
@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
|
||||
import Piece from '../../../../../components/ascribe_detail/piece';
|
||||
|
||||
import WalletActionPanel from './wallet_action_panel';
|
||||
import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import HistoryIterator from '../../../../ascribe_detail/history_iterator';
|
||||
import Note from '../../../../ascribe_detail/note';
|
||||
|
||||
import DetailProperty from '../../../../ascribe_detail/detail_property';
|
||||
|
||||
import ApiUrls from '../../../../../constants/api_urls';
|
||||
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
import AppConstants from '../../../../../constants/application_constants';
|
||||
|
||||
let WalletPieceContainer = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
piece: React.PropTypes.object.isRequired,
|
||||
currentUser: React.PropTypes.object.isRequired,
|
||||
loadPiece: React.PropTypes.func.isRequired,
|
||||
submitButtonType: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
|
||||
render() {
|
||||
if(this.props.piece && this.props.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
piece={this.props.piece}
|
||||
loadPiece={this.loadPiece}
|
||||
header={
|
||||
<div className="ascribe-detail-header">
|
||||
<hr style={{marginTop: 0}}/>
|
||||
<h1 className="ascribe-detail-title">{this.props.piece.title}</h1>
|
||||
<DetailProperty label="BY" value={this.props.piece.artist_name} />
|
||||
<DetailProperty label="DATE" value={ this.props.piece.date_created.slice(0, 4) } />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
subheader={
|
||||
<div className="ascribe-detail-header">
|
||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.props.piece.user_registered } />
|
||||
<DetailProperty label={getLangText('ID')} value={ this.props.piece.bitcoin_id } ellipsis={true} />
|
||||
<hr/>
|
||||
</div>
|
||||
}>
|
||||
<WalletActionPanel
|
||||
piece={this.props.piece}
|
||||
currentUser={this.props.currentUser}
|
||||
loadPiece={this.loadPiece}
|
||||
submitButtonType={this.props.submitButtonType}/>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Loan History')}
|
||||
show={this.props.piece.loan_history && this.props.piece.loan_history.length > 0}>
|
||||
<HistoryIterator
|
||||
history={this.props.piece.loan_history}/>
|
||||
</CollapsibleParagraph>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Notes')}
|
||||
show={!!(this.props.currentUser.username || this.props.piece.public_note)}>
|
||||
<Note
|
||||
id={() => {return {'id': this.props.piece.id}; }}
|
||||
label={getLangText('Personal note (private)')}
|
||||
defaultValue={this.props.piece.private_note || null}
|
||||
placeholder={getLangText('Enter your comments ...')}
|
||||
editable={true}
|
||||
successMessage={getLangText('Private note saved')}
|
||||
url={ApiUrls.note_private_piece}
|
||||
currentUser={this.props.currentUser}/>
|
||||
</CollapsibleParagraph>
|
||||
|
||||
{this.props.children}
|
||||
</Piece>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="fullpage-spinner">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
export default WalletPieceContainer;
|
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
||||
|
||||
@ -64,7 +63,7 @@ let CylandAccordionListItem = React.createClass({
|
||||
<div>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submit">
|
||||
aclName="acl_wallet_submit">
|
||||
<CylandSubmitButton
|
||||
className="pull-right"
|
||||
piece={this.props.content}
|
||||
@ -72,7 +71,7 @@ let CylandAccordionListItem = React.createClass({
|
||||
</AclProxy>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submitted">
|
||||
aclName="acl_wallet_submitted">
|
||||
<button
|
||||
disabled
|
||||
className="btn btn-default btn-xs pull-right">
|
||||
@ -82,7 +81,7 @@ let CylandAccordionListItem = React.createClass({
|
||||
</AclProxy>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_accepted">
|
||||
aclName="acl_wallet_accepted">
|
||||
<button
|
||||
disabled
|
||||
className="btn btn-default btn-xs pull-right">
|
||||
|
@ -7,27 +7,19 @@ import PieceStore from '../../../../../../stores/piece_store';
|
||||
|
||||
import UserStore from '../../../../../../stores/user_store';
|
||||
|
||||
import Piece from '../../../../../../components/ascribe_detail/piece';
|
||||
import CylandSubmitButton from '../ascribe_buttons/cyland_submit_button';
|
||||
|
||||
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import CylandAdditionalDataForm from '../ascribe_forms/cyland_additional_data_form';
|
||||
|
||||
import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
|
||||
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
import Form from '../../../../../../components/ascribe_forms/form';
|
||||
import Property from '../../../../../../components/ascribe_forms/property';
|
||||
import InputTextAreaToggable from '../../../../../../components/ascribe_forms/input_textarea_toggable';
|
||||
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import HistoryIterator from '../../../../../ascribe_detail/history_iterator';
|
||||
import Note from '../../../../../ascribe_detail/note';
|
||||
|
||||
import FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader';
|
||||
import DetailProperty from '../../../../../ascribe_detail/detail_property';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||
|
||||
|
||||
let CylandPieceContainer = React.createClass({
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
@ -38,23 +30,18 @@ let CylandPieceContainer = React.createClass({
|
||||
|
||||
componentDidMount() {
|
||||
PieceStore.listen(this.onChange);
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
UserStore.listen(this.onChange);
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.props.params.pieceId !== nextProps.params.pieceId) {
|
||||
PieceActions.updatePiece({});
|
||||
PieceActions.fetchOne(nextProps.params.pieceId);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
// Every time we're leaving the piece detail page,
|
||||
// just reset the piece that is saved in the piece store
|
||||
// as it will otherwise display wrong/old data once the user loads
|
||||
// the piece detail a second time
|
||||
PieceActions.updatePiece({});
|
||||
|
||||
this.loadPiece();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
PieceStore.unlisten(this.onChange);
|
||||
UserStore.unlisten(this.onChange);
|
||||
},
|
||||
@ -70,50 +57,24 @@ let CylandPieceContainer = React.createClass({
|
||||
render() {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
<WalletPieceContainer
|
||||
piece={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
loadPiece={this.loadPiece}
|
||||
header={
|
||||
<div className="ascribe-detail-header">
|
||||
<hr style={{marginTop: 0}}/>
|
||||
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
||||
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
subheader={
|
||||
<div className="ascribe-detail-header">
|
||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } />
|
||||
<DetailProperty label={getLangText('ID')} value={ this.state.piece.bitcoin_id } ellipsis={true} />
|
||||
<hr/>
|
||||
</div>
|
||||
}>
|
||||
|
||||
submitButtonType={CylandSubmitButton}>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Loan History')}
|
||||
show={this.state.piece.loan_history && this.state.piece.loan_history.length > 0}>
|
||||
<HistoryIterator
|
||||
history={this.state.piece.loan_history} />
|
||||
title={getLangText('Further Details')}
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
<CylandAdditionalDataForm
|
||||
piece={this.state.piece}
|
||||
disabled={!this.state.piece.acl.acl_edit}
|
||||
isInline={true} />
|
||||
</CollapsibleParagraph>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Notes')}
|
||||
show={(this.state.currentUser.username && true || false) ||
|
||||
(this.state.piece.public_note)}>
|
||||
<Note
|
||||
id={() => {return {'id': this.state.piece.id}; }}
|
||||
label={getLangText('Personal note (private)')}
|
||||
defaultValue={this.state.piece.private_note ? this.state.piece.private_note : null}
|
||||
placeholder={getLangText('Enter your comments ...')}
|
||||
editable={true}
|
||||
successMessage={getLangText('Private note saved')}
|
||||
url={ApiUrls.note_private_piece}
|
||||
currentUser={this.state.currentUser}/>
|
||||
</CollapsibleParagraph>
|
||||
<CylandPieceDetails piece={this.state.piece}/>
|
||||
</Piece>
|
||||
</WalletPieceContainer>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="fullpage-spinner">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
@ -123,47 +84,4 @@ let CylandPieceContainer = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let CylandPieceDetails = React.createClass({
|
||||
propTypes: {
|
||||
piece: React.PropTypes.object
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.props.piece && Object.keys(this.props.piece.extra_data).length !== 0){
|
||||
return (
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Further Details')}
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
<Form ref='form'>
|
||||
{Object.keys(this.props.piece.extra_data).map((data, i) => {
|
||||
let label = data.replace('_', ' ');
|
||||
return (
|
||||
<Property
|
||||
key={i}
|
||||
name={data}
|
||||
label={label}
|
||||
editable={false}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={false}
|
||||
defaultValue={this.props.piece.extra_data[data]}/>
|
||||
</Property>);
|
||||
}
|
||||
)}
|
||||
<FurtherDetailsFileuploader
|
||||
editable={false}
|
||||
pieceId={this.props.piece.id}
|
||||
otherData={this.props.piece.other_data}
|
||||
multiple={false}/>
|
||||
<hr />
|
||||
</Form>
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export default CylandPieceContainer;
|
||||
|
@ -9,6 +9,9 @@ import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_t
|
||||
|
||||
import FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
@ -20,10 +23,21 @@ import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_
|
||||
|
||||
let CylandAdditionalDataForm = React.createClass({
|
||||
propTypes: {
|
||||
handleSuccess: React.PropTypes.func.isRequired,
|
||||
handleSuccess: React.PropTypes.func,
|
||||
piece: React.PropTypes.object.isRequired,
|
||||
disabled: React.PropTypes.bool,
|
||||
isInline: React.PropTypes.bool
|
||||
},
|
||||
|
||||
disabled: React.PropTypes.bool
|
||||
getDefaultProps() {
|
||||
return {
|
||||
isInline: false
|
||||
};
|
||||
},
|
||||
|
||||
handleSuccess() {
|
||||
let notification = new GlobalNotificationModel('Further details successfully updated', 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
@ -63,61 +77,69 @@ let CylandAdditionalDataForm = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
if(this.props.piece && this.props.piece.id) {
|
||||
let { piece, isInline, disabled, handleSuccess } = this.props;
|
||||
let buttons, spinner, heading;
|
||||
|
||||
if(!isInline) {
|
||||
buttons = (
|
||||
<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={!this.state.isUploadReady || disabled}>
|
||||
{getLangText('Proceed to loan')}
|
||||
</button>
|
||||
);
|
||||
|
||||
spinner = (
|
||||
<div className="modal-footer">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||
</div>
|
||||
);
|
||||
|
||||
heading = (
|
||||
<div className="ascribe-form-header">
|
||||
<h3>
|
||||
{getLangText('Provide supporting materials')}
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(piece && piece.id) {
|
||||
return (
|
||||
<Form
|
||||
disabled={this.props.disabled}
|
||||
disabled={disabled}
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.piece.id})}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: piece.id})}
|
||||
handleSuccess={handleSuccess || this.handleSuccess}
|
||||
getFormData={this.getFormData}
|
||||
buttons={
|
||||
<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={!this.state.isUploadReady || this.props.disabled}>
|
||||
{getLangText('Proceed to loan')}
|
||||
</button>
|
||||
}
|
||||
spinner={
|
||||
<div className="modal-footer">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||
</div>
|
||||
}>
|
||||
<div className="ascribe-form-header">
|
||||
<h3>
|
||||
{getLangText('Provide supporting materials')}
|
||||
</h3>
|
||||
</div>
|
||||
buttons={buttons}
|
||||
spinner={spinner}>
|
||||
{heading}
|
||||
<Property
|
||||
name='artist_bio'
|
||||
label={getLangText('Artist Biography')}
|
||||
editable={!this.props.disabled}>
|
||||
label={getLangText('Artist Biography')}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={!this.props.disabled}
|
||||
placeholder={getLangText('Enter the artist\'s biography...')}
|
||||
required="required"/>
|
||||
defaultValue={piece.extra_data.artist_bio}
|
||||
placeholder={getLangText('Enter the artist\'s biography...')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='conceptual_overview'
|
||||
label={getLangText('Conceptual Overview')}
|
||||
editable={!this.props.disabled}>
|
||||
label={getLangText('Conceptual Overview')}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={!this.props.disabled}
|
||||
placeholder={getLangText('Enter a conceptual overview...')}
|
||||
required="required"/>
|
||||
defaultValue={piece.extra_data.conceptual_overview}
|
||||
placeholder={getLangText('Enter a conceptual overview...')}/>
|
||||
</Property>
|
||||
<FurtherDetailsFileuploader
|
||||
uploadStarted={this.uploadStarted}
|
||||
submitFile={this.submitFile}
|
||||
setIsUploadReady={this.setIsUploadReady}
|
||||
isReadyForFormSubmission={formSubmissionValidation.fileOptional}
|
||||
editable={!this.props.disabled}
|
||||
pieceId={this.props.piece.id}
|
||||
otherData={this.props.piece.other_data}
|
||||
pieceId={piece.id}
|
||||
otherData={piece.other_data}
|
||||
multiple={true}/>
|
||||
</Form>
|
||||
);
|
||||
|
@ -10,9 +10,6 @@ import Row from 'react-bootstrap/lib/Row';
|
||||
|
||||
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
|
||||
|
||||
import Property from '../../../../ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../ascribe_forms/input_checkbox';
|
||||
|
||||
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
|
||||
import WhitelabelStore from '../../../../../stores/whitelabel_store';
|
||||
|
||||
@ -136,7 +133,7 @@ let CylandRegisterPiece = React.createClass({
|
||||
this.transitionTo('piece', {pieceId: this.state.piece.id});
|
||||
},
|
||||
|
||||
// We need to increase the step to lock the forms that are already filed out
|
||||
// We need to increase the step to lock the forms that are already filled out
|
||||
incrementStep() {
|
||||
// also increase step
|
||||
let newStep = this.state.step + 1;
|
||||
|
@ -63,16 +63,20 @@ let IkonotvAccordionListItem = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submit">
|
||||
<IkonotvSubmitButton
|
||||
className="btn-xs pull-right"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.props.content}/>
|
||||
aclObject={this.state.currentUser.acl}
|
||||
aclName="acl_wallet_submit">
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_wallet_submit">
|
||||
<IkonotvSubmitButton
|
||||
className="btn-xs pull-right"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.props.content}/>
|
||||
</AclProxy>
|
||||
</AclProxy>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submitted">
|
||||
aclName="acl_wallet_submitted">
|
||||
<button
|
||||
disabled
|
||||
className="btn btn-default btn-xs pull-right">
|
||||
@ -82,7 +86,7 @@ let IkonotvAccordionListItem = React.createClass({
|
||||
</AclProxy>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_accepted">
|
||||
aclName="acl_wallet_accepted">
|
||||
<button
|
||||
disabled
|
||||
className="btn btn-default btn-xs pull-right">
|
||||
|
@ -1,17 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
|
||||
|
||||
import LoanForm from '../../../../../ascribe_forms/form_loan';
|
||||
|
||||
import Property from '../../../../../ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
|
||||
@ -22,37 +13,36 @@ let IkonotvSubmitButton = React.createClass({
|
||||
piece: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getSubmitButton() {
|
||||
return (
|
||||
<button
|
||||
className={classNames('btn', 'btn-default', this.props.className)}>
|
||||
{getLangText('Loan to IkonoTV')}
|
||||
</button>
|
||||
);
|
||||
getDefaultProps() {
|
||||
return {
|
||||
className: 'btn-xs'
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
let piece = this.props.piece;
|
||||
let startFrom = 1;
|
||||
|
||||
let today = new Moment();
|
||||
let enddate = new Moment();
|
||||
enddate.add(1, 'years');
|
||||
// In the Ikonotv loan page a user has to complete two steps.
|
||||
// Since every one of those steps is atomic a user should always be able to continue
|
||||
// where he left of.
|
||||
// This is why we start the process form slide 1/2 if the user has already finished
|
||||
// it in another session.
|
||||
if(piece && piece.extra_data && Object.keys(piece.extra_data).length > 0) {
|
||||
startFrom = 1;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
trigger={this.getSubmitButton()}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
title={getLangText('Loan to IkonoTV archive')}>
|
||||
<LoanForm
|
||||
id={{piece_id: this.props.piece.id}}
|
||||
url={ApiUrls.ownership_loans_pieces}
|
||||
email="submissions@ikono.org"
|
||||
startdate={today}
|
||||
enddate={enddate}
|
||||
gallery="IkonoTV archive"
|
||||
showPersonalMessage={false}
|
||||
handleSuccess={this.props.handleSuccess} />
|
||||
</ModalWrapper>
|
||||
|
||||
<ButtonLink
|
||||
to="register_piece"
|
||||
query={{
|
||||
'slide_num': 0,
|
||||
'start_from': startFrom,
|
||||
'piece_id': piece.id
|
||||
}}
|
||||
className={classNames('ascribe-margin-1px', this.props.className)}>
|
||||
{getLangText('Loan to IkonoTV')}
|
||||
</ButtonLink>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -5,53 +5,45 @@ import React from 'react';
|
||||
import PieceActions from '../../../../../../actions/piece_actions';
|
||||
import PieceStore from '../../../../../../stores/piece_store';
|
||||
|
||||
import PieceListActions from '../../../../../../actions/piece_list_actions';
|
||||
import PieceListStore from '../../../../../../stores/piece_list_store';
|
||||
|
||||
import UserStore from '../../../../../../stores/user_store';
|
||||
|
||||
import Piece from '../../../../../../components/ascribe_detail/piece';
|
||||
|
||||
import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions';
|
||||
import AclButtonList from '../../../../../ascribe_buttons/acl_button_list';
|
||||
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
|
||||
|
||||
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
|
||||
|
||||
import HistoryIterator from '../../../../../ascribe_detail/history_iterator';
|
||||
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import DetailProperty from '../../../../../ascribe_detail/detail_property';
|
||||
import IkonotvArtistDetailsForm from '../ascribe_forms/ikonotv_artist_details_form';
|
||||
import IkonotvArtworkDetailsForm from '../ascribe_forms/ikonotv_artwork_details_form';
|
||||
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import AclProxy from '../../../../../acl_proxy';
|
||||
import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
|
||||
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||
|
||||
|
||||
let IkonotvPieceContainer = React.createClass({
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
PieceStore.getState(),
|
||||
UserStore.getState(),
|
||||
PieceListStore.getState()
|
||||
UserStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PieceStore.listen(this.onChange);
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
UserStore.listen(this.onChange);
|
||||
PieceListStore.listen(this.onChange);
|
||||
|
||||
// Every time we're leaving the piece detail page,
|
||||
// just reset the piece that is saved in the piece store
|
||||
// as it will otherwise display wrong/old data once the user loads
|
||||
// the piece detail a second time
|
||||
PieceActions.updatePiece({});
|
||||
|
||||
this.loadPiece();
|
||||
},
|
||||
|
||||
// We need this for when the user clicks on a notification while being in another piece view
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.props.params.pieceId !== nextProps.params.pieceId) {
|
||||
PieceActions.updatePiece({});
|
||||
@ -60,14 +52,8 @@ let IkonotvPieceContainer = React.createClass({
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
// Every time we're leaving the piece detail page,
|
||||
// just reset the piece that is saved in the piece store
|
||||
// as it will otherwise display wrong/old data once the user loads
|
||||
// the piece detail a second time
|
||||
PieceActions.updatePiece({});
|
||||
PieceStore.unlisten(this.onChange);
|
||||
UserStore.unlisten(this.onChange);
|
||||
PieceListStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
@ -78,91 +64,46 @@ let IkonotvPieceContainer = React.createClass({
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
},
|
||||
|
||||
handleSubmitSuccess(response) {
|
||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||
render() {
|
||||
let furtherDetails = (
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Further Details')}
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
<span>{getLangText('This piece has been loaned before we started to collect further details.')}</span>
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
|
||||
this.loadPiece();
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
getActions(){
|
||||
if (this.state.piece &&
|
||||
this.state.piece.notifications &&
|
||||
this.state.piece.notifications.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
notifications={this.state.piece.notifications}/>);
|
||||
}
|
||||
else {
|
||||
|
||||
//We need to disable the normal acl_loan because we're inserting a custom acl_loan button
|
||||
let availableAcls;
|
||||
|
||||
if(this.state.piece && this.state.piece.acl && typeof this.state.piece.acl.acl_loan !== 'undefined') {
|
||||
// make a copy to not have side effects
|
||||
availableAcls = mergeOptions({}, this.state.piece.acl);
|
||||
availableAcls.acl_loan = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<AclButtonList
|
||||
className="text-center ascribe-button-list"
|
||||
availableAcls={availableAcls}
|
||||
editions={this.state.piece}
|
||||
handleSuccess={this.loadPiece}>
|
||||
<AclProxy
|
||||
aclObject={availableAcls}
|
||||
aclName="acl_submit">
|
||||
<IkonotvSubmitButton
|
||||
className="btn-sm"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</AclProxy>
|
||||
<DeleteButton
|
||||
handleSuccess={this.handleDeleteSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</AclButtonList>
|
||||
if(this.state.piece.extra_data && Object.keys(this.state.piece.extra_data).length > 0 && this.state.piece.acl) {
|
||||
furtherDetails = (
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Further Details')}
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
<IkonotvArtistDetailsForm
|
||||
piece={this.state.piece}
|
||||
isInline={true}
|
||||
disabled={!this.state.piece.acl.acl_edit} />
|
||||
<IkonotvArtworkDetailsForm
|
||||
piece={this.state.piece}
|
||||
isInline={true}
|
||||
disabled={!this.state.piece.acl.acl_edit} />
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
<WalletPieceContainer
|
||||
piece={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
loadPiece={this.loadPiece}
|
||||
header={
|
||||
<div className="ascribe-detail-header">
|
||||
<hr style={{marginTop: 0}}/>
|
||||
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
||||
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
subheader={
|
||||
<div className="ascribe-detail-header">
|
||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } />
|
||||
<DetailProperty label={getLangText('ID')} value={ this.state.piece.bitcoin_id } ellipsis={true} />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
buttons={this.getActions()}>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Loan History')}
|
||||
show={this.state.piece.loan_history && this.state.piece.loan_history.length > 0}>
|
||||
<HistoryIterator
|
||||
history={this.state.piece.loan_history} />
|
||||
</CollapsibleParagraph>
|
||||
</Piece>
|
||||
submitButtonType={IkonotvSubmitButton}>
|
||||
{furtherDetails}
|
||||
</WalletPieceContainer>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="fullpage-spinner">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
|
@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Form from '../../../../../ascribe_forms/form';
|
||||
import Property from '../../../../../ascribe_forms/property';
|
||||
|
||||
import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
import requests from '../../../../../../utils/requests';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
|
||||
|
||||
let IkonotvArtistDetailsForm = React.createClass({
|
||||
propTypes: {
|
||||
handleSuccess: React.PropTypes.func,
|
||||
piece: React.PropTypes.object.isRequired,
|
||||
|
||||
disabled: React.PropTypes.bool,
|
||||
|
||||
isInline: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
isInline: false
|
||||
};
|
||||
},
|
||||
|
||||
getFormData() {
|
||||
let extradata = {};
|
||||
let formRefs = this.refs.form.refs;
|
||||
|
||||
// Put additional fields in extra data object
|
||||
Object
|
||||
.keys(formRefs)
|
||||
.forEach((fieldName) => {
|
||||
extradata[fieldName] = formRefs[fieldName].state.value;
|
||||
});
|
||||
|
||||
return {
|
||||
extradata: extradata,
|
||||
piece_id: this.props.piece.id
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
handleSuccess() {
|
||||
let notification = new GlobalNotificationModel('Artist details successfully updated', 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
render() {
|
||||
let buttons, spinner, heading;
|
||||
let { isInline, handleSuccess } = this.props;
|
||||
|
||||
|
||||
if(!isInline) {
|
||||
buttons = (
|
||||
<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={this.props.disabled}>
|
||||
{getLangText('Proceed to loan')}
|
||||
</button>
|
||||
);
|
||||
|
||||
spinner = (
|
||||
<div className="modal-footer">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||
</div>
|
||||
);
|
||||
|
||||
heading = (
|
||||
<div className="ascribe-form-header">
|
||||
<h3>
|
||||
{getLangText('Artist Details')}
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.piece && this.props.piece.id && this.props.piece.extra_data) {
|
||||
return (
|
||||
<Form
|
||||
disabled={this.props.disabled}
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.piece.id})}
|
||||
handleSuccess={handleSuccess || this.handleSuccess}
|
||||
getFormData={this.getFormData}
|
||||
buttons={buttons}
|
||||
spinner={spinner}>
|
||||
{heading}
|
||||
<Property
|
||||
name='artist_website'
|
||||
label={getLangText('Artist Website')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.artist_website}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.artist_website}
|
||||
placeholder={getLangText('The artist\'s website if present...')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='gallery_website'
|
||||
label={getLangText('Website of related Gallery, Museum, etc.')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.gallery_website}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.gallery_website}
|
||||
placeholder={getLangText('The website of any related Gallery or Museum')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='additional_websites'
|
||||
label={getLangText('Additional Websites/Publications/Museums/Galleries')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.additional_websites}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.additional_websites}
|
||||
placeholder={getLangText('Enter additional Websites/Publications if any')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='conceptual_overview'
|
||||
label={getLangText('Short text about the Artist')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.conceptual_overview}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.conceptual_overview}
|
||||
placeholder={getLangText('Enter a short bio about the Artist')}
|
||||
/>
|
||||
</Property>
|
||||
</Form>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="ascribe-loading-position">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvArtistDetailsForm;
|
@ -0,0 +1,167 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Form from '../../../../../ascribe_forms/form';
|
||||
import Property from '../../../../../ascribe_forms/property';
|
||||
|
||||
import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
import requests from '../../../../../../utils/requests';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
|
||||
|
||||
let IkonotvArtworkDetailsForm = React.createClass({
|
||||
propTypes: {
|
||||
handleSuccess: React.PropTypes.func,
|
||||
piece: React.PropTypes.object.isRequired,
|
||||
|
||||
disabled: React.PropTypes.bool,
|
||||
|
||||
isInline: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
isInline: false
|
||||
};
|
||||
},
|
||||
|
||||
getFormData() {
|
||||
let extradata = {};
|
||||
let formRefs = this.refs.form.refs;
|
||||
|
||||
// Put additional fields in extra data object
|
||||
Object
|
||||
.keys(formRefs)
|
||||
.forEach((fieldName) => {
|
||||
extradata[fieldName] = formRefs[fieldName].state.value;
|
||||
});
|
||||
|
||||
return {
|
||||
extradata: extradata,
|
||||
piece_id: this.props.piece.id
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
handleSuccess() {
|
||||
let notification = new GlobalNotificationModel('Artwork details successfully updated', 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
render() {
|
||||
let buttons, spinner, heading;
|
||||
let { isInline, handleSuccess } = this.props;
|
||||
|
||||
if(!isInline) {
|
||||
buttons = (
|
||||
<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={this.props.disabled}>
|
||||
{getLangText('Proceed to artist details')}
|
||||
</button>
|
||||
);
|
||||
|
||||
spinner = (
|
||||
<div className="modal-footer">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||
</div>
|
||||
);
|
||||
|
||||
heading = (
|
||||
<div className="ascribe-form-header">
|
||||
<h3>
|
||||
{getLangText('Artwork Details')}
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.piece && this.props.piece.id && this.props.piece.extra_data) {
|
||||
return (
|
||||
<Form
|
||||
disabled={this.props.disabled}
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.piece.id})}
|
||||
handleSuccess={handleSuccess || this.handleSuccess}
|
||||
getFormData={this.getFormData}
|
||||
buttons={buttons}
|
||||
spinner={spinner}>
|
||||
{heading}
|
||||
<Property
|
||||
name='medium'
|
||||
label={getLangText('Medium')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.medium}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.medium}
|
||||
placeholder={getLangText('The medium of the file (i.e. photo, video, other, ...)')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='size_duration'
|
||||
label={getLangText('Size/Duration')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.size_duration}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.size_duration}
|
||||
placeholder={getLangText('Size in centimeters. Duration in minutes.')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='copyright'
|
||||
label={getLangText('Copyright')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.copyright}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.copyright}
|
||||
placeholder={getLangText('Which copyright is attached to this work?')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='courtesy_of'
|
||||
label={getLangText('Courtesy of')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.courtesy_of}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.courtesy_of}
|
||||
placeholder={getLangText('The current owner of the artwork')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='copyright_of_photography'
|
||||
label={getLangText('Copyright of Photography')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.copyright_of_photography}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.copyright_of_photography}
|
||||
placeholder={getLangText('Who should be attributed for the photography?')}/>
|
||||
</Property>
|
||||
<Property
|
||||
name='additional_details'
|
||||
label={getLangText('Additional Details about the artwork')}
|
||||
hidden={this.props.disabled && !this.props.piece.extra_data.additional_details}>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
defaultValue={this.props.piece.extra_data.additional_details}
|
||||
placeholder={getLangText('Insert artwork overview')}/>
|
||||
</Property>
|
||||
</Form>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="ascribe-loading-position">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvArtworkDetailsForm;
|
@ -6,20 +6,26 @@ import Router from 'react-router';
|
||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||
import Button from 'react-bootstrap/lib/Button';
|
||||
|
||||
import Form from '../../../../ascribe_forms/form';
|
||||
import Property from '../../../../ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../ascribe_forms/input_checkbox';
|
||||
|
||||
import NotificationActions from '../../../../../actions/notification_actions';
|
||||
import NotificationStore from '../../../../../stores/notification_store';
|
||||
|
||||
import UserActions from '../../../../../actions/user_actions';
|
||||
import UserStore from '../../../../../stores/user_store';
|
||||
|
||||
import OwnershipFetcher from '../../../../../fetchers/ownership_fetcher';
|
||||
|
||||
import WhitelabelStore from '../../../../../stores/whitelabel_store';
|
||||
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||
|
||||
import apiUrls from '../../../../../constants/api_urls';
|
||||
import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association';
|
||||
|
||||
import Property from '../../../../ascribe_forms/property';
|
||||
|
||||
import AppConstants from '../../../../../constants/application_constants';
|
||||
|
||||
import requests from '../../../../../utils/requests';
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
|
||||
@ -32,13 +38,17 @@ let IkonotvContractNotifications = React.createClass({
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
NotificationStore.getState(),
|
||||
UserStore.getState(),
|
||||
WhitelabelStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
NotificationStore.listen(this.onChange);
|
||||
UserStore.listen(this.onChange);
|
||||
UserActions.fetchCurrentUser();
|
||||
WhitelabelStore.listen(this.onChange);
|
||||
WhitelabelActions.fetchWhitelabel();
|
||||
if (this.state.contractAgreementListNotifications === null){
|
||||
NotificationActions.fetchContractAgreementListNotifications();
|
||||
}
|
||||
@ -59,16 +69,11 @@ let IkonotvContractNotifications = React.createClass({
|
||||
if (blob.mime === 'pdf') {
|
||||
return (
|
||||
<div className='notification-contract-pdf'>
|
||||
<embed src={blob.url_safe} alt="pdf"
|
||||
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
|
||||
<div className='notification-contract-pdf-download'>
|
||||
<a href={blob.url_safe} target="_blank">
|
||||
<Glyphicon glyph='download-alt'/>
|
||||
<span style={{padding: '0.3em'}}>
|
||||
Download PDF version
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<embed
|
||||
height
|
||||
src={blob.url_safe}
|
||||
alt="pdf"
|
||||
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -87,87 +92,98 @@ let IkonotvContractNotifications = React.createClass({
|
||||
getAppendix() {
|
||||
let notifications = this.state.contractAgreementListNotifications[0];
|
||||
let appendix = notifications.contract_agreement.appendix;
|
||||
if (appendix) {
|
||||
return (<div>
|
||||
<h1>{getLangText('Appendix')}</h1>
|
||||
<pre>
|
||||
{appendix.default}
|
||||
</pre>
|
||||
</div>
|
||||
if (appendix && appendix.default) {
|
||||
return (
|
||||
<Property
|
||||
name='appendix'
|
||||
label={getLangText('Appendix')}>
|
||||
<pre className="ascribe-pre">{appendix.default}</pre>
|
||||
</Property>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
|
||||
OwnershipFetcher.confirmContractAgreement(contractAgreement).then(
|
||||
() => this.handleConfirmSuccess()
|
||||
);
|
||||
},
|
||||
|
||||
handleConfirmSuccess() {
|
||||
let notification = new GlobalNotificationModel(getLangText('You have accepted the conditions'), 'success', 10000);
|
||||
let notification = new GlobalNotificationModel(getLangText('You have accepted the conditions'), 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
},
|
||||
|
||||
handleDeny() {
|
||||
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
|
||||
requests.put(apiUrls.ownership_contract_agreements_deny, {contract_agreement_id: contractAgreement.id}).then(
|
||||
OwnershipFetcher.denyContractAgreement(contractAgreement).then(
|
||||
() => this.handleDenySuccess()
|
||||
);
|
||||
},
|
||||
|
||||
handleDenySuccess() {
|
||||
let notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 10000);
|
||||
let notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
},
|
||||
|
||||
render() {
|
||||
getCopyrightAssociationForm(){
|
||||
let currentUser = this.state.currentUser;
|
||||
|
||||
if (currentUser && currentUser.profile && !currentUser.profile.copyright_association) {
|
||||
return (
|
||||
<div className='notification-contract-footer'>
|
||||
<h1>{getLangText('Are you a member of any copyright societies?')}</h1>
|
||||
|
||||
<p>
|
||||
{AppConstants.copyrightAssociations.join(', ')}
|
||||
</p>
|
||||
<CopyrightAssociationForm currentUser={currentUser}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.state.contractAgreementListNotifications &&
|
||||
this.state.contractAgreementListNotifications.length > 0) {
|
||||
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
|
||||
|
||||
let notifications = this.state.contractAgreementListNotifications[0];
|
||||
let blob = notifications.contract_agreement.contract.blob;
|
||||
|
||||
return (
|
||||
<div className='container'>
|
||||
<div className='notification-contract-wrapper'>
|
||||
<div className='notification-contract-logo'>
|
||||
<img src={this.state.whitelabel.logo}/>
|
||||
<div className='notification-contract-header'>
|
||||
{getLangText('Production Contract')}
|
||||
{getLangText('Contract')}
|
||||
</div>
|
||||
</div>
|
||||
{this.getContract()}
|
||||
|
||||
<div className='notification-contract-footer'>
|
||||
{this.getAppendix}
|
||||
<h1>{getLangText('Are you a member of any copyright societies?')}</h1>
|
||||
<p>
|
||||
ARS, DACS, Bildkunst, Pictoright, SODRAC, Copyright Agency/Viscopy, SAVA, Bildrecht GmbH,
|
||||
SABAM, AUTVIS, CREAIMAGEN, SONECA, Copydan, EAU, Kuvasto, GCA, HUNGART, IVARO, SIAE, JASPAR-SPDA,
|
||||
AKKA/LAA, LATGA-A, SOMAAP, ARTEGESTION, CARIER, BONO, APSAV, SPA, GESTOR, VISaRTA, RAO, LITA,
|
||||
DALRO, VeGaP, BUS, ProLitteris, AGADU, AUTORARTE, BUBEDRA, BBDA, BCDA, BURIDA, ADAVIS, BSDA
|
||||
{this.getAppendix()}
|
||||
<div className='notification-contract-pdf-download'>
|
||||
<a href={blob.url_safe} target="_blank">
|
||||
<Glyphicon glyph='download-alt'/>
|
||||
<span style={{padding: '0.3em'}}>
|
||||
Download PDF version
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
{this.getCopyrightAssociationForm()}
|
||||
<p style={{marginTop: '1em'}}>
|
||||
<Button type="submit" onClick={this.handleConfirm}>
|
||||
{getLangText('I agree with the conditions')}
|
||||
</Button>
|
||||
<Button bsStyle="danger" className="btn-delete" bsSize="medium" onClick={this.handleDeny}>
|
||||
{getLangText('I disagree')}
|
||||
</Button>
|
||||
</p>
|
||||
<Form
|
||||
ref='form'
|
||||
url={requests.prepareUrl(apiUrls.ownership_contract_agreements_confirm, {contract_agreement_id: contractAgreement.id})}
|
||||
handleSuccess={this.handleConfirmSuccess}
|
||||
method='put'
|
||||
buttons={
|
||||
<p style={{marginTop: '1em'}}>
|
||||
<Button type="submit">{getLangText('I agree with the conditions')}</Button>
|
||||
<Button bsStyle="danger" className="btn-delete" bsSize="medium" onClick={this.handleDeny}>
|
||||
{getLangText('I disagree')}
|
||||
</Button>
|
||||
</p>
|
||||
}>
|
||||
<Property
|
||||
name="terms"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox>
|
||||
<span>
|
||||
{' ' + getLangText('Yes') }
|
||||
</span>
|
||||
</InputCheckbox>
|
||||
</Property>
|
||||
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
||||
|
||||
import UserActions from '../../../../../actions/user_actions';
|
||||
import UserStore from '../../../../../stores/user_store';
|
||||
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
@ -12,6 +12,8 @@ import { getLangText } from '../../../../../utils/lang_utils';
|
||||
|
||||
let IkonotvLanding = React.createClass({
|
||||
|
||||
mixins: [Router.Navigation, Router.State],
|
||||
|
||||
getInitialState() {
|
||||
return UserStore.getState();
|
||||
},
|
||||
@ -29,19 +31,19 @@ let IkonotvLanding = React.createClass({
|
||||
},
|
||||
|
||||
getEnterButton() {
|
||||
let redirect = 'login';
|
||||
|
||||
if(this.state.currentUser && this.state.currentUser.email) {
|
||||
return (
|
||||
<ButtonLink to="pieces">
|
||||
{getLangText('ENTER TO START')}
|
||||
</ButtonLink>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ButtonLink to="signup">
|
||||
{getLangText('ENTER TO START')}
|
||||
</ButtonLink>
|
||||
);
|
||||
redirect = 'pieces';
|
||||
}
|
||||
else if (this.getQuery() && this.getQuery().redirect) {
|
||||
redirect = this.getQuery().redirect;
|
||||
}
|
||||
return (
|
||||
<ButtonLink to={redirect} query={this.getQuery()}>
|
||||
{getLangText('ENTER TO START')}
|
||||
</ButtonLink>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
@ -55,7 +57,7 @@ let IkonotvLanding = React.createClass({
|
||||
<div className="content">
|
||||
</div>
|
||||
</div>
|
||||
<h1>& SHARE</h1>
|
||||
<h1>& SHARE</h1>
|
||||
</div>
|
||||
<h2>Welcome to the ikonoTV<br />Registration Page</h2>
|
||||
</header>
|
||||
@ -74,7 +76,7 @@ let IkonotvLanding = React.createClass({
|
||||
NEW SUBSCRIPTION SERVICE
|
||||
</h1>
|
||||
<p>
|
||||
IkonoTV has developed an app that provides playlists on demand—soon to be available on all online devices and SmartTVs. The app is a paid service; in view of the interest in distributing this service in public spaces (hospitals, airports, hotels, etc.), we can now offer the possibility of a share in revenue to compensate for the artist’s work.
|
||||
IkonoTV has developed an app that provides playlists on demand—soon to be available on all online devices and SmartTVs. We can now offer the possibility of a share in revenue to compensate for the artist’s work.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
@ -82,7 +84,7 @@ let IkonotvLanding = React.createClass({
|
||||
THE RAPID GROWTH OF IkonoTV
|
||||
</h1>
|
||||
<p>
|
||||
In October 2014, our first app was installed on Amazon Fire TV. During the first month it was downloaded 200 times, and jumped to 5,000 by the second month. Today, we’re well over the 175,000 mark, making us the number one app in our category in the US, Canada, UK and Germany.
|
||||
In October 2014, our first app was installed on Amazon Fire TV. During the first month it was downloaded 200 times, and jumped to 5,000 by the second month. Today, we’re well over the 285,000 mark, making us the number one app in our category in the US, Canada, UK and Germany.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
@ -95,7 +97,7 @@ let IkonotvLanding = React.createClass({
|
||||
</section>
|
||||
<footer>
|
||||
<p>Elizabeth Markevitch</p>
|
||||
<p>Founder & CEO Markevitch Media GmbH</p>
|
||||
<p>Founder & CEO Markevitch Media GmbH</p>
|
||||
{this.getEnterButton()}
|
||||
</footer>
|
||||
</article>
|
||||
|
@ -0,0 +1,271 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Moment from 'moment';
|
||||
import Router from 'react-router';
|
||||
|
||||
import Col from 'react-bootstrap/lib/Col';
|
||||
import Row from 'react-bootstrap/lib/Row';
|
||||
|
||||
import PieceListStore from '../../../../../stores/piece_list_store';
|
||||
import PieceListActions from '../../../../../actions/piece_list_actions';
|
||||
|
||||
import UserStore from '../../../../../stores/user_store';
|
||||
import UserActions from '../../../../../actions/user_actions';
|
||||
|
||||
import PieceStore from '../../../../../stores/piece_store';
|
||||
import PieceActions from '../../../../../actions/piece_actions';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||
|
||||
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
|
||||
import LoanForm from '../../../../ascribe_forms/form_loan';
|
||||
|
||||
import IkonotvArtistDetailsForm from './ascribe_forms/ikonotv_artist_details_form';
|
||||
import IkonotvArtworkDetailsForm from './ascribe_forms/ikonotv_artwork_details_form';
|
||||
|
||||
import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
|
||||
|
||||
import ApiUrls from '../../../../../constants/api_urls';
|
||||
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
|
||||
let IkonotvRegisterPiece = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
handleSuccess: React.PropTypes.func,
|
||||
piece: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
mixins: [Router.Navigation, Router.State],
|
||||
|
||||
getInitialState(){
|
||||
return mergeOptions(
|
||||
UserStore.getState(),
|
||||
PieceListStore.getState(),
|
||||
PieceStore.getState(),
|
||||
{
|
||||
step: 0
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PieceListStore.listen(this.onChange);
|
||||
UserStore.listen(this.onChange);
|
||||
PieceStore.listen(this.onChange);
|
||||
UserActions.fetchCurrentUser();
|
||||
|
||||
// Before we load the new piece, we reset the piece store to delete old data that we do
|
||||
// not want to display to the user.
|
||||
PieceActions.updatePiece({});
|
||||
|
||||
let queryParams = this.getQuery();
|
||||
|
||||
// Since every step of this register process is atomic,
|
||||
// we may need to enter the process at step 1 or 2.
|
||||
// If this is the case, we'll need the piece number to complete submission.
|
||||
// It is encoded in the URL as a queryParam and we're checking for it here.
|
||||
//
|
||||
// We're using 'in' here as we want to know if 'piece_id' is present in the url,
|
||||
// we don't care about the value.
|
||||
if (queryParams && 'piece_id' in queryParams) {
|
||||
PieceActions.fetchOne(queryParams.piece_id);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
PieceListStore.unlisten(this.onChange);
|
||||
UserStore.unlisten(this.onChange);
|
||||
PieceStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
|
||||
if(this.state.currentUser && this.state.currentUser.email) {
|
||||
// we should also make the fineuploader component editable again
|
||||
this.setState({
|
||||
isFineUploaderActive: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
handleRegisterSuccess(response){
|
||||
|
||||
this.refreshPieceList();
|
||||
|
||||
// also start loading the piece for the next step
|
||||
if(response && response.piece) {
|
||||
PieceActions.updatePiece(response.piece);
|
||||
}
|
||||
if (!this.canSubmit()) {
|
||||
this.transitionTo('pieces');
|
||||
}
|
||||
else {
|
||||
this.incrementStep();
|
||||
this.refs.slidesContainer.nextSlide();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
handleAdditionalDataSuccess() {
|
||||
|
||||
// We need to refetch the piece again after submitting the additional data
|
||||
// since we want it's otherData to be displayed when the user choses to click
|
||||
// on the browsers back button.
|
||||
PieceActions.fetchOne(this.state.piece.id);
|
||||
|
||||
this.refreshPieceList();
|
||||
|
||||
this.incrementStep();
|
||||
|
||||
this.refs.slidesContainer.nextSlide();
|
||||
},
|
||||
|
||||
handleLoanSuccess(response) {
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
this.refreshPieceList();
|
||||
|
||||
PieceActions.fetchOne(this.state.piece.id);
|
||||
this.transitionTo('piece', {pieceId: this.state.piece.id});
|
||||
},
|
||||
|
||||
// We need to increase the step to lock the forms that are already filled out
|
||||
incrementStep() {
|
||||
// also increase step
|
||||
let newStep = this.state.step + 1;
|
||||
this.setState({
|
||||
step: newStep
|
||||
});
|
||||
},
|
||||
|
||||
refreshPieceList() {
|
||||
PieceListActions.fetchPieceList(
|
||||
this.state.page,
|
||||
this.state.pageSize,
|
||||
this.state.searchTerm,
|
||||
this.state.orderBy,
|
||||
this.state.orderAsc,
|
||||
this.state.filterBy
|
||||
);
|
||||
},
|
||||
|
||||
changeSlide() {
|
||||
// only transition to the login store, if user is not logged in
|
||||
// ergo the currentUser object is not properly defined
|
||||
if(this.state.currentUser && !this.state.currentUser.email) {
|
||||
this.onLoggedOut();
|
||||
}
|
||||
},
|
||||
|
||||
// basically redirects to the second slide (index: 1), when the user is not logged in
|
||||
onLoggedOut() {
|
||||
this.transitionTo('login');
|
||||
},
|
||||
|
||||
canSubmit() {
|
||||
let currentUser = this.state.currentUser;
|
||||
return currentUser && currentUser.acl && currentUser.acl.acl_wallet_submit;
|
||||
},
|
||||
|
||||
getSlideArtistDetails() {
|
||||
if (this.canSubmit()) {
|
||||
return (
|
||||
<div data-slide-title={getLangText('Artist details')}>
|
||||
<Row className="no-margin">
|
||||
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
||||
<IkonotvArtistDetailsForm
|
||||
handleSuccess={this.handleAdditionalDataSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getSlideArtworkDetails() {
|
||||
if (this.canSubmit()) {
|
||||
return (
|
||||
<div data-slide-title={getLangText('Artwork details')}>
|
||||
<Row className="no-margin">
|
||||
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
||||
<IkonotvArtworkDetailsForm
|
||||
handleSuccess={this.handleAdditionalDataSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getSlideLoan() {
|
||||
if (this.canSubmit()) {
|
||||
let today = new Moment();
|
||||
let enddate = new Moment();
|
||||
enddate.add(2, 'years');
|
||||
return (
|
||||
<div data-slide-title={getLangText('Loan')}>
|
||||
<Row className="no-margin">
|
||||
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
||||
<LoanForm
|
||||
loanHeading={getLangText('Loan to IkonoTV archive')}
|
||||
id={{piece_id: this.state.piece.id}}
|
||||
url={ApiUrls.ownership_loans_pieces}
|
||||
email="submissions@ikono.org"
|
||||
startdate={today}
|
||||
enddate={enddate}
|
||||
showStartDate={false}
|
||||
showEndDate={false}
|
||||
gallery="IkonoTV archive"
|
||||
showPersonalMessage={false}
|
||||
createPublicContractAgreement={false}
|
||||
handleSuccess={this.handleLoanSuccess}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SlidesContainer
|
||||
ref="slidesContainer"
|
||||
forwardProcess={true}
|
||||
glyphiconClassNames={{
|
||||
pending: 'glyphicon glyphicon-chevron-right',
|
||||
completed: 'glyphicon glyphicon-lock'
|
||||
}}>
|
||||
<div data-slide-title={getLangText('Register work')}>
|
||||
<Row className="no-margin">
|
||||
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
||||
<RegisterPieceForm
|
||||
disabled={this.state.step > 0}
|
||||
enableLocalHashing={false}
|
||||
headerMessage={getLangText('Register work')}
|
||||
submitMessage={getLangText('Register')}
|
||||
isFineUploaderActive={this.state.isFineUploaderActive}
|
||||
handleSuccess={this.handleRegisterSuccess}
|
||||
onLoggedOut={this.onLoggedOut} />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
{this.getSlideArtworkDetails()}
|
||||
{this.getSlideArtistDetails()}
|
||||
{this.getSlideLoan()}
|
||||
</SlidesContainer>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvRegisterPiece;
|
@ -23,7 +23,7 @@ let WalletApp = React.createClass({
|
||||
let activeRoutes = this.getRoutes().map(elem => 'route--' + elem.name);
|
||||
|
||||
let header = null;
|
||||
if ((this.isActive('landing') || this.isActive('login') || this.isActive('signup'))
|
||||
if ((this.isActive('landing') || this.isActive('login') || this.isActive('signup') || this.isActive('contract_notifications'))
|
||||
&& (['ikonotv', 'cyland']).indexOf(subdomain) > -1) {
|
||||
header = (
|
||||
<div className="hero"/>);
|
||||
|
@ -22,6 +22,7 @@ import CylandPieceList from './components/cyland/cyland_piece_list';
|
||||
import IkonotvLanding from './components/ikonotv/ikonotv_landing';
|
||||
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
|
||||
import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan';
|
||||
import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece';
|
||||
import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container';
|
||||
import IkonotvContractNotifications from './components/ikonotv/ikonotv_contract_notifications';
|
||||
|
||||
@ -72,8 +73,8 @@ let ROUTES = {
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="request_loan" path="request_loan" handler={IkonotvRequestLoan} headerTitle="SEND NEW CONTRACT" aclName="acl_send_contract" />
|
||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK"/>
|
||||
<Route name="request_loan" path="request_loan" handler={IkonotvRequestLoan} headerTitle="SEND NEW CONTRACT" aclName="acl_create_contractagreement" />
|
||||
<Route name="register_piece" path="register_piece" handler={IkonotvRegisterPiece} headerTitle="+ NEW WORK"/>
|
||||
<Route name="pieces" path="collection" handler={IkonotvPieceList} headerTitle="COLLECTION"/>
|
||||
<Route name="piece" path="pieces/:pieceId" handler={IkonotvPieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
|
@ -12,7 +12,7 @@ let constants = {
|
||||
'baseUrl': window.BASE_URL,
|
||||
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
|
||||
'acl_loan', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
||||
'acl_withdraw_transfer', 'acl_submit'],
|
||||
'acl_withdraw_transfer', 'acl_wallet_submit'],
|
||||
|
||||
'version': 0.1,
|
||||
'csrftoken': 'csrftoken2',
|
||||
@ -74,7 +74,11 @@ let constants = {
|
||||
'whitelabel': {},
|
||||
'raven': {
|
||||
'url': 'https://0955da3388c64ab29bd32c2a429f9ef4@app.getsentry.com/48351'
|
||||
}
|
||||
},
|
||||
'copyrightAssociations': ['ARS', 'DACS', 'Bildkunst', 'Pictoright', 'SODRAC', 'Copyright Agency/Viscopy', 'SAVA',
|
||||
'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART',
|
||||
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
|
||||
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA']
|
||||
};
|
||||
|
||||
export default constants;
|
||||
|
@ -44,6 +44,14 @@ let OwnershipFetcher = {
|
||||
return requests.get(ApiUrls.ownership_contract_agreements, queryParams);
|
||||
},
|
||||
|
||||
confirmContractAgreement(contractAgreement){
|
||||
return requests.put(ApiUrls.ownership_contract_agreements_confirm, {contract_agreement_id: contractAgreement.id});
|
||||
},
|
||||
|
||||
denyContractAgreement(contractAgreement){
|
||||
return requests.put(ApiUrls.ownership_contract_agreements_deny, {contract_agreement_id: contractAgreement.id});
|
||||
},
|
||||
|
||||
fetchLoanPieceRequestList(){
|
||||
return requests.get(ApiUrls.ownership_loans_pieces_request);
|
||||
},
|
||||
|
@ -78,7 +78,7 @@
|
||||
"react-router": "^0.13.3",
|
||||
"react-router-bootstrap": "~0.16.0",
|
||||
"react-star-rating": "~1.3.2",
|
||||
"react-textarea-autosize": "^2.2.3",
|
||||
"react-textarea-autosize": "^2.5.2",
|
||||
"reactify": "^1.1.0",
|
||||
"shmui": "^0.1.0",
|
||||
"spark-md5": "~1.0.0",
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
.notification-contract-download {
|
||||
|
||||
.notification-contract-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notification-contract-wrapper{
|
||||
text-align: center;
|
||||
.notification-contract-pdf-download {
|
||||
text-align: right;
|
||||
margin: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.notification-contract-logo {
|
||||
@ -29,8 +29,17 @@
|
||||
border: 1px solid #cccccc;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
margin-bottom: 0.4em;
|
||||
margin-bottom: 0;
|
||||
|
||||
.loan-form {
|
||||
height: 45vh;
|
||||
}
|
||||
}
|
||||
|
||||
.loan-form {
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
.notification-contract-pdf-download {
|
||||
text-align: left;
|
||||
margin-left: 1em;
|
||||
@ -39,7 +48,7 @@
|
||||
|
||||
.notification-contract-footer {
|
||||
text-align: left;
|
||||
padding: 1em;
|
||||
padding: 0 0 1em 0;
|
||||
> h1 {
|
||||
margin-top: 0.4em;
|
||||
font-size: 1.4em;
|
||||
|
Loading…
Reference in New Issue
Block a user