Merge remote-tracking branch 'origin/master' into AD-901-add-lint-for-scss

Conflicts:
	sass/main.scss
This commit is contained in:
vrde 2015-08-31 19:09:09 +02:00
commit d2adc7a388
26 changed files with 444 additions and 183 deletions

View File

@ -24,8 +24,11 @@ gulp serve
Additionally, to work on the white labeling functionality, you need to edit your `/etc/hosts` file and add:
```
127.0.0.1 localhost.com
127.0.0.1 cc.localhost.com
127.0.0.1 localhost.com
127.0.0.1 cc.localhost.com
127.0.0.1 cyland.localhost.com
127.0.0.1 ikonotv.localhost.com
127.0.0.1 sluice.localhost.com
```

View File

@ -23,7 +23,7 @@ var argv = require('yargs').argv;
var server = require('./server.js').app;
var minifyCss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var opn = require('opn');
var config = {
@ -48,8 +48,7 @@ var config = {
},
filesToWatch: [
'build/css/*.css',
'build/js/*.js',
'node_modules/react-s3-fine_uploader/*.js'
'build/js/*.js'
]
};
@ -73,6 +72,9 @@ gulp.task('js:build', function() {
gulp.task('serve', ['browser-sync', 'run-server', 'sass:build', 'sass:watch', 'copy'], function() {
bundle(true);
// opens the browser window with the correct url, which is localhost.com
opn('http://www.localhost.com:3000');
});
gulp.task('jest', function(done) {
@ -93,7 +95,9 @@ gulp.task('browser-sync', function() {
browserSync({
files: config.filesToWatch,
proxy: 'http://localhost:4000',
port: 3000
port: 3000,
open: false, // does not open the browser-window anymore (handled manually)
ghostMode: false
});
});

View File

@ -25,8 +25,9 @@ class PieceListActions {
orderBy,
orderAsc,
filterBy,
'pieceList': [],
'pieceListCount': -1
pieceList: [],
pieceListCount: -1,
unfilteredPieceListCount: -1
});
// afterwards, we can load the list
@ -42,8 +43,9 @@ class PieceListActions {
orderBy,
orderAsc,
filterBy,
'pieceList': res.pieces,
'pieceListCount': res.count
pieceList: res.pieces,
pieceListCount: res.count,
unfilteredPieceListCount: res.unfiltered_count
});
resolve();
})

View File

