{header}
{subheader}
{buttons}
diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js
index 04665024..6c3b82a6 100644
--- a/js/components/ascribe_detail/piece_container.js
+++ b/js/components/ascribe_detail/piece_container.js
@@ -159,7 +159,7 @@ let PieceContainer = React.createClass({
let notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
- this.history.pushState(null, '/collection');
+ this.history.push('/collection');
},
getCreateEditionsDialog() {
@@ -219,7 +219,9 @@ let PieceContainer = React.createClass({
no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
`AclInformation` would show up
*/}
-
+
-
+
{piece.title}
diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js
index d4002e85..91d00f65 100644
--- a/js/components/ascribe_forms/form.js
+++ b/js/components/ascribe_forms/form.js
@@ -178,20 +178,20 @@ let Form = React.createClass({
let formData = this.getFormData();
// sentry shouldn't post the user's password
- if(formData.password) {
+ if (formData.password) {
delete formData.password;
}
console.logGlobal(err, formData);
- if(this.props.isInline) {
+ if (this.props.isInline) {
let notification = new GlobalNotificationModel(getLangText('Something went wrong, please try again later'), 'danger');
GlobalNotificationActions.appendGlobalNotification(notification);
} else {
this.setState({errors: [getLangText('Something went wrong, please try again later')]});
}
-
}
+
this.setState({submitted: false});
},
diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js
index 2f0ebf05..a28d2cff 100644
--- a/js/components/ascribe_forms/form_consign.js
+++ b/js/components/ascribe_forms/form_consign.js
@@ -41,6 +41,14 @@ let ConsignForm = React.createClass({
};
},
+ componentWillReceiveProps(nextProps) {
+ if (this.props.email !== nextProps.email) {
+ this.setState({
+ email: nextProps.email
+ });
+ }
+ },
+
getFormData() {
return this.props.id;
},
diff --git a/js/components/ascribe_forms/form_loan.js b/js/components/ascribe_forms/form_loan.js
index 53a3bb80..861806ae 100644
--- a/js/components/ascribe_forms/form_loan.js
+++ b/js/components/ascribe_forms/form_loan.js
@@ -61,6 +61,14 @@ let LoanForm = React.createClass({
};
},
+ componentWillReceiveProps(nextProps) {
+ if (this.props.email !== nextProps.email) {
+ this.setState({
+ email: nextProps.email
+ });
+ }
+ },
+
onChange(state) {
this.setState(state);
},
diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js
index 83d38b50..596f8a56 100644
--- a/js/components/ascribe_forms/form_register_piece.js
+++ b/js/components/ascribe_forms/form_register_piece.js
@@ -109,6 +109,11 @@ let RegisterPieceForm = React.createClass({
);
},
+ handleThumbnailValidationFailed(thumbnailFile) {
+ // If the validation fails, set the thumbnail as submittable since its optional
+ this.refs.submitButton.setReadyStateForKey('thumbnailKeyReady', true);
+ },
+
isThumbnailDialogExpanded() {
const { enableSeparateThumbnail } = this.props;
const { digitalWorkFile } = this.state;
@@ -194,14 +199,15 @@ let RegisterPieceForm = React.createClass({
url: ApiUrls.blob_thumbnails
}}
handleChangedFile={this.handleChangedThumbnail}
+ onValidationFailed={this.handleThumbnailValidationFailed}
isReadyForFormSubmission={formSubmissionValidation.fileOptional}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'thumbnail'
}}
validation={{
- itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
- sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
+ itemLimit: AppConstants.fineUploader.validation.workThumbnail.itemLimit,
+ sizeLimit: AppConstants.fineUploader.validation.workThumbnail.sizeLimit,
allowedExtensions: ['png', 'jpg', 'jpeg', 'gif']
}}
setIsUploadReady={this.setIsUploadReady('thumbnailKeyReady')}
diff --git a/js/components/ascribe_forms/form_send_contract_agreement.js b/js/components/ascribe_forms/form_send_contract_agreement.js
index 6f5f74d7..043c0361 100644
--- a/js/components/ascribe_forms/form_send_contract_agreement.js
+++ b/js/components/ascribe_forms/form_send_contract_agreement.js
@@ -58,7 +58,7 @@ let SendContractAgreementForm = React.createClass({
notification = new GlobalNotificationModel(notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
- this.history.pushState(null, '/collection');
+ this.history.push('/collection');
},
getFormData() {
diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js
index db5bae05..fa9c72b6 100644
--- a/js/components/ascribe_forms/input_fineuploader.js
+++ b/js/components/ascribe_forms/input_fineuploader.js
@@ -52,6 +52,7 @@ const InputFineUploader = React.createClass({
plural: string
}),
handleChangedFile: func,
+ onValidationFailed: func,
// Provided by `Property`
onChange: React.PropTypes.func
@@ -107,6 +108,7 @@ const InputFineUploader = React.createClass({
isFineUploaderActive,
isReadyForFormSubmission,
keyRoutine,
+ onValidationFailed,
setIsUploadReady,
uploadMethod,
validation,
@@ -127,6 +129,7 @@ const InputFineUploader = React.createClass({
createBlobRoutine={createBlobRoutine}
validation={validation}
submitFile={this.submitFile}
+ onValidationFailed={onValidationFailed}
setIsUploadReady={setIsUploadReady}
isReadyForFormSubmission={isReadyForFormSubmission}
areAssetsDownloadable={areAssetsDownloadable}
diff --git a/js/components/ascribe_modal/modal_wrapper.js b/js/components/ascribe_modal/modal_wrapper.js
index 3f3b4af4..fd77e5ae 100644
--- a/js/components/ascribe_modal/modal_wrapper.js
+++ b/js/components/ascribe_modal/modal_wrapper.js
@@ -46,7 +46,13 @@ let ModalWrapper = React.createClass({
renderChildren() {
return ReactAddons.Children.map(this.props.children, (child) => {
return ReactAddons.addons.cloneWithProps(child, {
- handleSuccess: this.handleSuccess
+ handleSuccess: (response) => {
+ if (typeof child.props.handleSuccess === 'function') {
+ child.props.handleSuccess(response);
+ }
+
+ this.handleSuccess(response);
+ }
});
});
},
diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js
index 60370431..bcc15603 100644
--- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js
+++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js
@@ -39,30 +39,6 @@ let PieceListToolbar = React.createClass({
])
},
- getFilterWidget(){
- if (this.props.filterParams){
- return (
-
- );
- }
- return null;
- },
-
- getOrderWidget(){
- if (this.props.orderParams){
- return (
-
- );
- }
- return null;
- },
-
render() {
const { className, children, searchFor, searchQuery } = this.props;
@@ -75,8 +51,14 @@ let PieceListToolbar = React.createClass({
{children}
- {this.getOrderWidget()}
- {this.getFilterWidget()}
+
+ 0) {
+ if (trueValuesOnly.length) {
return { visibility: 'visible'};
} else {
return { visibility: 'hidden' };
@@ -81,62 +81,66 @@ let PieceListToolbarFilterWidget = React.createClass({
);
- return (
-
- {/* We iterate over filterParams, to receive the label and then for each
- label also iterate over its items, to get all filterable options */}
- {this.props.filterParams.map(({ label, items }, i) => {
- return (
-
-
- {label}:
-
- {items.map((param, j) => {
+ if (this.props.filterParams && this.props.filterParams.length) {
+ return (
+
+ {/* We iterate over filterParams, to receive the label and then for each
+ label also iterate over its items, to get all filterable options */}
+ {this.props.filterParams.map(({ label, items }, i) => {
+ return (
+
+
+ {label}:
+
+ {items.map((param, j) => {
- // As can be seen in the PropTypes, a param can either
- // be a string or an object of the shape:
- //
- // {
- // key: ,
- // label:
- // }
- //
- // This is why we need to distinguish between both here.
- if(typeof param !== 'string') {
- label = param.label;
- param = param.key;
- } else {
- param = param;
- label = param.split('acl_')[1].replace(/_/g, ' ');
- }
+ // As can be seen in the PropTypes, a param can either
+ // be a string or an object of the shape:
+ //
+ // {
+ // key: ,
+ // label:
+ // }
+ //
+ // This is why we need to distinguish between both here.
+ if (typeof param !== 'string') {
+ label = param.label;
+ param = param.key;
+ } else {
+ param = param;
+ label = param.split('acl_')[1].replace(/_/g, ' ');
+ }
- return (
-
-
-
- {getLangText(label)}
-
-
-
-
- );
- })}
-
- );
- })}
-
- );
+ return (
+
+
+
+ {getLangText(label)}
+
+
+
+
+ );
+ })}
+
+ );
+ })}
+
+ );
+ } else {
+ return null;
+ }
}
});
diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
index c38144b0..23c0d355 100644
--- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
+++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
@@ -37,7 +37,7 @@ let PieceListToolbarOrderWidget = React.createClass({
isOrderActive() {
// We're hiding the star in that complicated matter so that,
// the surrounding button is not resized up on appearance
- if(this.props.orderBy.length > 0) {
+ if (this.props.orderBy && this.props.orderBy.length) {
return { visibility: 'visible'};
} else {
return { visibility: 'hidden' };
@@ -51,37 +51,41 @@ let PieceListToolbarOrderWidget = React.createClass({
·
);
- return (
-
-
{/*
The frontend in live is hosted under /app,
Since `Link` is appending that base url, if its defined
@@ -85,32 +87,40 @@ let FileDragAndDropDialog = React.createClass({
);
} else {
if (multipleFiles) {
- return (
-
- {this.getDragDialog(fileClassToUpload.plural)}
-
- {getLangText('choose %s to upload', fileClassToUpload.plural)}
-
+ dialogElement = [
+ this.getDragDialog(fileClassToUpload.plural),
+
+ {getLangText('choose %s to upload', fileClassToUpload.plural)}
- );
+ ];
} else {
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
: getLangText('choose a %s to upload', fileClassToUpload.singular);
- return (
-
- {this.getDragDialog(fileClassToUpload.singular)}
-
- {dialog}
-
+ dialogElement = [
+ this.getDragDialog(fileClassToUpload.singular),
+
+ {dialog}
- );
+ ];
}
}
+
+ return (
+
+
+ {dialogElement}
+
+ {/* Hide the uploader and just show that there's been on files uploaded yet when printing */}
+
+ {getLangText('No files uploaded')}
+
+
+ );
}
}
});
diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js
index 927a5b22..5c757121 100644
--- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js
+++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js
@@ -49,7 +49,7 @@ const FileDragAndDropPreviewImage = React.createClass({
};
let actionSymbol;
-
+
// only if assets are actually downloadable, there should be a download icon if the process is already at
// 100%. If not, no actionSymbol should be displayed
if(progress === 100 && areAssetsDownloadable) {
@@ -68,7 +68,7 @@ const FileDragAndDropPreviewImage = React.createClass({
return (
diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js
index eb211504..877146a5 100644
--- a/js/components/ascribe_uploader/react_s3_fine_uploader.js
+++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js
@@ -50,6 +50,7 @@ const ReactS3FineUploader = React.createClass({
}),
handleChangedFile: func, // is for when a file is dropped or selected
submitFile: func, // is for when a file has been successfully uploaded, TODO: rename to handleSubmitFile
+ onValidationFailed: func,
autoUpload: bool,
debug: bool,
objectProperties: shape({
@@ -523,13 +524,16 @@ const ReactS3FineUploader = React.createClass({
},
isFileValid(file) {
- if(file.size > this.props.validation.sizeLimit) {
+ if (file.size > this.props.validation.sizeLimit) {
+ const fileSizeInMegaBytes = this.props.validation.sizeLimit / 1000000;
- let fileSizeInMegaBytes = this.props.validation.sizeLimit / 1000000;
-
- let notification = new GlobalNotificationModel(getLangText('A file you submitted is bigger than ' + fileSizeInMegaBytes + 'MB.'), 'danger', 5000);
+ const notification = new GlobalNotificationModel(getLangText('A file you submitted is bigger than ' + fileSizeInMegaBytes + 'MB.'), 'danger', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
+ if (typeof this.props.onValidationFailed === 'function') {
+ this.props.onValidationFailed(file);
+ }
+
return false;
} else {
return true;
diff --git a/js/components/footer.js b/js/components/footer.js
index 31145d4b..f2e35dfc 100644
--- a/js/components/footer.js
+++ b/js/components/footer.js
@@ -7,7 +7,7 @@ import { getLangText } from '../utils/lang_utils';
let Footer = React.createClass({
render() {
return (
-
+
api |
diff --git a/js/components/header.js b/js/components/header.js
index 797684ec..c16cba86 100644
--- a/js/components/header.js
+++ b/js/components/header.js
@@ -219,10 +219,11 @@ let Header = React.createClass({
return (
+ className="hidden-print">
+
+
+
);
}
diff --git a/js/components/password_reset_container.js b/js/components/password_reset_container.js
index 31275a08..6d2d089e 100644
--- a/js/components/password_reset_container.js
+++ b/js/components/password_reset_container.js
@@ -130,8 +130,9 @@ let PasswordResetForm = React.createClass({
},
handleSuccess() {
- this.history.pushState(null, '/collection');
- let notification = new GlobalNotificationModel(getLangText('password successfully updated'), 'success', 10000);
+ this.history.push('/collection');
+
+ const notification = new GlobalNotificationModel(getLangText('password successfully updated'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
diff --git a/js/components/piece_list.js b/js/components/piece_list.js
index 9424117c..68ba5c33 100644
--- a/js/components/piece_list.js
+++ b/js/components/piece_list.js
@@ -37,6 +37,7 @@ let PieceList = React.createClass({
bulkModalButtonListType: React.PropTypes.func,
canLoadPieceList: React.PropTypes.bool,
redirectTo: React.PropTypes.string,
+ shouldRedirect: React.PropTypes.func,
customSubmitButton: React.PropTypes.element,
customThumbnailPlaceholder: React.PropTypes.func,
filterParams: React.PropTypes.array,
@@ -114,9 +115,13 @@ let PieceList = React.createClass({
},
componentDidUpdate() {
- if (this.props.redirectTo && this.state.unfilteredPieceListCount === 0) {
+ const { location: { query }, redirectTo, shouldRedirect } = this.props;
+ const { unfilteredPieceListCount } = this.state;
+
+ if (redirectTo && unfilteredPieceListCount === 0 &&
+ (typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) {
// FIXME: hack to redirect out of the dispatch cycle
- window.setTimeout(() => this.history.pushState(null, this.props.redirectTo, this.props.location.query), 0);
+ window.setTimeout(() => this.history.push({ query, pathname: redirectTo }), 0);
}
},
@@ -169,15 +174,16 @@ let PieceList = React.createClass({
}
},
- searchFor(searchTerm) {
- this.loadPieceList({
- page: 1,
- search: searchTerm
- });
- this.history.pushState(null, this.props.location.pathname, {page: 1});
+ searchFor(search) {
+ const { location: { pathname } } = this.props;
+
+ this.loadPieceList({ search, page: 1 });
+ this.history.push({ pathname, query: { page: 1 } });
},
- applyFilterBy(filterBy){
+ applyFilterBy(filterBy) {
+ const { location: { pathname } } = this.props;
+
this.setState({
isFilterDirty: true
});
@@ -202,7 +208,7 @@ let PieceList = React.createClass({
// we have to redirect the user always to page one as it could be that there is no page two
// for filtered pieces
- this.history.pushState(null, this.props.location.pathname, {page: 1});
+ this.history.push({ pathname, query: { page: 1 } });
},
applyOrderBy(orderBy) {
diff --git a/js/components/register_piece.js b/js/components/register_piece.js
index 8211e91e..b69ed7c2 100644
--- a/js/components/register_piece.js
+++ b/js/components/register_piece.js
@@ -66,7 +66,7 @@ let RegisterPiece = React.createClass( {
},
handleSuccess(response){
- let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
+ const notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
// once the user was able to register a piece successfully, we need to make sure to keep
@@ -80,7 +80,7 @@ let RegisterPiece = React.createClass( {
this.state.filterBy
);
- this.history.pushState(null, `/pieces/${response.piece.id}`);
+ this.history.push(`/pieces/${response.piece.id}`);
},
getSpecifyEditions() {
diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js
index a8d946b5..271901bb 100644
--- a/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js
+++ b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js
@@ -19,8 +19,10 @@ import ApiUrls from '../../../../../../constants/api_urls';
import requests from '../../../../../../utils/requests';
-import { getLangText } from '../../../../../../utils/lang_utils';
+import { getErrorNotificationMessage } from '../../../../../../utils/error_utils';
import { setCookie } from '../../../../../../utils/fetch_api_utils';
+import { validateForms } from '../../../../../../utils/form_utils';
+import { getLangText } from '../../../../../../utils/lang_utils';
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
@@ -35,7 +37,7 @@ const PRRegisterPieceForm = React.createClass({
mixins: [History],
- getInitialState(){
+ getInitialState() {
return {
digitalWorkKeyReady: true,
thumbnailKeyReady: true,
@@ -54,16 +56,16 @@ const PRRegisterPieceForm = React.createClass({
* second adding all the additional details
*/
submit() {
- if(!this.validateForms()) {
+ if (!this.validateForms()) {
return;
- } else {
- // disable the submission button right after the user
- // clicks on it to avoid double submission
- this.setState({
- submitted: true
- });
}
+ // disable the submission button right after the user
+ // clicks on it to avoid double submission
+ this.setState({
+ submitted: true
+ });
+
const { currentUser } = this.props;
const { registerPieceForm,
additionalDataForm,
@@ -104,12 +106,20 @@ const PRRegisterPieceForm = React.createClass({
GlobalNotificationActions.appendGlobalNotification(notificationMessage);
});
})
- .then(() => this.history.pushState(null, `/pieces/${this.state.piece.id}`))
+ .then(() => this.history.push(`/pieces/${this.state.piece.id}`))
.catch((err) => {
- const notificationMessage = new GlobalNotificationModel(getLangText("Oops! We weren't able to send your submission. Contact: support@ascribe.io"), 'danger', 5000);
+ const errMessage = (getErrorNotificationMessage(err) || getLangText("Oops! We weren't able to send your submission.")) +
+ getLangText(' Please contact support@ascribe.io');
+
+ const notificationMessage = new GlobalNotificationModel(errMessage, 'danger', 10000);
GlobalNotificationActions.appendGlobalNotification(notificationMessage);
console.logGlobal(new Error('Portfolio Review piece registration failed'), err);
+
+ // Reset the submit button
+ this.setState({
+ submitted: false
+ });
});
},
@@ -118,11 +128,7 @@ const PRRegisterPieceForm = React.createClass({
additionalDataForm,
uploadersForm } = this.refs;
- const registerPieceFormValidation = registerPieceForm.validate();
- const additionalDataFormValidation = additionalDataForm.validate();
- const uploaderFormValidation = uploadersForm.validate();
-
- return registerPieceFormValidation && additionalDataFormValidation && uploaderFormValidation;
+ return validateForms([registerPieceForm, additionalDataForm, uploadersForm], true);
},
getCreateBlobRoutine() {
@@ -139,7 +145,7 @@ const PRRegisterPieceForm = React.createClass({
},
/**
- * This method is overloaded so that we can track the ready-state
+ * These two methods are overloaded so that we can track the ready-state
* of each uploader in the component
* @param {string} uploaderKey Name of the uploader's key to track
*/
@@ -151,6 +157,14 @@ const PRRegisterPieceForm = React.createClass({
};
},
+ handleOptionalFileValidationFailed(uploaderKey) {
+ return () => {
+ this.setState({
+ [uploaderKey]: true
+ });
+ };
+ },
+
getSubmitButton() {
const { digitalWorkKeyReady,
thumbnailKeyReady,
@@ -303,7 +317,7 @@ const PRRegisterPieceForm = React.createClass({
+ label={getLangText('Featured Cover photo (max 5MB)')}>
this.history.replaceState(null, `/${location.query.redirect}`, queryCopy));
+ if (location && location.query && location.query.redirect) {
+ window.setTimeout(() => this.history.replace({
+ pathname: `/${location.query.redirect}`,
+ query: omitFromObject(location.query, ['redirect'])
+ }));
}
},
@@ -125,4 +128,4 @@ const PRLanding = React.createClass({
}
});
-export default PRLanding;
\ No newline at end of file
+export default PRLanding;
diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js
index 0fbca419..99c9a401 100644
--- a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js
+++ b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js
@@ -39,7 +39,7 @@ const PRRegisterPiece = React.createClass({
if(currentUser && currentUser.email) {
const submittedPieceId = getCookie(currentUser.email);
if(submittedPieceId) {
- this.history.pushState(null, `/pieces/${submittedPieceId}`);
+ this.history.push(`/pieces/${submittedPieceId}`);
}
}
},
@@ -62,7 +62,7 @@ const PRRegisterPiece = React.createClass({
Portfolio Review
-
{getLangText('Submission closing on %s', ' 22 Dec 2015')}
+
{getLangText('Submission closing on %s', ' 27 Dec 2015')}