1
0
mirror of https://github.com/ascribe/onion.git synced 2025-02-14 21:10:27 +01:00

Merge branch 'master' into AD-412-mediaplayer

Conflicts:
	js/components/ascribe_media/resource_viewer.js
	js/components/edition_container.js
	js/mixins/inject_in_head_mixin.js
	package.json
This commit is contained in:
vrde 2015-06-05 13:18:16 +02:00
commit e8627cd0a3
90 changed files with 818 additions and 446 deletions

54
.eslintrc Normal file
View File

@ -0,0 +1,54 @@
{
"parser": "babel-eslint",
"env": {
"browser": true,
"es6": true
},
"rules": {
"quotes": [2, "single"],
"eol-last": [0],
"no-mixed-requires": [0],
"no-underscore-dangle": [0],
"global-strict": [2, "always"],
"no-trailing-spaces": [2, { skipBlankLines: true }],
"no-console": 0,
"camelcase": [2, {"properties": "never"}],
"react/display-name": 0,
"react/jsx-boolean-value": 1,
"react/jsx-no-undef": 1,
"react/jsx-quotes": 1,
"react/jsx-sort-prop-types": 0,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-multi-comp": 0,
"react/no-unknown-property": 1,
"react/prop-types": 1,
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1
},
"plugins": [
"react"
],
"ecmaFeatures": {
"jsx": 1,
"modules": 1,
"arrowFunctions",
"classes": 1,
"blockBindings": 1,
"defaultParams": 1,
"destructuring": 1,
"objectLiteralComputedProperties": 1,
"objectLiteralDuplicateProperties": 0,
"objectLiteralShorthandMethods": 1,
"objectLiteralShorthandProperties": 1,
"restParams": 1,
"spread": 1,
"superInFunctions": 1,
"templateStrings": 1
}
}

View File

@ -1,3 +1,5 @@
'use strict';
var gulp = require('gulp'); var gulp = require('gulp');
var gulpif = require('gulp-if'); var gulpif = require('gulp-if');
var sourcemaps = require('gulp-sourcemaps'); var sourcemaps = require('gulp-sourcemaps');
@ -12,6 +14,7 @@ var notify = require('gulp-notify');
var sass = require('gulp-sass'); var sass = require('gulp-sass');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var _ = require('lodash'); var _ = require('lodash');
var eslint = require('gulp-eslint');
var config = { var config = {
bootstrapDir: './node_modules/bootstrap-sass' bootstrapDir: './node_modules/bootstrap-sass'
@ -21,7 +24,7 @@ gulp.task('build', function() {
bundle(false); bundle(false);
}); });
gulp.task('serve', ['browser-sync', 'sass', 'sass:watch', 'copy'], function() { gulp.task('serve', ['browser-sync', 'lint:watch', 'sass', 'sass:watch', 'copy'], function() {
bundle(true); bundle(true);
}); });
@ -64,6 +67,23 @@ gulp.task('copy', function () {
.pipe(gulp.dest('./build/fonts')); .pipe(gulp.dest('./build/fonts'));
}); });
gulp.task('lint', function () {
return gulp.src(['js/**/*.js'])
// eslint() attaches the lint output to the eslint property
// of the file object so it can be used by other modules.
.pipe(eslint())
// eslint.format() outputs the lint results to the console.
// Alternatively use eslint.formatEach() (see Docs).
.pipe(eslint.format())
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failOnError last.
.pipe(eslint.failOnError());
});
gulp.task('lint:watch', function () {
gulp.watch('js/**/*.js', ['lint']);
});
function bundle(watch) { function bundle(watch) {
var bro; var bro;

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import EditionFetcher from '../fetchers/edition_fetcher'; import EditionFetcher from '../fetchers/edition_fetcher';

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import EditionListFetcher from '../fetchers/edition_list_fetcher.js'; import EditionListFetcher from '../fetchers/edition_list_fetcher.js';
@ -12,6 +14,11 @@ class EditionListActions {
} }
fetchEditionList(pieceId, orderBy, orderAsc) { fetchEditionList(pieceId, orderBy, orderAsc) {
if(!orderBy && typeof orderAsc == 'undefined') {
orderBy = 'edition_number';
orderAsc = true;
}
EditionListFetcher EditionListFetcher
.fetch(pieceId, orderBy, orderAsc) .fetch(pieceId, orderBy, orderAsc)
.then((res) => { .then((res) => {

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import PieceFetcher from '../fetchers/piece_fetcher'; import PieceFetcher from '../fetchers/piece_fetcher';

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import PieceListFetcher from '../fetchers/piece_list_fetcher'; import PieceListFetcher from '../fetchers/piece_list_fetcher';
@ -7,7 +9,8 @@ class PieceListActions {
constructor() { constructor() {
this.generateActions( this.generateActions(
'updatePieceList', 'updatePieceList',
'showEditionList' 'showEditionList',
'closeAllEditionLists'
); );
} }
@ -27,6 +30,6 @@ class PieceListActions {
}); });
} }
}; }
export default alt.createActions(PieceListActions); export default alt.createActions(PieceListActions);

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import UserFetcher from '../fetchers/user_fetcher'; import UserFetcher from '../fetchers/user_fetcher';
@ -12,12 +14,12 @@ class UserActions {
fetchCurrentUser() { fetchCurrentUser() {
UserFetcher.fetchOne() UserFetcher.fetchOne()
.then((res) => { .then((res) => {
this.actions.updateCurrentUser(res['users'][0]); this.actions.updateCurrentUser(res.users[0]);
}) })
.catch((err) => { .catch((err) => {
console.log(err); console.log(err);
}); });
} }
}; }
export default alt.createActions(UserActions); export default alt.createActions(UserActions);

View File

@ -1,3 +1,5 @@
'use strict';
import Alt from 'alt'; import Alt from 'alt';
export default new Alt(); export default new Alt();

View File

