1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

ikono slides

This commit is contained in:
diminator 2015-09-15 13:15:29 +02:00
parent 9085a2bafe
commit b29bf38f13
5 changed files with 378 additions and 42 deletions

View File

@ -10,9 +10,6 @@ import Row from 'react-bootstrap/lib/Row';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import Property from '../../../../ascribe_forms/property';
import InputCheckbox from '../../../../ascribe_forms/input_checkbox';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
@ -136,7 +133,7 @@ let CylandRegisterPiece = React.createClass({
this.transitionTo('piece', {pieceId: this.state.piece.id});
},
// We need to increase the step to lock the forms that are already filed out
// We need to increase the step to lock the forms that are already filled out
incrementStep() {
// also increase step
let newStep = this.state.step + 1;

View File

@ -1,17 +1,8 @@
'use strict';
import React from 'react';
import Moment from 'moment';
import classNames from 'classnames';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import LoanForm from '../../../../../ascribe_forms/form_loan';
import Property from '../../../../../ascribe_forms/property';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
import ApiUrls from '../../../../../../constants/api_urls';
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
import { getLangText } from '../../../../../../utils/lang_utils';
@ -22,37 +13,30 @@ let IkonotvSubmitButton = React.createClass({
piece: React.PropTypes.object.isRequired
},
getSubmitButton() {
return (
<button
className={classNames('btn', 'btn-default', this.props.className)}>
{getLangText('Loan to IkonoTV')}
</button>
);
},
render() {
let piece = this.props.piece;
let startFrom = 1;
let today = new Moment();
let enddate = new Moment();
enddate.add(1, 'years');
// In the Ikonotv loan page a user has to complete two steps.
// Since every one of those steps is atomic a user should always be able to continue
// where he left of.
// This is why we start the process form slide 1/2 if the user has already finished
// it in another session.
if(piece && piece.extra_data && Object.keys(piece.extra_data).length > 0) {
startFrom = 1;
}
return (
<ModalWrapper
trigger={this.getSubmitButton()}
handleSuccess={this.props.handleSuccess}
title={getLangText('Loan to IkonoTV archive')}>
<LoanForm
id={{piece_id: this.props.piece.id}}
url={ApiUrls.ownership_loans_pieces}
email="submissions@ikono.org"
startdate={today}
enddate={enddate}
gallery="IkonoTV archive"
showPersonalMessage={false}
handleSuccess={this.props.handleSuccess} />
</ModalWrapper>
<ButtonLink
to="register_piece"
query={{
'slide_num': 0,
'start_from': startFrom,
'piece_id': piece.id
}}
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
{getLangText('Loan to IkonoTV')}
</ButtonLink>
);
}
});

View File

@ -0,0 +1,134 @@
'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 FurtherDetailsFileuploader from '../../../../../ascribe_detail/further_details_fileuploader';
import ApiUrls from '../../../../../../constants/api_urls';
import AppConstants from '../../../../../../constants/application_constants';
import requests from '../../../../../../utils/requests';
import { getLangText } from '../../../../../../utils/lang_utils';
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
let IkonotvAdditionalDataForm = React.createClass({
propTypes: {
handleSuccess: React.PropTypes.func.isRequired,
piece: React.PropTypes.object.isRequired,
disabled: React.PropTypes.bool
},
getInitialState() {
return {
isUploadReady: true
};
},
getFormData() {
let extradata = {};
let formRefs = this.refs.form.refs;
// Put additional fields in extra data object
Object
.keys(formRefs)
.forEach((fieldName) => {
extradata[fieldName] = formRefs[fieldName].state.value;
});
return {
extradata: extradata,
piece_id: this.props.piece.id
};
},
uploadStarted() {
this.setState({
isUploadReady: false
});
},
setIsUploadReady(isReady) {
this.setState({
isUploadReady: isReady
});
},
render() {
if(this.props.piece && this.props.piece.id) {
return (
<Form
disabled={this.props.disabled}
className="ascribe-form-bordered"
ref='form'
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.piece.id})}
handleSuccess={this.props.handleSuccess}
getFormData={this.getFormData}
buttons={
<button
type="submit"
className="btn ascribe-btn ascribe-btn-login"
disabled={!this.state.isUploadReady || this.props.disabled}>
{getLangText('Proceed to loan')}
</button>
}
spinner={
<div className="modal-footer">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
</div>
}>
<div className="ascribe-form-header">
<h3>
{getLangText('Provide supporting materials')}
</h3>
</div>
<Property
name='artist_bio'
label={getLangText('Artist Biography')}
editable={!this.props.disabled}>
<InputTextAreaToggable
rows={1}
editable={!this.props.disabled}
placeholder={getLangText('Enter the artist\'s biography...')}
required="required"/>
</Property>
<Property
name='conceptual_overview'
label={getLangText('Conceptual Overview')}
editable={!this.props.disabled}>
<InputTextAreaToggable
rows={1}
editable={!this.props.disabled}
placeholder={getLangText('Enter a conceptual overview...')}
required="required"/>
</Property>
<FurtherDetailsFileuploader
uploadStarted={this.uploadStarted}
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.fileOptional}
editable={!this.props.disabled}
pieceId={this.props.piece.id}
otherData={this.props.piece.other_data}
multiple={true}/>
</Form>
);
} else {
return (
<div className="ascribe-loading-position">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>
);
}
}
});
export default IkonotvAdditionalDataForm;

