diff --git a/js/components/ascribe_accordion_list/accordion_list_item_piece.js b/js/components/ascribe_accordion_list/accordion_list_item_piece.js
index 006479c5..9f876388 100644
--- a/js/components/ascribe_accordion_list/accordion_list_item_piece.js
+++ b/js/components/ascribe_accordion_list/accordion_list_item_piece.js
@@ -12,8 +12,11 @@ import { getLangText } from '../../utils/lang_utils';
let AccordionListItemPiece = React.createClass({
propTypes: {
className: React.PropTypes.string,
- artistName: React.PropTypes.string,
- piece: React.PropTypes.object,
+ artistName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.element
+ ]),
+ piece: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
@@ -51,17 +54,21 @@ let AccordionListItemPiece = React.createClass({
piece,
subsubheading,
thumbnailPlaceholder: ThumbnailPlaceholder } = this.props;
- const { url, url_safe } = piece.thumbnail;
+ const { url: thumbnailUrl, url_safe: thumbnailSafeUrl } = piece.thumbnail;
+
+ // Display the 300x300 thumbnail if we have it, otherwise just use the safe url
+ const thumbnailDisplayUrl = (piece.thumbnail.thumbnail_sizes && piece.thumbnail.thumbnail_sizes['300x300']) || thumbnailSafeUrl;
+
let thumbnail;
// Since we're going to refactor the thumbnail generation anyway at one point,
// for not use the annoying ascribe_spiral.png, we're matching the url against
// this name and replace it with a CSS version of the new logo.
- if (url.match(/https:\/\/.*\/media\/thumbnails\/ascribe_spiral.png/)) {
+ if (thumbnailUrl.match(/https:\/\/.*\/media\/thumbnails\/ascribe_spiral.png/)) {
thumbnail = ();
} else {
thumbnail = (
-
{this.props.header}
{this.props.subheader}
{this.props.buttons}
diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js
index 7dfc9570..9615c455 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
*/}
-
+
-
+
{this.state.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_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,60 +81,64 @@ 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 cfd1e108..1a4aaefc 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,36 +51,40 @@ 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 de979e65..68ba5c33 100644
--- a/js/components/piece_list.js
+++ b/js/components/piece_list.js
@@ -115,13 +115,13 @@ let PieceList = React.createClass({
},
componentDidUpdate() {
- const { redirectTo, shouldRedirect } = this.props;
+ 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);
}
},
@@ -174,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
});
@@ -207,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')}
);
diff --git a/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js
index 16849ed1..c5ab2c81 100644
--- a/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js
+++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js
@@ -9,15 +9,16 @@ import StarRating from 'react-star-rating';
import ReactError from '../../../../../../mixins/react_error';
import { ResourceNotFoundError } from '../../../../../../models/errors';
-import PieceActions from '../../../../../../actions/piece_actions';
-import PieceStore from '../../../../../../stores/piece_store';
-
-import PieceListStore from '../../../../../../stores/piece_list_store';
-import PieceListActions from '../../../../../../actions/piece_list_actions';
-
+import PrizeActions from '../../actions/prize_actions';
+import PrizeStore from '../../stores/prize_store';
import PrizeRatingActions from '../../actions/prize_rating_actions';
import PrizeRatingStore from '../../stores/prize_rating_store';
+import PieceActions from '../../../../../../actions/piece_actions';
+import PieceStore from '../../../../../../stores/piece_store';
+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';
@@ -34,9 +35,7 @@ import CollapsibleParagraph from '../../../../../../components/ascribe_collapsib
import FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
-import LoanForm from '../../../../../ascribe_forms/form_loan';
import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions';
-import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
@@ -52,39 +51,41 @@ import { setDocumentTitle } from '../../../../../../utils/dom_utils';
/**
* This is the component that implements resource/data specific functionality
*/
-let PieceContainer = React.createClass({
+let PrizePieceContainer = React.createClass({
propTypes: {
- params: React.PropTypes.object
+ params: React.PropTypes.object,
+ selectedPrizeActionButton: React.PropTypes.func
},
mixins: [ReactError],
getInitialState() {
- return mergeOptions(
- PieceStore.getState(),
- UserStore.getState()
- );
+ //FIXME: this component uses the PieceStore, but we avoid using the getState() here since it may contain stale data
+ // It should instead use something like getInitialState() where that call also resets the state.
+ return UserStore.getState();
+ },
+
+ componentWillMount() {
+ // Every time we enter the piece detail page, just reset the piece
+ // store as it will otherwise display wrong/old data once the user loads
+ // the piece detail a second time
+ PieceActions.updatePiece({});
},
componentDidMount() {
PieceStore.listen(this.onChange);
UserStore.listen(this.onChange);
- // Every time we enter the piece detail page, just reset the piece
- // store as it will otherwise display wrong/old data once the user loads
- // the piece detail a second time
- PieceActions.updatePiece({});
-
- PieceActions.fetchOne(this.props.params.pieceId);
UserActions.fetchCurrentUser();
+ this.loadPiece();
},
// This is done to update the container when the user clicks on the prev or next
// button to update the URL parameter (and therefore to switch pieces)
componentWillReceiveProps(nextProps) {
- if(this.props.params.pieceId !== nextProps.params.pieceId) {
+ if (this.props.params.pieceId !== nextProps.params.pieceId) {
PieceActions.updatePiece({});
- PieceActions.fetchOne(nextProps.params.pieceId);
+ this.loadPiece(nextProps.params.pieceId);
}
},
@@ -101,26 +102,32 @@ let PieceContainer = React.createClass({
UserStore.unlisten(this.onChange);
},
-
onChange(state) {
this.setState(state);
},
getActions() {
- if (this.state.piece &&
- this.state.piece.notifications &&
- this.state.piece.notifications.length > 0) {
+ const { currentUser, piece } = this.state;
+
+ if (piece && piece.notifications && piece.notifications.length > 0) {
return (
);
+ notifications={piece.notifications}/>);
}
},
+ loadPiece(pieceId = this.props.params.pieceId) {
+ PieceActions.fetchOne(pieceId);
+ },
+
render() {
- if(this.state.piece && this.state.piece.id) {
+ const { selectedPrizeActionButton } = this.props;
+ const { currentUser, piece } = this.state;
+
+ if (piece && piece.id) {
/*
This really needs a refactor!
@@ -129,37 +136,32 @@ let PieceContainer = React.createClass({
*/
// Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted
- let artistName = ((this.state.currentUser.is_jury && !this.state.currentUser.is_judge) ||
- (this.state.currentUser.is_judge && !this.state.piece.selected )) ?
- null : this.state.piece.artist_name;
+ let artistName;
+ if ((currentUser.is_jury && !currentUser.is_judge) || (currentUser.is_judge && !piece.selected )) {
+ artistName = ;
+ setDocumentTitle(piece.title);
+ } else {
+ artistName = piece.artist_name;
+ setDocumentTitle([artistName, piece.title].join(', '));
+ }
// Only show the artist email if you are a judge and the piece is shortlisted
- let artistEmail = (this.state.currentUser.is_judge && this.state.piece.selected ) ?
- : null;
-
- if (artistName === null) {
- setDocumentTitle(this.state.piece.title);
- } else {
- setDocumentTitle([artistName, this.state.piece.title].join(', '));
- }
-
- if (artistName === null) {
- artistName = ;
- }
+ const artistEmail = (currentUser.is_judge && piece.selected ) ?
+ : null;
return (
+ piece={piece}
+ currentUser={currentUser}/>
-
{this.state.piece.title}
+
{piece.title}
-
+
{artistEmail}
{this.getActions()}
@@ -168,10 +170,11 @@ let PieceContainer = React.createClass({
subheader={
+ piece={piece}
+ currentUser={currentUser}
+ selectedPrizeActionButton={selectedPrizeActionButton} />
}>
-
+
);
} else {
@@ -225,31 +228,31 @@ let PrizePieceRatings = React.createClass({
propTypes: {
loadPiece: React.PropTypes.func,
piece: React.PropTypes.object,
- currentUser: React.PropTypes.object
+ currentUser: React.PropTypes.object,
+ selectedPrizeActionButton: React.PropTypes.func
},
getInitialState() {
return mergeOptions(
PieceListStore.getState(),
- PrizeRatingStore.getState()
+ PrizeStore.getState(),
+ PrizeRatingStore.getInitialState()
);
},
componentDidMount() {
- PrizeRatingStore.listen(this.onChange);
- PrizeRatingActions.fetchOne(this.props.piece.id);
- PrizeRatingActions.fetchAverage(this.props.piece.id);
PieceListStore.listen(this.onChange);
+ PrizeStore.listen(this.onChange);
+ PrizeRatingStore.listen(this.onChange);
+
+ PrizeActions.fetchPrize();
+ this.fetchPrizeRatings();
},
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
- PrizeRatingActions.updateRating({});
- PrizeRatingStore.unlisten(this.onChange);
PieceListStore.unlisten(this.onChange);
+ PrizeStore.unlisten(this.onChange);
+ PrizeRatingStore.unlisten(this.onChange);
},
// The StarRating component does not have a property that lets us set
@@ -257,7 +260,12 @@ let PrizePieceRatings = React.createClass({
// every mouseover be overridden, we need to set it ourselves initially to deal
// with the problem.
onChange(state) {
+ if (state.prize && state.prize.active_round != this.state.prize.active_round) {
+ this.fetchPrizeRatings(state);
+ }
+
this.setState(state);
+
if (this.refs.rating) {
this.refs.rating.state.ratingCache = {
pos: this.refs.rating.state.pos,
@@ -268,50 +276,30 @@ let PrizePieceRatings = React.createClass({
}
},
+ fetchPrizeRatings(state = this.state) {
+ PrizeRatingActions.fetchOne(this.props.piece.id, state.prize.active_round);
+ PrizeRatingActions.fetchAverage(this.props.piece.id, state.prize.active_round);
+ },
+
onRatingClick(event, args) {
event.preventDefault();
- PrizeRatingActions.createRating(this.props.piece.id, args.rating).then(
- this.refreshPieceData()
- );
+ PrizeRatingActions
+ .createRating(this.props.piece.id, args.rating, this.state.prize.active_round)
+ .then(this.refreshPieceData);
},
- handleLoanRequestSuccess(message){
- let notification = new GlobalNotificationModel(message, 'success', 4000);
- GlobalNotificationActions.appendGlobalNotification(notification);
- },
+ getSelectedActionButton() {
+ const { currentUser, piece, selectedPrizeActionButton: SelectedPrizeActionButton } = this.props;
- getLoanButton(){
- let today = new Moment();
- let endDate = new Moment();
- endDate.add(6, 'months');
- return (
-
- {getLangText('SEND LOAN REQUEST')}
-
- }
- handleSuccess={this.handleLoanRequestSuccess}
- title='REQUEST LOAN'>
-
- );
- },
-
- handleShortlistSuccess(message){
- let notification = new GlobalNotificationModel(message, 'success', 2000);
- GlobalNotificationActions.appendGlobalNotification(notification);
+ if (piece.selected && SelectedPrizeActionButton) {
+ return (
+
+
+
+ );
+ }
},
refreshPieceData() {
@@ -321,20 +309,19 @@ let PrizePieceRatings = React.createClass({
},
onSelectChange() {
- PrizeRatingActions.toggleShortlist(this.props.piece.id)
- .then(
- (res) => {
+ PrizeRatingActions
+ .toggleShortlist(this.props.piece.id)
+ .then((res) => {
this.refreshPieceData();
- return res;
- })
- .then(
- (res) => {
- this.handleShortlistSuccess(res.notification);
- }
- );
+
+ if (res && res.notification) {
+ const notification = new GlobalNotificationModel(res.notification, 'success', 2000);
+ GlobalNotificationActions.appendGlobalNotification(notification);
+ }
+ });
},
- render(){
+ render() {
if (this.props.piece && this.props.currentUser && this.props.currentUser.is_judge && this.state.average) {
// Judge sees shortlisting, average and per-jury notes
return (
@@ -352,9 +339,7 @@ let PrizePieceRatings = React.createClass({
-
- {this.props.piece.selected ? this.getLoanButton() : null}
-
+ {this.getSelectedActionButton()}
@@ -373,13 +358,19 @@ let PrizePieceRatings = React.createClass({
{this.state.ratings.map((item, i) => {
- let note = item.note ?
+ let note = item.note ? (