- browsing by subcategory
This commit is contained in:
@@ -66,11 +66,18 @@ end
|
||||
|
||||
# gets items in sub category ( useful for page showing single sub_category )
|
||||
get '/item/sub_category/:sub_category_id/offset/:offset/limit/:limit' do |sub_category_id_s, offset_s, limit_s|
|
||||
|
||||
sub_category_id, offset, limit = mass_to_i(sub_category_id_s, offset_s, limit_s)
|
||||
input_invalid = offset_and_limit_invalid?(offset,limit) or sub_category_id <= 0
|
||||
return [].to_json if input_invalid
|
||||
|
||||
all_in_sub_cat = filter_by_traits(Item.all_in_sub_category(sub_category_id))
|
||||
|
||||
items = Item.best_selling_in_sub_category(sub_category_id, offset, limit)
|
||||
items = filter_by_traits(items)
|
||||
|
||||
add_total_count_header(all_in_sub_cat.count)
|
||||
|
||||
prepare_items_for_mass_display(items)
|
||||
end
|
||||
|
||||
|
||||
18
front-api/controllers/sub_category.rb
Normal file
18
front-api/controllers/sub_category.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
get '/subcategory' do
|
||||
SubCategory
|
||||
.eager_load(category: :section)
|
||||
.order(:order)
|
||||
.all
|
||||
.to_json(:include => [filter_criterias: {include: :filter_criteria_values}, category: {include: :section} ])
|
||||
end
|
||||
|
||||
get '/subcategory/:id' do
|
||||
id = params[:id].to_i
|
||||
|
||||
SubCategory
|
||||
.eager_load(category: :section)
|
||||
.order(:order)
|
||||
.find(id)
|
||||
.to_json(:include => [filter_criterias: {include: :filter_criteria_values}, category: {include: :section} ])
|
||||
end
|
||||
@@ -20,4 +20,5 @@ class Item < ActiveRecord::Base
|
||||
|
||||
scope :all_in_category, -> (category_id) { where(on_display: true).joins( sub_category: [:category]).where(["category_id = ?", category_id]) }
|
||||
|
||||
scope :all_in_sub_category, -> (sub_category_id) { where(on_display: true).where(["sub_category_id = ?", sub_category_id]) }
|
||||
end
|
||||
|
||||
30
front-ui/app/actions/bySubCategoryActions.js
Normal file
30
front-ui/app/actions/bySubCategoryActions.js
Normal file
@@ -0,0 +1,30 @@
|
||||
var AppDispatcher = require('../dispatcher/appDispatcher');
|
||||
var BySubCategoryConstants = require('../constants/bySubCategoryConstants');
|
||||
|
||||
// Define action methods
|
||||
var BySubCategoryActions = {
|
||||
load: function(subCategoryId, offset, limit, filter) {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: BySubCategoryConstants.LOAD,
|
||||
subCategoryId : subCategoryId,
|
||||
offset: offset,
|
||||
limit: limit,
|
||||
filter: filter
|
||||
});
|
||||
},
|
||||
filterCriteriaClick: function(fc, fcv) {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: BySubCategoryConstants.FILTER_CRITERIA_CLICK,
|
||||
fc: fc,
|
||||
fcv: fcv
|
||||
});
|
||||
},
|
||||
removeAppliedFilter: function(name) {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: BySubCategoryConstants.REMOVE_APPLIED_FILTER,
|
||||
name: name
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = BySubCategoryActions;
|
||||
@@ -3,7 +3,6 @@ var NavigationConstants = require('../constants/navigationConstants');
|
||||
|
||||
// Define action methods
|
||||
var NavigationActions = {
|
||||
|
||||
// select item
|
||||
goToItemDetails: function(item) {
|
||||
console.log("Going to item details");
|
||||
@@ -20,10 +19,7 @@ var NavigationActions = {
|
||||
url: '/sekcija/'+ section.get('id') + '/' + section.get('name')
|
||||
});
|
||||
},
|
||||
|
||||
goToCategory: function(category,section, query, offset, limit) {
|
||||
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
|
||||
|
||||
_getQueryStringPart: function(query, offset, limit) {
|
||||
var q = '';
|
||||
var qp = [];
|
||||
|
||||
@@ -46,17 +42,25 @@ var NavigationActions = {
|
||||
if (qp.length > 0) {
|
||||
q = '?' + qp.join('&');
|
||||
}
|
||||
|
||||
return q;
|
||||
},
|
||||
|
||||
goToCategory: function(category,section, query, offset, limit) {
|
||||
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
|
||||
var q = this._getQueryStringPart(query, offset, limit);
|
||||
AppDispatcher.handleAction({
|
||||
actionType: NavigationConstants.CHANGE_URL,
|
||||
url: (url + q)
|
||||
});
|
||||
},
|
||||
|
||||
goToSubCategory: function(subCategory) {
|
||||
// TODO: implement when ready
|
||||
goToSubCategory: function(subCategory, offset, limit, query) {
|
||||
var q = this._getQueryStringPart(query, offset, limit);
|
||||
var url = '/podkategorija/' + subCategory.get('id') + '/' + subCategory.get('name');
|
||||
AppDispatcher.handleAction({
|
||||
actionType: NavigationConstants.CHANGE_URL,
|
||||
url: '/'
|
||||
url: (url + q)
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var React = require('react'),
|
||||
Router = require('react-router'),
|
||||
Category = require('../../models/category'),
|
||||
SubCategory = require('../../models/subCategory'),
|
||||
Section = require('../../models/section'),
|
||||
ItemCollection = require('../../models/itemCollection'),
|
||||
ItemActions = require('../../actions/itemActions.js'),
|
||||
@@ -10,7 +11,7 @@ var React = require('react'),
|
||||
NavigationStore = require('../../stores/navigationStore'),
|
||||
ItemList = require('../items/itemList'),
|
||||
NavigationActions = require('../../actions/navigationActions'),
|
||||
LinkBanner = require('../linkBanner/linkBanner'),
|
||||
LinkBanner = require('../linkBanner/linkBanner'),
|
||||
Globals = require('../../globals');
|
||||
|
||||
var FilterCriteriaSelector = require('./filterCriteriaSelector');
|
||||
@@ -45,6 +46,11 @@ var ByCategory = React.createClass({
|
||||
NavigationActions.goToCategory(category, section, this.filter, 0, this.state.pagination.limit);
|
||||
|
||||
},
|
||||
onSCClick: function(sc) {
|
||||
var subCategory = new SubCategory(sc);
|
||||
NavigationActions.goToSubCategory(subCategory, 0, Globals.defaultLimit);
|
||||
event.preventDefault();
|
||||
},
|
||||
changePage: function(page) {
|
||||
var section = new Section(this.state.category.get('section'));
|
||||
var category = this.state.category;
|
||||
@@ -60,7 +66,7 @@ var ByCategory = React.createClass({
|
||||
<div>Podkategorije</div>
|
||||
<ul>
|
||||
{(this.state.category.get('sub_categories') || []).map(function(sc) {
|
||||
return (<li> <a>{sc.name}</a></li>)
|
||||
return (<li> <a onClick={self.onSCClick.bind(self, sc)}>{sc.name}</a></li>)
|
||||
})}
|
||||
</ul>
|
||||
|
||||
|
||||
80
front-ui/app/components/browsing/bySubCategory.js
Normal file
80
front-ui/app/components/browsing/bySubCategory.js
Normal file
@@ -0,0 +1,80 @@
|
||||
var React = require('react'),
|
||||
Router = require('react-router');
|
||||
|
||||
var BySubCategoryActions = require('../../actions/bySubCategoryActions');
|
||||
var BySubCategoryStore = require('../../stores/bySubCategoryStore');
|
||||
var ItemStore = require('../../stores/itemStore');
|
||||
var CategoryStore = require('../../stores/categoryStore');
|
||||
|
||||
var Globals = require('../../globals');
|
||||
var FilterCriteriaSelector = require('./filterCriteriaSelector');
|
||||
var AppliedFiltersList = require('./appliedFiltersList');
|
||||
|
||||
var NavigationActions = require('../../actions/navigationActions');
|
||||
var ItemList = require('../items/itemList');
|
||||
var BySubCategory = React.createClass({
|
||||
mixins: [Router.State],
|
||||
getInitialState : function() {
|
||||
return BySubCategoryStore.getState();
|
||||
},
|
||||
onFCClick: function(fc, fcv) {
|
||||
BySubCategoryActions.filterCriteriaClick(fc, fcv);
|
||||
},
|
||||
removeAppliedFilter: function(name) {
|
||||
BySubCategoryActions.removeAppliedFilter(name);
|
||||
},
|
||||
render : function() {
|
||||
|
||||
return (<div>
|
||||
<div className='col-md-2'>
|
||||
|
||||
<FilterCriteriaSelector filterCriterias={this.state.subCategory.get('filter_criterias')} onFCClick={this.onFCClick} />
|
||||
</div>
|
||||
<div classname='col-md-10'>
|
||||
|
||||
Podkategorija {this.state.subCategory.get('name')}
|
||||
|
||||
Number of items in this subcategory: {this.state.items.length}
|
||||
<div>
|
||||
<AppliedFiltersList filters={this.appliedSubCategoryFiltersArray()} onRemove={this.removeAppliedFilter} />
|
||||
|
||||
</div>
|
||||
<ItemList items={this.state.items} paginationEnabled={true} total={this.state.items.totalCount} limit={this.state.pagination.limit} onPageChange={this.changePage} currentOffset={this.state.pagination.offset} />
|
||||
</div>
|
||||
</div>)
|
||||
},
|
||||
appliedSubCategoryFiltersArray: function() {
|
||||
var filters = [];
|
||||
for(var key in this.state.filter) {
|
||||
if(this.state.filter.hasOwnProperty(key) && key !== 'limit' && key !== 'offset') {
|
||||
filters.push({name: key, value: this.state.filter[key]});
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
},
|
||||
update: function() {
|
||||
var subCategoryId = this.getParams().id;
|
||||
var filter = this.getQuery();
|
||||
var offset = filter.offset || 0;
|
||||
var limit = filter.limit || Globals.DefaultPageSize;
|
||||
|
||||
BySubCategoryActions.load(subCategoryId, offset, limit, filter);
|
||||
},
|
||||
componentWillReceiveProps: function() {
|
||||
this.update();
|
||||
},
|
||||
componentDidMount: function() {
|
||||
BySubCategoryStore.addChangeListener(this._onChange);
|
||||
this.update();
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
BySubCategoryStore.removeChangeListener(this._onChange);
|
||||
},
|
||||
_onChange: function() {
|
||||
if(this.isMounted()) {
|
||||
this.setState(BySubCategoryStore.getState());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BySubCategory;
|
||||
8
front-ui/app/constants/bySubCategoryConstants.js
Normal file
8
front-ui/app/constants/bySubCategoryConstants.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var keyMirror = require('react/lib/keyMirror');
|
||||
|
||||
// Define action constants
|
||||
module.exports = keyMirror({
|
||||
LOAD: null,
|
||||
FILTER_CRITERIA_CLICK: null,
|
||||
REMOVE_APPLIED_FILTER: null
|
||||
});
|
||||
13
front-ui/app/models/subCategory.js
Normal file
13
front-ui/app/models/subCategory.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var Backbone = require('backbone');
|
||||
var Globals = require('../globals');
|
||||
|
||||
var SubCategory = Backbone.Model.extend({
|
||||
urlRoot : Globals.ApiUrl + '/subcategory',
|
||||
defaults : {
|
||||
name: '',
|
||||
filter_criterias: []
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = SubCategory;
|
||||
@@ -14,6 +14,7 @@ var CheckoutPage = require('./components/cart/checkoutPage');
|
||||
var RootApp = require('./components/rootApp');
|
||||
var StartPage = require('./components/startPage/startPage');
|
||||
var ByCategory = require('./components/browsing/byCategory');
|
||||
var BySubCategory = require('./components/browsing/bySubCategory');
|
||||
var BySection = require('./components/browsing/bySection');
|
||||
var ThankYouPage = require('./components/thankyou/thankYouPage');
|
||||
|
||||
@@ -31,6 +32,7 @@ var routes = (
|
||||
<Route name='dostava' path="/dostava" handler={CheckoutPage} />
|
||||
<Route name='registracija' path="/registracija" handler={Register} />
|
||||
<Route name='login' path="/login" handler={Login} />
|
||||
<Route name='podkategorija' path="/podkategorija/:id/*" handler={BySubCategory} />
|
||||
<Route name='byCat' path="sekcija/:sekcijaName/kategorija/:id/*" handler={ByCategory} />
|
||||
<Route name='hvala' path="/hvala" handler={ThankYouPage} />
|
||||
<Route name='pretraga' path="/pretraga" handler={SearchResultsPage} />
|
||||
|
||||
170
front-ui/app/stores/bySubCategoryStore.js
Normal file
170
front-ui/app/stores/bySubCategoryStore.js
Normal file
@@ -0,0 +1,170 @@
|
||||
var AppDispatcher = require('../dispatcher/appDispatcher');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
//var SubCategoryCollection = require('../models/subCategoryCollection');
|
||||
var SubCategory = require('../models/subCategory');
|
||||
|
||||
var NavigationActions = require('../actions/navigationActions');
|
||||
var BySubCategoryConstants = require('../constants/bySubCategoryConstants');
|
||||
var ItemCollection = require('../models/itemCollection');
|
||||
var _ = require('underscore');
|
||||
|
||||
var _state = {
|
||||
subCategory : (new SubCategory()),
|
||||
items: (new ItemCollection()),
|
||||
filter: {},
|
||||
pagination: {}
|
||||
};
|
||||
//var _categoryDetails = new Category();
|
||||
|
||||
//var loadSections = function() {
|
||||
//var sections = new SectionCollection();
|
||||
//sections.fetch({success: function() {
|
||||
//sectionState.sections = sections.models;
|
||||
//// change will be called automatically when
|
||||
//// action is run but we need to emit it again
|
||||
//// when the data arive
|
||||
//// it's a bit "unfluxy" but convenient.
|
||||
//// "true philosophy" would be to run another "data arrived" action
|
||||
//SectionStore.emitChange();
|
||||
//}});
|
||||
//};
|
||||
var fetchItemsBySubCategory = function(subCategoryId, offset, limit, query) {
|
||||
//var items = _itemsByCategory;
|
||||
query = query || {};
|
||||
var items = new ItemCollection();
|
||||
items.clearFilter();
|
||||
items.setClassificationType(3);
|
||||
items.setClassificationId(subCategoryId);
|
||||
items.setLimit(limit);
|
||||
items.setOffset(offset);
|
||||
|
||||
for(var key in query) {
|
||||
if (query.hasOwnProperty(key) && key != 'limit' && key !='offset') {
|
||||
items.addFilter(key, query[key]);
|
||||
}
|
||||
}
|
||||
|
||||
items.fetch({
|
||||
success: function(collection, response, options) {
|
||||
var total = options.xhr.getResponseHeader('x-total-count');
|
||||
items.setTotalCount(total);
|
||||
|
||||
_state.items = items;
|
||||
BySubCategoryStore.emitChange();
|
||||
}});
|
||||
|
||||
};
|
||||
var load = function(subCategoryId, offset, limit, filter) {
|
||||
|
||||
var subCategory = new SubCategory({id : subCategoryId});
|
||||
subCategory.fetch({
|
||||
success: function() {
|
||||
_state.subCategory = subCategory;
|
||||
fetchItemsBySubCategory(subCategoryId, offset, limit, filter);
|
||||
|
||||
}
|
||||
});
|
||||
_state.filter = filter;
|
||||
//BySubCategoryStore.emitChange();
|
||||
};
|
||||
|
||||
var handleFilterCriteriaClick = function(fc, fcv) {
|
||||
_state.filter[fc.field_name] = fcv.filter_value;
|
||||
|
||||
setTimeout(function() {
|
||||
NavigationActions.goToSubCategory(_state.subCategory, 0, _state.pagination.limit, _state.filter);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
var handleRemoveAppliedFilter= function(name) {
|
||||
delete _state.filter[name];
|
||||
|
||||
setTimeout(function() {
|
||||
NavigationActions.goToSubCategory(_state.subCategory, 0, _state.pagination.limit, _state.filter);
|
||||
}, 0);
|
||||
};
|
||||
//var loadCategoryDetails = function(categoryId) {
|
||||
//var category = new Category({id : categoryId});
|
||||
//category.fetch({
|
||||
//success: function() {
|
||||
//_categoryDetails = category;
|
||||
//BySubCategoryStore.emitChange();
|
||||
//}
|
||||
//});
|
||||
//};
|
||||
|
||||
//var setHovered = function(id) {
|
||||
//sectionState.hoveredSection = id;
|
||||
//}
|
||||
|
||||
|
||||
// Extend SectionStore with EventEmitter to add eventing capabilities
|
||||
var BySubCategoryStore = _.extend({}, EventEmitter.prototype, {
|
||||
|
||||
getState: function() {
|
||||
return _state;
|
||||
},
|
||||
// Emit Change event
|
||||
emitChange: function() {
|
||||
console.log("Emmiting BySubCategory change!");
|
||||
this.emit('change');
|
||||
},
|
||||
|
||||
// Add change listener
|
||||
addChangeListener: function(callback) {
|
||||
this.on('change', callback);
|
||||
},
|
||||
|
||||
// Remove change listener
|
||||
removeChangeListener: function(callback) {
|
||||
this.removeListener('change', callback);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Register callback with AppDispatcher
|
||||
AppDispatcher.register(function(payload) {
|
||||
var action = payload.action;
|
||||
var text;
|
||||
|
||||
switch(action.actionType) {
|
||||
|
||||
case BySubCategoryConstants.LOAD:
|
||||
load(action.subCategoryId, action.offset, action.limit, action.filter);
|
||||
break;
|
||||
|
||||
case BySubCategoryConstants.FILTER_CRITERIA_CLICK:
|
||||
handleFilterCriteriaClick(action.fc, action.fcv);
|
||||
break;
|
||||
case BySubCategoryConstants.REMOVE_APPLIED_FILTER:
|
||||
handleRemoveAppliedFilter(action.name);
|
||||
break;
|
||||
|
||||
// Respond to SELECT_ITEM action
|
||||
//case SectionConstants.LOAD_SECTIONS:
|
||||
//loadSections();
|
||||
//break;
|
||||
|
||||
//case SectionConstants.SET_SECTION_HOVER:
|
||||
//setHovered(action.section.get('id'));
|
||||
//break;
|
||||
|
||||
//case SectionConstants.UNSET_SECTION_HOVER:
|
||||
//setHovered('');
|
||||
//break;
|
||||
|
||||
//case CategoryConstants.LOAD_CATEGORY_DETAILS:
|
||||
//loadCategoryDetails(action.categoryId);
|
||||
//break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
// If action was responded to, emit change event
|
||||
BySubCategoryStore.emitChange();
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
module.exports = BySubCategoryStore;
|
||||
Reference in New Issue
Block a user