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

Merge remote-tracking branch 'remotes/origin/master' into AD-44-in-piece-detail-support-public-not

Conflicts:
	js/utils/requests.js
This commit is contained in:
ddejongh 2015-06-16 14:06:35 +02:00
commit 1ecd579718
41 changed files with 220 additions and 182 deletions

View File

@ -2,4 +2,6 @@
gulpfile.js gulpfile.js
node_modules node_modules
js/**/__tests__ js/**/__tests__
server.js

View File

@ -105,7 +105,7 @@ gulp.task('sass:build', function () {
}); });
gulp.task('sass:watch', function () { gulp.task('sass:watch', function () {
gulp.watch('./sass/**/*.scss', ['sass']); gulp.watch('./sass/**/*.scss', ['sass:build']);
}); });
gulp.task('copy', function () { gulp.task('copy', function () {

View File

@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="//brick.a.ssl.fastly.net/Source+Sans+Pro:400,600,700,900"> <link rel="stylesheet" href="//brick.a.ssl.fastly.net/Source+Sans+Pro:400,600,700,900">
<link rel="stylesheet" href="<%= BASE_URL %>static/css/main.css"> <link rel="stylesheet" href="<%= BASE_URL %>static/css/main.css">
<link rel="stylesheet" href="<%= BASE_URL %>static/css/maps/main.css.map">
<script> <script>
window.BASE_URL = '<%= BASE_URL %>'; window.BASE_URL = '<%= BASE_URL %>';
window.API_ENDPOINT = '<%= API_ENDPOINT %>'; window.API_ENDPOINT = '<%= API_ENDPOINT %>';

View File

@ -9,7 +9,9 @@ class EditionListActions {
this.generateActions( this.generateActions(
'updateEditionList', 'updateEditionList',
'selectEdition', 'selectEdition',
'clearAllEditionSelections' 'clearAllEditionSelections',
'closeAllEditionLists',
'toggleEditionList'
); );
} }
@ -19,19 +21,24 @@ class EditionListActions {
orderAsc = true; orderAsc = true;
} }
EditionListFetcher return new Promise((resolve, reject) => {
.fetch(pieceId, orderBy, orderAsc) EditionListFetcher
.then((res) => { .fetch(pieceId, orderBy, orderAsc)
this.actions.updateEditionList({ .then((res) => {
'editionListOfPiece': res.editions, this.actions.updateEditionList({
pieceId, 'editionListOfPiece': res.editions,
orderBy, pieceId,
orderAsc orderBy,
orderAsc
});
resolve(res);
})
.catch((err) => {
reject(err);
console.log(err);
}); });
}) });
.catch((err) => {
console.log(err);
});
} }
} }

View File

@ -8,9 +8,7 @@ import PieceListFetcher from '../fetchers/piece_list_fetcher';
class PieceListActions { class PieceListActions {
constructor() { constructor() {
this.generateActions( this.generateActions(
'updatePieceList', 'updatePieceList'
'showEditionList',
'closeAllEditionLists'
); );
} }

View File

@ -5,17 +5,19 @@ import Router from 'react-router';
import promise from 'es6-promise'; import promise from 'es6-promise';
promise.polyfill(); promise.polyfill();
import fetch from 'isomorphic-fetch';
//require('isomorphic-fetch');
import ApiUrls from './constants/api_urls'; import ApiUrls from './constants/api_urls';
import routes from './routes'; import routes from './routes';
import fetch from './utils/fetch'; import requests from './utils/requests';
let headers = { let headers = {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}; };
fetch.defaults({ requests.defaults({
urlMap: ApiUrls, urlMap: ApiUrls,
http: { http: {
headers: headers, headers: headers,

View File

@ -2,11 +2,11 @@
import React from 'react'; import React from 'react';
let AccordionList = React.createClass({ let AccordionList = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
children: React.PropTypes.arrayOf(React.PropTypes.element).isRequired children: React.PropTypes.arrayOf(React.PropTypes.element).isRequired,
loadingElement: React.PropTypes.element
}, },
render() { render() {
@ -18,7 +18,9 @@ let AccordionList = React.createClass({
); );
} else { } else {
return ( return (
<p>Loading</p> <div className={this.props.className + ' ascribe-accordion-list-loading'}>
{this.props.loadingElement}
</div>
); );
} }
} }

View File

@ -1,12 +1,9 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link;
let AccordionListItem = React.createClass({ let AccordionListItem = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,

View File

@ -13,7 +13,6 @@ let AccordionListItemTable = React.createClass({
parentId: React.PropTypes.number, parentId: React.PropTypes.number,
itemList: React.PropTypes.array, itemList: React.PropTypes.array,
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
numOfTableItems: React.PropTypes.number,
show: React.PropTypes.bool, show: React.PropTypes.bool,
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,
orderBy: React.PropTypes.string, orderBy: React.PropTypes.string,

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router';
import EditionListStore from '../../stores/edition_list_store'; import EditionListStore from '../../stores/edition_list_store';
import EditionListActions from '../../actions/edition_list_actions'; import EditionListActions from '../../actions/edition_list_actions';
@ -19,15 +18,11 @@ import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link;
let AccordionListItemTableEditions = React.createClass({ let AccordionListItemTableEditions = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
parentId: React.PropTypes.number, parentId: React.PropTypes.number
numOfEditions: React.PropTypes.number,
show: React.PropTypes.bool
}, },
getInitialState() { getInitialState() {
@ -64,8 +59,13 @@ let AccordionListItemTableEditions = React.createClass({
}, },
toggleTable() { toggleTable() {
PieceListActions.showEditionList(this.props.parentId); let isEditionListOpen = this.state.isEditionListOpenForPieceId[this.props.parentId] ? this.state.isEditionListOpenForPieceId[this.props.parentId].show : false;
EditionListActions.fetchEditionList(this.props.parentId); if(isEditionListOpen) {
EditionListActions.toggleEditionList(this.props.parentId);
} else {
EditionListActions.toggleEditionList(this.props.parentId);
EditionListActions.fetchEditionList(this.props.parentId);
}
}, },
changeEditionListOrder(orderBy, orderAsc) { changeEditionListOrder(orderBy, orderAsc) {
@ -77,6 +77,7 @@ let AccordionListItemTableEditions = React.createClass({
let allEditionsCount = 0; let allEditionsCount = 0;
let orderBy; let orderBy;
let orderAsc; let orderAsc;
let show;
// here we need to check if all editions of a specific // here we need to check if all editions of a specific
// piece are already defined. Otherwise .length will throw an error and we'll not // piece are already defined. Otherwise .length will throw an error and we'll not
@ -88,7 +89,11 @@ let AccordionListItemTableEditions = React.createClass({
orderAsc = this.state.editionList[this.props.parentId].orderAsc; orderAsc = this.state.editionList[this.props.parentId].orderAsc;
} }
let transition = new TransitionModel('edition', 'editionId', 'bitcoin_id', PieceListActions.closeAllEditionLists); if(this.props.parentId in this.state.isEditionListOpenForPieceId) {
show = this.state.isEditionListOpenForPieceId[this.props.parentId].show;
}
let transition = new TransitionModel('edition', 'editionId', 'bitcoin_id');
let columnList = [ let columnList = [
new ColumnModel( new ColumnModel(
@ -153,16 +158,14 @@ let AccordionListItemTableEditions = React.createClass({
parentId={this.props.parentId} parentId={this.props.parentId}
itemList={this.state.editionList[this.props.parentId]} itemList={this.state.editionList[this.props.parentId]}
columnList={columnList} columnList={columnList}
numOfTableItems={this.props.numOfEditions} show={show}
show={this.props.show}
orderBy={orderBy} orderBy={orderBy}
orderAsc={orderAsc} orderAsc={orderAsc}
changeOrder={this.changeEditionListOrder}> changeOrder={this.changeEditionListOrder}>
<AccordionListItemTableToggle <AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle" className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable} onClick={this.toggleTable}
show={this.props.show} show={show} />
numOfTableItems={this.props.numOfEditions} />
</AccordionListItemTable> </AccordionListItemTable>
</div> </div>

View File

@ -6,8 +6,7 @@ let AccordionListItemTableToggle = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
show: React.PropTypes.bool, show: React.PropTypes.bool
numOfTableItems: React.PropTypes.number
}, },
render() { render() {
@ -15,7 +14,7 @@ let AccordionListItemTableToggle = React.createClass({
<span <span
className={this.props.className} className={this.props.className}
onClick={this.props.onClick}> onClick={this.props.onClick}>
{this.props.show ? 'Hide all ' + this.props.numOfTableItems + ' Editions' : 'Show all ' + this.props.numOfTableItems + ' Editions'} {this.props.show ? 'Hide all Editions' : 'Show all Editions'}
</span> </span>
); );
} }

View File

@ -2,6 +2,8 @@
import React from 'react'; import React from 'react';
import AppConstants from '../../constants/application_constants';
let ButtonSubmitOrClose = React.createClass({ let ButtonSubmitOrClose = React.createClass({
propTypes: { propTypes: {
submitted: React.PropTypes.bool.isRequired, submitted: React.PropTypes.bool.isRequired,
@ -13,7 +15,7 @@ let ButtonSubmitOrClose = React.createClass({
if (this.props.submitted){ if (this.props.submitted){
return ( return (
<div className="modal-footer"> <div className="modal-footer">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" /> <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div> </div>
); );
} }

View File

@ -23,12 +23,13 @@ let DeleteButton = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],
showNotification(response){ showNotification(response) {
this.props.editions this.props.editions
.forEach((edition) => { .forEach((edition) => {
EditionListActions.fetchEditionList(edition.parent); EditionListActions.fetchEditionList(edition.parent);
}); });
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
EditionListActions.closeAllEditionLists();
this.transitionTo('pieces'); this.transitionTo('pieces');
let notification = new GlobalNotificationModel(response.notification, 'success'); let notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
@ -45,7 +46,7 @@ let DeleteButton = React.createClass({
} }
else if (availableAcls.indexOf('del_from_collection') > -1){ else if (availableAcls.indexOf('del_from_collection') > -1){
content = <EditionRemoveFromCollectionForm editions={ this.props.editions }/>; content = <EditionRemoveFromCollectionForm editions={ this.props.editions }/>;
btnDelete = <Button bsStyle="danger" bsSize="small">REMOVE FROM LIST</Button>; btnDelete = <Button bsStyle="danger" bsSize="small">REMOVE FROM COLLECTION</Button>;
} }
else{ else{
return null; return null;

View File

@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import fetch from '../../utils/fetch'; import requests from '../../utils/requests';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin'; import FormMixin from '../../mixins/form_mixin';
@ -11,7 +11,7 @@ let EditionDeleteForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return fetch.prepareUrl(ApiUrls.edition_delete, {edition_id: this.getBitcoinIds().join()}); return requests.prepareUrl(ApiUrls.edition_delete, {edition_id: this.getBitcoinIds().join()});
}, },
httpVerb(){ httpVerb(){
return 'delete'; return 'delete';
@ -32,4 +32,4 @@ let EditionDeleteForm = React.createClass({
}); });
export default EditionDeleteForm; export default EditionDeleteForm;

View File

@ -52,7 +52,7 @@ let LoanForm = React.createClass({
loaneeHasContract: true loaneeHasContract: true
}); });
} }
else{ else {
this.resetLoanContract(); this.resetLoanContract();
} }
}) })
@ -63,10 +63,11 @@ let LoanForm = React.createClass({
}, },
resetLoanContract(){ resetLoanContract(){
this.setState({contract_key: null, this.setState({
contract_url: null, contract_key: null,
loaneeHasContract: false contract_url: null,
}); loaneeHasContract: false
});
}, },
renderForm() { renderForm() {

View File

@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import fetch from '../../utils/fetch'; import requests from '../../utils/requests';
import apiUrls from '../../constants/api_urls'; import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin'; import FormMixin from '../../mixins/form_mixin';
@ -14,7 +14,7 @@ let PieceExtraDataForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return fetch.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.editions[0].bitcoin_id}); return requests.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.editions[0].bitcoin_id});
}, },
getFormData() { getFormData() {
@ -48,4 +48,4 @@ let PieceExtraDataForm = React.createClass({
} }
}); });
export default PieceExtraDataForm; export default PieceExtraDataForm;

View File

@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import fetch from '../../utils/fetch'; import requests from '../../utils/requests';
import apiUrls from '../../constants/api_urls'; import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin'; import FormMixin from '../../mixins/form_mixin';
@ -11,7 +11,7 @@ let EditionRemoveFromCollectionForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return fetch.prepareUrl(apiUrls.edition_remove_from_collection, {edition_id: this.getBitcoinIds().join()}); return requests.prepareUrl(apiUrls.edition_remove_from_collection, {edition_id: this.getBitcoinIds().join()});
}, },
httpVerb(){ httpVerb(){
return 'delete'; return 'delete';
@ -32,4 +32,4 @@ let EditionRemoveFromCollectionForm = React.createClass({
}); });
export default EditionRemoveFromCollectionForm; export default EditionRemoveFromCollectionForm;

View File

@ -7,6 +7,8 @@ import Alert from 'react-bootstrap/lib/Alert';
import apiUrls from '../../constants/api_urls'; import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin'; import FormMixin from '../../mixins/form_mixin';
import AppConstants from '../../constants/application_constants';
let RequestActionForm = React.createClass({ let RequestActionForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
@ -62,7 +64,7 @@ let RequestActionForm = React.createClass({
if (this.state.submitted){ if (this.state.submitted){
buttons = ( buttons = (
<span> <span>
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_small.gif" /> <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</span> </span>
); );
} }

View File

@ -9,7 +9,6 @@ let Pagination = React.createClass({
goToPage: React.PropTypes.func.isRequired, goToPage: React.PropTypes.func.isRequired,
currentPage: React.PropTypes.number.isRequired, currentPage: React.PropTypes.number.isRequired,
totalPages: React.PropTypes.number.isRequired totalPages: React.PropTypes.number.isRequired
//itemListCount: React.PropTypes.number.isRequired
}, },
render() { render() {

View File

@ -65,6 +65,7 @@ let PieceListBulkModal = React.createClass({
clearAllSelections() { clearAllSelections() {
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
EditionListActions.closeAllEditionLists();
}, },
handleSuccess() { handleSuccess() {

View File

@ -2,37 +2,19 @@
import React from 'react'; import React from 'react';
import PieceListStore from '../../stores/piece_list_store';
import PieceListActions from '../../actions/piece_list_actions';
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';
let PieceListToolbar = React.createClass({ let PieceListToolbar = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string className: React.PropTypes.string,
}, searchFor: React.PropTypes.func
getInitialState() {
return PieceListStore.getState();
},
componentDidMount() {
PieceListStore.listen(this.onChange);
},
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
searchFor() { searchFor() {
let searchTerm = this.refs.search.getInputDOMNode().value; let searchTerm = this.refs.search.getInputDOMNode().value;
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc); this.props.searchFor(searchTerm);
}, },
render() { render() {

View File

@ -20,7 +20,6 @@ let Table = React.createClass({
}, },
renderChildren() { renderChildren() {
var that = this;
return ReactAddons.Children.map(this.props.children, (child, i) => { return ReactAddons.Children.map(this.props.children, (child, i) => {
return ReactAddons.addons.cloneWithProps(child, { return ReactAddons.addons.cloneWithProps(child, {
columnList: this.props.columnList, columnList: this.props.columnList,

View File

@ -17,7 +17,9 @@ let TableItemCheckbox = React.createClass({
render() { render() {
return ( return (
<input type="checkbox" onChange={this.selectItem} checked={this.props.selected}/> <span>
<input type="checkbox" onChange={this.selectItem} checked={this.props.selected}/>
</span>
); );
} }
}); });

View File

@ -4,7 +4,6 @@ import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import { ColumnModel } from './models/table_models'; import { ColumnModel } from './models/table_models';
import TableColumnMixin from '../../mixins/table_column_mixin';
let Link = Router.Link; let Link = Router.Link;
@ -15,7 +14,7 @@ let TableItemWrapper = React.createClass({
columnWidth: React.PropTypes.number.isRequired columnWidth: React.PropTypes.number.isRequired
}, },
mixins: [TableColumnMixin, Router.Navigation], mixins: [Router.Navigation],
render() { render() {
return ( return (
@ -25,8 +24,6 @@ let TableItemWrapper = React.createClass({
let TypeElement = column.displayType; let TypeElement = column.displayType;
let typeElementProps = column.transformFn(this.props.columnContent); let typeElementProps = column.transformFn(this.props.columnContent);
let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth);
if(!column.transition) { if(!column.transition) {
return ( return (
<td <td

View File

@ -73,7 +73,7 @@ let Header = React.createClass({
return ( return (
<Navbar> <Navbar>
<Nav> <Nav>
<Link className="navbar-brand" to="pieces"> <Link className="navbar-brand" to="pieces" path="/?page=1">
<span>ascribe </span> <span>ascribe </span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span> <span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</Link> </Link>

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
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';
@ -14,12 +15,16 @@ import Pagination from './ascribe_pagination/pagination';
import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal'; import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal';
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar'; import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
import AppConstants from '../constants/application_constants';
let PieceList = React.createClass({ let PieceList = React.createClass({
propTypes: { propTypes: {
query: React.PropTypes.object query: React.PropTypes.object
}, },
mixins: [Router.Navigation, Router.State],
getInitialState() { getInitialState() {
return PieceListStore.getState(); return PieceListStore.getState();
}, },
@ -30,7 +35,6 @@ let PieceList = React.createClass({
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, this.state.orderBy, this.state.orderAsc);
} }
}, },
componentWillUnmount() { componentWillUnmount() {
@ -47,6 +51,11 @@ let PieceList = React.createClass({
this.state.orderAsc); this.state.orderAsc);
}, },
searchFor(searchTerm) {
PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc);
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, orderBy, orderAsc); this.state.search, orderBy, orderAsc);
@ -55,10 +64,13 @@ let PieceList = React.createClass({
render() { render() {
let currentPage = parseInt(this.props.query.page, 10) || 1; let currentPage = parseInt(this.props.query.page, 10) || 1;
let totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize); let totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize);
let loadingElement = (<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />);
return ( return (
<div> <div>
<PieceListToolbar className="ascribe-piece-list-toolbar" /> <PieceListToolbar
className="ascribe-piece-list-toolbar"
searchFor={this.searchFor} />
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" /> <PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
<AccordionList <AccordionList
className="ascribe-accordion-list" className="ascribe-accordion-list"
@ -68,7 +80,8 @@ let PieceList = React.createClass({
orderAsc={this.state.orderAsc} orderAsc={this.state.orderAsc}
search={this.state.search} search={this.state.search}
page={this.state.page} page={this.state.page}
pageSize={this.state.pageSize}> pageSize={this.state.pageSize}
loadingElement={loadingElement}>
{this.state.pieceList.map((item, i) => { {this.state.pieceList.map((item, i) => {
return ( return (
<AccordionListItem <AccordionListItem
@ -77,9 +90,7 @@ let PieceList = React.createClass({
key={i}> key={i}>
<AccordionListItemTableEditions <AccordionListItemTableEditions
className="ascribe-accordion-list-item-table col-xs-12 col-sm-8 col-md-6 col-lg-6 col-sm-offset-2 col-md-offset-3 col-lg-offset-3" className="ascribe-accordion-list-item-table col-xs-12 col-sm-8 col-md-6 col-lg-6 col-sm-offset-2 col-md-offset-3 col-lg-offset-3"
parentId={item.id} parentId={item.id} />
show={item.show}
numOfEditions={item.num_editions}/>
</AccordionListItem> </AccordionListItem>
); );
})} })}

View File

@ -3,31 +3,31 @@
import AppConstants from './application_constants'; import AppConstants from './application_constants';
let apiUrls = { let apiUrls = {
'piece': AppConstants.apiEndpoint + 'pieces/${piece_id}',
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/', 'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/',
'editions_list': AppConstants.apiEndpoint + 'pieces/${piece_id}/editions/',
'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/', 'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/',
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/', 'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/',
'ownership_shares_mail': AppConstants.apiEndpoint + 'ownership/shares/mail/', 'editions_list': AppConstants.apiEndpoint + 'pieces/${piece_id}/editions/',
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/', 'note_notes': AppConstants.apiEndpoint + 'note/notes/',
'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/', 'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/',
'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/', 'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/',
'ownership_consigns_deny': AppConstants.apiEndpoint + 'ownership/consigns/deny/', 'ownership_consigns_deny': AppConstants.apiEndpoint + 'ownership/consigns/deny/',
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/',
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
'ownership_loans': AppConstants.apiEndpoint + 'ownership/loans/', 'ownership_loans': AppConstants.apiEndpoint + 'ownership/loans/',
'ownership_loans_confirm': AppConstants.apiEndpoint + 'ownership/loans/confirm/', 'ownership_loans_confirm': AppConstants.apiEndpoint + 'ownership/loans/confirm/',
'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/', 'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/',
'note_notes': AppConstants.apiEndpoint + 'note/notes/', 'ownership_shares_mail': AppConstants.apiEndpoint + 'ownership/shares/mail/',
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/',
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/',
'piece': AppConstants.apiEndpoint + 'pieces/${piece_id}',
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
'user': AppConstants.apiEndpoint + 'users/', 'user': AppConstants.apiEndpoint + 'users/',
'users_login': AppConstants.apiEndpoint + 'users/login/', 'users_login': AppConstants.apiEndpoint + 'users/login/',
'users_logout': AppConstants.apiEndpoint + 'users/logout/', 'users_logout': AppConstants.apiEndpoint + 'users/logout/',
'users_signup': AppConstants.apiEndpoint + 'users/', 'users_password_reset': AppConstants.apiEndpoint + 'users/reset_password/',
'users_password_reset_request': AppConstants.apiEndpoint + 'users/request_reset_password/', 'users_password_reset_request': AppConstants.apiEndpoint + 'users/request_reset_password/',
'users_password_reset': AppConstants.apiEndpoint + 'users/reset_password/' 'users_signup': AppConstants.apiEndpoint + 'users/'
}; };
export default apiUrls; export default apiUrls;

View File

@ -8,9 +8,8 @@ let constants = {
//'baseUrl': window.BASE_URL, //'baseUrl': window.BASE_URL,
'apiEndpoint': window.API_ENDPOINT, 'apiEndpoint': window.API_ENDPOINT,
'baseUrl': window.BASE_URL, 'baseUrl': window.BASE_URL,
//'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw', // dimi@mailinator:0000000000
'aclList': ['edit', 'consign', 'consign_request', 'unconsign', 'unconsign_request', 'transfer', 'aclList': ['edit', 'consign', 'consign_request', 'unconsign', 'unconsign_request', 'transfer',
'loan', 'loan_request', 'share', 'download', 'view', 'delete', 'del_from_collection', 'add_to_collection'] 'loan', 'loan_request', 'share', 'download', 'view', 'delete', 'del_from_collection', 'add_to_collection']
}; };
export default constants; export default constants;

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
let EditionFetcher = { let EditionFetcher = {
/** /**
@ -8,7 +8,7 @@ let EditionFetcher = {
* If no arg is supplied, load the current user * If no arg is supplied, load the current user
*/ */
fetchOne(editionId) { fetchOne(editionId) {
return fetch.get('edition', {'bitcoin_id': editionId}); return requests.get('edition', {'bitcoin_id': editionId});
} }
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
@ -11,7 +11,7 @@ let EditionListFetcher = {
*/ */
fetch(pieceId, orderBy, orderAsc) { fetch(pieceId, orderBy, orderAsc) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return fetch.get('editions_list', { 'piece_id': pieceId, ordering }); return requests.get('editions_list', { 'piece_id': pieceId, ordering });
} }
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
let PieceFetcher = { let PieceFetcher = {
@ -9,7 +9,7 @@ let PieceFetcher = {
* If no arg is supplied, load the current user * If no arg is supplied, load the current user
*/ */
fetchOne() { fetchOne() {
return fetch.get('piece'); return requests.get('piece');
} }
}; };

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
let PieceListFetcher = { let PieceListFetcher = {
@ -11,7 +11,7 @@ let PieceListFetcher = {
*/ */
fetch(page, pageSize, search, orderBy, orderAsc) { fetch(page, pageSize, search, orderBy, orderAsc) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return fetch.get('pieces_list', { page, pageSize, search, ordering }); return requests.get('pieces_list', { page, pageSize, search, ordering });
} }
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
let UserFetcher = { let UserFetcher = {
@ -9,7 +9,7 @@ let UserFetcher = {
* If no arg is supplied, load the current user * If no arg is supplied, load the current user
*/ */
fetchOne() { fetchOne() {
return fetch.get('user'); return requests.get('user');
} }
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import fetch from '../utils/fetch'; import requests from '../utils/requests';
import React from 'react'; import React from 'react';
import AlertDismissable from '../components/ascribe_forms/alert'; import AlertDismissable from '../components/ascribe_forms/alert';
@ -29,14 +29,14 @@ export const FormMixin = {
}, },
post(e){ post(e){
fetch requests
.post(this.url(e), { body: this.getFormData() }) .post(this.url(e), { body: this.getFormData() })
.then(this.handleSuccess) .then(this.handleSuccess)
.catch(this.handleError); .catch(this.handleError);
}, },
delete(e){ delete(e){
fetch requests
.delete(this.url(e)) .delete(this.url(e))
.then(this.handleSuccess) .then(this.handleSuccess)
.catch(this.handleError); .catch(this.handleError);

View File

@ -8,6 +8,7 @@ import EditionsListActions from '../actions/edition_list_actions';
class EditionListStore { class EditionListStore {
constructor() { constructor() {
this.editionList = {}; this.editionList = {};
this.isEditionListOpenForPieceId = {};
this.bindActions(EditionsListActions); this.bindActions(EditionsListActions);
} }
@ -16,15 +17,16 @@ class EditionListStore {
this.editionList[pieceId].forEach((edition, i) => { this.editionList[pieceId].forEach((edition, i) => {
// This uses the index of the new editionList for determining the edition. // This uses the index of the new editionList for determining the edition.
// If the list of editions can be sorted in the future, this needs to be changed! // If the list of editions can be sorted in the future, this needs to be changed!
if (editionListOfPiece[i]){ if (editionListOfPiece[i]) {
editionListOfPiece[i] = React.addons.update(edition, {$merge: editionListOfPiece[i]}); editionListOfPiece[i] = React.addons.update(edition, {$merge: editionListOfPiece[i]});
} }
}); });
} }
this.editionList[pieceId] = editionListOfPiece; this.editionList[pieceId] = editionListOfPiece;
/** /**
* orderBy and orderAsc are specific to a single list of editons * orderBy and orderAsc are specific to a single list of editions
* therefore they need to be saved in relation to their parent-piece. * therefore they need to be saved in relation to their parent-piece.
* *
* Default values for both are set in the editon_list-actions. * Default values for both are set in the editon_list-actions.
@ -36,7 +38,7 @@ class EditionListStore {
onSelectEdition({pieceId, editionId, toValue}) { onSelectEdition({pieceId, editionId, toValue}) {
this.editionList[pieceId].forEach((edition) => { this.editionList[pieceId].forEach((edition) => {
// http://stackoverflow.com/a/519157/1263876 // Taken from: http://stackoverflow.com/a/519157/1263876
if(typeof toValue !== 'undefined' && edition.id === editionId) { if(typeof toValue !== 'undefined' && edition.id === editionId) {
edition.selected = toValue; edition.selected = toValue;
} else if(edition.id === editionId) { } else if(edition.id === editionId) {
@ -57,12 +59,20 @@ class EditionListStore {
.forEach((edition) => { .forEach((edition) => {
try { try {
delete edition.selected; delete edition.selected;
} catch(err) { } catch(err) {/* ignore and keep going */}
//just ignore
}
}); });
}); });
} }
onToggleEditionList(pieceId) {
this.isEditionListOpenForPieceId[pieceId] = {
show: this.isEditionListOpenForPieceId[pieceId] ? !this.isEditionListOpenForPieceId[pieceId].show : true
};
}
onCloseAllEditionLists() {
this.isEditionListOpenForPieceId = {};
}
} }
export default alt.createStore(EditionListStore, 'EditionListStore'); export default alt.createStore(EditionListStore, 'EditionListStore');

View File

@ -1,5 +1,6 @@
'use strict'; 'use strict';
import React from 'react';
import alt from '../alt'; import alt from '../alt';
import PieceListActions from '../actions/piece_list_actions'; import PieceListActions from '../actions/piece_list_actions';
@ -27,7 +28,7 @@ class PieceListStore {
this.bindActions(PieceListActions); this.bindActions(PieceListActions);
} }
onShowEditionList(pieceId) { /*onShowEditionList(pieceId) {
this.pieceList this.pieceList
.forEach((piece) => { .forEach((piece) => {
if(piece.id === pieceId) { if(piece.id === pieceId) {
@ -38,14 +39,14 @@ class PieceListStore {
} }
} }
}); });
} }*/
onCloseAllEditionLists() { /*onCloseAllEditionLists() {
this.pieceList this.pieceList
.forEach((piece) => { .forEach((piece) => {
piece.show = false; piece.show = false;
}); });
} }*/
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) { onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) {
this.page = page; this.page = page;
@ -72,6 +73,16 @@ class PieceListStore {
* We did not implement this, as we're going to add pagination to pieceList at some * We did not implement this, as we're going to add pagination to pieceList at some
* point anyway. Then, this problem is automatically resolved. * point anyway. Then, this problem is automatically resolved.
*/ */
pieceList.forEach((piece, i) => {
let oldPiece = this.pieceList[i];
if(oldPiece) {
piece = React.addons.update(piece, {
show: { $set: oldPiece.show }
});
}
});
this.pieceList = pieceList; this.pieceList = pieceList;
} }
} }

View File

@ -1,7 +1,5 @@
'use strict'; 'use strict';
import { default as _fetch } from 'isomorphic-fetch';
import { argsToQueryParams, getCookie } from '../utils/fetch_api_utils'; import { argsToQueryParams, getCookie } from '../utils/fetch_api_utils';
@ -10,7 +8,7 @@ class ServerError extends Error {}
class APIError extends Error {} class APIError extends Error {}
class Fetch { class Requests {
_merge(defaults, options) { _merge(defaults, options) {
let merged = {}; let merged = {};
for (let key in defaults) { for (let key in defaults) {
@ -80,7 +78,7 @@ class Fetch {
let merged = this._merge(this.httpOptions, options); let merged = this._merge(this.httpOptions, options);
this.httpOptions.headers['X-CSRFToken'] = getCookie('csrftoken'); this.httpOptions.headers['X-CSRFToken'] = getCookie('csrftoken');
merged.method = verb; merged.method = verb;
return _fetch(url, merged) return fetch(url, merged)
.then(this.unpackResponse) .then(this.unpackResponse)
.then(JSON.parse) .then(JSON.parse)
.catch(this.handleFatalError.bind(this)) .catch(this.handleFatalError.bind(this))
@ -107,7 +105,6 @@ class Fetch {
if (paramsCopy && paramsCopy.body) { if (paramsCopy && paramsCopy.body) {
body = JSON.stringify(paramsCopy.body); body = JSON.stringify(paramsCopy.body);
} }
return this.request('post', newUrl, { body }); return this.request('post', newUrl, { body });
} }
@ -119,6 +116,6 @@ class Fetch {
} }
let fetch = new Fetch(); let requests = new Requests();
export default fetch; export default requests;

View File

@ -39,6 +39,16 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
} }
} }
.ascribe-accordion-list-loading {
padding-top: 30%;
padding-bottom: 30%;
}
.ascribe-accordion-list-loading img {
display: block;
margin: auto;
}
.ascribe-accordion-list-item-table { .ascribe-accordion-list-item-table {
text-align: center; text-align: center;
margin-bottom: 3em; margin-bottom: 3em;

44
sass/ascribe_table.scss Normal file
View File

@ -0,0 +1,44 @@
.ascribe-table {
margin-bottom:0;
}
/*This is aligning the first checkbox in pieclist detail with all the other ones*/
.table > thead:first-child > tr:first-child > th {
padding-left:0;
}
.ascribe-table-header-column > span {
display: table-cell;
vertical-align: middle;
font-family: 'Source Sans Pro';
font-weight: 600;
color: #424242;
}
.ascribe-table-item-column {
display: table;
font-family: 'Source Sans Pro';
font-size: .8em;
height:3em;
}
.ascribe-table-item-column > * {
display: table-cell;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ascribe-table-item-column > span > input {
margin-top:14px;
}
.ascribe-table-item-selected {
background-color: rgba(2, 182, 163, 0.5);
}
.ascribe-table-item-selectable {
cursor: default;
}

View File

@ -9,6 +9,7 @@ $BASE_URL: '<%= BASE_URL %>';
@import '../node_modules/react-datepicker/dist/react-datepicker'; @import '../node_modules/react-datepicker/dist/react-datepicker';
@import './ascribe-fonts/style'; @import './ascribe-fonts/style';
@import './ascribe-fonts/ascribe-fonts'; @import './ascribe-fonts/ascribe-fonts';
@import 'ascribe_table';
@import 'ascribe_accordion_list'; @import 'ascribe_accordion_list';
@import 'ascribe_piece_list_bulk_modal'; @import 'ascribe_piece_list_bulk_modal';
@import 'ascribe_piece_list_toolbar'; @import 'ascribe_piece_list_toolbar';
@ -49,45 +50,6 @@ $BASE_URL: '<%= BASE_URL %>';
float: none; float: none;
} }
.ascribe-table {
margin-bottom:0;
}
.ascribe-table-header-column > span {
display: table-cell;
vertical-align: middle;
font-family: 'Source Sans Pro';
font-weight: 600;
color: #424242;
}
.ascribe-table-header-column > span > .glyphicon {
font-size: .5em;
}
.ascribe-table-item-column {
display: table;
font-family: 'Source Sans Pro';
font-size: .8em;
height:3em;
}
.ascribe-table-item-column > * {
display: table-cell;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ascribe-table-item-selected {
background-color: rgba(2, 182, 163, 0.5);
}
.ascribe-table-item-selectable {
cursor: default;
}
.no-margin { .no-margin {
margin-right: 0; margin-right: 0;
margin-left: 0; margin-left: 0;
@ -100,7 +62,6 @@ $BASE_URL: '<%= BASE_URL %>';
margin-left: 0 !important; margin-left: 0 !important;
font-family: sans-serif !important; font-family: sans-serif !important;
border-radius: 0 !important; border-radius: 0 !important;
} }
.btn-ascribe, .btn-ascribe-inv:active, .btn-ascribe-inv:hover { .btn-ascribe, .btn-ascribe-inv:active, .btn-ascribe-inv:hover {
@ -130,7 +91,6 @@ $BASE_URL: '<%= BASE_URL %>';
margin-left: 0 !important; margin-left: 0 !important;
font-family: sans-serif !important; font-family: sans-serif !important;
border-radius: 0 !important; border-radius: 0 !important;
} }
.btn-ascribe-green, .btn-ascribe-green-inv:active, .btn-ascribe-green-inv:hover { .btn-ascribe-green, .btn-ascribe-green-inv:active, .btn-ascribe-green-inv:hover {
@ -149,7 +109,6 @@ $BASE_URL: '<%= BASE_URL %>';
margin-top: 2em; margin-top: 2em;
} }
.ascribe-detail-title { .ascribe-detail-title {
font-size: 2em; font-size: 2em;
margin-bottom: -0.2em; margin-bottom: -0.2em;

View File

@ -9,6 +9,7 @@ var app = express();
app.use(compression()); app.use(compression());
app.use(baseUrl + 'static/js', express.static(__dirname + '/build/js')); app.use(baseUrl + 'static/js', express.static(__dirname + '/build/js'));
app.use(baseUrl + 'static/img', express.static(__dirname + '/build/img'));
app.use(baseUrl + 'static/css', express.static(__dirname + '/build/css')); app.use(baseUrl + 'static/css', express.static(__dirname + '/build/css'));
app.use(baseUrl + 'static/fonts', express.static(__dirname + '/build/fonts')); app.use(baseUrl + 'static/fonts', express.static(__dirname + '/build/fonts'));
app.use(baseUrl + 'static/thirdparty/', express.static(__dirname + '/node_modules')); app.use(baseUrl + 'static/thirdparty/', express.static(__dirname + '/node_modules'));