@ -6,15 +6,16 @@ import Header from '../components/header';
import Footer from '../components/footer';
import GlobalNotification from './global_notification';
// let Link = Router.Link;
let RouteHandler = Router.RouteHandler;
import getRoutes from '../routes';
let RouteHandler = Router.RouteHandler;
let AscribeApp = React.createClass({
render() {
return (
<div className="container ascribe-default-app">
<Header />
<Header routes={getRoutes()} />
<RouteHandler />
<Footer />
<GlobalNotification />

View File

@ -84,6 +84,8 @@ let PieceContainer = React.createClass({
IT SHOULD BE REMOVED AND REPLACED WITH A BETTER SOLUTION ASAP!
ALSO, WE ENABLED THE LOAN BUTTON FOR IKONOTV TO LET THEM LOAN ON A PIECE LEVEL
*/
if(state && state.piece && state.piece.acl && typeof state.piece.acl.acl_loan !== 'undefined') {
@ -202,6 +204,7 @@ let PieceContainer = React.createClass({
);
}
},
render() {
if('title' in this.state.piece) {
return (

View File

@ -2,20 +2,21 @@
import React from 'react';
import Property from '../../../../../ascribe_forms/property';
import LoanContractListActions from '../../actions/loan_contract_list_actions';
import LoanContractListStore from '../../stores/loan_contract_list_store';
import LoanContractListActions from '../../../../../../actions/loan_contract_list_actions';
import LoanContractListStore from '../../../../../../stores/loan_contract_list_store';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import Form from './form';
import Property from './property';
import PropertyCollapsible from './property_collapsible';
import InputTextAreaToggable from './input_textarea_toggable';
import Form from '../../../../../ascribe_forms/form';
import ApiUrls from '../../constants/api_urls';
import ApiUrls from '../../../../../../constants/api_urls';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
let ContractForm = React.createClass({
@ -96,7 +97,7 @@ let ContractForm = React.createClass({
buttons={<button
type="submit"
className="btn ascribe-btn ascribe-btn-login">
{getLangText('SEND LOAN REQUEST')}
{getLangText('Send loan request')}
</button>}
spinner={
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
@ -104,7 +105,7 @@ let ContractForm = React.createClass({
</span>
}>
<div className="ascribe-form-header">
<h3>{getLangText('CONTRACT FORM')}</h3>
<h3>{getLangText('Contract form')}</h3>
</div>
<Property
name='artist_name'
@ -123,14 +124,15 @@ let ContractForm = React.createClass({
required/>
</Property>
{this.getContracts()}
<Property
<PropertyCollapsible
name='appendix'
label={getLangText('Appendix')}>
<input
type="text"
placeholder={getLangText('Add an appendix to the contract')}
required/>
</Property>
checkboxLabel={getLangText('Add appendix to the contract')}>
<span>{getLangText('Appendix')}</span>
<InputTextAreaToggable
rows={1}
editable={true}
placeholder={getLangText('This will be appended to the contract selected above')}/>
</PropertyCollapsible>
</Form>
);
}

View File

@ -68,8 +68,12 @@ let LoanForm = React.createClass({
return this.props.id;
},
handleOnBlur(event) {
LoanContractActions.fetchLoanContract(event.target.value);
handleOnChange(event) {
let potentialEmail = event.target.value;
if(potentialEmail.match(/.*@.*/)) {
LoanContractActions.fetchLoanContract(potentialEmail);
}
},
getContractCheckbox() {
@ -151,7 +155,7 @@ let LoanForm = React.createClass({
<Property
name='loanee'
label={getLangText('Loanee Email')}
onBlur={this.handleOnBlur}
onChange={this.handleOnChange}
editable={!this.props.email}
overrideForm={!!this.props.email}>
<input

View File

@ -20,6 +20,7 @@ import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
import HeaderNotificationDebug from './header_notification_debug';
import NavRoutesLinks from './nav_routes_links';
import { mergeOptions } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils';
@ -27,7 +28,8 @@ import { getLangText } from '../utils/lang_utils';
let Header = React.createClass({
propTypes: {
showAddWork: React.PropTypes.bool
showAddWork: React.PropTypes.bool,
routes: React.PropTypes.element
},
mixins: [Router.State],
@ -89,10 +91,9 @@ let Header = React.createClass({
},
render() {
let account = null;
let signup = null;
let collection = null;
let addNewWork = null;
let account;
let signup;
let navRoutesLinks;
if (this.state.currentUser.username){
account = (
<DropdownButton eventKey="1" title={this.state.currentUser.username}>
@ -101,9 +102,7 @@ let Header = React.createClass({
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
</DropdownButton>
);
collection = <NavItemLink to="pieces" query={this.getQuery()}>{getLangText('COLLECTION')}</NavItemLink>;
addNewWork = this.props.showAddWork ? <NavItemLink to="register_piece" query={{'slide_num': 0}}>+ {getLangText('NEW WORK')}</NavItemLink> : null;
navRoutesLinks = <NavRoutesLinks routes={this.props.routes} navbar right/>;
}
else {
account = <NavItemLink to="login">{getLangText('LOGIN')}</NavItemLink>;
@ -124,11 +123,10 @@ let Header = React.createClass({
</Nav>
<Nav navbar right>
<HeaderNotificationDebug show={false}/>
{addNewWork}
{collection}
{account}
{signup}
</Nav>
{navRoutesLinks}
</CollapsibleNav>
</Navbar>
</div>

View File

@ -0,0 +1,68 @@
'use strict';
import React from 'react';
import Nav from 'react-bootstrap/lib/Nav';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink';
import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
import { sanitizeList } from '../utils/general_utils';
let NavRoutesLinks = React.createClass({
propTypes: {
routes: React.PropTypes.element
},
extractLinksFromRoutes(node, i) {
if(!node) {
return;
}
node = node.props;
let links = node.children.map((child, j) => {
// check if this a candidate for a link generation
if(child.props.headerTitle && typeof child.props.headerTitle === 'string') {
// also check if it is a candidate for generating a dropdown menu
if(child.props.children && child.props.children.length > 0) {
return (
<DropdownButton title={child.props.headerTitle} key={j}>
{this.extractLinksFromRoutes(child, i++)}
</DropdownButton>
);
} else if(i === 1) {
// if the node's child is actually a node of level one (a child of a node), we're
// returning a DropdownButton matching MenuItemLink
return (
<MenuItemLink to={child.props.name} key={j}>{child.props.headerTitle}</MenuItemLink>
);
} else if(i === 0) {
return (
<NavItemLink to={child.props.name} key={j}>{child.props.headerTitle}</NavItemLink>
);
} else {
return null;
}
} else {
return null;
}
});
// remove all nulls from the list of generated links
return sanitizeList(links);
},
render() {
return (
<Nav {...this.props}>
{this.extractLinksFromRoutes(this.props.routes, 0)}
</Nav>
);
}
});
export default NavRoutesLinks;

View File

@ -69,7 +69,7 @@ let PieceList = React.createClass({
},
componentDidUpdate() {
if (this.props.redirectTo && this.state.pieceListCount === 0) {
if (this.props.redirectTo && this.state.unfilteredPieceListCount === 0) {
// FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => this.transitionTo(this.props.redirectTo), 0);
}

View File

@ -1,25 +0,0 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import App from './ascribe_app';
import AppConstants from '../constants/application_constants';
let Route = Router.Route;
let Redirect = Router.Redirect;
let baseUrl = AppConstants.baseUrl;
function getRoutes(commonRoutes) {
return (
<Route name="app" path={baseUrl} handler={App}>
<Redirect from={baseUrl} to="login" />
<Redirect from={baseUrl + '/'} to="login" />
{commonRoutes}
</Route>
);
}
export default getRoutes;

View File

@ -7,6 +7,8 @@ import Header from '../../header';
import Footer from '../../footer';
import GlobalNotification from '../../global_notification';
import getRoutes from './prize_routes';
let RouteHandler = Router.RouteHandler;
let PrizeApp = React.createClass({
@ -14,10 +16,14 @@ let PrizeApp = React.createClass({
render() {
let header = null;
let subdomain = window.location.host.split('.')[0];
let ROUTES = getRoutes(null, subdomain);
if (this.isActive('landing') || this.isActive('login') || this.isActive('signup')) {
header = <Hero />;
} else {
header = <Header showAddWork={false} />;
header = <Header showAddWork={false} routes={ROUTES}/>;
}
return (

View File

@ -29,8 +29,8 @@ function getRoutes() {
<Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="register_piece" path="register_piece" handler={PrizeRegisterPiece} />
<Route name="pieces" path="collection" handler={PrizePieceList} />
<Route name="register_piece" path="register_piece" handler={PrizeRegisterPiece} headerTitle="+ NEW WORK" />
<Route name="pieces" path="collection" handler={PrizePieceList} headerTitle="COLLECTION" />
<Route name="piece" path="pieces/:pieceId" handler={PrizePieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} />

View File

@ -216,6 +216,8 @@ let CylandRegisterPiece = React.createClass({
gallery="Cyland Archive"
startdate={today}
enddate={datetimeWhenWeAllWillBeFlyingCoolHoverboardsAndDinosaursWillLiveAgain}
showStartDate={false}
showEndDate={false}
showPersonalMessage={false}
handleSuccess={this.handleLoanSuccess}>
<Property

View File

@ -14,8 +14,11 @@ import GlobalNotificationModel from '../../../../../../models/global_notificatio
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
import AclProxy from '../../../../../acl_proxy';
import AclButton from '../../../../../ascribe_buttons/acl_button';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
@ -66,9 +69,19 @@ let IkonotvAccordionListItem = React.createClass({
aclObject={this.props.content.acl}
aclName="acl_submit">
<IkonotvSubmitButton
className="pull-right"
piece={this.props.content}
handleSuccess={this.handleSubmitSuccess}/>
className="btn-xs pull-right"
handleSuccess={this.handleSubmitSuccess}
piece={this.props.content}/>
</AclProxy>
<AclProxy
aclObject={this.props.content.acl}
aclName="acl_submitted">
<button
disabled
className="btn btn-default btn-xs pull-right">
{getLangText('Loaned to IkonoTV')} <span className="glyphicon glyphicon-ok"
aria-hidden="true"></span>
</button>
</AclProxy>
</div>
);

View File

@ -1,10 +1,18 @@
'use strict';
import React from 'react';
import Moment from 'moment';
import classNames from 'classnames';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import LoanForm from '../../../../../ascribe_forms/form_loan';
import Property from '../../../../../ascribe_forms/property';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
import ApiUrls from '../../../../../../constants/api_urls';
import { getLangText } from '../../../../../../utils/lang_utils';
let IkonotvSubmitButton = React.createClass({
@ -17,23 +25,50 @@ let IkonotvSubmitButton = React.createClass({
getSubmitButton() {
return (
<button
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
className={classNames('btn', 'btn-default', this.props.className)}>
{getLangText('Loan to IkonoTV')}
</button>
);
},
render() {
let today = new Moment();
let enddate = new Moment();
enddate.add(1, 'years');
return (
<ModalWrapper
trigger={this.getSubmitButton()}
handleSuccess={this.props.handleSuccess}
title={getLangText('Loan to IkonoTV')}>
title={getLangText('Loan to IkonoTV archive')}>
<LoanForm
id={{piece_id: this.props.piece.id}}
url={ApiUrls.ownership_loans_pieces}
email="submissions@ikono.org"
startdate={today}
enddate={enddate}
gallery="IkonoTV archive"
showPersonalMessage={false}
handleSuccess={this.props.handleSuccess}>
<Property
name="terms"
className="ascribe-settings-property-collapsible-toggle"
style={{paddingBottom: 0}}>
<InputCheckbox>
<span>
{' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '}
(<a href="https://d1qjsxua1o9x03.cloudfront.net/live/743394beff4b1282ba735e5e3723ed74/contract/bbc92f1d-4504-49f8-818c-8dd7113c6e06.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
{getLangText('read')}
</a>)
</span>
</InputCheckbox>
</Property>
</LoanForm>
</ModalWrapper>
);
}
});
export default IkonotvSubmitButton;
export default IkonotvSubmitButton;

View File

@ -0,0 +1,176 @@
'use strict';
import React from 'react';
import PieceActions from '../../../../../../actions/piece_actions';
import PieceStore from '../../../../../../stores/piece_store';
import PieceListActions from '../../../../../../actions/piece_list_actions';
import PieceListStore from '../../../../../../stores/piece_list_store';
import UserStore from '../../../../../../stores/user_store';
import Piece from '../../../../../../components/ascribe_detail/piece';
import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions';
import AclButtonList from '../../../../../ascribe_buttons/acl_button_list';
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
import HistoryIterator from '../../../../../ascribe_detail/history_iterator';
import DetailProperty from '../../../../../ascribe_detail/detail_property';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import AclProxy from '../../../../../acl_proxy';
import AppConstants from '../../../../../../constants/application_constants';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvPieceContainer = React.createClass({
getInitialState() {
return mergeOptions(
PieceStore.getState(),
UserStore.getState(),
PieceListStore.getState()
);
},
componentDidMount() {
PieceStore.listen(this.onChange);
PieceActions.fetchOne(this.props.params.pieceId);
UserStore.listen(this.onChange);
PieceListStore.listen(this.onChange);
},
componentWillReceiveProps(nextProps) {
if(this.props.params.pieceId !== nextProps.params.pieceId) {
PieceActions.updatePiece({});
PieceActions.fetchOne(nextProps.params.pieceId);
}
},
componentWillUnmount() {
// Every time we're leaving the piece detail page,
// just reset the piece that is saved in the piece store
// as it will otherwise display wrong/old data once the user loads
// the piece detail a second time
PieceActions.updatePiece({});
PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
loadPiece() {
PieceActions.fetchOne(this.props.params.pieceId);
},
handleSubmitSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
this.loadPiece();
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
getActions(){
if (this.state.piece &&
this.state.piece.request_action &&
this.state.piece.request_action.length > 0) {
return (
<ListRequestActions
pieceOrEditions={this.state.piece}
currentUser={this.state.currentUser}
handleSuccess={this.loadPiece}
requestActions={this.state.piece.request_action}/>
);
}
else {
//We need to disable the normal acl_loan because we're inserting a custom acl_loan button
let availableAcls;
if(this.state.piece && this.state.piece.acl && typeof this.state.piece.acl.acl_loan !== 'undefined') {
// make a copy to not have side effects
availableAcls = mergeOptions({}, this.state.piece.acl);
availableAcls.acl_loan = false;
}
return (
<AclButtonList
className="text-center ascribe-button-list"
availableAcls={availableAcls}
editions={this.state.piece}
handleSuccess={this.loadPiece}>
<AclProxy
aclObject={availableAcls}
aclName="acl_submit">
<IkonotvSubmitButton
className="btn-sm"
handleSuccess={this.handleSubmitSuccess}
piece={this.state.piece}/>
</AclProxy>
<DeleteButton
handleSuccess={this.handleDeleteSuccess}
piece={this.state.piece}/>
</AclButtonList>
);
}
},
render() {
if('title' in this.state.piece) {
return (
<Piece
piece={this.state.piece}
loadPiece={this.loadPiece}
header={
<div className="ascribe-detail-header">
<hr style={{marginTop: 0}}/>
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
<DetailProperty label="BY" value={this.state.piece.artist_name} />
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
<hr/>
</div>
}
subheader={
<div className="ascribe-detail-header">
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } />
<DetailProperty label={getLangText('ID')} value={ this.state.piece.bitcoin_id } ellipsis={true} />
<hr/>
</div>
}
buttons={this.getActions()}>
<CollapsibleParagraph
title={getLangText('Loan History')}
show={this.state.piece.loan_history && this.state.piece.loan_history.length > 0}>
<HistoryIterator
history={this.state.piece.loan_history} />
</CollapsibleParagraph>
</Piece>
);
} else {
return (
<div className="fullpage-spinner">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>
);
}
}
});
export default IkonotvPieceContainer;

View File

@ -1,80 +0,0 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions';
import ContractForm from './ascribe_forms/ikonotv_contract_form';
import RegisterPieceForm from '../../../../../components/ascribe_forms/form_register_piece';
import Property from '../../../../../components/ascribe_forms/property';
import InputCheckbox from '../../../../../components/ascribe_forms/input_checkbox';
import GlobalNotificationModel from '../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
import { getLangText } from '../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
let IkonotvRegisterPiece = React.createClass({
mixins: [Router.Navigation],
getInitialState(){
return mergeOptions(
UserStore.getState(),
WhitelabelStore.getState());
},
componentDidMount() {
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() {
if (this.state.currentUser &&
this.state.whitelabel &&
this.state.whitelabel.user &&
this.state.currentUser.email === this.state.whitelabel.user){
return (
<ContractForm />
);
}
return (
<div className="ascribe-form-bordered ascribe-form-wrapper">
<RegisterPieceForm
enableLocalHashing={false}
headerMessage={getLangText('Register your work')}
submitMessage={getLangText('Register')}
handleSuccess={this.handleRegisterSuccess}/>
</div>
);
}
});
export default IkonotvRegisterPiece;

View File

@ -0,0 +1,16 @@
'use strict';
import React from 'react';
import ContractForm from '../../../../../components/ascribe_forms/contract_form';
let IkonotvRequestLoan = React.createClass({
render() {
return (
<ContractForm />
);
}
});
export default IkonotvRequestLoan;

View File

@ -7,21 +7,26 @@ import Footer from '../../footer';
import GlobalNotification from '../../global_notification';
import getRoutes from './wallet_routes';
let RouteHandler = Router.RouteHandler;
let WalletApp = React.createClass({
mixins: [Router.State],
render() {
let header = null;
let subdomain = window.location.host.split('.')[0];
let ROUTES = getRoutes(null, subdomain);
let header = null;
if ((this.isActive('landing') || this.isActive('login') || this.isActive('signup'))
&& (['ikonotv', 'cyland']).indexOf(subdomain) > -1) {
header = (
<div className="hero"/>);
} else {
header = <Header showAddWork={true} />;
header = <Header showAddWork={true} routes={ROUTES} />;
}
return (
<div className="container ascribe-prize-app">
{header}

View File

@ -12,15 +12,16 @@ import PieceList from '../../../components/piece_list';
import PieceContainer from '../../../components/ascribe_detail/piece_container';
import EditionContainer from '../../../components/ascribe_detail/edition_container';
import SettingsContainer from '../../../components/settings_container';
import RegisterPiece from '../../../components/register_piece';
// specific components
import CylandLanding from './components/cyland/cyland_landing';
import CylandPieceContainer from './components/cyland/ascribe_detail/cyland_piece_container';
import CylandRegisterPiece from './components/cyland/cyland_register_piece';
import CylandPieceList from './components/cyland/cyland_piece_list';
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece';
import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan';
import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container';
import CCRegisterPiece from './components/cc/cc_register_piece';
@ -40,8 +41,8 @@ let ROUTES = {
<Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="register_piece" path="register_piece" handler={CylandRegisterPiece} />
<Route name="pieces" path="collection" handler={CylandPieceList} />
<Route name="register_piece" path="register_piece" handler={CylandRegisterPiece} headerTitle="+ NEW WORK" />
<Route name="pieces" path="collection" handler={CylandPieceList} headerTitle="COLLECTION" />
<Route name="piece" path="pieces/:pieceId" handler={CylandPieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} />
@ -55,8 +56,8 @@ let ROUTES = {
<Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="register_piece" path="register_piece" handler={CCRegisterPiece} />
<Route name="pieces" path="collection" handler={PieceList} />
<Route name="register_piece" path="register_piece" handler={CCRegisterPiece} headerTitle="+ NEW WORK" />
<Route name="pieces" path="collection" handler={PieceList} headerTitle="COLLECTION" />
<Route name="piece" path="pieces/:pieceId" handler={PieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} />
@ -64,14 +65,16 @@ let ROUTES = {
),
'ikonotv': (
<Route name="app" path={baseUrl} handler={WalletApp}>
<Route name="landing" path={baseUrl} handler={IkonotvRegisterPiece} />
<Redirect from={baseUrl} to="login" />
<Redirect from={baseUrl + '/'} to="login" />
<Route name="login" path="login" handler={LoginContainer} />
<Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="register_piece" path="register_piece" handler={IkonotvRegisterPiece} />
<Route name="pieces" path="collection" handler={IkonotvPieceList} />
<Route name="piece" path="pieces/:pieceId" handler={CylandPieceContainer} />
<Route name="request_loan" path="request_loan" handler={IkonotvRequestLoan}/>
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK"/>
<Route name="pieces" path="collection" handler={IkonotvPieceList} headerTitle="COLLECTION"/>
<Route name="piece" path="pieces/:pieceId" handler={IkonotvPieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} />
</Route>

View File

@ -5,7 +5,8 @@ import Router from 'react-router';
import getPrizeRoutes from './components/whitelabel/prize/prize_routes';
import getWalletRoutes from './components/whitelabel/wallet/wallet_routes';
import getDefaultRoutes from './components/routes';
import App from './components/ascribe_app';
import PieceList from './components/piece_list';
import PieceContainer from './components/ascribe_detail/piece_container';
@ -23,19 +24,25 @@ import RegisterPiece from './components/register_piece';
import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard';
import AppConstants from './constants/application_constants';
let Route = Router.Route;
let Redirect = Router.Redirect;
let baseUrl = AppConstants.baseUrl;
const COMMON_ROUTES = (
<Route>
<Route name="app" path={baseUrl} handler={App}>
<Redirect from={baseUrl} to="login" />
<Redirect from={baseUrl + '/'} to="login" />
<Route name="signup" path="signup" handler={SignupContainer} />
<Route name="login" path="login" handler={LoginContainer} />
<Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="pieces" path="collection" handler={PieceList} />
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK" />
<Route name="pieces" path="collection" handler={PieceList} headerTitle="COLLECTION" />
<Route name="piece" path="pieces/:pieceId" handler={PieceContainer} />
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
<Route name="settings" path="settings" handler={SettingsContainer} />
<Route name="coa_verify" path="verify" handler={CoaVerifyContainer} />
<Route name="prizes" path="prizes" handler={PrizesDashboard} />
@ -51,7 +58,7 @@ function getRoutes(type, subdomain) {
} else if(type === 'wallet') {
routes = getWalletRoutes(COMMON_ROUTES, subdomain);
} else {
routes = getDefaultRoutes(COMMON_ROUTES);
routes = COMMON_ROUTES;
}
return routes;

View File

@ -21,6 +21,7 @@ class PieceListStore {
this.pieceList = [];
// -1 specifies that the store is currently loading
this.pieceListCount = -1;
this.unfilteredPieceListCount = -1;
this.page = 1;
this.pageSize = 10;
this.search = '';
@ -30,13 +31,14 @@ class PieceListStore {
this.bindActions(PieceListActions);
}
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, filterBy }) {
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, unfilteredPieceListCount, filterBy }) {
this.page = page;
this.pageSize = pageSize;
this.search = search;
this.orderAsc = orderAsc;
this.orderBy = orderBy;
this.pieceListCount = pieceListCount;
this.unfilteredPieceListCount = unfilteredPieceListCount;
this.filterBy = filterBy;
/**

View File

@ -26,6 +26,23 @@ export function sanitize(obj, filterFn) {
return obj;
}
/**
* Removes all falsy values (undefined, null, false, ...) from a list/array
* @param {array} l the array to sanitize
* @return {array} the sanitized array
*/
export function sanitizeList(l) {
let sanitizedList = [];
for(let i = 0; i < l.length; i++) {
if(l[i]) {
sanitizedList.push(l[i]);
}
}
return sanitizedList;
}
/**
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
*/

View File

@ -69,6 +69,7 @@
"lodash": "^3.9.3",
"moment": "^2.10.6",
"object-assign": "^2.0.0",
"opn": "^3.0.2",
"q": "^1.4.1",
"raven-js": "^1.1.19",
"react": "^0.13.2",

View File

@ -115,9 +115,7 @@ hr {
.img-brand {
height: 45px;
margin: 5px 0;
padding: 0;
height: 60px;
}
.truncate {
@ -381,7 +379,7 @@ hr {
margin-top: 1px;
}
.margin-left-2px{
.margin-left-2px {
margin-left: 2px;
}