Merge branch '2112-order_catalog_improvements' of verdnatura/salix into dev
gitea/salix/dev This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2020-02-19 13:48:00 +00:00 committed by Gitea
commit aece3e8788
7 changed files with 179 additions and 72 deletions

View File

@ -620,9 +620,9 @@ export default {
orderCatalog: { orderCatalog: {
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]', plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
type: 'vn-autocomplete[data="$ctrl.itemTypes"]', type: 'vn-autocomplete[data="$ctrl.itemTypes"]',
itemId: 'vn-order-catalog > vn-side-menu vn-textfield[ng-model="$ctrl.itemId"]', itemId: 'vn-order-catalog > vn-side-menu vn-textfield[vn-id="itemId"]',
itemTagValue: 'vn-order-catalog > vn-side-menu vn-datalist[ng-model="$ctrl.value"]', itemTagValue: 'vn-order-catalog > vn-side-menu vn-datalist[vn-id="search"]',
openTagSearch: 'vn-order-catalog > vn-side-menu > div > vn-vertical > vn-datalist[ng-model="$ctrl.value"] .append i', openTagSearch: 'vn-order-catalog > vn-side-menu > div > vn-vertical > vn-datalist[vn-id="search"] .append i',
tag: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]', tag: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
tagValue: 'vn-order-catalog-search-panel vn-textfield[ng-model="filter.value"]', tagValue: 'vn-order-catalog-search-panel vn-textfield[ng-model="filter.value"]',
searchTagButton: 'vn-order-catalog-search-panel button[type=submit]', searchTagButton: 'vn-order-catalog-search-panel button[type=submit]',

View File

@ -1,13 +1,15 @@
<div class="vn-pa-lg" style="min-width: 10em"> <div class="vn-pa-lg" style="min-width: 10em">
<form ng-submit="$ctrl.onSearch()"> <form name="form" ng-submit="$ctrl.onSearch()">
<vn-horizontal> <vn-horizontal>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
label="Tag" label="Tag"
ng-model="filter.tagFk" ng-model="filter.tagFk"
selection="filter.tagSelection"
url="Tags" url="Tags"
show-field="name" show-field="name"
value-field="id"> value-field="id"
required="true">
<tpl-item>{{name}}</tpl-item> <tpl-item>{{name}}</tpl-item>
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
@ -15,7 +17,8 @@
<vn-textfield <vn-textfield
vn-one vn-one
label="Value" label="Value"
ng-model="filter.value"> ng-model="filter.value"
required="true">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-mt-lg"> <vn-horizontal class="vn-mt-lg">

View File

@ -77,10 +77,9 @@
</div> </div>
</vn-vertical> </vn-vertical>
<vn-vertical class="input vn-pt-md"> <vn-vertical class="input vn-pt-md">
<vn-textfield <vn-textfield vn-id="itemId"
ng-keyUp="$ctrl.onSearchById($event)" ng-keyUp="$ctrl.onSearchById($event)"
label="Item id" label="Item id">
ng-model="$ctrl.itemId">
<prepend> <prepend>
<vn-icon icon="icon-item"></vn-icon> <vn-icon icon="icon-item"></vn-icon>
</prepend> </prepend>
@ -89,7 +88,6 @@
<vn-datalist vn-one <vn-datalist vn-one
vn-id="search" vn-id="search"
data="$ctrl.tagValues" data="$ctrl.tagValues"
ng-model="$ctrl.value"
ng-keyUp="$ctrl.onSearchByTag($event)" ng-keyUp="$ctrl.onSearchByTag($event)"
show-field="value" show-field="value"
value-field="value" value-field="value"
@ -115,6 +113,14 @@
</vn-order-catalog-search-panel> </vn-order-catalog-search-panel>
</vn-popover> </vn-popover>
<div class="chips"> <div class="chips">
<vn-chip
ng-if="$ctrl.itemId"
removable="true"
translate-attr="{title: 'Item'}"
on-remove="$ctrl.itemId = null"
class="colored">
<span>Id: {{$ctrl.itemId}}</span>
</vn-chip>
<vn-chip <vn-chip
ng-if="category.selection" ng-if="category.selection"
removable="true" removable="true"
@ -137,7 +143,17 @@
translate-attr="{title: 'Tag'}" translate-attr="{title: 'Tag'}"
on-remove="$ctrl.remove($index)" on-remove="$ctrl.remove($index)"
class="colored"> class="colored">
<span translate>{{::tag.value}}</span> <div>
<span ng-if="::tag.tagFk">
<span translate>
{{::tag.tagSelection.name}}
</span>
<span ng-if="::tag.value">: </span>
</span>
<span translate ng-if="::tag.value">
"{{::tag.value}}"
</span>
</div>
</vn-chip> </vn-chip>
</div> </div>
</vn-side-menu> </vn-side-menu>

View File

@ -10,7 +10,7 @@ class Controller {
this.$compile = $compile; this.$compile = $compile;
this.$transitions = $transitions; this.$transitions = $transitions;
this.itemTypes = []; this.itemTypes = [];
this.tags = []; this._tags = [];
// Static autocomplete data // Static autocomplete data
this.orderWays = [ this.orderWays = [
@ -52,11 +52,17 @@ class Controller {
if (!value) return; if (!value) return;
this.$.$applyAsync(() => { this.$.$applyAsync(() => {
if (this.$stateParams.itemId)
this.itemId = parseInt(this.$stateParams.itemId);
if (this.$stateParams.categoryId) if (this.$stateParams.categoryId)
this.categoryId = this.$stateParams.categoryId; this.categoryId = parseInt(this.$stateParams.categoryId);
if (this.$stateParams.typeId) if (this.$stateParams.typeId)
this.typeId = this.$stateParams.typeId; this.typeId = parseInt(this.$stateParams.typeId);
if (this.$stateParams.tags)
this.tags = JSON.parse(this.$stateParams.tags);
}); });
} }
@ -109,6 +115,30 @@ class Controller {
this.applyFilters(); this.applyFilters();
} }
get itemId() {
return this._itemId;
}
set itemId(value) {
this._itemId = value;
this.updateStateParams();
this.applyFilters();
}
get tags() {
return this._tags;
}
set tags(value) {
this._tags = value;
this.updateStateParams();
if (value.length)
this.applyFilters();
}
/** /**
* Get order way ASC/DESC * Get order way ASC/DESC
*/ */
@ -169,23 +199,36 @@ class Controller {
this.itemTypes = res.data); this.itemTypes = res.data);
} }
/**
* Search by item id filter
* @param {object} event
*/
onSearchById(event) { onSearchById(event) {
const hasValue = this.tags.length > 0 || this.itemId || this.typeId; const value = this.$.itemId.value;
if (event.key === 'Enter' && hasValue) if (event.key === 'Enter' && value) {
this.applyFilters(); this.itemId = value;
this.$.itemId.value = null;
}
} }
/**
* Search by tag value
* @param {object} event
*/
onSearchByTag(event) { onSearchByTag(event) {
if (event.key !== 'Enter' || !this.value) return; const value = this.$.search.value;
if (event.key !== 'Enter' || !value) return;
this.tags.push({ this.tags.push({
value: this.value, value: value,
}); });
this.$.search.value = null; this.$.search.value = null;
this.updateStateParams();
this.applyFilters(); this.applyFilters();
} }
remove(index) { remove(index) {
this.tags.splice(index, 1); this.tags.splice(index, 1);
this.updateStateParams();
if (this.tags.length >= 0 || this.itemId || this.typeId) if (this.tags.length >= 0 || this.itemId || this.typeId)
this.applyFilters(); this.applyFilters();
@ -225,6 +268,7 @@ class Controller {
onPanelSubmit(filter) { onPanelSubmit(filter) {
this.$.popover.hide(); this.$.popover.hide();
this.tags.push(filter); this.tags.push(filter);
this.updateStateParams();
this.applyFilters(); this.applyFilters();
} }
@ -234,13 +278,36 @@ class Controller {
updateStateParams() { updateStateParams() {
const params = {}; const params = {};
params.categoryId = undefined;
if (this.categoryId) if (this.categoryId)
params.categoryId = this.categoryId; params.categoryId = this.categoryId;
else params.categoryId = undefined;
params.typeId = undefined;
if (this.typeId) if (this.typeId)
params.typeId = this.typeId; params.typeId = this.typeId;
else params.typeId = undefined;
params.itemId = undefined;
if (this.itemId)
params.itemId = this.itemId;
params.tags = undefined;
if (this.tags.length) {
const tags = [];
for (let tag of this.tags) {
const tagParam = {value: tag.value};
if (tag.tagSelection) {
tagParam.tagFk = tag.tagFk;
tagParam.tagSelection = {
name: tag.tagSelection.name
};
}
tags.push(tagParam);
}
params.tags = JSON.stringify(tags);
}
this.$state.go(this.$state.current.name, params); this.$state.go(this.$state.current.name, params);
} }

View File

@ -15,6 +15,7 @@ describe('Order', () => {
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$scope.model = crudModel; $scope.model = crudModel;
$scope.search = {}; $scope.search = {};
$scope.itemId = {};
$state = _$state_; $state = _$state_;
$state.params.categoryId = 1; $state.params.categoryId = 1;
$state.params.typeId = 2; $state.params.typeId = 2;
@ -37,8 +38,9 @@ describe('Order', () => {
describe('items() setter', () => { describe('items() setter', () => {
it(`should return an object with order params`, () => { it(`should return an object with order params`, () => {
spyOn(controller, 'buildTagsFilter'); jest.spyOn(controller, 'buildTagsFilter');
spyOn(controller, 'buildOrderFilter').and.callThrough(); jest.spyOn(controller, 'buildOrderFilter');
const expectedResult = [{field: 'showOrder, price', name: 'Color and price'}]; const expectedResult = [{field: 'showOrder, price', name: 'Color and price'}];
const items = [{id: 1, name: 'My Item', tags: [ const items = [{id: 1, name: 'My Item', tags: [
{tagFk: 4, name: 'Length'}, {tagFk: 4, name: 'Length'},
@ -55,14 +57,16 @@ describe('Order', () => {
describe('categoryId() setter', () => { describe('categoryId() setter', () => {
it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => { it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => {
spyOn(controller, 'updateStateParams'); jest.spyOn(controller, 'updateStateParams');
controller.categoryId = null; controller.categoryId = null;
expect(controller.updateStateParams).toHaveBeenCalledWith(); expect(controller.updateStateParams).toHaveBeenCalledWith();
}); });
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => { it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
spyOn(controller, 'updateStateParams'); jest.spyOn(controller, 'updateStateParams');
controller.categoryId = 2; controller.categoryId = 2;
expect(controller.updateStateParams).toHaveBeenCalledWith(); expect(controller.updateStateParams).toHaveBeenCalledWith();
@ -87,8 +91,9 @@ describe('Order', () => {
describe('typeId() setter', () => { describe('typeId() setter', () => {
it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => { it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => {
spyOn(controller, 'updateStateParams'); jest.spyOn(controller, 'updateStateParams');
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.typeId = null; controller.typeId = null;
expect(controller.updateStateParams).toHaveBeenCalledWith(); expect(controller.updateStateParams).toHaveBeenCalledWith();
@ -96,8 +101,9 @@ describe('Order', () => {
}); });
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => { it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
spyOn(controller, 'updateStateParams'); jest.spyOn(controller, 'updateStateParams');
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.typeId = 2; controller.typeId = 2;
expect(controller.updateStateParams).toHaveBeenCalledWith(); expect(controller.updateStateParams).toHaveBeenCalledWith();
@ -105,21 +111,46 @@ describe('Order', () => {
}); });
}); });
describe('itemId() setter', () => {
it(`should set itemId property and then call updateStateParams() and applyFilters() methods`, () => {
jest.spyOn(controller, 'updateStateParams');
jest.spyOn(controller, 'applyFilters');
controller.itemId = 1;
expect(controller.updateStateParams).toHaveBeenCalledWith();
expect(controller.applyFilters).toHaveBeenCalledWith();
});
});
describe('tags() setter', () => {
it(`should set tags property and then call updateStateParams() and applyFilters() methods`, () => {
jest.spyOn(controller, 'updateStateParams');
jest.spyOn(controller, 'applyFilters');
controller.tags = [{tagFk: 11, value: 'Brown'}];
expect(controller.updateStateParams).toHaveBeenCalledWith();
expect(controller.applyFilters).toHaveBeenCalledWith();
});
});
describe('onSearchByTag()', () => { describe('onSearchByTag()', () => {
it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => { it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.order = {id: 4}; controller.order = {id: 4};
controller.value = 'Color'; controller.$.search.value = 'Brown';
controller.onSearchByTag({key: 'Tab'}); controller.onSearchByTag({key: 'Tab'});
expect(controller.applyFilters).not.toHaveBeenCalledWith(); expect(controller.applyFilters).not.toHaveBeenCalledWith();
}); });
it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => { it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.order = {id: 4};
controller.value = 'Color';
controller.order = {id: 4};
controller.$.search.value = 'Brown';
controller.onSearchByTag({key: 'Enter'}); controller.onSearchByTag({key: 'Enter'});
expect(controller.applyFilters).toHaveBeenCalledWith(); expect(controller.applyFilters).toHaveBeenCalledWith();
@ -128,17 +159,18 @@ describe('Order', () => {
describe('onSearchById()', () => { describe('onSearchById()', () => {
it(`should not filter by id if the event key code doesn't equals to 'Enter'`, () => { it(`should not filter by id if the event key code doesn't equals to 'Enter'`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.itemId = 1;
controller.$.itemId.value = 1;
controller.onSearchById({key: 'Tab'}); controller.onSearchById({key: 'Tab'});
expect(controller.applyFilters).not.toHaveBeenCalledWith(); expect(controller.applyFilters).not.toHaveBeenCalledWith();
}); });
it(`should filter by id if the event key code equals to 'Enter' an then call applyFilters()`, () => { it(`should filter by id if the event key code equals to 'Enter' an then call applyFilters()`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.itemId = 1;
controller.$.itemId.value = 1;
controller.onSearchById({key: 'Enter'}); controller.onSearchById({key: 'Enter'});
expect(controller.applyFilters).toHaveBeenCalledWith(); expect(controller.applyFilters).toHaveBeenCalledWith();
@ -147,14 +179,14 @@ describe('Order', () => {
describe('applyFilters()', () => { describe('applyFilters()', () => {
it(`should call model applyFilter() method with a new filter`, () => { it(`should call model applyFilter() method with a new filter`, () => {
let model = controller.$.model; jest.spyOn(controller.$.model, 'applyFilter');
spyOn(model, 'applyFilter');
controller._categoryId = 2; controller._categoryId = 2;
controller._typeId = 4; controller._typeId = 4;
controller.applyFilters(); controller.applyFilters();
expect(model.applyFilter).toHaveBeenCalledWith( expect(controller.$.model.applyFilter).toHaveBeenCalledWith(
{where: {categoryFk: 2, typeFk: 4}}, {where: {categoryFk: 2, typeFk: 4}},
{orderFk: 4, orderBy: controller.getOrderBy(), tags: []}); {orderFk: 4, orderBy: controller.getOrderBy(), tags: []});
}); });
@ -162,7 +194,8 @@ describe('Order', () => {
describe('remove()', () => { describe('remove()', () => {
it(`should remove a tag from tags property`, () => { it(`should remove a tag from tags property`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller.tags = [{tagFk: 1, value: 'Blue'}, {tagFk: 2, value: '70'}]; controller.tags = [{tagFk: 1, value: 'Blue'}, {tagFk: 2, value: '70'}];
controller.remove(0); controller.remove(0);
@ -172,7 +205,8 @@ describe('Order', () => {
}); });
it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => { it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => {
spyOn(controller, 'applyFilters'); jest.spyOn(controller, 'applyFilters');
controller._categoryId = 1; controller._categoryId = 1;
controller._typeId = 1; controller._typeId = 1;
controller.tags = [{tagFk: 1, value: 'Blue'}]; controller.tags = [{tagFk: 1, value: 'Blue'}];
@ -185,10 +219,19 @@ describe('Order', () => {
describe('updateStateParams()', () => { describe('updateStateParams()', () => {
it(`should call state go() method passing category and type state params`, () => { it(`should call state go() method passing category and type state params`, () => {
spyOn(controller.$state, 'go'); jest.spyOn(controller.$state, 'go');
controller._categoryId = 2; controller._categoryId = 2;
controller._typeId = 4; controller._typeId = 4;
let result = {categoryId: 2, typeId: 4}; controller._itemId = 1;
controller._tags = [
{tagFk: 11, value: 'Precission', tagSelection: {name: 'Category'}}
];
const tags = JSON.stringify([{
value: 'Precission',
tagFk: 11, tagSelection: {name: 'Category'}}
]);
let result = {categoryId: 2, typeId: 4, itemId: 1, tags: tags};
controller.updateStateParams(); controller.updateStateParams();
expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', result); expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', result);
@ -212,13 +255,15 @@ describe('Order', () => {
describe('applyOrder()', () => { describe('applyOrder()', () => {
it(`should apply order param to model calling getOrderBy()`, () => { it(`should apply order param to model calling getOrderBy()`, () => {
jest.spyOn(controller, 'getOrderBy');
jest.spyOn(controller.$.model, 'addFilter');
controller.field = 'relevancy DESC, name'; controller.field = 'relevancy DESC, name';
controller.way = 'ASC'; controller.way = 'ASC';
controller._categoryId = 1; controller._categoryId = 1;
controller._typeId = 1; controller._typeId = 1;
let expectedOrder = {orderBy: controller.getOrderBy()}; let expectedOrder = {orderBy: controller.getOrderBy()};
spyOn(controller, 'getOrderBy').and.callThrough();
spyOn(controller.$.model, 'addFilter');
controller.applyOrder(); controller.applyOrder();
expect(controller.getOrderBy).toHaveBeenCalledWith(); expect(controller.getOrderBy).toHaveBeenCalledWith();

View File

@ -73,30 +73,6 @@ class Controller extends Component {
addQuantity(price) { addQuantity(price) {
if (this.total + price.grouping <= this.max) if (this.total + price.grouping <= this.max)
price.quantity += price.grouping; price.quantity += price.grouping;
this.validate();
}
validate() {
/*
this.$timeout(() => {
this.calculateTotal();
let inputs = this.$element[0].querySelectorAll('vn-input-number[name="quantity"] div.infix:not(.validated)');
inputs.forEach(input => {
if (this.total > this.item.available)
input.classList.add('invalid');
else
input.classList.remove('invalid');
});
let wrongInputs = this.$element[0].querySelectorAll('vn-input-number[name="quantity"] div.infix.invalid');
if (wrongInputs.length > 0)
this.$element[0].querySelector('vn-vertical.prices').classList.add('invalid');
else
this.$element[0].querySelector('vn-vertical.prices').classList.remove('invalid');
});
*/
} }
getFilledLines() { getFilledLines() {

View File

@ -41,7 +41,7 @@
"order": "$ctrl.order" "order": "$ctrl.order"
} }
}, { }, {
"url": "/catalog?categoryId&typeId", "url": "/catalog?categoryId&typeId&itemId&tags",
"state": "order.card.catalog", "state": "order.card.catalog",
"component": "vn-order-catalog", "component": "vn-order-catalog",
"description": "Catalog", "description": "Catalog",