mirror of
https://github.com/ascribe/onion.git
synced 2024-11-15 01:25:17 +01:00
Merged in AD-894-group-collection-by-status (pull request #61)
Ad 894 group collection by status
This commit is contained in:
commit
05fe507a3f
@ -21,7 +21,7 @@ let AccordionList = React.createClass({
|
||||
);
|
||||
} else if(this.props.count === 0) {
|
||||
return (
|
||||
<div>
|
||||
<div className="ascribe-accordion-list-placeholder">
|
||||
<p className="text-center">{getLangText('We could not find any works related to you...')}</p>
|
||||
<p className="text-center">{getLangText('To register one, click')} <a href="register_piece">{getLangText('here')}</a>!</p>
|
||||
</div>
|
||||
|
@ -14,7 +14,20 @@ let PieceListToolbar = React.createClass({
|
||||
propTypes: {
|
||||
className: React.PropTypes.string,
|
||||
searchFor: React.PropTypes.func,
|
||||
filterParams: React.PropTypes.array,
|
||||
filterParams: React.PropTypes.arrayOf(
|
||||
React.PropTypes.shape({
|
||||
label: React.PropTypes.string,
|
||||
items: React.PropTypes.arrayOf(
|
||||
React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.shape({
|
||||
key: React.PropTypes.string,
|
||||
label: React.PropTypes.string
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
),
|
||||
filterBy: React.PropTypes.object,
|
||||
applyFilterBy: React.PropTypes.func,
|
||||
orderParams: React.PropTypes.array,
|
||||
|
@ -3,20 +3,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||
|
||||
import { getLangText } from '../../utils/lang_utils.js';
|
||||
|
||||
|
||||
let PieceListToolbarFilterWidgetFilter = React.createClass({
|
||||
propTypes: {
|
||||
// An array of either strings (which represent acl enums) or objects of the form
|
||||
//
|
||||
// {
|
||||
// key: <acl enum>,
|
||||
// label: <a human readable string>
|
||||
// }
|
||||
//
|
||||
filterParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired,
|
||||
filterParams: React.PropTypes.arrayOf(
|
||||
React.PropTypes.shape({
|
||||
label: React.PropTypes.string,
|
||||
items: React.PropTypes.arrayOf(
|
||||
React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.shape({
|
||||
key: React.PropTypes.string,
|
||||
label: React.PropTypes.string
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
).isRequired,
|
||||
filterBy: React.PropTypes.object,
|
||||
applyFilterBy: React.PropTypes.func
|
||||
},
|
||||
@ -79,35 +85,53 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({
|
||||
<DropdownButton
|
||||
title={filterIcon}
|
||||
className="ascribe-piece-list-toolbar-filter-widget">
|
||||
<li style={{'textAlign': 'center'}}>
|
||||
<em>{getLangText('Show works I can')}:</em>
|
||||
</li>
|
||||
{this.props.filterParams.map((param, i) => {
|
||||
let label;
|
||||
|
||||
if(typeof param !== 'string') {
|
||||
label = param.label;
|
||||
param = param.key;
|
||||
} else {
|
||||
param = param;
|
||||
label = param.split('_')[1];
|
||||
}
|
||||
|
||||
{/* We iterate over filterParams, to receive the label and then for each
|
||||
label also iterate over its items, to get all filterable options */}
|
||||
{this.props.filterParams.map(({ label, items }, i) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
onClick={this.filterBy(param)}
|
||||
className="filter-widget-item">
|
||||
<div className="checkbox-line">
|
||||
<span>
|
||||
{getLangText(label)}
|
||||
</span>
|
||||
<input
|
||||
readOnly
|
||||
type="checkbox"
|
||||
checked={this.props.filterBy[param]} />
|
||||
</div>
|
||||
</MenuItem>
|
||||
<div>
|
||||
<li
|
||||
style={{'textAlign': 'center'}}
|
||||
key={i}>
|
||||
<em>{label}:</em>
|
||||
</li>
|
||||
{items.map((param, j) => {
|
||||
|
||||
// As can be seen in the PropTypes, a param can either
|
||||
// be a string or an object of the shape:
|
||||
//
|
||||
// {
|
||||
// key: <String>,
|
||||
// label: <String>
|
||||
// }
|
||||
//
|
||||
// This is why we need to distinguish between both here.
|
||||
if(typeof param !== 'string') {
|
||||
label = param.label;
|
||||
param = param.key;
|
||||
} else {
|
||||
param = param;
|
||||
label = param.split('acl_')[1].replace(/_/g, ' ');
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
key={j}
|
||||
onClick={this.filterBy(param)}
|
||||
className="filter-widget-item">
|
||||
<div className="checkbox-line">
|
||||
<span>
|
||||
{getLangText(label)}
|
||||
</span>
|
||||
<input
|
||||
readOnly
|
||||
type="checkbox"
|
||||
checked={this.props.filterBy[param]} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</DropdownButton>
|
||||
|
@ -15,6 +15,8 @@ import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_l
|
||||
|
||||
import Pagination from './ascribe_pagination/pagination';
|
||||
|
||||
import PieceListFilterDisplay from './piece_list_filter_display';
|
||||
|
||||
import GlobalAction from './global_action';
|
||||
import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal';
|
||||
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
|
||||
@ -22,6 +24,8 @@ import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
|
||||
import AppConstants from '../constants/application_constants';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
import { getLangText } from '../utils/lang_utils';
|
||||
|
||||
|
||||
let PieceList = React.createClass({
|
||||
propTypes: {
|
||||
@ -31,7 +35,6 @@ let PieceList = React.createClass({
|
||||
filterParams: React.PropTypes.array,
|
||||
orderParams: React.PropTypes.array,
|
||||
orderBy: React.PropTypes.string
|
||||
|
||||
},
|
||||
|
||||
mixins: [Router.Navigation, Router.State],
|
||||
@ -40,13 +43,14 @@ let PieceList = React.createClass({
|
||||
return {
|
||||
accordionListItemType: AccordionListItemWallet,
|
||||
orderParams: ['artist_name', 'title'],
|
||||
filterParams: [
|
||||
'acl_transfer',
|
||||
'acl_consign',
|
||||
{
|
||||
key: 'acl_create_editions',
|
||||
label: 'create editions'
|
||||
}]
|
||||
filterParams: [{
|
||||
label: getLangText('Show works I can'),
|
||||
items: [
|
||||
'acl_transfer',
|
||||
'acl_consign',
|
||||
'acl_create_editions'
|
||||
]
|
||||
}]
|
||||
};
|
||||
},
|
||||
getInitialState() {
|
||||
@ -91,8 +95,8 @@ let PieceList = React.createClass({
|
||||
// the site should go to the top
|
||||
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
||||
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
||||
this.state.orderBy, this.state.orderAsc,
|
||||
this.state.filterBy);
|
||||
this.state.orderBy, this.state.orderAsc,
|
||||
this.state.filterBy);
|
||||
};
|
||||
},
|
||||
|
||||
@ -149,9 +153,6 @@ let PieceList = React.createClass({
|
||||
let loadingElement = (<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />);
|
||||
let AccordionListItemType = this.props.accordionListItemType;
|
||||
|
||||
//<GlobalAction requestActions={this.state.requestActions} />
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PieceListToolbar
|
||||
@ -166,6 +167,9 @@ let PieceList = React.createClass({
|
||||
{this.props.customSubmitButton}
|
||||
</PieceListToolbar>
|
||||
<PieceListBulkModal className="ascribe-piece-list-bulk-modal" />
|
||||
<PieceListFilterDisplay
|
||||
filterBy={this.state.filterBy}
|
||||
filterParams={this.props.filterParams}/>
|
||||
<AccordionList
|
||||
className="ascribe-accordion-list"
|
||||
changeOrder={this.accordionChangeOrder}
|
||||
|
118
js/components/piece_list_filter_display.js
Normal file
118
js/components/piece_list_filter_display.js
Normal file
@ -0,0 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
|
||||
let PieceListFilterDisplay = React.createClass({
|
||||
propTypes: {
|
||||
filterBy: React.PropTypes.object,
|
||||
filterParams: React.PropTypes.arrayOf(
|
||||
React.PropTypes.shape({
|
||||
label: React.PropTypes.string,
|
||||
items: React.PropTypes.arrayOf(
|
||||
React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.shape({
|
||||
key: React.PropTypes.string,
|
||||
label: React.PropTypes.string
|
||||
})
|
||||
])
|
||||
)
|
||||
})
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes the above described filterParams prop,
|
||||
* assigns it it's true filterBy value that is derived from the filterBy prop
|
||||
* and also - if there wasn't already one defined - generates a label
|
||||
* @return {object}
|
||||
*/
|
||||
transformFilterParamsItemsToBools() {
|
||||
let { filterParams, filterBy } = this.props;
|
||||
|
||||
return filterParams.map((filterParam) => {
|
||||
return {
|
||||
label: filterParam.label,
|
||||
items: filterParam.items.map((item) => {
|
||||
if(typeof item !== 'string' && typeof item.key === 'string' && typeof item.label === 'string') {
|
||||
return {
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
value: filterBy[item.key] || false
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
key: item,
|
||||
label: item.split('acl_')[1].replace(/_/g, ' '),
|
||||
value: filterBy[item] || false
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes the list of filters generated in transformFilterParamsItemsToBools and
|
||||
* transforms them into human readable text.
|
||||
* @param {Object} filtersWithLabel An object of the shape {key: <String>, label: <String>, value: <Bool>}
|
||||
* @return {string} A human readable string
|
||||
*/
|
||||
getFilterText(filtersWithLabel) {
|
||||
let filterTextList = filtersWithLabel
|
||||
// Iterate over all provided filterLabels and generate a list
|
||||
// of human readable strings
|
||||
.map((filterWithLabel) => {
|
||||
let activeFilterWithLabel = filterWithLabel
|
||||
.items
|
||||
// If the filter is active (which it is when its value is true),
|
||||
// we're going to include it's label into a list,
|
||||
// otherwise we'll just return nothing
|
||||
.map((filter) => {
|
||||
if(filter.value) {
|
||||
return filter.label;
|
||||
}
|
||||
})
|
||||
// if nothing is returned, that index is 'undefined'.
|
||||
// As we only want active filter, we filter out all falsy values e.g. undefined
|
||||
.filter((filterName) => !!filterName)
|
||||
// and join the result to a string
|
||||
.join(', ');
|
||||
|
||||
// If this actually didn't generate an empty string,
|
||||
// we take the label and concat it to the result.
|
||||
if(activeFilterWithLabel) {
|
||||
return filterWithLabel.label + ': ' + activeFilterWithLabel;
|
||||
}
|
||||
})
|
||||
// filter out strings that are undefined, as their filter's were not activated
|
||||
.filter((filterText) => !!filterText)
|
||||
// if there are multiple sentences, capitalize the first one and lowercase the others
|
||||
.map((filterText, i) => i === 0 ? filterText.charAt(0).toUpperCase() + filterText.substr(1) : filterText.charAt(0).toLowerCase() + filterText.substr(1))
|
||||
.join(' and ');
|
||||
|
||||
return filterTextList;
|
||||
},
|
||||
|
||||
render() {
|
||||
let { filterBy } = this.props;
|
||||
let filtersWithLabel = this.transformFilterParamsItemsToBools();
|
||||
|
||||
// do not show the FilterDisplay if there are no filters applied
|
||||
if(filterBy && Object.keys(filterBy).length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="ascribe-piece-list-filter-display col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2">
|
||||
{this.getFilterText(filtersWithLabel)}
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default PieceListFilterDisplay;
|
@ -8,6 +8,8 @@ import UserStore from '../../../../../stores/user_store';
|
||||
|
||||
import CylandAccordionListItem from './ascribe_accordion_list/cyland_accordion_list_item';
|
||||
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
|
||||
|
||||
let CylandPieceList = React.createClass({
|
||||
getInitialState() {
|
||||
@ -33,6 +35,13 @@ let CylandPieceList = React.createClass({
|
||||
<PieceList
|
||||
redirectTo="register_piece"
|
||||
accordionListItemType={CylandAccordionListItem}
|
||||
filterParams={[{
|
||||
label: getLangText('Show works I have'),
|
||||
items: [{
|
||||
key: 'acl_loaned',
|
||||
label: getLangText('loaned to Cyland')
|
||||
}]
|
||||
}]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -8,6 +8,9 @@ import UserStore from '../../../../../stores/user_store';
|
||||
|
||||
import IkonotvAccordionListItem from './ascribe_accordion_list/ikonotv_accordion_list_item';
|
||||
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
|
||||
|
||||
let IkonotvPieceList = React.createClass({
|
||||
getInitialState() {
|
||||
return UserStore.getState();
|
||||
@ -32,7 +35,13 @@ let IkonotvPieceList = React.createClass({
|
||||
<PieceList
|
||||
redirectTo="register_piece"
|
||||
accordionListItemType={IkonotvAccordionListItem}
|
||||
/>
|
||||
filterParams={[{
|
||||
label: getLangText('Show works I have'),
|
||||
items: [{
|
||||
key: 'acl_loaned',
|
||||
label: getLangText('loaned to IkonoTV')
|
||||
}]
|
||||
}]}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,10 @@ $ascribe-accordion-list-item-height: 8em;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.ascribe-accordion-list-placeholder {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.ascribe-accordion-list-item {
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
|
@ -54,8 +54,7 @@
|
||||
}
|
||||
|
||||
.filter-widget-item {
|
||||
|
||||
> a {
|
||||
a {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
@ -64,6 +63,13 @@
|
||||
.checkbox-line {
|
||||
height: 25px;
|
||||
position: relative;
|
||||
color: #333333;
|
||||
|
||||
/* Fuck you react-bootstrap */
|
||||
&:hover {
|
||||
background-color: $dropdown-link-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
|
@ -487,3 +487,23 @@ hr {
|
||||
.ascribe-progress-bar-xs {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
|
||||
.ascribe-piece-list-filter-display {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
|
||||
> span {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: #616161;
|
||||
|
||||
padding-left: .3em;
|
||||
}
|
||||
|
||||
> hr {
|
||||
margin-top: .15em;
|
||||
margin-bottom: .1em;
|
||||
border-color: #ccc;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user