Merge branch 'tmp2' into AD-456-ikonotv-branded-page-for-registra

This commit is contained in:
diminator 2015-08-19 13:58:55 +02:00
commit 6d8d318e8d
14 changed files with 214 additions and 148 deletions

View File

@ -15,6 +15,8 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import FurtherDetailsFileuploader from './further_details_fileuploader';
import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let FurtherDetails = React.createClass({
propTypes: {
editable: React.PropTypes.bool,
@ -48,15 +50,6 @@ let FurtherDetails = React.createClass({
});
},
isReadyForFormSubmission(files) {
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
if(files.length > 0 && files[0].status === 'upload successful') {
return true;
} else {
return false;
}
},
render() {
//return (<span />);
return (
@ -88,7 +81,7 @@ let FurtherDetails = React.createClass({
<FurtherDetailsFileuploader
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={this.isReadyForFormSubmission}
isReadyForFormSubmission={isReadyForFormSubmission}
editable={this.props.editable}
pieceId={this.props.pieceId}
otherData={this.props.otherData}

View File

@ -16,6 +16,7 @@ import ApiUrls from '../../constants/api_urls';
import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({
@ -24,6 +25,7 @@ let RegisterPieceForm = React.createClass({
submitMessage: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
isFineUploaderActive: React.PropTypes.bool,
isFineUploaderEditable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
children: React.PropTypes.element,
onLoggedOut: React.PropTypes.func
@ -78,15 +80,6 @@ let RegisterPieceForm = React.createClass({
});
},
isReadyForFormSubmission(files) {
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
if (files.length > 0 && files[0].status === 'upload successful') {
return true;
} else {
return false;
}
},
render() {
let currentUser = this.state.currentUser;
let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
@ -117,7 +110,7 @@ let RegisterPieceForm = React.createClass({
<FileUploader
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={this.isReadyForFormSubmission}
isReadyForFormSubmission={isReadyForFormSubmission}
isFineUploaderActive={this.props.isFineUploaderActive}
onLoggedOut={this.props.onLoggedOut}
editable={this.props.isFineUploaderEditable}

View File

@ -145,7 +145,10 @@ let SlidesContainer = React.createClass({
<div className="no-margin row ascribe-breadcrumb-container">
{this.props.breadcrumbs.map((breadcrumb, i) => {
return (
<Col className="no-padding" sm={columnWidth}>
<Col
className="no-padding"
sm={columnWidth}
key={i}>
<div className="ascribe-breadcrumb">
<a className={this.state.slideNum === i ? 'active' : ''}>
{this.props.breadcrumbs[i]}

View File

@ -57,7 +57,13 @@ let FileDragAndDropDialog = React.createClass({
if(this.props.multipleFiles) {
return (
<span className="file-drag-and-drop-dialog">
{getLangText('Click or drag to add files')}
<p>{getLangText('Drag files here')}</p>
<p>{getLangText('or')}</p>
<span
className="btn btn-default"
onClick={this.props.onClick}>
{getLangText('choose files to upload')}
</span>
</span>
);
} else {

View File

@ -435,7 +435,9 @@ var ReactS3FineUploader = React.createClass({
},
onCancel(id) {
this.removeFileWithIdFromFilesToUpload(id);
// when a upload is canceled, we need to update this components file array
this.setStatusOfFile(id, 'canceled');
let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
@ -455,6 +457,7 @@ var ReactS3FineUploader = React.createClass({
},
onProgress(id, name, uploadedBytes, totalBytes) {
let newState = React.addons.update(this.state, {
filesToUpload: { [id]: {
progress: { $set: (uploadedBytes / totalBytes) * 100} }
@ -498,7 +501,9 @@ var ReactS3FineUploader = React.createClass({
let notification = new GlobalNotificationModel(getLangText('Couldn\'t delete file'), 'danger', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
} else {
this.removeFileWithIdFromFilesToUpload(id);
// To hide the file in this component, we need to set it's status to "deleted"
this.setStatusOfFile(id, 'deleted');
let notification = new GlobalNotificationModel(getLangText('File deleted'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
@ -521,7 +526,8 @@ var ReactS3FineUploader = React.createClass({
},
handleDeleteFile(fileId) {
// In some instances (when the file was already uploaded and is just displayed to the user)
// In some instances (when the file was already uploaded and is just displayed to the user
// - for example in the loan contract or additional files dialog)
// fineuploader does not register an id on the file (we do, don't be confused by this!).
// Since you can only delete a file by its id, we have to implement this method ourselves
//
@ -532,7 +538,7 @@ var ReactS3FineUploader = React.createClass({
if(this.state.filesToUpload[fileId].status !== 'online') {
// delete file from server
this.state.uploader.deleteFile(fileId);
// this is being continues in onDeleteFile, as
// this is being continued in onDeleteFile, as
// fineuploaders deleteFile does not return a correct callback or
// promise
} else {
@ -739,31 +745,23 @@ var ReactS3FineUploader = React.createClass({
this.setState(newState);
},
removeFileWithIdFromFilesToUpload(fileId) {
// also, sync files from state with the ones from fineuploader
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
// splice because I can
filesToUpload.splice(fileId, 1);
// set state
let newState = React.addons.update(this.state, {
filesToUpload: { $set: filesToUpload }
});
this.setState(newState);
},
setStatusOfFile(fileId, status) {
// also, sync files from state with the ones from fineuploader
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
// splice because I can
filesToUpload[fileId].status = status;
// is status is set to deleted or canceled, we also need to reset the progress
// back to zero
if(status === 'deleted' || status === 'canceled') {
filesToUpload[fileId].progress = 0;
}
// set state
let newState = React.addons.update(this.state, {
filesToUpload: { $set: filesToUpload }
});
this.setState(newState);
},

View File

@ -0,0 +1,16 @@
'use strict';
/**
* Returns a boolean if there has been at least one file uploaded
* successfully without it being deleted or canceled.
* @param {array of files} files provided by react fine uploader
* @return {Boolean}
*/
export function isReadyForFormSubmission(files) {
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
if (files.length > 0 && files[0].status === 'upload successful') {
return true;
} else {
return false;
}
}

View File

@ -3,16 +3,42 @@
import React from 'react';
import classNames from 'classnames';
import Moment from 'moment';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import CylandPieceSubmitForm from '../ascribe_forms/cyland_form_submit';
import LoanForm from '../../../../../ascribe_forms/form_loan';
import ApiUrls from '../../../../../../constants/api_urls';
import { getLangText } from '../../../../../../utils/lang_utils';
import { getAclFormMessage } from '../../../../../../utils/form_utils';
let CylandSubmitButton = React.createClass({
propTypes: {
className: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
piece: React.PropTypes.object.isRequired
piece: React.PropTypes.object.isRequired,
username: React.PropTypes.string
},
getInitialState() {
return WhitelabelStore.getState();
},
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
getSubmitButton() {
@ -25,16 +51,26 @@ let CylandSubmitButton = React.createClass({
},
render() {
let today = new Moment();
let loanEndDate = new Moment();
loanEndDate.add(1000, 'years');
return (
<ModalWrapper
trigger={this.getSubmitButton()}
handleSuccess={this.props.handleSuccess}
title={getLangText('Submit to Cyland')}>
<CylandPieceSubmitForm
piece={this.props.piece}
<LoanForm
message={getAclFormMessage('acl_loan', '\"' + this.props.piece.title + '\"', this.props.username)}
id={{piece_id: this.props.piece.id}}
url={ApiUrls.ownership_loans_pieces}
email={this.state.whitelabel.user}
gallery="Cyland Archive"
startdate={today}
enddate={loanEndDate}
showPersonalMessage={false}
handleSuccess={this.props.handleSuccess}/>
</ModalWrapper>
);
}
});

View File

@ -43,6 +43,7 @@ let CylandAdditionalDataForm = React.createClass({
extradata: extradata,
piece_id: this.props.piece.id
};
},
setIsUploadReady(isReady) {
@ -51,8 +52,15 @@ let CylandAdditionalDataForm = React.createClass({
});
},
isReadyForFormSubmission() {
return true;
isReadyForFormSubmission(files) {
let uploadedFiles = files.filter((file) => file.status === 'upload successful');
let uploadingFiles = files.filter((file) => file.status === 'submitting');
if (uploadedFiles.length > 0 && uploadingFiles.length === 0) {
return true;
} else {
return false;
}
},
render() {

View File

@ -1,89 +0,0 @@
'use strict';
import React from 'react';
import Form from '../../../../../ascribe_forms/form';
import Property from '../../../../../ascribe_forms/property';
import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
import Alert from 'react-bootstrap/lib/Alert';
import AppConstants from '../../../../../../constants/application_constants';
import ApiUrls from '../../../../../../constants/api_urls';
import { getLangText } from '../../../../../../utils/lang_utils.js';
import requests from '../../../../../../utils/requests';
let CylandPieceSubmitForm = React.createClass({
propTypes: {
piece: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},
render() {
//return (
// <Form
// ref='form'
// url={requests.prepareUrl(ApiUrls.piece_submit_to_prize, {piece_id: this.props.piece.id})}
// handleSuccess={this.props.handleSuccess}
// buttons={
// <div className="modal-footer">
// <p className="pull-right">
// <button
// className="btn btn-default btn-sm ascribe-margin-1px"
// type="submit">
// {getLangText('SUBMIT TO PRIZE')}
// </button>
// </p>
// </div>}
// spinner={
// <div className="modal-footer">
// <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
// </div>}>
// <Property
// name='artist_statement'
// label={getLangText('Artist statement')}
// editable={true}>
// <InputTextAreaToggable
// rows={1}
// editable={true}
// placeholder={getLangText('Enter your statement')}
// required="required"/>
// </Property>
// <Property
// name='work_description'
// label={getLangText('Work description')}
// editable={true}>
// <InputTextAreaToggable
// rows={1}
// editable={true}
// placeholder={getLangText('Enter the description for your work')}
// required="required"/>
// </Property>
// <Property
// name="terms"
// className="ascribe-settings-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>
// <Alert bsStyle='warning'>
// <p>{getLangText('Are you sure you want to submit to the prize?')}</p>
// <p>{getLangText('This is an irrevocable action%s', '.')}</p>
// </Alert>
// </Form>
//);
return null;
}
});
export default CylandPieceSubmitForm;

View File

@ -0,0 +1,20 @@
'use strict';
import React from 'react';
import constants from '../../../../constants/application_constants';
let Hero = React.createClass({
render() {
return (
<div className="hero">
<img
className="logo" src={constants.whitelabel.logo}
alt="Sluice Art Prize"
height="200px"/>
</div>
);
}
});
export default Hero;

View File

@ -0,0 +1,77 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import { mergeOptions } from '../../../../../utils/general_utils';
let CylandLanding = React.createClass({
mixins: [Router.Navigation],
getInitialState() {
return mergeOptions(
UserStore.getState()
);
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
// if user is already logged in, redirect him to piece list
if(this.state.currentUser && this.state.currentUser.email) {
// FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => this.replaceWith('pieces'), 0);
}
},
render() {
return (
<div className="container ascribe-form-wrapper">
<div className="row">
<div className="col-xs-12 wp-landing-wrapper">
<div className="row">
<img src="https://s3.amazonaws.com/upload.uxpin/files/308247/312701/logo.gif" />
</div>
<div className="row">
<div className="col-sm-6">
<div>
Existing ascribe user?
</div>
<ButtonLink to="login">
Log in
</ButtonLink>
</div>
<div className="col-sm-6">
<div>
Do you need an account?
</div>
<ButtonLink to="signup">
Sign up
</ButtonLink>
</div>
</div>
</div>
</div>
</div>
);
}
});
export default CylandLanding;

View File

@ -85,17 +85,6 @@ let CylandRegisterPiece = React.createClass({
handleRegisterSuccess(response){
// once the user was able to register a piece successfully, we need to make sure to keep
// the piece list up to date
PieceListActions.fetchPieceList(
this.state.page,
this.state.pageSize,
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
// also start loading the piece for the next step
if(response && response.piece) {
PieceActions.updatePiece(response.piece);
@ -111,6 +100,18 @@ let CylandRegisterPiece = React.createClass({
handleLoanSuccess(response) {
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
// once the user was able to register + loan a piece successfully, we need to make sure to keep
// the piece list up to date
PieceListActions.fetchPieceList(
this.state.page,
this.state.pageSize,
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
PieceActions.fetchOne(this.state.piece.id);
this.transitionTo('piece', {pieceId: this.state.piece.id});
},

View File

@ -30,6 +30,7 @@
border: 1px solid #EEE;
border-right: 1px solid rgba(0, 0, 0, 0);
margin-bottom: 0.6em;
background-color: white;
.active {
color: #666;

View File

@ -41,6 +41,9 @@
position: relative;
display: inline-block;
margin-left: .7em;
margin-right: .7em;
.delete-file {
display: block;
background-color: black;