View File

@ -0,0 +1,220 @@
'use strict';
import React from 'react';
import Moment from 'moment';
import Router from 'react-router';
import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row';
import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions';
import GlobalNotificationModel from '../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import LoanForm from '../../../../ascribe_forms/form_loan';
import IkonotvAdditionalDataForm from './ascribe_forms/ikonotv_additional_data_form';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
import ApiUrls from '../../../../../constants/api_urls';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvRegisterPiece = React.createClass({
propTypes: {
handleSuccess: React.PropTypes.func,
piece: React.PropTypes.object.isRequired
},
mixins: [Router.Navigation, Router.State],
getInitialState(){
return mergeOptions(
UserStore.getState(),
PieceListStore.getState(),
PieceStore.getState(),
{
step: 0
});
},
componentDidMount() {
PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceStore.listen(this.onChange);
UserActions.fetchCurrentUser();
let queryParams = this.getQuery();
// Since every step of this register process is atomic,
// we may need to enter the process at step 1 or 2.
// If this is the case, we'll need the piece number to complete submission.
// It is encoded in the URL as a queryParam and we're checking for it here.
//
// We're using 'in' here as we want to know if 'piece_id' is present in the url,
// we don't care about the value.
if (queryParams && 'piece_id' in queryParams) {
PieceActions.fetchOne(queryParams.piece_id);
}
},
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
if(this.state.currentUser && this.state.currentUser.email) {
// we should also make the fineuploader component editable again
this.setState({
isFineUploaderActive: true
});
}
},
handleRegisterSuccess(response){
this.refreshPieceList();
// also start loading the piece for the next step
if(response && response.piece) {
PieceActions.updatePiece(response.piece);
}
this.incrementStep();
this.refs.slidesContainer.nextSlide();
},
handleAdditionalDataSuccess() {
// We need to refetch the piece again after submitting the additional data
// since we want it's otherData to be displayed when the user choses to click
// on the browsers back button.
PieceActions.fetchOne(this.state.piece.id);
this.refreshPieceList();
this.incrementStep();
this.refs.slidesContainer.nextSlide();
},
handleLoanSuccess(response) {
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
this.refreshPieceList();
PieceActions.fetchOne(this.state.piece.id);
this.transitionTo('piece', {pieceId: this.state.piece.id});
},
// We need to increase the step to lock the forms that are already filled out
incrementStep() {
// also increase step
let newStep = this.state.step + 1;
this.setState({
step: newStep
});
},
refreshPieceList() {
PieceListActions.fetchPieceList(
this.state.page,
this.state.pageSize,
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
},
changeSlide() {
// only transition to the login store, if user is not logged in
// ergo the currentUser object is not properly defined
if(this.state.currentUser && !this.state.currentUser.email) {
this.onLoggedOut();
}
},
// basically redirects to the second slide (index: 1), when the user is not logged in
onLoggedOut() {
this.transitionTo('login');
},
render() {
let today = new Moment();
let enddate = new Moment();
enddate.add(1, 'years');
return (
<SlidesContainer
ref="slidesContainer"
forwardProcess={true}
glyphiconClassNames={{
pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock'
}}>
<div data-slide-title={getLangText('Register work')}>
<Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<RegisterPieceForm
disabled={this.state.step > 0}
enableLocalHashing={false}
headerMessage={getLangText('Submit to Cyland Archive')}
submitMessage={getLangText('Submit')}
isFineUploaderActive={this.state.isFineUploaderActive}
handleSuccess={this.handleRegisterSuccess}
onLoggedOut={this.onLoggedOut} />
</Col>
</Row>
</div>
<div data-slide-title={getLangText('Additional details')}>
<Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<IkonotvAdditionalDataForm
disabled={this.state.step > 1}
handleSuccess={this.handleAdditionalDataSuccess}
piece={this.state.piece}/>
</Col>
</Row>
</div>
<div data-slide-title={getLangText('Loan')}>
<Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<LoanForm
loanHeading={getLangText('Loan to IkonoTV archive')}
id={{piece_id: this.state.piece.id}}
url={ApiUrls.ownership_loans_pieces}
email="submissions@ikono.org"
startdate={today}
enddate={enddate}
gallery="IkonoTV archive"
showPersonalMessage={false}
handleSuccess={this.handleLoanSuccess} />
</Col>
</Row>
</div>
</SlidesContainer>
);
}
});
export default IkonotvRegisterPiece;

View File

@ -21,6 +21,7 @@ import CylandPieceList from './components/cyland/cyland_piece_list';
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan';
import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece';
import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container';
import IkonotvContractNotifications from './components/ikonotv/ikonotv_contract_notifications';
@ -73,7 +74,7 @@ let ROUTES = {
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="request_loan" path="request_loan" handler={IkonotvRequestLoan} headerTitle="SEND NEW CONTRACT" aclName="acl_send_contract" />
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK"/>
<Route name="register_piece" path="register_piece" handler={IkonotvRegisterPiece} headerTitle="+ NEW WORK"/>
<Route name="pieces" path="collection" handler={IkonotvPieceList} headerTitle="COLLECTION"/>
<Route name="piece" path="pieces/:pieceId" handler={IkonotvPieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />