1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-29 00:58:03 +02:00

Merged in AD-894-group-collection-by-status (pull request #61)

Ad 894 group collection by status
This commit is contained in:
TimDaubenschuetz 2015-09-18 09:17:14 +02:00
commit 05fe507a3f
10 changed files with 262 additions and 55 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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>

View File

@ -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}

View 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;

View File

@ -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>
);

View File

@ -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>
);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}