diff --git a/README.md b/README.md
index 7bca8c31..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
```
@@ -41,6 +43,7 @@ For this project, we're using:
* We don't use ES6's class declaration for React components because it does not support Mixins as well as Autobinding ([Blog post about it](http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding))
* We don't use camel case for file naming but in everything Javascript related
* We use `let` instead of `var`: [SA Post](http://stackoverflow.com/questions/762011/javascript-let-keyword-vs-var-keyword)
+* We don't use Javascript's `Date` object, as its interface introduced bugs previously and we're including `momentjs` for other dependencies anyways
Branch names
=====================
diff --git a/js/app.js b/js/app.js
index c9451e47..520bedbd 100644
--- a/js/app.js
+++ b/js/app.js
@@ -30,6 +30,7 @@ import GoogleAnalyticsHandler from './third_party/ga';
import RavenHandler from './third_party/raven';
import IntercomHandler from './third_party/intercom';
import NotificationsHandler from './third_party/notifications';
+import FacebookHandler from './third_party/facebook';
/* eslint-enable */
initLogging();
diff --git a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
index 185f6e05..da45d1e8 100644
--- a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
+++ b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js
@@ -1,6 +1,7 @@
'use strict';
import React from 'react';
+import Moment from 'moment';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
@@ -129,7 +130,7 @@ let AccordionListItemWallet = React.createClass({
piece={this.props.content}
subsubheading={
}
buttons={
diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js
index 1edf8b64..6a3df7b2 100644
--- a/js/components/ascribe_buttons/acls/acl_button.js
+++ b/js/components/ascribe_buttons/acls/acl_button.js
@@ -11,14 +11,8 @@ import ModalWrapper from '../../ascribe_modal/modal_wrapper';
import AppConstants from '../../../constants/application_constants';
-import GlobalNotificationModel from '../../../models/global_notification_model';
-import GlobalNotificationActions from '../../../actions/global_notification_actions';
-
-import ApiUrls from '../../../constants/api_urls';
import { AclInformationText } from '../../../constants/acl_information_text';
-import { getAclFormMessage, getAclFormDataId } from '../../../utils/form_utils';
-import { getLangText } from '../../../utils/lang_utils';
export default function ({ action, displayName, title, tooltip }) {
if (AppConstants.aclList.indexOf(action) < 0) {
diff --git a/js/components/ascribe_collapsible/collapsible_button.js b/js/components/ascribe_collapsible/collapsible_button.js
index 6fb39c71..caf89df3 100644
--- a/js/components/ascribe_collapsible/collapsible_button.js
+++ b/js/components/ascribe_collapsible/collapsible_button.js
@@ -21,13 +21,13 @@ let CollapsibleButton = React.createClass({
this.setState({expanded: !this.state.expanded});
},
render() {
- let isVisible = (this.state.expanded) ? '' : 'invisible';
+ let isHidden = (this.state.expanded) ? '' : 'hidden';
return (
{this.props.button}
-
+
{this.props.panel}
diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js
index 64cbf714..6b38ddf8 100644
--- a/js/components/ascribe_detail/edition.js
+++ b/js/components/ascribe_detail/edition.js
@@ -2,6 +2,7 @@
import React from 'react';
import { Link, History } from 'react-router';
+import Moment from 'moment';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
@@ -85,7 +86,7 @@ let Edition = React.createClass({
{this.props.edition.title}
-
+
+ multiple={this.props.multiple} />
);
}
});
-export default FurtherDetailsFileuploader;
\ No newline at end of file
+export default FurtherDetailsFileuploader;
diff --git a/js/components/ascribe_detail/media_container.js b/js/components/ascribe_detail/media_container.js
index 6ac2f745..c6845a44 100644
--- a/js/components/ascribe_detail/media_container.js
+++ b/js/components/ascribe_detail/media_container.js
@@ -7,10 +7,18 @@ import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import MediaPlayer from './../ascribe_media/media_player';
+import FacebookShareButton from '../ascribe_social_share/facebook_share_button';
+import TwitterShareButton from '../ascribe_social_share/twitter_share_button';
+
import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
import AclProxy from '../acl_proxy';
+import UserActions from '../../actions/user_actions';
+import UserStore from '../../stores/user_store';
+
+import { mergeOptions } from '../../utils/general_utils.js';
+import { getLangText } from '../../utils/lang_utils.js';
const EMBED_IFRAME_HEIGHT = {
video: 315,
@@ -24,10 +32,17 @@ let MediaContainer = React.createClass({
},
getInitialState() {
- return {timerId: null};
+ return mergeOptions(
+ UserStore.getState(),
+ {
+ timerId: null
+ });
},
componentDidMount() {
+ UserStore.listen(this.onChange);
+ UserActions.fetchCurrentUser();
+
if (!this.props.content.digital_work) {
return;
}
@@ -45,19 +60,32 @@ let MediaContainer = React.createClass({
},
componentWillUnmount() {
+ UserStore.unlisten(this.onChange);
+
window.clearInterval(this.state.timerId);
},
+ onChange(state) {
+ this.setState(state);
+ },
+
render() {
- let thumbnail = this.props.content.thumbnail.thumbnail_sizes && this.props.content.thumbnail.thumbnail_sizes['600x600'] ?
- this.props.content.thumbnail.thumbnail_sizes['600x600'] : this.props.content.thumbnail.url_safe;
- let mimetype = this.props.content.digital_work.mime;
+ const { content } = this.props;
+ // Pieces and editions are joined to the user by a foreign key in the database, so
+ // the information in content will be updated if a user updates their username.
+ // We also force uniqueness of usernames, so this check is safe to dtermine if the
+ // content was registered by the current user.
+ const didUserRegisterContent = this.state.currentUser && (this.state.currentUser.username === content.user_registered);
+
+ let thumbnail = content.thumbnail.thumbnail_sizes && content.thumbnail.thumbnail_sizes['600x600'] ?
+ content.thumbnail.thumbnail_sizes['600x600'] : content.thumbnail.url_safe;
+ let mimetype = content.digital_work.mime;
let embed = null;
let extraData = null;
- let isEmbedDisabled = mimetype === 'video' && this.props.content.digital_work.isEncoding !== undefined && this.props.content.digital_work.isEncoding !== 100;
+ let isEmbedDisabled = mimetype === 'video' && content.digital_work.isEncoding !== undefined && content.digital_work.isEncoding !== 100;
- if (this.props.content.digital_work.encoding_urls) {
- extraData = this.props.content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
+ if (content.digital_work.encoding_urls) {
+ extraData = content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
}
if (['video', 'audio'].indexOf(mimetype) > -1) {
@@ -73,7 +101,7 @@ let MediaContainer = React.createClass({
panel={
+
+
+
+
+
{embed}
diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js
index 8a7801ec..59fbfe5c 100644
--- a/js/components/ascribe_detail/piece_container.js
+++ b/js/components/ascribe_detail/piece_container.js
@@ -2,6 +2,7 @@
import React from 'react';
import { History } from 'react-router';
+import Moment from 'moment';
import PieceActions from '../../actions/piece_actions';
import PieceStore from '../../stores/piece_store';
@@ -236,7 +237,7 @@ let PieceContainer = React.createClass({
{this.state.piece.title}
-
+
{this.state.piece.num_editions > 0 ? : null}
diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js
index fe15f537..0e3517a2 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 { sanitize } from '../../utils/general_utils';
let Form = React.createClass({
@@ -124,12 +124,12 @@ let Form = React.createClass({
getFormData() {
let data = {};
- for(let ref in this.refs) {
+ for (let ref in this.refs) {
data[this.refs[ref].props.name] = this.refs[ref].state.value;
}
- if(typeof this.props.getFormData === 'function') {
- data = mergeOptionsWithDuplicates(data, this.props.getFormData());
+ if (typeof this.props.getFormData === 'function') {
+ data = Object.assign(data, this.props.getFormData());
}
return data;
@@ -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/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js
index fe00cebc..aac4c5ea 100644
--- a/js/components/ascribe_forms/form_create_contract.js
+++ b/js/components/ascribe_forms/form_create_contract.js
@@ -28,8 +28,7 @@ let CreateContractForm = React.createClass({
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
- }),
- location: React.PropTypes.object
+ })
},
getInitialState() {
@@ -87,8 +86,7 @@ let CreateContractForm = React.createClass({
areAssetsEditable={true}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
- fileClassToUpload={this.props.fileClassToUpload}
- location={this.props.location}/>
+ fileClassToUpload={this.props.fileClassToUpload} />
+ uploadMethod={this.props.location.query.method} />
+ uploadMethod={this.props.uploadMethod}
+ fileClassToUpload={this.props.fileClassToUpload} />
);
}
});
-export default InputFineUploader;
\ No newline at end of file
+export default InputFineUploader;
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..2a23f2ed 100644
--- a/js/components/ascribe_media/media_player.js
+++ b/js/components/ascribe_media/media_player.js
@@ -3,12 +3,13 @@
import React from 'react';
import Q from 'q';
-import { escapeHTML } from '../../utils/general_utils';
-
-import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
import Panel from 'react-bootstrap/lib/Panel';
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
-import AppConstants from '../../constants/application_constants.js';
+
+import AppConstants from '../../constants/application_constants';
+
+import { escapeHTML } from '../../utils/general_utils';
+import { InjectInHeadUtils } from '../../utils/inject_utils';
/**
* This is the component that implements display-specific functionality.
@@ -50,25 +51,33 @@ 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) {
+ InjectInHeadUtils.inject(AppConstants.jquery.sdkUrl)
+ .then(() =>
+ Q.all([
+ InjectInHeadUtils.inject(AppConstants.shmui.cssUrl),
+ InjectInHeadUtils.inject(AppConstants.shmui.sdkUrl)
+ ]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
+ }
},
render() {
- return (
-
- );
+ const { url, preview } = this.props;
+
+ if(url) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
}
});
@@ -77,10 +86,8 @@ let Audio = React.createClass({
url: React.PropTypes.string.isRequired
},
- mixins: [InjectInHeadMixin],
-
componentDidMount() {
- this.inject(AppConstants.baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js').then(this.ready);
+ InjectInHeadUtils.inject(AppConstants.audiojs.sdkUrl).then(this.ready);
},
ready() {
@@ -111,7 +118,7 @@ let Video = React.createClass({
* `false` if we failed to load the external library)
* 2) render the cover using the `` component (because libraryLoaded is null)
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
- * the `InjectInHeadMixin`, attaching a function to `Promise.then` to change
+ * the `InjectInHeadUtils`, attaching a function to `Promise.then` to change
* `state.libraryLoaded` to true
* 4) when the promise is succesfully resolved, we change `state.libraryLoaded` triggering
* a re-render
@@ -129,20 +136,22 @@ let Video = React.createClass({
encodingStatus: React.PropTypes.number
},
- mixins: [InjectInHeadMixin],
-
getInitialState() {
return { libraryLoaded: null, videoMounted: false };
},
componentDidMount() {
Q.all([
- this.inject('//vjs.zencdn.net/4.12/video-js.css'),
- this.inject('//vjs.zencdn.net/4.12/video.js')])
+ InjectInHeadUtils.inject(AppConstants.videojs.cssUrl),
+ InjectInHeadUtils.inject(AppConstants.videojs.sdkUrl)])
.then(() => this.setState({libraryLoaded: true}))
.fail(() => this.setState({libraryLoaded: false}));
},
+ shouldComponentUpdate(nextProps, nextState) {
+ return nextState.videoMounted === false;
+ },
+
componentDidUpdate() {
if (this.state.libraryLoaded && !this.state.videoMounted) {
window.videojs('#mainvideo');
@@ -168,10 +177,6 @@ let Video = React.createClass({
return html.join('\n');
},
- shouldComponentUpdate(nextProps, nextState) {
- return nextState.videoMounted === false;
- },
-
render() {
if (this.state.libraryLoaded !== null) {
return (
@@ -202,26 +207,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..aa13264a 100644
--- a/js/components/ascribe_settings/contract_settings_update_button.js
+++ b/js/components/ascribe_settings/contract_settings_update_button.js
@@ -20,8 +20,7 @@ import { getLangText } from '../../utils/lang_utils';
let ContractSettingsUpdateButton = React.createClass({
propTypes: {
- contract: React.PropTypes.object,
- location: React.PropTypes.object
+ contract: React.PropTypes.object
},
submitFile(file) {
@@ -56,7 +55,6 @@ let ContractSettingsUpdateButton = React.createClass({
render() {
return (
+ submitFile={this.submitFile} />
);
}
});
-export default ContractSettingsUpdateButton;
\ No newline at end of file
+export default ContractSettingsUpdateButton;
diff --git a/js/components/ascribe_social_share/facebook_share_button.js b/js/components/ascribe_social_share/facebook_share_button.js
new file mode 100644
index 00000000..87a2aef6
--- /dev/null
+++ b/js/components/ascribe_social_share/facebook_share_button.js
@@ -0,0 +1,51 @@
+'use strict';
+
+import React from 'react';
+
+import AppConstants from '../../constants/application_constants';
+
+import { InjectInHeadUtils } from '../../utils/inject_utils';
+
+let FacebookShareButton = React.createClass({
+ propTypes: {
+ url: React.PropTypes.string,
+ type: React.PropTypes.string
+ },
+
+ getDefaultProps() {
+ return {
+ type: 'button'
+ };
+ },
+
+ componentDidMount() {
+ /**
+ * Ideally we would only use FB.XFBML.parse() on the component that we're
+ * mounting, but doing this when we first load the FB sdk causes unpredictable behaviour.
+ * The button sometimes doesn't get initialized, likely because FB hasn't properly
+ * been initialized yet.
+ *
+ * To circumvent this, we always have the sdk parse the entire DOM on the initial load
+ * (see FacebookHandler) and then use FB.XFBML.parse() on the mounting component later.
+ */
+ if (!InjectInHeadUtils.isPresent('script', AppConstants.facebook.sdkUrl)) {
+ InjectInHeadUtils.inject(AppConstants.facebook.sdkUrl);
+ } else {
+ // Parse() searches the children of the element we give it, not the element itself.
+ FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parentElement);
+ }
+ },
+
+ render() {
+ return (
+
+
+ );
+ }
+});
+
+export default FacebookShareButton;
diff --git a/js/components/ascribe_social_share/twitter_share_button.js b/js/components/ascribe_social_share/twitter_share_button.js
new file mode 100644
index 00000000..b2e8a7dc
--- /dev/null
+++ b/js/components/ascribe_social_share/twitter_share_button.js
@@ -0,0 +1,55 @@
+'use strict';
+
+import React from 'react';
+
+import AppConstants from '../../constants/application_constants';
+
+import { InjectInHeadUtils } from '../../utils/inject_utils';
+
+let TwitterShareButton = React.createClass({
+ propTypes: {
+ count: React.PropTypes.string,
+ counturl: React.PropTypes.string,
+ hashtags: React.PropTypes.string,
+ size: React.PropTypes.string,
+ text: React.PropTypes.string,
+ url: React.PropTypes.string,
+ via: React.PropTypes.string
+ },
+
+ getDefaultProps() {
+ return {
+ count: 'none',
+ via: 'ascribeIO'
+ };
+ },
+
+ componentDidMount() {
+ InjectInHeadUtils.inject(AppConstants.twitter.sdkUrl).then(this.loadTwitterButton);
+ },
+
+ loadTwitterButton() {
+ const { count, counturl, hashtags, size, text, url, via } = this.props;
+
+ twttr.widgets.createShareButton(url, this.refs.twitterShareButton.getDOMNode(), {
+ count,
+ counturl,
+ hashtags,
+ size,
+ text,
+ via,
+ dnt: true // Do not track
+ });
+ },
+
+ render() {
+ return (
+
+
+ );
+ }
+});
+
+export default TwitterShareButton;
diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
index 38ec459a..0cc7ff5e 100644
--- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
+++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
@@ -27,6 +27,7 @@ let FileDragAndDrop = React.createClass({
areAssetsEditable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
+ uploadMethod: React.PropTypes.string,
// triggers a FileDragAndDrop-global spinner
hashingProgress: React.PropTypes.number,
@@ -41,8 +42,11 @@ let FileDragAndDrop = React.createClass({
plural: React.PropTypes.string
}),
- allowedExtensions: React.PropTypes.string,
- location: React.PropTypes.object
+ allowedExtensions: React.PropTypes.string
+ },
+
+ clearSelection() {
+ this.refs.fileSelector.getDOMNode().value = '';
},
handleDragOver(event) {
@@ -81,30 +85,30 @@ let FileDragAndDrop = React.createClass({
},
handleDeleteFile(fileId) {
- // input's value is not change the second time someone
+ // input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
- this.refs.fileinput.getDOMNode().value = '';
+ this.clearSelection();
this.props.handleDeleteFile(fileId);
},
handleCancelFile(fileId) {
- // input's value is not change the second time someone
+ // input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
- this.refs.fileinput.getDOMNode().value = '';
+ this.clearSelection();
this.props.handleCancelFile(fileId);
},
handlePauseFile(fileId) {
- // input's value is not change the second time someone
+ // input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
- this.refs.fileinput.getDOMNode().value = '';
+ this.clearSelection();
this.props.handlePauseFile(fileId);
},
handleResumeFile(fileId) {
- // input's value is not change the second time someone
+ // input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
- this.refs.fileinput.getDOMNode().value = '';
+ this.clearSelection();
this.props.handleResumeFile(fileId);
},
@@ -133,23 +137,23 @@ let FileDragAndDrop = React.createClass({
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
}
- this.refs.fileinput.getDOMNode().dispatchEvent(evt);
+ this.refs.fileSelector.getDOMNode().dispatchEvent(evt);
},
render: function () {
- let { filesToUpload,
- dropzoneInactive,
- className,
- hashingProgress,
- handleCancelHashing,
- multiple,
- enableLocalHashing,
- fileClassToUpload,
- areAssetsDownloadable,
- areAssetsEditable,
- allowedExtensions,
- location
- } = this.props;
+ const {
+ filesToUpload,
+ dropzoneInactive,
+ className,
+ hashingProgress,
+ handleCancelHashing,
+ multiple,
+ enableLocalHashing,
+ uploadMethod,
+ fileClassToUpload,
+ areAssetsDownloadable,
+ areAssetsEditable,
+ allowedExtensions } = this.props;
// has files only is true if there are files that do not have the status deleted or canceled
let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0;
@@ -185,8 +189,8 @@ let FileDragAndDrop = React.createClass({
hasFiles={hasFiles}
onClick={this.handleOnClick}
enableLocalHashing={enableLocalHashing}
- fileClassToUpload={fileClassToUpload}
- location={location}/>
+ uploadMethod={uploadMethod}
+ fileClassToUpload={fileClassToUpload} />
{getLangText('Drag %s here', fileClass)},
{getLangText('Hash your work')}
@@ -64,9 +67,9 @@ let FileDragAndDropDialog = React.createClass({
or
-
+
{getLangText('Upload and hash your work')}
@@ -75,26 +78,27 @@ let FileDragAndDropDialog = React.createClass({
);
} else {
- if(this.props.multipleFiles) {
+ if (multipleFiles) {
return (
- {this.getDragDialog(this.props.fileClassToUpload.plural)}
+ {this.getDragDialog(fileClassToUpload.plural)}
- {getLangText('choose %s to upload', this.props.fileClassToUpload.plural)}
+ onClick={onClick}>
+ {getLangText('choose %s to upload', fileClassToUpload.plural)}
);
} else {
- let dialog = queryParams.method === 'hash' ? getLangText('choose a %s to hash', this.props.fileClassToUpload.singular) : getLangText('choose a %s to upload', this.props.fileClassToUpload.singular);
+ const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
+ : getLangText('choose a %s to upload', fileClassToUpload.singular);
return (
- {this.getDragDialog(this.props.fileClassToUpload.singular)}
+ {this.getDragDialog(fileClassToUpload.singular)}
+ onClick={onClick}>
{dialog}
@@ -105,4 +109,4 @@ let FileDragAndDropDialog = React.createClass({
}
});
-export default FileDragAndDropDialog;
\ No newline at end of file
+export default FileDragAndDropDialog;
diff --git a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js
index 1547272e..252adabb 100644
--- a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js
+++ b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js
@@ -2,24 +2,30 @@
import React from 'react';
+import Glyphicon from 'react-bootstrap/lib/Glyphicon';
+
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang_utils';
+import { truncateTextAtCharIndex } from '../../../utils/general_utils';
+const { func, array, bool, shape, string } = React.PropTypes;
let UploadButton = React.createClass({
propTypes: {
- onDrop: React.PropTypes.func.isRequired,
- filesToUpload: React.PropTypes.array,
- multiple: React.PropTypes.bool,
+ onDrop: func.isRequired,
+ filesToUpload: array,
+ multiple: bool,
// For simplification purposes we're just going to use this prop as a
// label for the upload button
- fileClassToUpload: React.PropTypes.shape({
- singular: React.PropTypes.string,
- plural: React.PropTypes.string
+ fileClassToUpload: shape({
+ singular: string,
+ plural: string
}),
- allowedExtensions: React.PropTypes.string
+ allowedExtensions: string,
+
+ handleCancelFile: func // provided by ReactS3FineUploader
},
handleDrop(event) {
@@ -37,11 +43,20 @@ let UploadButton = React.createClass({
return this.props.filesToUpload.filter((file) => 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 (
-
+
}
buttons={this.getPrizeButtons()}
badge={this.getPrizeBadge()}>
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 f2e22412..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,10 +147,10 @@ let PieceContainer = React.createClass({
-
+
{this.state.piece.title}
-
+
{artistEmail}
{this.getActions()}
@@ -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 (
);
}
@@ -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({
+ );
+ })}
+ {}}
+ 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/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js b/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js
index 5644b5b0..b263e517 100644
--- a/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js
+++ b/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js
@@ -1,6 +1,7 @@
'use strict';
import React from 'react';
+import Moment from 'moment';
import Piece from '../../../../../components/ascribe_detail/piece';
@@ -39,7 +40,7 @@ let WalletPieceContainer = React.createClass({
{this.props.piece.title}
-
+
}
diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js b/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js
index 755e550b..9802e93e 100644
--- a/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js
+++ b/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js
@@ -1,6 +1,7 @@
'use strict';
import React from 'react';
+import Moment from 'moment';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
@@ -100,7 +101,7 @@ let CylandAccordionListItem = React.createClass({
piece={this.props.content}
subsubheading={