mirror of
https://github.com/ascribe/onion.git
synced 2024-11-15 09:35:10 +01:00
Merge branch 'master' into AD-1149-implement-lumenus-the-lumen-mark
Conflicts: js/constants/application_constants.js js/utils/requests.js
This commit is contained in:
commit
ac07107210
20
README.md
20
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 cyland.localhost.com
|
||||||
127.0.0.1 ikonotv.localhost.com
|
127.0.0.1 ikonotv.localhost.com
|
||||||
127.0.0.1 sluice.localhost.com
|
127.0.0.1 sluice.localhost.com
|
||||||
|
127.0.0.1 lumenus.localhost.com
|
||||||
|
127.0.0.1 portfolioreview.localhost.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +43,25 @@ 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 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 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 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
|
||||||
|
=====================
|
||||||
|
Since we moved to Github, we cannot create branch names automatically with JIRA anymore.
|
||||||
|
To not lose context, but still be able to switch branches quickly using a ticket's number, we're recommending the following rules when naming our branches in onion.
|
||||||
|
|
||||||
|
```
|
||||||
|
AD-<JIRA-ticket-id>-brief-and-sane-description-of-the-ticket
|
||||||
|
```
|
||||||
|
|
||||||
|
where `brief-and-sane-description-of-the-ticket` does not need to equal to the ticket's title.
|
||||||
|
This allows JIRA to still track branches and pull-requests while allowing us to keep our peace of mind.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------------
|
||||||
|
**JIRA ticket name:** `AD-1242 - Frontend caching for simple endpoints to measure perceived page load <more useless information>`
|
||||||
|
|
||||||
|
**Github branch name:** `AD-1242-caching-solution-for-stores`
|
||||||
|
|
||||||
SCSS Code Conventions
|
SCSS Code Conventions
|
||||||
=====================
|
=====================
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
@ -129,7 +130,7 @@ let AccordionListItemWallet = React.createClass({
|
|||||||
piece={this.props.content}
|
piece={this.props.content}
|
||||||
subsubheading={
|
subsubheading={
|
||||||
<div className="pull-left">
|
<div className="pull-left">
|
||||||
<span>{new Date(this.props.content.date_created).getFullYear()}</span>
|
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span>
|
||||||
{this.getLicences()}
|
{this.getLicences()}
|
||||||
</div>}
|
</div>}
|
||||||
buttons={
|
buttons={
|
||||||
|
@ -11,13 +11,6 @@ import ModalWrapper from '../../ascribe_modal/modal_wrapper';
|
|||||||
|
|
||||||
import AppConstants from '../../../constants/application_constants';
|
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 { getAclFormMessage, getAclFormDataId } from '../../../utils/form_utils';
|
|
||||||
import { getLangText } from '../../../utils/lang_utils';
|
|
||||||
|
|
||||||
export default function ({ action, displayName, title, tooltip }) {
|
export default function ({ action, displayName, title, tooltip }) {
|
||||||
if (AppConstants.aclList.indexOf(action) < 0) {
|
if (AppConstants.aclList.indexOf(action) < 0) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, History } from 'react-router';
|
import { Link, History } from 'react-router';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import Row from 'react-bootstrap/lib/Row';
|
import Row from 'react-bootstrap/lib/Row';
|
||||||
import Col from 'react-bootstrap/lib/Col';
|
import Col from 'react-bootstrap/lib/Col';
|
||||||
@ -95,7 +96,7 @@ let Edition = React.createClass({
|
|||||||
<hr style={{marginTop: 0}}/>
|
<hr style={{marginTop: 0}}/>
|
||||||
<h1 className="ascribe-detail-title">{this.props.edition.title}</h1>
|
<h1 className="ascribe-detail-title">{this.props.edition.title}</h1>
|
||||||
<EditionDetailProperty label="BY" value={this.props.edition.artist_name} />
|
<EditionDetailProperty label="BY" value={this.props.edition.artist_name} />
|
||||||
<EditionDetailProperty label="DATE" value={ new Date(this.props.edition.date_created).getFullYear() } />
|
<EditionDetailProperty label="DATE" value={Moment(this.props.edition.date_created, 'YYYY-MM-DD').year()} />
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
<EditionSummary
|
<EditionSummary
|
||||||
|
@ -92,7 +92,7 @@ let MediaContainer = React.createClass({
|
|||||||
aclObject={this.props.content.acl}
|
aclObject={this.props.content.acl}
|
||||||
aclName="acl_download">
|
aclName="acl_download">
|
||||||
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
||||||
Download <Glyphicon glyph="cloud-download"/>
|
Download .{mimetype} <Glyphicon glyph="cloud-download"/>
|
||||||
</Button>
|
</Button>
|
||||||
</AclProxy>
|
</AclProxy>
|
||||||
{embed}
|
{embed}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { History } from 'react-router';
|
import { History } from 'react-router';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import PieceActions from '../../actions/piece_actions';
|
import PieceActions from '../../actions/piece_actions';
|
||||||
import PieceStore from '../../stores/piece_store';
|
import PieceStore from '../../stores/piece_store';
|
||||||
@ -244,7 +245,7 @@ let PieceContainer = React.createClass({
|
|||||||
<hr style={{marginTop: 0}}/>
|
<hr style={{marginTop: 0}}/>
|
||||||
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||||
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
||||||
<DetailProperty label="DATE" value={ new Date(this.state.piece.date_created).getFullYear() } />
|
<DetailProperty label="DATE" value={Moment(this.state.piece.date_created, 'YYYY-MM-DD').year() } />
|
||||||
{this.state.piece.num_editions > 0 ? <DetailProperty label="EDITIONS" value={ this.state.piece.num_editions } /> : null}
|
{this.state.piece.num_editions > 0 ? <DetailProperty label="EDITIONS" value={ this.state.piece.num_editions } /> : null}
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,8 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
|
|||||||
import requests from '../../utils/requests';
|
import requests from '../../utils/requests';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
import { sanitize } from '../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let Form = React.createClass({
|
let Form = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -153,7 +155,7 @@ let Form = React.createClass({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleError(err){
|
handleError(err) {
|
||||||
if (err.json) {
|
if (err.json) {
|
||||||
for (let input in err.json.errors){
|
for (let input in err.json.errors){
|
||||||
if (this.refs && this.refs[input] && this.refs[input].state) {
|
if (this.refs && this.refs[input] && this.refs[input].state) {
|
||||||
@ -183,7 +185,7 @@ let Form = React.createClass({
|
|||||||
this.setState({submitted: false});
|
this.setState({submitted: false});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors(){
|
clearErrors() {
|
||||||
for(let ref in this.refs){
|
for(let ref in this.refs){
|
||||||
if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){
|
if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){
|
||||||
this.refs[ref].clearErrors();
|
this.refs[ref].clearErrors();
|
||||||
@ -234,12 +236,12 @@ let Form = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderChildren() {
|
renderChildren() {
|
||||||
return ReactAddons.Children.map(this.props.children, (child) => {
|
return ReactAddons.Children.map(this.props.children, (child, i) => {
|
||||||
if (child) {
|
if (child) {
|
||||||
return ReactAddons.addons.cloneWithProps(child, {
|
return ReactAddons.addons.cloneWithProps(child, {
|
||||||
handleChange: this.handleChangeChild,
|
handleChange: this.handleChangeChild,
|
||||||
ref: child.props.name,
|
ref: child.props.name,
|
||||||
|
key: i,
|
||||||
// We need this in order to make editable be overridable when setting it directly
|
// We need this in order to make editable be overridable when setting it directly
|
||||||
// on Property
|
// on Property
|
||||||
editable: child.props.overrideForm ? child.props.editable : !this.props.disabled
|
editable: child.props.overrideForm ? child.props.editable : !this.props.disabled
|
||||||
@ -267,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() {
|
render() {
|
||||||
let className = 'ascribe-form';
|
let className = 'ascribe-form';
|
||||||
|
|
||||||
|
@ -3,64 +3,80 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
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 AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getCookie } from '../../utils/fetch_api_utils';
|
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: {
|
propTypes: {
|
||||||
setIsUploadReady: React.PropTypes.func,
|
setIsUploadReady: func,
|
||||||
isReadyForFormSubmission: React.PropTypes.func,
|
isReadyForFormSubmission: func,
|
||||||
submitFileName: React.PropTypes.func,
|
submitFileName: func,
|
||||||
|
fileInputElement: func,
|
||||||
|
|
||||||
areAssetsDownloadable: React.PropTypes.bool,
|
areAssetsDownloadable: bool,
|
||||||
|
|
||||||
onClick: React.PropTypes.func,
|
keyRoutine: shape({
|
||||||
keyRoutine: React.PropTypes.shape({
|
url: string,
|
||||||
url: React.PropTypes.string,
|
fileClass: string
|
||||||
fileClass: React.PropTypes.string
|
|
||||||
}),
|
}),
|
||||||
createBlobRoutine: React.PropTypes.shape({
|
createBlobRoutine: shape({
|
||||||
url: React.PropTypes.string
|
url: string
|
||||||
}),
|
}),
|
||||||
validation: React.PropTypes.shape({
|
validation: shape({
|
||||||
itemLimit: React.PropTypes.number,
|
itemLimit: number,
|
||||||
sizeLimit: React.PropTypes.string,
|
sizeLimit: string,
|
||||||
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string)
|
allowedExtensions: arrayOf(string)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// isFineUploaderActive is used to lock react fine uploader in case
|
// isFineUploaderActive is used to lock react fine uploader in case
|
||||||
// a user is actually not logged in already to prevent him from droping files
|
// a user is actually not logged in already to prevent him from droping files
|
||||||
// before login in
|
// before login in
|
||||||
isFineUploaderActive: React.PropTypes.bool,
|
isFineUploaderActive: bool,
|
||||||
onLoggedOut: React.PropTypes.func,
|
onLoggedOut: func,
|
||||||
|
|
||||||
enableLocalHashing: React.PropTypes.bool,
|
enableLocalHashing: bool,
|
||||||
uploadMethod: React.PropTypes.string,
|
uploadMethod: string,
|
||||||
|
|
||||||
// provided by Property
|
// provided by Property
|
||||||
disabled: React.PropTypes.bool,
|
disabled: bool,
|
||||||
|
|
||||||
// A class of a file the user has to upload
|
// A class of a file the user has to upload
|
||||||
// Needs to be defined both in singular as well as in plural
|
// Needs to be defined both in singular as well as in plural
|
||||||
fileClassToUpload: React.PropTypes.shape({
|
fileClassToUpload: shape({
|
||||||
singular: React.PropTypes.string,
|
singular: string,
|
||||||
plural: React.PropTypes.string
|
plural: string
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
fileInputElement: FileDragAndDrop
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
value: null
|
value: null,
|
||||||
|
file: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
submitFile(file){
|
submitFile(file) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
file,
|
||||||
value: file.key
|
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') {
|
if(typeof this.props.submitFileName === 'function') {
|
||||||
this.props.submitFileName(file.originalName);
|
this.props.submitFileName(file.originalName);
|
||||||
}
|
}
|
||||||
@ -70,7 +86,25 @@ let InputFineUploader = React.createClass({
|
|||||||
this.refs.fineuploader.reset();
|
this.refs.fineuploader.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createBlobRoutine() {
|
||||||
|
const { fineuploader } = this.refs;
|
||||||
|
const { file } = this.state;
|
||||||
|
|
||||||
|
fineuploader.createBlob(file);
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { fileInputElement,
|
||||||
|
keyRoutine,
|
||||||
|
createBlobRoutine,
|
||||||
|
validation,
|
||||||
|
setIsUploadReady,
|
||||||
|
isReadyForFormSubmission,
|
||||||
|
areAssetsDownloadable,
|
||||||
|
onLoggedOut,
|
||||||
|
enableLocalHashing,
|
||||||
|
fileClassToUpload,
|
||||||
|
location } = this.props;
|
||||||
let editable = this.props.isFineUploaderActive;
|
let editable = this.props.isFineUploaderActive;
|
||||||
|
|
||||||
// if disabled is actually set by property, we want to override
|
// if disabled is actually set by property, we want to override
|
||||||
@ -82,14 +116,14 @@ let InputFineUploader = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<ReactS3FineUploader
|
<ReactS3FineUploader
|
||||||
ref="fineuploader"
|
ref="fineuploader"
|
||||||
onClick={this.props.onClick}
|
fileInputElement={fileInputElement}
|
||||||
keyRoutine={this.props.keyRoutine}
|
keyRoutine={keyRoutine}
|
||||||
createBlobRoutine={this.props.createBlobRoutine}
|
createBlobRoutine={createBlobRoutine}
|
||||||
validation={this.props.validation}
|
validation={validation}
|
||||||
submitFile={this.submitFile}
|
submitFile={this.submitFile}
|
||||||
setIsUploadReady={this.props.setIsUploadReady}
|
setIsUploadReady={setIsUploadReady}
|
||||||
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
isReadyForFormSubmission={isReadyForFormSubmission}
|
||||||
areAssetsDownloadable={this.props.areAssetsDownloadable}
|
areAssetsDownloadable={areAssetsDownloadable}
|
||||||
areAssetsEditable={editable}
|
areAssetsEditable={editable}
|
||||||
signature={{
|
signature={{
|
||||||
endpoint: AppConstants.serverUrl + 's3/signature/',
|
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||||
|
@ -191,9 +191,7 @@ let Property = React.createClass({
|
|||||||
|
|
||||||
setErrors(errors){
|
setErrors(errors){
|
||||||
this.setState({
|
this.setState({
|
||||||
errors: errors.map((error) => {
|
errors: errors.pop()
|
||||||
return <span className="pull-right" key={error}>{error}</span>;
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -265,8 +263,10 @@ let Property = React.createClass({
|
|||||||
placement="top"
|
placement="top"
|
||||||
overlay={tooltip}>
|
overlay={tooltip}>
|
||||||
<div className={'ascribe-property ' + this.props.className}>
|
<div className={'ascribe-property ' + this.props.className}>
|
||||||
{this.state.errors}
|
<p>
|
||||||
<span>{this.props.label}</span>
|
<span className="pull-left">{this.props.label}</span>
|
||||||
|
<span className="pull-right">{this.state.errors}</span>
|
||||||
|
</p>
|
||||||
{this.renderChildren(style)}
|
{this.renderChildren(style)}
|
||||||
{footer}
|
{footer}
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,25 +50,35 @@ let Other = React.createClass({
|
|||||||
|
|
||||||
let Image = React.createClass({
|
let Image = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
url: React.PropTypes.string.isRequired,
|
url: React.PropTypes.string,
|
||||||
preview: React.PropTypes.string.isRequired
|
preview: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [InjectInHeadMixin],
|
mixins: [InjectInHeadMixin],
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.inject('https://code.jquery.com/jquery-2.1.4.min.js')
|
if(this.props.url) {
|
||||||
.then(() =>
|
this.inject('https://code.jquery.com/jquery-2.1.4.min.js')
|
||||||
Q.all([
|
.then(() =>
|
||||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'),
|
Q.all([
|
||||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js')
|
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'),
|
||||||
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
|
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js')
|
||||||
|
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
const { url, preview } = this.props;
|
||||||
<img className="shmui-ascribe" src={this.props.preview} data-large-src={this.props.url}/>
|
|
||||||
);
|
if(url) {
|
||||||
|
return (
|
||||||
|
<img className="shmui-ascribe" src={preview} data-large-src={url}/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<img src={preview}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -202,26 +212,50 @@ let MediaPlayer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
<div className="ascribe-detail-header ascribe-media-player">
|
<div className="ascribe-detail-header ascribe-media-player">
|
||||||
<p>
|
<p>
|
||||||
<em>We successfully received your video and it is now being encoded.
|
<em>We successfully received your video and it is now being encoded.
|
||||||
<br />You can leave this page and check back on the status later.</em>
|
<br />You can leave this page and check back on the status later.</em>
|
||||||
</p>
|
</p>
|
||||||
<ProgressBar now={this.props.encodingStatus}
|
<ProgressBar now={encodingStatus}
|
||||||
label="%(percent)s%"
|
label="%(percent)s%"
|
||||||
className="ascribe-progress-bar" />
|
className="ascribe-progress-bar" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} 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 (
|
return (
|
||||||
<div className="ascribe-media-player">
|
<div className="ascribe-media-player">
|
||||||
<Component preview={this.props.preview}
|
<Component {...componentProps}/>
|
||||||
url={this.props.url}
|
|
||||||
extraData={this.props.extraData}
|
|
||||||
encodingStatus={this.props.encodingStatus} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ let ContractSettingsUpdateButton = React.createClass({
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ReactS3FineUploader
|
<ReactS3FineUploader
|
||||||
ref="fineuploader"
|
|
||||||
fileInputElement={UploadButton}
|
fileInputElement={UploadButton}
|
||||||
keyRoutine={{
|
keyRoutine={{
|
||||||
url: AppConstants.serverUrl + 's3/key/',
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
@ -45,6 +45,10 @@ let FileDragAndDrop = React.createClass({
|
|||||||
allowedExtensions: React.PropTypes.string
|
allowedExtensions: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
this.refs.fileSelector.getDOMNode().value = '';
|
||||||
|
},
|
||||||
|
|
||||||
handleDragOver(event) {
|
handleDragOver(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@ -81,30 +85,30 @@ let FileDragAndDrop = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleDeleteFile(fileId) {
|
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
|
// inputs the same file again, therefore we need to reset its value
|
||||||
this.refs.fileinput.getDOMNode().value = '';
|
this.clearSelection();
|
||||||
this.props.handleDeleteFile(fileId);
|
this.props.handleDeleteFile(fileId);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleCancelFile(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
|
// inputs the same file again, therefore we need to reset its value
|
||||||
this.refs.fileinput.getDOMNode().value = '';
|
this.clearSelection();
|
||||||
this.props.handleCancelFile(fileId);
|
this.props.handleCancelFile(fileId);
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePauseFile(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
|
// inputs the same file again, therefore we need to reset its value
|
||||||
this.refs.fileinput.getDOMNode().value = '';
|
this.clearSelection();
|
||||||
this.props.handlePauseFile(fileId);
|
this.props.handlePauseFile(fileId);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleResumeFile(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
|
// inputs the same file again, therefore we need to reset its value
|
||||||
this.refs.fileinput.getDOMNode().value = '';
|
this.clearSelection();
|
||||||
this.props.handleResumeFile(fileId);
|
this.props.handleResumeFile(fileId);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -133,7 +137,7 @@ let FileDragAndDrop = React.createClass({
|
|||||||
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
|
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 () {
|
render: function () {
|
||||||
@ -206,7 +210,7 @@ let FileDragAndDrop = React.createClass({
|
|||||||
*/}
|
*/}
|
||||||
<input
|
<input
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
ref="fileinput"
|
ref="fileSelector"
|
||||||
type="file"
|
type="file"
|
||||||
style={{
|
style={{
|
||||||
visibility: 'hidden',
|
visibility: 'hidden',
|
||||||
|
@ -2,24 +2,30 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
|
|
||||||
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
|
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
|
||||||
import { getLangText } from '../../../utils/lang_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({
|
let UploadButton = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onDrop: React.PropTypes.func.isRequired,
|
onDrop: func.isRequired,
|
||||||
filesToUpload: React.PropTypes.array,
|
filesToUpload: array,
|
||||||
multiple: React.PropTypes.bool,
|
multiple: bool,
|
||||||
|
|
||||||
// For simplification purposes we're just going to use this prop as a
|
// For simplification purposes we're just going to use this prop as a
|
||||||
// label for the upload button
|
// label for the upload button
|
||||||
fileClassToUpload: React.PropTypes.shape({
|
fileClassToUpload: shape({
|
||||||
singular: React.PropTypes.string,
|
singular: string,
|
||||||
plural: React.PropTypes.string
|
plural: string
|
||||||
}),
|
}),
|
||||||
|
|
||||||
allowedExtensions: React.PropTypes.string
|
allowedExtensions: string,
|
||||||
|
|
||||||
|
handleCancelFile: func // provided by ReactS3FineUploader
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDrop(event) {
|
handleDrop(event) {
|
||||||
@ -37,11 +43,20 @@ let UploadButton = React.createClass({
|
|||||||
return this.props.filesToUpload.filter((file) => file.status === 'uploading');
|
return this.props.filesToUpload.filter((file) => file.status === 'uploading');
|
||||||
},
|
},
|
||||||
|
|
||||||
handleOnClick() {
|
getUploadedFile() {
|
||||||
let uploadingFiles = this.getUploadingFiles();
|
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) {
|
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,
|
// 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
|
// but since Google Chrome propagates the event much further than needed, we
|
||||||
// need to stop propagation as soon as the event is created
|
// 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...
|
// filter invalid files that might have been deleted or canceled...
|
||||||
filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter);
|
filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter);
|
||||||
|
|
||||||
// Depending on wether there is an upload going on or not we
|
if(this.getUploadingFiles().length !== 0) {
|
||||||
// display the progress
|
|
||||||
if(filesToUpload.length > 0) {
|
|
||||||
return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%';
|
return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%';
|
||||||
} else {
|
} else {
|
||||||
return fileClassToUpload.singular;
|
return fileClassToUpload.singular;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
getUploadedFileLabel() {
|
||||||
let {
|
const uploadedFile = this.getUploadedFile();
|
||||||
multiple,
|
|
||||||
fileClassToUpload,
|
|
||||||
allowedExtensions
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
|
if(uploadedFile) {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<Glyphicon glyph="ok" />
|
||||||
|
{' ' + truncateTextAtCharIndex(uploadedFile.name, 40)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<span>{getLangText('No file chosen')}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<button
|
<div className="upload-button-wrapper">
|
||||||
onClick={this.handleOnClick}
|
<a
|
||||||
className="btn btn-default btn-sm margin-left-2px"
|
onClick={this.handleOnClick}
|
||||||
disabled={this.getUploadingFiles().length !== 0}>
|
className="btn btn-default btn-sm margin-left-2px"
|
||||||
{this.getButtonLabel()}
|
disabled={this.getUploadingFiles().length !== 0}>
|
||||||
<input
|
{this.getButtonLabel()}
|
||||||
multiple={multiple}
|
<input
|
||||||
ref="fileinput"
|
multiple={multiple}
|
||||||
type="file"
|
ref="fileinput"
|
||||||
style={{
|
type="file"
|
||||||
display: 'none',
|
style={{
|
||||||
height: 0,
|
display: 'none',
|
||||||
width: 0
|
height: 0,
|
||||||
}}
|
width: 0
|
||||||
onChange={this.handleDrop}
|
}}
|
||||||
accept={allowedExtensions}/>
|
onChange={this.handleDrop}
|
||||||
</button>
|
accept={allowedExtensions}/>
|
||||||
|
</a>
|
||||||
|
{this.getUploadedFileLabel()}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -113,7 +113,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
// 'hash': upload using the hash
|
// 'hash': upload using the hash
|
||||||
// 'upload': upload full file (default if not specified)
|
// 'upload': upload full file (default if not specified)
|
||||||
enableLocalHashing: React.PropTypes.bool,
|
enableLocalHashing: React.PropTypes.bool,
|
||||||
uploadMethod: React.PropTypes.string,
|
uploadMethod: React.PropTypes.oneOf(['hash', 'upload']),
|
||||||
|
|
||||||
// A class of a file the user has to upload
|
// A class of a file the user has to upload
|
||||||
// Needs to be defined both in singular as well as in plural
|
// Needs to be defined both in singular as well as in plural
|
||||||
@ -202,7 +202,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps() {
|
componentWillUpdate() {
|
||||||
// since the csrf header is defined in this component's props,
|
// since the csrf header is defined in this component's props,
|
||||||
// everytime the csrf cookie is changed we'll need to reinitalize
|
// everytime the csrf cookie is changed we'll need to reinitalize
|
||||||
// fineuploader and update the actual csrf token
|
// fineuploader and update the actual csrf token
|
||||||
@ -259,7 +259,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
// Resets the whole react fineuploader component to its initial state
|
// Resets the whole react fineuploader component to its initial state
|
||||||
reset() {
|
reset() {
|
||||||
// Cancel all currently ongoing uploads
|
// Cancel all currently ongoing uploads
|
||||||
this.state.uploader.cancelAll();
|
this.cancelUploads();
|
||||||
|
|
||||||
// and reset component in general
|
// and reset component in general
|
||||||
this.state.uploader.reset();
|
this.state.uploader.reset();
|
||||||
@ -271,6 +271,22 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Cancel uploads and clear previously selected files on the input element
|
||||||
|
cancelUploads(id) {
|
||||||
|
!!id ? this.state.uploader.cancel(id) : this.state.uploader.cancelAll();
|
||||||
|
|
||||||
|
// Reset the file input element to clear the previously selected files so that
|
||||||
|
// the user can reselect them again.
|
||||||
|
this.clearFileSelection();
|
||||||
|
},
|
||||||
|
|
||||||
|
clearFileSelection() {
|
||||||
|
const { fileInput } = this.refs;
|
||||||
|
if (fileInput && typeof fileInput.clearSelection === 'function') {
|
||||||
|
fileInput.clearSelection();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
requestKey(fileId) {
|
requestKey(fileId) {
|
||||||
let filename = this.state.uploader.getName(fileId);
|
let filename = this.state.uploader.getName(fileId);
|
||||||
let uuid = this.state.uploader.getUuid(fileId);
|
let uuid = this.state.uploader.getUuid(fileId);
|
||||||
@ -298,18 +314,27 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
resolve(res.key);
|
resolve(res.key);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.logGlobal(err, false, {
|
this.onErrorPromiseProxy(err);
|
||||||
files: this.state.filesToUpload,
|
|
||||||
chunks: this.state.chunks
|
|
||||||
});
|
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
createBlob(file) {
|
createBlob(file) {
|
||||||
|
const { createBlobRoutine } = this.props;
|
||||||
|
|
||||||
return Q.Promise((resolve, reject) => {
|
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',
|
method: 'post',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
@ -320,7 +345,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
'filename': file.name,
|
'filename': file.name,
|
||||||
'key': file.key,
|
'key': file.key,
|
||||||
'piece_id': this.props.createBlobRoutine.pieceId
|
'piece_id': createBlobRoutine.pieceId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -336,16 +361,16 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
} else if(res.contractblob) {
|
} else if(res.contractblob) {
|
||||||
file.s3Url = res.contractblob.url_safe;
|
file.s3Url = res.contractblob.url_safe;
|
||||||
file.s3UrlSafe = 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 {
|
} else {
|
||||||
throw new Error(getLangText('Could not find a url to download.'));
|
throw new Error(getLangText('Could not find a url to download.'));
|
||||||
}
|
}
|
||||||
resolve(res);
|
resolve(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.logGlobal(err, false, {
|
this.onErrorPromiseProxy(err);
|
||||||
files: this.state.filesToUpload,
|
|
||||||
chunks: this.state.chunks
|
|
||||||
});
|
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -385,13 +410,15 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onComplete(id, name, res, xhr) {
|
onComplete(id, name, res, xhr) {
|
||||||
// there has been an issue with the server's connection
|
// There has been an issue with the server's connection
|
||||||
if((xhr && xhr.status === 0) || res.error) {
|
if (xhr && xhr.status === 0 && res.success) {
|
||||||
console.logGlobal(new Error(res.error || 'Complete was called but there wasn\t a success'), false, {
|
console.logGlobal(new Error('Upload succeeded with a status code 0'), false, {
|
||||||
files: this.state.filesToUpload,
|
files: this.state.filesToUpload,
|
||||||
chunks: this.state.chunks
|
chunks: this.state.chunks,
|
||||||
|
xhr: this.getXhrErrorComment(xhr)
|
||||||
});
|
});
|
||||||
} else {
|
// onError will catch any errors, so we can ignore them here
|
||||||
|
} else if (!res.error || res.success) {
|
||||||
let files = this.state.filesToUpload;
|
let files = this.state.filesToUpload;
|
||||||
|
|
||||||
// Set the state of the completed file to 'upload successful' in order to
|
// Set the state of the completed file to 'upload successful' in order to
|
||||||
@ -410,7 +437,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
if(this.props.submitFile) {
|
if(this.props.submitFile) {
|
||||||
this.props.submitFile(files[id]);
|
this.props.submitFile(files[id]);
|
||||||
} else {
|
} 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
|
// for explanation, check comment of if statement above
|
||||||
@ -427,28 +454,46 @@ 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');
|
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(this.onErrorPromiseProxy);
|
||||||
console.logGlobal(err, false, {
|
|
||||||
files: this.state.filesToUpload,
|
|
||||||
chunks: this.state.chunks
|
|
||||||
});
|
|
||||||
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onError(id, name, errorReason) {
|
/**
|
||||||
|
* 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, xhr) {
|
||||||
console.logGlobal(errorReason, false, {
|
console.logGlobal(errorReason, false, {
|
||||||
files: this.state.filesToUpload,
|
files: this.state.filesToUpload,
|
||||||
chunks: this.state.chunks
|
chunks: this.state.chunks,
|
||||||
|
xhr: this.getXhrErrorComment(xhr)
|
||||||
});
|
});
|
||||||
this.state.uploader.cancelAll();
|
|
||||||
|
this.props.setIsUploadReady(true);
|
||||||
|
this.cancelUploads();
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000);
|
let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getXhrErrorComment(xhr) {
|
||||||
|
if (xhr) {
|
||||||
|
return {
|
||||||
|
response: xhr.response,
|
||||||
|
url: xhr.responseURL,
|
||||||
|
status: xhr.status,
|
||||||
|
statusText: xhr.statusText
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
isFileValid(file) {
|
isFileValid(file) {
|
||||||
if(file.size > this.props.validation.sizeLimit) {
|
if(file.size > this.props.validation.sizeLimit) {
|
||||||
|
|
||||||
@ -586,7 +631,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleCancelFile(fileId) {
|
handleCancelFile(fileId) {
|
||||||
this.state.uploader.cancel(fileId);
|
this.cancelUploads(fileId);
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePauseFile(fileId) {
|
handlePauseFile(fileId) {
|
||||||
@ -606,9 +651,14 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleUploadFile(files) {
|
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,
|
// If multiple set and user already uploaded its work,
|
||||||
// cancel upload
|
// cancel upload
|
||||||
if(!this.props.multiple && this.state.filesToUpload.filter(displayValidFilesFilter).length > 0) {
|
if(!this.props.multiple && this.state.filesToUpload.filter(displayValidFilesFilter).length > 0) {
|
||||||
|
this.clearFileSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,19 +888,16 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
multiple,
|
multiple,
|
||||||
areAssetsDownloadable,
|
areAssetsDownloadable,
|
||||||
areAssetsEditable,
|
areAssetsEditable,
|
||||||
onInactive,
|
onInactive,
|
||||||
enableLocalHashing,
|
enableLocalHashing,
|
||||||
uploadMethod,
|
fileClassToUpload,
|
||||||
fileClassToUpload,
|
fileInputElement: FileInputElement,
|
||||||
validation,
|
uploadMethod } = this.props;
|
||||||
fileInputElement } = this.props;
|
|
||||||
|
|
||||||
// Here we initialize the template that has been either provided from the outside
|
const props = {
|
||||||
// or the default input that is FileDragAndDrop.
|
|
||||||
return React.createElement(fileInputElement, {
|
|
||||||
multiple,
|
multiple,
|
||||||
areAssetsDownloadable,
|
areAssetsDownloadable,
|
||||||
areAssetsEditable,
|
areAssetsEditable,
|
||||||
@ -868,10 +915,14 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
dropzoneInactive: this.isDropzoneInactive(),
|
dropzoneInactive: this.isDropzoneInactive(),
|
||||||
hashingProgress: this.state.hashingProgress,
|
hashingProgress: this.state.hashingProgress,
|
||||||
allowedExtensions: this.getAllowedExtensions()
|
allowedExtensions: this.getAllowedExtensions()
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FileInputElement
|
||||||
|
ref="fileInput"
|
||||||
|
{...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default ReactS3FineUploader;
|
export default ReactS3FineUploader;
|
||||||
|
@ -4399,7 +4399,9 @@ qq.UploadHandlerController = function(o, namespace) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(function() {
|
.done(function() {
|
||||||
handler.clearXhr(id, chunkIdx);
|
if (handler._getFileState(id)) {
|
||||||
|
handler.clearXhr(id, chunkIdx);
|
||||||
|
}
|
||||||
}) ;
|
}) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8681,7 +8683,7 @@ qq.s3.RequestSigner = function(o) {
|
|||||||
options.log(errorMessage, "error");
|
options.log(errorMessage, "error");
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.failure(errorMessage);
|
promise.failure(errorMessage, xhrOrXdr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
promise.success(response);
|
promise.success(response);
|
||||||
@ -8813,7 +8815,7 @@ qq.s3.RequestSigner = function(o) {
|
|||||||
credentialsProvider.get().accessKey,
|
credentialsProvider.get().accessKey,
|
||||||
credentialsProvider.get().sessionToken);
|
credentialsProvider.get().sessionToken);
|
||||||
}, function(errorMsg) {
|
}, function(errorMsg) {
|
||||||
options.log("Attempt to update expired credentials apparently failed! Unable to sign request. ", "error");
|
options.log("Attempt to update expired credentials apparently failed! Unable to sign request: " + errorMsg, "error");
|
||||||
signatureEffort.failure("Unable to sign request - expired credentials.");
|
signatureEffort.failure("Unable to sign request - expired credentials.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -9627,8 +9629,8 @@ qq.s3.XhrUploadHandler = function(spec, proxy) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
xhr.send(chunkData.blob);
|
xhr.send(chunkData.blob);
|
||||||
}, function() {
|
}, function(errorMsg, xhr) {
|
||||||
promise.failure({error: "Problem signing the chunk!"}, xhr);
|
promise.failure({error: "Problem signing the chunk: " + errorMsg}, xhr);
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
@ -9672,10 +9674,10 @@ qq.s3.XhrUploadHandler = function(spec, proxy) {
|
|||||||
uploadIdPromise.success(uploadId);
|
uploadIdPromise.success(uploadId);
|
||||||
promise.success(uploadId);
|
promise.success(uploadId);
|
||||||
},
|
},
|
||||||
function(errorMsg) {
|
function(errorMsg, xhr) {
|
||||||
handler._getPersistableData(id).uploadId = null;
|
handler._getPersistableData(id).uploadId = null;
|
||||||
promise.failure(errorMsg);
|
promise.failure(errorMsg, xhr);
|
||||||
uploadIdPromise.failure(errorMsg);
|
uploadIdPromise.failure(errorMsg, xhr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -40,12 +40,6 @@ let RegisterPiece = React.createClass( {
|
|||||||
|
|
||||||
mixins: [History],
|
mixins: [History],
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
canSpecifyEditions: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState(){
|
getInitialState(){
|
||||||
return mergeOptions(
|
return mergeOptions(
|
||||||
UserStore.getState(),
|
UserStore.getState(),
|
||||||
|
@ -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);
|
|
@ -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 (
|
|
||||||
<RegisterPiece
|
|
||||||
enableLocalHashing={false}
|
|
||||||
headerMessage={getLangText('Submit to the prize')}
|
|
||||||
submitMessage={getLangText('Submit')}>
|
|
||||||
<Property
|
|
||||||
name='artist_statement'
|
|
||||||
label={getLangText('Artist statement')}
|
|
||||||
editable={true}
|
|
||||||
overrideForm={true}>
|
|
||||||
<InputTextAreaToggable
|
|
||||||
rows={1}
|
|
||||||
placeholder={getLangText('Enter your statement')}
|
|
||||||
required />
|
|
||||||
</Property>
|
|
||||||
<Property
|
|
||||||
name='work_description'
|
|
||||||
label={getLangText('Work description')}
|
|
||||||
editable={true}
|
|
||||||
overrideForm={true}>
|
|
||||||
<InputTextAreaToggable
|
|
||||||
rows={1}
|
|
||||||
placeholder={getLangText('Enter the description for your work')}
|
|
||||||
required />
|
|
||||||
</Property>
|
|
||||||
<Property
|
|
||||||
name="terms"
|
|
||||||
className="ascribe-property-collapsible-toggle"
|
|
||||||
style={{paddingBottom: 0}}>
|
|
||||||
<InputCheckbox>
|
|
||||||
<span>
|
|
||||||
{' ' + getLangText('I agree to the Terms of Service the art price') + ' '}
|
|
||||||
(<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/sluice/terms.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
|
|
||||||
{getLangText('read')}
|
|
||||||
</a>)
|
|
||||||
</span>
|
|
||||||
</InputCheckbox>
|
|
||||||
</Property>
|
|
||||||
</RegisterPiece>);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<div className='row'>
|
|
||||||
<div style={{textAlign: 'center'}}>
|
|
||||||
{getLangText('The prize is no longer active')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default PrizeRegisterPiece;
|
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import AppPrizeConstants from './prize_application_constants';
|
import AppPrizeConstants from './prize_application_constants';
|
||||||
|
|
||||||
|
|
||||||
function getPrizeApiUrls(subdomain) {
|
function getPrizeApiUrls(subdomain) {
|
||||||
return {
|
return {
|
||||||
'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/',
|
'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/',
|
||||||
@ -18,10 +19,9 @@ function getPrizeApiUrls(subdomain) {
|
|||||||
'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/',
|
'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/',
|
||||||
'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/',
|
'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/',
|
||||||
'rating_average': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/average/',
|
'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/',
|
'notes': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/',
|
||||||
'note': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/${piece_id}/'
|
'note': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/${piece_id}/'
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 (
|
||||||
|
<span disabled className="btn btn-default btn-wide btn-spinner">
|
||||||
|
<AscribeSpinner color="dark-blue" size="md" />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-default btn-wide"
|
||||||
|
disabled={!(digitalWorkKeyReady && thumbnailKeyReady && proofOfPaymentReady && supportingMaterialsReady)}
|
||||||
|
onClick={this.submit}>
|
||||||
|
{getLangText('Submit to Portfolio Review')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { location } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="register-piece--form">
|
||||||
|
<Form
|
||||||
|
buttons={{}}
|
||||||
|
className="ascribe-form-bordered"
|
||||||
|
ref="registerPieceForm">
|
||||||
|
<Property
|
||||||
|
name='artist_name'
|
||||||
|
label={getLangText('Full name')}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="(e.g. Andy Warhol)"
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='title'
|
||||||
|
label={getLangText('Title of the Work')}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="(e.g. 32 Campbell's Soup Cans)"
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='date_created'
|
||||||
|
label={getLangText('Year of creation')}>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="(e.g. 1962)"
|
||||||
|
min={1}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='artist_statement'
|
||||||
|
label={getLangText("Artist's statement")}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter your statement')}/>
|
||||||
|
</Property>
|
||||||
|
</Form>
|
||||||
|
<Form
|
||||||
|
buttons={{}}
|
||||||
|
className="ascribe-form-bordered"
|
||||||
|
ref="additionalDataForm">
|
||||||
|
<Property
|
||||||
|
name='artist_bio'
|
||||||
|
label={getLangText('Biography')}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter your biography')}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='exhibition'
|
||||||
|
label={getLangText('Exhibition / Publication history (optional)')}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter exhibitions and publication history')}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='contact_information'
|
||||||
|
label={getLangText('Contact information')}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter your contact information (phone/website)')}/>
|
||||||
|
</Property>
|
||||||
|
</Form>
|
||||||
|
<Form
|
||||||
|
buttons={{}}
|
||||||
|
className="ascribe-form-bordered"
|
||||||
|
ref="uploadersForm">
|
||||||
|
<Property
|
||||||
|
name="digitalWorkKey"
|
||||||
|
label={getLangText('Select the PDF with your work')}>
|
||||||
|
<InputFineuploader
|
||||||
|
fileInputElement={UploadButton}
|
||||||
|
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||||
|
setIsUploadReady={this.setIsUploadReady('digitalWorkKeyReady')}
|
||||||
|
createBlobRoutine={{
|
||||||
|
url: ApiUrls.blob_digitalworks
|
||||||
|
}}
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'digitalwork'
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
|
||||||
|
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
|
||||||
|
allowedExtensions: ['pdf']
|
||||||
|
}}
|
||||||
|
location={location}
|
||||||
|
fileClassToUpload={{
|
||||||
|
singular: getLangText('Select the Portfolio'),
|
||||||
|
plural: getLangText('Select the Portfolios')
|
||||||
|
}}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name="thumbnailKey"
|
||||||
|
label={getLangText('Featured Cover photo')}>
|
||||||
|
<InputFineuploader
|
||||||
|
fileInputElement={UploadButton}
|
||||||
|
createBlobRoutine={{
|
||||||
|
url: ApiUrls.blob_thumbnails
|
||||||
|
}}
|
||||||
|
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||||
|
setIsUploadReady={this.setIsUploadReady('thumbnailKeyReady')}
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'thumbnail'
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
|
||||||
|
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
|
||||||
|
allowedExtensions: ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
}}
|
||||||
|
location={location}
|
||||||
|
fileClassToUpload={{
|
||||||
|
singular: getLangText('Select cover photo'),
|
||||||
|
plural: getLangText('Select cover photos')
|
||||||
|
}}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name="supportingMaterials"
|
||||||
|
label={getLangText('Supporting Materials (Optional)')}>
|
||||||
|
<InputFineuploader
|
||||||
|
fileInputElement={UploadButton}
|
||||||
|
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||||
|
setIsUploadReady={this.setIsUploadReady('supportingMaterialsReady')}
|
||||||
|
createBlobRoutine={this.getCreateBlobRoutine()}
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'other_data'
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
|
||||||
|
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit
|
||||||
|
}}
|
||||||
|
location={location}
|
||||||
|
fileClassToUpload={{
|
||||||
|
singular: getLangText('Select supporting material'),
|
||||||
|
plural: getLangText('Select supporting materials')
|
||||||
|
}}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name="proofOfPayment"
|
||||||
|
label={getLangText('Proof of payment')}>
|
||||||
|
<InputFineuploader
|
||||||
|
fileInputElement={UploadButton}
|
||||||
|
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||||
|
setIsUploadReady={this.setIsUploadReady('proofOfPaymentReady')}
|
||||||
|
createBlobRoutine={this.getCreateBlobRoutine()}
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'other_data'
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
|
||||||
|
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
|
||||||
|
allowedExtensions: ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
}}
|
||||||
|
location={location}
|
||||||
|
fileClassToUpload={{
|
||||||
|
singular: getLangText('Select Screenshot'),
|
||||||
|
plural: getLangText('Select Screenshots')
|
||||||
|
}}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
</Form>
|
||||||
|
<Form
|
||||||
|
buttons={{}}
|
||||||
|
className="ascribe-form-bordered">
|
||||||
|
<Property
|
||||||
|
name="terms"
|
||||||
|
className="ascribe-property-collapsible-toggle"
|
||||||
|
style={{paddingBottom: 0}}>
|
||||||
|
<span>
|
||||||
|
{getLangText('By submitting this form, you agree to the') + ' '}
|
||||||
|
<a
|
||||||
|
href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/portfolioreview/tos-portfolioreview.pdf"
|
||||||
|
target="_blank">
|
||||||
|
{getLangText('Terms of Service')}
|
||||||
|
</a>
|
||||||
|
{' of Portfolio Review.'}
|
||||||
|
</span>
|
||||||
|
</Property>
|
||||||
|
</Form>
|
||||||
|
{this.getSubmitButton()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PRRegisterPieceForm;
|
@ -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 (
|
||||||
|
<div className="piece--hero">
|
||||||
|
<h2><Glyphicon glyph="ok" /> Congratulations {currentUser.email}!</h2>
|
||||||
|
<h1>You have successfully submitted to Portfolio Review 2016</h1>
|
||||||
|
<p>See below, your uploaded portfolio:</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PRHero;
|
@ -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 (
|
||||||
|
<ButtonGroup className="enter" bsSize="large" vertical>
|
||||||
|
<LinkContainer to="/signup">
|
||||||
|
<Button>
|
||||||
|
{getLangText('Sign up to submit')}
|
||||||
|
</Button>
|
||||||
|
</LinkContainer>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{getLangText('or, already an ascribe user?')}
|
||||||
|
</p>
|
||||||
|
<LinkContainer to="/login">
|
||||||
|
<Button>
|
||||||
|
{getLangText('Log in to submit')}
|
||||||
|
</Button>
|
||||||
|
</LinkContainer>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ButtonGroup className="enter" bsSize="large" vertical>
|
||||||
|
<a className="btn btn-default" href="https://www.ascribe.io/app/signup">
|
||||||
|
{getLangText('Sign up to ascribe')}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{getLangText('or, already an ascribe user?')}
|
||||||
|
</p>
|
||||||
|
<LinkContainer to="/login">
|
||||||
|
<Button>
|
||||||
|
{getLangText('Log in')}
|
||||||
|
</Button>
|
||||||
|
</LinkContainer>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
if (this.state.prize && this.state.prize.active){
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{getLangText('This is the submission page for Portfolio Review 2016.')}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{getLangText('Submissions for Portfolio Review 2016 are now closed.')}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12 wp-landing-wrapper">
|
||||||
|
<h1>
|
||||||
|
{getLangText('Welcome to Portfolio Review 2016')}
|
||||||
|
</h1>
|
||||||
|
{this.getTitle()}
|
||||||
|
{this.getButtons()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PRLanding;
|
@ -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 (
|
||||||
|
<Row>
|
||||||
|
<Col xs={6}>
|
||||||
|
<div className="register-piece--info">
|
||||||
|
<h1>Portfolio Review</h1>
|
||||||
|
<h2>{getLangText('Submission closing on %s', ' 22 Dec 2015')}</h2>
|
||||||
|
<p style={{marginTop: '1em'}}>
|
||||||
|
{getLangText("You're submitting as %s. ", currentUser.email)}
|
||||||
|
<Link to="/logout">{getLangText('Change account?')}</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col xs={6}>
|
||||||
|
<PRRegisterPieceForm
|
||||||
|
location={location}
|
||||||
|
currentUser={currentUser}/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PRRegisterPiece;
|
65
js/components/whitelabel/prize/portfolioreview/pr_app.js
Normal file
65
js/components/whitelabel/prize/portfolioreview/pr_app.js
Normal file
@ -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 = <Hero />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{header}
|
||||||
|
<div className={'container ascribe-prize-app client--' + subdomain}>
|
||||||
|
{children}
|
||||||
|
<GlobalNotification />
|
||||||
|
<div id="modal" className="container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PRApp;
|
@ -3,58 +3,94 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, IndexRoute } from 'react-router';
|
import { Route, IndexRoute } from 'react-router';
|
||||||
|
|
||||||
import Landing from './components/prize_landing';
|
import SPLanding from './simple_prize/components/prize_landing';
|
||||||
import LoginContainer from './components/prize_login_container';
|
import SPLoginContainer from './simple_prize/components/prize_login_container';
|
||||||
import LogoutContainer from '../../../components/logout_container';
|
import SPSignupContainer from './simple_prize/components/prize_signup_container';
|
||||||
import SignupContainer from './components/prize_signup_container';
|
import SPRegisterPiece from './simple_prize/components/prize_register_piece';
|
||||||
import PasswordResetContainer from '../../../components/password_reset_container';
|
import SPPieceList from './simple_prize/components/prize_piece_list';
|
||||||
import PrizeRegisterPiece from './components/prize_register_piece';
|
import SPPieceContainer from './simple_prize/components/ascribe_detail/prize_piece_container';
|
||||||
import PrizePieceList from './components/prize_piece_list';
|
import SPSettingsContainer from './simple_prize/components/prize_settings_container';
|
||||||
import PrizePieceContainer from './components/ascribe_detail/prize_piece_container';
|
import SPApp from './simple_prize/prize_app';
|
||||||
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 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';
|
import AuthProxyHandler from '../../../components/ascribe_routes/proxy_routes/auth_proxy_handler';
|
||||||
|
|
||||||
|
|
||||||
function getRoutes() {
|
const ROUTES = {
|
||||||
return (
|
sluice: (
|
||||||
<Route path='/' component={App}>
|
<Route path='/' component={SPApp}>
|
||||||
<IndexRoute component={Landing} />
|
<IndexRoute component={SPLanding} />
|
||||||
<Route
|
<Route
|
||||||
path='login'
|
path='login'
|
||||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(LoginContainer)} />
|
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPLoginContainer)} />
|
||||||
<Route
|
<Route
|
||||||
path='logout'
|
path='logout'
|
||||||
component={AuthProxyHandler({to: '/', when: 'loggedOut'})(LogoutContainer)}/>
|
component={AuthProxyHandler({to: '/', when: 'loggedOut'})(LogoutContainer)}/>
|
||||||
<Route
|
<Route
|
||||||
path='signup'
|
path='signup'
|
||||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SignupContainer)} />
|
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPSignupContainer)} />
|
||||||
<Route
|
<Route
|
||||||
path='password_reset'
|
path='password_reset'
|
||||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(PasswordResetContainer)} />
|
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(PasswordResetContainer)} />
|
||||||
<Route
|
<Route
|
||||||
path='settings'
|
path='settings'
|
||||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SettingsContainer)}/>
|
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SPSettingsContainer)}/>
|
||||||
<Route
|
<Route
|
||||||
path='register_piece'
|
path='register_piece'
|
||||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(PrizeRegisterPiece)}
|
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SPRegisterPiece)}
|
||||||
headerTitle='+ NEW WORK'/>
|
headerTitle='+ NEW WORK'/>
|
||||||
<Route
|
<Route
|
||||||
path='collection'
|
path='collection'
|
||||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(PrizePieceList)}
|
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SPPieceList)}
|
||||||
headerTitle='COLLECTION'/>
|
headerTitle='COLLECTION'/>
|
||||||
|
|
||||||
<Route path='pieces/:pieceId' component={PrizePieceContainer} />
|
<Route path='pieces/:pieceId' component={SPPieceContainer} />
|
||||||
<Route path='editions/:editionId' component={EditionContainer} />
|
<Route path='editions/:editionId' component={EditionContainer} />
|
||||||
<Route path='verify' component={CoaVerifyContainer} />
|
<Route path='verify' component={CoaVerifyContainer} />
|
||||||
<Route path='*' component={ErrorNotFoundPage} />
|
<Route path='*' component={ErrorNotFoundPage} />
|
||||||
</Route>
|
</Route>
|
||||||
);
|
),
|
||||||
|
portfolioreview: (
|
||||||
|
<Route path='/' component={PRApp}>
|
||||||
|
<IndexRoute component={PRLanding} />
|
||||||
|
<Route
|
||||||
|
path='register_piece'
|
||||||
|
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(PRRegisterPiece)}
|
||||||
|
headerTitle='+ NEW WORK'/>
|
||||||
|
<Route
|
||||||
|
path='login'
|
||||||
|
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPLoginContainer)} />
|
||||||
|
<Route
|
||||||
|
path='logout'
|
||||||
|
component={AuthProxyHandler({to: '/', when: 'loggedOut'})(LogoutContainer)}/>
|
||||||
|
<Route
|
||||||
|
path='signup'
|
||||||
|
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPSignupContainer)} />
|
||||||
|
<Route
|
||||||
|
path='password_reset'
|
||||||
|
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(PasswordResetContainer)} />
|
||||||
|
<Route path='pieces/:pieceId' component={SPPieceContainer} />
|
||||||
|
<Route path='*' component={ErrorNotFoundPage} />
|
||||||
|
</Route>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function getRoutes(commonRoutes, subdomain) {
|
||||||
|
if(subdomain in ROUTES) {
|
||||||
|
return ROUTES[subdomain];
|
||||||
|
} else {
|
||||||
|
throw new Error('Subdomain wasn\'t specified in the wallet app.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { alt } from '../../../../alt';
|
import { alt } from '../../../../../alt';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher';
|
import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher';
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { alt } from '../../../../alt';
|
import { alt } from '../../../../../alt';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
import PrizeRatingFetcher from '../fetchers/prize_rating_fetcher';
|
import PrizeRatingFetcher from '../fetchers/prize_rating_fetcher';
|
@ -3,26 +3,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import StarRating from 'react-star-rating';
|
import StarRating from 'react-star-rating';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import PieceListActions from '../../../../../actions/piece_list_actions';
|
import PieceListActions from '../../../../../../actions/piece_list_actions';
|
||||||
import PieceListStore from '../../../../../stores/piece_list_store';
|
import PieceListStore from '../../../../../../stores/piece_list_store';
|
||||||
|
|
||||||
import PrizeRatingActions from '../../actions/prize_rating_actions';
|
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 GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
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 SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button';
|
||||||
|
|
||||||
import { getLangText } from '../../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let AccordionListItemPrize = React.createClass({
|
let AccordionListItemPrize = React.createClass({
|
||||||
@ -182,7 +183,7 @@ let AccordionListItemPrize = React.createClass({
|
|||||||
artistName={artistName}
|
artistName={artistName}
|
||||||
subsubheading={
|
subsubheading={
|
||||||
<div>
|
<div>
|
||||||
<span>{new Date(this.props.content.date_created).getFullYear()}</span>
|
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span>
|
||||||
</div>}
|
</div>}
|
||||||
buttons={this.getPrizeButtons()}
|
buttons={this.getPrizeButtons()}
|
||||||
badge={this.getPrizeBadge()}>
|
badge={this.getPrizeBadge()}>
|
@ -3,10 +3,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import ModalWrapper from '../../../../ascribe_modal/modal_wrapper';
|
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
|
||||||
import PieceSubmitToPrizeForm from '../../../../ascribe_forms/form_submit_to_prize';
|
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({
|
let SubmitToPrizeButton = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
@ -6,41 +6,44 @@ import Moment from 'moment';
|
|||||||
|
|
||||||
import StarRating from 'react-star-rating';
|
import StarRating from 'react-star-rating';
|
||||||
|
|
||||||
import PieceActions from '../../../../../actions/piece_actions';
|
import PieceActions from '../../../../../../actions/piece_actions';
|
||||||
import PieceStore from '../../../../../stores/piece_store';
|
import PieceStore from '../../../../../../stores/piece_store';
|
||||||
|
|
||||||
import PieceListStore from '../../../../../stores/piece_list_store';
|
import PieceListStore from '../../../../../../stores/piece_list_store';
|
||||||
import PieceListActions from '../../../../../actions/piece_list_actions';
|
import PieceListActions from '../../../../../../actions/piece_list_actions';
|
||||||
|
|
||||||
import PrizeRatingActions from '../../actions/prize_rating_actions';
|
import PrizeRatingActions from '../../actions/prize_rating_actions';
|
||||||
import PrizeRatingStore from '../../stores/prize_rating_store';
|
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 Piece from '../../../../../../components/ascribe_detail/piece';
|
||||||
import Note from '../../../../../components/ascribe_detail/note';
|
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 Form from '../../../../../../components/ascribe_forms/form';
|
||||||
import Property from '../../../../../components/ascribe_forms/property';
|
import Property from '../../../../../../components/ascribe_forms/property';
|
||||||
import InputTextAreaToggable from '../../../../../components/ascribe_forms/input_textarea_toggable';
|
import InputTextAreaToggable from '../../../../../../components/ascribe_forms/input_textarea_toggable';
|
||||||
import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
import InputCheckbox from '../../../../ascribe_forms/input_checkbox';
|
import FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader';
|
||||||
import LoanForm from '../../../../ascribe_forms/form_loan';
|
|
||||||
import ListRequestActions from '../../../../ascribe_forms/list_form_request_actions';
|
|
||||||
import ModalWrapper from '../../../../ascribe_modal/modal_wrapper';
|
|
||||||
|
|
||||||
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
|
||||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
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 DetailProperty from '../../../../../ascribe_detail/detail_property';
|
||||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
|
||||||
import { getLangText } from '../../../../../utils/lang_utils';
|
import ApiUrls from '../../../../../../constants/api_urls';
|
||||||
import { setDocumentTitle } from '../../../../../utils/dom_utils';
|
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({
|
let PieceContainer = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
params: React.PropTypes.object
|
params: React.PropTypes.object,
|
||||||
|
location: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
@ -62,6 +66,7 @@ let PieceContainer = React.createClass({
|
|||||||
PieceStore.listen(this.onChange);
|
PieceStore.listen(this.onChange);
|
||||||
PieceActions.fetchOne(this.props.params.pieceId);
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
UserStore.listen(this.onChange);
|
UserStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
|
|
||||||
// Every time we enter the piece detail page, just reset the piece
|
// 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
|
// store as it will otherwise display wrong/old data once the user loads
|
||||||
@ -142,10 +147,10 @@ let PieceContainer = React.createClass({
|
|||||||
<NavigationHeader
|
<NavigationHeader
|
||||||
piece={this.state.piece}
|
piece={this.state.piece}
|
||||||
currentUser={this.state.currentUser}/>
|
currentUser={this.state.currentUser}/>
|
||||||
<hr/>
|
|
||||||
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||||
<DetailProperty label={getLangText('BY')} value={artistName} />
|
<DetailProperty label={getLangText('BY')} value={artistName} />
|
||||||
<DetailProperty label={getLangText('DATE')} value={new Date(this.state.piece.date_created).getFullYear()} />
|
<DetailProperty label={getLangText('DATE')} value={Moment(this.state.piece.date_created, 'YYYY-MM-DD').year()} />
|
||||||
{artistEmail}
|
{artistEmail}
|
||||||
{this.getActions()}
|
{this.getActions()}
|
||||||
<hr/>
|
<hr/>
|
||||||
@ -157,7 +162,7 @@ let PieceContainer = React.createClass({
|
|||||||
piece={this.state.piece}
|
piece={this.state.piece}
|
||||||
currentUser={this.state.currentUser}/>
|
currentUser={this.state.currentUser}/>
|
||||||
}>
|
}>
|
||||||
<PrizePieceDetails piece={this.state.piece}/>
|
<PrizePieceDetails piece={this.state.piece} location={this.props.location}/>
|
||||||
</Piece>
|
</Piece>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -177,24 +182,28 @@ let NavigationHeader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.currentUser && this.props.currentUser.email && this.props.piece && this.props.piece.navigation) {
|
const { currentUser, piece } = this.props;
|
||||||
let nav = this.props.piece.navigation;
|
|
||||||
|
if (currentUser && currentUser.email && currentUser.is_judge && currentUser.is_jury &&
|
||||||
|
!currentUser.is_admin && piece && piece.navigation) {
|
||||||
|
let nav = piece.navigation;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginBottom: '1em'}}>
|
<div style={{marginBottom: '1em'}}>
|
||||||
<div className="row no-margin">
|
<div className="row no-margin">
|
||||||
<Link className="disable-select" to={`/pieces/${ nav.prev_index || this.props.piece.id }`}>
|
<Link className="disable-select" to={`/pieces/${ nav.prev_index || piece.id }`}>
|
||||||
<span className="glyphicon glyphicon-chevron-left pull-left link-ascribe" aria-hidden="true">
|
<span className="glyphicon glyphicon-chevron-left pull-left link-ascribe" aria-hidden="true">
|
||||||
{getLangText('Previous')}
|
{getLangText('Previous')}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link className="disable-select" to={`/pieces/${ nav.next_index || this.props.piece.id }`}>
|
<Link className="disable-select" to={`/pieces/${ nav.next_index || piece.id }`}>
|
||||||
<span className="pull-right link-ascribe">
|
<span className="pull-right link-ascribe">
|
||||||
{getLangText('Next')}
|
{getLangText('Next')}
|
||||||
<span className="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
<span className="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -417,7 +426,8 @@ let PrizePieceRatings = React.createClass({
|
|||||||
|
|
||||||
let PrizePieceDetails = React.createClass({
|
let PrizePieceDetails = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
piece: React.PropTypes.object
|
piece: React.PropTypes.object,
|
||||||
|
location: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -432,6 +442,8 @@ let PrizePieceDetails = React.createClass({
|
|||||||
<Form ref='form'>
|
<Form ref='form'>
|
||||||
{Object.keys(this.props.piece.extra_data).map((data) => {
|
{Object.keys(this.props.piece.extra_data).map((data) => {
|
||||||
let label = data.replace('_', ' ');
|
let label = data.replace('_', ' ');
|
||||||
|
const value = this.props.piece.extra_data[data] || 'N/A';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Property
|
<Property
|
||||||
name={data}
|
name={data}
|
||||||
@ -440,11 +452,20 @@ let PrizePieceDetails = React.createClass({
|
|||||||
overrideForm={true}>
|
overrideForm={true}>
|
||||||
<InputTextAreaToggable
|
<InputTextAreaToggable
|
||||||
rows={1}
|
rows={1}
|
||||||
defaultValue={this.props.piece.extra_data[data]}/>
|
defaultValue={value}/>
|
||||||
</Property>);
|
</Property>
|
||||||
}
|
);
|
||||||
)}
|
})}
|
||||||
<hr />
|
<FurtherDetailsFileuploader
|
||||||
|
submitFile={() => {}}
|
||||||
|
setIsUploadReady={() => {}}
|
||||||
|
isReadyForFormSubmission={() => {}}
|
||||||
|
editable={false}
|
||||||
|
overrideForm={true}
|
||||||
|
pieceId={this.props.piece.id}
|
||||||
|
otherData={this.props.piece.other_data}
|
||||||
|
multiple={true}
|
||||||
|
location={location}/>
|
||||||
</Form>
|
</Form>
|
||||||
</CollapsibleParagraph>
|
</CollapsibleParagraph>
|
||||||
);
|
);
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import constants from '../../../../constants/application_constants';
|
import constants from '../../../../../constants/application_constants';
|
||||||
|
|
||||||
|
|
||||||
let Hero = React.createClass({
|
let Hero = React.createClass({
|
@ -11,11 +11,11 @@ import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
|
|||||||
|
|
||||||
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
|
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
|
||||||
|
|
||||||
import UserStore from '../../../../stores/user_store';
|
import UserStore from '../../../../../stores/user_store';
|
||||||
import UserActions from '../../../../actions/user_actions';
|
import UserActions from '../../../../../actions/user_actions';
|
||||||
|
|
||||||
import { mergeOptions } from '../../../../utils/general_utils';
|
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||||
import { getLangText } from '../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
|
|
||||||
let Landing = React.createClass({
|
let Landing = React.createClass({
|
||||||
|
|
@ -3,10 +3,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router';
|
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 { getLangText } from '../../../../../utils/lang_utils';
|
||||||
import { setDocumentTitle } from '../../../../utils/dom_utils';
|
import { setDocumentTitle } from '../../../../../utils/dom_utils';
|
||||||
|
|
||||||
|
|
||||||
let LoginContainer = React.createClass({
|
let LoginContainer = React.createClass({
|
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PieceList from '../../../piece_list';
|
import PieceList from '../../../../piece_list';
|
||||||
|
|
||||||
import UserActions from '../../../../actions/user_actions';
|
import UserActions from '../../../../../actions/user_actions';
|
||||||
import UserStore from '../../../../stores/user_store';
|
import UserStore from '../../../../../stores/user_store';
|
||||||
|
|
||||||
import PrizeActions from '../actions/prize_actions';
|
import PrizeActions from '../actions/prize_actions';
|
||||||
import PrizeStore from '../stores/prize_store';
|
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 AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize';
|
||||||
|
|
||||||
import { mergeOptions } from '../../../../utils/general_utils';
|
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||||
import { getLangText } from '../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
import { setDocumentTitle } from '../../../../utils/dom_utils';
|
import { setDocumentTitle } from '../../../../../utils/dom_utils';
|
||||||
|
|
||||||
let PrizePieceList = React.createClass({
|
let PrizePieceList = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
@ -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 (
|
||||||
|
<div>
|
||||||
|
<RegisterPiece
|
||||||
|
enableLocalHashing={false}
|
||||||
|
headerMessage={''}
|
||||||
|
submitMessage={getLangText('Submit')}
|
||||||
|
location={location}>
|
||||||
|
<Property
|
||||||
|
name='artist_statement'
|
||||||
|
label={getLangText('Artist statement')}
|
||||||
|
editable={true}
|
||||||
|
overrideForm={true}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter your statement')}
|
||||||
|
required />
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='work_description'
|
||||||
|
label={getLangText('Work description')}
|
||||||
|
editable={true}
|
||||||
|
overrideForm={true}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
placeholder={getLangText('Enter the description for your work')}
|
||||||
|
required />
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name="terms"
|
||||||
|
className="ascribe-property-collapsible-toggle"
|
||||||
|
style={{paddingBottom: 0}}>
|
||||||
|
<InputCheckbox>
|
||||||
|
<span>
|
||||||
|
{' ' + getLangText('I agree to the Terms of Service the art price') + ' '}
|
||||||
|
(<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/sluice/terms.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
|
||||||
|
{getLangText('read')}
|
||||||
|
</a>)
|
||||||
|
</span>
|
||||||
|
</InputCheckbox>
|
||||||
|
</Property>
|
||||||
|
</RegisterPiece>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<div className='row'>
|
||||||
|
<div style={{textAlign: 'center'}}>
|
||||||
|
{getLangText('The prize is no longer active')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PrizeRegisterPiece;
|
@ -2,29 +2,29 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import UserStore from '../../../../stores/user_store';
|
import UserStore from '../../../../../stores/user_store';
|
||||||
import UserActions from '../../../../actions/user_actions';
|
import UserActions from '../../../../../actions/user_actions';
|
||||||
import PrizeActions from '../actions/prize_actions';
|
import PrizeActions from '../actions/prize_actions';
|
||||||
import PrizeStore from '../stores/prize_store';
|
import PrizeStore from '../stores/prize_store';
|
||||||
import PrizeJuryActions from '../actions/prize_jury_actions';
|
import PrizeJuryActions from '../actions/prize_jury_actions';
|
||||||
import PrizeJuryStore from '../stores/prize_jury_store';
|
import PrizeJuryStore from '../stores/prize_jury_store';
|
||||||
|
|
||||||
import SettingsContainer from '../../../ascribe_settings/settings_container';
|
import SettingsContainer from '../../../../ascribe_settings/settings_container';
|
||||||
import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_paragraph';
|
import CollapsibleParagraph from '../../../../ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
import Form from '../../../ascribe_forms/form';
|
import Form from '../../../../ascribe_forms/form';
|
||||||
import Property from '../../../ascribe_forms/property';
|
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 GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||||
|
|
||||||
import AscribeSpinner from '../../../ascribe_spinner';
|
import AscribeSpinner from '../../../../ascribe_spinner';
|
||||||
import ApiUrls from '../../../../constants/api_urls';
|
import ApiUrls from '../../../../../constants/api_urls';
|
||||||
|
|
||||||
import { getLangText } from '../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
import { setDocumentTitle } from '../../../../utils/dom_utils';
|
import { setDocumentTitle } from '../../../../../utils/dom_utils';
|
||||||
|
|
||||||
|
|
||||||
let Settings = React.createClass({
|
let Settings = React.createClass({
|
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
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 { getLangText } from '../../../../../utils/lang_utils';
|
||||||
import { setDocumentTitle } from '../../../../utils/dom_utils';
|
import { setDocumentTitle } from '../../../../../utils/dom_utils';
|
||||||
|
|
||||||
let SignupContainer = React.createClass({
|
let SignupContainer = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import requests from '../../../../utils/requests';
|
import requests from '../../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
let PrizeFetcher = {
|
let PrizeFetcher = {
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import requests from '../../../../utils/requests';
|
import requests from '../../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
let PrizeJuryFetcher = {
|
let PrizeJuryFetcher = {
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import requests from '../../../../utils/requests';
|
import requests from '../../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
let PrizeRatingFetcher = {
|
let PrizeRatingFetcher = {
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Hero from './components/prize_hero';
|
import Hero from './components/prize_hero';
|
||||||
import Header from '../../header';
|
import Header from '../../../header';
|
||||||
import Footer from '../../footer';
|
import Footer from '../../../footer';
|
||||||
import GlobalNotification from '../../global_notification';
|
import GlobalNotification from '../../../global_notification';
|
||||||
|
|
||||||
import { getSubdomain } from '../../../utils/general_utils';
|
import { getSubdomain } from '../../../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let PrizeApp = React.createClass({
|
let PrizeApp = React.createClass({
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { alt } from '../../../../alt';
|
import { alt } from '../../../../../alt';
|
||||||
|
|
||||||
import PrizeJuryActions from '../actions/prize_jury_actions';
|
import PrizeJuryActions from '../actions/prize_jury_actions';
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { alt } from '../../../../alt';
|
import { alt } from '../../../../../alt';
|
||||||
|
|
||||||
import PrizeRatingActions from '../actions/prize_rating_actions';
|
import PrizeRatingActions from '../actions/prize_rating_actions';
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { alt } from '../../../../alt';
|
import { alt } from '../../../../../alt';
|
||||||
|
|
||||||
import PrizeActions from '../actions/prize_actions';
|
import PrizeActions from '../actions/prize_actions';
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import Piece from '../../../../../components/ascribe_detail/piece';
|
import Piece from '../../../../../components/ascribe_detail/piece';
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ let WalletPieceContainer = React.createClass({
|
|||||||
<hr style={{marginTop: 0}}/>
|
<hr style={{marginTop: 0}}/>
|
||||||
<h1 className="ascribe-detail-title">{this.props.piece.title}</h1>
|
<h1 className="ascribe-detail-title">{this.props.piece.title}</h1>
|
||||||
<DetailProperty label="BY" value={this.props.piece.artist_name} />
|
<DetailProperty label="BY" value={this.props.piece.artist_name} />
|
||||||
<DetailProperty label="DATE" value={new Date(this.props.piece.date_created).getFullYear()} />
|
<DetailProperty label="DATE" value={Moment(this.props.piece.date_created, 'YYYY-MM-DD').year()} />
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ let CylandAccordionListItem = React.createClass({
|
|||||||
piece={this.props.content}
|
piece={this.props.content}
|
||||||
subsubheading={
|
subsubheading={
|
||||||
<div className="pull-left">
|
<div className="pull-left">
|
||||||
<span>{new Date(this.props.content.date_created).getFullYear()}</span>
|
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span>
|
||||||
</div>}
|
</div>}
|
||||||
buttons={this.getSubmitButtons()}>
|
buttons={this.getSubmitButtons()}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -77,7 +77,7 @@ let CylandAdditionalDataForm = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { piece, isInline, disabled, handleSuccess } = this.props;
|
let { piece, isInline, disabled, handleSuccess, location } = this.props;
|
||||||
let buttons, spinner, heading;
|
let buttons, spinner, heading;
|
||||||
|
|
||||||
if(!isInline) {
|
if(!isInline) {
|
||||||
@ -121,21 +121,69 @@ let CylandAdditionalDataForm = React.createClass({
|
|||||||
{heading}
|
{heading}
|
||||||
<Property
|
<Property
|
||||||
name='artist_bio'
|
name='artist_bio'
|
||||||
label={getLangText('Artist Biography')}>
|
label={getLangText('Artist Biography')}
|
||||||
|
hidden={disabled && !piece.extra_data.artist_bio}>
|
||||||
<InputTextAreaToggable
|
<InputTextAreaToggable
|
||||||
rows={1}
|
rows={1}
|
||||||
defaultValue={piece.extra_data.artist_bio}
|
defaultValue={piece.extra_data.artist_bio}
|
||||||
placeholder={getLangText('Enter the artist\'s biography...')}/>
|
placeholder={getLangText('Enter the artist\'s biography...')}/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='artist_contact_information'
|
||||||
|
label={getLangText('Artist Contact Information')}
|
||||||
|
hidden={disabled && !piece.extra_data.artist_contact_information}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
defaultValue={piece.extra_data.artist_contact_information}
|
||||||
|
placeholder={getLangText('Enter the artist\'s contact information...')}/>
|
||||||
|
</Property>
|
||||||
<Property
|
<Property
|
||||||
name='conceptual_overview'
|
name='conceptual_overview'
|
||||||
label={getLangText('Conceptual Overview')}>
|
label={getLangText('Conceptual Overview')}
|
||||||
|
hidden={disabled && !piece.extra_data.conceptual_overview}>
|
||||||
<InputTextAreaToggable
|
<InputTextAreaToggable
|
||||||
rows={1}
|
rows={1}
|
||||||
defaultValue={piece.extra_data.conceptual_overview}
|
defaultValue={piece.extra_data.conceptual_overview}
|
||||||
placeholder={getLangText('Enter a conceptual overview...')}/>
|
placeholder={getLangText('Enter a conceptual overview...')}/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='medium'
|
||||||
|
label={getLangText('Medium (technical specifications)')}
|
||||||
|
hidden={disabled && !piece.extra_data.medium}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
defaultValue={piece.extra_data.medium}
|
||||||
|
placeholder={getLangText('Enter the medium (and other technical specifications)...')}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='size_duration'
|
||||||
|
label={getLangText('Size / Duration')}
|
||||||
|
hidden={disabled && !piece.extra_data.size_duration}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
defaultValue={piece.extra_data.size_duration}
|
||||||
|
placeholder={getLangText('Enter the size / duration...')}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='display_instructions'
|
||||||
|
label={getLangText('Display instructions')}
|
||||||
|
hidden={disabled && !piece.extra_data.display_instructions}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
defaultValue={piece.extra_data.display_instructions}
|
||||||
|
placeholder={getLangText('Enter the display instructions...')}/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='additional_details'
|
||||||
|
label={getLangText('Additional details')}
|
||||||
|
hidden={disabled && !piece.extra_data.additional_details}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
defaultValue={piece.extra_data.additional_details}
|
||||||
|
placeholder={getLangText('Enter additional details...')}/>
|
||||||
|
</Property>
|
||||||
<FurtherDetailsFileuploader
|
<FurtherDetailsFileuploader
|
||||||
|
label={getLangText('Additional files (e.g. still images, pdf)')}
|
||||||
uploadStarted={this.uploadStarted}
|
uploadStarted={this.uploadStarted}
|
||||||
submitFile={function () {}}
|
submitFile={function () {}}
|
||||||
setIsUploadReady={this.setIsUploadReady}
|
setIsUploadReady={this.setIsUploadReady}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ let IkonotvAccordionListItem = React.createClass({
|
|||||||
piece={this.props.content}
|
piece={this.props.content}
|
||||||
subsubheading={
|
subsubheading={
|
||||||
<div className="pull-left">
|
<div className="pull-left">
|
||||||
<span>{new Date(this.props.content.date_created).getFullYear()}</span>
|
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span>
|
||||||
</div>}
|
</div>}
|
||||||
buttons={this.getSubmitButtons()}>
|
buttons={this.getSubmitButtons()}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -14,6 +14,7 @@ let ApiUrls = {
|
|||||||
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/',
|
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/',
|
||||||
'blob_otherdatas': AppConstants.apiEndpoint + 'blob/otherdatas/',
|
'blob_otherdatas': AppConstants.apiEndpoint + 'blob/otherdatas/',
|
||||||
'blob_contracts': AppConstants.apiEndpoint + 'blob/contracts/',
|
'blob_contracts': AppConstants.apiEndpoint + 'blob/contracts/',
|
||||||
|
'blob_thumbnails': AppConstants.apiEndpoint + 'blob/thumbnails/',
|
||||||
'coa': AppConstants.apiEndpoint + 'coa/${id}/',
|
'coa': AppConstants.apiEndpoint + 'coa/${id}/',
|
||||||
'coa_create': AppConstants.apiEndpoint + 'coa/',
|
'coa_create': AppConstants.apiEndpoint + 'coa/',
|
||||||
'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/',
|
'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/',
|
||||||
|
@ -52,7 +52,14 @@ let constants = {
|
|||||||
'name': 'Lumenus',
|
'name': 'Lumenus',
|
||||||
'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/lumenus/lumenus-logo.png',
|
'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/lumenus/lumenus-logo.png',
|
||||||
'permissions': ['register', 'edit', 'share', 'del_from_collection'],
|
'permissions': ['register', 'edit', 'share', 'del_from_collection'],
|
||||||
'type': 'wallet',
|
'type': 'wallet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subdomain': 'portfolioreview',
|
||||||
|
'name': 'Portfolio Review',
|
||||||
|
'logo': 'http://notfoundlogo.de',
|
||||||
|
'permissions': ['register', 'edit', 'share', 'del_from_collection'],
|
||||||
|
'type': 'prize'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'defaultDomain': {
|
'defaultDomain': {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import AppConstants from '../constants/application_constants';
|
import AppConstants from '../constants/application_constants';
|
||||||
|
|
||||||
@ -17,12 +18,19 @@ export function getCookie(name) {
|
|||||||
let parts = document.cookie.split(';');
|
let parts = document.cookie.split(';');
|
||||||
|
|
||||||
for(let i = 0; i < parts.length; i++) {
|
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();
|
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
|
Given a url for an image, this method fetches it and returns a promise that resolves to
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
import SparkMD5 from 'spark-md5';
|
import SparkMD5 from 'spark-md5';
|
||||||
|
import Moment from 'moment';
|
||||||
|
|
||||||
import { getLangText } from './lang_utils';
|
import { getLangText } from './lang_utils';
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ export function computeHashOfFile(file) {
|
|||||||
let spark = new SparkMD5.ArrayBuffer();
|
let spark = new SparkMD5.ArrayBuffer();
|
||||||
let fileReader = new FileReader();
|
let fileReader = new FileReader();
|
||||||
|
|
||||||
let startTime = new Date();
|
let startTime = new Moment();
|
||||||
|
|
||||||
// comment: We should convert this to es6 at some point, however if so please consider that
|
// comment: We should convert this to es6 at some point, however if so please consider that
|
||||||
// an arrow function will get rid of the function's scope...
|
// an arrow function will get rid of the function's scope...
|
||||||
@ -53,7 +54,7 @@ export function computeHashOfFile(file) {
|
|||||||
|
|
||||||
console.info('computed hash %s (took %d s)',
|
console.info('computed hash %s (took %d s)',
|
||||||
fileHash,
|
fileHash,
|
||||||
Math.round(((new Date() - startTime) / 1000) % 60)); // Compute hash
|
Math.round(((new Moment() - startTime) / 1000) % 60)); // Compute hash
|
||||||
|
|
||||||
let blobTextFile = makeTextFile(fileHash, file);
|
let blobTextFile = makeTextFile(fileHash, file);
|
||||||
resolve(blobTextFile);
|
resolve(blobTextFile);
|
||||||
|
@ -8,6 +8,7 @@ import { getCookie } from '../utils/fetch_api_utils';
|
|||||||
import { omitFromObject } from '../utils/general_utils';
|
import { omitFromObject } from '../utils/general_utils';
|
||||||
import { argsToQueryParams } from '../utils/url_utils';
|
import { argsToQueryParams } from '../utils/url_utils';
|
||||||
|
|
||||||
|
|
||||||
class Requests {
|
class Requests {
|
||||||
unpackResponse(response) {
|
unpackResponse(response) {
|
||||||
if (response.status >= 500) {
|
if (response.status >= 500) {
|
||||||
|
@ -508,7 +508,10 @@ fieldset[disabled] .btn-secondary.active {
|
|||||||
> pre,
|
> pre,
|
||||||
> select,
|
> select,
|
||||||
> span:not(.glyphicon),
|
> span:not(.glyphicon),
|
||||||
|
> p,
|
||||||
|
> p > span,
|
||||||
> textarea {
|
> textarea {
|
||||||
|
color: $ascribe-dark-blue;
|
||||||
font-family: $ascribe--font;
|
font-family: $ascribe--font;
|
||||||
font-weight: $ascribe--font-weight-light;
|
font-weight: $ascribe--font-weight-light;
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,4 @@
|
|||||||
@media (max-width: 550px) {
|
@media (max-width: 550px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,11 +30,14 @@ $ascribe-red-error: rgb(169, 68, 66);
|
|||||||
border-left: 3px solid rgba($ascribe-red-error, 1);
|
border-left: 3px solid rgba($ascribe-red-error, 1);
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
> span {
|
> p {
|
||||||
color: rgba($ascribe-red-error, 1);
|
> span {
|
||||||
font-size: .9em;
|
color: rgba($ascribe-red-error, 1);
|
||||||
margin-right: 1em;
|
font-size: .9em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
> input,
|
> input,
|
||||||
> textarea {
|
> textarea {
|
||||||
@ -86,10 +89,13 @@ $ascribe-red-error: rgb(169, 68, 66);
|
|||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> p {
|
||||||
color: rgba(0, 0, 0, .5);
|
height: 20px;
|
||||||
font-size: .9em;
|
margin-bottom: 0;
|
||||||
font-weight: normal;
|
> span {
|
||||||
|
font-size: .9em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
@ -107,6 +113,11 @@ $ascribe-red-error: rgb(169, 68, 66);
|
|||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .upload-button-wrapper {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
> input,
|
> input,
|
||||||
> pre,
|
> pre,
|
||||||
> textarea,
|
> textarea,
|
||||||
@ -139,7 +150,7 @@ $ascribe-red-error: rgb(169, 68, 66);
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> textarea{
|
> textarea {
|
||||||
color: #666;
|
color: #666;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -218,4 +229,4 @@ $ascribe-red-error: rgb(169, 68, 66);
|
|||||||
> span > span {
|
> span > span {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -177,3 +177,15 @@
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-button-wrapper {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span + .btn {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@import 'sluice/sluice_custom_style';
|
@import 'simple_prize/simple_prize_custom_style';
|
||||||
|
@import 'portfolioreview/portfolioreview_custom_style';
|
||||||
|
|
||||||
.ascribe-prize-app {
|
.ascribe-prize-app {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -178,7 +178,7 @@ $ikono--font: 'Helvetica Neue', 'Helvetica', sans-serif !important;
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ascribe-property > span {
|
.ascribe-property > p > span {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user