@ -6,14 +6,31 @@ import promise from 'es6-promise';
promise.polyfill(); promise.polyfill();
import AscribeApp from './components/ascribe_app';
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 routes from './routes'; import routes from './routes';
import alt from './alt';
import fetch from './utils/fetch'; import fetch from './utils/fetch';
import AlertDismissable from './components/ascribe_forms/alert';
/*
Taken from
http://stackoverflow.com/questions/30613447/how-to-debug-reactjss-setstate?noredirect=1#comment49301874_30613447
<remove this in production>
*/
var warn = console.warn;
console.warn = function(warning) {
if (/(setState)/.test(warning)) {
throw new Error(warning);
}
warn.apply(console, arguments);
};
/*
</remove this in production>
*/
fetch.defaults({ fetch.defaults({
urlMap: ApiUrls, urlMap: ApiUrls,

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import ConsignForm from './ascribe_forms/form_consign'; import ConsignForm from './ascribe_forms/form_consign';
@ -17,31 +19,33 @@ let AclButton = React.createClass({
}, },
actionProperties(){ actionProperties(){
if (this.props.action == 'consign'){ if (this.props.action === 'consign'){
return { return {
title: "Consign artwork", title: 'Consign artwork',
tooltip: "Have someone else sell the artwork", tooltip: 'Have someone else sell the artwork',
form: <ConsignForm /> form: <ConsignForm />
};
} }
} else if (this.props.action === 'transfer') {
else if (this.props.action == 'transfer') {
return { return {
title: "Transfer artwork", title: 'Transfer artwork',
tooltip: "Transfer the ownership of the artwork", tooltip: 'Transfer the ownership of the artwork',
form: <TransferForm /> form: <TransferForm />
};
} }
} else if (this.props.action === 'loan'){
else if (this.props.action == 'loan'){
return { return {
title: "Loan artwork", title: 'Loan artwork',
tooltip: "Loan your artwork for a limited period of time", tooltip: 'Loan your artwork for a limited period of time',
form: <LoanForm />} form: <LoanForm />
};
} }
else if (this.props.action == 'share'){ else if (this.props.action === 'share'){
return { return {
title: "Share artwork", title: 'Share artwork',
tooltip: "Share the artwork", tooltip: 'Share the artwork',
form: <ShareForm />} form: <ShareForm />
};
} }
}, },
render() { render() {

View File

@ -1,5 +1,6 @@
'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons';
let AccordionList = React.createClass({ let AccordionList = React.createClass({

View File

@ -1,8 +1,8 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import AccordionListItemTable from './accordion_list_item_table';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link; let Link = Router.Link;
@ -10,7 +10,8 @@ let Link = Router.Link;
let AccordionListItem = React.createClass({ let AccordionListItem = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
content: React.PropTypes.object content: React.PropTypes.object,
children: React.PropTypes.object
}, },
render() { render() {
@ -18,12 +19,13 @@ let AccordionListItem = React.createClass({
<div className="row"> <div className="row">
<div className={this.props.className}> <div className={this.props.className}>
<div className="wrapper"> <div className="wrapper">
<div className="thumbnail-wrapper"> <div className="col-xs-4 col-sm-4 col-md-4 col-lg-4 thumbnail-wrapper">
<img src={this.props.content.thumbnail} /> <img src={this.props.content.thumbnail} />
</div> </div>
<div className="info-wrapper"> <div className="col-xs-8 col-sm-8 col-md-8 col-lg-8">
<h1>{this.props.content.title}</h1> <h1>{this.props.content.title}</h1>
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3> <h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
<h3>{this.props.content.date_created.split('-')[0]}</h3>
</div> </div>
<span style={{'clear': 'both'}}></span> <span style={{'clear': 'both'}}></span>
</div> </div>

View File

@ -1,18 +1,18 @@
'use strict';
import React from 'react'; import React from 'react';
import Table from '../ascribe_table/table'; import Table from '../ascribe_table/table';
import TableItem from '../ascribe_table/table_item'; import TableItem from '../ascribe_table/table_item';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from '../ascribe_table/models/table_models';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemTable = React.createClass({ let AccordionListItemTable = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
parentId: React.PropTypes.number, parentId: React.PropTypes.number,
itemList: React.PropTypes.array, itemList: React.PropTypes.array,
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
numOfTableItems: React.PropTypes.number, numOfTableItems: React.PropTypes.number,
show: React.PropTypes.bool, show: React.PropTypes.bool,
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,
@ -25,6 +25,7 @@ let AccordionListItemTable = React.createClass({
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
<Table <Table
className="ascribe-table"
columnList={this.props.columnList} columnList={this.props.columnList}
itemList={this.props.itemList} itemList={this.props.itemList}
changeOrder={this.props.changeOrder} changeOrder={this.props.changeOrder}
@ -34,8 +35,7 @@ let AccordionListItemTable = React.createClass({
return ( return (
<TableItem <TableItem
className="ascribe-table-item-selectable" className="ascribe-table-item-selectable"
key={i}> key={i} />
</TableItem>
); );
})} })}
</Table> </Table>

View File

@ -1,18 +1,18 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; 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';
import PieceListStore from '../../stores/piece_list_store';
import PieceListActions from '../../actions/piece_list_actions'; import PieceListActions from '../../actions/piece_list_actions';
import AccordionListItemTable from './accordion_list_item_table'; import AccordionListItemTable from './accordion_list_item_table';
import AccordionListItemTableToggle from './accordion_list_item_table_toggle'; import AccordionListItemTableToggle from './accordion_list_item_table_toggle';
import AccordionListItemTableSelectAllEditionsToggle from './accordion_list_item_table_select_all_editions_toggle'; import AccordionListItemTableSelectAllEditionsToggle from './accordion_list_item_table_select_all_editions_toggle';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel, TransitionModel } from '../ascribe_table/models/table_models';
import TableItemImg from '../ascribe_table/table_item_img';
import TableItemText from '../ascribe_table/table_item_text'; import TableItemText from '../ascribe_table/table_item_text';
import TableItemCheckbox from '../ascribe_table/table_item_checkbox'; import TableItemCheckbox from '../ascribe_table/table_item_checkbox';
import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered'; import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
@ -34,18 +34,18 @@ let AccordionListItemTableEditions = React.createClass({
return EditionListStore.getState(); return EditionListStore.getState();
}, },
onChange(state) {
this.setState(state);
},
componentDidMount() { componentDidMount() {
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
}, },
componentDidUnmount() { componentWillUnmount() {
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
}, },
onChange(state) {
this.setState(state);
},
selectItem(pieceId, editionId) { selectItem(pieceId, editionId) {
EditionListActions.selectEdition({pieceId, editionId}); EditionListActions.selectEdition({pieceId, editionId});
}, },
@ -65,7 +65,7 @@ let AccordionListItemTableEditions = React.createClass({
toggleTable() { toggleTable() {
PieceListActions.showEditionList(this.props.parentId); PieceListActions.showEditionList(this.props.parentId);
EditionListActions.fetchEditionList(this.props.parentId, this.state.orderBy, this.state.orderAsc); EditionListActions.fetchEditionList(this.props.parentId);
}, },
changeEditionListOrder(orderBy, orderAsc) { changeEditionListOrder(orderBy, orderAsc) {
@ -75,6 +75,8 @@ let AccordionListItemTableEditions = React.createClass({
render() { render() {
let selectedEditionsCount = 0; let selectedEditionsCount = 0;
let allEditionsCount = 0; let allEditionsCount = 0;
let orderBy;
let orderAsc;
// 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
@ -82,17 +84,21 @@ let AccordionListItemTableEditions = React.createClass({
if(this.state.editionList[this.props.parentId]) { if(this.state.editionList[this.props.parentId]) {
selectedEditionsCount = this.filterSelectedEditions().length; selectedEditionsCount = this.filterSelectedEditions().length;
allEditionsCount = this.state.editionList[this.props.parentId].length; allEditionsCount = this.state.editionList[this.props.parentId].length;
orderBy = this.state.editionList[this.props.parentId].orderBy;
orderAsc = this.state.editionList[this.props.parentId].orderAsc;
} }
let transition = new TransitionModel('edition', 'editionId', 'bitcoin_id', PieceListActions.closeAllEditionLists);
let columnList = [ let columnList = [
new TableColumnContentModel( new ColumnModel(
(item) => { (item) => {
return { return {
'editionId': item.id, 'editionId': item.id,
'pieceId': this.props.parentId, 'pieceId': this.props.parentId,
'selectItem': this.selectItem, 'selectItem': this.selectItem,
'selected': item.selected 'selected': item.selected
}}, }; },
'', '',
<AccordionListItemTableSelectAllEditionsToggle <AccordionListItemTableSelectAllEditionsToggle
onChange={this.selectAllItems} onChange={this.selectAllItems}
@ -102,41 +108,41 @@ let AccordionListItemTableEditions = React.createClass({
1, 1,
false false
), ),
new TableColumnContentModel( new ColumnModel(
(item) => { (item) => {
return { return {
'content': item.edition_number 'content': item.edition_number
}}, }; },
'edition_number', 'edition_number',
'#', '#',
TableItemText, TableItemText,
1, 1,
true, true,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'} transition
), ),
new TableColumnContentModel( new ColumnModel(
(item) => { (item) => {
return { return {
'content': item.bitcoin_id 'content': item.bitcoin_id
}}, }; },
'bitcoin_id', 'bitcoin_id',
getLangText('Bitcoin Address'), getLangText('Bitcoin Address'),
TableItemText, TableItemText,
5, 5,
true, true,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'} transition
), ),
new TableColumnContentModel( new ColumnModel(
(item) => { (item) => {
return { return {
'content': item.acl 'content': item.acl
}}, }; },
'acl', 'acl',
getLangText('Actions'), getLangText('Actions'),
TableItemAclFiltered, TableItemAclFiltered,
4, 4,
false, false,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'} transition
) )
]; ];
@ -149,8 +155,8 @@ let AccordionListItemTableEditions = React.createClass({
columnList={columnList} columnList={columnList}
numOfTableItems={this.props.numOfEditions} numOfTableItems={this.props.numOfEditions}
show={this.props.show} show={this.props.show}
orderBy={this.state.orderBy} orderBy={orderBy}
orderAsc={this.state.orderAsc} orderAsc={orderAsc}
changeOrder={this.changeEditionListOrder}> changeOrder={this.changeEditionListOrder}>
<AccordionListItemTableToggle <AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle" className="ascribe-accordion-list-table-toggle"

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
let AccordionListItemTableToggle = React.createClass({ let AccordionListItemTableToggle = React.createClass({

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import Header from '../components/header'; import Header from '../components/header';

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Alert from 'react-bootstrap/lib/Alert'; import Alert from 'react-bootstrap/lib/Alert';
@ -7,12 +9,15 @@ let AlertDismissable = React.createClass({
alertVisible: true alertVisible: true
}; };
}, },
show() { show() {
this.setState({alertVisible: true}); this.setState({alertVisible: true});
}, },
hide() { hide() {
this.setState({alertVisible: false}); this.setState({alertVisible: false});
}, },
render() { render() {
if (this.state.alertVisible) { if (this.state.alertVisible) {
let key = this.props.error; let key = this.props.error;

View File

@ -1,5 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
/*
Is this even used somewhere?
Deprecate? 5.6.15 - Tim
*/
let ButtonSubmitOrClose = React.createClass({ let ButtonSubmitOrClose = React.createClass({
render() { render() {
if (this.props.submitted){ if (this.props.submitted){
@ -14,7 +22,7 @@ let ButtonSubmitOrClose = React.createClass({
<button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button> <button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button>
<button className="btn btn-ascribe-inv" onClick={this.props.onClose}>CLOSE</button> <button className="btn btn-ascribe-inv" onClick={this.props.onClose}>CLOSE</button>
</div> </div>
) );
} }
}); });

View File

@ -1,4 +1,4 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
@ -12,19 +12,20 @@ let ConsignForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_consigns return ApiUrls.ownership_consigns;
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.getBitcoinIds().join(), bitcoin_id: this.getBitcoinIds().join(),
consignee: this.refs.consignee.state.value, consignee: this.refs.consignee.state.value,
consign_message: this.refs.consign_message.state.value, consign_message: this.refs.consign_message.state.value,
password: this.refs.password.state.value password: this.refs.password.state.value
} };
}, },
renderForm() { renderForm() {
let title = this.getTitlesString().join(""); let title = this.getTitlesString().join('');
let username = this.props.currentUser.username; let username = this.props.currentUser.username;
let message = let message =
`Hi, `Hi,

View File

@ -1,4 +1,4 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
@ -9,39 +9,47 @@ import InputHidden from './input_hidden';
import InputCheckbox from './input_checkbox'; import InputCheckbox from './input_checkbox';
import InputDate from './input_date'; import InputDate from './input_date';
import InputTextArea from './input_textarea'; import InputTextArea from './input_textarea';
import OwnershipFetcher from '../../fetchers/ownership_fetcher' import OwnershipFetcher from '../../fetchers/ownership_fetcher';
import ButtonSubmitOrClose from './button_submit_close'; import ButtonSubmitOrClose from './button_submit_close';
let LoanForm = React.createClass({ let LoanForm = React.createClass({
getInitialState() {
this.setState({
contract_key: null,
contract_url: null,
loaneeHasContract: false
});
},
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_loans return ApiUrls.ownership_loans;
},
componentDidMount(){
this.setState({contract_key: null,
contract_url: null,
loaneeHasContract: false});
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.getBitcoinIds().join(), bitcoin_id: this.getBitcoinIds().join(),
loanee: this.refs.loanee.state.value, loanee: this.refs.loanee.state.value,
gallery_name: this.refs.gallery_name.state.value, gallery_name: this.refs.gallery_name.state.value,
startdate: this.refs.startdate.state.value.format("YYYY-MM-DD"), startdate: this.refs.startdate.state.value.format('YYYY-MM-DD'),
enddate: this.refs.enddate.state.value.format("YYYY-MM-DD"), enddate: this.refs.enddate.state.value.format('YYYY-MM-DD'),
loan_message: this.refs.loan_message.state.value, loan_message: this.refs.loan_message.state.value,
password: this.refs.password.state.value, password: this.refs.password.state.value,
terms: this.refs.terms.state.value terms: this.refs.terms.state.value
} };
}, },
handleLoanEmailBlur(e){
handleLoanEmailBlur(){
OwnershipFetcher.fetchLoanContract(this.refs.loanee.state.value) OwnershipFetcher.fetchLoanContract(this.refs.loanee.state.value)
.then((res) => { .then((res) => {
if (res && res.length > 0) { if (res && res.length > 0) {
this.setState({contract_key: res[0].s3Key, this.setState({
contract_key: res[0].s3Key,
contract_url: res[0].s3Url, contract_url: res[0].s3Url,
loaneeHasContract: true}); loaneeHasContract: true
});
} }
else{ else{
this.resetLoanContract(); this.resetLoanContract();
@ -52,14 +60,16 @@ let LoanForm = React.createClass({
this.resetLoanContract(); this.resetLoanContract();
}); });
}, },
resetLoanContract(){ resetLoanContract(){
this.setState({contract_key: null, this.setState({contract_key: null,
contract_url: null, contract_url: null,
loaneeHasContract: false loaneeHasContract: false
}); });
}, },
renderForm() { renderForm() {
let title = this.getTitlesString().join(""); let title = this.getTitlesString().join('');
let username = this.props.currentUser.username; let username = this.props.currentUser.username;
let message = let message =
`Hi, `Hi,
@ -72,18 +82,19 @@ ${username}`;
let contract = <InputHidden ref="terms" value="True"/>; let contract = <InputHidden ref="terms" value="True"/>;
if (this.state.loaneeHasContract){ if (this.state.loaneeHasContract){
let label = <div> let label = (<div>
I agree to the&nbsp; I agree to the&nbsp;
<a href={this.state.contract_url} target="_blank"> <a href={this.state.contract_url} target="_blank">
terms of {this.refs.loanee.state.value} terms of {this.refs.loanee.state.value}
</a> </a>
</div>; </div>);
contract = <InputCheckbox contract = (<InputCheckbox
ref="terms" ref="terms"
required="required" required="required"
label={label} label={label}
/> />);
} }
return ( return (
<form id="loan_modal_content" role="form" onSubmit={this.submit}> <form id="loan_modal_content" role="form" onSubmit={this.submit}>
<input className="invisible" type="email" name="fake_loanee"/> <input className="invisible" type="email" name="fake_loanee"/>

View File

@ -1,4 +1,4 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
@ -12,17 +12,19 @@ let ShareForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_shares_mail return ApiUrls.ownership_shares_mail;
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.getBitcoinIds().join(), bitcoin_id: this.getBitcoinIds().join(),
share_emails: this.refs.share_emails.state.value, share_emails: this.refs.share_emails.state.value,
share_message: this.refs.share_message.state.value share_message: this.refs.share_message.state.value
} };
}, },
renderForm() { renderForm() {
let title = this.getTitlesString().join(""); let title = this.getTitlesString().join('');
let username = this.props.currentUser.username; let username = this.props.currentUser.username;
let message = let message =
`Hi, `Hi,

View File

@ -1,4 +1,4 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
@ -9,23 +9,24 @@ import InputTextArea from './input_textarea';
import ButtonSubmitOrClose from './button_submit_close'; import ButtonSubmitOrClose from './button_submit_close';
let TransferForm = React.createClass({ let TransferForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_transfers return ApiUrls.ownership_transfers;
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.getBitcoinIds().join(), bitcoin_id: this.getBitcoinIds().join(),
transferee: this.refs.transferee.state.value, transferee: this.refs.transferee.state.value,
transfer_message: this.refs.transfer_message.state.value, transfer_message: this.refs.transfer_message.state.value,
password: this.refs.password.state.value password: this.refs.password.state.value
} };
}, },
renderForm() { renderForm() {
let title = this.getTitlesString().join(""); let title = this.getTitlesString().join('');
let username = this.props.currentUser.username; let username = this.props.currentUser.username;
let message = let message =
`Hi, `Hi,

View File

@ -1,4 +1,4 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
@ -12,15 +12,17 @@ let UnConsignForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_unconsigns return ApiUrls.ownership_unconsigns;
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.props.edition.bitcoin_id, bitcoin_id: this.props.edition.bitcoin_id,
unconsign_message: this.refs.unconsign_message.state.value, unconsign_message: this.refs.unconsign_message.state.value,
password: this.refs.password.state.value password: this.refs.password.state.value
} };
}, },
renderForm() { renderForm() {
let title = this.props.edition.title; let title = this.props.edition.title;
let username = this.props.currentUser.username; let username = this.props.currentUser.username;

View File

@ -1,10 +1,9 @@
import fetch from 'isomorphic-fetch'; 'use strict';
import React from 'react'; import React from 'react';
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 InputText from './input_text';
import InputTextArea from './input_textarea'; import InputTextArea from './input_textarea';
import ButtonSubmitOrClose from './button_submit_close'; import ButtonSubmitOrClose from './button_submit_close';
@ -12,14 +11,16 @@ let UnConsignRequestForm = React.createClass({
mixins: [FormMixin], mixins: [FormMixin],
url() { url() {
return ApiUrls.ownership_unconsigns_request return ApiUrls.ownership_unconsigns_request;
}, },
getFormData() { getFormData() {
return { return {
bitcoin_id: this.props.edition.bitcoin_id, bitcoin_id: this.props.edition.bitcoin_id,
unconsign_request_message: this.refs.unconsign_request_message.state.value unconsign_request_message: this.refs.unconsign_request_message.state.value
} };
}, },
renderForm() { renderForm() {
let title = this.props.edition.title; let title = this.props.edition.title;
let username = this.props.currentUser.username; let username = this.props.currentUser.username;

View File

@ -1,19 +1,24 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertMixin from '../../mixins/alert_mixin' import AlertMixin from '../../mixins/alert_mixin';
let InputCheckbox = React.createClass({ let InputCheckbox = React.createClass({
mixins : [AlertMixin], mixins: [AlertMixin],
getInitialState() { getInitialState() {
return {value: null, return {
value: null,
alerts: null // needed in AlertMixin alerts: null // needed in AlertMixin
}; };
}, },
handleChange(event) { handleChange(event) {
this.setState({value: event.target.value}); this.setState({value: event.target.value});
}, },
render() { render() {
let alerts = (this.props.submitted) ? null : this.state.alerts; let alerts = (this.props.submitted) ? null : this.state.alerts;
return ( return (

View File

@ -1,14 +1,17 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertMixin from '../../mixins/alert_mixin' import AlertMixin from '../../mixins/alert_mixin';
import DatePicker from 'react-datepicker/dist/react-datepicker' import DatePicker from 'react-datepicker/dist/react-datepicker';
let InputDate = React.createClass({ let InputDate = React.createClass({
mixins : [AlertMixin], mixins: [AlertMixin],
getInitialState() { getInitialState() {
return {value: null, return {
value: null,
alerts: null // needed in AlertMixin alerts: null // needed in AlertMixin
}; };
}, },
@ -18,7 +21,7 @@ let InputDate = React.createClass({
}, },
render: function () { render: function () {
let className = "form-control input-text-ascribe"; let className = 'form-control input-text-ascribe';
let alerts = (this.props.submitted) ? null : this.state.alerts; let alerts = (this.props.submitted) ? null : this.state.alerts;
return ( return (
<DatePicker <DatePicker
@ -29,6 +32,10 @@ let InputDate = React.createClass({
placeholderText={this.props.placeholderText} placeholderText={this.props.placeholderText}
/> />
); );
// CAN THIS BE REMOVED???
//
// - Tim?
//
//return ( //return (
// <div className="input-group date" // <div className="input-group date"
// ref={this.props.name + "_picker"} // ref={this.props.name + "_picker"}

View File

@ -1,10 +1,12 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertMixin from '../../mixins/alert_mixin' import AlertMixin from '../../mixins/alert_mixin';
let InputHidden = React.createClass({ let InputHidden = React.createClass({
mixins : [AlertMixin], mixins: [AlertMixin],
getInitialState() { getInitialState() {
return {value: this.props.value, return {value: this.props.value,

View File

@ -1,21 +1,25 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertMixin from '../../mixins/alert_mixin' import AlertMixin from '../../mixins/alert_mixin';
let InputText = React.createClass({ let InputText = React.createClass({
mixins : [AlertMixin], mixins: [AlertMixin],
getInitialState() { getInitialState() {
return {value: null, return {value: null,
alerts: null // needed in AlertMixin alerts: null // needed in AlertMixin
}; };
}, },
handleChange(event) { handleChange(event) {
this.setState({value: event.target.value}); this.setState({value: event.target.value});
}, },
render() { render() {
let className = "form-control input-text-ascribe"; let className = 'form-control input-text-ascribe';
let alerts = (this.props.submitted) ? null : this.state.alerts; let alerts = (this.props.submitted) ? null : this.state.alerts;
return ( return (
<div className="form-group"> <div className="form-group">

View File

@ -1,13 +1,16 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertMixin from '../../mixins/alert_mixin' import AlertMixin from '../../mixins/alert_mixin';
let InputTextArea = React.createClass({ let InputTextArea = React.createClass({
mixins : [AlertMixin], mixins: [AlertMixin],
getInitialState() { getInitialState() {
return {value: this.props.defaultValue, return {
value: this.props.defaultValue,
alerts: null // needed in AlertMixin alerts: null // needed in AlertMixin
}; };
}, },
@ -15,7 +18,7 @@ let InputTextArea = React.createClass({
this.setState({value: event.target.value}); this.setState({value: event.target.value});
}, },
render() { render() {
let className = "form-control input-text-ascribe textarea-ascribe-message"; let className = 'form-control input-text-ascribe textarea-ascribe-message';
let alerts = (this.props.submitted) ? null : this.state.alerts; let alerts = (this.props.submitted) ? null : this.state.alerts;
return ( return (

View File

@ -1,11 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
import Modal from 'react-bootstrap/lib/Modal'; import Modal from 'react-bootstrap/lib/Modal';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger'; import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import LoanForm from '../ascribe_forms/form_loan' import LoanForm from '../ascribe_forms/form_loan';
import ModalMixin from '../../mixins/modal_mixin' import ModalMixin from '../../mixins/modal_mixin';
let LoanModalButton = React.createClass({ let LoanModalButton = React.createClass({
render() { render() {
@ -19,12 +21,12 @@ let LoanModalButton = React.createClass({
</div> </div>
</ModalTrigger> </ModalTrigger>
</OverlayTrigger> </OverlayTrigger>
) );
} }
}); });
let LoanModal = React.createClass({ let LoanModal = React.createClass({
mixins : [ModalMixin], mixins: [ModalMixin],
render() { render() {
return ( return (
@ -35,7 +37,7 @@ let LoanModal = React.createClass({
onRequestHide={this.onRequestHide}/> onRequestHide={this.onRequestHide}/>
</div> </div>
</Modal> </Modal>
) );
} }
}); });

View File

@ -1,13 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
import Modal from 'react-bootstrap/lib/Modal'; import Modal from 'react-bootstrap/lib/Modal';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger'; import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import ModalMixin from '../../mixins/modal_mixin' import ModalMixin from '../../mixins/modal_mixin';
import ShareForm from '../ascribe_forms/form_share_email';
import ShareForm from '../ascribe_forms/form_share_email'
let ShareModalButton = React.createClass({ let ShareModalButton = React.createClass({
render() { render() {
@ -20,12 +20,12 @@ let ShareModalButton = React.createClass({
</div> </div>
</ModalTrigger> </ModalTrigger>
</OverlayTrigger> </OverlayTrigger>
) );
} }
}); });
let ShareModal = React.createClass({ let ShareModal = React.createClass({
mixins : [ModalMixin], mixins: [ModalMixin],
render() { render() {
return ( return (
@ -36,7 +36,7 @@ let ShareModal = React.createClass({
onRequestHide={this.onRequestHide}/> onRequestHide={this.onRequestHide}/>
</div> </div>
</Modal> </Modal>
) );
} }
}); });

View File

@ -1,11 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
import Modal from 'react-bootstrap/lib/Modal'; import Modal from 'react-bootstrap/lib/Modal';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger'; import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import UnConsignForm from '../ascribe_forms/form_unconsign' import UnConsignForm from '../ascribe_forms/form_unconsign';
import ModalMixin from '../../mixins/modal_mixin' import ModalMixin from '../../mixins/modal_mixin';
let UnConsignModalButton = React.createClass({ let UnConsignModalButton = React.createClass({
render() { render() {
@ -19,12 +21,12 @@ let UnConsignModalButton = React.createClass({
</div> </div>
</ModalTrigger> </ModalTrigger>
</OverlayTrigger> </OverlayTrigger>
) );
} }
}); });
let UnConsignModal = React.createClass({ let UnConsignModal = React.createClass({
mixins : [ModalMixin], mixins: [ModalMixin],
render() { render() {
return ( return (
@ -35,7 +37,7 @@ let UnConsignModal = React.createClass({
onRequestHide={this.onRequestHide}/> onRequestHide={this.onRequestHide}/>
</div> </div>
</Modal> </Modal>
) );
} }
}); });

View File

@ -1,11 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
import Modal from 'react-bootstrap/lib/Modal'; import Modal from 'react-bootstrap/lib/Modal';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger'; import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import UnConsignRequestForm from '../ascribe_forms/form_unconsign_request' import UnConsignRequestForm from '../ascribe_forms/form_unconsign_request';
import ModalMixin from '../../mixins/modal_mixin' import ModalMixin from '../../mixins/modal_mixin';
let UnConsignRequestModalButton = React.createClass({ let UnConsignRequestModalButton = React.createClass({
render() { render() {
@ -19,12 +21,12 @@ let UnConsignRequestModalButton = React.createClass({
</div> </div>
</ModalTrigger> </ModalTrigger>
</OverlayTrigger> </OverlayTrigger>
) );
} }
}); });
let UnConsignRequestModal = React.createClass({ let UnConsignRequestModal = React.createClass({
mixins : [ModalMixin], mixins: [ModalMixin],
render() { render() {
return ( return (
@ -35,7 +37,7 @@ let UnConsignRequestModal = React.createClass({
onRequestHide={this.onRequestHide}/> onRequestHide={this.onRequestHide}/>
</div> </div>
</Modal> </Modal>
) );
} }
}); });

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons'; import ReactAddons from 'react/addons';
@ -6,7 +8,7 @@ import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger'; import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import ModalMixin from '../../mixins/modal_mixin' import ModalMixin from '../../mixins/modal_mixin';
let ModalWrapper = React.createClass({ let ModalWrapper = React.createClass({
@ -27,20 +29,22 @@ let ModalWrapper = React.createClass({
{this.props.button} {this.props.button}
</ModalTrigger> </ModalTrigger>
</OverlayTrigger> </OverlayTrigger>
) );
} }
}); });
// //
let ModalBody = React.createClass({ let ModalBody = React.createClass({
mixins : [ModalMixin],
mixins: [ModalMixin],
handleSuccess(){ handleSuccess(){
this.props.handleSuccess(); this.props.handleSuccess();
this.props.onRequestHide(); this.props.onRequestHide();
}, },
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { return ReactAddons.Children.map(this.props.children, (child) => {
return ReactAddons.addons.cloneWithProps(child, { return ReactAddons.addons.cloneWithProps(child, {
editions: this.props.editions, editions: this.props.editions,
currentUser: this.props.currentUser, currentUser: this.props.currentUser,
@ -49,6 +53,7 @@ let ModalBody = React.createClass({
}); });
}); });
}, },
render() { render() {
return ( return (
<Modal {...this.props} title={this.props.title}> <Modal {...this.props} title={this.props.title}>
@ -56,7 +61,7 @@ let ModalBody = React.createClass({
{this.renderChildren()} {this.renderChildren()}
</div> </div>
</Modal> </Modal>
) );
} }
}); });

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import PaginationButton from './pagination_button'; import PaginationButton from './pagination_button';
@ -11,21 +13,19 @@ let Pagination = React.createClass({
}, },
render() { render() {
return( return (
<nav> <nav>
<ul className="pager"> <ul className="pager">
<PaginationButton <PaginationButton
direction='previous' direction='previous'
goToPage={this.props.goToPage} goToPage={this.props.goToPage}
currentPage={this.props.currentPage} currentPage={this.props.currentPage}
totalPages={this.props.totalPages}> totalPages={this.props.totalPages} />
</PaginationButton>
<PaginationButton <PaginationButton
direction='next' direction='next'
goToPage={this.props.goToPage} goToPage={this.props.goToPage}
currentPage={this.props.currentPage} currentPage={this.props.currentPage}
totalPages={this.props.totalPages}> totalPages={this.props.totalPages} />
</PaginationButton>
</ul> </ul>
</nav> </nav>
); );

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';

View File

@ -1,5 +1,9 @@
'use strict';
import React from 'react'; import React from 'react';
import { mergeOptions } from '../../utils/general_utils';
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';
@ -15,7 +19,7 @@ let PieceListBulkModal = React.createClass({
}, },
getInitialState() { getInitialState() {
return EditionListStore.getState(); return mergeOptions(EditionListStore.getState(), UserStore.getState());
}, },
onChange(state) { onChange(state) {
@ -23,23 +27,32 @@ let PieceListBulkModal = React.createClass({
}, },
componentDidMount() { componentDidMount() {
UserActions.fetchCurrentUser();
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
}, },
componentDidUnmount() { componentWillUnmount() {
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
}, },
fetchSelectedPieceEditionList() {
let filteredPieceIdList = Object.keys(this.state.editionList)
.filter((pieceId) => {
return this.state.editions.editionList[pieceId]
.filter((edition) => edition.selected).length > 0;
});
return filteredPieceIdList;
},
fetchSelectedEditionList() { fetchSelectedEditionList() {
let selectedEditionList = []; let selectedEditionList = [];
Object Object
.keys(this.state.editionList) .keys(this.state.editionList)
.forEach((key) => { .forEach((pieceId) => {
let filteredEditionsForPiece = this.state.editionList[key].filter((edition) => edition.selected); let filteredEditionsForPiece = this.state.editionList[pieceId].filter((edition) => edition.selected);
selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece); selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece);
}); });
@ -73,8 +86,13 @@ let PieceListBulkModal = React.createClass({
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
}, },
handleSuccess(){ handleSuccess() {
this.fetchSelectedPieceEditionList()
.forEach((pieceId) => {
EditionListActions.fetchEditionList(pieceId, this.state.orderBy, this.state.orderAsc);
});
EditionListActions.clearAllEditionSelections();
}, },
render() { render() {

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
let PieceListBulkModalSelectedEditionsWidget = React.createClass({ let PieceListBulkModalSelectedEditionsWidget = React.createClass({

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import PieceListStore from '../../stores/piece_list_store'; import PieceListStore from '../../stores/piece_list_store';
@ -6,26 +8,28 @@ 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';
import PieceListToolbarFilterWidgetFilter from './piece_list_toolbar_filter_widget';
let PieceListToolbar = React.createClass({ let PieceListToolbar = React.createClass({
propTypes: {
className: React.PropTypes.string
},
getInitialState() { getInitialState() {
return PieceListStore.getState(); return PieceListStore.getState();
}, },
onChange(state) {
this.setState(state);
},
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
}, },
componentDidUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); 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.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc); PieceListActions.fetchPieceList(this.state.page, this.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc);

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';

View File

@ -0,0 +1,47 @@
'use strict';
export class ColumnModel {
// ToDo: Add validation for all passed-in parameters
constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered, transition) {
this.transformFn = transformFn;
this.columnName = columnName;
this.displayName = displayName;
this.displayType = displayType;
this.rowWidth = rowWidth;
this.canBeOrdered = canBeOrdered;
this.transition = transition;
}
}
/**
* If a user opens an editionList of a piece and clicks on a specific edition to go to the
* piece detail page, all previously opened editionLists are still saved as show = true in the
* pieceList store.
*
* So if the user now comes back to this view the old data will still be in this store,
* since the browser wasn't able to load the new data (only containing show = undefined = false).
*
* This means that without closing all pieces after a transition, we'll get this flickering of editionLists.
*
* Since react-router does not implement a callback function for its transitionTo method, we have to do it
* our selfes, using this TransitionModel.
*/
export class TransitionModel {
constructor(to, queryKey, valueKey, callback) {
this.to = to;
this.queryKey = queryKey;
this.valueKey = valueKey;
this.callback = callback;
}
toReactRouterLinkProps(queryValue) {
let props = {
to: this.to,
params: {}
};
props.params[this.queryKey] = queryValue;
return props;
}
}

View File

@ -1,17 +1,22 @@
'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons'; import ReactAddons from 'react/addons';
import TableHeader from './table_header'; import TableHeader from './table_header';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
let Table = React.createClass({ let Table = React.createClass({
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,
orderBy: React.PropTypes.string, orderBy: React.PropTypes.string,
orderAsc: React.PropTypes.bool, orderAsc: React.PropTypes.bool,
className: React.PropTypes.string,
children: React.PropTypes.array,
itemList: React.PropTypes.array
}, },
renderChildren() { renderChildren() {
@ -27,17 +32,17 @@ let Table = React.createClass({
render() { render() {
return ( return (
<div className="ascribe-table"> <table className={'table ' + this.props.className}>
<TableHeader <TableHeader
columnList={this.props.columnList} columnList={this.props.columnList}
itemList={this.props.itemList} itemList={this.props.itemList}
changeOrder={this.props.changeOrder} changeOrder={this.props.changeOrder}
orderAsc={this.props.orderAsc} orderAsc={this.props.orderAsc}
orderBy={this.props.orderBy} /> orderBy={this.props.orderBy} />
<div className="row"> <tbody>
{this.renderChildren()} {this.renderChildren()}
</div> </tbody>
</div> </table>
); );
} }
}); });

View File

@ -1,25 +1,28 @@
'use strict';
import React from 'react'; import React from 'react';
import TableColumnMixin from '../../mixins/table_column_mixin'; import TableColumnMixin from '../../mixins/table_column_mixin';
import TableHeaderItem from './table_header_item'; import TableHeaderItem from './table_header_item';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
let TableHeader = React.createClass({ let TableHeader = React.createClass({
mixins: [TableColumnMixin],
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
itemList: React.PropTypes.array.isRequired, itemList: React.PropTypes.array.isRequired,
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,
orderAsc: React.PropTypes.bool, orderAsc: React.PropTypes.bool,
orderBy: React.PropTypes.string orderBy: React.PropTypes.string
}, },
mixins: [TableColumnMixin],
render() { render() {
return ( return (
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 ascribe-table-header-row"> <thead>
<div className="row"> <tr>
{this.props.columnList.map((val, i) => { {this.props.columnList.map((val, i) => {
let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12); let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12);
@ -35,14 +38,12 @@ let TableHeader = React.createClass({
canBeOrdered={canBeOrdered} canBeOrdered={canBeOrdered}
orderAsc={this.props.orderAsc} orderAsc={this.props.orderAsc}
orderBy={this.props.orderBy} orderBy={this.props.orderBy}
changeOrder={this.props.changeOrder}> changeOrder={this.props.changeOrder} />
</TableHeaderItem>
); );
})} })}
</div> </tr>
</div> </thead>
); );
} }
}); });

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import TableHeaderItemCarret from './table_header_item_carret'; import TableHeaderItemCarret from './table_header_item_carret';
@ -25,28 +27,28 @@ let TableHeaderItem = React.createClass({
if(this.props.canBeOrdered && this.props.changeOrder && this.props.orderAsc != null && this.props.orderBy) { if(this.props.canBeOrdered && this.props.changeOrder && this.props.orderAsc != null && this.props.orderBy) {
if(this.props.columnName === this.props.orderBy) { if(this.props.columnName === this.props.orderBy) {
return ( return (
<div <th
className={this.props.columnClasses + ' ascribe-table-header-column'} className={'ascribe-table-header-column'}
onClick={this.changeOrder}> onClick={this.changeOrder}>
<span>{this.props.displayName} <TableHeaderItemCarret orderAsc={this.props.orderAsc} /></span> <span>{this.props.displayName} <TableHeaderItemCarret orderAsc={this.props.orderAsc} /></span>
</div> </th>
); );
} else { } else {
return ( return (
<div <th
className={this.props.columnClasses + ' ascribe-table-header-column'} className={'ascribe-table-header-column'}
onClick={this.changeOrder}> onClick={this.changeOrder}>
<span>{this.props.displayName}</span> <span>{this.props.displayName}</span>
</div> </th>
); );
} }
} else { } else {
return ( return (
<div className={this.props.columnClasses + ' ascribe-table-header-column'}> <th className={'ascribe-table-header-column'}>
<span> <span>
{this.props.displayName} {this.props.displayName}
</span> </span>
</div> </th>
); );
} }
} }

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
let TableHeaderItemCarret = React.createClass({ let TableHeaderItemCarret = React.createClass({

View File

@ -1,6 +1,8 @@
'use strict';
import React from 'react'; import React from 'react';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
import TableItemWrapper from './table_item_wrapper'; import TableItemWrapper from './table_item_wrapper';
@ -8,24 +10,17 @@ import TableItemWrapper from './table_item_wrapper';
let TableItem = React.createClass({ let TableItem = React.createClass({
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
columnContent: React.PropTypes.object, columnContent: React.PropTypes.object,
onClick: React.PropTypes.func, // See: https://facebook.github.io/react/tips/expose-component-functions.html
className: React.PropTypes.string className: React.PropTypes.string
}, },
render() { render() {
return ( return (
<div className={this.props.className + ' col-xs-12 col-sm-12 col-md-12 col-lg-12 ascribe-table-item'}
onClick={this.props.onClick}>
<div className="row">
<TableItemWrapper <TableItemWrapper
columnList={this.props.columnList} columnList={this.props.columnList}
columnContent={this.props.columnContent} columnContent={this.props.columnContent}
columnWidth={12}> columnWidth={12} />
</TableItemWrapper>
</div>
</div>
); );
} }
}); });

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
/** /**
@ -5,7 +7,7 @@ import React from 'react';
*/ */
let TableItemImg = React.createClass({ let TableItemImg = React.createClass({
propTypes: { propTypes: {
content: React.PropTypes.string.isRequired, content: React.PropTypes.string.isRequired
}, },
render() { render() {

View File

@ -1,7 +1,9 @@
'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
import TableItem from './table_item'; import TableItem from './table_item';
@ -9,10 +11,11 @@ import TableItem from './table_item';
let TableItemSelectable = React.createClass({ let TableItemSelectable = React.createClass({
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
columnContent: React.PropTypes.object, columnContent: React.PropTypes.object,
parentId: React.PropTypes.number, parentId: React.PropTypes.number,
className: React.PropTypes.string className: React.PropTypes.string,
selectItem: React.PropTypes.func
}, },
selectItem() { selectItem() {
@ -29,8 +32,7 @@ let TableItemSelectable = React.createClass({
className={tableItemClasses + ' ' + this.props.className} className={tableItemClasses + ' ' + this.props.className}
columnList={this.props.columnList} columnList={this.props.columnList}
columnContent={this.props.columnContent} columnContent={this.props.columnContent}
onClick={this.selectItem}> onClick={this.selectItem} />
</TableItem>
); );
} }

View File

@ -1,6 +1,8 @@
'use strict';
import React from 'react'; import React from 'react';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
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';
@ -16,7 +18,7 @@ import TableItemSubtableButton from './table_item_subtable_button';
let TableItemSubtable = React.createClass({ let TableItemSubtable = React.createClass({
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
columnContent: React.PropTypes.object columnContent: React.PropTypes.object
}, },
@ -26,14 +28,18 @@ let TableItemSubtable = React.createClass({
}; };
}, },
onChange(state) {
this.setState(state);
},
componentDidMount() { componentDidMount() {
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
}, },
componentWillUnmount() {
EditionListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
loadEditionList() { loadEditionList() {
if(this.state.open) { if(this.state.open) {
this.setState({ this.setState({
@ -61,9 +67,9 @@ let TableItemSubtable = React.createClass({
let renderEditionListTable = () => { let renderEditionListTable = () => {
let columnList = [ let columnList = [
new TableColumnContentModel('edition_number', 'Number', TableItemText, 2, false), new ColumnModel('edition_number', 'Number', TableItemText, 2, false),
new TableColumnContentModel('user_registered', 'User', TableItemText, 4, true), new ColumnModel('user_registered', 'User', TableItemText, 4, true),
new TableColumnContentModel('acl', 'Actions', TableItemAcl, 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) { if(this.state.open && this.state.editionList[this.props.columnContent.id] && this.state.editionList[this.props.columnContent.id].length) {
@ -77,8 +83,7 @@ let TableItemSubtable = React.createClass({
className="ascribe-table-item-selectable" className="ascribe-table-item-selectable"
selectItem={this.selectItem} selectItem={this.selectItem}
parentId={this.props.columnContent.id} parentId={this.props.columnContent.id}
key={i}> key={i} />
</TableItemSelectable>
); );
})} })}
</Table> </Table>
@ -94,11 +99,9 @@ let TableItemSubtable = React.createClass({
<TableItemWrapper <TableItemWrapper
columnList={this.props.columnList} columnList={this.props.columnList}
columnContent={this.props.columnContent} columnContent={this.props.columnContent}
columnWidth={12}> columnWidth={12} />
</TableItemWrapper>
<div className="col-xs-1 col-sm-1 col-md-1 col-lg-1 ascribe-table-item-column"> <div className="col-xs-1 col-sm-1 col-md-1 col-lg-1 ascribe-table-item-column">
<TableItemSubtableButton content="+" onClick={this.loadEditionList}> <TableItemSubtableButton content="+" onClick={this.loadEditionList} />
</TableItemSubtableButton>
</div> </div>
</div> </div>
{renderEditionListTable()} {renderEditionListTable()}

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
let TableItemSubtableButton = React.createClass({ let TableItemSubtableButton = React.createClass({

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';

View File

@ -1,34 +1,25 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import TableColumnContentModel from '../../models/table_column_content_model'; import { ColumnModel } from './models/table_models';
import TableColumnMixin from '../../mixins/table_column_mixin'; import TableColumnMixin from '../../mixins/table_column_mixin';
let Link = Router.Link;
let TableItemWrapper = React.createClass({ let TableItemWrapper = React.createClass({
mixins: [TableColumnMixin, Router.Navigation],
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)),
columnContent: React.PropTypes.object, columnContent: React.PropTypes.object,
columnWidth: React.PropTypes.number.isRequired columnWidth: React.PropTypes.number.isRequired
}, },
/** mixins: [TableColumnMixin, Router.Navigation],
* If a link is defined in columnContent, then we can use
* Router.Navigation.transitionTo to redirect the user
* programmatically
*/
transition(column) {
if(column.link) {
let params = {};
params[column.link.paramsKey] = this.props.columnContent[column.link.contentKey];
this.transitionTo(column.link.to, params);
}
},
render() { render() {
return ( return (
<div> <tr>
{this.props.columnList.map((column, i) => { {this.props.columnList.map((column, i) => {
let TypeElement = column.displayType; let TypeElement = column.displayType;
@ -36,19 +27,35 @@ let TableItemWrapper = React.createClass({
let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth); let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth);
let transition = this.transition.bind(this, column); if(!column.transition) {
return ( return (
<div <td
className={columnClass + ' ascribe-table-item-column'} className={'ascribe-table-item-column'}
key={i} key={i}>
onClick={transition}>
<TypeElement {...typeElementProps} /> <TypeElement {...typeElementProps} />
</div> </td>
); );
} else {
let linkProps = column.transition.toReactRouterLinkProps(this.props.columnContent[column.transition.valueKey]);
/**
* If a transition is defined in columnContent, then we can use
* Router.Navigation.transitionTo to redirect the user
* programmatically
*/
return (
<td key={i}>
<Link
className={'ascribe-table-item-column'}
onClick={column.transition.callback}
{...linkProps}>
<TypeElement {...typeElementProps} />
</Link>
</td>
);
}
})} })}
</div> </tr>
); );
} }
}); });

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import MediaPlayer from './ascribe_media/media_player'; import MediaPlayer from './ascribe_media/media_player';
@ -6,13 +8,18 @@ import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import EditionActions from '../actions/edition_actions' import EditionActions from '../actions/edition_actions';
import AclButton from './acl_button' import AclButton from './acl_button';
/** /**
* This is the component that implements display-specific functionality * This is the component that implements display-specific functionality
*/ */
let Edition = React.createClass({ let Edition = React.createClass({
propTypes: {
edition: React.PropTypes.object,
currentUser: React.PropTypes.object
},
render() { render() {
let thumbnail = this.props.edition.thumbnail; let thumbnail = this.props.edition.thumbnail;
let mimetype = this.props.edition.digital_work.mime; let mimetype = this.props.edition.digital_work.mime;
@ -45,13 +52,17 @@ let Edition = React.createClass({
}); });
let EditionHeader = React.createClass({ let EditionHeader = React.createClass({
propTypes: {
edition: React.PropTypes.object
},
render() { render() {
var title_html = <div className="ascribe-detail-title">{this.props.edition.title}</div>; var titleHtml = <div className="ascribe-detail-title">{this.props.edition.title}</div>;
return ( return (
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<EditionDetailProperty label="title" value={title_html} /> <EditionDetailProperty label="title" value={titleHtml} />
<EditionDetailProperty label="by" value={this.props.edition.artist_name} /> <EditionDetailProperty label="by" value={this.props.edition.artist_name} />
<EditionDetailProperty label="date" value={ this.props.edition.date_created.slice(0,4) } /> <EditionDetailProperty label="date" value={ this.props.edition.date_created.slice(0, 4) } />
<hr/> <hr/>
</div> </div>
); );
@ -59,26 +70,32 @@ let EditionHeader = React.createClass({
}); });
let EditionDetails = React.createClass({ let EditionDetails = React.createClass({
propTypes: {
edition: React.PropTypes.object,
currentUser: React.PropTypes.object
},
handleSuccess(){ handleSuccess(){
EditionActions.fetchOne(this.props.edition.id); EditionActions.fetchOne(this.props.edition.id);
}, },
render() { render() {
return ( return (
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<EditionDetailProperty label="edition" <EditionDetailProperty label="edition"
value={this.props.edition.edition_number + " of " + this.props.edition.num_editions} /> value={this.props.edition.edition_number + ' of ' + this.props.edition.num_editions} />
<EditionDetailProperty label="id" value={ this.props.edition.bitcoin_id } /> <EditionDetailProperty label="id" value={ this.props.edition.bitcoin_id } />
<EditionDetailProperty label="owner" value={ this.props.edition.owner } /> <EditionDetailProperty label="owner" value={ this.props.edition.owner } />
<br/> <br/>
<AclButton <AclButton
availableAcls={["transfer"]} availableAcls={['transfer']}
action="transfer" action="transfer"
editions={[this.props.edition]} editions={[this.props.edition]}
currentUser={this.props.currentUser} currentUser={this.props.currentUser}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
/> />
<AclButton <AclButton
availableAcls={["consign"]} availableAcls={['consign']}
action="consign" action="consign"
editions={[this.props.edition]} editions={[this.props.edition]}
currentUser={this.props.currentUser} currentUser={this.props.currentUser}
@ -92,6 +109,14 @@ let EditionDetails = React.createClass({
}); });
let EditionDetailProperty = React.createClass({ let EditionDetailProperty = React.createClass({
propTypes: {
label: React.PropTypes.string,
value: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.element
])
},
render() { render() {
return ( return (
<div className="row ascribe-detail-property"> <div className="row ascribe-detail-property">

View File

@ -1,5 +1,9 @@
'use strict';
import React from 'react'; import React from 'react';
import { mergeOptions } from '../utils/general_utils';
import EditionActions from '../actions/edition_actions'; import EditionActions from '../actions/edition_actions';
import EditionStore from '../stores/edition_store'; import EditionStore from '../stores/edition_store';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
@ -11,10 +15,8 @@ import Edition from './edition';
* This is the component that implements resource/data specific functionality * This is the component that implements resource/data specific functionality
*/ */
let EditionContainer = React.createClass({ let EditionContainer = React.createClass({
getInitialState() { getInitialState() {
return {'user': UserStore.getState(), return mergeOptions(UserStore.getState(), EditionStore.getState());
'edition': EditionStore.getState()}
}, },
onChange(state) { onChange(state) {
@ -22,18 +24,19 @@ let EditionContainer = React.createClass({
}, },
componentDidMount() { componentDidMount() {
EditionActions.fetchOne(this.props.params.editionId);
EditionStore.listen(this.onChange); EditionStore.listen(this.onChange);
UserActions.fetchCurrentUser();
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
EditionActions.fetchOne(this.props.params.editionId);
}, },
componentDidUnmount() {
componentWillUnmount() {
EditionStore.unlisten(this.onChange); EditionStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
}, },
render() { render() {
if('title' in this.state.edition) { if('title' in this.state.edition) {
return ( return (
<Edition edition={this.state.edition} <Edition edition={this.state.edition}
@ -44,8 +47,6 @@ let EditionContainer = React.createClass({
<p>Loading</p> <p>Loading</p>
); );
} }
} }
}); });

View File

@ -1,13 +1,13 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import AltContainer from 'alt/AltContainer';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store'; import UserStore from '../stores/user_store';
import Nav from 'react-bootstrap/lib/Nav'; import Nav from 'react-bootstrap/lib/Nav';
import Navbar from 'react-bootstrap/lib/Navbar'; import Navbar from 'react-bootstrap/lib/Navbar';
import NavItem from 'react-bootstrap/lib/NavItem';
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';
@ -22,10 +22,14 @@ let Header = React.createClass({
}, },
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange) UserStore.listen(this.onChange);
UserActions.fetchCurrentUser(); UserActions.fetchCurrentUser();
}, },
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
}, },

View File

@ -1,5 +1,6 @@
'use strict';
import React from 'react'; import React from 'react';
import AltContainer from 'alt/AltContainer';
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';
@ -15,6 +16,9 @@ import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
let PieceList = React.createClass({ let PieceList = React.createClass({
propTypes: {
query: React.PropTypes.object
},
getInitialState() { getInitialState() {
return PieceListStore.getState(); return PieceListStore.getState();
@ -35,7 +39,7 @@ let PieceList = React.createClass({
}, },
paginationGoToPage(page) { paginationGoToPage(page) {
return (e) => PieceListActions.fetchPieceList(page, this.state.pageSize, return () => PieceListActions.fetchPieceList(page, this.state.pageSize,
this.state.search, this.state.orderBy, this.state.search, this.state.orderBy,
this.state.orderAsc); this.state.orderAsc);
}, },
@ -47,7 +51,7 @@ 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);
return ( return (
<div> <div>
@ -69,7 +73,7 @@ let PieceList = React.createClass({
content={item} content={item}
key={i}> key={i}>
<AccordionListItemTableEditions <AccordionListItemTableEditions
className="ascribe-accordion-list-item-table col-xs-6 col-sm-6 col-md-6 col-lg-6 col-xs-offset-3 col-sm-offset-3 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} show={item.show}
numOfEditions={item.num_editions}/> numOfEditions={item.num_editions}/>
@ -80,8 +84,7 @@ let PieceList = React.createClass({
<Pagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
goToPage={this.paginationGoToPage}> goToPage={this.paginationGoToPage} />
</Pagination>
</div> </div>
); );
} }

View File

@ -1,17 +1,19 @@
'use strict';
import AppConstants from './application_constants'; import AppConstants from './application_constants';
let apiUrls = { let apiUrls = {
'ownership_shares_mail' : AppConstants.baseUrl + 'ownership/shares/mail/', 'ownership_shares_mail': AppConstants.baseUrl + 'ownership/shares/mail/',
'ownership_transfers' : AppConstants.baseUrl + 'ownership/transfers/', 'ownership_transfers': AppConstants.baseUrl + 'ownership/transfers/',
'user': AppConstants.baseUrl + 'users/', 'user': AppConstants.baseUrl + 'users/',
'pieces_list': AppConstants.baseUrl + 'pieces/', 'pieces_list': AppConstants.baseUrl + 'pieces/',
'piece': AppConstants.baseUrl + 'pieces/${piece_id}', 'piece': AppConstants.baseUrl + 'pieces/${piece_id}',
'edition': AppConstants.baseUrl + 'editions/${bitcoin_id}/', 'edition': AppConstants.baseUrl + 'editions/${bitcoin_id}/',
'editions_list': AppConstants.baseUrl + 'pieces/${piece_id}/editions/', 'editions_list': AppConstants.baseUrl + 'pieces/${piece_id}/editions/',
'ownership_loans' : AppConstants.baseUrl + 'ownership/loans/', 'ownership_loans': AppConstants.baseUrl + 'ownership/loans/',
'ownership_consigns' : AppConstants.baseUrl + 'ownership/consigns/', 'ownership_consigns': AppConstants.baseUrl + 'ownership/consigns/',
'ownership_unconsigns' : AppConstants.baseUrl + 'ownership/unconsigns/', 'ownership_unconsigns': AppConstants.baseUrl + 'ownership/unconsigns/',
'ownership_unconsigns_request' : AppConstants.baseUrl + 'ownership/unconsigns/request/' 'ownership_unconsigns_request': AppConstants.baseUrl + 'ownership/unconsigns/request/'
}; };
export default apiUrls; export default apiUrls;

View File

@ -1,3 +1,5 @@
'use strict';
let constants = { let constants = {
//'baseUrl': 'http://localhost:8000/api/', //'baseUrl': 'http://localhost:8000/api/',
'baseUrl': 'http://staging.ascribe.io/api/', 'baseUrl': 'http://staging.ascribe.io/api/',

View File

@ -1,3 +1,5 @@
'use strict';
const languages = { const languages = {
'en-US': { 'en-US': {
'Bitcoin Address': 'Bitcoin Address', 'Bitcoin Address': 'Bitcoin Address',

View File

@ -1,13 +1,11 @@
'use strict';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import AppConstants from '../constants/application_constants';
let EditionFetcher = { let EditionFetcher = {
/** /**
* Fetch one user from the API. * Fetch one user from the API.
* 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 fetch.get('edition', {'bitcoin_id': editionId});

View File

@ -1,9 +1,9 @@
'use strict';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
import AppConstants from '../constants/application_constants';
let EditionListFetcher = { let EditionListFetcher = {
/** /**

View File

@ -1,14 +1,14 @@
'use strict';
import fetch from 'isomorphic-fetch'; import fetch from 'isomorphic-fetch';
import AppConstants from '../constants/application_constants'; import AppConstants from '../constants/application_constants';
import FetchApiUtils from '../utils/fetch_api_utils';
let OwnershipFetcher = { let OwnershipFetcher = {
/** /**
* Fetch one user from the API. * Fetch one user from the API.
* If no arg is supplied, load the current user * If no arg is supplied, load the current user
*
*/ */
fetchLoanContract(email) { fetchLoanContract(email) {
return fetch(AppConstants.baseUrl + 'ownership/loans/contract/?loanee=' + email, { return fetch(AppConstants.baseUrl + 'ownership/loans/contract/?loanee=' + email, {

View File

@ -1,15 +1,14 @@
import fetch from '../utils/fetch'; 'use strict';
import AppConstants from '../constants/application_constants'; import fetch from '../utils/fetch';
let PieceFetcher = { let PieceFetcher = {
/** /**
* Fetch one user from the API. * Fetch one user from the API.
* If no arg is supplied, load the current user * If no arg is supplied, load the current user
*
*/ */
fetchOne(pieceId) { fetchOne() {
return fetch.get('piece'); return fetch.get('piece');
} }
}; };

View File

@ -1,4 +1,5 @@
import AppConstants from '../constants/application_constants'; 'use strict';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';

View File

@ -1,13 +1,12 @@
import fetch from '../utils/fetch'; 'use strict';
import AppConstants from '../constants/application_constants'; import fetch from '../utils/fetch';
let UserFetcher = { let UserFetcher = {
/** /**
* Fetch one user from the API. * Fetch one user from the API.
* 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 fetch.get('user');

View File

@ -1,15 +1,17 @@
'use strict';
import React from 'react'; import React from 'react';
import AlertDismissable from '../components/ascribe_forms/alert'; import AlertDismissable from '../components/ascribe_forms/alert';
let AlertMixin = { let AlertMixin = {
setAlerts(errors){ setAlerts(errors){
let alerts = errors.map( let alerts = errors.map((error) => {
function(error) {
return <AlertDismissable error={error} key={error}/>; return <AlertDismissable error={error} key={error}/>;
}.bind(this) });
);
this.setState({alerts: alerts}); this.setState({alerts: alerts});
}, },
clearAlerts(){ clearAlerts(){
this.setState({alerts: null}); this.setState({alerts: null});
} }

View File

@ -1,8 +1,9 @@
'use strict';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import React from 'react'; import React from 'react';
import AppConstants from '../constants/application_constants' import AlertDismissable from '../components/ascribe_forms/alert';
import AlertDismissable from '../components/ascribe_forms/alert'
export const FormMixin = { export const FormMixin = {
getInitialState() { getInitialState() {
@ -10,7 +11,7 @@ export const FormMixin = {
submitted: false submitted: false
, status: null , status: null
, errors: [] , errors: []
} };
}, },
submit(e) { submit(e) {
@ -18,7 +19,7 @@ export const FormMixin = {
this.setState({submitted: true}); this.setState({submitted: true});
fetch fetch
.post(this.url(), { body: this.getFormData() }) .post(this.url(), { body: this.getFormData() })
.then(response => { this.props.handleSuccess(); }) .then(() => { this.props.handleSuccess(); })
.catch(this.handleError); .catch(this.handleError);
}, },
@ -40,31 +41,29 @@ export const FormMixin = {
getBitcoinIds(){ getBitcoinIds(){
return this.props.editions.map(function(edition){ return this.props.editions.map(function(edition){
return edition.bitcoin_id return edition.bitcoin_id;
}) });
}, },
getTitlesString(){ getTitlesString(){
return this.props.editions.map(function(edition){ return this.props.editions.map(function(edition){
return '- \"' + edition.title + ', edition ' + edition.edition_number + '\"\n' return '- \"' + edition.title + ', edition ' + edition.edition_number + '\"\n';
}) });
}, },
render(){ render(){
let alert = null; let alert = null;
if (this.state.errors.length > 0){ if (this.state.errors.length > 0){
alert = this.state.errors.map( alert = this.state.errors.map((error) => {
function(error) {
return <AlertDismissable error={error} key={error}/>; return <AlertDismissable error={error} key={error}/>;
}.bind(this) });
);
} }
return ( return (
<div> <div>
{alert} {alert}
{this.renderForm()} {this.renderForm()}
</div> </div>
) );
} }
}; };

View File

@ -1,12 +1,14 @@
'use strict';
let mapAttr = { let mapAttr = {
link: 'href', link: 'href',
script: 'src' source: 'src'
} };
let mapTag = { let mapTag = {
js: 'script', js: 'script',
css: 'link' css: 'link'
} };
let InjectInHeadMixin = { let InjectInHeadMixin = {
@ -57,8 +59,9 @@ let InjectInHeadMixin = {
inject(src) { inject(src) {
let ext = src.split('.').pop(); let ext = src.split('.').pop();
let tag = mapTag[ext]; let tag = mapTag[ext];
if (!tag) if (!tag) {
throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`); throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
}
return InjectInHeadMixin.injectTag(tag, src); return InjectInHeadMixin.injectTag(tag, src);
} }

View File

@ -1,9 +1,10 @@
import React from 'react'; 'use strict';
let ModalMixin = { let ModalMixin = {
onRequestHide(e){ onRequestHide(e){
if (e) if (e) {
e.preventDefault(); e.preventDefault();
}
this.props.onRequestHide(); this.props.onRequestHide();
} }
}; };

View File

@ -1,4 +1,4 @@
import React from 'react'; 'use strict';
import { sumNumList } from '../utils/general_utils'; import { sumNumList } from '../utils/general_utils';
@ -14,7 +14,7 @@ let TableColumnMixin = {
let numOfUsedColumns = sumNumList(listOfRowValues); let numOfUsedColumns = sumNumList(listOfRowValues);
if(numOfUsedColumns > numOfColumns) { 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.') 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 { } else {
return bootstrapClasses.join( listOfRowValues[i] + ' ') + listOfRowValues[i]; return bootstrapClasses.join( listOfRowValues[i] + ' ') + listOfRowValues[i];
} }

View File

@ -1,14 +0,0 @@
class TableColumnContentModel {
// ToDo: Add validation for all passed-in parameters
constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered, link) {
this.transformFn = transformFn;
this.columnName = columnName;
this.displayName = displayName;
this.displayType = displayType;
this.rowWidth = rowWidth;
this.canBeOrdered = canBeOrdered;
this.link = link;
}
}
export default TableColumnContentModel;

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
@ -10,11 +12,8 @@ let Route = Router.Route;
let routes = ( let routes = (
<Route name="app" handler={AscribeApp}> <Route name="app" handler={AscribeApp}>
<Route name="pieces" path="/" handler={PieceList}> <Route name="pieces" path="/" handler={PieceList} />
<Route name="edition" path="/editions/:editionId" handler={EditionContainer} />
</Route>
<Route name="edition" path="/editions/:editionId" handler={EditionContainer}>
</Route>
</Route> </Route>
); );

View File

@ -1,3 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import alt from '../alt'; import alt from '../alt';
@ -6,8 +8,6 @@ import EditionsListActions from '../actions/edition_list_actions';
class EditionListStore { class EditionListStore {
constructor() { constructor() {
this.editionList = {}; this.editionList = {};
this.orderBy = 'edition_number';
this.orderAsc = true;
this.bindActions(EditionsListActions); this.bindActions(EditionsListActions);
} }
@ -17,11 +17,18 @@ class EditionListStore {
// 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!
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;
this.orderBy = orderBy;
this.orderAsc = orderAsc; /**
* orderBy and orderAsc are specific to a single list of editons
* therefore they need to be saved in relation to their parent-piece.
*
* Default values for both are set in the editon_list-actions.
*/
this.editionList[pieceId].orderBy = orderBy;
this.editionList[pieceId].orderAsc = orderAsc;
} }
onSelectEdition({pieceId, editionId}) { onSelectEdition({pieceId, editionId}) {
@ -50,6 +57,6 @@ class EditionListStore {
}); });
}); });
} }
}; }
export default alt.createStore(EditionListStore); export default alt.createStore(EditionListStore);

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import EditionActions from '../actions/edition_actions'; import EditionActions from '../actions/edition_actions';

View File

@ -1,4 +1,7 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import PieceListActions from '../actions/piece_list_actions'; import PieceListActions from '../actions/piece_list_actions';
@ -18,8 +21,8 @@ class PieceListStore {
this.pieceListCount = 0; this.pieceListCount = 0;
this.page = 1; this.page = 1;
this.pageSize = 10; this.pageSize = 10;
this.search = ""; this.search = '';
this.orderBy = "artist_name"; this.orderBy = 'artist_name';
this.orderAsc = true; this.orderAsc = true;
this.bindActions(PieceListActions); this.bindActions(PieceListActions);
} }
@ -37,6 +40,13 @@ class PieceListStore {
}); });
} }
onCloseAllEditionLists() {
this.pieceList
.forEach((piece) => {
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;
this.pageSize = pageSize; this.pageSize = pageSize;
@ -46,6 +56,10 @@ class PieceListStore {
this.pieceListCount = pieceListCount; this.pieceListCount = pieceListCount;
/** /**
* Pagination - Known Issue:
* #########################
*
*
* The piece list store currently stores the open/close state of a piece list item. * The piece list store currently stores the open/close state of a piece list item.
* *
* Once a new page is requested, this.pieceList will be overwritten, which means that the * Once a new page is requested, this.pieceList will be overwritten, which means that the
@ -57,10 +71,9 @@ 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.
*
*/ */
this.pieceList = pieceList; this.pieceList = pieceList;
} }
}; }
export default alt.createStore(PieceListStore); export default alt.createStore(PieceListStore);

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import PieceAction from '../actions/piece_actions'; import PieceAction from '../actions/piece_actions';

View File

@ -1,3 +1,5 @@
'use strict';
import alt from '../alt'; import alt from '../alt';
import UserAction from '../actions/user_actions'; import UserAction from '../actions/user_actions';

View File

@ -1,11 +1,13 @@
'use strict';
import { default as _fetch } from 'isomorphic-fetch'; import { default as _fetch } from 'isomorphic-fetch';
import { argsToQueryParams } from '../utils/fetch_api_utils'; import { argsToQueryParams } from '../utils/fetch_api_utils';
class UrlMapError extends Error {}; class UrlMapError extends Error {}
class ServerError extends Error {}; class ServerError extends Error {}
class APIError extends Error {}; class APIError extends Error {}
class Fetch { class Fetch {
@ -33,7 +35,7 @@ class Fetch {
} }
handleAPIError(json) { handleAPIError(json) {
if (!json['success']) { if (!json.success) {
let error = new APIError(); let error = new APIError();
error.json = json; error.json = json;
throw error; throw error;
@ -58,7 +60,7 @@ class Fetch {
let re = /\${(\w+)}/g; let re = /\${(\w+)}/g;
newUrl = newUrl.replace(re, (match, key) => { newUrl = newUrl.replace(re, (match, key) => {
let val = params[key] let val = params[key];
if (!val) { if (!val) {
throw new Error(`Cannot find param ${key}`); throw new Error(`Cannot find param ${key}`);
} }
@ -76,7 +78,7 @@ class Fetch {
request(verb, url, options) { request(verb, url, options) {
options = options || {}; options = options || {};
let merged = this._merge(this.httpOptions, options); let merged = this._merge(this.httpOptions, options);
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)
@ -95,8 +97,8 @@ class Fetch {
let newUrl = this.prepareUrl(url, params); let newUrl = this.prepareUrl(url, params);
let body = null; let body = null;
if (params['body']) { if (params.body) {
body = JSON.stringify(params['body']) body = JSON.stringify(params.body);
} }
return this.request('post', url, { body }); return this.request('post', url, { body });
} }

View File

@ -1,3 +1,5 @@
'use strict';
import { sanitize } from './general_utils'; import { sanitize } from './general_utils';
// TODO: Create Unittests that test all functions // TODO: Create Unittests that test all functions
@ -38,7 +40,7 @@ export function argsToQueryParams(obj) {
return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]); return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]);
}) })
.join(''); .join('');
}; }
/** /**
* Takes a string and a boolean and generates a string query parameter for * Takes a string and a boolean and generates a string query parameter for
@ -52,11 +54,11 @@ export function generateOrderingQueryParams(orderBy, orderAsc) {
} }
return interpolation + orderBy; return interpolation + orderBy;
}; }
export function status(response) { export function status(response) {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return response return response;
} }
throw new Error(response.json()) throw new Error(response.json());
}; }

View File

@ -1,3 +1,5 @@
'use strict';
// TODO: Create Unittests that test all functions // TODO: Create Unittests that test all functions
export function sanitize(obj) { export function sanitize(obj) {
@ -12,7 +14,7 @@ export function sanitize(obj) {
}); });
return obj; return obj;
}; }
/** /**
* Returns the values of an object. * Returns the values of an object.
@ -21,7 +23,7 @@ export function valuesOfObject(obj) {
return Object return Object
.keys(obj) .keys(obj)
.map(key => obj[key]); .map(key => obj[key]);
}; }
/** /**
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum... * Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
@ -30,11 +32,14 @@ export function sumNumList(l) {
let sum = 0; let sum = 0;
l.forEach((num) => sum += parseFloat(num) || 0); l.forEach((num) => sum += parseFloat(num) || 0);
return sum; return sum;
}; }
/* /*
Taken from http://stackoverflow.com/a/4795914/1263876 Taken from http://stackoverflow.com/a/4795914/1263876
Behaves like C's format string function Behaves like C's format string function
REFACTOR TO ES6 (let instead of var)
*/ */
export function formatText() { export function formatText() {
var args = arguments, var args = arguments,
@ -60,4 +65,34 @@ export function formatText() {
} }
return val; return val;
}); });
}; }
/**
* Takes a list of object and merges their keys to one object.
* Uses mergeOptions for two objects.
* @param {[type]} l [description]
* @return {[type]} [description]
*/
export function mergeOptions(...l) {
let newObj = {};
for(let i = 1; i < l.length; i++) {
newObj = _mergeOptions(newObj, _mergeOptions(l[i - 1], l[i]));
}
return newObj;
}
/**
* Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
* @param obj1
* @param obj2
* @returns obj3 a new object based on obj1 and obj2
* Taken from: http://stackoverflow.com/a/171256/1263876
*/
function _mergeOptions(obj1, obj2){
let obj3 = {};
for (let attrname in obj1) { obj3[attrname] = obj1[attrname]; }
for (let attrname in obj2) { obj3[attrname] = obj2[attrname]; }
return obj3;
}

View File

@ -1,3 +1,5 @@
'use strict';
import languages from '../constants/languages'; import languages from '../constants/languages';
import { formatText } from './general_utils'; import { formatText } from './general_utils';
@ -27,4 +29,4 @@ export function getLangText(s, ...args) {
} }
} }
}; }

View File

@ -3,18 +3,25 @@
"version": "0.0.1", "version": "0.0.1",
"description": "Das neue web client for Ascribe", "description": "Das neue web client for Ascribe",
"main": "js/app.js", "main": "js/app.js",
"scripts": {
"lint": "eslint ./js"
},
"author": "Ascribe", "author": "Ascribe",
"license": "Copyright", "license": "Copyright",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"babel-eslint": "^3.1.11",
"babel-jest": "^4.0.0", "babel-jest": "^4.0.0",
"babelify": "^6.1.2", "babelify": "^6.1.2",
"bootstrap-sass": "^3.3.4", "bootstrap-sass": "^3.3.4",
"browser-sync": "^2.7.5", "browser-sync": "^2.7.5",
"browserify": "^9.0.8", "browserify": "^9.0.8",
"envify": "^3.4.0", "envify": "^3.4.0",
"eslint": "^0.22.1",
"eslint-plugin-react": "^2.5.0",
"gulp": "^3.8.11", "gulp": "^3.8.11",
"gulp-concat": "^2.5.2", "gulp-concat": "^2.5.2",
"gulp-eslint": "^0.13.2",
"gulp-if": "^1.2.5", "gulp-if": "^1.2.5",
"gulp-notify": "^2.2.0", "gulp-notify": "^2.2.0",
"gulp-sass": "^2.0.1", "gulp-sass": "^2.0.1",
@ -38,7 +45,8 @@
"react-datepicker": "~0.8.0", "react-datepicker": "~0.8.0",
"react-router": "^0.13.3", "react-router": "^0.13.3",
"shmui": "^0.1.0", "shmui": "^0.1.0",
"uglifyjs": "^2.4.10" "uglifyjs": "^2.4.10",
"react-datepicker": "~0.8.0"
}, },
"jest": { "jest": {
"scriptPreprocessor": "node_modules/babel-jest", "scriptPreprocessor": "node_modules/babel-jest",

View File

@ -1,4 +1,4 @@
$ascribe-accordion-list-item-height: 9em; $ascribe-accordion-list-item-height: 8em;
$ascribe-accordion-list-font: 'Source Sans Pro'; $ascribe-accordion-list-font: 'Source Sans Pro';
.ascribe-accordion-list-item { .ascribe-accordion-list-item {
@ -21,23 +21,15 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
height:100%; height:100%;
// ToDo: Include media queries for thumbnail // ToDo: Include media queries for thumbnail
.thumbnail-wrapper { .thumbnail-wrapper {
float:left; margin-left:0;
height:100%;
width:$ascribe-accordion-list-item-height;
overflow:hidden;
padding-left:0; padding-left:0;
padding-right:0;
img { img {
display:block;
height: $ascribe-accordion-list-item-height; height: $ascribe-accordion-list-item-height;
} }
} }
.info-wrapper {
float:left;
font-family: $ascribe-accordion-list-font;
margin-left: 2em;
padding-top: .75em;
h1 { h1 {
margin-top: .3em;
font-size: 2.25em; font-size: 2.25em;
} }
h3 { h3 {
@ -45,7 +37,6 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
margin: .7em 0 0 0; margin: .7em 0 0 0;
} }
} }
}
} }
.ascribe-accordion-list-item-table { .ascribe-accordion-list-item-table {

View File

@ -11,7 +11,7 @@
border-bottom-left-radius: 5px; border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px; border-bottom-right-radius: 5px;
border-bottom: 0.2em solid #E0E0E0; border-bottom: 0.2em solid #E0E0E0;
z-index:9999; z-index:1000;
} }
.piece-list-bulk-modal-clear-all { .piece-list-bulk-modal-clear-all {

View File

@ -38,15 +38,8 @@
float: none; float: none;
} }
.ascribe-table-header-row { .ascribe-table {
border-bottom: 2px solid #E0E0E0; margin-bottom:0;
padding: 0;
}
.ascribe-table-header-column {
display: table;
height:3em;
padding: 0;
} }
.ascribe-table-header-column > span { .ascribe-table-header-column > span {
@ -60,14 +53,6 @@
.ascribe-table-header-column > span > .glyphicon { .ascribe-table-header-column > span > .glyphicon {
font-size: .5em; font-size: .5em;
} }
/*
.ascribe-table-item:nth-child(even) {
background-color: #F5F5F5;
}*/
/*.ascribe-table-item:hover {
background-color: #EEEEEE;
}*/
.ascribe-table-item-column { .ascribe-table-item-column {
display: table; display: table;
@ -79,9 +64,9 @@
.ascribe-table-item-column > * { .ascribe-table-item-column > * {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
} }
.ascribe-table-item-selected { .ascribe-table-item-selected {