1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 17:33:14 +01:00

Finalizing basic submission flow with lazy evaluated form

This commit is contained in:
Tim Daubenschütz 2015-11-09 17:52:09 +01:00
parent deceb61c60
commit 9ae6b10add
9 changed files with 183 additions and 37 deletions

View File

@ -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/',
@ -21,7 +22,6 @@ function getPrizeApiUrls(subdomain) {
'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}/'
}; };
} }

View File

@ -5,30 +5,107 @@ import React from 'react';
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 InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable'; import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
import UploadFileButton from '../../../../../ascribe_buttons/upload_file_button'; import UploadFileButton from '../../../../../ascribe_buttons/upload_file_button';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import AppConstants from '../../../../../../constants/application_constants'; import AppConstants from '../../../../../../constants/application_constants';
import ApiUrls from '../../../../../../constants/api_urls';
import requests from '../../../../../../utils/requests';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { setCookie } from '../../../../../../utils/fetch_api_utils';
const { object } = React.PropTypes; const { object } = React.PropTypes;
const PRRegisterPieceForm = React.createClass({ const PRRegisterPieceForm = React.createClass({
propTypes: { propTypes: {
location: object location: object,
history: object,
currentUser: object
}, },
getInitialState(){ getInitialState(){
return { return {
isUploadReady: false isUploadReady: false,
piece: null
}; };
}, },
handleSuccess() { /**
* 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() {
const { currentUser } = this.props;
const { registerPieceForm,
digitalWorkForm,
proofOfPaymentForm,
supportingMaterialsForm,
additionalDataForm } = this.refs;
const additionalDataFormData = additionalDataForm.getFormData();
// composing data for piece registration
let registerPieceFormData = registerPieceForm.getFormData();
registerPieceFormData.digital_work_key = digitalWorkForm.state.file ? digitalWorkForm.state.file.key : '';
registerPieceFormData.terms = true;
// submitting the piece
requests
.post(ApiUrls.pieces_list, { body: registerPieceFormData })
.then(({ success, piece, notification }) => {
if(success) {
this.setState({
piece
}, () => {
supportingMaterialsForm.createBlobRoutine();
proofOfPaymentForm.createBlobRoutine();
//thumbnailForm.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);
}
})
.catch((err) => {
console.log(err);
});
},
getCreateBlobRoutine(fileClass) {
const { piece } = this.state;
if(piece && piece.id) {
if(fileClass === 'other_data') {
return {
url: ApiUrls.blob_otherdatas,
pieceId: piece.id
};
} else if(fileClass === 'thumbnail') {
return {
url: ApiUrls.blob_thumbnail,
pieceId: piece.id
};
}
} else {
return null;
}
}, },
render() { render() {
@ -37,8 +114,9 @@ const PRRegisterPieceForm = React.createClass({
return ( return (
<div className="register-piece--form"> <div className="register-piece--form">
<Form <Form
buttons={{}}
className="ascribe-form-bordered" className="ascribe-form-bordered"
ref="registerPieceFields"> ref="registerPieceForm">
<Property <Property
name='artist_name' name='artist_name'
label={getLangText('Full name')}> label={getLangText('Full name')}>
@ -64,35 +142,40 @@ const PRRegisterPieceForm = React.createClass({
min={1} min={1}
required/> required/>
</Property> </Property>
</Form>
<Form
className="ascribe-form-bordered"
ref="additionalData">
<Property
name='biography'
label={getLangText('Biography')}>
<InputTextAreaToggable
rows={1}
placeholder={getLangText('THIS NEEDS TEXT')}/>
</Property>
<Property <Property
name='artist_statement' name='artist_statement'
label={getLangText("Artist's statement")}> label={getLangText("Artist's statement")}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
placeholder={getLangText('THIS NEEDS TEXT')}/> 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>
<Property <Property
name='exhibition' name='exhibition'
label={getLangText('Exhibition / Publication history (optional)')}> label={getLangText('Exhibition / Publication history (optional)')}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
placeholder={getLangText('THIS NEEDS TEXT')}/> placeholder={getLangText('Enter exhibitions and publication history')}/>
</Property> </Property>
</Form> </Form>
<div className="input-upload-file-button-property"> <div className="input-upload-file-button-property">
{getLangText('Select the PDF with your work')} {getLangText('Select the PDF with your work')}
<UploadFileButton <UploadFileButton
ref="digitalWorkForm"
createBlobRoutine={{
url: ApiUrls.blob_digitalworks
}}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork' fileClass: 'digitalwork'
@ -111,6 +194,8 @@ const PRRegisterPieceForm = React.createClass({
<div className="input-upload-file-button-property"> <div className="input-upload-file-button-property">
{getLangText('Featured Cover photo')} {getLangText('Featured Cover photo')}
<UploadFileButton <UploadFileButton
ref="thumbnailForm"
createBlobRoutine={this.getCreateBlobRoutine('thumbnail')}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'thumbnail' fileClass: 'thumbnail'
@ -129,6 +214,8 @@ const PRRegisterPieceForm = React.createClass({
<div className="input-upload-file-button-property"> <div className="input-upload-file-button-property">
{getLangText('Supporting Materials (Optional)')} {getLangText('Supporting Materials (Optional)')}
<UploadFileButton <UploadFileButton
ref="supportingMaterialsForm"
createBlobRoutine={this.getCreateBlobRoutine('other_data')}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'other_data' fileClass: 'other_data'
@ -146,13 +233,16 @@ const PRRegisterPieceForm = React.createClass({
<div className="input-upload-file-button-property"> <div className="input-upload-file-button-property">
{getLangText('Proof of payment')} {getLangText('Proof of payment')}
<UploadFileButton <UploadFileButton
ref="proofOfPaymentForm"
createBlobRoutine={this.getCreateBlobRoutine('other_data')}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'other_data' fileClass: 'other_data'
}} }}
validation={{ validation={{
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
allowedExtensions: ['png', 'jpg']
}} }}
location={location} location={location}
fileClassToUpload={{ fileClassToUpload={{
@ -161,23 +251,23 @@ const PRRegisterPieceForm = React.createClass({
}}/> }}/>
</div> </div>
<Form <Form
className="ascribe-form-bordered" buttons={{}}
ref="terms"> className="ascribe-form-bordered">
<Property <Property
ref="termsForm"
name="terms" name="terms"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle"
style={{paddingBottom: 0}}> style={{paddingBottom: 0}}>
<InputCheckbox <span>
key="terms_explicitly" <input type="checkbox" value="true" />
defaultChecked={false}> {getLangText('By submitting this form, you agree to the Terms of Service of Portfolio Review.')}
&nbsp;{getLangText('I agree to the Terms and Conditions of the Portfolio Review')} </span>
</InputCheckbox>
</Property> </Property>
</Form> </Form>
<button <button
type="submit" type="submit"
className="btn btn-default btn-wide" className="btn btn-default btn-wide"
disabled={!this.state.isUploadReady}> onClick={this.submit}>
{getLangText('Submit to Portfolio Review')} {getLangText('Submit to Portfolio Review')}
</button> </button>
</div> </div>

View File

@ -1,14 +1,19 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link, History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; 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 PRRegisterPieceForm from './pr_forms/pr_register_piece_form';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getCookie } from '../../../../../utils/fetch_api_utils';
const { object } = React.PropTypes; const { object } = React.PropTypes;
@ -18,7 +23,37 @@ const PRRegisterPiece = React.createClass({
location: object 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() { render() {
const { currentUser } = this.state;
const { location } = this.props; const { location } = this.props;
setDocumentTitle(getLangText('Submission form')); setDocumentTitle(getLangText('Submission form'));
@ -28,14 +63,25 @@ const PRRegisterPiece = React.createClass({
<div className="register-piece--hero"> <div className="register-piece--hero">
<h1>Portfolio Review</h1> <h1>Portfolio Review</h1>
<h2>{getLangText('Submission closing on %s', ' 21 Dec 2015')}</h2> <h2>{getLangText('Submission closing on %s', ' 21 Dec 2015')}</h2>
<p>{getLangText('Submissions are open to everyone, we accept only PDFs.')}</p> <p>
<p>{getLangText('We accept only one PDF with up to 20 images from every participant.')}</p> {getLangText('Submissions are open to everyone, we accept only PDFs.')}
<p>{getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')}</p> </p>
<p>
{getLangText('We accept only one PDF with up to 20 images from every participant.')}
</p>
<p>
{getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')}
</p>
<p style={{marginTop: '1em'}}>
{getLangText("You're submitting as %s. ", currentUser.email)}
<Link to="/logout">{getLangText('Change account?')}</Link>
</p>
</div> </div>
</Col> </Col>
<Col xs={6}> <Col xs={6}>
<PRRegisterPieceForm <PRRegisterPieceForm
location={location}/> location={location}
currentUser={currentUser}/>
</Col> </Col>
</Row> </Row>
); );

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
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';
@ -26,7 +25,6 @@ let PrizeApp = React.createClass({
{children} {children}
<GlobalNotification /> <GlobalNotification />
<div id="modal" className="container"></div> <div id="modal" className="container"></div>
<Footer />
</div> </div>
); );
} }

View File

@ -78,6 +78,7 @@ const ROUTES = {
<Route <Route
path='password_reset' path='password_reset'
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(PasswordResetContainer)} /> component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(PasswordResetContainer)} />
<Route path='pieces/:pieceId' component={SPPieceContainer} />
<Route path='*' component={ErrorNotFoundPage} /> <Route path='*' component={ErrorNotFoundPage} />
</Route> </Route>
) )

View File

@ -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/',

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import Q from 'q'; import Q from 'q';
import moment from 'moment';
import { sanitize } from './general_utils'; import { sanitize } from './general_utils';
import AppConstants from '../constants/application_constants'; import AppConstants from '../constants/application_constants';
@ -70,12 +71,20 @@ 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');
console.log(exdate.utc());
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

View File

@ -222,7 +222,7 @@ $ascribe-red-error: rgb(169, 68, 66);
.input-upload-file-button-property { .input-upload-file-button-property {
background-color: white; background-color: white;
padding: 1.5em 0 1.5em 0; padding: 1em 0 1em 0;
text-align: right; text-align: right;
button { button {

View File

@ -31,7 +31,8 @@
} }
.register-piece--form { .register-piece--form {
margin-bottom: 3em;
form { form {
border-top: none; border-top: none;
} }