From 4f2aa430f2d62f3abd456fbb86b963a7548eaba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 29 Jul 2015 18:03:49 +0200 Subject: [PATCH 01/21] refactor ascribe table and add prizes dashboard boilerplate code --- .gitignore | 2 + js/actions/prize_list_actions.js | 34 ++++++++ .../prizes_dashboard.js | 82 +++++++++++++++++++ js/components/ascribe_table/table_header.js | 5 -- .../ascribe_table/table_header_item.js | 1 - js/constants/api_urls.js | 3 +- js/fetchers/prize_list_fetcher.js | 12 +++ js/mixins/table_column_mixin.js | 24 ------ js/routes.js | 3 + js/stores/prize_list_store.js | 20 +++++ 10 files changed, 155 insertions(+), 31 deletions(-) create mode 100644 js/actions/prize_list_actions.js create mode 100644 js/components/ascribe_prizes_dashboard/prizes_dashboard.js create mode 100644 js/fetchers/prize_list_fetcher.js delete mode 100644 js/mixins/table_column_mixin.js create mode 100644 js/stores/prize_list_store.js diff --git a/.gitignore b/.gitignore index 04138bb0..b39d7830 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ lib-cov *.out *.pid *.gz +*.sublime-project +*.sublime-workspace pids logs diff --git a/js/actions/prize_list_actions.js b/js/actions/prize_list_actions.js new file mode 100644 index 00000000..fddf1a04 --- /dev/null +++ b/js/actions/prize_list_actions.js @@ -0,0 +1,34 @@ +'use strict'; + +import alt from '../alt'; +import Q from 'q'; + +import PrizeListFetcher from '../fetchers/prize_list_fetcher'; + +class PrizeListActions { + constructor() { + this.generateActions( + 'updatePrizeList' + ); + } + + fetchPrizeList() { + return Q.Promise((resolve, reject) => { + PrizeListFetcher + .fetch() + .then((res) => { + this.actions.updatePrizeList({ + prizeList: res.prizes, + prizeListCount: res.count + }); + resolve(res); + }) + .catch((err) => { + console.logGlobal(err); + reject(err); + }); + }); + } +} + +export default alt.createActions(PrizeListActions); \ No newline at end of file diff --git a/js/components/ascribe_prizes_dashboard/prizes_dashboard.js b/js/components/ascribe_prizes_dashboard/prizes_dashboard.js new file mode 100644 index 00000000..b4c695f4 --- /dev/null +++ b/js/components/ascribe_prizes_dashboard/prizes_dashboard.js @@ -0,0 +1,82 @@ +'use strict'; + +import React from 'react'; + +import PrizeListActions from '../../actions/prize_list_actions'; +import PrizeListStore from '../../stores/prize_list_store'; + +import Table from '../ascribe_table/table'; +import TableItem from '../ascribe_table/table_item'; +import TableItemText from '../ascribe_table/table_item_text'; + +import { ColumnModel} from '../ascribe_table/models/table_models'; +import { getLangText } from '../../utils/lang_utils'; + +let PrizesDashboard = React.createClass({ + + getInitialState() { + return PrizeListStore.getState(); + }, + + componentDidMount() { + PrizeListStore.listen(this.onChange); + PrizeListActions.fetchPrizeList(); + }, + + componentWillUnmount() { + PrizeListStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + getColumnList() { + return [ + new ColumnModel( + (item) => { + return { + 'content': item.name + }; }, + 'name', + getLangText('Name'), + TableItemText, + 6, + false, + null + ), + new ColumnModel( + (item) => { + return { + 'content': item.domain + }; }, + 'domain', + getLangText('Domain'), + TableItemText, + 1, + false, + null + ) + ]; + }, + + render() { + return ( + + {this.state.prizeList.map((item, i) => { + return ( + + ); + })} +
+ ); + } +}); + +export default PrizesDashboard; \ No newline at end of file diff --git a/js/components/ascribe_table/table_header.js b/js/components/ascribe_table/table_header.js index 8f138fb4..f807627b 100644 --- a/js/components/ascribe_table/table_header.js +++ b/js/components/ascribe_table/table_header.js @@ -2,7 +2,6 @@ import React from 'react'; -import TableColumnMixin from '../../mixins/table_column_mixin'; import TableHeaderItem from './table_header_item'; import { ColumnModel } from './models/table_models'; @@ -17,15 +16,12 @@ let TableHeader = React.createClass({ orderBy: React.PropTypes.string }, - mixins: [TableColumnMixin], - render() { return ( {this.props.columnList.map((column, i) => { - let columnClasses = this.calcColumnClasses(this.props.columnList, i, 12); let columnName = column.columnName; let canBeOrdered = column.canBeOrdered; @@ -33,7 +29,6 @@ let TableHeader = React.createClass({ column.rowWidth ); - let numOfUsedColumns = sumNumList(listOfRowValues); - - 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.'); - } else { - return bootstrapClasses.join( listOfRowValues[i] + ' ') + listOfRowValues[i]; - } - } -}; - -export default TableColumnMixin; diff --git a/js/routes.js b/js/routes.js index 90248ef2..f7ea2ea6 100644 --- a/js/routes.js +++ b/js/routes.js @@ -20,6 +20,8 @@ import CoaVerifyContainer from './components/coa_verify_container'; import RegisterPiece from './components/register_piece'; +import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard'; + let Route = Router.Route; @@ -35,6 +37,7 @@ const COMMON_ROUTES = ( + ); diff --git a/js/stores/prize_list_store.js b/js/stores/prize_list_store.js new file mode 100644 index 00000000..99ee5d14 --- /dev/null +++ b/js/stores/prize_list_store.js @@ -0,0 +1,20 @@ +'use strict'; + +import alt from '../alt'; + +import PrizeListActions from '../actions/prize_list_actions'; + +class PrizeListStore { + constructor() { + this.prizeList = []; + this.prizeListCount = -1; + this.bindActions(PrizeListActions); + } + + onUpdatePrizeList({ prizeList, prizeListCount }) { + this.prizeList = prizeList; + this.prizeListCount = prizeListCount; + } +} + +export default alt.createStore(PrizeListStore, 'PrizeListStore'); \ No newline at end of file From 88cbf1641c017efb920486805f052ceb5e380175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 29 Jul 2015 18:19:24 +0200 Subject: [PATCH 02/21] fix table cell layout --- sass/ascribe_table.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sass/ascribe_table.scss b/sass/ascribe_table.scss index ebb40672..225c27ca 100644 --- a/sass/ascribe_table.scss +++ b/sass/ascribe_table.scss @@ -22,10 +22,11 @@ } .ascribe-table-item-column { - display: table; font-family: 'Source Sans Pro'; font-size: .8em; height:3em; + vertical-align: middle; + display: table-cell; } .ascribe-table-item-column > * { @@ -37,7 +38,8 @@ } .ascribe-table-item-column > span > input { - margin-top:18px; + margin-top:10px; + margin-left:2px; } .ascribe-table-item-selected { From 84703779a92e397a227503b471b79794e4a314c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 31 Jul 2015 13:44:57 +0200 Subject: [PATCH 03/21] rename prize endpoint to prizes --- .gitignore | 1 + .../whitelabel/prize/constants/api_urls.js | 14 +++++++------- .../prize/constants/application_prize_constants.js | 9 +++++++++ js/components/whitelabel/prize/routes.js | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 js/components/whitelabel/prize/constants/application_prize_constants.js diff --git a/.gitignore b/.gitignore index b39d7830..8378467f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ lib-cov *.gz *.sublime-project *.sublime-workspace +webapp-dependencies.txt pids logs diff --git a/js/components/whitelabel/prize/constants/api_urls.js b/js/components/whitelabel/prize/constants/api_urls.js index e868d403..178dfdfa 100644 --- a/js/components/whitelabel/prize/constants/api_urls.js +++ b/js/components/whitelabel/prize/constants/api_urls.js @@ -1,15 +1,15 @@ 'use strict'; -import AppConstants from '../../../../constants/application_constants'; +import AppPrizeConstants from './application_prize_constants'; function getApiUrls(subdomain) { return { - 'pieces_list': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/', - 'users_login': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/login/', - 'users_signup': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', - 'user': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', - 'piece_submit_to_prize': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/submit/', - 'piece': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/' + 'pieces_list': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/', + 'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/', + 'users_signup': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/', + 'user': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/', + 'piece_submit_to_prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/submit/', + 'piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/' }; } diff --git a/js/components/whitelabel/prize/constants/application_prize_constants.js b/js/components/whitelabel/prize/constants/application_prize_constants.js new file mode 100644 index 00000000..6026193b --- /dev/null +++ b/js/components/whitelabel/prize/constants/application_prize_constants.js @@ -0,0 +1,9 @@ +'use strict'; + +import AppConstants from '../../../../constants/application_constants'; + +let constants = { + prizeApiEndpoint: AppConstants.apiEndpoint + 'prizes/' +}; + +export default constants; \ No newline at end of file diff --git a/js/components/whitelabel/prize/routes.js b/js/components/whitelabel/prize/routes.js index ac490c4e..9d4e12e0 100644 --- a/js/components/whitelabel/prize/routes.js +++ b/js/components/whitelabel/prize/routes.js @@ -21,7 +21,7 @@ let Route = Router.Route; let baseUrl = AppConstants.baseUrl; -function getRoutes(commonRoutes) { +function getRoutes() { return ( From 7ee296c9631bd5784c4844216de39f3998e02840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 10:06:02 +0200 Subject: [PATCH 04/21] static styling of filter widget --- ...ist_bulk_modal_selected_editions_widget.js | 2 +- .../piece_list_toolbar.js | 5 +++++ .../piece_list_toolbar_filter_widget.js | 19 +++++++++++-------- sass/ascribe_piece_list_toolbar.scss | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal_selected_editions_widget.js b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal_selected_editions_widget.js index 17e72072..e169adaa 100644 --- a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal_selected_editions_widget.js +++ b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal_selected_editions_widget.js @@ -1,7 +1,7 @@ 'use strict'; import React from 'react'; -import { getLangText } from '../../utils/lang_utils.js' +import { getLangText } from '../../utils/lang_utils.js'; let PieceListBulkModalSelectedEditionsWidget = React.createClass({ propTypes: { diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js index 3f9d04ab..85c3c2b9 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js @@ -2,6 +2,8 @@ import React from 'react'; +import PieceListToolbarFilterWidget from './piece_list_toolbar_filter_widget'; + import Input from 'react-bootstrap/lib/Input'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import { getLangText } from '../../utils/lang_utils'; @@ -41,6 +43,9 @@ let PieceListToolbar = React.createClass({ onChange={this.searchFor} addonAfter={searchIcon} /> + + + diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index d624dc68..389f201c 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -5,25 +5,28 @@ import React from 'react'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import MenuItem from 'react-bootstrap/lib/MenuItem'; -import { getLangText } from '../../utils/lang_utils.js' +import { getLangText } from '../../utils/lang_utils.js'; let PieceListToolbarFilterWidgetFilter = React.createClass({ + render() { let filterIcon = ; return ( - +
  • {getLangText('Show Pieces that')}:
  • - -
    - {getLangText('I can transfer')} + +
    +
    - -
    - {getLangText('I can consign')} + +
    +
    diff --git a/sass/ascribe_piece_list_toolbar.scss b/sass/ascribe_piece_list_toolbar.scss index 2fa612aa..cb133b4e 100644 --- a/sass/ascribe_piece_list_toolbar.scss +++ b/sass/ascribe_piece_list_toolbar.scss @@ -6,4 +6,21 @@ .search-bar { max-width: 160px; + input { + height: 33px; + } +} + +.ascribe-piece-list-toolbar-filter-widget { + margin-right: 1em; + .checkbox-line { + label { + font-weight: normal; + } + input { + position: relative; + top: 2px; + margin-left: 10px; + } + } } \ No newline at end of file From e3b46c69a52de738ea38f6a6fcff17e4f3e865ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 11:39:33 +0200 Subject: [PATCH 05/21] implement piece list filter widget functionality --- js/actions/piece_list_actions.js | 6 +- .../piece_list_toolbar.js | 3 +- .../piece_list_toolbar_filter_widget.js | 87 ++++++++++++++++--- js/fetchers/piece_list_fetcher.js | 20 ++++- js/stores/piece_list_store.js | 4 +- sass/ascribe_piece_list_toolbar.scss | 6 +- 6 files changed, 105 insertions(+), 21 deletions(-) diff --git a/js/actions/piece_list_actions.js b/js/actions/piece_list_actions.js index dfb9bd44..1ebe7f42 100644 --- a/js/actions/piece_list_actions.js +++ b/js/actions/piece_list_actions.js @@ -14,7 +14,7 @@ class PieceListActions { ); } - fetchPieceList(page, pageSize, search, orderBy, orderAsc) { + fetchPieceList(page, pageSize, search, orderBy, orderAsc, filterBy) { // To prevent flickering on a pagination request, // we overwrite the piecelist with an empty list before // pieceListCount === -1 defines the loading state @@ -24,6 +24,7 @@ class PieceListActions { search, orderBy, orderAsc, + filterBy, 'pieceList': [], 'pieceListCount': -1 }); @@ -32,7 +33,7 @@ class PieceListActions { return Q.Promise((resolve, reject) => { PieceListFetcher - .fetch(page, pageSize, search, orderBy, orderAsc) + .fetch(page, pageSize, search, orderBy, orderAsc, filterBy) .then((res) => { this.actions.updatePieceList({ page, @@ -40,6 +41,7 @@ class PieceListActions { search, orderBy, orderAsc, + filterBy, 'pieceList': res.pieces, 'pieceListCount': res.count }); diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js index 85c3c2b9..6f206c36 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js @@ -44,7 +44,8 @@ let PieceListToolbar = React.createClass({ addonAfter={searchIcon} /> - +
    diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index 389f201c..975bb9fc 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -2,12 +2,70 @@ import React from 'react'; +import PieceListStore from '../../stores/piece_list_store'; +import PieceListActions from '../../actions/piece_list_actions'; + import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 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: { + filterParams: React.PropTypes.arrayOf(React.PropTypes.string).isRequired + }, + + getInitialState() { + return PieceListStore.getState(); + }, + + componentDidMount() { + PieceListStore.listen(this.onChange); + }, + + componentWillUnmount() { + PieceListStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + generateFilterByStatement(param) { + let filterBy = this.state.filterBy; + + if(filterBy) { + // we need hasOwnProperty since the values are all booleans + if(filterBy.hasOwnProperty(param)) { + filterBy[param] = !filterBy[param]; + + // if the parameter is false, then we want to remove it again + // from the list of queryParameters as this component is only about + // which actions *CAN* be done and not what *CANNOT* + if(!filterBy[param]) { + delete filterBy[param]; + } + + } else { + filterBy[param] = true; + } + } + + return filterBy; + }, + + /** + * We need overloading here to find the correct parameter of the label + * the user is clicking on. + */ + filterBy(param) { + return () => { + let filterBy = this.generateFilterByStatement(param); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, filterBy); + }; + }, render() { let filterIcon = ; @@ -17,18 +75,25 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ title={filterIcon} className="ascribe-piece-list-toolbar-filter-widget">
  • - {getLangText('Show Pieces that')}: + {getLangText('Show works that')}:
  • - -
    - -
    -
    - -
    - -
    -
    + {this.props.filterParams.map((param, i) => { + let name = param.split('_')[1]; + return ( + +
    + + {getLangText('I can') + ' ' + getLangText(name)} + + +
    +
    + ); + })}
    ); } diff --git a/js/fetchers/piece_list_fetcher.js b/js/fetchers/piece_list_fetcher.js index bdf0ea5e..8e58402a 100644 --- a/js/fetchers/piece_list_fetcher.js +++ b/js/fetchers/piece_list_fetcher.js @@ -1,17 +1,31 @@ 'use strict'; -import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import requests from '../utils/requests'; +import { mergeOptions } from '../utils/general_utils'; +import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; let PieceListFetcher = { /** * Fetches a list of pieces from the API. * Can be called with all supplied queryparams the API. */ - fetch(page, pageSize, search, orderBy, orderAsc) { + fetch(page, pageSize, search, orderBy, orderAsc, filterBy) { let ordering = generateOrderingQueryParams(orderBy, orderAsc); - return requests.get('pieces_list', { page, pageSize, search, ordering }); + + // filterBy is an object of acl key-value pairs. + // The values are booleans + let queryParams = mergeOptions( + { + page, + pageSize, + search, + ordering + }, + filterBy + ); + + return requests.get('pieces_list', queryParams); }, fetchRequestActions() { diff --git a/js/stores/piece_list_store.js b/js/stores/piece_list_store.js index a8b73e44..c1622d99 100644 --- a/js/stores/piece_list_store.js +++ b/js/stores/piece_list_store.js @@ -26,16 +26,18 @@ class PieceListStore { this.search = ''; this.orderBy = 'artist_name'; this.orderAsc = true; + this.filterBy = {}; this.bindActions(PieceListActions); } - onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount }) { + onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, filterBy }) { this.page = page; this.pageSize = pageSize; this.search = search; this.orderAsc = orderAsc; this.orderBy = orderBy; this.pieceListCount = pieceListCount; + this.filterBy = filterBy; /** * Pagination - Known Issue: diff --git a/sass/ascribe_piece_list_toolbar.scss b/sass/ascribe_piece_list_toolbar.scss index cb133b4e..81141c55 100644 --- a/sass/ascribe_piece_list_toolbar.scss +++ b/sass/ascribe_piece_list_toolbar.scss @@ -5,7 +5,7 @@ } .search-bar { - max-width: 160px; + max-width: 200px; input { height: 33px; } @@ -14,8 +14,8 @@ .ascribe-piece-list-toolbar-filter-widget { margin-right: 1em; .checkbox-line { - label { - font-weight: normal; + span { + cursor: pointer; } input { position: relative; From 17b991e06b75f9c3621866816cd1cf09c542222b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 11:48:12 +0200 Subject: [PATCH 06/21] adjust store calls to fetch list to new interface --- .../accordion_list_item.js | 6 ++++-- js/components/ascribe_detail/edition.js | 3 ++- js/components/ascribe_detail/piece.js | 9 ++++++--- .../piece_list_bulk_modal.js | 3 ++- js/components/piece_list.js | 16 +++++++++------- js/components/register_piece.js | 3 ++- 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/js/components/ascribe_accordion_list/accordion_list_item.js b/js/components/ascribe_accordion_list/accordion_list_item.js index 5c47d060..a561b4ed 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item.js +++ b/js/components/ascribe_accordion_list/accordion_list_item.js @@ -87,14 +87,16 @@ let AccordionListItem = React.createClass({ }, handleSubmitPrizeSuccess(response) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); let notification = new GlobalNotificationModel(response.notification, 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); }, onPollingSuccess(pieceId) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); EditionListActions.toggleEditionList(pieceId); let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index b19f93b3..25e8a97c 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -86,7 +86,8 @@ let Edition = React.createClass({ }, handleDeleteSuccess(response) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); EditionListActions.refreshEditionList(this.props.edition.parent); EditionListActions.closeAllEditionLists(); diff --git a/js/components/ascribe_detail/piece.js b/js/components/ascribe_detail/piece.js index 7dff529f..3fabb055 100644 --- a/js/components/ascribe_detail/piece.js +++ b/js/components/ascribe_detail/piece.js @@ -79,12 +79,14 @@ let Piece = React.createClass({ handleEditionCreationSuccess() { PieceActions.updateProperty({key: 'num_editions', value: 0}); - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); this.toggleCreateEditionsDialog(); }, handleDeleteSuccess(response) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); // since we're deleting a piece, we just need to close // all editions dialogs and not reload them @@ -124,7 +126,8 @@ let Piece = React.createClass({ // btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion // list item also uses the firstEdition property which we can only get from the server in that case. // Therefore we need to at least refetch the changed piece from the server or on our case simply all - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); diff --git a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js index 36a4c53d..3642a667 100644 --- a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js +++ b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js @@ -76,7 +76,8 @@ let PieceListBulkModal = React.createClass({ }, handleSuccess() { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); this.fetchSelectedPieceEditionList() .forEach((pieceId) => { diff --git a/js/components/piece_list.js b/js/components/piece_list.js index 711f53ea..c3bb284a 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -34,7 +34,8 @@ let PieceList = React.createClass({ let page = this.getQuery().page || 1; PieceListStore.listen(this.onChange); if (this.state.pieceList.length === 0){ - PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc) + PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy) .then(PieceListActions.fetchPieceRequestActions()); } }, @@ -59,9 +60,9 @@ let PieceList = React.createClass({ // the site should go to the top document.body.scrollTop = document.documentElement.scrollTop = 0; - return () => PieceListActions.fetchPieceList(page, this.state.pageSize, - this.state.search, this.state.orderBy, - this.state.orderAsc); + return () => PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, + this.state.filterBy); }, getPagination() { @@ -79,13 +80,14 @@ let PieceList = React.createClass({ }, searchFor(searchTerm) { - PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy, this.state.orderAsc); + PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy, + this.state.orderAsc, this.state.filterBy); this.transitionTo(this.getPathname(), {page: 1}); }, accordionChangeOrder(orderBy, orderAsc) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, - this.state.search, orderBy, orderAsc); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + orderBy, orderAsc, this.state.filterBy); }, render() { diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 20826b7d..71dc7bc1 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -101,7 +101,8 @@ let RegisterPiece = React.createClass( { this.state.pageSize, this.state.searchTerm, this.state.orderBy, - this.state.orderAsc + this.state.orderAsc, + this.state.filterBy ); this.transitionTo('piece', {pieceId: response.piece.id}); From 5ce549e533b0ec1ff96678f8861c7b97490514cf Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 4 Aug 2015 13:15:38 +0200 Subject: [PATCH 07/21] disable submit prize with ACL --- .../accordion_list_item.js | 14 ++--- .../ascribe_buttons/submit_to_prize_button.js | 42 --------------- .../ascribe_buttons/submit_to_prize_button.js | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 53 deletions(-) delete mode 100644 js/components/ascribe_buttons/submit_to_prize_button.js create mode 100644 js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js diff --git a/js/components/ascribe_accordion_list/accordion_list_item.js b/js/components/ascribe_accordion_list/accordion_list_item.js index 5c47d060..be51bdf4 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item.js +++ b/js/components/ascribe_accordion_list/accordion_list_item.js @@ -21,7 +21,7 @@ import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; import AclProxy from '../acl_proxy'; -import SubmitToPrizeButton from '../ascribe_buttons/submit_to_prize_button'; +import SubmitToPrizeButton from '../whitelabel/prize/components/ascribe_buttons/submit_to_prize_button'; import { getLangText } from '../../utils/lang_utils'; import { mergeOptions } from '../../utils/general_utils'; @@ -178,21 +178,13 @@ let AccordionListItem = React.createClass({ onPollingSuccess={this.onPollingSuccess}/> + aclObject={this.props.content.acl} + aclName="acl_submit_to_prize"> - - - {this.getLicences()} diff --git a/js/components/ascribe_buttons/submit_to_prize_button.js b/js/components/ascribe_buttons/submit_to_prize_button.js deleted file mode 100644 index 3565a3ee..00000000 --- a/js/components/ascribe_buttons/submit_to_prize_button.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -import React from 'react'; -import classNames from 'classnames'; - -import ModalWrapper from '../ascribe_modal/modal_wrapper'; -import PieceSubmitToPrizeForm from '../ascribe_forms/form_submit_to_prize'; - -import { getLangText } from '../../utils/lang_utils'; - -let SubmitToPrizeButton = React.createClass({ - propTypes: { - className: React.PropTypes.string, - handleSuccess: React.PropTypes.func, - piece: React.PropTypes.object.isRequired - }, - - getSubmitButton() { - return ( - - ); - }, - - render() { - return ( - - - - - ); - } -}); - -export default SubmitToPrizeButton; \ No newline at end of file diff --git a/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js b/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js new file mode 100644 index 00000000..91594d44 --- /dev/null +++ b/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js @@ -0,0 +1,54 @@ +'use strict'; + +import React from 'react'; +import classNames from 'classnames'; + +import ModalWrapper from '../../../../ascribe_modal/modal_wrapper'; +import PieceSubmitToPrizeForm from '../../../../ascribe_forms/form_submit_to_prize'; + +import { getLangText } from '../../../../../utils/lang_utils'; + +let SubmitToPrizeButton = React.createClass({ + propTypes: { + className: React.PropTypes.string, + handleSuccess: React.PropTypes.func, + piece: React.PropTypes.object.isRequired + }, + + getSubmitButton() { + if (this.props.piece.prize) { + return ( + + ); + } + else { + return ( + + ); + } + }, + + render() { + return ( + + + + + ); + } +}); + +export default SubmitToPrizeButton; \ No newline at end of file From 6f1c6a0168dbc3e5179d8c88474768524f1faac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 13:28:44 +0200 Subject: [PATCH 08/21] add 'filter active' functionality to piece list filter widget --- .../piece_list_toolbar_filter_widget.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index 975bb9fc..ee178651 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -5,7 +5,6 @@ import React from 'react'; import PieceListStore from '../../stores/piece_list_store'; import PieceListActions from '../../actions/piece_list_actions'; -import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import MenuItem from 'react-bootstrap/lib/MenuItem'; @@ -67,8 +66,25 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ }; }, + isFilterActive() { + let trueValuesOnly = Object.keys(this.state.filterBy).filter((acl) => acl); + + // We're hiding the star in that complicated matter so that, + // the surrounding button is not resized up on appearance + if(trueValuesOnly.length > 0) { + return { visibility: 'visible'}; + } else { + return { visibility: 'hidden' }; + } + }, + render() { - let filterIcon = ; + let filterIcon = ( + + + * + + ); return ( Date: Tue, 4 Aug 2015 15:06:02 +0200 Subject: [PATCH 09/21] add filterBy to edition list action|store --- js/actions/edition_list_actions.js | 9 +- .../accordion_list_item_edition_widget.js | 13 +- .../accordion_list_item_table_editions.js | 8 +- .../ascribe_buttons/create_editions_button.js | 4 +- .../piece_list_toolbar_filter_widget.js | 1 + .../ascribe_table/table_item_subtable.js | 113 ------------------ .../table_item_subtable_button.js | 23 ---- js/fetchers/edition_list_fetcher.js | 22 ++-- js/stores/edition_list_store.js | 8 +- 9 files changed, 42 insertions(+), 159 deletions(-) delete mode 100644 js/components/ascribe_table/table_item_subtable.js delete mode 100644 js/components/ascribe_table/table_item_subtable_button.js diff --git a/js/actions/edition_list_actions.js b/js/actions/edition_list_actions.js index 2d9868c1..bc163aaa 100644 --- a/js/actions/edition_list_actions.js +++ b/js/actions/edition_list_actions.js @@ -17,21 +17,21 @@ class EditionListActions { ); } - fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc) { - if(!orderBy && typeof orderAsc === 'undefined') { + fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc, filterBy) { + if(!orderBy && typeof orderAsc === 'undefined' || !orderAsc) { orderBy = 'edition_number'; orderAsc = true; } // Taken from: http://stackoverflow.com/a/519157/1263876 - if(typeof page === 'undefined' && typeof pageSize === 'undefined') { + if(typeof page === 'undefined' || !page && typeof pageSize === 'undefined' || !pageSize) { page = 1; pageSize = 10; } return Q.Promise((resolve, reject) => { EditionListFetcher - .fetch(pieceId, page, pageSize, orderBy, orderAsc) + .fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) .then((res) => { this.actions.updateEditionList({ pieceId, @@ -39,6 +39,7 @@ class EditionListActions { pageSize, orderBy, orderAsc, + filterBy, 'editionListOfPiece': res.editions, 'count': res.count }); diff --git a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js index 9fb72b06..fe73a4cc 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js @@ -6,10 +6,14 @@ import classNames from 'classnames'; import EditionListActions from '../../actions/edition_list_actions'; import EditionListStore from '../../stores/edition_list_store'; +import PieceListActions from '../../actions/piece_list_actions'; +import PieceListStore from '../../stores/piece_list_store'; + import Button from 'react-bootstrap/lib/Button'; import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; +import { mergeOptions } from '../../utils/general_utils'; import { getLangText } from '../../utils/lang_utils'; let AccordionListItemEditionWidget = React.createClass({ @@ -21,15 +25,20 @@ let AccordionListItemEditionWidget = React.createClass({ }, getInitialState() { - return EditionListStore.getState(); + return mergeOptions( + EditionListStore.getState(), + PieceListStore.getState() + ); }, componentDidMount() { EditionListStore.listen(this.onChange); + PieceListStore.listen(this.onChange); }, componentWillUnmount() { EditionListStore.unlisten(this.onChange); + PieceListStore.unlisten(this.onChange); }, onChange(state) { @@ -47,7 +56,7 @@ let AccordionListItemEditionWidget = React.createClass({ EditionListActions.toggleEditionList(pieceId); } else { EditionListActions.toggleEditionList(pieceId); - EditionListActions.fetchEditionList(pieceId); + EditionListActions.fetchEditionList(pieceId, null, null, null, null, this.state.filterBy); } }, diff --git a/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js b/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js index 077fc9b5..d1ab2112 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js @@ -76,13 +76,9 @@ let AccordionListItemTableEditions = React.createClass({ }); let editionList = this.state.editionList[this.props.parentId]; - EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize); + EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize, + editionList.orderBy, editionList.orderAsc, editionList.filterBy); }, - - changeEditionListOrder(orderBy, orderAsc) { - EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc); - }, - render() { let selectedEditionsCount = 0; let allEditionsCount = 0; diff --git a/js/components/ascribe_buttons/create_editions_button.js b/js/components/ascribe_buttons/create_editions_button.js index ac6fe543..1bd90545 100644 --- a/js/components/ascribe_buttons/create_editions_button.js +++ b/js/components/ascribe_buttons/create_editions_button.js @@ -44,7 +44,9 @@ let CreateEditionsButton = React.createClass({ startPolling() { // start polling until editions are defined let pollingIntervalIndex = setInterval(() => { - EditionListActions.fetchEditionList(this.props.piece.id) + let editionsForPiece = this.state.editionList[this.props.piece.id]; + + EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, editionsForPiece.filterBy) .then((res) => { clearInterval(this.state.pollingIntervalIndex); diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index ee178651..3bd893c2 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -103,6 +103,7 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ {getLangText('I can') + ' ' + getLangText(name)} diff --git a/js/components/ascribe_table/table_item_subtable.js b/js/components/ascribe_table/table_item_subtable.js deleted file mode 100644 index 56b22fa0..00000000 --- a/js/components/ascribe_table/table_item_subtable.js +++ /dev/null @@ -1,113 +0,0 @@ -'use strict'; - -import React from 'react'; - -import { ColumnModel } from './models/table_models'; - -import EditionListStore from '../../stores/edition_list_store'; -import EditionListActions from '../../actions/edition_list_actions'; - - -import Table from './table'; -import TableItemWrapper from './table_item_wrapper'; -import TableItemText from './table_item_text'; -import TableItemAcl from './table_item_acl'; -import TableItemSelectable from './table_item_selectable'; -import TableItemSubtableButton from './table_item_subtable_button'; - - -let TableItemSubtable = React.createClass({ - propTypes: { - columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnModel)), - columnContent: React.PropTypes.object - }, - - getInitialState() { - return { - 'open': false - }; - }, - - componentDidMount() { - EditionListStore.listen(this.onChange); - }, - - componentWillUnmount() { - EditionListStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - loadEditionList() { - if(this.state.open) { - this.setState({ - 'open': false - }); - } else { - - EditionListActions.fetchEditionList(this.props.columnContent.id); - this.setState({ - 'open': true, - 'editionList': EditionListStore.getState() - }); - } - }, - - selectItem(parentId, itemId) { - EditionListActions.selectEdition({ - 'pieceId': parentId, - 'editionId': itemId - }); - }, - - render() { - - let renderEditionListTable = () => { - - let columnList = [ - new ColumnModel('edition_number', 'Number', TableItemText, 2, false), - new ColumnModel('user_registered', 'User', TableItemText, 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) { - return ( -
    -
    - - {this.state.editionList[this.props.columnContent.id].map((edition, i) => { - return ( - - ); - })} -
    -
    -
    - ); - } - }; - - return ( -
    -
    - -
    - -
    -
    - {renderEditionListTable()} -
    - ); - } -}); - -export default TableItemSubtable; diff --git a/js/components/ascribe_table/table_item_subtable_button.js b/js/components/ascribe_table/table_item_subtable_button.js deleted file mode 100644 index 75aeba7f..00000000 --- a/js/components/ascribe_table/table_item_subtable_button.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -import React from 'react'; - -let TableItemSubtableButton = React.createClass({ - - propTypes: { - content: React.PropTypes.string.isRequired, - onClick: React.PropTypes.func.isRequired - }, - - render() { - return ( - - - - ); - } -}); - -export default TableItemSubtableButton; \ No newline at end of file diff --git a/js/fetchers/edition_list_fetcher.js b/js/fetchers/edition_list_fetcher.js index 94a2116d..b416c595 100644 --- a/js/fetchers/edition_list_fetcher.js +++ b/js/fetchers/edition_list_fetcher.js @@ -3,20 +3,26 @@ import requests from '../utils/requests'; import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; - +import { mergeOptions } from '../utils/general_utils'; let EditionListFetcher = { /** * Fetches a list of editions from the API. */ - fetch(pieceId, page, pageSize, orderBy, orderAsc) { + fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) { let ordering = generateOrderingQueryParams(orderBy, orderAsc); - return requests.get('editions_list', { - 'piece_id': pieceId, - page, - pageSize, - ordering - }); + + let queryParams = mergeOptions( + { + page, + pageSize, + ordering, + piece_id: pieceId + }, + filterBy + ); + + return requests.get('editions_list', queryParams); } }; diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js index 12835603..d3fac31d 100644 --- a/js/stores/edition_list_store.js +++ b/js/stores/edition_list_store.js @@ -12,7 +12,7 @@ class EditionListStore { this.bindActions(EditionsListActions); } - onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count}) { + onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count, filterBy}) { /* Basically there are two modes an edition list can be updated. @@ -54,6 +54,7 @@ class EditionListStore { this.editionList[pieceId].orderBy = orderBy; this.editionList[pieceId].orderAsc = orderAsc; this.editionList[pieceId].count = count; + this.editionList[pieceId].filterBy = filterBy; } /** @@ -80,7 +81,10 @@ class EditionListStore { this.editionList[pieceId].length = 0; // refetch editions with adjusted page size - EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength, this.editionList[pieceId].orderBy, this.editionList[pieceId].orderAsc) + EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength, + this.editionList[pieceId].orderBy, + this.editionList[pieceId].orderAsc, + this.editionList[pieceId].filterBy) .then(() => { // reset back to the normal pageSize and page this.editionList[pieceId].page = prevEditionListPage; From fb10d9329ba98086c9428b2c2b8208420ef1c752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 15:39:20 +0200 Subject: [PATCH 10/21] add labels for filter params --- .../piece_list_toolbar.js | 5 +++- .../piece_list_toolbar_filter_widget.js | 25 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js index 6f206c36..161cb929 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js @@ -45,7 +45,10 @@ let PieceListToolbar = React.createClass({ + filterParams={['acl_transfer', 'acl_consign', { + key: 'acl_editions', + label: 'create editions' + }]} /> diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index 3bd893c2..dc77469f 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -12,7 +12,14 @@ import { getLangText } from '../../utils/lang_utils.js'; let PieceListToolbarFilterWidgetFilter = React.createClass({ propTypes: { - filterParams: React.PropTypes.arrayOf(React.PropTypes.string).isRequired + // An array of either strings (which represent acl enums) or objects of the form + // + // { + // key: , + // label: + // } + // + filterParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired }, getInitialState() { @@ -94,14 +101,24 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ {getLangText('Show works that')}: {this.props.filterParams.map((param, i) => { - let name = param.split('_')[1]; + let label; + + if(typeof param !== 'string') { + label = param.label; + param = param.key; + } else { + param = param; + label = param.split('_')[1]; + } + return (
    - - {getLangText('I can') + ' ' + getLangText(name)} + + {getLangText('I can') + ' ' + getLangText(label)} Date: Tue, 4 Aug 2015 15:58:39 +0200 Subject: [PATCH 11/21] add styling for piece list filter widget --- .../piece_list_toolbar_filter_widget.js | 10 +++---- sass/ascribe_piece_list_toolbar.scss | 27 ++++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index dc77469f..7045011d 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -115,15 +115,15 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ + className="filter-widget-item">
    {getLangText('I can') + ' ' + getLangText(label)} - +
    ); diff --git a/sass/ascribe_piece_list_toolbar.scss b/sass/ascribe_piece_list_toolbar.scss index 81141c55..902d5be9 100644 --- a/sass/ascribe_piece_list_toolbar.scss +++ b/sass/ascribe_piece_list_toolbar.scss @@ -13,13 +13,32 @@ .ascribe-piece-list-toolbar-filter-widget { margin-right: 1em; - .checkbox-line { - span { - cursor: pointer; + + .filter-widget-item { + + > a { + padding-left: 0; + padding-right: 0; } + } + + .checkbox-line { + position: relative; + height: 25px; + + span { + position: absolute; + left: 9px; + top: 3px; + cursor: pointer; + + margin-right: 10px; + } + input { - position: relative; + position: absolute; top: 2px; + right: 9px; margin-left: 10px; } } From c4e372e72fbeb85bc46a936ed6f100a4a54ba45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 16:18:14 +0200 Subject: [PATCH 12/21] pr review: fix indentation --- js/stores/edition_list_store.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js index d3fac31d..4e3a2fe8 100644 --- a/js/stores/edition_list_store.js +++ b/js/stores/edition_list_store.js @@ -82,9 +82,9 @@ class EditionListStore { // refetch editions with adjusted page size EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength, - this.editionList[pieceId].orderBy, - this.editionList[pieceId].orderAsc, - this.editionList[pieceId].filterBy) + this.editionList[pieceId].orderBy, + this.editionList[pieceId].orderAsc, + this.editionList[pieceId].filterBy) .then(() => { // reset back to the normal pageSize and page this.editionList[pieceId].page = prevEditionListPage; From 23bbbb792a868f401645136b42451c00c24fce28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 4 Aug 2015 16:58:36 +0200 Subject: [PATCH 13/21] pr review: clear: operators precedence --- js/actions/edition_list_actions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/actions/edition_list_actions.js b/js/actions/edition_list_actions.js index bc163aaa..d13882cd 100644 --- a/js/actions/edition_list_actions.js +++ b/js/actions/edition_list_actions.js @@ -18,13 +18,13 @@ class EditionListActions { } fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc, filterBy) { - if(!orderBy && typeof orderAsc === 'undefined' || !orderAsc) { + if((!orderBy && typeof orderAsc === 'undefined') || !orderAsc) { orderBy = 'edition_number'; orderAsc = true; } // Taken from: http://stackoverflow.com/a/519157/1263876 - if(typeof page === 'undefined' || !page && typeof pageSize === 'undefined' || !pageSize) { + if((typeof page === 'undefined' || !page) && (typeof pageSize === 'undefined' || !pageSize)) { page = 1; pageSize = 10; } From f134ca29d632ae8d03556728894dd5aa3c58f916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 5 Aug 2015 09:52:17 +0200 Subject: [PATCH 14/21] fix property reset function --- js/components/ascribe_forms/property.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index 5f7fe603..5a72270c 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -70,23 +70,22 @@ let Property = React.createClass({ }); } - if(!this.state.initialValue) { + if(!this.state.initialValue && childInput.props.defaultValue) { this.setState({ - initialValue: childInput.defaultValue + initialValue: childInput.props.defaultValue }); } }, - reset(){ + reset() { // maybe do reset by reload instead of front end state? this.setState({value: this.state.initialValue}); - if (this.refs.input.state){ - // This is probably not the right way but easy fix - this.refs.input.state.value = this.state.initialValue; - } - else{ - this.refs.input.getDOMNode().value = this.state.initialValue; - } + + // resets the value of a custom react component input + this.refs.input.state.value = this.state.initialValue; + + // resets the value of a plain HTML5 input + this.refs.input.getDOMNode().value = this.state.initialValue; }, From 6ab5bec8d3ed4f0d96506825485b834b3d54ddb9 Mon Sep 17 00:00:00 2001 From: diminator Date: Wed, 5 Aug 2015 14:40:26 +0200 Subject: [PATCH 15/21] WIP prize settings for admin --- js/components/ascribe_forms/form.js | 11 +- js/components/settings_container.js | 9 +- .../whitelabel/prize/actions/prize_actions.js | 33 +++ .../prize/actions/prize_jury_actions.js | 31 +++ .../prize/components/settings_container.js | 255 ++++++++++++++++++ .../whitelabel/prize/constants/api_urls.js | 4 +- .../prize/fetchers/prize_fetcher.js | 12 + .../prize/fetchers/prize_jury_fetcher.js | 12 + js/components/whitelabel/prize/routes.js | 2 +- .../prize/stores/prize_jury_store.js | 18 ++ .../whitelabel/prize/stores/prize_store.js | 18 ++ 11 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 js/components/whitelabel/prize/actions/prize_actions.js create mode 100644 js/components/whitelabel/prize/actions/prize_jury_actions.js create mode 100644 js/components/whitelabel/prize/components/settings_container.js create mode 100644 js/components/whitelabel/prize/fetchers/prize_fetcher.js create mode 100644 js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js create mode 100644 js/components/whitelabel/prize/stores/prize_jury_store.js create mode 100644 js/components/whitelabel/prize/stores/prize_store.js diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js index 0f022a0d..89e51da2 100644 --- a/js/components/ascribe_forms/form.js +++ b/js/components/ascribe_forms/form.js @@ -15,6 +15,9 @@ import { mergeOptionsWithDuplicates } from '../../utils/general_utils'; let Form = React.createClass({ propTypes: { url: React.PropTypes.string, + buttons: React.PropTypes.object, + buttonSubmitText: React.PropTypes.string, + spinner: React.PropTypes.object, handleSuccess: React.PropTypes.func, getFormData: React.PropTypes.func, children: React.PropTypes.oneOfType([ @@ -24,6 +27,12 @@ let Form = React.createClass({ className: React.PropTypes.string }, + getDefaultProps() { + return { + buttonSubmitText: 'SAVE' + }; + }, + getInitialState() { return { edited: false, @@ -125,7 +134,7 @@ let Form = React.createClass({ buttons = (

    - +

    diff --git a/js/components/settings_container.js b/js/components/settings_container.js index 381cd1c4..c35fba1e 100644 --- a/js/components/settings_container.js +++ b/js/components/settings_container.js @@ -29,12 +29,19 @@ import { getLangText } from '../utils/lang_utils'; import { getCookie } from '../utils/fetch_api_utils'; let SettingsContainer = React.createClass({ + propTypes: { + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.element), + React.PropTypes.element]) + }, + mixins: [Router.Navigation], render() { return (
    + {this.props.children} @@ -121,6 +128,7 @@ let AccountSettings = React.createClass({ +
    {/* */} -
    ); } diff --git a/js/components/whitelabel/prize/actions/prize_actions.js b/js/components/whitelabel/prize/actions/prize_actions.js new file mode 100644 index 00000000..5646e2d6 --- /dev/null +++ b/js/components/whitelabel/prize/actions/prize_actions.js @@ -0,0 +1,33 @@ +'use strict'; + +import alt from '../../../../alt'; +import Q from 'q'; + +import PrizeFetcher from '../fetchers/prize_fetcher'; + +class PrizeActions { + constructor() { + this.generateActions( + 'updatePrize' + ); + } + + fetchPrize() { + return Q.Promise((resolve, reject) => { + PrizeFetcher + .fetch() + .then((res) => { + this.actions.updatePrize({ + prize: res.prize + }); + resolve(res); + }) + .catch((err) => { + console.logGlobal(err); + reject(err); + }); + }); + } +} + +export default alt.createActions(PrizeActions); \ No newline at end of file diff --git a/js/components/whitelabel/prize/actions/prize_jury_actions.js b/js/components/whitelabel/prize/actions/prize_jury_actions.js new file mode 100644 index 00000000..e2fe1a96 --- /dev/null +++ b/js/components/whitelabel/prize/actions/prize_jury_actions.js @@ -0,0 +1,31 @@ +'use strict'; + +import alt from '../../../../alt'; +import Q from 'q'; + +import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher'; + +class PrizeJuryActions { + constructor() { + this.generateActions( + 'updatePrizeJury' + ); + } + + fetchJury() { + return Q.Promise((resolve, reject) => { + PrizeJuryFetcher + .fetch() + .then((res) => { + this.actions.updatePrizeJury(res.members); + resolve(res); + }) + .catch((err) => { + console.logGlobal(err); + reject(err); + }); + }); + } +} + +export default alt.createActions(PrizeJuryActions); \ No newline at end of file diff --git a/js/components/whitelabel/prize/components/settings_container.js b/js/components/whitelabel/prize/components/settings_container.js new file mode 100644 index 00000000..af72ac18 --- /dev/null +++ b/js/components/whitelabel/prize/components/settings_container.js @@ -0,0 +1,255 @@ +'use strict'; + +import React from 'react'; + +import UserStore from '../../../../stores/user_store'; +import UserActions from '../../../../actions/user_actions'; +import PrizeActions from '../actions/prize_actions'; +import PrizeStore from '../stores/prize_store'; +import PrizeJuryActions from '../actions/prize_jury_actions'; +import PrizeJuryStore from '../stores/prize_jury_store'; + +import SettingsContainer from '../../../settings_container'; +import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_paragraph'; + +import Form from '../../../ascribe_forms/form'; +import Property from '../../../ascribe_forms/property'; +import FormPropertyHeader from '../../../ascribe_forms/form_property_header'; + +import Table from '../../../ascribe_table/table'; +import TableItem from '../../../ascribe_table/table_item'; +import TableItemText from '../../../ascribe_table/table_item_text'; + +import GlobalNotificationModel from '../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../actions/global_notification_actions'; + +import AppConstants from '../../../../constants/application_constants'; +import apiUrls from '../../../../constants/api_urls'; + +import { ColumnModel} from '../../../ascribe_table/models/table_models'; +import { getLangText } from '../../../../utils/lang_utils'; + + +let Settings = React.createClass({ + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + let prize_settings = null; + if (this.state.currentUser.is_admin){ + prize_settings = ; + } + return ( + + {prize_settings} + + ); + } +}); + +let PrizeSettings = React.createClass({ + + getInitialState() { + return PrizeStore.getState(); + }, + + componentDidMount() { + PrizeStore.listen(this.onChange); + PrizeActions.fetchPrize(); + }, + + componentWillUnmount() { + PrizeStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + render() { + return ( + +
    + +
    {this.state.prize.name}
    +
    + +
    {this.state.prize.active_round}/{this.state.prize.rounds}
    +
    + +
    {this.state.prize.num_submissions}
    +
    +
    +
    + +
    + ); + } +}); + +let PrizeJurySettings = React.createClass({ + propTypes: { + prize: React.PropTypes.object + }, + + getInitialState() { + return PrizeJuryStore.getState(); + }, + + componentDidMount() { + PrizeJuryStore.listen(this.onChange); + PrizeJuryActions.fetchJury(); + }, + + componentWillUnmount() { + PrizeJuryStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + handleCreateSuccess(response) { + PrizeJuryActions.fetchJury(); + let notification = new GlobalNotificationModel(response.notification, 'success', 5000); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + + render() { + let content = ( +
    + +
    ); + + if (this.state.members.length > -1) { + content = this.state.members.map(function(member, i) { + return ( + +
    +
    + {member.status} +
    +
    + +
    +
    +
    ); + }, this); + content = ( +
    +
    + {content} +
    +
    +
    ); + } + return ( +
    +
    + +

    Jury Members

    +
    + + + +
    +
    + {content} +
    + ); + } +}); + + +let PrizesDashboard = React.createClass({ + + getColumnList() { + return [ + new ColumnModel( + (item) => { + return { + 'content': item.name + }; }, + 'name', + getLangText('Name'), + TableItemText, + 6, + false, + null + ), + new ColumnModel( + (item) => { + return { + 'content': item.domain + }; }, + 'domain', + getLangText('Domain'), + TableItemText, + 1, + false, + null + ) + ]; + }, + + render() { + return ( + + {this.state.prizeList.map((item, i) => { + return ( + + ); + })} +
    + ); + } +}); + +export default Settings; \ No newline at end of file diff --git a/js/components/whitelabel/prize/constants/api_urls.js b/js/components/whitelabel/prize/constants/api_urls.js index e868d403..49441341 100644 --- a/js/components/whitelabel/prize/constants/api_urls.js +++ b/js/components/whitelabel/prize/constants/api_urls.js @@ -9,7 +9,9 @@ function getApiUrls(subdomain) { 'users_signup': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', 'user': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', 'piece_submit_to_prize': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/submit/', - 'piece': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/' + 'piece': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/', + 'prize': AppConstants.apiEndpoint + 'prize/' + subdomain + '/', + 'jury': AppConstants.apiEndpoint + 'prize/' + subdomain + '/jury/' }; } diff --git a/js/components/whitelabel/prize/fetchers/prize_fetcher.js b/js/components/whitelabel/prize/fetchers/prize_fetcher.js new file mode 100644 index 00000000..0bf9fc55 --- /dev/null +++ b/js/components/whitelabel/prize/fetchers/prize_fetcher.js @@ -0,0 +1,12 @@ +'use strict'; + +import requests from '../../../../utils/requests'; + + +let PrizeFetcher = { + fetch() { + return requests.get('prize'); + } +}; + +export default PrizeFetcher; diff --git a/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js b/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js new file mode 100644 index 00000000..be00e930 --- /dev/null +++ b/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js @@ -0,0 +1,12 @@ +'use strict'; + +import requests from '../../../../utils/requests'; + + +let PrizeJuryFetcher = { + fetch() { + return requests.get('jury'); + } +}; + +export default PrizeJuryFetcher; diff --git a/js/components/whitelabel/prize/routes.js b/js/components/whitelabel/prize/routes.js index ac490c4e..62b190d4 100644 --- a/js/components/whitelabel/prize/routes.js +++ b/js/components/whitelabel/prize/routes.js @@ -12,7 +12,7 @@ import PrizeRegisterPiece from './components/register_piece'; import PrizePieceList from './components/piece_list'; import PrizePieceContainer from './components/ascribe_detail/piece_container'; import EditionContainer from '../../ascribe_detail/edition_container'; -import SettingsContainer from '../../../components/settings_container'; +import SettingsContainer from './components/settings_container'; import App from './app'; import AppConstants from '../../../constants/application_constants'; diff --git a/js/components/whitelabel/prize/stores/prize_jury_store.js b/js/components/whitelabel/prize/stores/prize_jury_store.js new file mode 100644 index 00000000..59a54329 --- /dev/null +++ b/js/components/whitelabel/prize/stores/prize_jury_store.js @@ -0,0 +1,18 @@ +'use strict'; + +import alt from '../../../../alt'; + +import PrizeJuryActions from '../actions/prize_jury_actions'; + +class PrizeJuryStore { + constructor() { + this.members = []; + this.bindActions(PrizeJuryActions); + } + + onUpdatePrizeJury( members ) { + this.members = members; + } +} + +export default alt.createStore(PrizeJuryStore, 'PrizeJuryStore'); \ No newline at end of file diff --git a/js/components/whitelabel/prize/stores/prize_store.js b/js/components/whitelabel/prize/stores/prize_store.js new file mode 100644 index 00000000..f311e1fe --- /dev/null +++ b/js/components/whitelabel/prize/stores/prize_store.js @@ -0,0 +1,18 @@ +'use strict'; + +import alt from '../../../../alt'; + +import PrizeActions from '../actions/prize_actions'; + +class PrizeStore { + constructor() { + this.prize = []; + this.bindActions(PrizeActions); + } + + onUpdatePrize({ prize }) { + this.prize = prize; + } +} + +export default alt.createStore(PrizeStore, 'PrizeStore'); \ No newline at end of file From 5108ca2fc6e90aeeb3bc9b1ce30fbe8e93428b66 Mon Sep 17 00:00:00 2001 From: diminator Date: Wed, 5 Aug 2015 14:50:44 +0200 Subject: [PATCH 16/21] merge conflict --- js/components/whitelabel/prize/constants/api_urls.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/components/whitelabel/prize/constants/api_urls.js b/js/components/whitelabel/prize/constants/api_urls.js index ceaa0ff3..f9030794 100644 --- a/js/components/whitelabel/prize/constants/api_urls.js +++ b/js/components/whitelabel/prize/constants/api_urls.js @@ -10,8 +10,9 @@ function getApiUrls(subdomain) { 'user': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/', 'piece_submit_to_prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/submit/', 'piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/', - 'prize': AppPrizeConstants.prizeApiEndpoint + 'prize/' + subdomain + '/', - 'jury': AppPrizeConstants.prizeApiEndpoint + 'prize/' + subdomain + '/jury/' + 'prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/', + 'jury': AppPrizeConstants.prizeApiEndpoint + subdomain + '/jury/' + }; } export default getApiUrls; From 792325f71d22672fbbc2ba41c01b1204340f5b98 Mon Sep 17 00:00:00 2001 From: diminator Date: Wed, 5 Aug 2015 16:02:48 +0200 Subject: [PATCH 17/21] acl_create_editions --- .../accordion_list_item_edition_widget.js | 2 +- js/components/ascribe_buttons/create_editions_button.js | 2 +- .../ascribe_piece_list_toolbar/piece_list_toolbar.js | 2 +- js/components/register_piece.js | 2 +- js/constants/application_constants.js | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js index fe73a4cc..e4ba0d2b 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_edition_widget.js @@ -94,7 +94,7 @@ let AccordionListItemEditionWidget = React.createClass({ let numEditions = piece.num_editions; if(numEditions <= 0) { - if (piece.acl.acl_editions){ + if (piece.acl.acl_create_editions){ return ( 0){ + if (!piece.acl.acl_create_editions || piece.num_editions > 0){ return null; } diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js index 161cb929..b75ce239 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js @@ -46,7 +46,7 @@ let PieceListToolbar = React.createClass({ diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 71dc7bc1..f46ab99c 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -140,7 +140,7 @@ let RegisterPiece = React.createClass( { }, getSpecifyEditions() { - if(this.state.whitelabel && this.state.whitelabel.acl_editions || Object.keys(this.state.whitelabel).length === 0) { + if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) { return ( Date: Wed, 5 Aug 2015 19:22:58 +0200 Subject: [PATCH 18/21] invite user with token and signup without email validation --- js/components/ascribe_forms/form_signup.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/js/components/ascribe_forms/form_signup.js b/js/components/ascribe_forms/form_signup.js index 238cd74e..91479e4b 100644 --- a/js/components/ascribe_forms/form_signup.js +++ b/js/components/ascribe_forms/form_signup.js @@ -27,7 +27,7 @@ let SignupForm = React.createClass({ children: React.PropTypes.element }, - mixins: [Router.Navigation], + mixins: [Router.Navigation, Router.State], getDefaultProps() { return { @@ -35,7 +35,6 @@ let SignupForm = React.createClass({ submitMessage: getLangText('Sign up') }; }, - getInitialState() { return UserStore.getState(); }, @@ -57,22 +56,32 @@ let SignupForm = React.createClass({ } }, - handleSuccess(response){ - let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000); - GlobalNotificationActions.appendGlobalNotification(notification); - this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.'); + getFormData() { + return this.getQuery(); + }, + handleSuccess(response){ + if (response.user) { + let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000); + GlobalNotificationActions.appendGlobalNotification(notification); + this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.'); + } + else if (response.redirect) { + this.transitionTo('pieces'); + } }, render() { let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' + getLangText('This password is securing your digital property like a bank account') + '.\n ' + getLangText('Store it in a safe place') + '!'; + return (
    From ee97e600ef7945664675887589eee1992efdb4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 6 Aug 2015 13:15:52 +0200 Subject: [PATCH 19/21] fix bug in piece list filter widget --- .../ascribe_buttons/create_editions_button.js | 4 +-- .../piece_list_toolbar.js | 6 +++- .../piece_list_toolbar_filter_widget.js | 32 ++++--------------- js/components/piece_list.js | 12 ++++++- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/js/components/ascribe_buttons/create_editions_button.js b/js/components/ascribe_buttons/create_editions_button.js index b37af5ad..891aa01d 100644 --- a/js/components/ascribe_buttons/create_editions_button.js +++ b/js/components/ascribe_buttons/create_editions_button.js @@ -42,11 +42,11 @@ let CreateEditionsButton = React.createClass({ }, startPolling() { + let filterBy = this.state.editionList[this.props.piece.id].filterBy; // start polling until editions are defined let pollingIntervalIndex = setInterval(() => { - let editionsForPiece = this.state.editionList[this.props.piece.id]; - EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, editionsForPiece.filterBy) + EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, filterBy) .then((res) => { clearInterval(this.state.pollingIntervalIndex); diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js index b75ce239..22034f0d 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar.js @@ -13,6 +13,8 @@ let PieceListToolbar = React.createClass({ propTypes: { className: React.PropTypes.string, searchFor: React.PropTypes.func, + filterBy: React.PropTypes.object, + applyFilterBy: React.PropTypes.func, children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.element @@ -48,7 +50,9 @@ let PieceListToolbar = React.createClass({ filterParams={['acl_transfer', 'acl_consign', { key: 'acl_create_editions', label: 'create editions' - }]} /> + }]} + filterBy={this.props.filterBy} + applyFilterBy={this.props.applyFilterBy}/>
    diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index 7045011d..13bfd9a0 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -2,9 +2,6 @@ import React from 'react'; -import PieceListStore from '../../stores/piece_list_store'; -import PieceListActions from '../../actions/piece_list_actions'; - import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import MenuItem from 'react-bootstrap/lib/MenuItem'; @@ -19,27 +16,13 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ // label:
    // } // - filterParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired - }, - - getInitialState() { - return PieceListStore.getState(); - }, - - componentDidMount() { - PieceListStore.listen(this.onChange); - }, - - componentWillUnmount() { - PieceListStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); + filterParams: React.PropTypes.arrayOf(React.PropTypes.any).isRequired, + filterBy: React.PropTypes.object, + applyFilterBy: React.PropTypes.func }, generateFilterByStatement(param) { - let filterBy = this.state.filterBy; + let filterBy = this.props.filterBy; if(filterBy) { // we need hasOwnProperty since the values are all booleans @@ -68,13 +51,12 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ filterBy(param) { return () => { let filterBy = this.generateFilterByStatement(param); - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, filterBy); + this.props.applyFilterBy(filterBy); }; }, isFilterActive() { - let trueValuesOnly = Object.keys(this.state.filterBy).filter((acl) => acl); + let trueValuesOnly = Object.keys(this.props.filterBy).filter((acl) => acl); // We're hiding the star in that complicated matter so that, // the surrounding button is not resized up on appearance @@ -123,7 +105,7 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({ + checked={this.props.filterBy[param]} /> ); diff --git a/js/components/piece_list.js b/js/components/piece_list.js index c3bb284a..1792b17a 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -85,6 +85,14 @@ let PieceList = React.createClass({ this.transitionTo(this.getPathname(), {page: 1}); }, + applyFilterBy(filterBy) { + PieceListActions.fetchPieceList(1, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, filterBy); + // we have to redirect the user always to page one as it could be that there is no page two + // for filtered pieces + this.transitionTo(this.getPathname(), {page: 1}); + }, + accordionChangeOrder(orderBy, orderAsc) { PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, orderBy, orderAsc, this.state.filterBy); @@ -97,7 +105,9 @@ let PieceList = React.createClass({
    + searchFor={this.searchFor} + filterBy={this.state.filterBy} + applyFilterBy={this.applyFilterBy}> {this.props.customSubmitButton} From e227db1b3be67716df50b03ff9eb9193b828c7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 6 Aug 2015 13:43:53 +0200 Subject: [PATCH 20/21] fix create editions for filter widget --- js/components/ascribe_buttons/create_editions_button.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/components/ascribe_buttons/create_editions_button.js b/js/components/ascribe_buttons/create_editions_button.js index 891aa01d..8eebeaf7 100644 --- a/js/components/ascribe_buttons/create_editions_button.js +++ b/js/components/ascribe_buttons/create_editions_button.js @@ -42,18 +42,19 @@ let CreateEditionsButton = React.createClass({ }, startPolling() { - let filterBy = this.state.editionList[this.props.piece.id].filterBy; // start polling until editions are defined let pollingIntervalIndex = setInterval(() => { - EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, filterBy) + // requests, will try to merge the filterBy parameter with other parameters (mergeOptions). + // Therefore it can't but null but instead has to be an empty object + EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, {}) .then((res) => { clearInterval(this.state.pollingIntervalIndex); this.props.onPollingSuccess(this.props.piece.id, res.editions[0].num_editions); }) - .catch(() => { + .catch((err) => { /* Ignore and keep going */ }); }, 5000); From 99ea0e1af86be52dd238594a19a4e119d6eba6a7 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 6 Aug 2015 13:56:37 +0200 Subject: [PATCH 21/21] action-panel WIP --- js/components/ascribe_forms/form_signup.js | 3 +- js/components/ascribe_panel/action_panel.js | 71 +++++++++++++++++++ .../prize/components/settings_container.js | 35 ++++----- sass/ascribe_panel.scss | 13 ++++ sass/main.scss | 1 + 5 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 js/components/ascribe_panel/action_panel.js create mode 100644 sass/ascribe_panel.scss diff --git a/js/components/ascribe_forms/form_signup.js b/js/components/ascribe_forms/form_signup.js index 91479e4b..55aff4b8 100644 --- a/js/components/ascribe_forms/form_signup.js +++ b/js/components/ascribe_forms/form_signup.js @@ -75,7 +75,7 @@ let SignupForm = React.createClass({ let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' + getLangText('This password is securing your digital property like a bank account') + '.\n ' + getLangText('Store it in a safe place') + '!'; - + let email = this.getQuery().email ? this.getQuery().email : null; return ( +
    + {this.props.title} +
    +
    + + {this.props.content} + + + {this.props.buttons} + +
    +
    + ); + } +}); + +export default ActionPanel; \ No newline at end of file diff --git a/js/components/whitelabel/prize/components/settings_container.js b/js/components/whitelabel/prize/components/settings_container.js index af72ac18..fecc1fd0 100644 --- a/js/components/whitelabel/prize/components/settings_container.js +++ b/js/components/whitelabel/prize/components/settings_container.js @@ -16,6 +16,8 @@ import Form from '../../../ascribe_forms/form'; import Property from '../../../ascribe_forms/property'; import FormPropertyHeader from '../../../ascribe_forms/form_property_header'; +import ActionPanel from '../../../ascribe_panel/action_panel'; + import Table from '../../../ascribe_table/table'; import TableItem from '../../../ascribe_table/table_item'; import TableItemText from '../../../ascribe_table/table_item_text'; @@ -49,13 +51,13 @@ let Settings = React.createClass({ }, render() { - let prize_settings = null; + let prizeSettings = null; if (this.state.currentUser.is_admin){ - prize_settings = ; + prizeSettings = ; } return ( - {prize_settings} + {prizeSettings} ); } @@ -139,6 +141,7 @@ let PrizeJurySettings = React.createClass({ PrizeJuryActions.fetchJury(); let notification = new GlobalNotificationModel(response.notification, 'success', 5000); GlobalNotificationActions.appendGlobalNotification(notification); + this.refs.form.refs.email.refs.input.getDOMNode().value = null; }, render() { @@ -150,30 +153,21 @@ let PrizeJurySettings = React.createClass({ if (this.state.members.length > -1) { content = this.state.members.map(function(member, i) { return ( - -
    -
    - {member.status} -
    -
    - -
    -
    -
    ); + } + />); }, this); content = (
    - - {content} -
    - + {content}
    ); } return ( @@ -181,6 +175,7 @@ let PrizeJurySettings = React.createClass({

    Jury Members

    diff --git a/sass/ascribe_panel.scss b/sass/ascribe_panel.scss new file mode 100644 index 00000000..43f8f15f --- /dev/null +++ b/sass/ascribe_panel.scss @@ -0,0 +1,13 @@ +.ascribe-panel-wrapper { + border: 1px solid #F5F5F5; +} + +.ascribe-panel-title { + padding: 1em 0 1em 1.5em; +} + +.ascribe-panel-content { + padding: .75em 0 .75em 1em; + font-size: 1em; + color: #616161; +} \ No newline at end of file diff --git a/sass/main.scss b/sass/main.scss index d8e5e7ff..5f00d2c9 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -27,6 +27,7 @@ $BASE_URL: '<%= BASE_URL %>'; @import 'ascribe_settings'; @import 'ascribe_slides_container'; @import 'ascribe_form'; +@import 'ascribe_panel'; @import 'whitelabel/index';