mirror of
https://github.com/ascribe/onion.git
synced 2024-11-14 09:05:08 +01:00
Merge branch 'master' into AD-613-cyland-white-label-page
Conflicts: .gitignore
This commit is contained in:
commit
95e0b256b6
7
.gitignore
vendored
7
.gitignore
vendored
@ -7,9 +7,12 @@ lib-cov
|
|||||||
*.pid
|
*.pid
|
||||||
*.gz
|
*.gz
|
||||||
*.sublime-project
|
*.sublime-project
|
||||||
|
|
||||||
spool-project.sublime-project
|
spool-project.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
*.sublime-workspace
|
||||||
|
webapp-dependencies.txt
|
||||||
|
|
||||||
pids
|
pids
|
||||||
logs
|
logs
|
||||||
results
|
results
|
||||||
@ -18,4 +21,4 @@ node_modules/*
|
|||||||
|
|
||||||
build
|
build
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -101,6 +101,9 @@ A: Easily by starting the your gulp process with the following command:
|
|||||||
ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve
|
ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Q: I want to know all dependencies that get bundled into the live build.
|
||||||
|
A: ```browserify -e js/app.js --list > webapp-dependencies.txt```
|
||||||
|
|
||||||
Reading list
|
Reading list
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -2,16 +2,18 @@
|
|||||||
|
|
||||||
*This should be a living document. So if you have any ideas for refactoring stuff, then feel free to add them to this document*
|
*This should be a living document. So if you have any ideas for refactoring stuff, then feel free to add them to this document*
|
||||||
|
|
||||||
- Get rid of all Mixins. (making good progress there :))
|
|
||||||
- Make all standalone components independent from things like global utilities (GeneralUtils is maybe used in table for example)
|
- Make all standalone components independent from things like global utilities (GeneralUtils is maybe used in table for example)
|
||||||
- Check if all polyfills are appropriately initialized and available: Compare to this
|
|
||||||
- Extract all standalone components to their own folder structure and write application independent tests (+ figure out how to do that in a productive way) (fetch lib especially)
|
- Extract all standalone components to their own folder structure and write application independent tests (+ figure out how to do that in a productive way) (fetch lib especially)
|
||||||
- Refactor forms to generic-declarative form component
|
|
||||||
- Check for mobile compatibility: Is site responsive anywhere?
|
- Check for mobile compatibility: Is site responsive anywhere?
|
||||||
queryParams of the piece_list_store should all be reflected in the url and not a single component each should manipulate the URL bar (refactor pagination, use actions and state)
|
queryParams of the piece_list_store should all be reflected in the url and not a single component each should manipulate the URL bar (refactor pagination, use actions and state)
|
||||||
- Refactor string-templating for api_urls
|
- Refactor string-templating for api_urls
|
||||||
- Use classNames plugin instead of if-conditional-classes
|
- Use classNames plugin instead of if-conditional-classes
|
||||||
|
|
||||||
|
# Refactor DONE
|
||||||
|
- Refactor forms to generic-declarative form component ✓
|
||||||
|
- Get rid of all Mixins (inject head is fine) ✓
|
||||||
|
- Check if all polyfills are appropriately initialized and available: Compare to this ✓
|
||||||
|
|
||||||
## React-S3-Fineuploader
|
## React-S3-Fineuploader
|
||||||
- implementation should enable to define all important methods outside
|
- implementation should enable to define all important methods outside
|
||||||
- and: maybe create a utility class for all methods to avoid code duplication
|
- and: maybe create a utility class for all methods to avoid code duplication
|
||||||
|
@ -17,21 +17,21 @@ class EditionListActions {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc) {
|
fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc, filterBy) {
|
||||||
if(!orderBy && typeof orderAsc === 'undefined') {
|
if((!orderBy && typeof orderAsc === 'undefined') || !orderAsc) {
|
||||||
orderBy = 'edition_number';
|
orderBy = 'edition_number';
|
||||||
orderAsc = true;
|
orderAsc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from: http://stackoverflow.com/a/519157/1263876
|
// Taken from: http://stackoverflow.com/a/519157/1263876
|
||||||
if(typeof page === 'undefined' && typeof pageSize === 'undefined') {
|
if((typeof page === 'undefined' || !page) && (typeof pageSize === 'undefined' || !pageSize)) {
|
||||||
page = 1;
|
page = 1;
|
||||||
pageSize = 10;
|
pageSize = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Q.Promise((resolve, reject) => {
|
return Q.Promise((resolve, reject) => {
|
||||||
EditionListFetcher
|
EditionListFetcher
|
||||||
.fetch(pieceId, page, pageSize, orderBy, orderAsc)
|
.fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.actions.updateEditionList({
|
this.actions.updateEditionList({
|
||||||
pieceId,
|
pieceId,
|
||||||
@ -39,6 +39,7 @@ class EditionListActions {
|
|||||||
pageSize,
|
pageSize,
|
||||||
orderBy,
|
orderBy,
|
||||||
orderAsc,
|
orderAsc,
|
||||||
|
filterBy,
|
||||||
'editionListOfPiece': res.editions,
|
'editionListOfPiece': res.editions,
|
||||||
'count': res.count
|
'count': res.count
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,8 @@ import OwnershipFetcher from '../fetchers/ownership_fetcher';
|
|||||||
class LoanContractActions {
|
class LoanContractActions {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.generateActions(
|
this.generateActions(
|
||||||
'updateLoanContract'
|
'updateLoanContract',
|
||||||
|
'flushLoanContract'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class PieceListActions {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPieceList(page, pageSize, search, orderBy, orderAsc) {
|
fetchPieceList(page, pageSize, search, orderBy, orderAsc, filterBy) {
|
||||||
// To prevent flickering on a pagination request,
|
// To prevent flickering on a pagination request,
|
||||||
// we overwrite the piecelist with an empty list before
|
// we overwrite the piecelist with an empty list before
|
||||||
// pieceListCount === -1 defines the loading state
|
// pieceListCount === -1 defines the loading state
|
||||||
@ -24,6 +24,7 @@ class PieceListActions {
|
|||||||
search,
|
search,
|
||||||
orderBy,
|
orderBy,
|
||||||
orderAsc,
|
orderAsc,
|
||||||
|
filterBy,
|
||||||
'pieceList': [],
|
'pieceList': [],
|
||||||
'pieceListCount': -1
|
'pieceListCount': -1
|
||||||
});
|
});
|
||||||
@ -32,7 +33,7 @@ class PieceListActions {
|
|||||||
|
|
||||||
return Q.Promise((resolve, reject) => {
|
return Q.Promise((resolve, reject) => {
|
||||||
PieceListFetcher
|
PieceListFetcher
|
||||||
.fetch(page, pageSize, search, orderBy, orderAsc)
|
.fetch(page, pageSize, search, orderBy, orderAsc, filterBy)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.actions.updatePieceList({
|
this.actions.updatePieceList({
|
||||||
page,
|
page,
|
||||||
@ -40,6 +41,7 @@ class PieceListActions {
|
|||||||
search,
|
search,
|
||||||
orderBy,
|
orderBy,
|
||||||
orderAsc,
|
orderAsc,
|
||||||
|
filterBy,
|
||||||
'pieceList': res.pieces,
|
'pieceList': res.pieces,
|
||||||
'pieceListCount': res.count
|
'pieceListCount': res.count
|
||||||
});
|
});
|
||||||
|
34
js/actions/prize_list_actions.js
Normal file
34
js/actions/prize_list_actions.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../alt';
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
|
import PrizeListFetcher from '../fetchers/prize_list_fetcher';
|
||||||
|
|
||||||
|
class PrizeListActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'updatePrizeList'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchPrizeList() {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeListFetcher
|
||||||
|
.fetch()
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrizeList({
|
||||||
|
prizeList: res.prizes,
|
||||||
|
prizeListCount: res.count
|
||||||
|
});
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(PrizeListActions);
|
@ -22,6 +22,7 @@ class UserActions {
|
|||||||
this.actions.updateCurrentUser({});
|
this.actions.updateCurrentUser({});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logoutCurrentUser() {
|
logoutCurrentUser() {
|
||||||
return UserFetcher.logout()
|
return UserFetcher.logout()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -36,9 +36,9 @@ let AclProxy = React.createClass({
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if(typeof this.props.aclObject[this.props.aclName] === 'undefined') {
|
/* if(typeof this.props.aclObject[this.props.aclName] === 'undefined') {
|
||||||
console.warn('The aclName you\'re filtering for was not present (or undefined) in the aclObject.');
|
console.warn('The aclName you\'re filtering for was not present (or undefined) in the aclObject.');
|
||||||
}
|
} */
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import GlobalNotificationModel from '../../models/global_notification_model';
|
|||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import AclProxy from '../acl_proxy';
|
import AclProxy from '../acl_proxy';
|
||||||
import SubmitToPrizeButton from '../ascribe_buttons/submit_to_prize_button';
|
import SubmitToPrizeButton from '../whitelabel/prize/components/ascribe_buttons/submit_to_prize_button';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
import { mergeOptions } from '../../utils/general_utils';
|
import { mergeOptions } from '../../utils/general_utils';
|
||||||
@ -87,14 +87,16 @@ let AccordionListItem = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSubmitPrizeSuccess(response) {
|
handleSubmitPrizeSuccess(response) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
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);
|
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
onPollingSuccess(pieceId) {
|
onPollingSuccess(pieceId) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
EditionListActions.toggleEditionList(pieceId);
|
EditionListActions.toggleEditionList(pieceId);
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
||||||
@ -178,21 +180,13 @@ let AccordionListItem = React.createClass({
|
|||||||
onPollingSuccess={this.onPollingSuccess}/>
|
onPollingSuccess={this.onPollingSuccess}/>
|
||||||
</AclProxy>
|
</AclProxy>
|
||||||
<AclProxy
|
<AclProxy
|
||||||
show={this.props.content.prize === null}>
|
aclObject={this.props.content.acl}
|
||||||
|
aclName="acl_submit_to_prize">
|
||||||
<SubmitToPrizeButton
|
<SubmitToPrizeButton
|
||||||
className="pull-right"
|
className="pull-right"
|
||||||
piece={this.props.content}
|
piece={this.props.content}
|
||||||
handleSuccess={this.handleSubmitPrizeSuccess}/>
|
handleSuccess={this.handleSubmitPrizeSuccess}/>
|
||||||
</AclProxy>
|
</AclProxy>
|
||||||
<AclProxy
|
|
||||||
show={this.props.content.prize}>
|
|
||||||
<button
|
|
||||||
disabled
|
|
||||||
className="btn btn-default btn-xs pull-right">
|
|
||||||
{getLangText('Submitted to prize')} <span className="glyphicon glyphicon-ok"
|
|
||||||
aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
</AclProxy>
|
|
||||||
{this.getLicences()}
|
{this.getLicences()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,12 +6,14 @@ import classNames from 'classnames';
|
|||||||
import EditionListActions from '../../actions/edition_list_actions';
|
import EditionListActions from '../../actions/edition_list_actions';
|
||||||
import EditionListStore from '../../stores/edition_list_store';
|
import EditionListStore from '../../stores/edition_list_store';
|
||||||
|
|
||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
import PieceListActions from '../../actions/piece_list_actions';
|
||||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
import PieceListStore from '../../stores/piece_list_store';
|
||||||
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
|
|
||||||
import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
|
import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
|
||||||
|
|
||||||
|
import { mergeOptions } from '../../utils/general_utils';
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
let AccordionListItemEditionWidget = React.createClass({
|
let AccordionListItemEditionWidget = React.createClass({
|
||||||
@ -23,15 +25,20 @@ let AccordionListItemEditionWidget = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return EditionListStore.getState();
|
return mergeOptions(
|
||||||
|
EditionListStore.getState(),
|
||||||
|
PieceListStore.getState()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
EditionListStore.listen(this.onChange);
|
EditionListStore.listen(this.onChange);
|
||||||
|
PieceListStore.listen(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
EditionListStore.unlisten(this.onChange);
|
EditionListStore.unlisten(this.onChange);
|
||||||
|
PieceListStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
@ -49,7 +56,7 @@ let AccordionListItemEditionWidget = React.createClass({
|
|||||||
EditionListActions.toggleEditionList(pieceId);
|
EditionListActions.toggleEditionList(pieceId);
|
||||||
} else {
|
} else {
|
||||||
EditionListActions.toggleEditionList(pieceId);
|
EditionListActions.toggleEditionList(pieceId);
|
||||||
EditionListActions.fetchEditionList(pieceId);
|
EditionListActions.fetchEditionList(pieceId, null, null, null, null, this.state.filterBy);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -87,7 +94,7 @@ let AccordionListItemEditionWidget = React.createClass({
|
|||||||
let numEditions = piece.num_editions;
|
let numEditions = piece.num_editions;
|
||||||
|
|
||||||
if(numEditions <= 0) {
|
if(numEditions <= 0) {
|
||||||
if (piece.acl.acl_editions){
|
if (piece.acl.acl_create_editions){
|
||||||
return (
|
return (
|
||||||
<CreateEditionsButton
|
<CreateEditionsButton
|
||||||
label={getLangText('Create editions')}
|
label={getLangText('Create editions')}
|
||||||
|
@ -76,13 +76,9 @@ let AccordionListItemTableEditions = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let editionList = this.state.editionList[this.props.parentId];
|
let editionList = this.state.editionList[this.props.parentId];
|
||||||
EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize);
|
EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize,
|
||||||
|
editionList.orderBy, editionList.orderAsc, editionList.filterBy);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeEditionListOrder(orderBy, orderAsc) {
|
|
||||||
EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc);
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let selectedEditionsCount = 0;
|
let selectedEditionsCount = 0;
|
||||||
let allEditionsCount = 0;
|
let allEditionsCount = 0;
|
||||||
|
@ -13,8 +13,10 @@ import AppConstants from '../../constants/application_constants';
|
|||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import apiUrls from '../../constants/api_urls';
|
|
||||||
|
import { getAclFormMessage } from '../../utils/form_utils';
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
let AclButton = React.createClass({
|
let AclButton = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -34,15 +36,18 @@ let AclButton = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
actionProperties(){
|
actionProperties(){
|
||||||
|
|
||||||
|
let message = getAclFormMessage(this.props.action, this.getTitlesString(), this.props.currentUser.username);
|
||||||
|
|
||||||
if (this.props.action === 'acl_consign'){
|
if (this.props.action === 'acl_consign'){
|
||||||
return {
|
return {
|
||||||
title: getLangText('Consign artwork'),
|
title: getLangText('Consign artwork'),
|
||||||
tooltip: getLangText('Have someone else sell the artwork'),
|
tooltip: getLangText('Have someone else sell the artwork'),
|
||||||
form: (
|
form: (
|
||||||
<ConsignForm
|
<ConsignForm
|
||||||
message={this.getConsignMessage()}
|
message={message}
|
||||||
id={this.getFormDataId()}
|
id={this.getFormDataId()}
|
||||||
url={apiUrls.ownership_consigns}/>
|
url={ApiUrls.ownership_consigns}/>
|
||||||
),
|
),
|
||||||
handleSuccess: this.showNotification
|
handleSuccess: this.showNotification
|
||||||
};
|
};
|
||||||
@ -53,9 +58,9 @@ let AclButton = React.createClass({
|
|||||||
tooltip: getLangText('Have the owner manage his sales again'),
|
tooltip: getLangText('Have the owner manage his sales again'),
|
||||||
form: (
|
form: (
|
||||||
<UnConsignForm
|
<UnConsignForm
|
||||||
message={this.getUnConsignMessage()}
|
message={message}
|
||||||
id={this.getFormDataId()}
|
id={this.getFormDataId()}
|
||||||
url={apiUrls.ownership_unconsigns}/>
|
url={ApiUrls.ownership_unconsigns}/>
|
||||||
),
|
),
|
||||||
handleSuccess: this.showNotification
|
handleSuccess: this.showNotification
|
||||||
};
|
};
|
||||||
@ -65,9 +70,9 @@ let AclButton = React.createClass({
|
|||||||
tooltip: getLangText('Transfer the ownership of the artwork'),
|
tooltip: getLangText('Transfer the ownership of the artwork'),
|
||||||
form: (
|
form: (
|
||||||
<TransferForm
|
<TransferForm
|
||||||
message={this.getTransferMessage()}
|
message={message}
|
||||||
id={this.getFormDataId()}
|
id={this.getFormDataId()}
|
||||||
url={apiUrls.ownership_transfers}/>
|
url={ApiUrls.ownership_transfers}/>
|
||||||
),
|
),
|
||||||
handleSuccess: this.showNotification
|
handleSuccess: this.showNotification
|
||||||
};
|
};
|
||||||
@ -77,9 +82,9 @@ let AclButton = React.createClass({
|
|||||||
title: getLangText('Loan artwork'),
|
title: getLangText('Loan artwork'),
|
||||||
tooltip: getLangText('Loan your artwork for a limited period of time'),
|
tooltip: getLangText('Loan your artwork for a limited period of time'),
|
||||||
form: (<LoanForm
|
form: (<LoanForm
|
||||||
message={this.getLoanMessage()}
|
message={message}
|
||||||
id={this.getFormDataId()}
|
id={this.getFormDataId()}
|
||||||
url={this.isPiece() ? apiUrls.ownership_loans_pieces : apiUrls.ownership_loans_editions}/>
|
url={this.isPiece() ? ApiUrls.ownership_loans_pieces : ApiUrls.ownership_loans_editions}/>
|
||||||
),
|
),
|
||||||
handleSuccess: this.showNotification
|
handleSuccess: this.showNotification
|
||||||
};
|
};
|
||||||
@ -90,9 +95,9 @@ let AclButton = React.createClass({
|
|||||||
tooltip: getLangText('Share the artwork'),
|
tooltip: getLangText('Share the artwork'),
|
||||||
form: (
|
form: (
|
||||||
<ShareForm
|
<ShareForm
|
||||||
message={this.getShareMessage()}
|
message={message}
|
||||||
id={this.getFormDataId()}
|
id={this.getFormDataId()}
|
||||||
url={this.isPiece() ? apiUrls.ownership_shares_pieces : apiUrls.ownership_shares_editions }/>
|
url={this.isPiece() ? ApiUrls.ownership_shares_pieces : ApiUrls.ownership_shares_editions }/>
|
||||||
),
|
),
|
||||||
handleSuccess: this.showNotification
|
handleSuccess: this.showNotification
|
||||||
};
|
};
|
||||||
@ -133,76 +138,6 @@ let AclButton = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// plz move to transfer form
|
|
||||||
getTransferMessage(){
|
|
||||||
return (
|
|
||||||
`${getLangText('Hi')},
|
|
||||||
|
|
||||||
${getLangText('I transfer ownership of')}:
|
|
||||||
${this.getTitlesString()} ${getLangText('to you')}.
|
|
||||||
|
|
||||||
${getLangText('Truly yours')},
|
|
||||||
${this.props.currentUser.username}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// plz move to transfer form
|
|
||||||
getLoanMessage(){
|
|
||||||
return (
|
|
||||||
`${getLangText('Hi')},
|
|
||||||
|
|
||||||
${getLangText('I loan')}:
|
|
||||||
${this.getTitlesString()} ${getLangText('to you')}.
|
|
||||||
|
|
||||||
${getLangText('Truly yours')},
|
|
||||||
${this.props.currentUser.username}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// plz move to consign form
|
|
||||||
getConsignMessage(){
|
|
||||||
return (
|
|
||||||
`${getLangText('Hi')},
|
|
||||||
|
|
||||||
${getLangText('I consign')}:
|
|
||||||
${this.getTitlesString()} ${getLangText('to you')}.
|
|
||||||
|
|
||||||
${getLangText('Truly yours')},
|
|
||||||
${this.props.currentUser.username}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// plz move to consign form
|
|
||||||
getUnConsignMessage(){
|
|
||||||
return (
|
|
||||||
`${getLangText('Hi')},
|
|
||||||
|
|
||||||
${getLangText('I un-consign')}:
|
|
||||||
${this.getTitlesString()} ${getLangText('from you')}.
|
|
||||||
|
|
||||||
${getLangText('Truly yours')},
|
|
||||||
${this.props.currentUser.username}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// plz move to share form
|
|
||||||
getShareMessage(){
|
|
||||||
return (
|
|
||||||
`${getLangText('Hi')},
|
|
||||||
|
|
||||||
${getLangText('I am sharing')}:
|
|
||||||
${this.getTitlesString()} ${getLangText('with you')}.
|
|
||||||
|
|
||||||
${getLangText('Truly yours')},
|
|
||||||
${this.props.currentUser.username}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Removes the acl_ prefix and converts to upper case
|
// Removes the acl_ prefix and converts to upper case
|
||||||
sanitizeAction() {
|
sanitizeAction() {
|
||||||
return this.props.action.split('acl_')[1].toUpperCase();
|
return this.props.action.split('acl_')[1].toUpperCase();
|
||||||
@ -214,14 +149,13 @@ ${this.props.currentUser.username}
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
button={
|
trigger={
|
||||||
<button className={shouldDisplay ? 'btn btn-default btn-sm ' : 'hidden'}>
|
<button className={shouldDisplay ? 'btn btn-default btn-sm ' : 'hidden'}>
|
||||||
{this.sanitizeAction()}
|
{this.sanitizeAction()}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
handleSuccess={aclProps.handleSuccess}
|
handleSuccess={aclProps.handleSuccess}
|
||||||
title={aclProps.title}
|
title={aclProps.title}>
|
||||||
tooltip={aclProps.tooltip}>
|
|
||||||
{aclProps.form}
|
{aclProps.form}
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
);
|
);
|
||||||
|
@ -44,14 +44,17 @@ let CreateEditionsButton = React.createClass({
|
|||||||
startPolling() {
|
startPolling() {
|
||||||
// start polling until editions are defined
|
// start polling until editions are defined
|
||||||
let pollingIntervalIndex = setInterval(() => {
|
let pollingIntervalIndex = setInterval(() => {
|
||||||
EditionListActions.fetchEditionList(this.props.piece.id)
|
|
||||||
|
// requests, will try to merge the filterBy parameter with other parameters (mergeOptions).
|
||||||
|
// Therefore it can't but null but instead has to be an empty object
|
||||||
|
EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, {})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
||||||
clearInterval(this.state.pollingIntervalIndex);
|
clearInterval(this.state.pollingIntervalIndex);
|
||||||
this.props.onPollingSuccess(this.props.piece.id, res.editions[0].num_editions);
|
this.props.onPollingSuccess(this.props.piece.id, res.editions[0].num_editions);
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
/* Ignore and keep going */
|
/* Ignore and keep going */
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
@ -64,7 +67,7 @@ let CreateEditionsButton = React.createClass({
|
|||||||
render: function () {
|
render: function () {
|
||||||
let piece = this.props.piece;
|
let piece = this.props.piece;
|
||||||
|
|
||||||
if (!piece.acl.acl_editions || piece.num_editions > 0){
|
if (!piece.acl.acl_create_editions || piece.num_editions > 0){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ let DeleteButton = React.createClass({
|
|||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
render: function () {
|
render() {
|
||||||
let availableAcls;
|
let availableAcls;
|
||||||
let btnDelete;
|
let btnDelete;
|
||||||
let content;
|
let content;
|
||||||
@ -61,13 +61,14 @@ let DeleteButton = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
btnDelete = <Button bsStyle="danger" className="btn-delete" bsSize="small">{getLangText('REMOVE FROM COLLECTION')}</Button>;
|
btnDelete = <Button bsStyle="danger" className="btn-delete" bsSize="small">{getLangText('REMOVE FROM COLLECTION')}</Button>;
|
||||||
}
|
|
||||||
else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
button={btnDelete}
|
trigger={btnDelete}
|
||||||
handleSuccess={this.props.handleSuccess}
|
handleSuccess={this.props.handleSuccess}
|
||||||
title={title}>
|
title={title}>
|
||||||
{content}
|
{content}
|
||||||
@ -77,4 +78,3 @@ let DeleteButton = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default DeleteButton;
|
export default DeleteButton;
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import ModalWrapper from '../ascribe_modal/modal_wrapper';
|
|
||||||
import PieceSubmitToPrizeForm from '../ascribe_forms/form_submit_to_prize';
|
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
|
||||||
|
|
||||||
let SubmitToPrizeButton = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
className: React.PropTypes.string,
|
|
||||||
handleSuccess: React.PropTypes.func,
|
|
||||||
piece: React.PropTypes.object.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
getSubmitButton() {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
|
|
||||||
{getLangText('Submit to prize')}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ModalWrapper
|
|
||||||
button={this.getSubmitButton()}
|
|
||||||
handleSuccess={this.props.handleSuccess}
|
|
||||||
title={getLangText('Submit to prize')}>
|
|
||||||
<PieceSubmitToPrizeForm
|
|
||||||
piece={this.props.piece}
|
|
||||||
handleSuccess={this.props.handleSuccess}/>
|
|
||||||
</ModalWrapper>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default SubmitToPrizeButton;
|
|
@ -8,7 +8,7 @@ import ModalWrapper from '../ascribe_modal/modal_wrapper';
|
|||||||
import UnConsignRequestForm from './../ascribe_forms/form_unconsign_request';
|
import UnConsignRequestForm from './../ascribe_forms/form_unconsign_request';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
|
|
||||||
let UnConsignRequestButton = React.createClass({
|
let UnConsignRequestButton = React.createClass({
|
||||||
@ -21,16 +21,15 @@ let UnConsignRequestButton = React.createClass({
|
|||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
button={
|
trigger={
|
||||||
<Button bsStyle="danger" className="btn-delete pull-center" bsSize="small" type="submit">
|
<Button bsStyle="danger" className="btn-delete pull-center" bsSize="small" type="submit">
|
||||||
REQUEST UNCONSIGN
|
REQUEST UNCONSIGN
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
handleSuccess={this.props.handleSuccess}
|
handleSuccess={this.props.handleSuccess}
|
||||||
title='Request to Un-Consign'
|
title='Request to Un-Consign'>
|
||||||
tooltip='Ask the consignee to return the ownership of the work back to you'>
|
|
||||||
<UnConsignRequestForm
|
<UnConsignRequestForm
|
||||||
url={apiUrls.ownership_unconsigns_request}
|
url={ApiUrls.ownership_unconsigns_request}
|
||||||
id={{'bitcoin_id': this.props.edition.bitcoin_id}}
|
id={{'bitcoin_id': this.props.edition.bitcoin_id}}
|
||||||
message={`${getLangText('Hi')},
|
message={`${getLangText('Hi')},
|
||||||
|
|
||||||
|
@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
|
import Panel from 'react-bootstrap/lib/Panel';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
|
|
||||||
const CollapsibleParagraph = React.createClass({
|
const CollapsibleParagraph = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
children: React.PropTypes.oneOfType([
|
children: React.PropTypes.oneOfType([
|
||||||
@ -20,18 +17,14 @@ const CollapsibleParagraph = React.createClass({
|
|||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
show: true
|
show: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CollapsibleMixin],
|
getInitialState() {
|
||||||
|
return {
|
||||||
getCollapsibleDOMNode(){
|
expanded: false
|
||||||
return React.findDOMNode(this.refs.panel);
|
};
|
||||||
},
|
|
||||||
|
|
||||||
getCollapsibleDimensionValue(){
|
|
||||||
return React.findDOMNode(this.refs.panel).scrollHeight;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleToggle(e){
|
handleToggle(e){
|
||||||
@ -40,8 +33,7 @@ const CollapsibleParagraph = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let styles = this.getCollapsibleClassSet();
|
let text = this.state.expanded ? '-' : '+';
|
||||||
let text = this.isExpanded() ? '-' : '+';
|
|
||||||
|
|
||||||
if(this.props.show) {
|
if(this.props.show) {
|
||||||
return (
|
return (
|
||||||
@ -50,9 +42,12 @@ const CollapsibleParagraph = React.createClass({
|
|||||||
<div onClick={this.handleToggle}>
|
<div onClick={this.handleToggle}>
|
||||||
<span>{text} {this.props.title}</span>
|
<span>{text} {this.props.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div ref='panel' className={classNames(styles) + ' ascribe-edition-collapible-content'}>
|
<Panel
|
||||||
|
collapsible
|
||||||
|
expanded={this.state.expanded}
|
||||||
|
className="ascribe-edition-collapsible-content">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@ let DetailProperty = React.createClass({
|
|||||||
separator: React.PropTypes.string,
|
separator: React.PropTypes.string,
|
||||||
labelClassName: React.PropTypes.string,
|
labelClassName: React.PropTypes.string,
|
||||||
valueClassName: React.PropTypes.string,
|
valueClassName: React.PropTypes.string,
|
||||||
breakWord: React.PropTypes.bool
|
ellipsis: React.PropTypes.bool
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
@ -25,6 +25,16 @@ let DetailProperty = React.createClass({
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let value = this.props.value;
|
let value = this.props.value;
|
||||||
|
let styles = {};
|
||||||
|
|
||||||
|
if(this.props.ellipsis) {
|
||||||
|
styles = {
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.props.children){
|
if (this.props.children){
|
||||||
value = (
|
value = (
|
||||||
@ -32,7 +42,9 @@ let DetailProperty = React.createClass({
|
|||||||
<div className="col-xs-6 col-xs-height col-bottom no-padding">
|
<div className="col-xs-6 col-xs-height col-bottom no-padding">
|
||||||
{ this.props.value }
|
{ this.props.value }
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xs-6 col-xs-height">
|
<div
|
||||||
|
className="col-xs-6 col-xs-height"
|
||||||
|
style={styles}>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
@ -43,7 +55,9 @@ let DetailProperty = React.createClass({
|
|||||||
<div className={this.props.labelClassName + ' col-xs-height col-bottom ascribe-detail-property-label'}>
|
<div className={this.props.labelClassName + ' col-xs-height col-bottom ascribe-detail-property-label'}>
|
||||||
{ this.props.label + this.props.separator}
|
{ this.props.label + this.props.separator}
|
||||||
</div>
|
</div>
|
||||||
<div className={this.props.valueClassName + ' col-xs-height col-bottom ascribe-detail-property-value'}>
|
<div
|
||||||
|
className={this.props.valueClassName + ' col-xs-height col-bottom ascribe-detail-property-value'}
|
||||||
|
style={styles}>
|
||||||
{value}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,7 +36,7 @@ import DeleteButton from '../ascribe_buttons/delete_button';
|
|||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
@ -86,9 +86,10 @@ let Edition = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleDeleteSuccess(response) {
|
handleDeleteSuccess(response) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
|
||||||
EditionListActions.refreshEditionList(this.props.edition.parent);
|
EditionListActions.refreshEditionList({pieceId: this.props.edition.parent});
|
||||||
EditionListActions.closeAllEditionLists();
|
EditionListActions.closeAllEditionLists();
|
||||||
EditionListActions.clearAllEditionSelections();
|
EditionListActions.clearAllEditionSelections();
|
||||||
|
|
||||||
@ -232,10 +233,11 @@ let EditionSummary = React.createClass({
|
|||||||
if (this.props.edition.status.length > 0 && this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer) {
|
if (this.props.edition.status.length > 0 && this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer) {
|
||||||
withdrawButton = (
|
withdrawButton = (
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.ownership_transfers_withdraw}
|
url={ApiUrls.ownership_transfers_withdraw}
|
||||||
getFormData={this.getTransferWithdrawData}
|
getFormData={this.getTransferWithdrawData}
|
||||||
handleSuccess={this.showNotification}
|
handleSuccess={this.showNotification}
|
||||||
className='inline'>
|
className='inline'
|
||||||
|
isInline={true}>
|
||||||
<Button bsStyle="danger" className="btn-delete pull-center" bsSize="small" type="submit">
|
<Button bsStyle="danger" className="btn-delete pull-center" bsSize="small" type="submit">
|
||||||
WITHDRAW TRANSFER
|
WITHDRAW TRANSFER
|
||||||
</Button>
|
</Button>
|
||||||
@ -273,10 +275,16 @@ let EditionSummary = React.createClass({
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="ascribe-detail-header">
|
<div className="ascribe-detail-header">
|
||||||
<EditionDetailProperty label={getLangText('EDITION')}
|
<EditionDetailProperty
|
||||||
|
label={getLangText('EDITION')}
|
||||||
value={this.props.edition.edition_number + ' ' + getLangText('of') + ' ' + this.props.edition.num_editions} />
|
value={this.props.edition.edition_number + ' ' + getLangText('of') + ' ' + this.props.edition.num_editions} />
|
||||||
<EditionDetailProperty label={getLangText('ID')} value={ this.props.edition.bitcoin_id } />
|
<EditionDetailProperty
|
||||||
<EditionDetailProperty label={getLangText('OWNER')} value={ this.props.edition.owner } />
|
label={getLangText('ID')}
|
||||||
|
value={ this.props.edition.bitcoin_id }
|
||||||
|
ellipsis={true} />
|
||||||
|
<EditionDetailProperty
|
||||||
|
label={getLangText('OWNER')}
|
||||||
|
value={ this.props.edition.owner } />
|
||||||
{this.getStatus()}
|
{this.getStatus()}
|
||||||
{this.getActions()}
|
{this.getActions()}
|
||||||
<hr/>
|
<hr/>
|
||||||
@ -328,7 +336,7 @@ let EditionPersonalNote = React.createClass({
|
|||||||
if (this.props.currentUser.username && true || false) {
|
if (this.props.currentUser.username && true || false) {
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.note_notes}
|
url={ApiUrls.note_notes}
|
||||||
handleSuccess={this.showNotification}>
|
handleSuccess={this.showNotification}>
|
||||||
<Property
|
<Property
|
||||||
name='note'
|
name='note'
|
||||||
@ -366,7 +374,7 @@ let EditionPublicEditionNote = React.createClass({
|
|||||||
if (isEditable || this.props.edition.public_note){
|
if (isEditable || this.props.edition.public_note){
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.note_edition}
|
url={ApiUrls.note_edition}
|
||||||
handleSuccess={this.showNotification}>
|
handleSuccess={this.showNotification}>
|
||||||
<Property
|
<Property
|
||||||
name='note'
|
name='note'
|
||||||
|
@ -16,7 +16,7 @@ import ReactS3FineUploader from './../ascribe_uploader/react_s3_fine_uploader';
|
|||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getCookie } from '../../utils/fetch_api_utils';
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
@ -133,7 +133,7 @@ let FileUploader = React.createClass({
|
|||||||
pieceId: this.props.pieceId
|
pieceId: this.props.pieceId
|
||||||
}}
|
}}
|
||||||
createBlobRoutine={{
|
createBlobRoutine={{
|
||||||
url: apiUrls.blob_otherdatas,
|
url: ApiUrls.blob_otherdatas,
|
||||||
pieceId: this.props.pieceId
|
pieceId: this.props.pieceId
|
||||||
}}
|
}}
|
||||||
validation={{
|
validation={{
|
||||||
|
@ -46,7 +46,7 @@ let MediaContainer = React.createClass({
|
|||||||
}
|
}
|
||||||
panel={
|
panel={
|
||||||
<pre className="">
|
<pre className="">
|
||||||
{'<iframe width="560" height="' + height + '" src="http://embed.ascribe.io/content/'
|
{'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/'
|
||||||
+ this.props.content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
+ this.props.content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
||||||
</pre>
|
</pre>
|
||||||
}/>
|
}/>
|
||||||
|
@ -79,12 +79,14 @@ let Piece = React.createClass({
|
|||||||
|
|
||||||
handleEditionCreationSuccess() {
|
handleEditionCreationSuccess() {
|
||||||
PieceActions.updateProperty({key: 'num_editions', value: 0});
|
PieceActions.updateProperty({key: 'num_editions', value: 0});
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
this.toggleCreateEditionsDialog();
|
this.toggleCreateEditionsDialog();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDeleteSuccess(response) {
|
handleDeleteSuccess(response) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
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
|
// since we're deleting a piece, we just need to close
|
||||||
// all editions dialogs and not reload them
|
// all editions dialogs and not reload them
|
||||||
@ -113,11 +115,20 @@ let Piece = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handlePollingSuccess(pieceId, numEditions) {
|
handlePollingSuccess(pieceId, numEditions) {
|
||||||
|
|
||||||
|
// we need to refresh the num_editions property of the actual piece we're looking at
|
||||||
PieceActions.updateProperty({
|
PieceActions.updateProperty({
|
||||||
key: 'num_editions',
|
key: 'num_editions',
|
||||||
value: numEditions
|
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);
|
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
@ -8,12 +8,11 @@ import Property from '../ascribe_forms/property';
|
|||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
let CreateEditionsForm = React.createClass({
|
let CreateEditionsForm = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
handleSuccess: React.PropTypes.func,
|
handleSuccess: React.PropTypes.func,
|
||||||
pieceId: React.PropTypes.number
|
pieceId: React.PropTypes.number
|
||||||
@ -38,7 +37,7 @@ let CreateEditionsForm = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
ref='form'
|
ref='form'
|
||||||
url={apiUrls.editions}
|
url={ApiUrls.editions}
|
||||||
getFormData={this.getFormData}
|
getFormData={this.getFormData}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -4,23 +4,46 @@ import React from 'react';
|
|||||||
import ReactAddons from 'react/addons';
|
import ReactAddons from 'react/addons';
|
||||||
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
|
import AlertDismissable from './alert';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
import requests from '../../utils/requests';
|
import requests from '../../utils/requests';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
import { mergeOptionsWithDuplicates } from '../../utils/general_utils';
|
import { mergeOptionsWithDuplicates } from '../../utils/general_utils';
|
||||||
import AlertDismissable from './alert';
|
|
||||||
|
|
||||||
|
|
||||||
let Form = React.createClass({
|
let Form = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
url: React.PropTypes.string,
|
url: React.PropTypes.string,
|
||||||
|
method: React.PropTypes.string,
|
||||||
|
buttonSubmitText: React.PropTypes.string,
|
||||||
handleSuccess: React.PropTypes.func,
|
handleSuccess: React.PropTypes.func,
|
||||||
getFormData: React.PropTypes.func,
|
getFormData: React.PropTypes.func,
|
||||||
children: React.PropTypes.oneOfType([
|
children: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.object,
|
React.PropTypes.object,
|
||||||
React.PropTypes.array
|
React.PropTypes.array
|
||||||
]),
|
]),
|
||||||
className: React.PropTypes.string
|
className: React.PropTypes.string,
|
||||||
|
spinner: React.PropTypes.element,
|
||||||
|
buttons: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.element,
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element)
|
||||||
|
]),
|
||||||
|
|
||||||
|
// You can use the form for inline requests, like the submit click on a button.
|
||||||
|
// For the form to then not display the error on top, you need to enable this option.
|
||||||
|
// It will make use of the GlobalNotification
|
||||||
|
isInline: React.PropTypes.bool
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
buttonSubmitText: 'SAVE'
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
@ -30,6 +53,7 @@ let Form = React.createClass({
|
|||||||
errors: []
|
errors: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
reset(){
|
reset(){
|
||||||
for (let ref in this.refs){
|
for (let ref in this.refs){
|
||||||
if (typeof this.refs[ref].reset === 'function'){
|
if (typeof this.refs[ref].reset === 'function'){
|
||||||
@ -38,22 +62,38 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
},
|
},
|
||||||
|
|
||||||
submit(event){
|
submit(event){
|
||||||
if (event) {
|
|
||||||
|
if(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({submitted: true});
|
this.setState({submitted: true});
|
||||||
this.clearErrors();
|
this.clearErrors();
|
||||||
let action = (this.httpVerb && this.httpVerb()) || 'post';
|
|
||||||
window.setTimeout(() => this[action](), 100);
|
// selecting http method based on props
|
||||||
|
if(this[this.props.method]) {
|
||||||
|
window.setTimeout(() => this[this.props.method](), 100);
|
||||||
|
} else {
|
||||||
|
throw new Error('This HTTP method is not supported by form.js (' + this.props.method + ')');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
post(){
|
|
||||||
|
post() {
|
||||||
requests
|
requests
|
||||||
.post(this.props.url, { body: this.getFormData() })
|
.post(this.props.url, { body: this.getFormData() })
|
||||||
.then(this.handleSuccess)
|
.then(this.handleSuccess)
|
||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
requests
|
||||||
|
.delete(this.props.url, this.getFormData())
|
||||||
|
.then(this.handleSuccess)
|
||||||
|
.catch(this.handleError);
|
||||||
|
},
|
||||||
|
|
||||||
getFormData(){
|
getFormData(){
|
||||||
let data = {};
|
let data = {};
|
||||||
for (let ref in this.refs){
|
for (let ref in this.refs){
|
||||||
@ -69,6 +109,7 @@ let Form = React.createClass({
|
|||||||
handleChangeChild(){
|
handleChangeChild(){
|
||||||
this.setState({edited: true});
|
this.setState({edited: true});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess(response){
|
handleSuccess(response){
|
||||||
if ('handleSuccess' in this.props){
|
if ('handleSuccess' in this.props){
|
||||||
this.props.handleSuccess(response);
|
this.props.handleSuccess(response);
|
||||||
@ -78,8 +119,12 @@ let Form = React.createClass({
|
|||||||
this.refs[ref].handleSuccess();
|
this.refs[ref].handleSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({edited: false, submitted: false});
|
this.setState({
|
||||||
|
edited: false,
|
||||||
|
submitted: false
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleError(err){
|
handleError(err){
|
||||||
if (err.json) {
|
if (err.json) {
|
||||||
for (var input in err.json.errors){
|
for (var input in err.json.errors){
|
||||||
@ -91,11 +136,26 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.logGlobal(err, false, this.getFormData());
|
let formData = this.getFormData();
|
||||||
this.setState({errors: [getLangText('Something went wrong, please try again later')]});
|
|
||||||
|
// sentry shouldn't post the user's password
|
||||||
|
if(formData.password) {
|
||||||
|
delete formData.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.logGlobal(err, false, formData);
|
||||||
|
|
||||||
|
if(this.props.isInline) {
|
||||||
|
let notification = new GlobalNotificationModel(getLangText('Something went wrong, please try again later'), 'danger');
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
} else {
|
||||||
|
this.setState({errors: [getLangText('Something went wrong, please try again later')]});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
this.setState({submitted: false});
|
this.setState({submitted: false});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors(){
|
clearErrors(){
|
||||||
for (var ref in this.refs){
|
for (var ref in this.refs){
|
||||||
if ('clearErrors' in this.refs[ref]){
|
if ('clearErrors' in this.refs[ref]){
|
||||||
@ -104,6 +164,7 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
this.setState({errors: []});
|
this.setState({errors: []});
|
||||||
},
|
},
|
||||||
|
|
||||||
getButtons() {
|
getButtons() {
|
||||||
if (this.state.submitted){
|
if (this.state.submitted){
|
||||||
return this.props.spinner;
|
return this.props.spinner;
|
||||||
@ -117,7 +178,7 @@ let Form = React.createClass({
|
|||||||
buttons = (
|
buttons = (
|
||||||
<div className="row" style={{margin: 0}}>
|
<div className="row" style={{margin: 0}}>
|
||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button className="btn btn-default btn-sm ascribe-margin-1px" type="submit">SAVE</Button>
|
<Button className="btn btn-default btn-sm ascribe-margin-1px" type="submit">{this.props.buttonSubmitText}</Button>
|
||||||
<Button className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.reset}>CANCEL</Button>
|
<Button className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.reset}>CANCEL</Button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -126,6 +187,7 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
return buttons;
|
return buttons;
|
||||||
},
|
},
|
||||||
|
|
||||||
getErrors() {
|
getErrors() {
|
||||||
let errors = null;
|
let errors = null;
|
||||||
if (this.state.errors.length > 0){
|
if (this.state.errors.length > 0){
|
||||||
@ -135,6 +197,7 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderChildren() {
|
renderChildren() {
|
||||||
return ReactAddons.Children.map(this.props.children, (child) => {
|
return ReactAddons.Children.map(this.props.children, (child) => {
|
||||||
if (child) {
|
if (child) {
|
||||||
@ -145,6 +208,7 @@ let Form = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let className = 'ascribe-form';
|
let className = 'ascribe-form';
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import Form from './form';
|
|||||||
import Property from './property';
|
import Property from './property';
|
||||||
import InputTextAreaToggable from './input_textarea_toggable';
|
import InputTextAreaToggable from './input_textarea_toggable';
|
||||||
|
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ let ConsignForm = React.createClass({
|
|||||||
url: React.PropTypes.string,
|
url: React.PropTypes.string,
|
||||||
id: React.PropTypes.object,
|
id: React.PropTypes.object,
|
||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -27,7 +25,6 @@ let ConsignForm = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
ref='form'
|
ref='form'
|
||||||
@ -39,11 +36,9 @@ let ConsignForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('CONSIGN')}</Button>
|
type="submit">
|
||||||
<Button
|
{getLangText('CONSIGN')}
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -2,33 +2,65 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import requests from '../../utils/requests';
|
import Form from './form';
|
||||||
|
|
||||||
import ApiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let EditionDeleteForm = React.createClass({
|
let EditionDeleteForm = React.createClass({
|
||||||
|
|
||||||
mixins: [FormMixin],
|
propTypes: {
|
||||||
|
editions: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
|
||||||
url() {
|
// Propagated by ModalWrapper in most cases
|
||||||
return requests.prepareUrl(ApiUrls.edition_delete, {edition_id: this.getBitcoinIds().join()});
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
|
||||||
httpVerb(){
|
|
||||||
return 'delete';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderForm () {
|
getBitcoinIds() {
|
||||||
|
return this.props.editions.map(function(edition){
|
||||||
|
return edition.bitcoin_id;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Since this form can be used for either deleting a single edition or multiple
|
||||||
|
// we need to call getBitcoinIds to get the value of edition_id
|
||||||
|
getFormData() {
|
||||||
|
return {
|
||||||
|
edition_id: this.getBitcoinIds().join(',')
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="modal-body">
|
<Form
|
||||||
|
ref='form'
|
||||||
|
url={ApiUrls.edition_delete}
|
||||||
|
getFormData={this.getFormData}
|
||||||
|
method="delete"
|
||||||
|
handleSuccess={this.props.handleSuccess}
|
||||||
|
buttons={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<p className="pull-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
||||||
|
onClick={this.submit}>
|
||||||
|
{getLangText('YES, DELETE')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
spinner={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<p>{getLangText('Are you sure you would like to permanently delete this edition')}?</p>
|
<p>{getLangText('Are you sure you would like to permanently delete this edition')}?</p>
|
||||||
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
||||||
<div className="modal-footer">
|
</Form>
|
||||||
<button type="submit" className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.submit}>{getLangText('YES, DELETE')}</button>
|
|
||||||
<button className="btn btn-default btn-sm ascribe-margin-1px" style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,37 +2,56 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import requests from '../../utils/requests';
|
import Form from '../ascribe_forms/form';
|
||||||
|
|
||||||
import ApiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let PieceDeleteForm = React.createClass({
|
let PieceDeleteForm = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
pieceId: React.PropTypes.number
|
pieceId: React.PropTypes.number,
|
||||||
|
|
||||||
|
// Propagated by ModalWrapper in most cases
|
||||||
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [FormMixin],
|
getFormData() {
|
||||||
|
return {
|
||||||
url() {
|
piece_id: this.props.pieceId
|
||||||
return requests.prepareUrl(ApiUrls.piece, {piece_id: this.props.pieceId});
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
httpVerb() {
|
render() {
|
||||||
return 'delete';
|
|
||||||
},
|
|
||||||
|
|
||||||
renderForm () {
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-body">
|
<Form
|
||||||
|
ref='form'
|
||||||
|
url={ApiUrls.piece}
|
||||||
|
getFormData={this.getFormData}
|
||||||
|
method="delete"
|
||||||
|
handleSuccess={this.props.handleSuccess}
|
||||||
|
buttons={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<p className="pull-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
||||||
|
onClick={this.submit}>
|
||||||
|
{getLangText('YES, DELETE')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
spinner={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<p>{getLangText('Are you sure you would like to permanently delete this piece')}?</p>
|
<p>{getLangText('Are you sure you would like to permanently delete this piece')}?</p>
|
||||||
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
||||||
<div className="modal-footer">
|
</Form>
|
||||||
<button type="submit" className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.submit}>{getLangText('YES, DELETE')}</button>
|
|
||||||
<button className="btn btn-default btn-sm ascribe-margin-1px" style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,6 @@ let LoanForm = React.createClass({
|
|||||||
url: React.PropTypes.string,
|
url: React.PropTypes.string,
|
||||||
id: React.PropTypes.object,
|
id: React.PropTypes.object,
|
||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -33,6 +32,7 @@ let LoanForm = React.createClass({
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
LoanContractStore.listen(this.onChange);
|
LoanContractStore.listen(this.onChange);
|
||||||
|
LoanContractActions.flushLoanContract();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -53,13 +53,16 @@ let LoanForm = React.createClass({
|
|||||||
|
|
||||||
getContractCheckbox() {
|
getContractCheckbox() {
|
||||||
if(this.state.contractKey && this.state.contractUrl) {
|
if(this.state.contractKey && this.state.contractUrl) {
|
||||||
|
// we need to define a key on the InputCheckboxes as otherwise
|
||||||
|
// react is not rerendering them on a store switch and is keeping
|
||||||
|
// the default value of the component (which is in that case true)
|
||||||
return (
|
return (
|
||||||
<Property
|
<Property
|
||||||
name="terms"
|
name="terms"
|
||||||
className="ascribe-settings-property-collapsible-toggle"
|
className="ascribe-settings-property-collapsible-toggle"
|
||||||
style={{paddingBottom: 0}}>
|
style={{paddingBottom: 0}}>
|
||||||
<InputCheckbox
|
<InputCheckbox
|
||||||
key='contract_terms'
|
key="terms_explicitly"
|
||||||
defaultChecked={false}>
|
defaultChecked={false}>
|
||||||
<span>
|
<span>
|
||||||
{getLangText('I agree to the')}
|
{getLangText('I agree to the')}
|
||||||
@ -77,27 +80,13 @@ let LoanForm = React.createClass({
|
|||||||
style={{paddingBottom: 0}}
|
style={{paddingBottom: 0}}
|
||||||
hidden={true}>
|
hidden={true}>
|
||||||
<InputCheckbox
|
<InputCheckbox
|
||||||
key='implicit_terms'
|
key="terms_implicitly"
|
||||||
defaultChecked={true} />
|
defaultChecked={true} />
|
||||||
</Property>
|
</Property>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRequestHide() {
|
|
||||||
// Since the modal can be opened without sending it to the server
|
|
||||||
// and therefore clearing the store,
|
|
||||||
// we'll need to make sure to flush the store once the
|
|
||||||
// modal unmounts
|
|
||||||
LoanContractActions.updateLoanContract({
|
|
||||||
contractUrl: null,
|
|
||||||
contractEmail: null,
|
|
||||||
contractKey: null
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onRequestHide();
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -111,11 +100,9 @@ let LoanForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('LOAN')}</Button>
|
type="submit">
|
||||||
<Button
|
{getLangText('LOAN')}
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.onRequestHide}>{getLangText('CLOSE')}</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -11,16 +11,14 @@ import UserActions from '../../actions/user_actions';
|
|||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
import FormPropertyHeader from './form_property_header';
|
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let LoginForm = React.createClass({
|
let LoginForm = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
headerMessage: React.PropTypes.string,
|
headerMessage: React.PropTypes.string,
|
||||||
submitMessage: React.PropTypes.string,
|
submitMessage: React.PropTypes.string,
|
||||||
@ -101,7 +99,7 @@ let LoginForm = React.createClass({
|
|||||||
<Form
|
<Form
|
||||||
className="ascribe-form-bordered"
|
className="ascribe-form-bordered"
|
||||||
ref="loginForm"
|
ref="loginForm"
|
||||||
url={apiUrls.users_login}
|
url={ApiUrls.users_login}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
buttons={
|
buttons={
|
||||||
<button
|
<button
|
||||||
@ -114,9 +112,9 @@ let LoginForm = React.createClass({
|
|||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
<FormPropertyHeader>
|
<div className="ascribe-form-header">
|
||||||
<h3>{this.props.headerMessage}</h3>
|
<h3>{this.props.headerMessage}</h3>
|
||||||
</FormPropertyHeader>
|
</div>
|
||||||
<Property
|
<Property
|
||||||
name='email'
|
name='email'
|
||||||
label={getLangText('Email')}>
|
label={getLangText('Email')}>
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import requests from '../../utils/requests';
|
import requests from '../../utils/requests';
|
||||||
import { getLangText } from '../../utils/lang_utils.js'
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
@ -20,7 +20,8 @@ let PieceExtraDataForm = React.createClass({
|
|||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
editable: React.PropTypes.bool
|
editable: React.PropTypes.bool
|
||||||
},
|
},
|
||||||
getFormData(){
|
|
||||||
|
getFormData() {
|
||||||
let extradata = {};
|
let extradata = {};
|
||||||
extradata[this.props.name] = this.refs.form.refs[this.props.name].state.value;
|
extradata[this.props.name] = this.refs.form.refs[this.props.name].state.value;
|
||||||
return {
|
return {
|
||||||
@ -28,12 +29,13 @@ let PieceExtraDataForm = React.createClass({
|
|||||||
piece_id: this.props.pieceId
|
piece_id: this.props.pieceId
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let defaultValue = this.props.extraData[this.props.name] || '';
|
let defaultValue = this.props.extraData[this.props.name] || '';
|
||||||
if (defaultValue.length === 0 && !this.props.editable){
|
if (defaultValue.length === 0 && !this.props.editable){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let url = requests.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.pieceId});
|
let url = requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.pieceId});
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
ref='form'
|
ref='form'
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
let FormPropertyHeader = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
children: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
|
||||||
React.PropTypes.element
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="ascribe-form-header">
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FormPropertyHeader;
|
|
@ -7,12 +7,11 @@ import UserActions from '../../actions/user_actions';
|
|||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
import FormPropertyHeader from './form_property_header';
|
|
||||||
|
|
||||||
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
import { getCookie } from '../../utils/fetch_api_utils';
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
@ -96,7 +95,7 @@ let RegisterPieceForm = React.createClass({
|
|||||||
<Form
|
<Form
|
||||||
className="ascribe-form-bordered"
|
className="ascribe-form-bordered"
|
||||||
ref='form'
|
ref='form'
|
||||||
url={apiUrls.pieces_list}
|
url={ApiUrls.pieces_list}
|
||||||
getFormData={this.getFormData}
|
getFormData={this.getFormData}
|
||||||
handleSuccess={this.props.handleSuccess}
|
handleSuccess={this.props.handleSuccess}
|
||||||
buttons={<button
|
buttons={<button
|
||||||
@ -110,9 +109,9 @@ let RegisterPieceForm = React.createClass({
|
|||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
<FormPropertyHeader>
|
<div className="ascribe-form-header">
|
||||||
<h3>{this.props.headerMessage}</h3>
|
<h3>{this.props.headerMessage}</h3>
|
||||||
</FormPropertyHeader>
|
</div>
|
||||||
<Property
|
<Property
|
||||||
ignoreFocus={true}>
|
ignoreFocus={true}>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
@ -180,7 +179,7 @@ let FileUploader = React.createClass({
|
|||||||
fileClass: 'digitalwork'
|
fileClass: 'digitalwork'
|
||||||
}}
|
}}
|
||||||
createBlobRoutine={{
|
createBlobRoutine={{
|
||||||
url: apiUrls.blob_digitalworks
|
url: ApiUrls.blob_digitalworks
|
||||||
}}
|
}}
|
||||||
submitKey={this.props.submitKey}
|
submitKey={this.props.submitKey}
|
||||||
validation={{
|
validation={{
|
||||||
|
@ -2,34 +2,63 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import Form from './form';
|
||||||
import requests from '../../utils/requests';
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
let EditionRemoveFromCollectionForm = React.createClass({
|
let EditionRemoveFromCollectionForm = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
editions: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
|
||||||
mixins: [FormMixin],
|
// Propagated by ModalWrapper in most cases
|
||||||
|
handleSuccess: React.PropTypes.func
|
||||||
url() {
|
|
||||||
return requests.prepareUrl(apiUrls.edition_remove_from_collection, {edition_id: this.getBitcoinIds().join()});
|
|
||||||
},
|
|
||||||
|
|
||||||
httpVerb(){
|
|
||||||
return 'delete';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderForm () {
|
getBitcoinIds() {
|
||||||
|
return this.props.editions.map(function(edition){
|
||||||
|
return edition.bitcoin_id;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Since this form can be used for either removing a single edition or multiple
|
||||||
|
// we need to call getBitcoinIds to get the value of edition_id
|
||||||
|
getFormData() {
|
||||||
|
return {
|
||||||
|
edition_id: this.getBitcoinIds().join(',')
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="modal-body">
|
<Form
|
||||||
|
ref='form'
|
||||||
|
url={ApiUrls.edition_remove_from_collection}
|
||||||
|
getFormData={this.getFormData}
|
||||||
|
method="delete"
|
||||||
|
handleSuccess={this.props.handleSuccess}
|
||||||
|
buttons={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<p className="pull-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
||||||
|
onClick={this.submit}>
|
||||||
|
{getLangText('YES, REMOVE')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
spinner={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<p>{getLangText('Are you sure you would like to remove these editions from your collection')}?</p>
|
<p>{getLangText('Are you sure you would like to remove these editions from your collection')}?</p>
|
||||||
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
||||||
<div className="modal-footer">
|
</Form>
|
||||||
<button type="submit" className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.submit}>{getLangText('YES, REMOVE')}</button>
|
|
||||||
<button className="btn btn-default btn-sm ascribe-margin-1px" style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,38 +2,56 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import Form from './form';
|
||||||
import requests from '../../utils/requests';
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let PieceRemoveFromCollectionForm = React.createClass({
|
let PieceRemoveFromCollectionForm = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
pieceId: React.PropTypes.number
|
pieceId: React.PropTypes.number,
|
||||||
|
|
||||||
|
// Propagated by ModalWrapper in most cases
|
||||||
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [FormMixin],
|
getFormData() {
|
||||||
|
return {
|
||||||
url() {
|
piece_id: this.props.pieceId
|
||||||
return requests.prepareUrl(apiUrls.piece_remove_from_collection, {piece_id: this.props.pieceId});
|
};
|
||||||
},
|
|
||||||
|
|
||||||
httpVerb(){
|
|
||||||
return 'delete';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderForm () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="modal-body">
|
<Form
|
||||||
|
ref='form'
|
||||||
|
url={ApiUrls.piece_remove_from_collection}
|
||||||
|
getFormData={this.getFormData}
|
||||||
|
method="delete"
|
||||||
|
handleSuccess={this.props.handleSuccess}
|
||||||
|
buttons={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<p className="pull-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
||||||
|
onClick={this.submit}>
|
||||||
|
{getLangText('YES, REMOVE')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
spinner={
|
||||||
|
<div className="modal-footer">
|
||||||
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
<p>{getLangText('Are you sure you would like to remove this piece from your collection')}?</p>
|
<p>{getLangText('Are you sure you would like to remove this piece from your collection')}?</p>
|
||||||
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
||||||
<div className="modal-footer">
|
</Form>
|
||||||
<button type="submit" className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.submit}>{getLangText('YES, REMOVE')}</button>
|
|
||||||
<button className="btn btn-default btn-sm ascribe-margin-1px" style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,48 +2,48 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Alert from 'react-bootstrap/lib/Alert';
|
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
|
||||||
|
|
||||||
import AclButton from './../ascribe_buttons/acl_button';
|
import AclButton from './../ascribe_buttons/acl_button';
|
||||||
|
import ActionPanel from '../ascribe_panel/action_panel';
|
||||||
|
import Form from './form';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
let RequestActionForm = React.createClass({
|
|
||||||
mixins: [FormMixin],
|
|
||||||
|
|
||||||
url(e){
|
let RequestActionForm = React.createClass({
|
||||||
let edition = this.props.editions[0];
|
propTypes: {
|
||||||
if (e.target.id === 'request_accept'){
|
editions: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
if (edition.request_action === 'consign'){
|
currentUser: React.PropTypes.object,
|
||||||
return apiUrls.ownership_consigns_confirm;
|
handleSuccess: React.PropTypes.func
|
||||||
}
|
|
||||||
else if (edition.request_action === 'unconsign'){
|
|
||||||
return apiUrls.ownership_unconsigns;
|
|
||||||
}
|
|
||||||
else if (edition.request_action === 'loan'){
|
|
||||||
return apiUrls.ownership_loans_confirm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(e.target.id === 'request_deny'){
|
|
||||||
if (edition.request_action === 'consign') {
|
|
||||||
return apiUrls.ownership_consigns_deny;
|
|
||||||
}
|
|
||||||
else if (edition.request_action === 'unconsign') {
|
|
||||||
return apiUrls.ownership_unconsigns_deny;
|
|
||||||
}
|
|
||||||
else if (edition.request_action === 'loan') {
|
|
||||||
return apiUrls.ownership_loans_deny;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRequest: function(e){
|
getUrls() {
|
||||||
e.preventDefault();
|
let edition = this.props.editions[0];
|
||||||
this.submit(e);
|
let urls = {};
|
||||||
|
|
||||||
|
|
||||||
|
if (edition.request_action === 'consign'){
|
||||||
|
urls.accept = ApiUrls.ownership_consigns_confirm;
|
||||||
|
urls.deny = ApiUrls.ownership_consigns_deny;
|
||||||
|
} else if (edition.request_action === 'unconsign'){
|
||||||
|
urls.accept = ApiUrls.ownership_unconsigns;
|
||||||
|
urls.deny = ApiUrls.ownership_unconsigns_deny;
|
||||||
|
} else if (edition.request_action === 'loan'){
|
||||||
|
urls.accept = ApiUrls.ownership_loans_confirm;
|
||||||
|
urls.deny = ApiUrls.ownership_loans_deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
},
|
||||||
|
|
||||||
|
getBitcoinIds(){
|
||||||
|
return this.props.editions.map(function(edition){
|
||||||
|
return edition.bitcoin_id;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getFormData() {
|
getFormData() {
|
||||||
@ -52,16 +52,35 @@ let RequestActionForm = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
renderForm() {
|
showNotification(option, action, owner) {
|
||||||
|
return () => {
|
||||||
|
let message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner;
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel(message, 'success');
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
|
||||||
|
if(this.props.handleSuccess) {
|
||||||
|
this.props.handleSuccess();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getContent() {
|
||||||
let edition = this.props.editions[0];
|
let edition = this.props.editions[0];
|
||||||
let buttonAccept = (
|
let message = edition.owner + ' ' + getLangText('requests you') + ' ' + edition.request_action + ' ' + getLangText('this edition%s', '.');
|
||||||
<div id="request_accept"
|
|
||||||
onClick={this.handleRequest}
|
return (
|
||||||
className='btn btn-default btn-sm ascribe-margin-1px'>{getLangText('ACCEPT')}
|
<span>
|
||||||
</div>);
|
{message}
|
||||||
if (edition.request_action === 'unconsign'){
|
</span>
|
||||||
console.log(this.props)
|
);
|
||||||
buttonAccept = (
|
},
|
||||||
|
|
||||||
|
getAcceptButtonForm(urls) {
|
||||||
|
let edition = this.props.editions[0];
|
||||||
|
|
||||||
|
if(edition.request_action === 'unconsign') {
|
||||||
|
return (
|
||||||
<AclButton
|
<AclButton
|
||||||
availableAcls={{'acl_unconsign': true}}
|
availableAcls={{'acl_unconsign': true}}
|
||||||
action="acl_unconsign"
|
action="acl_unconsign"
|
||||||
@ -69,31 +88,54 @@ let RequestActionForm = React.createClass({
|
|||||||
currentUser={this.props.currentUser}
|
currentUser={this.props.currentUser}
|
||||||
handleSuccess={this.props.handleSuccess} />
|
handleSuccess={this.props.handleSuccess} />
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
let buttons = (
|
return (
|
||||||
<span>
|
<Form
|
||||||
<span>
|
url={urls.accept}
|
||||||
{buttonAccept}
|
getFormData={this.getFormData}
|
||||||
</span>
|
handleSuccess={this.showNotification(getLangText('accepted'), edition.request_action, edition.owner)}
|
||||||
<span>
|
isInline={true}
|
||||||
<div id="request_deny" onClick={this.handleRequest} className='btn btn-danger btn-delete btn-sm ascribe-margin-1px'>{getLangText('REJECT')}</div>
|
className='inline pull-right'>
|
||||||
</span>
|
<button
|
||||||
</span>
|
type="submit"
|
||||||
);
|
className='btn btn-default btn-sm ascribe-margin-1px'>
|
||||||
if (this.state.submitted){
|
{getLangText('ACCEPT')}
|
||||||
buttons = (
|
</button>
|
||||||
<span>
|
</Form>
|
||||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getButtonForm() {
|
||||||
|
let edition = this.props.editions[0];
|
||||||
|
|
||||||
|
let urls = this.getUrls();
|
||||||
|
let acceptButtonForm = this.getAcceptButtonForm(urls);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert bsStyle='warning'>
|
<div>
|
||||||
<div style={{textAlign: 'center'}}>
|
<Form
|
||||||
<div>{ edition.owner } {getLangText('requests you')} { edition.request_action } {getLangText('this edition%s', '.')} </div>
|
url={urls.deny}
|
||||||
{buttons}
|
isInline={true}
|
||||||
</div>
|
getFormData={this.getFormData}
|
||||||
</Alert>
|
handleSuccess={this.showNotification(getLangText('denied'), edition.request_action, edition.owner)}
|
||||||
|
className='inline pull-right'>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className='btn btn-danger btn-delete btn-sm ascribe-margin-1px'>
|
||||||
|
{getLangText('REJECT')}
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
{acceptButtonForm}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ActionPanel
|
||||||
|
content={this.getContent()}
|
||||||
|
buttons={this.getButtonForm()}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
import InputTextAreaToggable from './input_textarea_toggable';
|
import InputTextAreaToggable from './input_textarea_toggable';
|
||||||
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +20,6 @@ let ShareForm = React.createClass({
|
|||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
editions: React.PropTypes.array,
|
editions: React.PropTypes.array,
|
||||||
currentUser: React.PropTypes.object,
|
currentUser: React.PropTypes.object,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -41,11 +40,9 @@ let ShareForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">SHARE</Button>
|
type="submit">
|
||||||
<Button
|
SHARE
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>CLOSE</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -12,10 +12,9 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
|
|||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
import FormPropertyHeader from './form_property_header';
|
|
||||||
import InputCheckbox from './input_checkbox';
|
import InputCheckbox from './input_checkbox';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
|
|
||||||
let SignupForm = React.createClass({
|
let SignupForm = React.createClass({
|
||||||
@ -27,7 +26,7 @@ let SignupForm = React.createClass({
|
|||||||
children: React.PropTypes.element
|
children: React.PropTypes.element
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation, Router.State],
|
||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
@ -35,7 +34,6 @@ let SignupForm = React.createClass({
|
|||||||
submitMessage: getLangText('Sign up')
|
submitMessage: getLangText('Sign up')
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return UserStore.getState();
|
return UserStore.getState();
|
||||||
},
|
},
|
||||||
@ -58,21 +56,27 @@ let SignupForm = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess(response){
|
handleSuccess(response){
|
||||||
let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000);
|
if (response.user) {
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000);
|
||||||
this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.');
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.');
|
||||||
|
}
|
||||||
|
else if (response.redirect) {
|
||||||
|
this.transitionTo('pieces');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' +
|
let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' +
|
||||||
getLangText('This password is securing your digital property like a bank account') + '.\n ' +
|
getLangText('This password is securing your digital property like a bank account') + '.\n ' +
|
||||||
getLangText('Store it in a safe place') + '!';
|
getLangText('Store it in a safe place') + '!';
|
||||||
|
let email = this.getQuery().email ? this.getQuery().email : null;
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
className="ascribe-form-bordered"
|
className="ascribe-form-bordered"
|
||||||
ref='form'
|
ref='form'
|
||||||
url={apiUrls.users_signup}
|
url={ApiUrls.users_signup}
|
||||||
|
getFormData={this.getQuery}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
buttons={
|
buttons={
|
||||||
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||||
@ -83,9 +87,9 @@ let SignupForm = React.createClass({
|
|||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
<FormPropertyHeader>
|
<div className="ascribe-form-header">
|
||||||
<h3>{this.props.headerMessage}</h3>
|
<h3>{this.props.headerMessage}</h3>
|
||||||
</FormPropertyHeader>
|
</div>
|
||||||
<Property
|
<Property
|
||||||
name='email'
|
name='email'
|
||||||
label={getLangText('Email')}>
|
label={getLangText('Email')}>
|
||||||
@ -93,6 +97,7 @@ let SignupForm = React.createClass({
|
|||||||
type="email"
|
type="email"
|
||||||
placeholder={getLangText('(e.g. andy@warhol.co.uk)')}
|
placeholder={getLangText('(e.g. andy@warhol.co.uk)')}
|
||||||
autoComplete="on"
|
autoComplete="on"
|
||||||
|
defaultValue={email}
|
||||||
required/>
|
required/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property
|
<Property
|
||||||
|
@ -19,10 +19,7 @@ import requests from '../../utils/requests';
|
|||||||
let PieceSubmitToPrizeForm = React.createClass({
|
let PieceSubmitToPrizeForm = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
piece: React.PropTypes.object,
|
piece: React.PropTypes.object,
|
||||||
handleSuccess: React.PropTypes.func,
|
handleSuccess: React.PropTypes.func
|
||||||
|
|
||||||
// this is set by ModalWrapper automatically
|
|
||||||
onRequestHide: React.PropTypes.func
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -36,7 +33,9 @@ let PieceSubmitToPrizeForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<button
|
<button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('SUBMIT TO PRIZE')}</button>
|
type="submit">
|
||||||
|
{getLangText('SUBMIT TO PRIZE')}
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
@ -80,7 +79,6 @@ let PieceSubmitToPrizeForm = React.createClass({
|
|||||||
<p>{getLangText('Are you sure you want to submit to the prize?')}</p>
|
<p>{getLangText('Are you sure you want to submit to the prize?')}</p>
|
||||||
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ let TransferForm = React.createClass({
|
|||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
editions: React.PropTypes.array,
|
editions: React.PropTypes.array,
|
||||||
currentUser: React.PropTypes.object,
|
currentUser: React.PropTypes.object,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -42,11 +41,9 @@ let TransferForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('TRANSFER')}</Button>
|
type="submit">
|
||||||
<Button
|
{getLangText('TRANSFER')}
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -18,7 +18,6 @@ let UnConsignForm = React.createClass({
|
|||||||
id: React.PropTypes.object,
|
id: React.PropTypes.object,
|
||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
editions: React.PropTypes.array,
|
editions: React.PropTypes.array,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -39,11 +38,9 @@ let UnConsignForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('UNCONSIGN')}</Button>
|
type="submit">
|
||||||
<Button
|
{getLangText('UNCONSIGN')}
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
import Alert from 'react-bootstrap/lib/Alert';
|
|
||||||
|
|
||||||
import Form from './form';
|
import Form from './form';
|
||||||
import Property from './property';
|
import Property from './property';
|
||||||
@ -19,7 +18,6 @@ let UnConsignRequestForm = React.createClass({
|
|||||||
url: React.PropTypes.string,
|
url: React.PropTypes.string,
|
||||||
id: React.PropTypes.object,
|
id: React.PropTypes.object,
|
||||||
message: React.PropTypes.string,
|
message: React.PropTypes.string,
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -40,11 +38,9 @@ let UnConsignRequestForm = React.createClass({
|
|||||||
<p className="pull-right">
|
<p className="pull-right">
|
||||||
<Button
|
<Button
|
||||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||||
type="submit">{getLangText('REQUEST UNCONSIGN')}</Button>
|
type="submit">
|
||||||
<Button
|
{getLangText('REQUEST UNCONSIGN')}
|
||||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
</Button>
|
||||||
style={{marginLeft: '0'}}
|
|
||||||
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</Button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
spinner={
|
spinner={
|
||||||
|
@ -7,7 +7,8 @@ import DatePicker from 'react-datepicker/dist/react-datepicker';
|
|||||||
let InputDate = React.createClass({
|
let InputDate = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitted: React.PropTypes.bool,
|
submitted: React.PropTypes.bool,
|
||||||
placeholderText: React.PropTypes.string
|
placeholderText: React.PropTypes.string,
|
||||||
|
onChange: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
|
@ -5,7 +5,6 @@ import React from 'react';
|
|||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
|
|
||||||
let InputTextAreaToggable = React.createClass({
|
let InputTextAreaToggable = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
editable: React.PropTypes.bool.isRequired,
|
editable: React.PropTypes.bool.isRequired,
|
||||||
rows: React.PropTypes.number.isRequired,
|
rows: React.PropTypes.number.isRequired,
|
||||||
|
@ -70,23 +70,22 @@ let Property = React.createClass({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.state.initialValue) {
|
if(!this.state.initialValue && childInput.props.defaultValue) {
|
||||||
this.setState({
|
this.setState({
|
||||||
initialValue: childInput.defaultValue
|
initialValue: childInput.props.defaultValue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reset(){
|
reset() {
|
||||||
// maybe do reset by reload instead of front end state?
|
// maybe do reset by reload instead of front end state?
|
||||||
this.setState({value: this.state.initialValue});
|
this.setState({value: this.state.initialValue});
|
||||||
if (this.refs.input.state){
|
|
||||||
// This is probably not the right way but easy fix
|
// resets the value of a custom react component input
|
||||||
this.refs.input.state.value = this.state.initialValue;
|
this.refs.input.state.value = this.state.initialValue;
|
||||||
}
|
|
||||||
else{
|
// resets the value of a plain HTML5 input
|
||||||
this.refs.input.getDOMNode().value = this.state.initialValue;
|
this.refs.input.getDOMNode().value = this.state.initialValue;
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactAddons from 'react/addons';
|
import ReactAddons from 'react/addons';
|
||||||
|
|
||||||
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
|
|
||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||||
|
import Panel from 'react-bootstrap/lib/Panel';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
|
|
||||||
let PropertyCollapsile = React.createClass({
|
let PropertyCollapsile = React.createClass({
|
||||||
@ -17,22 +15,12 @@ let PropertyCollapsile = React.createClass({
|
|||||||
tooltip: React.PropTypes.string
|
tooltip: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CollapsibleMixin],
|
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
show: false
|
show: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getCollapsibleDOMNode(){
|
|
||||||
return React.findDOMNode(this.refs.panel);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCollapsibleDimensionValue(){
|
|
||||||
return React.findDOMNode(this.refs.panel).scrollHeight;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFocus() {
|
handleFocus() {
|
||||||
this.refs.checkboxCollapsible.getDOMNode().checked = !this.refs.checkboxCollapsible.getDOMNode().checked;
|
this.refs.checkboxCollapsible.getDOMNode().checked = !this.refs.checkboxCollapsible.getDOMNode().checked;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -85,11 +73,14 @@ let PropertyCollapsile = React.createClass({
|
|||||||
<span className="checkbox"> {this.props.checkboxLabel}</span>
|
<span className="checkbox"> {this.props.checkboxLabel}</span>
|
||||||
</div>
|
</div>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
<div
|
<Panel
|
||||||
className={classNames(this.getCollapsibleClassSet()) + ' ascribe-settings-property'}
|
collapsible
|
||||||
ref="panel">
|
expanded={this.state.show}
|
||||||
|
className="bs-custom-panel">
|
||||||
|
<div className="ascribe-settings-property">
|
||||||
{this.renderChildren()}
|
{this.renderChildren()}
|
||||||
</div>
|
</div>
|
||||||
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
|
import { escapeHTML } from '../../utils/general_utils';
|
||||||
|
|
||||||
import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
|
import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
|
||||||
import Panel from 'react-bootstrap/lib/Panel';
|
import Panel from 'react-bootstrap/lib/Panel';
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
@ -86,7 +88,29 @@ let Audio = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
let Video = React.createClass({
|
let Video = React.createClass({
|
||||||
|
/**
|
||||||
|
* The solution here is a bit convoluted.
|
||||||
|
* ReactJS is responsible for DOM manipulation but VideoJS updates the DOM
|
||||||
|
* to install itself to display the video, therefore ReactJS complains that we are
|
||||||
|
* changing the DOM under its feet.
|
||||||
|
*
|
||||||
|
* What we do is the following:
|
||||||
|
* 1) set `state.ready = false`
|
||||||
|
* 2) render the cover using the `<Image />` component (because ready is false)
|
||||||
|
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
|
||||||
|
* the `InjectInHeadMixin`, attaching a function to `Promise.then` to change
|
||||||
|
* `state.ready` to true
|
||||||
|
* 4) when the promise is succesfully resolved, we change `state.ready` triggering
|
||||||
|
* a re-render
|
||||||
|
* 5) the new render calls `prepareVideoHTML` to get the raw HTML of the video tag
|
||||||
|
* (that will be later processed and expanded by VideoJS)
|
||||||
|
* 6) `componentDidUpdate` is called after `render`, setting `state.videoMounted` to true,
|
||||||
|
* to avoid re-installing the VideoJS library
|
||||||
|
* 7) to close the lifecycle, `componentWillUnmount` is called removing VideoJS from the DOM.
|
||||||
|
*/
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
preview: React.PropTypes.string.isRequired,
|
preview: React.PropTypes.string.isRequired,
|
||||||
url: React.PropTypes.string.isRequired,
|
url: React.PropTypes.string.isRequired,
|
||||||
@ -97,7 +121,7 @@ let Video = React.createClass({
|
|||||||
mixins: [InjectInHeadMixin],
|
mixins: [InjectInHeadMixin],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return { ready: false };
|
return { ready: false, videoMounted: false };
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -108,24 +132,40 @@ let Video = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.state.ready && !this.state.videojs) {
|
if (this.state.ready && !this.state.videoMounted) {
|
||||||
window.videojs(React.findDOMNode(this.refs.video));
|
window.videojs('#mainvideo');
|
||||||
|
/* eslint-disable */
|
||||||
|
this.setState({videoMounted: true});
|
||||||
|
/* eslint-enable*/
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.videojs('#mainvideo').dispose();
|
||||||
|
},
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
this.setState({ready: true, videojs: false});
|
this.setState({ready: true, videoMounted: false});
|
||||||
|
},
|
||||||
|
|
||||||
|
prepareVideoHTML() {
|
||||||
|
let sources = this.props.extraData.map((data) => '<source type="video/' + data.type + '" src="' + escapeHTML(data.url) + '" />');
|
||||||
|
let html = [
|
||||||
|
'<video id="mainvideo" class="video-js vjs-default-skin" poster="' + escapeHTML(this.props.preview) + '"',
|
||||||
|
'controls preload="none" width="auto" height="auto">',
|
||||||
|
sources.join('\n'),
|
||||||
|
'</video>'];
|
||||||
|
return html.join('\n');
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
return nextState.videoMounted === false;
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.ready) {
|
if (this.state.ready) {
|
||||||
return (
|
return (
|
||||||
<video ref="video" className="video-js vjs-default-skin" poster={this.props.preview}
|
<div dangerouslySetInnerHTML={{__html: this.prepareVideoHTML() }}/>
|
||||||
controls preload="none" width="auto" height="auto">
|
|
||||||
{this.props.extraData.map((data, i) =>
|
|
||||||
<source key={i} type={'video/' + data.type} src={data.url} />
|
|
||||||
)}
|
|
||||||
</video>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
@ -7,9 +7,13 @@ import PasswordResetRequestForm from '../ascribe_forms/form_password_reset_reque
|
|||||||
|
|
||||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
import { getLangText } from '../../utils/lang_utils.js'
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
let PasswordResetRequestModal = React.createClass({
|
let PasswordResetRequestModal = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
button: React.PropTypes.element
|
||||||
|
},
|
||||||
|
|
||||||
handleResetSuccess(){
|
handleResetSuccess(){
|
||||||
let notificationText = getLangText('Request successfully sent, check your email');
|
let notificationText = getLangText('Request successfully sent, check your email');
|
||||||
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
|
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
|
||||||
@ -18,10 +22,9 @@ let PasswordResetRequestModal = React.createClass({
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
button={this.props.button}
|
trigger={this.props.button}
|
||||||
title={getLangText('Reset your password')}
|
title={getLangText('Reset your password')}
|
||||||
handleSuccess={this.handleResetSuccess}
|
handleSuccess={this.handleResetSuccess}>
|
||||||
tooltip={getLangText('Reset your password')}>
|
|
||||||
<PasswordResetRequestForm />
|
<PasswordResetRequestForm />
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
);
|
);
|
||||||
|
@ -4,92 +4,74 @@ import React from 'react';
|
|||||||
import ReactAddons from 'react/addons';
|
import ReactAddons from 'react/addons';
|
||||||
|
|
||||||
import Modal from 'react-bootstrap/lib/Modal';
|
import Modal from 'react-bootstrap/lib/Modal';
|
||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
|
||||||
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
|
|
||||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
|
||||||
|
|
||||||
import ModalMixin from '../../mixins/modal_mixin';
|
|
||||||
|
|
||||||
let ModalWrapper = React.createClass({
|
let ModalWrapper = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
title: React.PropTypes.string.isRequired,
|
trigger: React.PropTypes.element.isRequired,
|
||||||
onRequestHide: React.PropTypes.func,
|
title: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element,
|
||||||
|
React.PropTypes.string
|
||||||
|
]).isRequired,
|
||||||
handleSuccess: React.PropTypes.func.isRequired,
|
handleSuccess: React.PropTypes.func.isRequired,
|
||||||
button: React.PropTypes.object.isRequired,
|
children: React.PropTypes.oneOfType([
|
||||||
children: React.PropTypes.object,
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
tooltip: React.PropTypes.string
|
React.PropTypes.element
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
getModalTrigger() {
|
getInitialState() {
|
||||||
return (
|
return {
|
||||||
<ModalTrigger modal={
|
showModal: false
|
||||||
<ModalBody
|
};
|
||||||
title={this.props.title}
|
|
||||||
handleSuccess={this.props.handleSuccess}>
|
|
||||||
{this.props.children}
|
|
||||||
</ModalBody>
|
|
||||||
}>
|
|
||||||
{this.props.button}
|
|
||||||
</ModalTrigger>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
show() {
|
||||||
if(this.props.tooltip) {
|
this.setState({
|
||||||
return (
|
showModal: true
|
||||||
<OverlayTrigger
|
});
|
||||||
delay={500}
|
|
||||||
placement="left"
|
|
||||||
overlay={<Tooltip>{this.props.tooltip}</Tooltip>}>
|
|
||||||
{this.getModalTrigger()}
|
|
||||||
</OverlayTrigger>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{/* This needs to be some kind of inline-block */}
|
|
||||||
{this.getModalTrigger()}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let ModalBody = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
onRequestHide: React.PropTypes.func,
|
|
||||||
handleSuccess: React.PropTypes.func,
|
|
||||||
children: React.PropTypes.object,
|
|
||||||
title: React.PropTypes.string.isRequired
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [ModalMixin],
|
hide() {
|
||||||
|
this.setState({
|
||||||
|
showModal: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
handleSuccess(response){
|
handleSuccess(response){
|
||||||
this.props.handleSuccess(response);
|
this.props.handleSuccess(response);
|
||||||
this.props.onRequestHide();
|
this.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
renderChildren() {
|
renderChildren() {
|
||||||
return ReactAddons.Children.map(this.props.children, (child) => {
|
return ReactAddons.Children.map(this.props.children, (child) => {
|
||||||
return ReactAddons.addons.cloneWithProps(child, {
|
return ReactAddons.addons.cloneWithProps(child, {
|
||||||
onRequestHide: this.props.onRequestHide,
|
|
||||||
handleSuccess: this.handleSuccess
|
handleSuccess: this.handleSuccess
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// this adds the onClick method show of modal_wrapper to the trigger component
|
||||||
|
// which is in most cases a button.
|
||||||
|
let trigger = React.cloneElement(this.props.trigger, {onClick: this.show});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal {...this.props} title={this.props.title}>
|
<span>
|
||||||
<div className="modal-body">
|
{trigger}
|
||||||
{this.renderChildren()}
|
<Modal show={this.state.showModal} onHide={this.hide}>
|
||||||
</div>
|
<Modal.Header closeButton>
|
||||||
</Modal>
|
<Modal.Title>
|
||||||
|
{this.props.title}
|
||||||
|
</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<div className="modal-body">
|
||||||
|
{this.renderChildren()}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default ModalWrapper;
|
export default ModalWrapper;
|
||||||
|
61
js/components/ascribe_panel/action_panel.js
Normal file
61
js/components/ascribe_panel/action_panel.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
let ActionPanel = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
title: React.PropTypes.string,
|
||||||
|
content: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.element
|
||||||
|
]),
|
||||||
|
buttons: React.PropTypes.element,
|
||||||
|
onClick: React.PropTypes.func,
|
||||||
|
ignoreFocus: React.PropTypes.bool
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
isFocused: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFocus() {
|
||||||
|
// if ignoreFocus (bool) is defined, then just ignore focusing on
|
||||||
|
// the property and input
|
||||||
|
if(this.props.ignoreFocus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if onClick is defined from the outside,
|
||||||
|
// just call it
|
||||||
|
if(this.props.onClick) {
|
||||||
|
this.props.onClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refs.input.getDOMNode().focus();
|
||||||
|
this.setState({
|
||||||
|
isFocused: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={classnames('ascribe-panel-wrapper', {'is-focused': this.state.isFocused})}>
|
||||||
|
<div className="ascribe-panel-table">
|
||||||
|
<div className="ascribe-panel-content">
|
||||||
|
{this.props.content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-table">
|
||||||
|
<div className="ascribe-panel-content">
|
||||||
|
{this.props.buttons}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ActionPanel;
|
@ -76,11 +76,12 @@ let PieceListBulkModal = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess() {
|
handleSuccess() {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc);
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
|
|
||||||
this.fetchSelectedPieceEditionList()
|
this.fetchSelectedPieceEditionList()
|
||||||
.forEach((pieceId) => {
|
.forEach((pieceId) => {
|
||||||
EditionListActions.refreshEditionList(pieceId);
|
EditionListActions.refreshEditionList({pieceId, filterBy: {}});
|
||||||
});
|
});
|
||||||
EditionListActions.clearAllEditionSelections();
|
EditionListActions.clearAllEditionSelections();
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getLangText } from '../../utils/lang_utils.js'
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
let PieceListBulkModalSelectedEditionsWidget = React.createClass({
|
let PieceListBulkModalSelectedEditionsWidget = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import PieceListToolbarFilterWidget from './piece_list_toolbar_filter_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';
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
@ -11,6 +13,8 @@ let PieceListToolbar = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
className: React.PropTypes.string,
|
className: React.PropTypes.string,
|
||||||
searchFor: React.PropTypes.func,
|
searchFor: React.PropTypes.func,
|
||||||
|
filterBy: React.PropTypes.object,
|
||||||
|
applyFilterBy: 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
|
||||||
@ -41,6 +45,15 @@ let PieceListToolbar = React.createClass({
|
|||||||
onChange={this.searchFor}
|
onChange={this.searchFor}
|
||||||
addonAfter={searchIcon} />
|
addonAfter={searchIcon} />
|
||||||
</span>
|
</span>
|
||||||
|
<span className="pull-right">
|
||||||
|
<PieceListToolbarFilterWidget
|
||||||
|
filterParams={['acl_transfer', 'acl_consign', {
|
||||||
|
key: 'acl_create_editions',
|
||||||
|
label: 'create editions'
|
||||||
|
}]}
|
||||||
|
filterBy={this.props.filterBy}
|
||||||
|
applyFilterBy={this.props.applyFilterBy}/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,30 +2,114 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|
||||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||||
import { getLangText } from '../../utils/lang_utils.js'
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
let PieceListToolbarFilterWidgetFilter = React.createClass({
|
let PieceListToolbarFilterWidgetFilter = 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>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
filterParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired,
|
||||||
|
filterBy: React.PropTypes.object,
|
||||||
|
applyFilterBy: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
generateFilterByStatement(param) {
|
||||||
|
let filterBy = this.props.filterBy;
|
||||||
|
|
||||||
|
if(filterBy) {
|
||||||
|
// we need hasOwnProperty since the values are all booleans
|
||||||
|
if(filterBy.hasOwnProperty(param)) {
|
||||||
|
filterBy[param] = !filterBy[param];
|
||||||
|
|
||||||
|
// if the parameter is false, then we want to remove it again
|
||||||
|
// from the list of queryParameters as this component is only about
|
||||||
|
// which actions *CAN* be done and not what *CANNOT*
|
||||||
|
if(!filterBy[param]) {
|
||||||
|
delete filterBy[param];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
filterBy[param] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterBy;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need overloading here to find the correct parameter of the label
|
||||||
|
* the user is clicking on.
|
||||||
|
*/
|
||||||
|
filterBy(param) {
|
||||||
|
return () => {
|
||||||
|
let filterBy = this.generateFilterByStatement(param);
|
||||||
|
this.props.applyFilterBy(filterBy);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
isFilterActive() {
|
||||||
|
let trueValuesOnly = Object.keys(this.props.filterBy).filter((acl) => acl);
|
||||||
|
|
||||||
|
// We're hiding the star in that complicated matter so that,
|
||||||
|
// the surrounding button is not resized up on appearance
|
||||||
|
if(trueValuesOnly.length > 0) {
|
||||||
|
return { visibility: 'visible'};
|
||||||
|
} else {
|
||||||
|
return { visibility: 'hidden' };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let filterIcon = <Glyphicon glyph='filter' className="filter-glyph"/>;
|
let filterIcon = (
|
||||||
|
<span>
|
||||||
|
<span className="glyphicon glyphicon-filter" aria-hidden="true"></span>
|
||||||
|
<span style={this.isFilterActive()}>*</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownButton title={filterIcon}>
|
<DropdownButton
|
||||||
|
title={filterIcon}
|
||||||
|
className="ascribe-piece-list-toolbar-filter-widget">
|
||||||
<li style={{'textAlign': 'center'}}>
|
<li style={{'textAlign': 'center'}}>
|
||||||
<em>{getLangText('Show Pieces that')}:</em>
|
<em>{getLangText('Show works I can')}:</em>
|
||||||
</li>
|
</li>
|
||||||
<MenuItem eventKey='1'>
|
{this.props.filterParams.map((param, i) => {
|
||||||
<div className="checkbox">
|
let label;
|
||||||
{getLangText('I can transfer')} <input type="checkbox" />
|
|
||||||
</div>
|
if(typeof param !== 'string') {
|
||||||
</MenuItem>
|
label = param.label;
|
||||||
<MenuItem eventKey='2'>
|
param = param.key;
|
||||||
<div className="checkbox">
|
} else {
|
||||||
{getLangText('I can consign')} <input type="checkbox" />
|
param = param;
|
||||||
</div>
|
label = param.split('_')[1];
|
||||||
</MenuItem>
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={i}
|
||||||
|
onClick={this.filterBy(param)}
|
||||||
|
className="filter-widget-item">
|
||||||
|
<div className="checkbox-line">
|
||||||
|
<span>
|
||||||
|
{getLangText(label)}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
readOnly
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.props.filterBy[param]} />
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
82
js/components/ascribe_prizes_dashboard/prizes_dashboard.js
Normal file
82
js/components/ascribe_prizes_dashboard/prizes_dashboard.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import PrizeListActions from '../../actions/prize_list_actions';
|
||||||
|
import PrizeListStore from '../../stores/prize_list_store';
|
||||||
|
|
||||||
|
import Table from '../ascribe_table/table';
|
||||||
|
import TableItem from '../ascribe_table/table_item';
|
||||||
|
import TableItemText from '../ascribe_table/table_item_text';
|
||||||
|
|
||||||
|
import { ColumnModel} from '../ascribe_table/models/table_models';
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
let PrizesDashboard = React.createClass({
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return PrizeListStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PrizeListStore.listen(this.onChange);
|
||||||
|
PrizeListActions.fetchPrizeList();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
PrizeListStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumnList() {
|
||||||
|
return [
|
||||||
|
new ColumnModel(
|
||||||
|
(item) => {
|
||||||
|
return {
|
||||||
|
'content': item.name
|
||||||
|
}; },
|
||||||
|
'name',
|
||||||
|
getLangText('Name'),
|
||||||
|
TableItemText,
|
||||||
|
6,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
new ColumnModel(
|
||||||
|
(item) => {
|
||||||
|
return {
|
||||||
|
'content': item.domain
|
||||||
|
}; },
|
||||||
|
'domain',
|
||||||
|
getLangText('Domain'),
|
||||||
|
TableItemText,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
responsive
|
||||||
|
className="ascribe-table"
|
||||||
|
columnList={this.getColumnList()}
|
||||||
|
itemList={this.state.prizeList}>
|
||||||
|
{this.state.prizeList.map((item, i) => {
|
||||||
|
return (
|
||||||
|
<TableItem
|
||||||
|
className="ascribe-table-item-selectable"
|
||||||
|
key={i}/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PrizesDashboard;
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import TableColumnMixin from '../../mixins/table_column_mixin';
|
|
||||||
import TableHeaderItem from './table_header_item';
|
import TableHeaderItem from './table_header_item';
|
||||||
|
|
||||||
import { ColumnModel } from './models/table_models';
|
import { ColumnModel } from './models/table_models';
|
||||||
@ -17,15 +16,12 @@ let TableHeader = React.createClass({
|
|||||||
orderBy: React.PropTypes.string
|
orderBy: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [TableColumnMixin],
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{this.props.columnList.map((column, i) => {
|
{this.props.columnList.map((column, i) => {
|
||||||
|
|
||||||
let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12);
|
|
||||||
let columnName = column.columnName;
|
let columnName = column.columnName;
|
||||||
let canBeOrdered = column.canBeOrdered;
|
let canBeOrdered = column.canBeOrdered;
|
||||||
|
|
||||||
@ -33,7 +29,6 @@ let TableHeader = React.createClass({
|
|||||||
<TableHeaderItem
|
<TableHeaderItem
|
||||||
className={column.className}
|
className={column.className}
|
||||||
key={i}
|
key={i}
|
||||||
columnClasses={columnClasses}
|
|
||||||
displayName={column.displayName}
|
displayName={column.displayName}
|
||||||
columnName={columnName}
|
columnName={columnName}
|
||||||
canBeOrdered={canBeOrdered}
|
canBeOrdered={canBeOrdered}
|
||||||
|
@ -7,7 +7,6 @@ import TableHeaderItemCarret from './table_header_item_carret';
|
|||||||
let TableHeaderItem = React.createClass({
|
let TableHeaderItem = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
columnClasses: React.PropTypes.string.isRequired,
|
|
||||||
displayName: React.PropTypes.oneOfType([
|
displayName: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.element
|
React.PropTypes.element
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { ColumnModel } from './models/table_models';
|
|
||||||
|
|
||||||
import EditionListStore from '../../stores/edition_list_store';
|
|
||||||
import EditionListActions from '../../actions/edition_list_actions';
|
|
||||||
|
|
||||||
|
|
||||||
import Table from './table';
|
|
||||||
import TableItemWrapper from './table_item_wrapper';
|
|
||||||
import TableItemText from './table_item_text';
|
|
||||||
import TableItemAcl from './table_item_acl';
|
|
||||||
import TableItemSelectable from './table_item_selectable';
|
|
||||||
import TableItemSubtableButton from './table_item_subtable_button';
|
|
||||||
|
|
||||||
|
|
||||||
let TableItemSubtable = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
|
|
||||||
columnContent: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
'open': false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
EditionListStore.listen(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
EditionListStore.unlisten(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(state) {
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadEditionList() {
|
|
||||||
if(this.state.open) {
|
|
||||||
this.setState({
|
|
||||||
'open': false
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
EditionListActions.fetchEditionList(this.props.columnContent.id);
|
|
||||||
this.setState({
|
|
||||||
'open': true,
|
|
||||||
'editionList': EditionListStore.getState()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
selectItem(parentId, itemId) {
|
|
||||||
EditionListActions.selectEdition({
|
|
||||||
'pieceId': parentId,
|
|
||||||
'editionId': itemId
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
let renderEditionListTable = () => {
|
|
||||||
|
|
||||||
let columnList = [
|
|
||||||
new ColumnModel('edition_number', 'Number', TableItemText, 2, false),
|
|
||||||
new ColumnModel('user_registered', 'User', TableItemText, 4, true),
|
|
||||||
new ColumnModel('acl', 'Actions', TableItemAcl, 4, true)
|
|
||||||
];
|
|
||||||
|
|
||||||
if(this.state.open && this.state.editionList[this.props.columnContent.id] && this.state.editionList[this.props.columnContent.id].length) {
|
|
||||||
return (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
|
||||||
<Table itemList={this.state.editionList[this.props.columnContent.id]} columnList={columnList}>
|
|
||||||
{this.state.editionList[this.props.columnContent.id].map((edition, i) => {
|
|
||||||
return (
|
|
||||||
<TableItemSelectable
|
|
||||||
className="ascribe-table-item-selectable"
|
|
||||||
selectItem={this.selectItem}
|
|
||||||
parentId={this.props.columnContent.id}
|
|
||||||
key={i} />
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 ascribe-table-item">
|
|
||||||
<div className="row">
|
|
||||||
<TableItemWrapper
|
|
||||||
columnList={this.props.columnList}
|
|
||||||
columnContent={this.props.columnContent}
|
|
||||||
columnWidth={12} />
|
|
||||||
<div className="col-xs-1 col-sm-1 col-md-1 col-lg-1 ascribe-table-item-column">
|
|
||||||
<TableItemSubtableButton content="+" onClick={this.loadEditionList} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{renderEditionListTable()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default TableItemSubtable;
|
|
@ -1,23 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
let TableItemSubtableButton = React.createClass({
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
content: React.PropTypes.string.isRequired,
|
|
||||||
onClick: React.PropTypes.func.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<button type="button" className="btn btn-default btn-sm ascribe-table-expand-button" onClick={this.props.onClick}>
|
|
||||||
{this.props.content}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default TableItemSubtableButton;
|
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import React from 'react/addons';
|
import React from 'react/addons';
|
||||||
import Router from 'react-router';
|
import Router from 'react-router';
|
||||||
import Raven from 'raven-js';
|
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
import { getCookie } from '../../utils/fetch_api_utils';
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
@ -171,9 +170,13 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
filesToUpload: [],
|
filesToUpload: [],
|
||||||
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
||||||
csrfToken: getCookie(AppConstants.csrftoken),
|
csrfToken: getCookie(AppConstants.csrftoken),
|
||||||
hashingProgress: -2
|
|
||||||
// -1: aborted
|
// -1: aborted
|
||||||
// -2: uninitialized
|
// -2: uninitialized
|
||||||
|
hashingProgress: -2,
|
||||||
|
|
||||||
|
// this is for logging
|
||||||
|
chunks: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -225,7 +228,9 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
onDeleteComplete: this.onDeleteComplete,
|
onDeleteComplete: this.onDeleteComplete,
|
||||||
onSessionRequestComplete: this.onSessionRequestComplete,
|
onSessionRequestComplete: this.onSessionRequestComplete,
|
||||||
onError: this.onError,
|
onError: this.onError,
|
||||||
onValidate: this.onValidate
|
onValidate: this.onValidate,
|
||||||
|
onUploadChunk: this.onUploadChunk,
|
||||||
|
onUploadChunkSuccess: this.onUploadChunkSuccess
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -257,6 +262,10 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
resolve(res.key);
|
resolve(res.key);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
console.logGlobal(err, false, {
|
||||||
|
files: this.state.filesToUpload,
|
||||||
|
chunks: this.state.chunks
|
||||||
|
});
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -294,15 +303,66 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
resolve(res);
|
resolve(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
console.logGlobal(err, false, {
|
||||||
|
files: this.state.filesToUpload,
|
||||||
|
chunks: this.state.chunks
|
||||||
|
});
|
||||||
reject(err);
|
reject(err);
|
||||||
console.logGlobal(err);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/* FineUploader specific callback function handlers */
|
/* FineUploader specific callback function handlers */
|
||||||
|
|
||||||
onComplete(id) {
|
onUploadChunk(id, name, chunkData) {
|
||||||
|
|
||||||
|
let chunks = this.state.chunks;
|
||||||
|
|
||||||
|
chunks[id + '-' + chunkData.startByte + '-' + chunkData.endByte] = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
chunkData,
|
||||||
|
completed: false
|
||||||
|
};
|
||||||
|
|
||||||
|
let newState = React.addons.update(this.state, {
|
||||||
|
startedChunks: { $set: chunks }
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
onUploadChunkSuccess(id, chunkData, responseJson, xhr) {
|
||||||
|
|
||||||
|
let chunks = this.state.chunks;
|
||||||
|
let chunkKey = id + '-' + chunkData.startByte + '-' + chunkData.endByte;
|
||||||
|
|
||||||
|
if(chunks[chunkKey]) {
|
||||||
|
chunks[chunkKey].completed = true;
|
||||||
|
chunks[chunkKey].responseJson = responseJson;
|
||||||
|
chunks[chunkKey].xhr = xhr;
|
||||||
|
|
||||||
|
let newState = React.addons.update(this.state, {
|
||||||
|
startedChunks: { $set: chunks }
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
onComplete(id, name, res, xhr) {
|
||||||
|
// there has been an issue with the server's connection
|
||||||
|
if(xhr.status === 0) {
|
||||||
|
|
||||||
|
console.logGlobal(new Error('Complete was called but there wasn\t a success'), false, {
|
||||||
|
files: this.state.filesToUpload,
|
||||||
|
chunks: this.state.chunks
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let files = this.state.filesToUpload;
|
let files = this.state.filesToUpload;
|
||||||
|
|
||||||
// Set the state of the completed file to 'upload successful' in order to
|
// Set the state of the completed file to 'upload successful' in order to
|
||||||
@ -342,7 +402,10 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.logGlobal(err);
|
console.logGlobal(err, false, {
|
||||||
|
files: this.state.filesToUpload,
|
||||||
|
chunks: this.state.chunks
|
||||||
|
});
|
||||||
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
});
|
});
|
||||||
@ -351,7 +414,12 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onError(id, name, errorReason) {
|
onError(id, name, errorReason) {
|
||||||
Raven.captureException(errorReason);
|
console.logGlobal(errorReason, false, {
|
||||||
|
files: this.state.filesToUpload,
|
||||||
|
chunks: this.state.chunks
|
||||||
|
});
|
||||||
|
this.state.uploader.cancelAll();
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(this.props.defaultErrorMessage, 'danger', 5000);
|
let notification = new GlobalNotificationModel(this.props.defaultErrorMessage, 'danger', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
@ -9489,6 +9489,12 @@ qq.s3.XhrUploadHandler = function(spec, proxy) {
|
|||||||
result.success,
|
result.success,
|
||||||
|
|
||||||
function failure(reason, xhr) {
|
function failure(reason, xhr) {
|
||||||
|
console.logGlobal(reason + 'in chunked.combine', false, {
|
||||||
|
uploadId,
|
||||||
|
etagMap,
|
||||||
|
result
|
||||||
|
});
|
||||||
|
|
||||||
result.failure(upload.done(id, xhr).response, xhr);
|
result.failure(upload.done(id, xhr).response, xhr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,7 @@ import Form from './ascribe_forms/form';
|
|||||||
import Property from './ascribe_forms/property';
|
import Property from './ascribe_forms/property';
|
||||||
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
|
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
|
||||||
|
|
||||||
import apiUrls from '../constants/api_urls';
|
import ApiUrls from '../constants/api_urls';
|
||||||
import { getLangText } from '../utils/lang_utils';
|
import { getLangText } from '../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ let CoaVerifyForm = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.coa_verify}
|
url={ApiUrls.coa_verify}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
buttons={
|
buttons={
|
||||||
<button
|
<button
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
|
||||||
import Modal from 'react-bootstrap/lib/Modal';
|
|
||||||
import OverlayMixin from 'react-bootstrap/lib/OverlayMixin';
|
|
||||||
import { getLangText } from '../utils/lang_utils.js';
|
|
||||||
|
|
||||||
let LoginModalHandler = React.createClass({
|
|
||||||
mixins: [OverlayMixin],
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
isModalOpen: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
handleToggle() {
|
|
||||||
this.setState({
|
|
||||||
isModalOpen: !this.state.isModalOpen
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if(!this.state.isModalOpen || !(this.props.query.login === '')) {
|
|
||||||
return <span/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal title='Modal heading' onRequestHide={this.handleToggle}>
|
|
||||||
<div className='modal-body'>
|
|
||||||
This modal is controlled by our custom trigger component.
|
|
||||||
</div>
|
|
||||||
<div className='modal-footer'>
|
|
||||||
<Button onClick={this.handleToggle}>{getLangText('Close')}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default LoginModalHandler;
|
|
@ -5,8 +5,7 @@ import Router from 'react-router';
|
|||||||
|
|
||||||
import Form from './ascribe_forms/form';
|
import Form from './ascribe_forms/form';
|
||||||
import Property from './ascribe_forms/property';
|
import Property from './ascribe_forms/property';
|
||||||
import FormPropertyHeader from './ascribe_forms/form_property_header';
|
import ApiUrls from '../constants/api_urls';
|
||||||
import apiUrls from '../constants/api_urls';
|
|
||||||
|
|
||||||
import GlobalNotificationModel from '../models/global_notification_model';
|
import GlobalNotificationModel from '../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||||
@ -15,12 +14,15 @@ import { getLangText } from '../utils/lang_utils';
|
|||||||
|
|
||||||
let PasswordResetContainer = React.createClass({
|
let PasswordResetContainer = React.createClass({
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {isRequested: false};
|
return {isRequested: false};
|
||||||
},
|
},
|
||||||
handleRequestSuccess(email){
|
|
||||||
|
handleRequestSuccess(email) {
|
||||||
this.setState({isRequested: email});
|
this.setState({isRequested: email});
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.query.email && this.props.query.token) {
|
if (this.props.query.email && this.props.query.token) {
|
||||||
return (
|
return (
|
||||||
@ -57,17 +59,22 @@ let PasswordResetContainer = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let PasswordRequestResetForm = React.createClass({
|
let PasswordRequestResetForm = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
handleRequestSuccess: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
handleSuccess() {
|
handleSuccess() {
|
||||||
let notificationText = getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.');
|
let notificationText = getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.');
|
||||||
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
|
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
this.props.handleRequestSuccess(this.refs.form.refs.email.state.value);
|
this.props.handleRequestSuccess(this.refs.form.refs.email.state.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
ref="form"
|
ref="form"
|
||||||
url={apiUrls.users_password_reset_request}
|
url={ApiUrls.users_password_reset_request}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
buttons={
|
buttons={
|
||||||
<button
|
<button
|
||||||
@ -80,15 +87,15 @@ let PasswordRequestResetForm = React.createClass({
|
|||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
<FormPropertyHeader>
|
<div className="ascribe-form-header">
|
||||||
<h3>{getLangText('Reset your password')}</h3>
|
<h3>{getLangText('Reset your password')}</h3>
|
||||||
</FormPropertyHeader>
|
</div>
|
||||||
<Property
|
<Property
|
||||||
name='email'
|
name='email'
|
||||||
label={getLangText('Email')}>
|
label={getLangText('Email')}>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
placeholder={getLangText("Enter your email and we'll send a link")}
|
placeholder={getLangText('Enter your email and we\'ll send a link')}
|
||||||
name="email"
|
name="email"
|
||||||
required/>
|
required/>
|
||||||
</Property>
|
</Property>
|
||||||
@ -99,24 +106,31 @@ let PasswordRequestResetForm = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let PasswordResetForm = React.createClass({
|
let PasswordResetForm = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
email: React.PropTypes.string,
|
||||||
|
token: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getFormData(){
|
getFormData() {
|
||||||
return {
|
return {
|
||||||
email: this.props.email,
|
email: this.props.email,
|
||||||
token: this.props.token
|
token: this.props.token
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess() {
|
handleSuccess() {
|
||||||
this.transitionTo('pieces');
|
this.transitionTo('pieces');
|
||||||
let notification = new GlobalNotificationModel(getLangText('password successfully updated'), 'success', 10000);
|
let notification = new GlobalNotificationModel(getLangText('password successfully updated'), 'success', 10000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
ref="form"
|
ref="form"
|
||||||
url={apiUrls.users_password_reset}
|
url={ApiUrls.users_password_reset}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
getFormData={this.getFormData}
|
getFormData={this.getFormData}
|
||||||
buttons={
|
buttons={
|
||||||
@ -130,9 +144,9 @@ let PasswordResetForm = React.createClass({
|
|||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
<FormPropertyHeader>
|
<div className="ascribe-form-header">
|
||||||
<h3>{getLangText('Reset the password for')} {this.props.email}</h3>
|
<h3>{getLangText('Reset the password for')} {this.props.email}</h3>
|
||||||
</FormPropertyHeader>
|
</div>
|
||||||
<Property
|
<Property
|
||||||
name='password'
|
name='password'
|
||||||
label={getLangText('Password')}>
|
label={getLangText('Password')}>
|
||||||
|
@ -6,6 +6,9 @@ import Router from 'react-router';
|
|||||||
import PieceListStore from '../stores/piece_list_store';
|
import PieceListStore from '../stores/piece_list_store';
|
||||||
import PieceListActions from '../actions/piece_list_actions';
|
import PieceListActions from '../actions/piece_list_actions';
|
||||||
|
|
||||||
|
import EditionListStore from '../stores/edition_list_store';
|
||||||
|
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 AccordionListItem from './ascribe_accordion_list/accordion_list_item';
|
||||||
import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_list_item_table_editions';
|
import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_list_item_table_editions';
|
||||||
@ -17,6 +20,7 @@ import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
|
|||||||
|
|
||||||
import AppConstants from '../constants/application_constants';
|
import AppConstants from '../constants/application_constants';
|
||||||
|
|
||||||
|
import { mergeOptions } from '../utils/general_utils';
|
||||||
|
|
||||||
let PieceList = React.createClass({
|
let PieceList = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -27,15 +31,22 @@ let PieceList = React.createClass({
|
|||||||
mixins: [Router.Navigation, Router.State],
|
mixins: [Router.Navigation, Router.State],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return PieceListStore.getState();
|
return mergeOptions(
|
||||||
|
PieceListStore.getState(),
|
||||||
|
EditionListStore.getState()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let page = this.getQuery().page || 1;
|
let page = this.getQuery().page || 1;
|
||||||
|
|
||||||
PieceListStore.listen(this.onChange);
|
PieceListStore.listen(this.onChange);
|
||||||
|
EditionListStore.listen(this.onChange);
|
||||||
|
|
||||||
if (this.state.pieceList.length === 0){
|
if (this.state.pieceList.length === 0){
|
||||||
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc)
|
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
||||||
.then(PieceListActions.fetchPieceRequestActions());
|
this.state.orderBy, this.state.orderAsc, this.state.filterBy)
|
||||||
|
.then(() => PieceListActions.fetchPieceRequestActions());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -48,6 +59,7 @@ let PieceList = React.createClass({
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
PieceListStore.unlisten(this.onChange);
|
PieceListStore.unlisten(this.onChange);
|
||||||
|
EditionListStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
@ -55,13 +67,14 @@ let PieceList = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
paginationGoToPage(page) {
|
paginationGoToPage(page) {
|
||||||
// if the users clicks a pager of the pagination,
|
return () => {
|
||||||
// the site should go to the top
|
// if the users clicks a pager of the pagination,
|
||||||
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
// the site should go to the top
|
||||||
|
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
||||||
return () => PieceListActions.fetchPieceList(page, this.state.pageSize,
|
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
||||||
this.state.search, this.state.orderBy,
|
this.state.orderBy, this.state.orderAsc,
|
||||||
this.state.orderAsc);
|
this.state.filterBy);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getPagination() {
|
getPagination() {
|
||||||
@ -79,23 +92,49 @@ let PieceList = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
searchFor(searchTerm) {
|
searchFor(searchTerm) {
|
||||||
PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc);
|
PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy,
|
||||||
|
this.state.orderAsc, this.state.filterBy);
|
||||||
this.transitionTo(this.getPathname(), {page: 1});
|
this.transitionTo(this.getPathname(), {page: 1});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
applyFilterBy(filterBy) {
|
||||||
|
// first we need to apply the filter on the piece list
|
||||||
|
PieceListActions.fetchPieceList(1, this.state.pageSize, this.state.search,
|
||||||
|
this.state.orderBy, this.state.orderAsc, filterBy)
|
||||||
|
.then(() => {
|
||||||
|
// but also, we need to filter all the open edition lists
|
||||||
|
this.state.pieceList
|
||||||
|
.forEach((piece) => {
|
||||||
|
// but only if they're actually open
|
||||||
|
if(this.state.isEditionListOpenForPieceId[piece.id].show) {
|
||||||
|
EditionListActions.refreshEditionList({
|
||||||
|
pieceId: piece.id,
|
||||||
|
filterBy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// we have to redirect the user always to page one as it could be that there is no page two
|
||||||
|
// for filtered pieces
|
||||||
|
this.transitionTo(this.getPathname(), {page: 1});
|
||||||
|
},
|
||||||
|
|
||||||
accordionChangeOrder(orderBy, orderAsc) {
|
accordionChangeOrder(orderBy, orderAsc) {
|
||||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize,
|
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||||
this.state.search, orderBy, orderAsc);
|
orderBy, 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'} />);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PieceListToolbar
|
<PieceListToolbar
|
||||||
className="ascribe-piece-list-toolbar"
|
className="ascribe-piece-list-toolbar"
|
||||||
searchFor={this.searchFor}>
|
searchFor={this.searchFor}
|
||||||
|
filterBy={this.state.filterBy}
|
||||||
|
applyFilterBy={this.applyFilterBy}>
|
||||||
{this.props.customSubmitButton}
|
{this.props.customSubmitButton}
|
||||||
</PieceListToolbar>
|
</PieceListToolbar>
|
||||||
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
|
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
/**
|
|
||||||
* This component is essentially a port of https://github.com/simplefocus/FlowType.JS
|
|
||||||
* to Reactjs in order to not being forced to use jQuery
|
|
||||||
*
|
|
||||||
* Author: Tim Daubenschütz
|
|
||||||
*
|
|
||||||
* Thanks to the guys at Simple Focus http://simplefocus.com/
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ReactAddons from 'react/addons';
|
|
||||||
|
|
||||||
let FlowType = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
|
|
||||||
// standard FlowTypes.JS options
|
|
||||||
maximum: React.PropTypes.number,
|
|
||||||
minimum: React.PropTypes.number,
|
|
||||||
maxFont: React.PropTypes.number,
|
|
||||||
minFont: React.PropTypes.number,
|
|
||||||
fontRatio: React.PropTypes.number,
|
|
||||||
|
|
||||||
// react specific options
|
|
||||||
children: React.PropTypes.element.isRequired // only supporting one child element at once right now
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
maximum: 9999,
|
|
||||||
minimum: 1,
|
|
||||||
maxFont: 9999,
|
|
||||||
minFont: 1,
|
|
||||||
fontRatio: 35
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
// 32 because that's the default font display size
|
|
||||||
// doesn't really matter though
|
|
||||||
fontSize: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// Make changes upon resize, calculate changes and rerender
|
|
||||||
this.handleResize();
|
|
||||||
window.addEventListener('resize', this.handleResize);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
// stop listening to window once the component was unmounted
|
|
||||||
window.removeEventListener('resize', this.handleResize);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleResize() {
|
|
||||||
let elemWidth = this.refs.flowTypeElement.getDOMNode().offsetWidth;
|
|
||||||
let width = elemWidth > this.props.maximum ? this.props.maximum : elemWidth < this.props.minimum ? this.props.minimum : elemWidth;
|
|
||||||
let fontBase = width / this.props.fontRatio;
|
|
||||||
let fontSize = fontBase > this.props.maxFont ? this.props.maxFont : fontBase < this.props.minFont ? this.props.minFont : fontBase;
|
|
||||||
|
|
||||||
this.setState({ fontSize });
|
|
||||||
},
|
|
||||||
|
|
||||||
// The child the user passes to this component needs to have it's
|
|
||||||
// style.fontSize property to be updated
|
|
||||||
renderChildren() {
|
|
||||||
return ReactAddons.Children.map(this.props.children, (child) => {
|
|
||||||
return ReactAddons.addons.cloneWithProps(child, {
|
|
||||||
ref: 'flowTypeFontElement',
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref="flowTypeElement"
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
fontSize: this.state.fontSize
|
|
||||||
}}>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FlowType;
|
|
@ -23,7 +23,6 @@ import GlobalNotificationActions from '../actions/global_notification_actions';
|
|||||||
import Property from './ascribe_forms/property';
|
import Property from './ascribe_forms/property';
|
||||||
import PropertyCollapsible from './ascribe_forms/property_collapsible';
|
import PropertyCollapsible from './ascribe_forms/property_collapsible';
|
||||||
import RegisterPieceForm from './ascribe_forms/form_register_piece';
|
import RegisterPieceForm from './ascribe_forms/form_register_piece';
|
||||||
//import FormPropertyHeader from './ascribe_forms/form_property_header';
|
|
||||||
|
|
||||||
import LoginContainer from './login_container';
|
import LoginContainer from './login_container';
|
||||||
import SlidesContainer from './ascribe_slides_container/slides_container';
|
import SlidesContainer from './ascribe_slides_container/slides_container';
|
||||||
@ -40,7 +39,9 @@ let RegisterPiece = React.createClass( {
|
|||||||
submitMessage: React.PropTypes.string,
|
submitMessage: React.PropTypes.string,
|
||||||
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,
|
||||||
|
React.PropTypes.string
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
@ -101,7 +102,8 @@ let RegisterPiece = React.createClass( {
|
|||||||
this.state.pageSize,
|
this.state.pageSize,
|
||||||
this.state.searchTerm,
|
this.state.searchTerm,
|
||||||
this.state.orderBy,
|
this.state.orderBy,
|
||||||
this.state.orderAsc
|
this.state.orderAsc,
|
||||||
|
this.state.filterBy
|
||||||
);
|
);
|
||||||
|
|
||||||
this.transitionTo('piece', {pieceId: response.piece.id});
|
this.transitionTo('piece', {pieceId: response.piece.id});
|
||||||
@ -139,7 +141,7 @@ let RegisterPiece = React.createClass( {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getSpecifyEditions() {
|
getSpecifyEditions() {
|
||||||
if(this.state.whitelabel && this.state.whitelabel.acl_editions || Object.keys(this.state.whitelabel).length === 0) {
|
if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) {
|
||||||
return (
|
return (
|
||||||
<PropertyCollapsible
|
<PropertyCollapsible
|
||||||
name="num_editions"
|
name="num_editions"
|
||||||
|
@ -22,19 +22,28 @@ import Form from './ascribe_forms/form';
|
|||||||
import Property from './ascribe_forms/property';
|
import Property from './ascribe_forms/property';
|
||||||
import InputCheckbox from './ascribe_forms/input_checkbox';
|
import InputCheckbox from './ascribe_forms/input_checkbox';
|
||||||
|
|
||||||
import apiUrls from '../constants/api_urls';
|
import ActionPanel from './ascribe_panel/action_panel';
|
||||||
import AppConstants from '../constants/application_constants';
|
|
||||||
import { getLangText } from '../utils/lang_utils';
|
|
||||||
|
|
||||||
|
import ApiUrls from '../constants/api_urls';
|
||||||
|
import AppConstants from '../constants/application_constants';
|
||||||
|
|
||||||
|
import { getLangText } from '../utils/lang_utils';
|
||||||
import { getCookie } from '../utils/fetch_api_utils';
|
import { getCookie } from '../utils/fetch_api_utils';
|
||||||
|
|
||||||
let SettingsContainer = React.createClass({
|
let SettingsContainer = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
children: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||||
|
React.PropTypes.element])
|
||||||
|
},
|
||||||
|
|
||||||
mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="settings-container">
|
<div className="settings-container">
|
||||||
<AccountSettings />
|
<AccountSettings />
|
||||||
|
{this.props.children}
|
||||||
<APISettings />
|
<APISettings />
|
||||||
<BitcoinWalletSettings />
|
<BitcoinWalletSettings />
|
||||||
<LoanContractSettings />
|
<LoanContractSettings />
|
||||||
@ -81,7 +90,7 @@ let AccountSettings = React.createClass({
|
|||||||
if (this.state.currentUser.username) {
|
if (this.state.currentUser.username) {
|
||||||
content = (
|
content = (
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.users_username}
|
url={ApiUrls.users_username}
|
||||||
handleSuccess={this.handleSuccess}>
|
handleSuccess={this.handleSuccess}>
|
||||||
<Property
|
<Property
|
||||||
name='username'
|
name='username'
|
||||||
@ -107,7 +116,7 @@ let AccountSettings = React.createClass({
|
|||||||
);
|
);
|
||||||
profile = (
|
profile = (
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.users_profile}
|
url={ApiUrls.users_profile}
|
||||||
handleSuccess={this.handleSuccess}
|
handleSuccess={this.handleSuccess}
|
||||||
getFormData={this.getFormDataProfile}>
|
getFormData={this.getFormDataProfile}>
|
||||||
<Property
|
<Property
|
||||||
@ -117,11 +126,11 @@ let AccountSettings = React.createClass({
|
|||||||
<InputCheckbox
|
<InputCheckbox
|
||||||
defaultChecked={this.state.currentUser.profile.hash_locally}>
|
defaultChecked={this.state.currentUser.profile.hash_locally}>
|
||||||
<span>
|
<span>
|
||||||
{' ' + getLangText('Enable hash option for slow connections. ' +
|
{' ' + getLangText('Enable hash option, e.g. slow connections or to keep piece private')}
|
||||||
'Computes and uploads a hash of the work instead.')}
|
|
||||||
</span>
|
</span>
|
||||||
</InputCheckbox>
|
</InputCheckbox>
|
||||||
</Property>
|
</Property>
|
||||||
|
<hr />
|
||||||
{/*<Property
|
{/*<Property
|
||||||
name='language'
|
name='language'
|
||||||
label={getLangText('Choose your Language')}
|
label={getLangText('Choose your Language')}
|
||||||
@ -136,7 +145,6 @@ let AccountSettings = React.createClass({
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</Property>*/}
|
</Property>*/}
|
||||||
<hr />
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -258,14 +266,14 @@ let FileUploader = React.createClass({
|
|||||||
fileClass: 'contract'
|
fileClass: 'contract'
|
||||||
}}
|
}}
|
||||||
createBlobRoutine={{
|
createBlobRoutine={{
|
||||||
url: apiUrls.ownership_loans_contract
|
url: ApiUrls.ownership_loans_contract
|
||||||
}}
|
}}
|
||||||
validation={{
|
validation={{
|
||||||
itemLimit: 100000,
|
itemLimit: 100000,
|
||||||
sizeLimit: '10000000'
|
sizeLimit: '10000000'
|
||||||
}}
|
}}
|
||||||
session={{
|
session={{
|
||||||
endpoint: apiUrls.ownership_loans_contract,
|
endpoint: ApiUrls.ownership_loans_contract,
|
||||||
customHeaders: {
|
customHeaders: {
|
||||||
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||||
},
|
},
|
||||||
@ -333,45 +341,50 @@ let APISettings = React.createClass({
|
|||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
getApplications(){
|
||||||
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||||
if (this.state.applications.length > -1) {
|
if (this.state.applications.length > -1) {
|
||||||
content = this.state.applications.map(function(app, i) {
|
content = this.state.applications.map(function(app, i) {
|
||||||
return (
|
return (
|
||||||
<Property
|
<ActionPanel
|
||||||
name={app.name}
|
name={app.name}
|
||||||
label={app.name}
|
key={i}
|
||||||
key={i}>
|
content={
|
||||||
<div className="row-same-height">
|
<div>
|
||||||
<div className="no-padding col-xs-6 col-sm-10 col-xs-height col-middle">
|
<div className='ascribe-panel-title'>
|
||||||
{'Bearer ' + app.bearer_token.token}
|
{app.name}
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-subtitle">
|
||||||
|
{'Bearer ' + app.bearer_token.token}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xs-6 col-sm-2 col-xs-height">
|
}
|
||||||
<button
|
buttons={
|
||||||
className="pull-right btn btn-default btn-sm"
|
<div className="pull-right">
|
||||||
onClick={this.handleTokenRefresh}
|
<div className="pull-right">
|
||||||
data-id={app.name}>
|
<button
|
||||||
{getLangText('REFRESH')}
|
className="pull-right btn btn-default btn-sm"
|
||||||
</button>
|
onClick={this.handleTokenRefresh}
|
||||||
|
data-id={app.name}>
|
||||||
|
{getLangText('REFRESH')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}/>
|
||||||
</Property>);
|
);
|
||||||
}, this);
|
}, this);
|
||||||
content = (
|
|
||||||
<div>
|
|
||||||
<Form>
|
|
||||||
{content}
|
|
||||||
<hr />
|
|
||||||
</Form>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<CollapsibleParagraph
|
<CollapsibleParagraph
|
||||||
title={getLangText('API Integration')}
|
title={getLangText('API Integration')}
|
||||||
show={true}
|
show={true}
|
||||||
defaultExpanded={this.props.defaultExpanded}>
|
defaultExpanded={this.props.defaultExpanded}>
|
||||||
<Form
|
<Form
|
||||||
url={apiUrls.applications}
|
url={ApiUrls.applications}
|
||||||
handleSuccess={this.handleCreateSuccess}>
|
handleSuccess={this.handleCreateSuccess}>
|
||||||
<Property
|
<Property
|
||||||
name='name'
|
name='name'
|
||||||
@ -386,7 +399,7 @@ let APISettings = React.createClass({
|
|||||||
<pre>
|
<pre>
|
||||||
Usage: curl <url> -H 'Authorization: Bearer <token>'
|
Usage: curl <url> -H 'Authorization: Bearer <token>'
|
||||||
</pre>
|
</pre>
|
||||||
{content}
|
{this.getApplications()}
|
||||||
</CollapsibleParagraph>
|
</CollapsibleParagraph>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
33
js/components/whitelabel/prize/actions/prize_actions.js
Normal file
33
js/components/whitelabel/prize/actions/prize_actions.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
|
import PrizeFetcher from '../fetchers/prize_fetcher';
|
||||||
|
|
||||||
|
class PrizeActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'updatePrize'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchPrize() {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeFetcher
|
||||||
|
.fetch()
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrize({
|
||||||
|
prize: res.prize
|
||||||
|
});
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(PrizeActions);
|
76
js/components/whitelabel/prize/actions/prize_jury_actions.js
Normal file
76
js/components/whitelabel/prize/actions/prize_jury_actions.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
|
import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher';
|
||||||
|
|
||||||
|
class PrizeJuryActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'updatePrizeJury',
|
||||||
|
'removePrizeJury',
|
||||||
|
'activatePrizeJury'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchJury() {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeJuryFetcher
|
||||||
|
.fetch()
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrizeJury(res.members);
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
activateJury(email) {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeJuryFetcher
|
||||||
|
.activate(email)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeJury(email) {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeJuryFetcher
|
||||||
|
.delete(email)
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.removePrizeJury(email);
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resendJuryInvitation(email) {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeJuryFetcher
|
||||||
|
.resend(email)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(PrizeJuryActions);
|
@ -0,0 +1,66 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
|
import PrizeRatingFetcher from '../fetchers/prize_rating_fetcher';
|
||||||
|
|
||||||
|
class PrizeRatingActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'updatePrizeRatings',
|
||||||
|
'updatePrizeRating'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeRatingFetcher
|
||||||
|
.fetch()
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrizeRatings(res.ratings);
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchOne(pieceId) {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeRatingFetcher
|
||||||
|
.fetchOne(pieceId)
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrizeRating(res.rating.rating);
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createRating(pieceId, rating) {
|
||||||
|
return Q.Promise((resolve, reject) => {
|
||||||
|
PrizeRatingFetcher
|
||||||
|
.rate(pieceId, rating)
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updatePrizeRating(res.rating.rating);
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.logGlobal(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRating(rating) {
|
||||||
|
this.actions.updatePrizeRating(rating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(PrizeRatingActions);
|
@ -0,0 +1,54 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import ModalWrapper from '../../../../ascribe_modal/modal_wrapper';
|
||||||
|
import PieceSubmitToPrizeForm from '../../../../ascribe_forms/form_submit_to_prize';
|
||||||
|
|
||||||
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
|
|
||||||
|
let SubmitToPrizeButton = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
className: React.PropTypes.string,
|
||||||
|
handleSuccess: React.PropTypes.func,
|
||||||
|
piece: React.PropTypes.object.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubmitButton() {
|
||||||
|
if (this.props.piece.prize) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
disabled
|
||||||
|
className="btn btn-default btn-xs pull-right">
|
||||||
|
{getLangText('Submitted to prize')} <span className="glyphicon glyphicon-ok"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
|
||||||
|
{getLangText('Submit to prize')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ModalWrapper
|
||||||
|
trigger={this.getSubmitButton()}
|
||||||
|
handleSuccess={this.props.handleSuccess}
|
||||||
|
title={getLangText('Submit to prize')}>
|
||||||
|
<PieceSubmitToPrizeForm
|
||||||
|
piece={this.props.piece}
|
||||||
|
handleSuccess={this.props.handleSuccess}/>
|
||||||
|
</ModalWrapper>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SubmitToPrizeButton;
|
@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
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 PrizeRatingActions from '../../actions/prize_rating_actions';
|
||||||
|
import PrizeRatingStore from '../../stores/prize_rating_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';
|
||||||
@ -70,6 +75,34 @@ let PrizePieceDetails = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
piece: React.PropTypes.object
|
piece: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return PrizeRatingStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PrizeRatingStore.listen(this.onChange);
|
||||||
|
PrizeRatingActions.fetchOne(this.props.piece.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// Every time we're leaving the piece detail page,
|
||||||
|
// just reset the piece that is saved in the piece store
|
||||||
|
// as it will otherwise display wrong/old data once the user loads
|
||||||
|
// the piece detail a second time
|
||||||
|
PrizeRatingActions.updateRating({});
|
||||||
|
PrizeRatingStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRatingClick(event, args) {
|
||||||
|
event.preventDefault();
|
||||||
|
PrizeRatingActions.createRating(this.props.piece.id, args.rating);
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.piece.prize
|
if (this.props.piece.prize
|
||||||
&& this.props.piece.prize.name
|
&& this.props.piece.prize.name
|
||||||
|
@ -6,9 +6,37 @@ import Router from 'react-router';
|
|||||||
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';
|
||||||
|
|
||||||
let Link = Router.Link;
|
import UserStore from '../../../../stores/user_store';
|
||||||
|
import UserActions from '../../../../actions/user_actions';
|
||||||
|
|
||||||
|
|
||||||
let Landing = React.createClass({
|
let Landing = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return UserStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
|
||||||
|
// if user is already logged in, redirect him to piece list
|
||||||
|
if(this.state.currentUser && this.state.currentUser.email) {
|
||||||
|
// FIXME: hack to redirect out of the dispatch cycle
|
||||||
|
window.setTimeout(() => this.replaceWith('pieces'), 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@ -18,7 +46,7 @@ let Landing = React.createClass({
|
|||||||
<p>
|
<p>
|
||||||
This is the submission page for Sluice_screens ↄc Prize 2015.
|
This is the submission page for Sluice_screens ↄc Prize 2015.
|
||||||
</p>
|
</p>
|
||||||
<ButtonGroup className="enter" bsSize="large" vertical block>
|
<ButtonGroup className="enter" bsSize="large" vertical>
|
||||||
<ButtonLink to="signup">
|
<ButtonLink to="signup">
|
||||||
Sign up to submit
|
Sign up to submit
|
||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
|
316
js/components/whitelabel/prize/components/settings_container.js
Normal file
316
js/components/whitelabel/prize/components/settings_container.js
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import UserStore from '../../../../stores/user_store';
|
||||||
|
import UserActions from '../../../../actions/user_actions';
|
||||||
|
import PrizeActions from '../actions/prize_actions';
|
||||||
|
import PrizeStore from '../stores/prize_store';
|
||||||
|
import PrizeJuryActions from '../actions/prize_jury_actions';
|
||||||
|
import PrizeJuryStore from '../stores/prize_jury_store';
|
||||||
|
|
||||||
|
import SettingsContainer from '../../../settings_container';
|
||||||
|
import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
|
import Form from '../../../ascribe_forms/form';
|
||||||
|
import Property from '../../../ascribe_forms/property';
|
||||||
|
|
||||||
|
import ActionPanel from '../../../ascribe_panel/action_panel';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import AppConstants from '../../../../constants/application_constants';
|
||||||
|
import ApiUrls from '../../../../constants/api_urls';
|
||||||
|
|
||||||
|
import { getLangText } from '../../../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
|
let Settings = React.createClass({
|
||||||
|
getInitialState() {
|
||||||
|
return UserStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let prizeSettings = null;
|
||||||
|
if (this.state.currentUser.is_admin){
|
||||||
|
prizeSettings = <PrizeSettings />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SettingsContainer>
|
||||||
|
{prizeSettings}
|
||||||
|
</SettingsContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let PrizeSettings = React.createClass({
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return PrizeStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PrizeStore.listen(this.onChange);
|
||||||
|
PrizeActions.fetchPrize();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
PrizeStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title={'Prize Settings for ' + this.state.prize.name}
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
<Form >
|
||||||
|
<Property
|
||||||
|
name='prize_name'
|
||||||
|
label={getLangText('Prize name')}
|
||||||
|
editable={false}>
|
||||||
|
<pre className="ascribe-pre">{this.state.prize.name}</pre>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='prize_rounds'
|
||||||
|
label={getLangText('Active round/Number of rounds')}
|
||||||
|
editable={false}>
|
||||||
|
<pre className="ascribe-pre">{this.state.prize.active_round}/{this.state.prize.rounds}</pre>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='num_submissions'
|
||||||
|
label={getLangText('Allowed number of submissions per user')}
|
||||||
|
editable={false}>
|
||||||
|
<pre className="ascribe-pre">{this.state.prize.num_submissions}</pre>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
<PrizeJurySettings
|
||||||
|
prize={this.state.prize}/>
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let PrizeJurySettings = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
prize: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return PrizeJuryStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PrizeJuryStore.listen(this.onChange);
|
||||||
|
PrizeJuryActions.fetchJury();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
PrizeJuryStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreateSuccess(response) {
|
||||||
|
PrizeJuryActions.fetchJury();
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
this.refs.form.refs.email.refs.input.getDOMNode().value = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleActivate(event) {
|
||||||
|
let email = event.target.getAttribute('data-id');
|
||||||
|
PrizeJuryActions.activateJury(email).then((response) => {
|
||||||
|
PrizeJuryActions.fetchJury();
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleRevoke(event) {
|
||||||
|
let email = event.target.getAttribute('data-id');
|
||||||
|
PrizeJuryActions.revokeJury(email).then((response) => {
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleResend(event) {
|
||||||
|
let email = event.target.getAttribute('data-id');
|
||||||
|
PrizeJuryActions.resendJuryInvitation(email).then((response) => {
|
||||||
|
let notification = new GlobalNotificationModel(response.notification, 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMembersPending() {
|
||||||
|
return this.state.membersPending.map(function(member, i) {
|
||||||
|
return (
|
||||||
|
<ActionPanel
|
||||||
|
name={member.email}
|
||||||
|
key={i}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className='ascribe-panel-title'>
|
||||||
|
{member.email}
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-subtitle">
|
||||||
|
{member.status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<div className="pull-right">
|
||||||
|
<button
|
||||||
|
className="btn btn-default btn-sm margin-left-2px"
|
||||||
|
onClick={this.handleResend}
|
||||||
|
data-id={member.email}>
|
||||||
|
{getLangText('RESEND')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-default btn-sm ascribe-btn-gray margin-left-2px"
|
||||||
|
onClick={this.handleRevoke}
|
||||||
|
data-id={member.email}>
|
||||||
|
{getLangText('REVOKE')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}/>
|
||||||
|
);
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
getMembersActive() {
|
||||||
|
return this.state.membersActive.map(function(member, i) {
|
||||||
|
return (
|
||||||
|
<ActionPanel
|
||||||
|
name={member.email}
|
||||||
|
key={i}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className='ascribe-panel-title'>
|
||||||
|
{member.email}
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-subtitle">
|
||||||
|
{member.status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<button
|
||||||
|
className="btn btn-default btn-sm ascribe-btn-gray"
|
||||||
|
onClick={this.handleRevoke}
|
||||||
|
data-id={member.email}>
|
||||||
|
{getLangText('REVOKE')}
|
||||||
|
</button>
|
||||||
|
}/>
|
||||||
|
);
|
||||||
|
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
getMembersInactive() {
|
||||||
|
return this.state.membersInactive.map(function(member, i) {
|
||||||
|
return (
|
||||||
|
<ActionPanel
|
||||||
|
name={member.email}
|
||||||
|
key={i}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className='ascribe-panel-title'>
|
||||||
|
{member.email}
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-subtitle">
|
||||||
|
{member.status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<button
|
||||||
|
className="btn btn-default btn-sm"
|
||||||
|
onClick={this.handleActivate}
|
||||||
|
data-id={member.email}>
|
||||||
|
{getLangText('ACTIVATE')}
|
||||||
|
</button>
|
||||||
|
}/>
|
||||||
|
);
|
||||||
|
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
getMembers() {
|
||||||
|
let content = (
|
||||||
|
<div style={{textAlign: 'center'}}>
|
||||||
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||||
|
</div>);
|
||||||
|
|
||||||
|
if (this.state.members.length > -1) {
|
||||||
|
content = (
|
||||||
|
<div style={{padding: '1em'}}>
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title={'Active Jury Members'}
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
{this.getMembersActive()}
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title={'Pending Jury Invitations'}
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
{this.getMembersPending()}
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title={'Deactivated Jury Members'}
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={false}>
|
||||||
|
{this.getMembersInactive()}
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form
|
||||||
|
url={ApiUrls.jurys}
|
||||||
|
handleSuccess={this.handleCreateSuccess}
|
||||||
|
ref='form'
|
||||||
|
buttonSubmitText='INVITE'>
|
||||||
|
<div className="ascribe-form-header">
|
||||||
|
<h4 style={{margin: '30px 0px 10px 10px'}}>Jury Members</h4>
|
||||||
|
</div>
|
||||||
|
<Property
|
||||||
|
name='email'
|
||||||
|
label={getLangText('New jury member')}>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder={getLangText('Enter an email to invite a jury member')}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
{this.getMembers()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default Settings;
|
@ -1,15 +1,22 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import AppConstants from '../../../../constants/application_constants';
|
import AppPrizeConstants from './application_prize_constants';
|
||||||
|
|
||||||
function getApiUrls(subdomain) {
|
function getApiUrls(subdomain) {
|
||||||
return {
|
return {
|
||||||
'pieces_list': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/',
|
'pieces_list': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/',
|
||||||
'users_login': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/login/',
|
'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/',
|
||||||
'users_signup': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/',
|
'users_signup': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/',
|
||||||
'user': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/',
|
'user': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/',
|
||||||
'piece_submit_to_prize': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/submit/',
|
'piece_submit_to_prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/submit/',
|
||||||
'piece': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/'
|
'piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/',
|
||||||
|
'prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/',
|
||||||
|
'jurys': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/',
|
||||||
|
'jury': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/',
|
||||||
|
'jury_activate': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/activate/',
|
||||||
|
'jury_resend': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/${email}/resend/',
|
||||||
|
'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/',
|
||||||
|
'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import AppConstants from '../../../../constants/application_constants';
|
||||||
|
|
||||||
|
let constants = {
|
||||||
|
prizeApiEndpoint: AppConstants.apiEndpoint + 'prizes/'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default constants;
|
12
js/components/whitelabel/prize/fetchers/prize_fetcher.js
Normal file
12
js/components/whitelabel/prize/fetchers/prize_fetcher.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
|
let PrizeFetcher = {
|
||||||
|
fetch() {
|
||||||
|
return requests.get('prize');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrizeFetcher;
|
@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
|
let PrizeJuryFetcher = {
|
||||||
|
fetch() {
|
||||||
|
return requests.get('jurys');
|
||||||
|
},
|
||||||
|
|
||||||
|
activate(email) {
|
||||||
|
return requests.post('jury_activate', {'email': email});
|
||||||
|
},
|
||||||
|
|
||||||
|
delete(email) {
|
||||||
|
return requests.delete('jury', {'email': email});
|
||||||
|
},
|
||||||
|
|
||||||
|
resend(email) {
|
||||||
|
return requests.post('jury_resend', {'email': email});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrizeJuryFetcher;
|
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../../../../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
|
let PrizeRatingFetcher = {
|
||||||
|
fetch() {
|
||||||
|
return requests.get('rating');
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchOne(pieceId) {
|
||||||
|
return requests.get('rating', {'piece_id': pieceId});
|
||||||
|
},
|
||||||
|
|
||||||
|
rate(pieceId, rating) {
|
||||||
|
return requests.post('ratings', {body: {'piece_id': pieceId, 'rating': rating}});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrizeRatingFetcher;
|
@ -12,7 +12,7 @@ import PrizeRegisterPiece from './components/register_piece';
|
|||||||
import PrizePieceList from './components/piece_list';
|
import PrizePieceList from './components/piece_list';
|
||||||
import PrizePieceContainer from './components/ascribe_detail/piece_container';
|
import PrizePieceContainer from './components/ascribe_detail/piece_container';
|
||||||
import EditionContainer from '../../ascribe_detail/edition_container';
|
import EditionContainer from '../../ascribe_detail/edition_container';
|
||||||
import SettingsContainer from '../../../components/settings_container';
|
import SettingsContainer from './components/settings_container';
|
||||||
|
|
||||||
import App from './app';
|
import App from './app';
|
||||||
import AppConstants from '../../../constants/application_constants';
|
import AppConstants from '../../../constants/application_constants';
|
||||||
@ -21,7 +21,7 @@ let Route = Router.Route;
|
|||||||
let baseUrl = AppConstants.baseUrl;
|
let baseUrl = AppConstants.baseUrl;
|
||||||
|
|
||||||
|
|
||||||
function getRoutes(commonRoutes) {
|
function getRoutes() {
|
||||||
return (
|
return (
|
||||||
<Route name="app" path={baseUrl} handler={App}>
|
<Route name="app" path={baseUrl} handler={App}>
|
||||||
<Route name="landing" path={baseUrl} handler={Landing} />
|
<Route name="landing" path={baseUrl} handler={Landing} />
|
||||||
|
35
js/components/whitelabel/prize/stores/prize_jury_store.js
Normal file
35
js/components/whitelabel/prize/stores/prize_jury_store.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
|
||||||
|
import PrizeJuryActions from '../actions/prize_jury_actions';
|
||||||
|
|
||||||
|
class PrizeJuryStore {
|
||||||
|
constructor() {
|
||||||
|
this.members = [];
|
||||||
|
this.membersActive = [];
|
||||||
|
this.membersPending = [];
|
||||||
|
this.membersInactive = [];
|
||||||
|
this.bindActions(PrizeJuryActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatePrizeJury( members ) {
|
||||||
|
this.members = members;
|
||||||
|
this.splitJuryMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemovePrizeJury( email ) {
|
||||||
|
let memberInactive = this.members.filter((item)=> item.email === email );
|
||||||
|
this.membersActive = this.membersActive.filter((item)=> item.email !== email );
|
||||||
|
this.membersPending = this.membersPending.filter((item)=> item.email !== email );
|
||||||
|
this.membersInactive = this.membersInactive.concat(memberInactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
splitJuryMembers(){
|
||||||
|
this.membersActive = this.members.filter((item)=> item.status === 'Invitation accepted' );
|
||||||
|
this.membersPending = this.members.filter((item)=> item.status === 'Invitation pending' );
|
||||||
|
this.membersInactive = this.members.filter((item)=> item.status === 'Deactivated' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(PrizeJuryStore, 'PrizeJuryStore');
|
23
js/components/whitelabel/prize/stores/prize_rating_store.js
Normal file
23
js/components/whitelabel/prize/stores/prize_rating_store.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
|
||||||
|
import PrizeRatingActions from '../actions/prize_rating_actions';
|
||||||
|
|
||||||
|
class PrizeRatingStore {
|
||||||
|
constructor() {
|
||||||
|
this.ratings = [];
|
||||||
|
this.currentRating = null;
|
||||||
|
this.bindActions(PrizeRatingActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatePrizeRatings( ratings ) {
|
||||||
|
this.ratings = ratings;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatePrizeRating( rating ) {
|
||||||
|
this.currentRating = parseInt(rating, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(PrizeRatingStore, 'PrizeRatingStore');
|
18
js/components/whitelabel/prize/stores/prize_store.js
Normal file
18
js/components/whitelabel/prize/stores/prize_store.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../../../../alt';
|
||||||
|
|
||||||
|
import PrizeActions from '../actions/prize_actions';
|
||||||
|
|
||||||
|
class PrizeStore {
|
||||||
|
constructor() {
|
||||||
|
this.prize = [];
|
||||||
|
this.bindActions(PrizeActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatePrize({ prize }) {
|
||||||
|
this.prize = prize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(PrizeStore, 'PrizeStore');
|
@ -5,7 +5,7 @@ import getPrizeApiUrls from '../components/whitelabel/prize/constants/api_urls';
|
|||||||
import { update } from '../utils/general_utils';
|
import { update } from '../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let apiUrls = {
|
let ApiUrls = {
|
||||||
'applications': AppConstants.apiEndpoint + 'applications/',
|
'applications': AppConstants.apiEndpoint + 'applications/',
|
||||||
'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/',
|
'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/',
|
||||||
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/',
|
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/',
|
||||||
@ -52,7 +52,8 @@ let apiUrls = {
|
|||||||
'users_profile': AppConstants.apiEndpoint + 'users/profile/',
|
'users_profile': AppConstants.apiEndpoint + 'users/profile/',
|
||||||
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/',
|
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/',
|
||||||
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
|
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
|
||||||
'delete_s3_file': AppConstants.serverUrl + 's3/delete/'
|
'delete_s3_file': AppConstants.serverUrl + 's3/delete/',
|
||||||
|
'prize_list': AppConstants.apiEndpoint + 'prize/'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ export function updateApiUrls(type, subdomain) {
|
|||||||
if (type === 'prize') {
|
if (type === 'prize') {
|
||||||
newUrls = getPrizeApiUrls(subdomain);
|
newUrls = getPrizeApiUrls(subdomain);
|
||||||
}
|
}
|
||||||
update(apiUrls, newUrls);
|
update(ApiUrls, newUrls);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiUrls;
|
export default ApiUrls;
|
||||||
|
@ -10,9 +10,9 @@ let constants = {
|
|||||||
'apiEndpoint': window.API_ENDPOINT,
|
'apiEndpoint': window.API_ENDPOINT,
|
||||||
'serverUrl': window.SERVER_URL,
|
'serverUrl': window.SERVER_URL,
|
||||||
'baseUrl': window.BASE_URL,
|
'baseUrl': window.BASE_URL,
|
||||||
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_editions',
|
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
|
||||||
'acl_loan', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
'acl_loan', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
||||||
'acl_withdraw_transfer'],
|
'acl_withdraw_transfer', 'acl_submit_to_prize'],
|
||||||
|
|
||||||
'version': 0.1,
|
'version': 0.1,
|
||||||
'csrftoken': 'csrftoken2',
|
'csrftoken': 'csrftoken2',
|
||||||
|
@ -3,20 +3,26 @@
|
|||||||
import requests from '../utils/requests';
|
import requests from '../utils/requests';
|
||||||
|
|
||||||
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
||||||
|
import { mergeOptions } from '../utils/general_utils';
|
||||||
|
|
||||||
let EditionListFetcher = {
|
let EditionListFetcher = {
|
||||||
/**
|
/**
|
||||||
* Fetches a list of editions from the API.
|
* Fetches a list of editions from the API.
|
||||||
*/
|
*/
|
||||||
fetch(pieceId, page, pageSize, orderBy, orderAsc) {
|
fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) {
|
||||||
let ordering = generateOrderingQueryParams(orderBy, orderAsc);
|
let ordering = generateOrderingQueryParams(orderBy, orderAsc);
|
||||||
return requests.get('editions_list', {
|
|
||||||
'piece_id': pieceId,
|
let queryParams = mergeOptions(
|
||||||
page,
|
{
|
||||||
pageSize,
|
page,
|
||||||
ordering
|
pageSize,
|
||||||
});
|
ordering,
|
||||||
|
piece_id: pieceId
|
||||||
|
},
|
||||||
|
filterBy
|
||||||
|
);
|
||||||
|
|
||||||
|
return requests.get('editions_list', queryParams);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
|
||||||
import requests from '../utils/requests';
|
import requests from '../utils/requests';
|
||||||
|
|
||||||
|
import { mergeOptions } from '../utils/general_utils';
|
||||||
|
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
||||||
|
|
||||||
let PieceListFetcher = {
|
let PieceListFetcher = {
|
||||||
/**
|
/**
|
||||||
* Fetches a list of pieces from the API.
|
* Fetches a list of pieces from the API.
|
||||||
* Can be called with all supplied queryparams the API.
|
* Can be called with all supplied queryparams the API.
|
||||||
*/
|
*/
|
||||||
fetch(page, pageSize, search, orderBy, orderAsc) {
|
fetch(page, pageSize, search, orderBy, orderAsc, filterBy) {
|
||||||
let ordering = generateOrderingQueryParams(orderBy, orderAsc);
|
let ordering = generateOrderingQueryParams(orderBy, orderAsc);
|
||||||
return requests.get('pieces_list', { page, pageSize, search, ordering });
|
|
||||||
|
// filterBy is an object of acl key-value pairs.
|
||||||
|
// The values are booleans
|
||||||
|
let queryParams = mergeOptions(
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
search,
|
||||||
|
ordering
|
||||||
|
},
|
||||||
|
filterBy
|
||||||
|
);
|
||||||
|
|
||||||
|
return requests.get('pieces_list', queryParams);
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchRequestActions() {
|
fetchRequestActions() {
|
||||||
|
12
js/fetchers/prize_list_fetcher.js
Normal file
12
js/fetchers/prize_list_fetcher.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../utils/requests';
|
||||||
|
|
||||||
|
|
||||||
|
let PrizeListFetcher = {
|
||||||
|
fetch() {
|
||||||
|
return requests.get('prize_list');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrizeListFetcher;
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import requests from '../utils/requests';
|
import requests from '../utils/requests';
|
||||||
import apiUrls from '../constants/api_urls';
|
import ApiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
let UserFetcher = {
|
let UserFetcher = {
|
||||||
/**
|
/**
|
||||||
@ -13,7 +13,7 @@ let UserFetcher = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
return requests.get(apiUrls.users_logout);
|
return requests.get(ApiUrls.users_logout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import AlertDismissable from '../components/ascribe_forms/alert';
|
|
||||||
|
|
||||||
let AlertMixin = {
|
|
||||||
setAlerts(errors){
|
|
||||||
let alerts = errors.map((error) => {
|
|
||||||
return <AlertDismissable error={error} key={error}/>;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({alerts: alerts});
|
|
||||||
},
|
|
||||||
|
|
||||||
clearAlerts(){
|
|
||||||
this.setState({alerts: null});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlertMixin;
|
|
@ -1,108 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import requests from '../utils/requests';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import AlertDismissable from '../components/ascribe_forms/alert';
|
|
||||||
import { getLangText } from '../utils/lang_utils.js';
|
|
||||||
|
|
||||||
export const FormMixin = {
|
|
||||||
propTypes: {
|
|
||||||
editions: React.PropTypes.array,
|
|
||||||
currentUser: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
submitted: false,
|
|
||||||
errors: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
submit(e) {
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
this.setState({submitted: true});
|
|
||||||
this.clearErrors();
|
|
||||||
let action = (this.httpVerb && this.httpVerb()) || 'post';
|
|
||||||
this[action](e);
|
|
||||||
},
|
|
||||||
|
|
||||||
post(e){
|
|
||||||
requests
|
|
||||||
.post(this.url(e), { body: this.getFormData() })
|
|
||||||
.then(this.handleSuccess)
|
|
||||||
.catch(this.handleError);
|
|
||||||
},
|
|
||||||
|
|
||||||
delete(e){
|
|
||||||
requests
|
|
||||||
.delete(this.url(e))
|
|
||||||
.then(this.handleSuccess)
|
|
||||||
.catch(this.handleError);
|
|
||||||
},
|
|
||||||
|
|
||||||
clearErrors(){
|
|
||||||
for (var ref in this.refs){
|
|
||||||
if ('clearAlerts' in this.refs[ref]){
|
|
||||||
this.refs[ref].clearAlerts();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
this.setState({errors: []});
|
|
||||||
},
|
|
||||||
handleSuccess(response){
|
|
||||||
if ('handleSuccess' in this.props){
|
|
||||||
this.props.handleSuccess(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
handleError(err){
|
|
||||||
if (err.json) {
|
|
||||||
for (var input in err.json.errors){
|
|
||||||
if (this.refs && this.refs[input] && this.refs[input].state) {
|
|
||||||
this.refs[input].setAlerts( err.json.errors[input]);
|
|
||||||
} else {
|
|
||||||
this.setState({errors: this.state.errors.concat(err.json.errors[input])});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO translate?
|
|
||||||
this.setState({errors: ['Something went wrong, please try again later']});
|
|
||||||
}
|
|
||||||
this.setState({submitted: false});
|
|
||||||
},
|
|
||||||
|
|
||||||
getBitcoinIds(){
|
|
||||||
return this.props.editions.map(function(edition){
|
|
||||||
return edition.bitcoin_id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getTitlesString(){
|
|
||||||
return this.props.editions.map(function(edition){
|
|
||||||
return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render(){
|
|
||||||
let alert = null;
|
|
||||||
if (this.state.errors.length > 0){
|
|
||||||
alert = this.state.errors.map((error) => {
|
|
||||||
return <AlertDismissable error={error} key={error}/>;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{alert}
|
|
||||||
{this.renderForm()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FormMixin;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
let ModalMixin = {
|
|
||||||
onRequestHide(e){
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
this.props.onRequestHide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModalMixin;
|
|
@ -1,24 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import { sumNumList } from '../utils/general_utils';
|
|
||||||
|
|
||||||
let TableColumnMixin = {
|
|
||||||
/**
|
|
||||||
* Generates the bootstrap grid column declarations automatically using
|
|
||||||
* the columnMap.
|
|
||||||
*/
|
|
||||||
calcColumnClasses(list, i, numOfColumns) {
|
|
||||||
let bootstrapClasses = ['col-xs-', 'col-sm-', 'col-md-', 'col-lg-'];
|
|
||||||
|
|
||||||
let listOfRowValues = list.map((column) => column.rowWidth );
|
|
||||||
let numOfUsedColumns = sumNumList(listOfRowValues);
|
|
||||||
|
|
||||||
if(numOfUsedColumns > numOfColumns) {
|
|
||||||
throw new Error('This table has only ' + numOfColumns + ' columns to assign. You defined ' + numOfUsedColumns + '. Change this in the columnMap you\'re passing to the table.');
|
|
||||||
} else {
|
|
||||||
return bootstrapClasses.join( listOfRowValues[i] + ' ') + listOfRowValues[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableColumnMixin;
|
|
@ -20,6 +20,8 @@ import CoaVerifyContainer from './components/coa_verify_container';
|
|||||||
|
|
||||||
import RegisterPiece from './components/register_piece';
|
import RegisterPiece from './components/register_piece';
|
||||||
|
|
||||||
|
import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard';
|
||||||
|
|
||||||
let Route = Router.Route;
|
let Route = Router.Route;
|
||||||
|
|
||||||
|
|
||||||
@ -35,6 +37,7 @@ const COMMON_ROUTES = (
|
|||||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
||||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||||
<Route name="coa_verify" path="verify" handler={CoaVerifyContainer} />
|
<Route name="coa_verify" path="verify" handler={CoaVerifyContainer} />
|
||||||
|
<Route name="prizes" path="prizes" handler={PrizesDashboard} />
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class EditionListStore {
|
|||||||
this.bindActions(EditionsListActions);
|
this.bindActions(EditionsListActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count}) {
|
onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count, filterBy}) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Basically there are two modes an edition list can be updated.
|
Basically there are two modes an edition list can be updated.
|
||||||
@ -54,14 +54,14 @@ class EditionListStore {
|
|||||||
this.editionList[pieceId].orderBy = orderBy;
|
this.editionList[pieceId].orderBy = orderBy;
|
||||||
this.editionList[pieceId].orderAsc = orderAsc;
|
this.editionList[pieceId].orderAsc = orderAsc;
|
||||||
this.editionList[pieceId].count = count;
|
this.editionList[pieceId].count = count;
|
||||||
|
this.editionList[pieceId].filterBy = filterBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We often just have to refresh the edition list for a certain pieceId,
|
* We often just have to refresh the edition list for a certain pieceId,
|
||||||
* this method provides exactly that functionality without any side effects
|
* this method provides exactly that functionality without any side effects
|
||||||
*/
|
*/
|
||||||
onRefreshEditionList(pieceId) {
|
onRefreshEditionList({pieceId, filterBy}) {
|
||||||
|
|
||||||
// It may happen that the user enters the site logged in already
|
// It may happen that the user enters the site logged in already
|
||||||
// through /editions
|
// through /editions
|
||||||
// If he then tries to delete a piece/edition and this method is called,
|
// If he then tries to delete a piece/edition and this method is called,
|
||||||
@ -71,16 +71,28 @@ class EditionListStore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevEditionListLength = this.editionList[pieceId].length;
|
let prevEditionListLength = this.editionList[pieceId].length;
|
||||||
const prevEditionListPage = this.editionList[pieceId].page;
|
let prevEditionListPage = this.editionList[pieceId].page;
|
||||||
const prevEditionListPageSize = this.editionList[pieceId].pageSize;
|
let prevEditionListPageSize = this.editionList[pieceId].pageSize;
|
||||||
|
|
||||||
|
// we can also refresh the edition list using filterBy,
|
||||||
|
// if we decide not to do that then the old filter will just be applied.
|
||||||
|
if(filterBy && Object.keys(filterBy).length <= 0) {
|
||||||
|
filterBy = this.editionList[pieceId].filterBy;
|
||||||
|
prevEditionListLength = 10;
|
||||||
|
prevEditionListPage = 1;
|
||||||
|
prevEditionListPageSize = 10;
|
||||||
|
}
|
||||||
|
|
||||||
// to clear an array, david walsh recommends to just set it's length to zero
|
// to clear an array, david walsh recommends to just set it's length to zero
|
||||||
// http://davidwalsh.name/empty-array
|
// http://davidwalsh.name/empty-array
|
||||||
this.editionList[pieceId].length = 0;
|
this.editionList[pieceId].length = 0;
|
||||||
|
|
||||||
// refetch editions with adjusted page size
|
// refetch editions with adjusted page size
|
||||||
EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength, this.editionList[pieceId].orderBy, this.editionList[pieceId].orderAsc)
|
EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength,
|
||||||
|
this.editionList[pieceId].orderBy,
|
||||||
|
this.editionList[pieceId].orderAsc,
|
||||||
|
filterBy)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// reset back to the normal pageSize and page
|
// reset back to the normal pageSize and page
|
||||||
this.editionList[pieceId].page = prevEditionListPage;
|
this.editionList[pieceId].page = prevEditionListPage;
|
||||||
@ -121,9 +133,21 @@ class EditionListStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onToggleEditionList(pieceId) {
|
onToggleEditionList(pieceId) {
|
||||||
|
|
||||||
this.isEditionListOpenForPieceId[pieceId] = {
|
this.isEditionListOpenForPieceId[pieceId] = {
|
||||||
show: this.isEditionListOpenForPieceId[pieceId] ? !this.isEditionListOpenForPieceId[pieceId].show : true
|
show: this.isEditionListOpenForPieceId[pieceId] ? !this.isEditionListOpenForPieceId[pieceId].show : true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When loading all editions of a piece, closing the table and then applying the filter
|
||||||
|
// the merge fails, as the edition list is not refreshed when closed.
|
||||||
|
// Therefore in the case of a filter application when closed, we need to reload the
|
||||||
|
// edition list
|
||||||
|
if(!this.isEditionListOpenForPieceId[pieceId].show) {
|
||||||
|
// to clear an array, david walsh recommends to just set it's length to zero
|
||||||
|
// http://davidwalsh.name/empty-array
|
||||||
|
|
||||||
|
this.editionList[pieceId].length = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCloseAllEditionLists() {
|
onCloseAllEditionLists() {
|
||||||
|
@ -17,6 +17,12 @@ class LoanContractStore {
|
|||||||
this.contractUrl = contractUrl;
|
this.contractUrl = contractUrl;
|
||||||
this.contractEmail = contractEmail;
|
this.contractEmail = contractEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFlushLoanContract() {
|
||||||
|
this.contractKey = null;
|
||||||
|
this.contractUrl = null;
|
||||||
|
this.contractEmail = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default alt.createStore(LoanContractStore, 'LoanContractStore');
|
export default alt.createStore(LoanContractStore, 'LoanContractStore');
|
||||||
|
@ -26,16 +26,18 @@ class PieceListStore {
|
|||||||
this.search = '';
|
this.search = '';
|
||||||
this.orderBy = 'artist_name';
|
this.orderBy = 'artist_name';
|
||||||
this.orderAsc = true;
|
this.orderAsc = true;
|
||||||
|
this.filterBy = {};
|
||||||
this.bindActions(PieceListActions);
|
this.bindActions(PieceListActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) {
|
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, filterBy }) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
this.search = search;
|
this.search = search;
|
||||||
this.orderAsc = orderAsc;
|
this.orderAsc = orderAsc;
|
||||||
this.orderBy = orderBy;
|
this.orderBy = orderBy;
|
||||||
this.pieceListCount = pieceListCount;
|
this.pieceListCount = pieceListCount;
|
||||||
|
this.filterBy = filterBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pagination - Known Issue:
|
* Pagination - Known Issue:
|
||||||
@ -80,7 +82,6 @@ class PieceListStore {
|
|||||||
|
|
||||||
let piece = filteredPieceList[0];
|
let piece = filteredPieceList[0];
|
||||||
piece[key] = value;
|
piece[key] = value;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Could not find a matching piece in piece list since its either not there or piecelist contains duplicates.');
|
throw new Error('Could not find a matching piece in piece list since its either not there or piecelist contains duplicates.');
|
||||||
}
|
}
|
||||||
|
20
js/stores/prize_list_store.js
Normal file
20
js/stores/prize_list_store.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
import PrizeListActions from '../actions/prize_list_actions';
|
||||||
|
|
||||||
|
class PrizeListStore {
|
||||||
|
constructor() {
|
||||||
|
this.prizeList = [];
|
||||||
|
this.prizeListCount = -1;
|
||||||
|
this.bindActions(PrizeListActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatePrizeList({ prizeList, prizeListCount }) {
|
||||||
|
this.prizeList = prizeList;
|
||||||
|
this.prizeListCount = prizeListCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(PrizeListStore, 'PrizeListStore');
|
51
js/utils/form_utils.js
Normal file
51
js/utils/form_utils.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { getLangText } from './lang_utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a message for submitting a form
|
||||||
|
* @param {string} aclName Enum name of a acl
|
||||||
|
* @param {string} entities Already computed name of entities
|
||||||
|
* @param {string} senderName Name of the sender
|
||||||
|
* @return {string} Completed message
|
||||||
|
*/
|
||||||
|
export function getAclFormMessage(aclName, entities, senderName) {
|
||||||
|
let message = '';
|
||||||
|
|
||||||
|
message += getLangText('Hi');
|
||||||
|
message += ',\n\n';
|
||||||
|
|
||||||
|
if(aclName === 'acl_transfer') {
|
||||||
|
message += getLangText('I transfer ownership of');
|
||||||
|
} else if(aclName === 'acl_consign') {
|
||||||
|
message += getLangText('I consign');
|
||||||
|
} else if(aclName === 'acl_unconsign') {
|
||||||
|
message += getLangText('I un-consign');
|
||||||
|
} else if(aclName === 'acl_loan') {
|
||||||
|
message += getLangText('I loan');
|
||||||
|
} else if(aclName === 'acl_share') {
|
||||||
|
message += getLangText('I share');
|
||||||
|
} else {
|
||||||
|
throw new Error('Your specified aclName did not match a an acl class.');
|
||||||
|
}
|
||||||
|
|
||||||
|
message += ':\n';
|
||||||
|
message += entities;
|
||||||
|
|
||||||
|
if(aclName === 'acl_transfer' || aclName === 'acl_loan' || aclName === 'acl_consign') {
|
||||||
|
message += getLangText('to you');
|
||||||
|
} else if(aclName === 'acl_unconsign') {
|
||||||
|
message += getLangText('from you');
|
||||||
|
} else if(aclName === 'acl_share') {
|
||||||
|
message += getLangText('with you');
|
||||||
|
} else {
|
||||||
|
throw new Error('Your specified aclName did not match a an acl class.');
|
||||||
|
}
|
||||||
|
|
||||||
|
message += '\n\n';
|
||||||
|
message += getLangText('Truly yours,');
|
||||||
|
message += '\n';
|
||||||
|
message += senderName;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user