diff --git a/README.md b/README.md index b027e8fe..36f47954 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Additionally, to work on the white labeling functionality, you need to edit your 127.0.0.1 cyland.localhost.com 127.0.0.1 ikonotv.localhost.com 127.0.0.1 sluice.localhost.com +127.0.0.1 lumenus.localhost.com +127.0.0.1 portfolioreview.localhost.com ``` diff --git a/js/components/ascribe_detail/media_container.js b/js/components/ascribe_detail/media_container.js index 6ac2f745..d31a205f 100644 --- a/js/components/ascribe_detail/media_container.js +++ b/js/components/ascribe_detail/media_container.js @@ -92,7 +92,7 @@ let MediaContainer = React.createClass({ aclObject={this.props.content.acl} aclName="acl_download"> {embed} diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js index fe15f537..54c06d98 100644 --- a/js/components/ascribe_forms/form.js +++ b/js/components/ascribe_forms/form.js @@ -12,7 +12,7 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import requests from '../../utils/requests'; import { getLangText } from '../../utils/lang_utils'; -import { mergeOptionsWithDuplicates } from '../../utils/general_utils'; +import { mergeOptionsWithDuplicates, sanitize } from '../../utils/general_utils'; let Form = React.createClass({ @@ -155,7 +155,7 @@ let Form = React.createClass({ }); }, - handleError(err){ + handleError(err) { if (err.json) { for (let input in err.json.errors){ if (this.refs && this.refs[input] && this.refs[input].state) { @@ -185,7 +185,7 @@ let Form = React.createClass({ this.setState({submitted: false}); }, - clearErrors(){ + clearErrors() { for(let ref in this.refs){ if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){ this.refs[ref].clearErrors(); @@ -236,12 +236,12 @@ let Form = React.createClass({ }, renderChildren() { - return ReactAddons.Children.map(this.props.children, (child) => { + return ReactAddons.Children.map(this.props.children, (child, i) => { if (child) { return ReactAddons.addons.cloneWithProps(child, { handleChange: this.handleChangeChild, ref: child.props.name, - + key: i, // We need this in order to make editable be overridable when setting it directly // on Property editable: child.props.overrideForm ? child.props.editable : !this.props.disabled @@ -269,6 +269,83 @@ let Form = React.createClass({ } }, + /** + * Validates a single ref and returns a human-readable error message + * @param {object} refToValidate A customly constructed object to check + * @return {oneOfType([arrayOf(string), bool])} Either an error message or false, saying that + * everything is valid + */ + _hasRefErrors(refToValidate) { + let errors = Object + .keys(refToValidate) + .reduce((a, constraintKey) => { + const contraintValue = refToValidate[constraintKey]; + + if(!contraintValue) { + switch(constraintKey) { + case 'min' || 'max': + a.push(getLangText('The field you defined is not in the valid range')); + break; + case 'pattern': + a.push(getLangText('The value you defined is not matching the valid pattern')); + break; + case 'required': + a.push(getLangText('This field is required')); + break; + } + } + + return a; + }, []); + + return errors.length ? errors : false; + }, + + /** + * This method validates all child inputs of the form. + * + * As of now, it only considers + * - `max` + * - `min` + * - `pattern` + * - `required` + * + * The idea is to enhance this method everytime we need more thorough validation. + * So feel free to add props that additionally should be checked, if they're present + * in the input's props. + * + * @return {[type]} [description] + */ + validate() { + this.clearErrors(); + const validatedFormInputs = {}; + + Object + .keys(this.refs) + .forEach((refName) => { + let refToValidate = {}; + const property = this.refs[refName]; + const input = property.refs.input; + const value = input.getDOMNode().value || input.state.value; + const { max, + min, + pattern, + required, + type } = input.props; + + refToValidate.required = required ? value : true; + refToValidate.pattern = pattern && typeof value === 'string' ? value.match(pattern) : true; + refToValidate.max = type === 'number' ? parseInt(value, 10) <= max : true; + refToValidate.min = type === 'number' ? parseInt(value, 10) >= min : true; + + const validatedRef = this._hasRefErrors(refToValidate); + validatedFormInputs[refName] = validatedRef; + }); + const errorMessagesForRefs = sanitize(validatedFormInputs, (val) => !val); + this.handleError({ json: { errors: errorMessagesForRefs } }); + return !Object.keys(errorMessagesForRefs).length; + }, + render() { let className = 'ascribe-form'; diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index f0af2143..cc4713df 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -3,64 +3,80 @@ import React from 'react'; import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; +import FileDragAndDrop from '../ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop'; import AppConstants from '../../constants/application_constants'; import { getCookie } from '../../utils/fetch_api_utils'; -let InputFineUploader = React.createClass({ + +const { func, bool, object, shape, string, number, arrayOf } = React.PropTypes; + +const InputFineUploader = React.createClass({ propTypes: { - setIsUploadReady: React.PropTypes.func, - isReadyForFormSubmission: React.PropTypes.func, - submitFileName: React.PropTypes.func, + setIsUploadReady: func, + isReadyForFormSubmission: func, + submitFileName: func, + fileInputElement: func, - areAssetsDownloadable: React.PropTypes.bool, + areAssetsDownloadable: bool, - onClick: React.PropTypes.func, - keyRoutine: React.PropTypes.shape({ - url: React.PropTypes.string, - fileClass: React.PropTypes.string + keyRoutine: shape({ + url: string, + fileClass: string }), - createBlobRoutine: React.PropTypes.shape({ - url: React.PropTypes.string + createBlobRoutine: shape({ + url: string }), - validation: React.PropTypes.shape({ - itemLimit: React.PropTypes.number, - sizeLimit: React.PropTypes.string, - allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) + validation: shape({ + itemLimit: number, + sizeLimit: string, + allowedExtensions: arrayOf(string) }), // isFineUploaderActive is used to lock react fine uploader in case // a user is actually not logged in already to prevent him from droping files // before login in - isFineUploaderActive: React.PropTypes.bool, - onLoggedOut: React.PropTypes.func, + isFineUploaderActive: bool, + onLoggedOut: func, - enableLocalHashing: React.PropTypes.bool, + enableLocalHashing: bool, // provided by Property - disabled: React.PropTypes.bool, + disabled: bool, // A class of a file the user has to upload // Needs to be defined both in singular as well as in plural - fileClassToUpload: React.PropTypes.shape({ - singular: React.PropTypes.string, - plural: React.PropTypes.string + fileClassToUpload: shape({ + singular: string, + plural: string }), - location: React.PropTypes.object + location: object + }, + + getDefaultProps() { + return { + fileInputElement: FileDragAndDrop + }; }, getInitialState() { return { - value: null + value: null, + file: null }; }, - submitFile(file){ + submitFile(file) { this.setState({ + file, value: file.key }); + if(this.state.value && typeof this.props.onChange === 'function') { + this.props.onChange({ target: { value: this.state.value } }); + } + if(typeof this.props.submitFileName === 'function') { this.props.submitFileName(file.originalName); } @@ -70,7 +86,25 @@ let InputFineUploader = React.createClass({ this.refs.fineuploader.reset(); }, + createBlobRoutine() { + const { fineuploader } = this.refs; + const { file } = this.state; + + fineuploader.createBlob(file); + }, + render() { + const { fileInputElement, + keyRoutine, + createBlobRoutine, + validation, + setIsUploadReady, + isReadyForFormSubmission, + areAssetsDownloadable, + onLoggedOut, + enableLocalHashing, + fileClassToUpload, + location } = this.props; let editable = this.props.isFineUploaderActive; // if disabled is actually set by property, we want to override @@ -82,14 +116,14 @@ let InputFineUploader = React.createClass({ return ( + onInactive={onLoggedOut} + enableLocalHashing={enableLocalHashing} + fileClassToUpload={fileClassToUpload} + location={location}/> ); } }); diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index 793be538..ac272988 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -181,9 +181,7 @@ let Property = React.createClass({ setErrors(errors){ this.setState({ - errors: errors.map((error) => { - return {error}; - }) + errors: errors.pop() }); }, @@ -255,8 +253,10 @@ let Property = React.createClass({ placement="top" overlay={tooltip}>
- {this.state.errors} - {this.props.label} +

+ {this.props.label} + {this.state.errors} +

{this.renderChildren(style)} {footer}
diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index a7e56fb7..19d6a1c7 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -50,25 +50,35 @@ let Other = React.createClass({ let Image = React.createClass({ propTypes: { - url: React.PropTypes.string.isRequired, + url: React.PropTypes.string, preview: React.PropTypes.string.isRequired }, mixins: [InjectInHeadMixin], componentDidMount() { - this.inject('https://code.jquery.com/jquery-2.1.4.min.js') - .then(() => - Q.all([ - this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'), - this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js') - ]).then(() => { window.jQuery('.shmui-ascribe').shmui(); })); + if(this.props.url) { + this.inject('https://code.jquery.com/jquery-2.1.4.min.js') + .then(() => + Q.all([ + this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'), + this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js') + ]).then(() => { window.jQuery('.shmui-ascribe').shmui(); })); + } }, render() { - return ( - - ); + const { url, preview } = this.props; + + if(url) { + return ( + + ); + } else { + return ( + + ); + } } }); @@ -202,26 +212,50 @@ let MediaPlayer = React.createClass({ }, render() { - if (this.props.mimetype === 'video' && this.props.encodingStatus !== undefined && this.props.encodingStatus !== 100) { + const { mimetype, + preview, + url, + extraData, + encodingStatus } = this.props; + + if (mimetype === 'video' && encodingStatus !== undefined && encodingStatus !== 100) { return (

We successfully received your video and it is now being encoded.
You can leave this page and check back on the status later.

-
); } else { - let Component = resourceMap[this.props.mimetype] || Other; + let Component = resourceMap[mimetype] || Other; + let componentProps = { + preview, + url, + extraData, + encodingStatus + }; + + // Since the launch of the portfolio whitelabel submission, + // we allow the user to specify a thumbnail upon piece-registration. + // As the `Component` is chosen according to its filetype but could potentially + // have a manually submitted thumbnail, we match if the to `Mediaplayer` submitted thumbnail + // is not the generally used fallback `url` (ascribe_spiral.png). + // + // If this is the case, we disable shmui by deleting the original `url` prop and replace + // the assigned component to `Image`. + if(!decodeURIComponent(preview).match(/https:\/\/.*\/media\/thumbnails\/ascribe_spiral.png/) && + Component === Other) { + Component = resourceMap.image; + delete componentProps.url; + } + return (
- +
); } diff --git a/js/components/ascribe_settings/contract_settings_update_button.js b/js/components/ascribe_settings/contract_settings_update_button.js index f3bab156..265e6548 100644 --- a/js/components/ascribe_settings/contract_settings_update_button.js +++ b/js/components/ascribe_settings/contract_settings_update_button.js @@ -56,7 +56,6 @@ let ContractSettingsUpdateButton = React.createClass({ render() { return ( file.status === 'uploading'); }, - handleOnClick() { - let uploadingFiles = this.getUploadingFiles(); + getUploadedFile() { + return this.props.filesToUpload.filter((file) => file.status === 'upload successful')[0]; + }, - // We only want the button to be clickable if there are no files currently uploading + handleOnClick() { + const uploadingFiles = this.getUploadingFiles(); + const uploadedFile = this.getUploadedFile(); + + if(uploadedFile) { + this.props.handleCancelFile(uploadedFile.id); + } if(uploadingFiles.length === 0) { + // We only want the button to be clickable if there are no files currently uploading + // Firefox only recognizes the simulated mouse click if bubbles is set to true, // but since Google Chrome propagates the event much further than needed, we // need to stop propagation as soon as the event is created @@ -62,40 +77,61 @@ let UploadButton = React.createClass({ // filter invalid files that might have been deleted or canceled... filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter); - // Depending on wether there is an upload going on or not we - // display the progress - if(filesToUpload.length > 0) { + if(this.getUploadingFiles().length !== 0) { return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; } else { return fileClassToUpload.singular; } }, - render() { - let { - multiple, - fileClassToUpload, - allowedExtensions - } = this.props; + getUploadedFileLabel() { + const uploadedFile = this.getUploadedFile(); + if(uploadedFile) { + return ( + + + {' ' + truncateTextAtCharIndex(uploadedFile.name, 40)} + + ); + } else { + return ( + {getLangText('No file chosen')} + ); + } + }, + + render() { + let { multiple, + allowedExtensions } = this.props; + + /* + * We do not want a button that submits here. + * As UploadButton could be used in forms that want to be submitted independent + * of clicking the selector. + * Therefore the wrapping component needs to be an `anchor` tag instead of a `button` + */ return ( - +
+ + {this.getButtonLabel()} + + + {this.getUploadedFileLabel()} +
); } }); diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index e8cc8bfa..bd2da199 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -298,18 +298,27 @@ let ReactS3FineUploader = React.createClass({ resolve(res.key); }) .catch((err) => { - console.logGlobal(err, false, { - files: this.state.filesToUpload, - chunks: this.state.chunks - }); + this.onErrorPromiseProxy(err); reject(err); }); }); }, createBlob(file) { + const { createBlobRoutine } = this.props; + return Q.Promise((resolve, reject) => { - window.fetch(this.props.createBlobRoutine.url, { + + // if createBlobRoutine is not defined, + // we're progressing right away without posting to S3 + // so that this can be done manually by the form + if(!createBlobRoutine) { + // still we warn the user of this component + console.warn('createBlobRoutine was not defined for ReactS3FineUploader. Continuing without creating the blob on the server.'); + resolve(); + } + + window.fetch(createBlobRoutine.url, { method: 'post', headers: { 'Accept': 'application/json', @@ -320,7 +329,7 @@ let ReactS3FineUploader = React.createClass({ body: JSON.stringify({ 'filename': file.name, 'key': file.key, - 'piece_id': this.props.createBlobRoutine.pieceId + 'piece_id': createBlobRoutine.pieceId }) }) .then((res) => { @@ -336,16 +345,16 @@ let ReactS3FineUploader = React.createClass({ } else if(res.contractblob) { file.s3Url = res.contractblob.url_safe; file.s3UrlSafe = res.contractblob.url_safe; + } else if(res.thumbnail) { + file.s3Url = res.thumbnail.url_safe; + file.s3UrlSafe = res.thumbnail.url_safe; } else { throw new Error(getLangText('Could not find a url to download.')); } resolve(res); }) .catch((err) => { - console.logGlobal(err, false, { - files: this.state.filesToUpload, - chunks: this.state.chunks - }); + this.onErrorPromiseProxy(err); reject(err); }); }); @@ -412,9 +421,8 @@ let ReactS3FineUploader = React.createClass({ if(this.props.submitFile) { this.props.submitFile(files[id]); } else { - console.warn('You didn\'t define submitFile in as a prop in react-s3-fine-uploader'); + console.warn('You didn\'t define submitFile as a prop in react-s3-fine-uploader'); } - // for explanation, check comment of if statement above if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { // also, lets check if after the completion of this upload, @@ -429,22 +437,27 @@ let ReactS3FineUploader = React.createClass({ console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader'); } }) - .catch((err) => { - console.logGlobal(err, false, { - files: this.state.filesToUpload, - chunks: this.state.chunks - }); - let notification = new GlobalNotificationModel(err.message, 'danger', 5000); - GlobalNotificationActions.appendGlobalNotification(notification); - }); + .catch(this.onErrorPromiseProxy); } }, + /** + * We want to channel all errors in this component through one single method. + * As fineuploader's `onError` method cannot handle the callback parameters of + * a promise we define this proxy method to crunch them into the correct form. + * + * @param {error} err a plain Javascript error + */ + onErrorPromiseProxy(err) { + this.onError(null, null, err.message); + }, + onError(id, name, errorReason) { console.logGlobal(errorReason, false, { files: this.state.filesToUpload, chunks: this.state.chunks }); + this.props.setIsUploadReady(true); this.state.uploader.cancelAll(); let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000); @@ -609,6 +622,10 @@ let ReactS3FineUploader = React.createClass({ }, handleUploadFile(files) { + // While files are being uploaded, the form cannot be ready + // for submission + this.props.setIsUploadReady(false); + // If multiple set and user already uploaded its work, // cancel upload if(!this.props.multiple && this.state.filesToUpload.filter(displayValidFilesFilter).length > 0) { diff --git a/js/components/register_piece.js b/js/components/register_piece.js index f127c149..43ac7bb7 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -40,12 +40,6 @@ let RegisterPiece = React.createClass( { mixins: [History], - getDefaultProps() { - return { - canSpecifyEditions: true - }; - }, - getInitialState(){ return mergeOptions( UserStore.getState(), diff --git a/js/components/whitelabel/prize/actions/prize_actions.js b/js/components/whitelabel/prize/actions/prize_actions.js deleted file mode 100644 index fcd9e91e..00000000 --- a/js/components/whitelabel/prize/actions/prize_actions.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -import { alt } from '../../../../alt'; -import Q from 'q'; - -import PrizeFetcher from '../fetchers/prize_fetcher'; - -class PrizeActions { - constructor() { - this.generateActions( - 'updatePrize' - ); - } - - fetchPrize() { - return Q.Promise((resolve, reject) => { - PrizeFetcher - .fetch() - .then((res) => { - this.actions.updatePrize({ - prize: res.prize - }); - resolve(res); - }) - .catch((err) => { - console.logGlobal(err); - reject(err); - }); - }); - } -} - -export default alt.createActions(PrizeActions); \ No newline at end of file diff --git a/js/components/whitelabel/prize/components/prize_register_piece.js b/js/components/whitelabel/prize/components/prize_register_piece.js deleted file mode 100644 index 87a23591..00000000 --- a/js/components/whitelabel/prize/components/prize_register_piece.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -import React from 'react'; - -import PrizeActions from '../actions/prize_actions'; -import PrizeStore from '../stores/prize_store'; - -import RegisterPiece from '../../../register_piece'; -import Property from '../../../ascribe_forms/property'; -import InputTextAreaToggable from '../../../ascribe_forms/input_textarea_toggable'; -import InputCheckbox from '../../../ascribe_forms/input_checkbox'; - -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; - - -let PrizeRegisterPiece = React.createClass({ - getInitialState() { - return PrizeStore.getState(); - }, - - componentDidMount() { - PrizeStore.listen(this.onChange); - PrizeActions.fetchPrize(); - }, - - componentWillUnmount() { - PrizeStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - render() { - setDocumentTitle(getLangText('Submit to the prize')); - - if(this.state.prize && this.state.prize.active){ - return ( - - - - - - - - - - - {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} - ( - {getLangText('read')} - ) - - - - ); - } - else { - return ( -
-
- {getLangText('The prize is no longer active')} -
-
- ); - } - } -}); - -export default PrizeRegisterPiece; diff --git a/js/components/whitelabel/prize/constants/prize_api_urls.js b/js/components/whitelabel/prize/constants/prize_api_urls.js index 2d35cf19..cb4e2e44 100644 --- a/js/components/whitelabel/prize/constants/prize_api_urls.js +++ b/js/components/whitelabel/prize/constants/prize_api_urls.js @@ -2,6 +2,7 @@ import AppPrizeConstants from './prize_application_constants'; + function getPrizeApiUrls(subdomain) { return { 'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/', @@ -18,10 +19,9 @@ function getPrizeApiUrls(subdomain) { 'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/', 'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/', 'rating_average': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/average/', - 'select_piece' : AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/select/', + 'select_piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/select/', 'notes': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/', 'note': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/${piece_id}/' - }; } 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 new file mode 100644 index 00000000..0c293b15 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js @@ -0,0 +1,375 @@ +'use strict'; + +import React from 'react'; +import { History } from 'react-router'; + +import Form from '../../../../../ascribe_forms/form'; +import Property from '../../../../../ascribe_forms/property'; +import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable'; + +import UploadButton from '../../../../../ascribe_uploader/ascribe_upload_button/upload_button'; +import InputFineuploader from '../../../../../ascribe_forms/input_fineuploader'; +import AscribeSpinner from '../../../../../ascribe_spinner'; + +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; + +import AppConstants from '../../../../../../constants/application_constants'; +import ApiUrls from '../../../../../../constants/api_urls'; + +import requests from '../../../../../../utils/requests'; + +import { getLangText } from '../../../../../../utils/lang_utils'; +import { setCookie } from '../../../../../../utils/fetch_api_utils'; +import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils'; + + +const { object } = React.PropTypes; + +const PRRegisterPieceForm = React.createClass({ + propTypes: { + location: object, + history: object, + currentUser: object + }, + + mixins: [History], + + getInitialState(){ + return { + digitalWorkKeyReady: true, + thumbnailKeyReady: true, + + // we set this to true, as it is not required + supportingMaterialsReady: true, + proofOfPaymentReady: true, + piece: null, + submitted: false + }; + }, + + /** + * In this method, we're composing all fields on the page + * in two steps, first submitting the registration of the piece and + * second adding all the additional details + */ + submit() { + if(!this.validateForms()) { + return; + } else { + // 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, + uploadersForm } = this.refs; + const { digitalWorkKey, + thumbnailKey, + supportingMaterials, + proofOfPayment } = uploadersForm.refs; + const additionalDataFormData = additionalDataForm.getFormData(); + + // composing data for piece registration + let registerPieceFormData = registerPieceForm.getFormData(); + registerPieceFormData.digital_work_key = digitalWorkKey.state.value; + registerPieceFormData.thumbnail_file = thumbnailKey.state.value; + registerPieceFormData.terms = true; + + // submitting the piece + requests + .post(ApiUrls.pieces_list, { body: registerPieceFormData }) + .then(({ success, piece, notification }) => { + if(success) { + this.setState({ + piece + }, () => { + supportingMaterials.refs.input.createBlobRoutine(); + proofOfPayment.refs.input.createBlobRoutine(); + }); + + setCookie(currentUser.email, piece.id); + + return requests.post(ApiUrls.piece_extradata, { + body: { + extradata: additionalDataFormData, + piece_id: piece.id + }, + piece_id: piece.id + }); + } else { + const notificationMessage = new GlobalNotificationModel(notification, 'danger', 5000); + GlobalNotificationActions.appendGlobalNotification(notificationMessage); + } + }) + .then(() => this.history.pushState(null, `/pieces/${this.state.piece.id}`)) + .catch(() => { + const notificationMessage = new GlobalNotificationModel(getLangText("Ups! We weren't able to send your submission. Contact: support@ascribe.io"), 'danger', 5000); + GlobalNotificationActions.appendGlobalNotification(notificationMessage); + }); + }, + + validateForms() { + const { registerPieceForm, + additionalDataForm, + uploadersForm } = this.refs; + + const registerPieceFormValidation = registerPieceForm.validate(); + const additionalDataFormValidation = additionalDataForm.validate(); + const uploaderFormValidation = uploadersForm.validate(); + + return registerPieceFormValidation && additionalDataFormValidation && uploaderFormValidation; + }, + + getCreateBlobRoutine() { + const { piece } = this.state; + + if(piece && piece.id) { + return { + url: ApiUrls.blob_otherdatas, + pieceId: piece.id + }; + } else { + return null; + } + }, + + /** + * This method is 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 + */ + setIsUploadReady(uploaderKey) { + return (isUploadReady) => { + this.setState({ + [uploaderKey]: isUploadReady + }); + }; + }, + + getSubmitButton() { + const { digitalWorkKeyReady, + thumbnailKeyReady, + supportingMaterialsReady, + proofOfPaymentReady, + submitted } = this.state; + + if(submitted) { + return ( + + + + ); + } else { + return ( + + ); + } + }, + + render() { + const { location } = this.props; + + return ( +
+
+ + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ + + {getLangText('By submitting this form, you agree to the') + ' '} + + {getLangText('Terms of Service')} + + {' of Portfolio Review.'} + + +
+ {this.getSubmitButton()} +
+ ); + } +}); + +export default PRRegisterPieceForm; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js b/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js new file mode 100644 index 00000000..a0fa0811 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js @@ -0,0 +1,42 @@ +'use strict'; + +import React from 'react'; + +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; + +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; + + +const PRHero = React.createClass({ + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + const { currentUser } = this.state; + + return ( +
+

Congratulations {currentUser.email}!

+

You have successfully submitted to Portfolio Review 2016

+

See below, your uploaded portfolio:

+
+ ); + } +}); + +export default PRHero; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js b/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js new file mode 100644 index 00000000..cdada68b --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js @@ -0,0 +1,128 @@ +'use strict'; + +import React from 'react'; +import { History } from 'react-router'; + +import PrizeActions from '../../simple_prize/actions/prize_actions'; +import PrizeStore from '../../simple_prize/stores/prize_store'; + +import Button from 'react-bootstrap/lib/Button'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; + +import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; + +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; + +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; + + +const PRLanding = React.createClass({ + propTypes: { + location: React.PropTypes.object + }, + + mixins: [History], + + getInitialState() { + return mergeOptions( + PrizeStore.getState(), + UserStore.getState() + ); + }, + + componentDidMount() { + const { location } = this.props; + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + PrizeStore.listen(this.onChange); + PrizeActions.fetchPrize(); + + if(location && location.query && location.query.redirect) { + let queryCopy = JSON.parse(JSON.stringify(location.query)); + delete queryCopy.redirect; + window.setTimeout(() => this.history.replaceState(null, `/${location.query.redirect}`, queryCopy)); + } + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + PrizeStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + getButtons() { + if (this.state.prize && this.state.prize.active){ + return ( + + + + + +

+ {getLangText('or, already an ascribe user?')} +

+ + + +
+ ); + } + return ( + + + {getLangText('Sign up to ascribe')} + + +

+ {getLangText('or, already an ascribe user?')} +

+ + + +
+ ); + }, + + getTitle() { + if (this.state.prize && this.state.prize.active){ + return ( +

+ {getLangText('This is the submission page for Portfolio Review 2016.')} +

+ ); + } + return ( +

+ {getLangText('Submissions for Portfolio Review 2016 are now closed.')} +

+ ); + }, + render() { + return ( +
+
+
+

+ {getLangText('Welcome to Portfolio Review 2016')} +

+ {this.getTitle()} + {this.getButtons()} +
+
+
+ ); + } +}); + +export default PRLanding; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js new file mode 100644 index 00000000..a2a70a97 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js @@ -0,0 +1,82 @@ +'use strict'; + +import React from 'react'; +import { Link, History } from 'react-router'; + +import Col from 'react-bootstrap/lib/Col'; +import Row from 'react-bootstrap/lib/Row'; + +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; + +import PRRegisterPieceForm from './pr_forms/pr_register_piece_form'; + +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import { getCookie } from '../../../../../utils/fetch_api_utils'; + + +const { object } = React.PropTypes; + +const PRRegisterPiece = React.createClass({ + propTypes: { + location: object + }, + + mixins: [History], + + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentDidUpdate() { + const { currentUser } = this.state; + if(currentUser && currentUser.email) { + const submittedPieceId = getCookie(currentUser.email); + if(submittedPieceId) { + this.history.pushState(null, `/pieces/${submittedPieceId}`); + } + } + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + const { currentUser } = this.state; + const { location } = this.props; + + setDocumentTitle(getLangText('Submit to Portfolio Review')); + return ( + + +
+

Portfolio Review

+

{getLangText('Submission closing on %s', ' 22 Dec 2015')}

+

+ {getLangText("You're submitting as %s. ", currentUser.email)} + {getLangText('Change account?')} +

+
+ + + + +
+ ); + } +}); + +export default PRRegisterPiece; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/pr_app.js b/js/components/whitelabel/prize/portfolioreview/pr_app.js new file mode 100644 index 00000000..7e66c43c --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/pr_app.js @@ -0,0 +1,65 @@ +'use strict'; + +import React from 'react'; +import GlobalNotification from '../../../global_notification'; + +import Hero from './components/pr_hero'; + +import UserStore from '../../../../stores/user_store'; +import UserActions from '../../../../actions/user_actions'; + +import { getSubdomain } from '../../../../utils/general_utils'; +import { getCookie } from '../../../../utils/fetch_api_utils'; + + +let PRApp = React.createClass({ + propTypes: { + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.element), + React.PropTypes.element + ]), + history: React.PropTypes.object, + routes: React.PropTypes.arrayOf(React.PropTypes.object) + }, + + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + const { history, children } = this.props; + const { currentUser } = this.state; + let subdomain = getSubdomain(); + let header; + + if (currentUser && currentUser.email && history.isActive(`/pieces/${getCookie(currentUser.email)}`)) { + header = ; + } + + return ( +
+ {header} +
+ {children} + + +
+
+ ); + } +}); + +export default PRApp; diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 14d8b4d9..1f2c6374 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -3,58 +3,94 @@ import React from 'react'; import { Route, IndexRoute } from 'react-router'; -import Landing from './components/prize_landing'; -import LoginContainer from './components/prize_login_container'; -import LogoutContainer from '../../../components/logout_container'; -import SignupContainer from './components/prize_signup_container'; -import PasswordResetContainer from '../../../components/password_reset_container'; -import PrizeRegisterPiece from './components/prize_register_piece'; -import PrizePieceList from './components/prize_piece_list'; -import PrizePieceContainer from './components/ascribe_detail/prize_piece_container'; -import EditionContainer from '../../ascribe_detail/edition_container'; -import SettingsContainer from './components/prize_settings_container'; -import CoaVerifyContainer from '../../../components/coa_verify_container'; -import ErrorNotFoundPage from '../../../components/error_not_found_page'; +import SPLanding from './simple_prize/components/prize_landing'; +import SPLoginContainer from './simple_prize/components/prize_login_container'; +import SPSignupContainer from './simple_prize/components/prize_signup_container'; +import SPRegisterPiece from './simple_prize/components/prize_register_piece'; +import SPPieceList from './simple_prize/components/prize_piece_list'; +import SPPieceContainer from './simple_prize/components/ascribe_detail/prize_piece_container'; +import SPSettingsContainer from './simple_prize/components/prize_settings_container'; +import SPApp from './simple_prize/prize_app'; -import App from './prize_app'; +import PRApp from './portfolioreview/pr_app'; +import PRLanding from './portfolioreview/components/pr_landing'; +import PRRegisterPiece from './portfolioreview/components/pr_register_piece'; + +import EditionContainer from '../../ascribe_detail/edition_container'; +import LogoutContainer from '../../logout_container'; +import PasswordResetContainer from '../../password_reset_container'; +import CoaVerifyContainer from '../../coa_verify_container'; +import ErrorNotFoundPage from '../../error_not_found_page'; import AuthProxyHandler from '../../../components/ascribe_routes/proxy_routes/auth_proxy_handler'; -function getRoutes() { - return ( - - +const ROUTES = { + sluice: ( + + + component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPLoginContainer)} /> + component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPSignupContainer)} /> + component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SPSettingsContainer)}/> - + - ); + ), + portfolioreview: ( + + + + + + + + + + + ) +}; + + +function getRoutes(commonRoutes, subdomain) { + if(subdomain in ROUTES) { + return ROUTES[subdomain]; + } else { + throw new Error('Subdomain wasn\'t specified in the wallet app.'); + } } diff --git a/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js new file mode 100644 index 00000000..dbca1b5d --- /dev/null +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js @@ -0,0 +1,28 @@ +'use strict'; + +import { alt } from '../../../../../alt'; + +import PrizeFetcher from '../fetchers/prize_fetcher'; + +class PrizeActions { + constructor() { + this.generateActions( + 'updatePrize' + ); + } + + fetchPrize() { + PrizeFetcher + .fetch() + .then((res) => { + this.actions.updatePrize({ + prize: res.prize + }); + }) + .catch((err) => { + console.logGlobal(err); + }); + } +} + +export default alt.createActions(PrizeActions); \ No newline at end of file diff --git a/js/components/whitelabel/prize/actions/prize_jury_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js similarity index 97% rename from js/components/whitelabel/prize/actions/prize_jury_actions.js rename to js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js index 9bd03f59..24cf08e8 100644 --- a/js/components/whitelabel/prize/actions/prize_jury_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import Q from 'q'; import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher'; diff --git a/js/components/whitelabel/prize/actions/prize_rating_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js similarity index 98% rename from js/components/whitelabel/prize/actions/prize_rating_actions.js rename to js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js index 184d84e7..68b5334b 100644 --- a/js/components/whitelabel/prize/actions/prize_rating_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import Q from 'q'; import PrizeRatingFetcher from '../fetchers/prize_rating_fetcher'; diff --git a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js similarity index 89% rename from js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js index 08ebce42..965b9012 100644 --- a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js @@ -5,25 +5,25 @@ import { Link } from 'react-router'; import StarRating from 'react-star-rating'; import Moment from 'moment'; -import PieceListActions from '../../../../../actions/piece_list_actions'; -import PieceListStore from '../../../../../stores/piece_list_store'; +import PieceListActions from '../../../../../../actions/piece_list_actions'; +import PieceListStore from '../../../../../../stores/piece_list_store'; import PrizeRatingActions from '../../actions/prize_rating_actions'; -import UserStore from '../../../../../stores/user_store'; +import UserStore from '../../../../../../stores/user_store'; -import InputCheckbox from '../../../../ascribe_forms/input_checkbox'; +import InputCheckbox from '../../../../../ascribe_forms/input_checkbox'; -import AccordionListItemPiece from '../../../../ascribe_accordion_list/accordion_list_item_piece'; +import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; -import GlobalNotificationModel from '../../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; -import AclProxy from '../../../../acl_proxy'; +import AclProxy from '../../../../../acl_proxy'; import SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button'; -import { getLangText } from '../../../../../utils/lang_utils'; -import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../../utils/general_utils'; let AccordionListItemPrize = React.createClass({ diff --git a/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js similarity index 86% rename from js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js index 409b8aa1..8ceb87ea 100644 --- a/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js @@ -3,10 +3,10 @@ import React from 'react'; import classNames from 'classnames'; -import ModalWrapper from '../../../../ascribe_modal/modal_wrapper'; -import PieceSubmitToPrizeForm from '../../../../ascribe_forms/form_submit_to_prize'; +import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; +import PieceSubmitToPrizeForm from '../../../../../ascribe_forms/form_submit_to_prize'; -import { getLangText } from '../../../../../utils/lang_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; let SubmitToPrizeButton = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js similarity index 82% rename from js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js index 30a91607..93ca50f3 100644 --- a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js @@ -6,41 +6,44 @@ import Moment from 'moment'; import StarRating from 'react-star-rating'; -import PieceActions from '../../../../../actions/piece_actions'; -import PieceStore from '../../../../../stores/piece_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 PieceListStore from '../../../../../../stores/piece_list_store'; +import PieceListActions from '../../../../../../actions/piece_list_actions'; import PrizeRatingActions from '../../actions/prize_rating_actions'; import PrizeRatingStore from '../../stores/prize_rating_store'; -import UserStore from '../../../../../stores/user_store'; +import UserStore from '../../../../../../stores/user_store'; +import UserActions from '../../../../../../actions/user_actions'; -import Piece from '../../../../../components/ascribe_detail/piece'; -import Note from '../../../../../components/ascribe_detail/note'; +import Piece from '../../../../../../components/ascribe_detail/piece'; +import Note from '../../../../../../components/ascribe_detail/note'; -import AscribeSpinner from '../../../../ascribe_spinner'; +import AscribeSpinner from '../../../../../ascribe_spinner'; -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 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 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 FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader'; -import GlobalNotificationModel from '../../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; +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 DetailProperty from '../../../../ascribe_detail/detail_property'; +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; -import ApiUrls from '../../../../../constants/api_urls'; -import { mergeOptions } from '../../../../../utils/general_utils'; -import { getLangText } from '../../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import DetailProperty from '../../../../../ascribe_detail/detail_property'; + +import ApiUrls from '../../../../../../constants/api_urls'; +import { mergeOptions } from '../../../../../../utils/general_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../../utils/dom_utils'; /** @@ -48,7 +51,8 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; */ let PieceContainer = React.createClass({ propTypes: { - params: React.PropTypes.object + params: React.PropTypes.object, + location: React.PropTypes.object }, getInitialState() { @@ -62,6 +66,7 @@ let PieceContainer = React.createClass({ PieceStore.listen(this.onChange); PieceActions.fetchOne(this.props.params.pieceId); UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); // 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 @@ -142,7 +147,7 @@ let PieceContainer = React.createClass({ -
+

{this.state.piece.title}

@@ -157,7 +162,7 @@ let PieceContainer = React.createClass({ piece={this.state.piece} currentUser={this.state.currentUser}/> }> - + ); } else { @@ -177,24 +182,28 @@ let NavigationHeader = React.createClass({ }, render() { - if (this.props.currentUser && this.props.currentUser.email && this.props.piece && this.props.piece.navigation) { - let nav = this.props.piece.navigation; + const { currentUser, piece } = this.props; + + if (currentUser && currentUser.email && currentUser.is_judge && currentUser.is_jury && + !currentUser.is_admin && piece && piece.navigation) { + let nav = piece.navigation; return (
- + - + {getLangText('Next')}
+
); } @@ -417,7 +426,8 @@ let PrizePieceRatings = React.createClass({ let PrizePieceDetails = React.createClass({ propTypes: { - piece: React.PropTypes.object + piece: React.PropTypes.object, + location: React.PropTypes.object }, render() { @@ -432,6 +442,8 @@ let PrizePieceDetails = React.createClass({
{Object.keys(this.props.piece.extra_data).map((data) => { let label = data.replace('_', ' '); + const value = this.props.piece.extra_data[data] || 'N/A'; + return ( - ); - } - )} -
+ defaultValue={value}/> + + ); + })} + {}} + setIsUploadReady={() => {}} + isReadyForFormSubmission={() => {}} + editable={false} + overrideForm={true} + pieceId={this.props.piece.id} + otherData={this.props.piece.other_data} + multiple={true} + location={location}/> ); diff --git a/js/components/whitelabel/prize/components/prize_hero.js b/js/components/whitelabel/prize/simple_prize/components/prize_hero.js similarity index 83% rename from js/components/whitelabel/prize/components/prize_hero.js rename to js/components/whitelabel/prize/simple_prize/components/prize_hero.js index b98f407e..8842acf9 100644 --- a/js/components/whitelabel/prize/components/prize_hero.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_hero.js @@ -1,7 +1,7 @@ 'use strict'; import React from 'react'; -import constants from '../../../../constants/application_constants'; +import constants from '../../../../../constants/application_constants'; let Hero = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_landing.js b/js/components/whitelabel/prize/simple_prize/components/prize_landing.js similarity index 93% rename from js/components/whitelabel/prize/components/prize_landing.js rename to js/components/whitelabel/prize/simple_prize/components/prize_landing.js index 355b3786..e26a05b5 100644 --- a/js/components/whitelabel/prize/components/prize_landing.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_landing.js @@ -11,11 +11,11 @@ import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; -import UserStore from '../../../../stores/user_store'; -import UserActions from '../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; -import { mergeOptions } from '../../../../utils/general_utils'; -import { getLangText } from '../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; let Landing = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_login_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_login_container.js similarity index 83% rename from js/components/whitelabel/prize/components/prize_login_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_login_container.js index 9a0de06d..e168ca68 100644 --- a/js/components/whitelabel/prize/components/prize_login_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_login_container.js @@ -3,10 +3,10 @@ import React from 'react'; import { Link } from 'react-router'; -import LoginForm from '../../../ascribe_forms/form_login'; +import LoginForm from '../../../../ascribe_forms/form_login'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let LoginContainer = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_piece_list.js b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js similarity index 86% rename from js/components/whitelabel/prize/components/prize_piece_list.js rename to js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js index 7a6a90ac..8e602012 100644 --- a/js/components/whitelabel/prize/components/prize_piece_list.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js @@ -1,10 +1,10 @@ 'use strict'; import React from 'react'; -import PieceList from '../../../piece_list'; +import PieceList from '../../../../piece_list'; -import UserActions from '../../../../actions/user_actions'; -import UserStore from '../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; import PrizeActions from '../actions/prize_actions'; import PrizeStore from '../stores/prize_store'; @@ -15,9 +15,9 @@ import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize'; -import { mergeOptions } from '../../../../utils/general_utils'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let PrizePieceList = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js new file mode 100644 index 00000000..ca4e8fa9 --- /dev/null +++ b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js @@ -0,0 +1,101 @@ +'use strict'; + +import React from 'react'; + +import PrizeActions from '../actions/prize_actions'; +import PrizeStore from '../stores/prize_store'; + +import RegisterPiece from '../../../../register_piece'; +import Property from '../../../../ascribe_forms/property'; +import InputTextAreaToggable from '../../../../ascribe_forms/input_textarea_toggable'; +import InputCheckbox from '../../../../ascribe_forms/input_checkbox'; + +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; + + +let PrizeRegisterPiece = React.createClass({ + propTypes: { + location: React.PropTypes.object + }, + + getInitialState() { + return PrizeStore.getState(); + }, + + componentDidMount() { + PrizeStore.listen(this.onChange); + PrizeActions.fetchPrize(); + }, + + componentWillUnmount() { + PrizeStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + const { location } = this.props; + + setDocumentTitle(getLangText('Submit to the prize')); + + if(this.state.prize && this.state.prize.active){ + return ( +
+ + + + + + + + + + + {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} + ( + {getLangText('read')} + ) + + + + +
+ ); + } + else { + return ( +
+
+ {getLangText('The prize is no longer active')} +
+
+ ); + } + } +}); + +export default PrizeRegisterPiece; diff --git a/js/components/whitelabel/prize/components/prize_settings_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js similarity index 91% rename from js/components/whitelabel/prize/components/prize_settings_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js index 81d62380..145a9d24 100644 --- a/js/components/whitelabel/prize/components/prize_settings_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js @@ -2,29 +2,29 @@ import React from 'react'; -import UserStore from '../../../../stores/user_store'; -import UserActions from '../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; import PrizeActions from '../actions/prize_actions'; import PrizeStore from '../stores/prize_store'; import PrizeJuryActions from '../actions/prize_jury_actions'; import PrizeJuryStore from '../stores/prize_jury_store'; -import SettingsContainer from '../../../ascribe_settings/settings_container'; -import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_paragraph'; +import SettingsContainer from '../../../../ascribe_settings/settings_container'; +import CollapsibleParagraph from '../../../../ascribe_collapsible/collapsible_paragraph'; -import Form from '../../../ascribe_forms/form'; -import Property from '../../../ascribe_forms/property'; +import Form from '../../../../ascribe_forms/form'; +import Property from '../../../../ascribe_forms/property'; -import ActionPanel from '../../../ascribe_panel/action_panel'; +import ActionPanel from '../../../../ascribe_panel/action_panel'; -import GlobalNotificationModel from '../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../actions/global_notification_actions'; +import GlobalNotificationModel from '../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; -import AscribeSpinner from '../../../ascribe_spinner'; -import ApiUrls from '../../../../constants/api_urls'; +import AscribeSpinner from '../../../../ascribe_spinner'; +import ApiUrls from '../../../../../constants/api_urls'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let Settings = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_signup_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js similarity index 86% rename from js/components/whitelabel/prize/components/prize_signup_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js index 884062da..7a44d521 100644 --- a/js/components/whitelabel/prize/components/prize_signup_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js @@ -1,10 +1,10 @@ 'use strict'; import React from 'react'; -import SignupForm from '../../../ascribe_forms/form_signup'; +import SignupForm from '../../../../ascribe_forms/form_signup'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let SignupContainer = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/fetchers/prize_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js similarity index 70% rename from js/components/whitelabel/prize/fetchers/prize_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js index 0bf9fc55..410d63a2 100644 --- a/js/components/whitelabel/prize/fetchers/prize_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeFetcher = { diff --git a/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js similarity index 88% rename from js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js index 1c5b0a0d..973107b4 100644 --- a/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeJuryFetcher = { diff --git a/js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js similarity index 90% rename from js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js index 33450dd6..38d0576e 100644 --- a/js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeRatingFetcher = { diff --git a/js/components/whitelabel/prize/prize_app.js b/js/components/whitelabel/prize/simple_prize/prize_app.js similarity index 87% rename from js/components/whitelabel/prize/prize_app.js rename to js/components/whitelabel/prize/simple_prize/prize_app.js index aadb0b05..d95d7772 100644 --- a/js/components/whitelabel/prize/prize_app.js +++ b/js/components/whitelabel/prize/simple_prize/prize_app.js @@ -2,11 +2,11 @@ import React from 'react'; import Hero from './components/prize_hero'; -import Header from '../../header'; -import Footer from '../../footer'; -import GlobalNotification from '../../global_notification'; +import Header from '../../../header'; +import Footer from '../../../footer'; +import GlobalNotification from '../../../global_notification'; -import { getSubdomain } from '../../../utils/general_utils'; +import { getSubdomain } from '../../../../utils/general_utils'; let PrizeApp = React.createClass({ diff --git a/js/components/whitelabel/prize/stores/prize_jury_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js similarity index 96% rename from js/components/whitelabel/prize/stores/prize_jury_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js index 69d73e3a..536b8633 100644 --- a/js/components/whitelabel/prize/stores/prize_jury_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeJuryActions from '../actions/prize_jury_actions'; diff --git a/js/components/whitelabel/prize/stores/prize_rating_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js similarity index 93% rename from js/components/whitelabel/prize/stores/prize_rating_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js index d67fa603..9f1552bb 100644 --- a/js/components/whitelabel/prize/stores/prize_rating_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeRatingActions from '../actions/prize_rating_actions'; diff --git a/js/components/whitelabel/prize/stores/prize_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_store.js similarity index 87% rename from js/components/whitelabel/prize/stores/prize_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_store.js index 68cc9264..8d9c4bbe 100644 --- a/js/components/whitelabel/prize/stores/prize_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeActions from '../actions/prize_actions'; diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index 2ff689fe..a07f29b1 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -14,6 +14,7 @@ let ApiUrls = { 'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/', 'blob_otherdatas': AppConstants.apiEndpoint + 'blob/otherdatas/', 'blob_contracts': AppConstants.apiEndpoint + 'blob/contracts/', + 'blob_thumbnails': AppConstants.apiEndpoint + 'blob/thumbnails/', 'coa': AppConstants.apiEndpoint + 'coa/${id}/', 'coa_create': AppConstants.apiEndpoint + 'coa/', 'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/', diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index 0fe5e210..b175c158 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -46,6 +46,13 @@ let constants = { 'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/ikonotv/ikono-logo-black.png', 'permissions': ['register', 'edit', 'share', 'del_from_collection'], 'type': 'wallet' + }, + { + 'subdomain': 'portfolioreview', + 'name': 'Portfolio Review', + 'logo': 'http://notfoundlogo.de', + 'permissions': ['register', 'edit', 'share', 'del_from_collection'], + 'type': 'prize' } ], 'defaultDomain': { diff --git a/js/utils/fetch_api_utils.js b/js/utils/fetch_api_utils.js index 3ed964ba..72b5b82b 100644 --- a/js/utils/fetch_api_utils.js +++ b/js/utils/fetch_api_utils.js @@ -1,6 +1,7 @@ 'use strict'; import Q from 'q'; +import moment from 'moment'; import { sanitize } from './general_utils'; import AppConstants from '../constants/application_constants'; @@ -70,12 +71,19 @@ export function getCookie(name) { let parts = document.cookie.split(';'); for(let i = 0; i < parts.length; i++) { - if(parts[i].indexOf(AppConstants.csrftoken + '=') > -1) { + if(parts[i].indexOf(name + '=') > -1) { return parts[i].split('=').pop(); } } } +export function setCookie(key, value, days) { + const exdate = moment(); + exdate.add(days, 'days'); + value = window.escape(value) + ((days === null) ? '' : `; expires= ${exdate.utc()}`); + document.cookie = `${key}=${value}`; +} + /* Given a url for an image, this method fetches it and returns a promise that resolves to diff --git a/sass/ascribe_custom_style.scss b/sass/ascribe_custom_style.scss index 05d39027..98cce937 100644 --- a/sass/ascribe_custom_style.scss +++ b/sass/ascribe_custom_style.scss @@ -508,7 +508,10 @@ fieldset[disabled] .btn-secondary.active { > pre, > select, > span:not(.glyphicon), + > p, + > p > span, > textarea { + color: $ascribe-dark-blue; font-family: $ascribe--font; font-weight: $ascribe--font-weight-light; } diff --git a/sass/ascribe_form.scss b/sass/ascribe_form.scss index 41e17de1..1b265e91 100644 --- a/sass/ascribe_form.scss +++ b/sass/ascribe_form.scss @@ -23,4 +23,4 @@ @media (max-width: 550px) { width: 100%; } -} +} \ No newline at end of file diff --git a/sass/ascribe_property.scss b/sass/ascribe_property.scss index e938a7c3..0bfd5496 100644 --- a/sass/ascribe_property.scss +++ b/sass/ascribe_property.scss @@ -30,11 +30,14 @@ $ascribe-red-error: rgb(169, 68, 66); border-left: 3px solid rgba($ascribe-red-error, 1); > div { - > span { - color: rgba($ascribe-red-error, 1); - font-size: .9em; - margin-right: 1em; + > p { + > span { + color: rgba($ascribe-red-error, 1); + font-size: .9em; + margin-right: 1em; + } } + > input, > textarea { @@ -86,10 +89,13 @@ $ascribe-red-error: rgb(169, 68, 66); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } - > span { - color: rgba(0, 0, 0, .5); - font-size: .9em; - font-weight: normal; + > p { + height: 20px; + margin-bottom: 0; + > span { + font-size: .9em; + font-weight: normal; + } } > div { @@ -107,6 +113,11 @@ $ascribe-red-error: rgb(169, 68, 66); margin-top: 0 !important; } + > .upload-button-wrapper { + margin-top: 1em; + margin-bottom: 1em; + } + > input, > pre, > textarea, @@ -139,7 +150,7 @@ $ascribe-red-error: rgb(169, 68, 66); padding: 0; } - > textarea{ + > textarea { color: #666; margin-top: 1em; padding: 0; @@ -218,4 +229,4 @@ $ascribe-red-error: rgb(169, 68, 66); > span > span { margin-top: 0; } -} +} \ No newline at end of file diff --git a/sass/ascribe_uploader.scss b/sass/ascribe_uploader.scss index 7819eda0..49bc70e9 100644 --- a/sass/ascribe_uploader.scss +++ b/sass/ascribe_uploader.scss @@ -177,3 +177,15 @@ height: 12px; } +.upload-button-wrapper { + text-align: left; + + .btn { + font-size: 1em; + margin-right: 1em; + } + + span + .btn { + margin-left: 1em; + } +} \ No newline at end of file diff --git a/sass/whitelabel/prize/index.scss b/sass/whitelabel/prize/index.scss index 5bc75746..24645c8a 100644 --- a/sass/whitelabel/prize/index.scss +++ b/sass/whitelabel/prize/index.scss @@ -1,4 +1,5 @@ -@import 'sluice/sluice_custom_style'; +@import 'simple_prize/simple_prize_custom_style'; +@import 'portfolioreview/portfolioreview_custom_style'; .ascribe-prize-app { border-radius: 0; diff --git a/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss new file mode 100644 index 00000000..d8ccd858 --- /dev/null +++ b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss @@ -0,0 +1,100 @@ +$pr--nav-fg-prim-color: black; +$pr--button-color: $pr--nav-fg-prim-color; + +.client--portfolioreview { + padding-top: 0 !important; + + .btn-wide, + .btn-default { + background-color: $pr--button-color; + border-color: $pr--button-color; + + &:hover, + &:active, + &:focus, + &:active:hover, + &:active:focus, + &:active.focus, + &.active:hover, + &.active:focus, + &.active.focus { + background-color: lighten($pr--button-color, 20%); + border-color: lighten($pr--button-color, 20%); + } + } + + .ascribe-property { + > p > span:not(> .span), + > textarea, + > input { + color: $pr--nav-fg-prim-color; + } + } + + .ascribe-property-wrapper:hover { + border-left-color: lighten($pr--nav-fg-prim-color, 60%); + } + + .is-focused { + border-left-color: $pr--nav-fg-prim-color !important; + background-color: lighten($pr--nav-fg-prim-color, 95%); + } + + .register-piece--info { + text-align: center; + + h1, h2 { + font-variant: small-caps; + } + + h1 { + font-size: 5em; + color: #757575; + } + + h2 { + font-size: 1.25em; + } + + p { + margin-bottom: 0; + } + + p + p { + margin-top: 0; + } + + p:last-child { + margin-bottom: 1.5em; + } + } + + .register-piece--form { + margin-top: 2em; + margin-bottom: 3em; + + form { + border-top: none; + border-bottom: none; + } + } + + .piece--hero { + text-align: center; + padding: 1em 0 1em 0; + margin-bottom: 3em; + border-bottom: 1px solid rgba(0, 0, 0, .1); + + background-color: white; + + h2 { + margin-top: 0; + } + } + + .ascribe-property { + > p > span:not(> .span) { + text-transform: capitalize; + } + } +} \ No newline at end of file diff --git a/sass/whitelabel/prize/sluice/sluice_custom_style.scss b/sass/whitelabel/prize/simple_prize/simple_prize_custom_style.scss similarity index 100% rename from sass/whitelabel/prize/sluice/sluice_custom_style.scss rename to sass/whitelabel/prize/simple_prize/simple_prize_custom_style.scss diff --git a/sass/whitelabel/wallet/ikonotv/ikonotv_custom_style.scss b/sass/whitelabel/wallet/ikonotv/ikonotv_custom_style.scss index 52affdaf..70a5cd18 100644 --- a/sass/whitelabel/wallet/ikonotv/ikonotv_custom_style.scss +++ b/sass/whitelabel/wallet/ikonotv/ikonotv_custom_style.scss @@ -178,7 +178,7 @@ $ikono--font: 'Helvetica Neue', 'Helvetica', sans-serif !important; border: none; } - .ascribe-property > span { + .ascribe-property > p > span { color: white; }