mirror of
https://github.com/ascribe/onion.git
synced 2024-06-30 13:41:57 +02:00
Merge remote-tracking branch 'remotes/origin/master' into AD-613-cyland-white-label-page
This commit is contained in:
commit
12dde0458c
|
@ -53,12 +53,12 @@ var config = {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
var SERVER_URL = process.env.ONION_SERVER_URL || 'http://staging.ascribe.io/';
|
var SERVER_URL = process.env.ONION_SERVER_URL || 'https://staging.ascribe.io/';
|
||||||
|
|
||||||
var constants = {
|
var constants = {
|
||||||
BASE_URL: (function () { var baseUrl = process.env.ONION_BASE_URL || '/'; return baseUrl + (baseUrl.match(/\/$/) ? '' : '/'); })(),
|
BASE_URL: (function () { var baseUrl = process.env.ONION_BASE_URL || '/'; return baseUrl + (baseUrl.match(/\/$/) ? '' : '/'); })(),
|
||||||
SERVER_URL: SERVER_URL,
|
SERVER_URL: SERVER_URL,
|
||||||
API_ENDPOINT: SERVER_URL + 'api/' || 'http://staging.ascribe.io/api/',
|
API_ENDPOINT: SERVER_URL + 'api/' || 'https://staging.ascribe.io/api/',
|
||||||
DEBUG: !argv.production,
|
DEBUG: !argv.production,
|
||||||
CREDENTIALS: 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
|
CREDENTIALS: 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,151 +3,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Router from 'react-router';
|
import Router from 'react-router';
|
||||||
|
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|
||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
|
||||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
|
||||||
|
|
||||||
import AccordionListItemEditionWidget from './accordion_list_item_edition_widget';
|
|
||||||
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
|
|
||||||
|
|
||||||
import PieceListActions from '../../actions/piece_list_actions';
|
|
||||||
import PieceListStore from '../../stores/piece_list_store';
|
|
||||||
|
|
||||||
import WhitelabelStore from '../../stores/whitelabel_store';
|
|
||||||
|
|
||||||
import EditionListActions from '../../actions/edition_list_actions';
|
|
||||||
|
|
||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
|
||||||
|
|
||||||
import AclProxy from '../acl_proxy';
|
|
||||||
import SubmitToPrizeButton from '../whitelabel/prize/components/ascribe_buttons/submit_to_prize_button';
|
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
|
||||||
import { mergeOptions } from '../../utils/general_utils';
|
|
||||||
|
|
||||||
let Link = Router.Link;
|
|
||||||
|
|
||||||
let AccordionListItem = React.createClass({
|
let AccordionListItem = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
badge: React.PropTypes.object,
|
||||||
className: React.PropTypes.string,
|
className: React.PropTypes.string,
|
||||||
content: React.PropTypes.object,
|
thumbnail: React.PropTypes.object,
|
||||||
children: React.PropTypes.object
|
heading: React.PropTypes.object,
|
||||||
|
subheading: React.PropTypes.object,
|
||||||
|
subsubheading: React.PropTypes.object,
|
||||||
|
buttons: React.PropTypes.object,
|
||||||
|
children: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return mergeOptions(
|
|
||||||
{
|
|
||||||
showCreateEditionsDialog: false
|
|
||||||
},
|
|
||||||
PieceListStore.getState(),
|
|
||||||
WhitelabelStore.getState()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
PieceListStore.listen(this.onChange);
|
|
||||||
WhitelabelStore.listen(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
PieceListStore.unlisten(this.onChange);
|
|
||||||
WhitelabelStore.unlisten(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(state) {
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
getGlyphicon(){
|
|
||||||
if (this.props.content.requestAction) {
|
|
||||||
return (
|
|
||||||
<OverlayTrigger
|
|
||||||
delay={500}
|
|
||||||
placement="left"
|
|
||||||
overlay={<Tooltip>{getLangText('You have actions pending in one of your editions')}</Tooltip>}>
|
|
||||||
<Glyphicon glyph='bell'/>
|
|
||||||
</OverlayTrigger>);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleCreateEditionsDialog() {
|
|
||||||
this.setState({
|
|
||||||
showCreateEditionsDialog: !this.state.showCreateEditionsDialog
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEditionCreationSuccess() {
|
|
||||||
PieceListActions.updatePropertyForPiece({pieceId: this.props.content.id, key: 'num_editions', value: 0});
|
|
||||||
|
|
||||||
this.toggleCreateEditionsDialog();
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSubmitPrizeSuccess(response) {
|
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
|
||||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
},
|
|
||||||
|
|
||||||
onPollingSuccess(pieceId) {
|
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
|
||||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
|
||||||
EditionListActions.toggleEditionList(pieceId);
|
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCreateEditionsDialog() {
|
|
||||||
if (this.props.content.num_editions < 1 && this.state.showCreateEditionsDialog) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2">
|
|
||||||
<CreateEditionsForm
|
|
||||||
pieceId={this.props.content.id}
|
|
||||||
handleSuccess={this.handleEditionCreationSuccess}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getLicences() {
|
|
||||||
// convert this to acl_view_licences later
|
|
||||||
if (this.state.whitelabel && this.state.whitelabel.name === 'Creative Commons France') {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<span>, </span>
|
|
||||||
<a href={this.props.content.license_type.url} target="_blank">
|
|
||||||
{getLangText('%s license', this.props.content.license_type.code)}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let linkData;
|
|
||||||
|
|
||||||
if (this.props.content.num_editions < 1 || !this.props.content.first_edition) {
|
|
||||||
linkData = {
|
|
||||||
to: 'piece',
|
|
||||||
params: {
|
|
||||||
pieceId: this.props.content.id
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
linkData = {
|
|
||||||
to: 'edition',
|
|
||||||
params: {
|
|
||||||
editionId: this.props.content.first_edition.bitcoin_id
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
@ -155,52 +28,22 @@ let AccordionListItem = React.createClass({
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<div className="col-xs-4 col-sm-3 col-md-2 col-lg-2 clear-paddings">
|
<div className="col-xs-4 col-sm-3 col-md-2 col-lg-2 clear-paddings">
|
||||||
<div className="thumbnail-wrapper">
|
<div className="thumbnail-wrapper">
|
||||||
<Link {...linkData}>
|
{this.props.thumbnail}
|
||||||
<img src={this.props.content.thumbnail.url_safe}/>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xs-8 col-sm-9 col-md-9 col-lg-9 col-md-offset-1 col-lg-offset-1 accordion-list-item-header">
|
<div className="col-xs-8 col-sm-9 col-md-9 col-lg-9 col-md-offset-1 col-lg-offset-1 accordion-list-item-header">
|
||||||
<Link {...linkData}>
|
{this.props.heading}
|
||||||
<h1>{this.props.content.title}</h1>
|
{this.props.subheading}
|
||||||
</Link>
|
{this.props.subsubheading}
|
||||||
|
{this.props.buttons}
|
||||||
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<span className="pull-left">{this.props.content.date_created.split('-')[0]}</span>
|
|
||||||
|
|
||||||
<AclProxy
|
|
||||||
aclObject={this.props.content.acl}
|
|
||||||
aclName="acl_view_editions">
|
|
||||||
<AccordionListItemEditionWidget
|
|
||||||
className="pull-right"
|
|
||||||
piece={this.props.content}
|
|
||||||
toggleCreateEditionsDialog={this.toggleCreateEditionsDialog}
|
|
||||||
onPollingSuccess={this.onPollingSuccess}/>
|
|
||||||
</AclProxy>
|
|
||||||
<AclProxy
|
|
||||||
aclObject={this.props.content.acl}
|
|
||||||
aclName="acl_submit_to_prize">
|
|
||||||
<SubmitToPrizeButton
|
|
||||||
className="pull-right"
|
|
||||||
piece={this.props.content}
|
|
||||||
handleSuccess={this.handleSubmitPrizeSuccess}/>
|
|
||||||
</AclProxy>
|
|
||||||
{this.getLicences()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<span style={{'clear': 'both'}}></span>
|
<span style={{'clear': 'both'}}></span>
|
||||||
|
|
||||||
<div className="request-action-batch">
|
<div className="request-action-batch">
|
||||||
{this.getGlyphicon()}
|
{this.props.badge}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.getCreateEditionsDialog()}
|
|
||||||
|
|
||||||
{/* this.props.children is AccordionListItemTableEditions */}
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Router from 'react-router';
|
||||||
|
|
||||||
|
import AccordionListItem from './accordion_list_item';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
let Link = Router.Link;
|
||||||
|
|
||||||
|
|
||||||
|
let AccordionListItemPiece = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
className: React.PropTypes.string,
|
||||||
|
piece: React.PropTypes.object,
|
||||||
|
children: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element
|
||||||
|
]),
|
||||||
|
subsubheading: React.PropTypes.object,
|
||||||
|
buttons: React.PropTypes.object,
|
||||||
|
badge: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
|
getLinkData(){
|
||||||
|
let linkData;
|
||||||
|
|
||||||
|
if (this.props.piece.num_editions < 1 || !this.props.piece.first_edition) {
|
||||||
|
linkData = {
|
||||||
|
to: 'piece',
|
||||||
|
params: {
|
||||||
|
pieceId: this.props.piece.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
linkData = {
|
||||||
|
to: 'edition',
|
||||||
|
params: {
|
||||||
|
editionId: this.props.piece.first_edition.bitcoin_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return linkData;
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<AccordionListItem
|
||||||
|
className={this.props.className}
|
||||||
|
thumbnail={
|
||||||
|
<Link {...this.getLinkData()}>
|
||||||
|
<img src={this.props.piece.thumbnail.url_safe}/>
|
||||||
|
</Link>}
|
||||||
|
heading={
|
||||||
|
<Link {...this.getLinkData()}>
|
||||||
|
<h1>{this.props.piece.title}</h1>
|
||||||
|
</Link>}
|
||||||
|
subheading={
|
||||||
|
<h3>
|
||||||
|
{getLangText('by ')}
|
||||||
|
{this.props.artistName ? this.props.artistName : this.props.piece.artist_name}
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
subsubheading={this.props.subsubheading}
|
||||||
|
buttons={this.props.buttons}
|
||||||
|
badge={this.props.badge}
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</AccordionListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AccordionListItemPiece;
|
|
@ -0,0 +1,156 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
|
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||||
|
|
||||||
|
import AccordionListItemPiece from './accordion_list_item_piece';
|
||||||
|
import AccordionListItemEditionWidget from './accordion_list_item_edition_widget';
|
||||||
|
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
|
||||||
|
|
||||||
|
import PieceListActions from '../../actions/piece_list_actions';
|
||||||
|
import PieceListStore from '../../stores/piece_list_store';
|
||||||
|
|
||||||
|
import WhitelabelStore from '../../stores/whitelabel_store';
|
||||||
|
|
||||||
|
import EditionListActions from '../../actions/edition_list_actions';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import AclProxy from '../acl_proxy';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
import { mergeOptions } from '../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
|
let AccordionListItemWallet = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
className: React.PropTypes.string,
|
||||||
|
content: React.PropTypes.object,
|
||||||
|
children: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return mergeOptions(
|
||||||
|
{
|
||||||
|
showCreateEditionsDialog: false
|
||||||
|
},
|
||||||
|
PieceListStore.getState(),
|
||||||
|
WhitelabelStore.getState()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PieceListStore.listen(this.onChange);
|
||||||
|
WhitelabelStore.listen(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
PieceListStore.unlisten(this.onChange);
|
||||||
|
WhitelabelStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
getGlyphicon(){
|
||||||
|
if (this.props.content.requestAction) {
|
||||||
|
return (
|
||||||
|
<OverlayTrigger
|
||||||
|
delay={500}
|
||||||
|
placement="left"
|
||||||
|
overlay={<Tooltip>{getLangText('You have actions pending in one of your editions')}</Tooltip>}>
|
||||||
|
<Glyphicon glyph='bell'/>
|
||||||
|
</OverlayTrigger>);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleCreateEditionsDialog() {
|
||||||
|
this.setState({
|
||||||
|
showCreateEditionsDialog: !this.state.showCreateEditionsDialog
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEditionCreationSuccess() {
|
||||||
|
PieceListActions.updatePropertyForPiece({pieceId: this.props.content.id, key: 'num_editions', value: 0});
|
||||||
|
|
||||||
|
this.toggleCreateEditionsDialog();
|
||||||
|
},
|
||||||
|
|
||||||
|
onPollingSuccess(pieceId) {
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
EditionListActions.toggleEditionList(pieceId);
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
getCreateEditionsDialog() {
|
||||||
|
if (this.props.content.num_editions < 1 && this.state.showCreateEditionsDialog) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2">
|
||||||
|
<CreateEditionsForm
|
||||||
|
pieceId={this.props.content.id}
|
||||||
|
handleSuccess={this.handleEditionCreationSuccess}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getLicences() {
|
||||||
|
// convert this to acl_view_licences later
|
||||||
|
if (this.state.whitelabel && this.state.whitelabel.name === 'Creative Commons France') {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<span>, </span>
|
||||||
|
<a href={this.props.content.license_type.url} target="_blank">
|
||||||
|
{getLangText('%s license', this.props.content.license_type.code)}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccordionListItemPiece
|
||||||
|
className={this.props.className}
|
||||||
|
piece={this.props.content}
|
||||||
|
subsubheading={
|
||||||
|
<div className="pull-left">
|
||||||
|
<span>{this.props.content.date_created.split('-')[0]}</span>
|
||||||
|
{this.getLicences()}
|
||||||
|
</div>}
|
||||||
|
buttons={
|
||||||
|
<div>
|
||||||
|
<AclProxy
|
||||||
|
aclObject={this.props.content.acl}
|
||||||
|
aclName="acl_view_editions">
|
||||||
|
<AccordionListItemEditionWidget
|
||||||
|
className="pull-right"
|
||||||
|
piece={this.props.content}
|
||||||
|
toggleCreateEditionsDialog={this.toggleCreateEditionsDialog}
|
||||||
|
onPollingSuccess={this.onPollingSuccess}/>
|
||||||
|
</AclProxy>
|
||||||
|
</div>}
|
||||||
|
badge={this.getGlyphicon()}>
|
||||||
|
{this.getCreateEditionsDialog()}
|
||||||
|
{/* this.props.children is AccordionListItemTableEditions */}
|
||||||
|
{this.props.children}
|
||||||
|
</AccordionListItemPiece>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AccordionListItemWallet;
|
|
@ -23,7 +23,7 @@ const CollapsibleParagraph = React.createClass({
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
expanded: false
|
expanded: this.props.defaultExpanded
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ let DetailProperty = React.createClass({
|
||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
separator: ':',
|
separator: '',
|
||||||
labelClassName: 'col-xs-3 col-sm-3 col-md-2 col-lg-2',
|
labelClassName: 'col-xs-3 col-sm-3 col-md-2 col-lg-2 col-xs-height col-bottom ascribe-detail-property-label',
|
||||||
valueClassName: 'col-xs-9 col-sm-9 col-md-10 col-lg-10'
|
valueClassName: 'col-xs-9 col-sm-9 col-md-10 col-lg-10 col-xs-height col-bottom ascribe-detail-property-value'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -52,11 +52,11 @@ let DetailProperty = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="row ascribe-detail-property">
|
<div className="row ascribe-detail-property">
|
||||||
<div className="row-same-height">
|
<div className="row-same-height">
|
||||||
<div className={this.props.labelClassName + ' col-xs-height col-bottom ascribe-detail-property-label'}>
|
<div className={this.props.labelClassName}>
|
||||||
{ this.props.label + this.props.separator}
|
{ this.props.label } { this.props.separator}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={this.props.valueClassName + ' col-xs-height col-bottom ascribe-detail-property-value'}
|
className={this.props.valueClassName}
|
||||||
style={styles}>
|
style={styles}>
|
||||||
{value}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,33 @@ const EMBED_IFRAME_HEIGHT = {
|
||||||
|
|
||||||
let MediaContainer = React.createClass({
|
let MediaContainer = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
content: React.PropTypes.object
|
content: React.PropTypes.object,
|
||||||
|
refreshObject: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {timerId: null};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (!this.props.content.digital_work) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let isEncoding = this.props.content.digital_work.isEncoding;
|
||||||
|
if (this.props.content.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) {
|
||||||
|
let timerId = window.setInterval(this.props.refreshObject, 10000);
|
||||||
|
this.setState({timerId: timerId});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUpdate() {
|
||||||
|
if (this.props.content.digital_work.isEncoding === 100) {
|
||||||
|
window.clearInterval(this.state.timerId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.clearInterval(this.state.timerId);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,38 +1,14 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Router from 'react-router';
|
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
import DetailProperty from './detail_property';
|
|
||||||
|
|
||||||
import UserActions from '../../actions/user_actions';
|
|
||||||
import UserStore from '../../stores/user_store';
|
|
||||||
|
|
||||||
import PieceListActions from '../../actions/piece_list_actions';
|
|
||||||
import PieceListStore from '../../stores/piece_list_store';
|
|
||||||
|
|
||||||
import EditionListActions from '../../actions/edition_list_actions';
|
|
||||||
|
|
||||||
import PieceActions from '../../actions/piece_actions';
|
import PieceActions from '../../actions/piece_actions';
|
||||||
|
|
||||||
import MediaContainer from './media_container';
|
import MediaContainer from './media_container';
|
||||||
|
|
||||||
import EditionDetailProperty from './detail_property';
|
|
||||||
|
|
||||||
import AclButtonList from './../ascribe_buttons/acl_button_list';
|
|
||||||
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
|
|
||||||
import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
|
|
||||||
import DeleteButton from '../ascribe_buttons/delete_button';
|
|
||||||
|
|
||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
|
||||||
import { mergeOptions } from '../../utils/general_utils';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements display-specific functionality
|
* This is the component that implements display-specific functionality
|
||||||
|
@ -40,97 +16,16 @@ import { mergeOptions } from '../../utils/general_utils';
|
||||||
let Piece = React.createClass({
|
let Piece = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
piece: React.PropTypes.object,
|
piece: React.PropTypes.object,
|
||||||
|
header: React.PropTypes.object,
|
||||||
|
subheader: React.PropTypes.object,
|
||||||
|
buttons: React.PropTypes.object,
|
||||||
loadPiece: React.PropTypes.func,
|
loadPiece: React.PropTypes.func,
|
||||||
children: React.PropTypes.object
|
children: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
|
||||||
|
|
||||||
getInitialState() {
|
updateObject() {
|
||||||
return mergeOptions(
|
return PieceActions.fetchOne(this.props.piece.id);
|
||||||
UserStore.getState(),
|
|
||||||
PieceListStore.getState(),
|
|
||||||
{
|
|
||||||
showCreateEditionsDialog: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
UserStore.listen(this.onChange);
|
|
||||||
PieceListStore.listen(this.onChange);
|
|
||||||
UserActions.fetchCurrentUser();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
UserStore.unlisten(this.onChange);
|
|
||||||
PieceListStore.unlisten(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(state) {
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleCreateEditionsDialog() {
|
|
||||||
this.setState({
|
|
||||||
showCreateEditionsDialog: !this.state.showCreateEditionsDialog
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEditionCreationSuccess() {
|
|
||||||
PieceActions.updateProperty({key: 'num_editions', value: 0});
|
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
|
||||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
|
||||||
this.toggleCreateEditionsDialog();
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDeleteSuccess(response) {
|
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
|
||||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
|
||||||
|
|
||||||
// since we're deleting a piece, we just need to close
|
|
||||||
// all editions dialogs and not reload them
|
|
||||||
EditionListActions.closeAllEditionLists();
|
|
||||||
EditionListActions.clearAllEditionSelections();
|
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(response.notification, 'success');
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
|
|
||||||
this.transitionTo('pieces');
|
|
||||||
},
|
|
||||||
|
|
||||||
getCreateEditionsDialog() {
|
|
||||||
if(this.props.piece.num_editions < 1 && this.state.showCreateEditionsDialog) {
|
|
||||||
return (
|
|
||||||
<div style={{marginTop: '1em'}}>
|
|
||||||
<CreateEditionsForm
|
|
||||||
pieceId={this.props.piece.id}
|
|
||||||
handleSuccess={this.handleEditionCreationSuccess} />
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (<hr/>);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePollingSuccess(pieceId, numEditions) {
|
|
||||||
|
|
||||||
// we need to refresh the num_editions property of the actual piece we're looking at
|
|
||||||
PieceActions.updateProperty({
|
|
||||||
key: 'num_editions',
|
|
||||||
value: numEditions
|
|
||||||
});
|
|
||||||
|
|
||||||
// as well as its representation in the collection
|
|
||||||
// btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion
|
|
||||||
// list item also uses the firstEdition property which we can only get from the server in that case.
|
|
||||||
// Therefore we need to at least refetch the changed piece from the server or on our case simply all
|
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
|
||||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -138,38 +33,14 @@ let Piece = React.createClass({
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={6}>
|
<Col md={6}>
|
||||||
<MediaContainer
|
<MediaContainer
|
||||||
|
refreshObject={this.updateObject}
|
||||||
content={this.props.piece}/>
|
content={this.props.piece}/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6} className="ascribe-edition-details">
|
<Col md={6} className="ascribe-edition-details">
|
||||||
<div className="ascribe-detail-header">
|
{this.props.header}
|
||||||
<h1 className="ascribe-detail-title">{this.props.piece.title}</h1>
|
{this.props.subheader}
|
||||||
<hr/>
|
{this.props.buttons}
|
||||||
<EditionDetailProperty label="BY" value={this.props.piece.artist_name} />
|
|
||||||
<EditionDetailProperty label="DATE" value={ this.props.piece.date_created.slice(0, 4) } />
|
|
||||||
{this.props.piece.num_editions > 0 ? <EditionDetailProperty label="EDITIONS" value={ this.props.piece.num_editions } /> : null}
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
<div className="ascribe-detail-header">
|
|
||||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.props.piece.user_registered } />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AclButtonList
|
|
||||||
className="text-center ascribe-button-list"
|
|
||||||
availableAcls={this.props.piece.acl}
|
|
||||||
editions={this.props.piece}
|
|
||||||
handleSuccess={this.props.loadPiece}>
|
|
||||||
<CreateEditionsButton
|
|
||||||
label={getLangText('CREATE EDITIONS')}
|
|
||||||
className="btn-sm"
|
|
||||||
piece={this.props.piece}
|
|
||||||
toggleCreateEditionsDialog={this.toggleCreateEditionsDialog}
|
|
||||||
onPollingSuccess={this.handlePollingSuccess}/>
|
|
||||||
<DeleteButton
|
|
||||||
handleSuccess={this.handleDeleteSuccess}
|
|
||||||
piece={this.props.piece}/>
|
|
||||||
</AclButtonList>
|
|
||||||
|
|
||||||
{this.getCreateEditionsDialog()}
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -1,37 +1,59 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Router from 'react-router';
|
||||||
|
|
||||||
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 PieceListActions from '../../actions/piece_list_actions';
|
||||||
|
import PieceListStore from '../../stores/piece_list_store';
|
||||||
|
|
||||||
|
import UserActions from '../../actions/user_actions';
|
||||||
|
import UserStore from '../../stores/user_store';
|
||||||
|
|
||||||
|
import EditionListActions from '../../actions/edition_list_actions';
|
||||||
|
|
||||||
import Piece from './piece';
|
import Piece from './piece';
|
||||||
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
|
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
|
||||||
import FurtherDetails from './further_details';
|
import FurtherDetails from './further_details';
|
||||||
|
|
||||||
|
import DetailProperty from './detail_property';
|
||||||
|
|
||||||
|
import AclButtonList from './../ascribe_buttons/acl_button_list';
|
||||||
|
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
|
||||||
|
import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
|
||||||
|
import DeleteButton from '../ascribe_buttons/delete_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 { mergeOptions } from '../../utils/general_utils';
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements resource/data specific functionality
|
* This is the component that implements resource/data specific functionality
|
||||||
*/
|
*/
|
||||||
let PieceContainer = React.createClass({
|
let PieceContainer = React.createClass({
|
||||||
getInitialState() {
|
|
||||||
return PieceStore.getState();
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(state) {
|
mixins: [Router.Navigation],
|
||||||
this.setState(state);
|
|
||||||
if (!state.piece.digital_work) {
|
getInitialState() {
|
||||||
return;
|
return mergeOptions(
|
||||||
}
|
UserStore.getState(),
|
||||||
let isEncoding = state.piece.digital_work.isEncoding;
|
PieceListStore.getState(),
|
||||||
if (state.piece.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) {
|
PieceStore.getState(),
|
||||||
let timerId = window.setInterval(() => PieceActions.fetchOne(this.props.params.pieceId), 10000);
|
{
|
||||||
this.setState({timerId: timerId});
|
showCreateEditionsDialog: false
|
||||||
}
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
PieceListStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
PieceStore.listen(this.onChange);
|
PieceStore.listen(this.onChange);
|
||||||
PieceActions.fetchOne(this.props.params.pieceId);
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
},
|
},
|
||||||
|
@ -42,21 +64,121 @@ let PieceContainer = React.createClass({
|
||||||
// as it will otherwise display wrong/old data once the user loads
|
// as it will otherwise display wrong/old data once the user loads
|
||||||
// the piece detail a second time
|
// the piece detail a second time
|
||||||
PieceActions.updatePiece({});
|
PieceActions.updatePiece({});
|
||||||
window.clearInterval(this.state.timerId);
|
|
||||||
PieceStore.unlisten(this.onChange);
|
PieceStore.unlisten(this.onChange);
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
PieceListStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
loadPiece() {
|
loadPiece() {
|
||||||
PieceActions.fetchOne(this.props.params.pieceId);
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
toggleCreateEditionsDialog() {
|
||||||
|
this.setState({
|
||||||
|
showCreateEditionsDialog: !this.state.showCreateEditionsDialog
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEditionCreationSuccess() {
|
||||||
|
PieceActions.updateProperty({key: 'num_editions', value: 0});
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
this.toggleCreateEditionsDialog();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteSuccess(response) {
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
|
||||||
|
// since we're deleting a piece, we just need to close
|
||||||
|
// all editions dialogs and not reload them
|
||||||
|
EditionListActions.closeAllEditionLists();
|
||||||
|
EditionListActions.clearAllEditionSelections();
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success');
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
|
||||||
|
this.transitionTo('pieces');
|
||||||
|
},
|
||||||
|
|
||||||
|
getCreateEditionsDialog() {
|
||||||
|
if(this.state.piece.num_editions < 1 && this.state.showCreateEditionsDialog) {
|
||||||
|
return (
|
||||||
|
<div style={{marginTop: '1em'}}>
|
||||||
|
<CreateEditionsForm
|
||||||
|
pieceId={this.state.piece.id}
|
||||||
|
handleSuccess={this.handleEditionCreationSuccess} />
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (<hr/>);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handlePollingSuccess(pieceId, numEditions) {
|
||||||
|
|
||||||
|
// we need to refresh the num_editions property of the actual piece we're looking at
|
||||||
|
PieceActions.updateProperty({
|
||||||
|
key: 'num_editions',
|
||||||
|
value: numEditions
|
||||||
|
});
|
||||||
|
|
||||||
|
// as well as its representation in the collection
|
||||||
|
// btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion
|
||||||
|
// list item also uses the firstEdition property which we can only get from the server in that case.
|
||||||
|
// Therefore we need to at least refetch the changed piece from the server or on our case simply all
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if('title' in this.state.piece) {
|
if('title' in this.state.piece) {
|
||||||
return (
|
return (
|
||||||
<Piece
|
<Piece
|
||||||
piece={this.state.piece}
|
piece={this.state.piece}
|
||||||
loadPiece={this.loadPiece}>
|
loadPiece={this.loadPiece}
|
||||||
|
header={
|
||||||
|
<div className="ascribe-detail-header">
|
||||||
|
<hr/>
|
||||||
|
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||||
|
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
||||||
|
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
|
||||||
|
{this.state.piece.num_editions > 0 ? <DetailProperty label="EDITIONS" value={ this.state.piece.num_editions } /> : null}
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
subheader={
|
||||||
|
<div className="ascribe-detail-header">
|
||||||
|
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<AclButtonList
|
||||||
|
className="text-center ascribe-button-list"
|
||||||
|
availableAcls={this.state.piece.acl}
|
||||||
|
editions={this.state.piece}
|
||||||
|
handleSuccess={this.loadPiece}>
|
||||||
|
<CreateEditionsButton
|
||||||
|
label={getLangText('CREATE EDITIONS')}
|
||||||
|
className="btn-sm"
|
||||||
|
piece={this.state.piece}
|
||||||
|
toggleCreateEditionsDialog={this.toggleCreateEditionsDialog}
|
||||||
|
onPollingSuccess={this.handlePollingSuccess}/>
|
||||||
|
<DeleteButton
|
||||||
|
handleSuccess={this.handleDeleteSuccess}
|
||||||
|
piece={this.state.piece}/>
|
||||||
|
</AclButtonList>
|
||||||
|
}>
|
||||||
|
{this.getCreateEditionsDialog()}
|
||||||
<CollapsibleParagraph
|
<CollapsibleParagraph
|
||||||
title="Further Details"
|
title="Further Details"
|
||||||
show={this.state.piece.acl.acl_edit
|
show={this.state.piece.acl.acl_edit
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import PieceListToolbarFilterWidget from './piece_list_toolbar_filter_widget';
|
import PieceListToolbarFilterWidget from './piece_list_toolbar_filter_widget';
|
||||||
|
import PieceListToolbarOrderWidget from './piece_list_toolbar_order_widget';
|
||||||
|
|
||||||
import Input from 'react-bootstrap/lib/Input';
|
import Input from 'react-bootstrap/lib/Input';
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
|
@ -13,8 +14,12 @@ let PieceListToolbar = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
className: React.PropTypes.string,
|
className: React.PropTypes.string,
|
||||||
searchFor: React.PropTypes.func,
|
searchFor: React.PropTypes.func,
|
||||||
|
filterParams: React.PropTypes.array,
|
||||||
filterBy: React.PropTypes.object,
|
filterBy: React.PropTypes.object,
|
||||||
applyFilterBy: React.PropTypes.func,
|
applyFilterBy: React.PropTypes.func,
|
||||||
|
orderParams: React.PropTypes.array,
|
||||||
|
orderBy: React.PropTypes.string,
|
||||||
|
applyOrderBy: React.PropTypes.func,
|
||||||
children: React.PropTypes.oneOfType([
|
children: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
React.PropTypes.element
|
React.PropTypes.element
|
||||||
|
@ -26,6 +31,29 @@ let PieceListToolbar = React.createClass({
|
||||||
this.props.searchFor(searchTerm);
|
this.props.searchFor(searchTerm);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFilterWidget(){
|
||||||
|
if (this.props.filterParams){
|
||||||
|
return (
|
||||||
|
<PieceListToolbarFilterWidget
|
||||||
|
filterParams={this.props.filterParams}
|
||||||
|
filterBy={this.props.filterBy}
|
||||||
|
applyFilterBy={this.props.applyFilterBy} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
getOrderWidget(){
|
||||||
|
if (this.props.orderParams){
|
||||||
|
return (
|
||||||
|
<PieceListToolbarOrderWidget
|
||||||
|
orderParams={this.props.orderParams}
|
||||||
|
orderBy={this.props.orderBy}
|
||||||
|
applyOrderBy={this.props.applyOrderBy}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let searchIcon = <Glyphicon glyph='search' className="filter-glyph"/>;
|
let searchIcon = <Glyphicon glyph='search' className="filter-glyph"/>;
|
||||||
|
|
||||||
|
@ -37,7 +65,7 @@ let PieceListToolbar = React.createClass({
|
||||||
<span className="pull-left">
|
<span className="pull-left">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</span>
|
</span>
|
||||||
<span className="pull-right search-bar">
|
<span className="pull-right search-bar ascribe-input-glyph">
|
||||||
<Input
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
ref="search"
|
ref="search"
|
||||||
|
@ -46,13 +74,8 @@ let PieceListToolbar = React.createClass({
|
||||||
addonAfter={searchIcon} />
|
addonAfter={searchIcon} />
|
||||||
</span>
|
</span>
|
||||||
<span className="pull-right">
|
<span className="pull-right">
|
||||||
<PieceListToolbarFilterWidget
|
{this.getOrderWidget()}
|
||||||
filterParams={['acl_transfer', 'acl_consign', {
|
{this.getFilterWidget()}
|
||||||
key: 'acl_create_editions',
|
|
||||||
label: 'create editions'
|
|
||||||
}]}
|
|
||||||
filterBy={this.props.filterBy}
|
|
||||||
applyFilterBy={this.props.applyFilterBy}/>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||||
|
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
|
let PieceListToolbarOrderWidget = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
// An array of either strings (which represent acl enums) or objects of the form
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// key: <acl enum>,
|
||||||
|
// label: <a human readable string>
|
||||||
|
// }
|
||||||
|
orderParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired,
|
||||||
|
orderBy: React.PropTypes.string,
|
||||||
|
applyOrderBy: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
generateOrderByStatement(param) {
|
||||||
|
let orderBy = this.props.orderBy;
|
||||||
|
return orderBy;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need overloading here to find the correct parameter of the label
|
||||||
|
* the user is clicking on.
|
||||||
|
*/
|
||||||
|
orderBy(orderBy) {
|
||||||
|
return () => {
|
||||||
|
this.props.applyOrderBy(orderBy);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
isOrderActive() {
|
||||||
|
// We're hiding the star in that complicated matter so that,
|
||||||
|
// the surrounding button is not resized up on appearance
|
||||||
|
if(this.props.orderBy.length > 0) {
|
||||||
|
return { visibility: 'visible'};
|
||||||
|
} else {
|
||||||
|
return { visibility: 'hidden' };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let filterIcon = (
|
||||||
|
<span>
|
||||||
|
<span className="glyphicon glyphicon-sort-by-alphabet" aria-hidden="true"></span>
|
||||||
|
<span style={this.isOrderActive()}>*</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
|
||||||
|
<DropdownButton
|
||||||
|
title={filterIcon}
|
||||||
|
className="ascribe-piece-list-toolbar-filter-widget">
|
||||||
|
<li style={{'textAlign': 'center'}}>
|
||||||
|
<em>{getLangText('Sort by')}:</em>
|
||||||
|
</li>
|
||||||
|
{this.props.orderParams.map((param) => {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={param}
|
||||||
|
onClick={this.orderBy(param)}
|
||||||
|
className="filter-widget-item">
|
||||||
|
<div className="checkbox-line">
|
||||||
|
<span>
|
||||||
|
{getLangText(param.replace('_', ' '))}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
readOnly
|
||||||
|
type="checkbox"
|
||||||
|
checked={param.indexOf(this.props.orderBy) > -1} />
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PieceListToolbarOrderWidget;
|
|
@ -10,7 +10,7 @@ import EditionListStore from '../stores/edition_list_store';
|
||||||
import EditionListActions from '../actions/edition_list_actions';
|
import EditionListActions from '../actions/edition_list_actions';
|
||||||
|
|
||||||
import AccordionList from './ascribe_accordion_list/accordion_list';
|
import AccordionList from './ascribe_accordion_list/accordion_list';
|
||||||
import AccordionListItem from './ascribe_accordion_list/accordion_list_item';
|
import AccordionListItemWallet from './ascribe_accordion_list/accordion_list_item_wallet';
|
||||||
import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_list_item_table_editions';
|
import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_list_item_table_editions';
|
||||||
|
|
||||||
import Pagination from './ascribe_pagination/pagination';
|
import Pagination from './ascribe_pagination/pagination';
|
||||||
|
@ -24,12 +24,29 @@ import { mergeOptions } from '../utils/general_utils';
|
||||||
|
|
||||||
let PieceList = React.createClass({
|
let PieceList = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
accordionListItemType: React.PropTypes.func,
|
||||||
redirectTo: React.PropTypes.string,
|
redirectTo: React.PropTypes.string,
|
||||||
customSubmitButton: React.PropTypes.element
|
customSubmitButton: React.PropTypes.element,
|
||||||
|
filterParams: React.PropTypes.array,
|
||||||
|
orderParams: React.PropTypes.array
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation, Router.State],
|
mixins: [Router.Navigation, Router.State],
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
accordionListItemType: AccordionListItemWallet,
|
||||||
|
orderParams: ['artist_name', 'title'],
|
||||||
|
filterParams: [
|
||||||
|
'acl_transfer',
|
||||||
|
'acl_consign',
|
||||||
|
{
|
||||||
|
key: 'acl_create_editions',
|
||||||
|
label: 'create editions'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return mergeOptions(
|
return mergeOptions(
|
||||||
PieceListStore.getState(),
|
PieceListStore.getState(),
|
||||||
|
@ -97,7 +114,7 @@ let PieceList = React.createClass({
|
||||||
this.transitionTo(this.getPathname(), {page: 1});
|
this.transitionTo(this.getPathname(), {page: 1});
|
||||||
},
|
},
|
||||||
|
|
||||||
applyFilterBy(filterBy) {
|
applyFilterBy(filterBy){
|
||||||
// first we need to apply the filter on the piece list
|
// first we need to apply the filter on the piece list
|
||||||
PieceListActions.fetchPieceList(1, this.state.pageSize, this.state.search,
|
PieceListActions.fetchPieceList(1, this.state.pageSize, this.state.search,
|
||||||
this.state.orderBy, this.state.orderAsc, filterBy)
|
this.state.orderBy, this.state.orderAsc, filterBy)
|
||||||
|
@ -121,20 +138,25 @@ let PieceList = React.createClass({
|
||||||
this.transitionTo(this.getPathname(), {page: 1});
|
this.transitionTo(this.getPathname(), {page: 1});
|
||||||
},
|
},
|
||||||
|
|
||||||
accordionChangeOrder(orderBy, orderAsc) {
|
applyOrderBy(orderBy, orderAsc) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
orderBy, orderAsc, this.state.filterBy);
|
orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let loadingElement = (<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />);
|
let loadingElement = (<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />);
|
||||||
|
let AccordionListItemType = this.props.accordionListItemType;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PieceListToolbar
|
<PieceListToolbar
|
||||||
className="ascribe-piece-list-toolbar"
|
className="ascribe-piece-list-toolbar"
|
||||||
searchFor={this.searchFor}
|
searchFor={this.searchFor}
|
||||||
|
filterParams={this.props.filterParams}
|
||||||
|
orderParams={this.props.orderParams}
|
||||||
filterBy={this.state.filterBy}
|
filterBy={this.state.filterBy}
|
||||||
applyFilterBy={this.applyFilterBy}>
|
orderBy={this.state.orderBy}
|
||||||
|
applyFilterBy={this.applyFilterBy}
|
||||||
|
applyOrderBy={this.applyOrderBy}>
|
||||||
{this.props.customSubmitButton}
|
{this.props.customSubmitButton}
|
||||||
</PieceListToolbar>
|
</PieceListToolbar>
|
||||||
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
|
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
|
||||||
|
@ -151,14 +173,14 @@ let PieceList = React.createClass({
|
||||||
loadingElement={loadingElement}>
|
loadingElement={loadingElement}>
|
||||||
{this.state.pieceList.map((piece, i) => {
|
{this.state.pieceList.map((piece, i) => {
|
||||||
return (
|
return (
|
||||||
<AccordionListItem
|
<AccordionListItemType
|
||||||
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
|
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
|
||||||
content={piece}
|
content={piece}
|
||||||
key={i}>
|
key={i}>
|
||||||
<AccordionListItemTableEditions
|
<AccordionListItemTableEditions
|
||||||
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2"
|
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2"
|
||||||
parentId={piece.id} />
|
parentId={piece.id} />
|
||||||
</AccordionListItem>
|
</AccordionListItemType>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</AccordionList>
|
</AccordionList>
|
||||||
|
|
|
@ -37,7 +37,6 @@ class PrizeRatingActions {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.logGlobal(err);
|
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -52,7 +51,6 @@ class PrizeRatingActions {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.logGlobal(err);
|
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Router from 'react-router';
|
||||||
|
import StarRating from 'react-star-rating';
|
||||||
|
|
||||||
|
import AccordionListItemPiece from '../../../../ascribe_accordion_list/accordion_list_item_piece';
|
||||||
|
|
||||||
|
import PieceListActions from '../../../../../actions/piece_list_actions';
|
||||||
|
import PieceListStore from '../../../../../stores/piece_list_store';
|
||||||
|
|
||||||
|
import UserStore from '../../../../../stores/user_store';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import AclProxy from '../../../../acl_proxy';
|
||||||
|
import SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button';
|
||||||
|
|
||||||
|
|
||||||
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
|
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||||
|
|
||||||
|
let Link = Router.Link;
|
||||||
|
|
||||||
|
|
||||||
|
let AccordionListItemPrize = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
className: React.PropTypes.string,
|
||||||
|
content: React.PropTypes.object,
|
||||||
|
children: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return mergeOptions(
|
||||||
|
PieceListStore.getState(),
|
||||||
|
UserStore.getState()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PieceListStore.listen(this.onChange);
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
PieceListStore.unlisten(this.onChange);
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmitPrizeSuccess(response) {
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
getPrizeButtons() {
|
||||||
|
if (this.state.currentUser && this.state.currentUser.is_jury){
|
||||||
|
if (this.props.content.ratings && this.props.content.ratings.rating){
|
||||||
|
// jury and rating available
|
||||||
|
let rating = parseInt(this.props.content.ratings.rating, 10);
|
||||||
|
return (
|
||||||
|
<div id="list-rating" className="pull-right">
|
||||||
|
<Link to='piece' params={{pieceId: this.props.content.id}}>
|
||||||
|
<StarRating
|
||||||
|
ref='rating'
|
||||||
|
name="prize-rating"
|
||||||
|
caption="Your rating"
|
||||||
|
step={1}
|
||||||
|
size='sm'
|
||||||
|
rating={rating}
|
||||||
|
ratingAmount={5} />
|
||||||
|
</Link>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// jury and no rating yet
|
||||||
|
return (
|
||||||
|
<div className="react-rating-caption pull-right">
|
||||||
|
<Link to='piece' params={{pieceId: this.props.content.id}}>
|
||||||
|
Submit your rating
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// participant
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AclProxy
|
||||||
|
aclObject={this.props.content.acl}
|
||||||
|
aclName="acl_submit_to_prize">
|
||||||
|
<SubmitToPrizeButton
|
||||||
|
className="pull-right"
|
||||||
|
piece={this.props.content}
|
||||||
|
handleSuccess={this.handleSubmitPrizeSuccess}/>
|
||||||
|
</AclProxy>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let artistName = this.state.currentUser.is_jury ?
|
||||||
|
<span className="glyphicon glyphicon-eye-close" style={{fontSize: '0.75em'}} aria-hidden="true"/> :
|
||||||
|
this.props.content.artist_name;
|
||||||
|
return (
|
||||||
|
<AccordionListItemPiece
|
||||||
|
className={this.props.className}
|
||||||
|
piece={this.props.content}
|
||||||
|
artistName={artistName}
|
||||||
|
subsubheading={
|
||||||
|
<div className="pull-left">
|
||||||
|
<span>{this.props.content.date_created.split('-')[0]}</span>
|
||||||
|
</div>}
|
||||||
|
buttons={this.getPrizeButtons()}>
|
||||||
|
{this.props.children}
|
||||||
|
</AccordionListItemPiece>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AccordionListItemPrize;
|
|
@ -1,15 +1,21 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Router from 'react-router';
|
||||||
|
|
||||||
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 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 Piece from '../../../../../components/ascribe_detail/piece';
|
import Piece from '../../../../../components/ascribe_detail/piece';
|
||||||
|
|
||||||
import AppConstants from '../../../../../constants/application_constants';
|
import AppConstants from '../../../../../constants/application_constants';
|
||||||
|
@ -19,21 +25,32 @@ 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 GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import DetailProperty from '../../../../ascribe_detail/detail_property';
|
||||||
|
|
||||||
|
import ApiUrls from '../../../../../constants/api_urls';
|
||||||
|
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||||
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
|
|
||||||
|
let Link = Router.Link;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements resource/data specific functionality
|
* This is the component that implements resource/data specific functionality
|
||||||
*/
|
*/
|
||||||
let PieceContainer = React.createClass({
|
let PieceContainer = React.createClass({
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return PieceStore.getState();
|
return mergeOptions(
|
||||||
},
|
PieceStore.getState(),
|
||||||
|
UserStore.getState()
|
||||||
onChange(state) {
|
);
|
||||||
this.setState(state);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
PieceStore.listen(this.onChange);
|
PieceStore.listen(this.onChange);
|
||||||
PieceActions.fetchOne(this.props.params.pieceId);
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -42,10 +59,20 @@ let PieceContainer = React.createClass({
|
||||||
// as it will otherwise display wrong/old data once the user loads
|
// as it will otherwise display wrong/old data once the user loads
|
||||||
// the piece detail a second time
|
// the piece detail a second time
|
||||||
PieceActions.updatePiece({});
|
PieceActions.updatePiece({});
|
||||||
|
|
||||||
PieceStore.unlisten(this.onChange);
|
PieceStore.unlisten(this.onChange);
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if(this.props.params.pieceId !== nextProps.params.pieceId) {
|
||||||
|
PieceActions.updatePiece({});
|
||||||
|
PieceActions.fetchOne(nextProps.params.pieceId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
loadPiece() {
|
loadPiece() {
|
||||||
PieceActions.fetchOne(this.props.params.pieceId);
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
|
@ -53,10 +80,29 @@ let PieceContainer = React.createClass({
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if('title' in this.state.piece) {
|
if('title' in this.state.piece) {
|
||||||
|
let artistName = this.state.currentUser.is_jury ?
|
||||||
|
<span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : this.state.piece.artist_name;
|
||||||
return (
|
return (
|
||||||
<Piece
|
<Piece
|
||||||
piece={this.state.piece}
|
piece={this.state.piece}
|
||||||
loadPiece={this.loadPiece}>
|
loadPiece={this.loadPiece}
|
||||||
|
header={
|
||||||
|
<div className="ascribe-detail-header">
|
||||||
|
<NavigationHeader
|
||||||
|
piece={this.state.piece}
|
||||||
|
currentUser={this.state.currentUser}/>
|
||||||
|
<hr/>
|
||||||
|
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||||
|
<DetailProperty label="BY" value={artistName} />
|
||||||
|
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
subheader={
|
||||||
|
<PrizePieceRatings
|
||||||
|
piece={this.state.piece}
|
||||||
|
currentUser={this.state.currentUser}/>
|
||||||
|
}>
|
||||||
<PrizePieceDetails piece={this.state.piece}/>
|
<PrizePieceDetails piece={this.state.piece}/>
|
||||||
</Piece>
|
</Piece>
|
||||||
);
|
);
|
||||||
|
@ -70,23 +116,55 @@ let PieceContainer = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let NavigationHeader = React.createClass({
|
||||||
let PrizePieceDetails = React.createClass({
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
piece: React.PropTypes.object
|
piece: React.PropTypes.object,
|
||||||
|
currentUser: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.currentUser && this.props.piece.navigation) {
|
||||||
|
let nav = this.props.piece.navigation;
|
||||||
|
return (
|
||||||
|
<div style={{marginBottom: '1em'}}>
|
||||||
|
<div className="row no-margin">
|
||||||
|
<Link className="disable-select" to='piece' params={{pieceId: nav.prev_index ? nav.prev_index : this.props.piece.id}}>
|
||||||
|
<span className="glyphicon glyphicon-chevron-left pull-left link-ascribe" aria-hidden="true">
|
||||||
|
Previous
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
<Link className="disable-select" to='piece' params={{pieceId: nav.next_index ? nav.next_index : this.props.piece.id}}>
|
||||||
|
<span className="pull-right link-ascribe">
|
||||||
|
Next
|
||||||
|
<span className="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let PrizePieceRatings = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
piece: React.PropTypes.object,
|
||||||
|
currentUser: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return PrizeRatingStore.getState();
|
return mergeOptions(
|
||||||
},
|
PieceListStore.getState(),
|
||||||
|
PrizeRatingStore.getState()
|
||||||
onChange(state) {
|
);
|
||||||
this.setState(state);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
PrizeRatingStore.listen(this.onChange);
|
PrizeRatingStore.listen(this.onChange);
|
||||||
PrizeRatingActions.fetchOne(this.props.piece.id);
|
PrizeRatingActions.fetchOne(this.props.piece.id);
|
||||||
|
PieceListStore.listen(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -96,11 +174,96 @@ let PrizePieceDetails = React.createClass({
|
||||||
// the piece detail a second time
|
// the piece detail a second time
|
||||||
PrizeRatingActions.updateRating({});
|
PrizeRatingActions.updateRating({});
|
||||||
PrizeRatingStore.unlisten(this.onChange);
|
PrizeRatingStore.unlisten(this.onChange);
|
||||||
|
PieceListStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
if (this.refs.rating) {
|
||||||
|
this.refs.rating.state.ratingCache = {
|
||||||
|
pos: this.refs.rating.state.pos,
|
||||||
|
rating: this.state.currentRating,
|
||||||
|
caption: this.refs.rating.props.caption,
|
||||||
|
name: this.refs.rating.props.name
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRatingClick(event, args) {
|
onRatingClick(event, args) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
PrizeRatingActions.createRating(this.props.piece.id, args.rating);
|
PrizeRatingActions.createRating(this.props.piece.id, args.rating).then(
|
||||||
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render(){
|
||||||
|
if (this.props.currentUser && this.props.currentUser.is_jury) {
|
||||||
|
return (
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title="Rating"
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
<div style={{marginLeft: '1.5em', marginBottom: '1em'}}>
|
||||||
|
<StarRating
|
||||||
|
ref='rating'
|
||||||
|
name="prize-rating"
|
||||||
|
caption=""
|
||||||
|
step={1}
|
||||||
|
size='md'
|
||||||
|
rating={this.state.currentRating}
|
||||||
|
onRatingClick={this.onRatingClick}
|
||||||
|
ratingAmount={5} />
|
||||||
|
</div>
|
||||||
|
<PersonalNote
|
||||||
|
piece={this.props.piece}
|
||||||
|
currentUser={this.props.currentUser}/>
|
||||||
|
</CollapsibleParagraph>);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let PersonalNote = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
piece: React.PropTypes.object,
|
||||||
|
currentUser: React.PropTypes.object
|
||||||
|
},
|
||||||
|
showNotification(){
|
||||||
|
let notification = new GlobalNotificationModel(getLangText('Jury note saved'), 'success');
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.currentUser.username && true || false) {
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
url={ApiUrls.notes}
|
||||||
|
handleSuccess={this.showNotification}>
|
||||||
|
<Property
|
||||||
|
name='value'
|
||||||
|
label={getLangText('Jury note')}
|
||||||
|
editable={true}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={1}
|
||||||
|
editable={true}
|
||||||
|
defaultValue={this.props.piece.note_from_user ? this.props.piece.note_from_user.note : null}
|
||||||
|
placeholder={getLangText('Enter your comments...')}/>
|
||||||
|
</Property>
|
||||||
|
<Property hidden={true} name='piece_id'>
|
||||||
|
<input defaultValue={this.props.piece.id}/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return nul
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let PrizePieceDetails = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
piece: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -3,28 +3,39 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Router from 'react-router';
|
import Router from 'react-router';
|
||||||
|
|
||||||
|
import PrizeActions from '../actions/prize_actions';
|
||||||
|
import PrizeStore from '../stores/prize_store';
|
||||||
|
|
||||||
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
||||||
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
|
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
|
||||||
let Landing = React.createClass({
|
let Landing = React.createClass({
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return UserStore.getState();
|
return mergeOptions(
|
||||||
|
PrizeStore.getState(),
|
||||||
|
UserStore.getState()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
UserStore.listen(this.onChange);
|
UserStore.listen(this.onChange);
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser();
|
||||||
|
PrizeStore.listen(this.onChange);
|
||||||
|
PrizeActions.fetchPrize();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
UserStore.unlisten(this.onChange);
|
UserStore.unlisten(this.onChange);
|
||||||
|
PrizeStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
|
@ -37,27 +48,61 @@ let Landing = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getButtons() {
|
||||||
|
if (this.state.prize && this.state.prize.active){
|
||||||
|
return (
|
||||||
|
<ButtonGroup className="enter" bsSize="large" vertical>
|
||||||
|
<ButtonLink to="signup">
|
||||||
|
Sign up to submit
|
||||||
|
</ButtonLink>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
or, already an ascribe user?
|
||||||
|
</p>
|
||||||
|
<ButtonLink to="login">
|
||||||
|
Log in to submit
|
||||||
|
</ButtonLink>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ButtonGroup className="enter" bsSize="large" vertical>
|
||||||
|
<a className="btn btn-default" href="https://www.ascribe.io/app/signup">
|
||||||
|
Sign up to ascribe
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
or, already an ascribe user?
|
||||||
|
</p>
|
||||||
|
<ButtonLink to="login">
|
||||||
|
Log in
|
||||||
|
</ButtonLink>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
if (this.state.prize && this.state.prize.active){
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
This is the submission page for Sluice_screens ↄc Prize 2015.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
Submissions for Sluice_screens ↄc Prize 2015 are now closed.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-xs-12 wp-landing-wrapper">
|
<div className="col-xs-12 wp-landing-wrapper">
|
||||||
<h1>Sluice_screens ↄc Prize 2015</h1>
|
<h1>Sluice_screens ↄc Prize 2015</h1>
|
||||||
<p>
|
{this.getTitle()}
|
||||||
This is the submission page for Sluice_screens ↄc Prize 2015.
|
{this.getButtons()}
|
||||||
</p>
|
|
||||||
<ButtonGroup className="enter" bsSize="large" vertical>
|
|
||||||
<ButtonLink to="signup">
|
|
||||||
Sign up to submit
|
|
||||||
</ButtonLink>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
or, already an ascribe user?
|
|
||||||
</p>
|
|
||||||
<ButtonLink to="login">
|
|
||||||
Log in to submit
|
|
||||||
</ButtonLink>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,20 +3,60 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PieceList from '../../../piece_list';
|
import PieceList from '../../../piece_list';
|
||||||
|
|
||||||
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
import UserActions from '../../../../actions/user_actions';
|
||||||
|
import UserStore from '../../../../stores/user_store';
|
||||||
|
|
||||||
|
import PrizeActions from '../actions/prize_actions';
|
||||||
|
import PrizeStore from '../stores/prize_store';
|
||||||
|
|
||||||
|
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
|
||||||
|
import AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize';
|
||||||
|
|
||||||
|
import { mergeOptions } from '../../../../utils/general_utils';
|
||||||
|
|
||||||
let PrizePieceList = React.createClass({
|
let PrizePieceList = React.createClass({
|
||||||
|
getInitialState() {
|
||||||
|
return mergeOptions(
|
||||||
|
PrizeStore.getState(),
|
||||||
|
UserStore.getState()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
|
PrizeStore.listen(this.onChange);
|
||||||
|
PrizeActions.fetchPrize();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
PrizeStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
getButtonSubmit() {
|
||||||
|
if (this.state.prize && this.state.prize.active){
|
||||||
|
return (
|
||||||
|
<ButtonLink to="register_piece">
|
||||||
|
Submit to prize
|
||||||
|
</ButtonLink>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PieceList
|
<PieceList
|
||||||
redirectTo="register_piece"
|
redirectTo="register_piece"
|
||||||
customSubmitButton={
|
accordionListItemType={AccordionListItemPrize}
|
||||||
<ButtonLink to="register_piece">
|
orderParams={this.state.currentUser.is_jury ? ['rating', 'title'] : ['artist_name', 'title']}
|
||||||
Submit to prize
|
filterParams={null}
|
||||||
</ButtonLink>
|
customSubmitButton={this.getButtonSubmit()}/>
|
||||||
}/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,10 @@ function getPrizeApiUrls(subdomain) {
|
||||||
'jury_activate': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/activate/',
|
'jury_activate': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/activate/',
|
||||||
'jury_resend': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/resend/',
|
'jury_resend': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/resend/',
|
||||||
'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/',
|
'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/',
|
||||||
'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/'
|
'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/',
|
||||||
|
'notes': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/',
|
||||||
|
'note': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/${piece_id}/'
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ let PrizeRatingFetcher = {
|
||||||
},
|
},
|
||||||
|
|
||||||
rate(pieceId, rating) {
|
rate(pieceId, rating) {
|
||||||
return requests.post('ratings', {body: {'piece_id': pieceId, 'rating': rating}});
|
return requests.post('ratings', {body: {'piece_id': pieceId, 'value': rating}});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ascribe-piece-list-toolbar-filter-widget {
|
.ascribe-input-glyph > .form-group > .input-group {
|
||||||
margin-right: 1em;
|
margin-left: 6px;
|
||||||
|
input {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid #02b6a3;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
> .input-group-addon {
|
||||||
|
background-color: transparent;
|
||||||
|
> .filter-glyph {
|
||||||
|
color: #02b6a3;
|
||||||
|
}
|
||||||
|
border: 1px solid #02b6a3;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ascribe-piece-list-toolbar-filter-widget {
|
||||||
|
button {
|
||||||
|
background-color: rgba(0,0,0,0);
|
||||||
|
color: #02b6a3;
|
||||||
|
border: 1px solid rgba(0,0,0,0);
|
||||||
|
padding: 6px 4px 6px 8px;
|
||||||
|
&:hover, &:active {
|
||||||
|
background-color: #02b6a3 !important;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid #02b6a3 !important;
|
||||||
|
}
|
||||||
|
.caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.filter-widget-item {
|
.filter-widget-item {
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
|
|
|
@ -80,6 +80,25 @@ hr {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-default .navbar-nav > .active {
|
||||||
|
a {
|
||||||
|
background-color: transparent!important;
|
||||||
|
> span {color: #02b6a3;}
|
||||||
|
color: #02b6a3;
|
||||||
|
border-bottom: 1px solid #02b6a3;
|
||||||
|
|
||||||
|
&:hover, &:focus{
|
||||||
|
> span {color: #02b6a3;}
|
||||||
|
color: #02b6a3;
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom: 1px solid #02b6a3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navbar-default .navbar-nav > li > a {
|
||||||
|
border: 1px solid rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
.img-brand {
|
.img-brand {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
|
@ -386,5 +405,26 @@ hr {
|
||||||
|
|
||||||
.rating-container .rating-stars {
|
.rating-container .rating-stars {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
|
color: #02b6a3;
|
||||||
|
}
|
||||||
|
#list-rating > a > span > span > .rating-container .rating-stars{
|
||||||
color: #000;
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-rating-caption {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disable-select {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-ascribe {
|
||||||
|
color: #666;
|
||||||
|
&:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user