refactor
gitea/salix/2064-catalog_refactor This commit looks good
Details
gitea/salix/2064-catalog_refactor This commit looks good
Details
This commit is contained in:
parent
31349b2ebf
commit
8f851d5c15
|
@ -604,14 +604,14 @@ export default {
|
||||||
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
|
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
|
||||||
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
|
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
|
||||||
typeAutocomplete: 'vn-autocomplete[data="$ctrl.itemTypes"]',
|
typeAutocomplete: 'vn-autocomplete[data="$ctrl.itemTypes"]',
|
||||||
itemIdInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.itemId"]',
|
itemIdInput: 'vn-order-catalog > vn-side-menu vn-textfield[ng-model="$ctrl.itemId"]',
|
||||||
itemTagValueInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.value"]',
|
itemTagValueInput: 'vn-order-catalog > vn-side-menu vn-textfield[ng-model="$ctrl.value"]',
|
||||||
openTagSearch: 'vn-catalog-filter > div > vn-vertical > vn-textfield[ng-model="$ctrl.value"] .append i',
|
openTagSearch: 'vn-order-catalog > vn-side-menu > div > vn-vertical > vn-textfield[ng-model="$ctrl.value"] .append i',
|
||||||
tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
|
tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
|
||||||
tagValueInput: 'vn-order-catalog-search-panel [ng-model="filter.value"]',
|
tagValueInput: 'vn-order-catalog-search-panel [ng-model="filter.value"]',
|
||||||
searchTagButton: 'vn-order-catalog-search-panel button[type=submit]',
|
searchTagButton: 'vn-order-catalog-search-panel button[type=submit]',
|
||||||
thirdFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(3) vn-icon[icon=cancel]',
|
thirdFilterRemoveButton: 'vn-order-catalog > vn-side-menu .chips > vn-chip:nth-child(3) vn-icon[icon=cancel]',
|
||||||
fourthFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(4) vn-icon[icon=cancel]',
|
fourthFilterRemoveButton: 'vn-order-catalog > vn-side-menu .chips > vn-chip:nth-child(4) vn-icon[icon=cancel]',
|
||||||
},
|
},
|
||||||
orderBasicData: {
|
orderBasicData: {
|
||||||
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<vn-data-viewer
|
||||||
|
model="$ctrl.model">
|
||||||
|
<vn-horizontal class="catalog-list">
|
||||||
|
<section class="product" ng-repeat="item in $ctrl.model.data">
|
||||||
|
<vn-card>
|
||||||
|
<div class="image">
|
||||||
|
<img
|
||||||
|
ng-src="//verdnatura.es/vn-image-data/catalog/200x200/{{::item.image}}"
|
||||||
|
zoom-image="//verdnatura.es/vn-image-data/catalog/1600x900/{{::item.image}}"
|
||||||
|
on-error-src/>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<h3>
|
||||||
|
{{::item.name}}
|
||||||
|
</h3>
|
||||||
|
<h4 class="ellipsize">
|
||||||
|
<span translate-attr="::{title: item.subName}">{{::item.subName}}</span>
|
||||||
|
</h4>
|
||||||
|
<div class="tags">
|
||||||
|
<vn-label-value
|
||||||
|
ng-if="::item.value5"
|
||||||
|
label="{{::item.tag5}}"
|
||||||
|
value="{{::item.value5}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
|
ng-if="::item.value6"
|
||||||
|
label="{{::item.tag6}}"
|
||||||
|
value="{{::item.value6}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
|
ng-if="::item.value7"
|
||||||
|
label="{{::item.tag7}}"
|
||||||
|
value="{{::item.value7}}">
|
||||||
|
</vn-label-value>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="price">
|
||||||
|
<vn-one>
|
||||||
|
<span>{{::item.available}}</span>
|
||||||
|
<span translate>from</span>
|
||||||
|
<span>{{::item.price | currency:'EUR':2}}</span>
|
||||||
|
</vn-one>
|
||||||
|
<vn-icon-button vn-none
|
||||||
|
icon="add_circle"
|
||||||
|
ng-click="$ctrl.preview($event, item)"
|
||||||
|
vn-tooltip="Add">
|
||||||
|
</vn-icon-button>
|
||||||
|
</div>
|
||||||
|
<div class="priceKg" ng-show="::item.priceKg">
|
||||||
|
<span>Precio por kilo {{::item.priceKg | currency: 'EUR'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</vn-card>
|
||||||
|
</section>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-data-viewer>
|
||||||
|
<vn-order-prices-popover
|
||||||
|
vn-id="pricesPopover"
|
||||||
|
order="$ctrl.order">
|
||||||
|
</vn-order-prices-popover>
|
|
@ -0,0 +1,24 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
|
||||||
|
class Controller extends Component {
|
||||||
|
preview(event, item) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$.pricesPopover.show(event, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDescriptorLoad() {
|
||||||
|
this.$.popover.relocate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.component('vnOrderCatalogView', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
order: '<',
|
||||||
|
model: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-order-catalog {
|
||||||
|
.catalog-header {
|
||||||
|
border-bottom: $border-thin;
|
||||||
|
padding: $spacing-md;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > vn-one {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $color-font-secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > vn-auto {
|
||||||
|
width: 28em;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
padding-left: $spacing-md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.catalog-list {
|
||||||
|
padding-top: $spacing-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,72 +1,140 @@
|
||||||
|
|
||||||
|
<vn-crud-model
|
||||||
|
url="ItemCategories"
|
||||||
|
data="categories"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Orders/CatalogFilter"
|
url="Orders/CatalogFilter"
|
||||||
params="{orderFk: $ctrl.$state.params.id}"
|
params="{orderFk: $ctrl.$state.params.id}"
|
||||||
limit="50"
|
limit="50"
|
||||||
data="items"
|
data="$ctrl.items">
|
||||||
on-data-change="$ctrl.onDataChange()">
|
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer
|
|
||||||
model="model">
|
<vn-order-catalog-view model="model"
|
||||||
<vn-horizontal class="catalog-list">
|
|
||||||
<section class="product" ng-repeat="item in items">
|
|
||||||
<vn-card>
|
|
||||||
<div class="image">
|
|
||||||
<img
|
|
||||||
ng-src="//verdnatura.es/vn-image-data/catalog/200x200/{{::item.image}}"
|
|
||||||
zoom-image="//verdnatura.es/vn-image-data/catalog/1600x900/{{::item.image}}"
|
|
||||||
on-error-src/>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<h3>
|
|
||||||
{{::item.name}}
|
|
||||||
</h3>
|
|
||||||
<h4 class="ellipsize">
|
|
||||||
<span translate-attr="::{title: item.subName}">{{::item.subName}}</span>
|
|
||||||
</h4>
|
|
||||||
<div class="tags">
|
|
||||||
<vn-label-value
|
|
||||||
ng-if="::item.value5"
|
|
||||||
label="{{::item.tag5}}"
|
|
||||||
value="{{::item.value5}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value
|
|
||||||
ng-if="::item.value6"
|
|
||||||
label="{{::item.tag6}}"
|
|
||||||
value="{{::item.value6}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value
|
|
||||||
ng-if="::item.value7"
|
|
||||||
label="{{::item.tag7}}"
|
|
||||||
value="{{::item.value7}}">
|
|
||||||
</vn-label-value>
|
|
||||||
</div>
|
|
||||||
<div class="footer">
|
|
||||||
<div class="price">
|
|
||||||
<vn-one>
|
|
||||||
<span>{{::item.available}}</span>
|
|
||||||
<span translate>from</span>
|
|
||||||
<span>{{::item.price | currency:'EUR':2}}</span>
|
|
||||||
</vn-one>
|
|
||||||
<vn-icon-button vn-none
|
|
||||||
icon="add_circle"
|
|
||||||
ng-click="$ctrl.preview($event, item)"
|
|
||||||
vn-tooltip="Add">
|
|
||||||
</vn-icon-button>
|
|
||||||
</div>
|
|
||||||
<div class="priceKg" ng-show="::item.priceKg">
|
|
||||||
<span>Precio por kilo {{::item.priceKg | currency: 'EUR'}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</vn-card>
|
|
||||||
</section>
|
|
||||||
</vn-horizontal>
|
|
||||||
</vn-data-viewer>
|
|
||||||
<vn-side-menu side="right">
|
|
||||||
<vn-catalog-filter order="$ctrl.order"></vn-catalog-filter>
|
|
||||||
</vn-side-menu>
|
|
||||||
<vn-order-prices-popover
|
|
||||||
vn-id="pricesPopover"
|
|
||||||
order="$ctrl.order">
|
order="$ctrl.order">
|
||||||
</vn-order-prices-popover>
|
</vn-order-catalog-view>
|
||||||
|
<vn-side-menu side="right">
|
||||||
|
<vn-horizontal class="item-category">
|
||||||
|
<vn-autocomplete vn-id="category"
|
||||||
|
data="categories"
|
||||||
|
ng-model="$ctrl.categoryId"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
label="Category">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-one ng-repeat="category in categories">
|
||||||
|
<vn-icon
|
||||||
|
ng-class="{'active': $ctrl.categoryId == category.id}"
|
||||||
|
icon="{{::category.icon}}"
|
||||||
|
vn-tooltip="{{::category.name}}"
|
||||||
|
ng-click="$ctrl.categoryId = category.id">
|
||||||
|
</vn-icon>
|
||||||
|
</vn-one>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-vertical class="input">
|
||||||
|
<vn-autocomplete vn-id="type"
|
||||||
|
data="$ctrl.itemTypes"
|
||||||
|
ng-model="$ctrl.typeId"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
label="Type"
|
||||||
|
fields="['categoryFk']"
|
||||||
|
include="'category'">
|
||||||
|
<tpl-item>
|
||||||
|
<div>{{name}}</div>
|
||||||
|
<div class="text-caption text-secondary">
|
||||||
|
{{categoryName}}
|
||||||
|
</div>
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-vertical class="input vn-pt-md">
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-id="field"
|
||||||
|
data="$ctrl.orderFields"
|
||||||
|
ng-model="$ctrl.orderField"
|
||||||
|
selection="$ctrl.orderSelection"
|
||||||
|
translate-fields="['name']"
|
||||||
|
order="name"
|
||||||
|
show-field="name"
|
||||||
|
value-field="field"
|
||||||
|
label="Order by"
|
||||||
|
disabled="!model.data">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
data="$ctrl.orderWays"
|
||||||
|
ng-model="$ctrl.orderWay"
|
||||||
|
translate-fields="['name']"
|
||||||
|
show-field="name"
|
||||||
|
value-field="way"
|
||||||
|
label="Order"
|
||||||
|
disabled="!model.data">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<div ng-if="false && model.moreRows">
|
||||||
|
<span translate>More than</span> {{model.limit}} <span translate>results</span>
|
||||||
|
</div>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-vertical class="input vn-pt-md">
|
||||||
|
<vn-textfield
|
||||||
|
ng-keyUp="$ctrl.onSearchById($event)"
|
||||||
|
label="Item id"
|
||||||
|
ng-model="$ctrl.itemId">
|
||||||
|
<prepend>
|
||||||
|
<vn-icon icon="icon-item"></vn-icon>
|
||||||
|
</prepend>
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
vn-id="search"
|
||||||
|
ng-keyUp="$ctrl.onSearchByTag($event)"
|
||||||
|
label="Search tag"
|
||||||
|
ng-model="$ctrl.value">
|
||||||
|
<prepend>
|
||||||
|
<vn-icon icon="search"></vn-icon>
|
||||||
|
</prepend>
|
||||||
|
<append>
|
||||||
|
<vn-icon
|
||||||
|
icon="keyboard_arrow_down"
|
||||||
|
ng-click="$ctrl.openPanel($event)"
|
||||||
|
style="cursor: pointer;">
|
||||||
|
</vn-icon>
|
||||||
|
</append>
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-popover
|
||||||
|
vn-id="popover"
|
||||||
|
on-close="$ctrl.onPopoverClose()">
|
||||||
|
<vn-order-catalog-search-panel
|
||||||
|
filter="panelFilter"
|
||||||
|
on-submit="$ctrl.onPanelSubmit($filter)">
|
||||||
|
</vn-order-catalog-search-panel>
|
||||||
|
</vn-popover>
|
||||||
|
<div class="chips">
|
||||||
|
<vn-chip
|
||||||
|
ng-if="category.selection"
|
||||||
|
removable="true"
|
||||||
|
translate-attr="{title: 'Category'}"
|
||||||
|
on-remove="$ctrl.categoryId = null"
|
||||||
|
class="colored">
|
||||||
|
<span translate>{{category.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="type.selection"
|
||||||
|
removable="true"
|
||||||
|
translate-attr="{title: 'Type'}"
|
||||||
|
on-remove="$ctrl.typeId = null"
|
||||||
|
class="colored">
|
||||||
|
<span translate>{{type.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-repeat="tag in $ctrl.tags"
|
||||||
|
removable="true"
|
||||||
|
translate-attr="{title: 'Tag'}"
|
||||||
|
on-remove="$ctrl.remove($index)"
|
||||||
|
class="colored">
|
||||||
|
<span translate>{{::tag.value}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
</div>
|
||||||
|
</vn-side-menu>
|
|
@ -2,10 +2,15 @@ import ngModule from '../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($scope, $state) {
|
constructor($http, $scope, $state, $compile, $transitions) {
|
||||||
this.$scope = $scope;
|
this.$http = $http;
|
||||||
|
this.$ = $scope;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$stateParams = $state.params;
|
this.$stateParams = $state.params;
|
||||||
|
this.$compile = $compile;
|
||||||
|
this.$transitions = $transitions;
|
||||||
|
this.itemTypes = [];
|
||||||
|
this.tags = [];
|
||||||
|
|
||||||
// Static autocomplete data
|
// Static autocomplete data
|
||||||
this.orderWays = [
|
this.orderWays = [
|
||||||
|
@ -23,16 +28,49 @@ class Controller {
|
||||||
this.orderField = this.orderFields[0].field;
|
this.orderField = this.orderFields[0].field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$onChanges() {
|
||||||
|
if (this.order && this.order.isConfirmed)
|
||||||
|
this.$state.go('order.card.line');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills order autocomplete with tags
|
* Fills order autocomplete with tags
|
||||||
* obtained from last filtered
|
* obtained from last filtered
|
||||||
*/
|
*/
|
||||||
onDataChange() {
|
get order() {
|
||||||
const items = this.$scope.model.data;
|
return this._order;
|
||||||
const newFilterList = [];
|
}
|
||||||
if (!items) return;
|
|
||||||
|
|
||||||
items.forEach(item => {
|
/**
|
||||||
|
* Sets filter values from state params
|
||||||
|
*
|
||||||
|
* @param {Object} value - Order data
|
||||||
|
*/
|
||||||
|
set order(value) {
|
||||||
|
this._order = value;
|
||||||
|
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
this.$.$applyAsync(() => {
|
||||||
|
if (this.$stateParams.categoryId)
|
||||||
|
this.categoryId = this.$stateParams.categoryId;
|
||||||
|
|
||||||
|
if (this.$stateParams.typeId)
|
||||||
|
this.typeId = this.$stateParams.typeId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
return this._items;
|
||||||
|
}
|
||||||
|
|
||||||
|
set items(value) {
|
||||||
|
this._items = value;
|
||||||
|
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
const newFilterList = [];
|
||||||
|
value.forEach(item => {
|
||||||
// Add new tag filters
|
// Add new tag filters
|
||||||
item.tags.forEach(itemTag => {
|
item.tags.forEach(itemTag => {
|
||||||
const alreadyAdded = newFilterList.findIndex(filter => {
|
const alreadyAdded = newFilterList.findIndex(filter => {
|
||||||
|
@ -64,6 +102,40 @@ class Controller {
|
||||||
this.orderFields = newFilterList;
|
this.orderFields = newFilterList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get categoryId() {
|
||||||
|
return this._categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
set categoryId(value) {
|
||||||
|
if (!value || (this.categoryId == value))
|
||||||
|
value = null;
|
||||||
|
|
||||||
|
this._categoryId = value;
|
||||||
|
this.itemTypes = [];
|
||||||
|
this.typeId = null;
|
||||||
|
|
||||||
|
this.updateStateParams();
|
||||||
|
|
||||||
|
if (this.tags.length > 0)
|
||||||
|
this.applyFilters();
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
this.updateItemTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
get typeId() {
|
||||||
|
return this._typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
set typeId(value) {
|
||||||
|
this._typeId = value;
|
||||||
|
|
||||||
|
this.updateStateParams();
|
||||||
|
|
||||||
|
if (value || this.tags.length > 0)
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get order way ASC/DESC
|
* Get order way ASC/DESC
|
||||||
*/
|
*/
|
||||||
|
@ -76,6 +148,9 @@ class Controller {
|
||||||
if (value) this.applyOrder();
|
if (value) this.applyOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the order way selection
|
||||||
|
*/
|
||||||
get orderSelection() {
|
get orderSelection() {
|
||||||
return this._orderSelection;
|
return this._orderSelection;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +161,14 @@ class Controller {
|
||||||
if (value) this.applyOrder();
|
if (value) this.applyOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply order to model
|
||||||
|
*/
|
||||||
|
applyOrder() {
|
||||||
|
if (this.typeId || this.tags.length > 0)
|
||||||
|
this.$.model.addFilter(null, {orderBy: this.getOrderBy()});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns order param
|
* Returns order param
|
||||||
*
|
*
|
||||||
|
@ -101,33 +184,101 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply order to model
|
* Refreshes item type dropdown data
|
||||||
*/
|
*/
|
||||||
applyOrder() {
|
updateItemTypes() {
|
||||||
this.$scope.model.addFilter(null, {orderBy: this.getOrderBy()});
|
let params = {
|
||||||
|
itemCategoryId: this.categoryId
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = `Orders/${this.order.id}/getItemTypeAvailable`;
|
||||||
|
this.$http.get(query, {params}).then(res =>
|
||||||
|
this.itemTypes = res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
preview(event, item) {
|
onSearchById(event) {
|
||||||
|
const hasValue = this.tags.length > 0 || this.itemId || this.typeId;
|
||||||
|
if (event.key === 'Enter' && hasValue)
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchByTag(event) {
|
||||||
|
if (event.key !== 'Enter' || !this.value) return;
|
||||||
|
this.tags.push({
|
||||||
|
value: this.value,
|
||||||
|
});
|
||||||
|
this.$.search.value = null;
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(index) {
|
||||||
|
this.tags.splice(index, 1);
|
||||||
|
|
||||||
|
if (this.tags.length >= 0 || this.itemId || this.typeId)
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilters() {
|
||||||
|
let newParams = {};
|
||||||
|
let newFilter = {};
|
||||||
|
const model = this.$.model;
|
||||||
|
|
||||||
|
if (this.categoryId)
|
||||||
|
newFilter.categoryFk = this.categoryId;
|
||||||
|
|
||||||
|
if (this.typeId)
|
||||||
|
newFilter.typeFk = this.typeId;
|
||||||
|
|
||||||
|
if (this.itemId)
|
||||||
|
newFilter = {'i.id': this.itemId};
|
||||||
|
|
||||||
|
newParams = {
|
||||||
|
orderFk: this.order.id,
|
||||||
|
orderBy: this.getOrderBy(),
|
||||||
|
tags: this.tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
model.applyFilter({where: newFilter}, newParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
openPanel(event) {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.$scope.pricesPopover.show(event, item);
|
|
||||||
|
this.panelFilter = {};
|
||||||
|
this.$.popover.show(this.$.search.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDescriptorLoad() {
|
onPanelSubmit(filter) {
|
||||||
this.$scope.popover.relocate();
|
this.$.popover.hide();
|
||||||
|
this.tags.push(filter);
|
||||||
|
this.applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onChanges() {
|
/**
|
||||||
if (this.order && this.order.isConfirmed)
|
* Updates url state params from filter values
|
||||||
this.$state.go('order.card.line');
|
*/
|
||||||
|
updateStateParams() {
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (this.categoryId)
|
||||||
|
params.categoryId = this.categoryId;
|
||||||
|
else params.categoryId = undefined;
|
||||||
|
|
||||||
|
if (this.typeId)
|
||||||
|
params.typeId = this.typeId;
|
||||||
|
else params.typeId = undefined;
|
||||||
|
|
||||||
|
this.$state.go(this.$state.current.name, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$state'];
|
Controller.$inject = ['$http', '$scope', '$state', '$compile', '$transitions'];
|
||||||
|
|
||||||
ngModule.component('vnOrderCatalog', {
|
ngModule.component('vnOrderCatalog', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
order: '<',
|
order: '<'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,29 +1,47 @@
|
||||||
import './index.js';
|
import './index.js';
|
||||||
import crudModel from 'core/mocks/crud-model';
|
import crudModel from 'core/mocks/crud-model';
|
||||||
|
|
||||||
describe('Order', () => {
|
fdescribe('Order', () => {
|
||||||
describe('Component vnOrderCatalog', () => {
|
describe('Component vnOrderCatalog', () => {
|
||||||
let $scope;
|
let $scope;
|
||||||
|
let $state;
|
||||||
let controller;
|
let controller;
|
||||||
|
let $httpBackend;
|
||||||
|
|
||||||
beforeEach(ngModule('order'));
|
beforeEach(ngModule('order'));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_, $rootScope) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$scope.model = crudModel;
|
$scope.model = crudModel;
|
||||||
$scope.field = {};
|
$scope.search = {};
|
||||||
controller = $componentController('vnOrderCatalog', {$scope: $scope});
|
$state = _$state_;
|
||||||
|
$state.params.categoryId = 1;
|
||||||
|
$state.params.typeId = 2;
|
||||||
|
$state.current.name = 'my.current.state';
|
||||||
|
controller = $componentController('vnOrderCatalog', {$scope, $state});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('onDataChange()', () => {
|
describe('order() setter', () => {
|
||||||
|
it(`should call scope $applyAsync() method and apply filters from state params`, () => {
|
||||||
|
$httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
|
||||||
|
controller.order = {id: 4};
|
||||||
|
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
|
expect(controller.categoryId).toEqual(1);
|
||||||
|
expect(controller.typeId).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('items() setter', () => {
|
||||||
it(`should return an object with order params`, () => {
|
it(`should return an object with order params`, () => {
|
||||||
$scope.model.data = [{id: 1, name: 'My Item', tags: [
|
let expectedResult = [{field: 'showOrder, price', name: 'Color'}];
|
||||||
|
let unexpectedResult = [{tagFk: 5, name: 'Color'}];
|
||||||
|
controller.items = [{id: 1, name: 'My Item', tags: [
|
||||||
{tagFk: 4, name: 'Length'},
|
{tagFk: 4, name: 'Length'},
|
||||||
{tagFk: 5, name: 'Color'}
|
{tagFk: 5, name: 'Color'}
|
||||||
]}];
|
]}];
|
||||||
let expectedResult = [{field: 'showOrder, price', name: 'Color'}];
|
|
||||||
let unexpectedResult = [{tagFk: 5, name: 'Color'}];
|
|
||||||
controller.onDataChange();
|
|
||||||
|
|
||||||
expect(controller.orderFields.length).toEqual(5);
|
expect(controller.orderFields.length).toEqual(5);
|
||||||
expect(controller.orderFields).toEqual(jasmine.arrayContaining(expectedResult));
|
expect(controller.orderFields).toEqual(jasmine.arrayContaining(expectedResult));
|
||||||
|
@ -31,6 +49,134 @@ describe('Order', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('categoryId() setter', () => {
|
||||||
|
it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
||||||
|
spyOn(controller, 'updateStateParams');
|
||||||
|
controller.categoryId = null;
|
||||||
|
|
||||||
|
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
||||||
|
spyOn(controller, 'updateStateParams');
|
||||||
|
controller._order = {id: 4};
|
||||||
|
controller.categoryId = 2;
|
||||||
|
|
||||||
|
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('typeId() setter', () => {
|
||||||
|
it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
||||||
|
spyOn(controller, 'updateStateParams');
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.typeId = null;
|
||||||
|
|
||||||
|
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||||
|
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
||||||
|
spyOn(controller, 'updateStateParams');
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.typeId = 2;
|
||||||
|
|
||||||
|
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||||
|
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSearchByTag()', () => {
|
||||||
|
it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.order = {id: 4};
|
||||||
|
controller.value = 'Color';
|
||||||
|
controller.onSearchByTag({key: 'Tab'});
|
||||||
|
|
||||||
|
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.order = {id: 4};
|
||||||
|
controller.value = 'Color';
|
||||||
|
|
||||||
|
controller.onSearchByTag({key: 'Enter'});
|
||||||
|
|
||||||
|
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSearchById()', () => {
|
||||||
|
it(`should not filter by id if the event key code doesn't equals to 'Enter'`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.itemId = 1;
|
||||||
|
controller.onSearchById({key: 'Tab'});
|
||||||
|
|
||||||
|
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should filter by id if the event key code equals to 'Enter' an then call applyFilters()`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.itemId = 1;
|
||||||
|
|
||||||
|
controller.onSearchById({key: 'Enter'});
|
||||||
|
|
||||||
|
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('applyFilters()', () => {
|
||||||
|
it(`should call model applyFilter() method with a new filter`, () => {
|
||||||
|
let model = controller.$.model;
|
||||||
|
spyOn(model, 'applyFilter');
|
||||||
|
controller._categoryId = 2;
|
||||||
|
controller._typeId = 4;
|
||||||
|
controller._order = {id: 4};
|
||||||
|
|
||||||
|
controller.applyFilters();
|
||||||
|
|
||||||
|
expect(model.applyFilter).toHaveBeenCalledWith(
|
||||||
|
{where: {categoryFk: 2, typeFk: 4}},
|
||||||
|
{orderFk: 4, orderBy: controller.getOrderBy(), tags: []});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remove()', () => {
|
||||||
|
it(`should remove a tag from tags property`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller.tags = [{tagFk: 1, value: 'Blue'}, {tagFk: 2, value: '70'}];
|
||||||
|
controller.remove(0);
|
||||||
|
|
||||||
|
expect(controller.tags.length).toEqual(1);
|
||||||
|
expect(controller.tags[0].tagFk).toEqual(2);
|
||||||
|
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => {
|
||||||
|
spyOn(controller, 'applyFilters');
|
||||||
|
controller._categoryId = 1;
|
||||||
|
controller._typeId = 1;
|
||||||
|
controller.tags = [{tagFk: 1, value: 'Blue'}];
|
||||||
|
controller.remove(0);
|
||||||
|
|
||||||
|
expect(controller.tags.length).toEqual(0);
|
||||||
|
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateStateParams()', () => {
|
||||||
|
it(`should call state go() method passing category and type state params`, () => {
|
||||||
|
spyOn(controller.$state, 'go');
|
||||||
|
controller._categoryId = 2;
|
||||||
|
controller._typeId = 4;
|
||||||
|
let result = {categoryId: 2, typeId: 4};
|
||||||
|
controller.updateStateParams();
|
||||||
|
|
||||||
|
expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getOrderBy()', () => {
|
describe('getOrderBy()', () => {
|
||||||
it(`should return an object with order params`, () => {
|
it(`should return an object with order params`, () => {
|
||||||
controller.orderField = 'relevancy DESC, name';
|
controller.orderField = 'relevancy DESC, name';
|
||||||
|
@ -50,13 +196,15 @@ describe('Order', () => {
|
||||||
it(`should apply order param to model calling getOrderBy()`, () => {
|
it(`should apply order param to model calling getOrderBy()`, () => {
|
||||||
controller.field = 'relevancy DESC, name';
|
controller.field = 'relevancy DESC, name';
|
||||||
controller.way = 'ASC';
|
controller.way = 'ASC';
|
||||||
|
controller._categoryId = 1;
|
||||||
|
controller._typeId = 1;
|
||||||
let expectedOrder = {orderBy: controller.getOrderBy()};
|
let expectedOrder = {orderBy: controller.getOrderBy()};
|
||||||
spyOn(controller, 'getOrderBy').and.callThrough();
|
spyOn(controller, 'getOrderBy').and.callThrough();
|
||||||
spyOn(controller.$scope.model, 'addFilter');
|
spyOn(controller.$.model, 'addFilter');
|
||||||
controller.applyOrder();
|
controller.applyOrder();
|
||||||
|
|
||||||
expect(controller.getOrderBy).toHaveBeenCalledWith();
|
expect(controller.getOrderBy).toHaveBeenCalledWith();
|
||||||
expect(controller.$scope.model.addFilter).toHaveBeenCalledWith(null, expectedOrder);
|
expect(controller.$.model.addFilter).toHaveBeenCalledWith(null, expectedOrder);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,31 +1,54 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-order-catalog {
|
vn-order-catalog vn-side-menu div {
|
||||||
.catalog-header {
|
& > .input {
|
||||||
|
padding-left: $spacing-md;
|
||||||
|
padding-right: $spacing-md;
|
||||||
|
border-color: $color-spacer;
|
||||||
border-bottom: $border-thin;
|
border-bottom: $border-thin;
|
||||||
padding: $spacing-md;
|
}
|
||||||
align-items: center;
|
.item-category {
|
||||||
|
padding: $spacing-sm;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
vn-autocomplete[vn-id="category"] {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
& > vn-one {
|
& > vn-one {
|
||||||
display: flex;
|
padding: $spacing-sm;
|
||||||
flex: 1;
|
min-width: 33.33%;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
span {
|
& > vn-icon {
|
||||||
color: $color-font-secondary
|
padding: $spacing-sm;
|
||||||
}
|
background-color: $color-font-secondary;
|
||||||
}
|
border-radius: 50%;
|
||||||
& > vn-auto {
|
cursor: pointer;
|
||||||
width: 28em;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
& > * {
|
&.active {
|
||||||
padding-left: $spacing-md;
|
background-color: $color-main;
|
||||||
|
color: #FFF
|
||||||
|
}
|
||||||
|
& > i:before {
|
||||||
|
font-size: 32pt;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.catalog-list {
|
.chips {
|
||||||
padding-top: $spacing-sm;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: $spacing-md;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
vn-autocomplete[vn-id="type"] .list {
|
||||||
|
max-height: 20em
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="ItemCategories"
|
|
||||||
data="categories"
|
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
|
||||||
<div>
|
|
||||||
<vn-horizontal class="item-category">
|
|
||||||
<vn-autocomplete vn-id="category"
|
|
||||||
data="categories"
|
|
||||||
ng-model="$ctrl.categoryId"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
label="Category">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-one ng-repeat="category in categories">
|
|
||||||
<vn-icon
|
|
||||||
ng-class="{'active': $ctrl.categoryId == category.id}"
|
|
||||||
icon="{{::category.icon}}"
|
|
||||||
vn-tooltip="{{::category.name}}"
|
|
||||||
ng-click="$ctrl.categoryId = category.id">
|
|
||||||
</vn-icon>
|
|
||||||
</vn-one>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-vertical class="input">
|
|
||||||
<vn-autocomplete vn-id="type"
|
|
||||||
data="$ctrl.itemTypes"
|
|
||||||
ng-model="$ctrl.typeId"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
label="Type"
|
|
||||||
fields="['categoryFk']"
|
|
||||||
include="'category'">
|
|
||||||
<tpl-item>
|
|
||||||
<div>{{name}}</div>
|
|
||||||
<div class="text-caption text-secondary">
|
|
||||||
{{categoryName}}
|
|
||||||
</div>
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-vertical>
|
|
||||||
<vn-vertical class="input vn-pt-md">
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-id="field"
|
|
||||||
data="$ctrl.catalog.orderFields"
|
|
||||||
ng-model="$ctrl.catalog.orderField"
|
|
||||||
selection="$ctrl.catalog.orderSelection"
|
|
||||||
translate-fields="['name']"
|
|
||||||
order="name"
|
|
||||||
show-field="name"
|
|
||||||
value-field="field"
|
|
||||||
label="Order by"
|
|
||||||
disabled="!model.data">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
|
||||||
data="$ctrl.catalog.orderWays"
|
|
||||||
ng-model="$ctrl.catalog.orderWay"
|
|
||||||
translate-fields="['name']"
|
|
||||||
show-field="name"
|
|
||||||
value-field="way"
|
|
||||||
label="Order"
|
|
||||||
disabled="!model.data">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<div ng-if="false && model.moreRows">
|
|
||||||
<span translate>More than</span> {{model.limit}} <span translate>results</span>
|
|
||||||
</div>
|
|
||||||
</vn-vertical>
|
|
||||||
<vn-vertical class="input vn-pt-md">
|
|
||||||
<vn-textfield
|
|
||||||
ng-keyUp="$ctrl.onSearchById($event)"
|
|
||||||
label="Item id"
|
|
||||||
ng-model="$ctrl.itemId">
|
|
||||||
<prepend>
|
|
||||||
<vn-icon icon="icon-item"></vn-icon>
|
|
||||||
</prepend>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
vn-id="search"
|
|
||||||
ng-keyUp="$ctrl.onSearchByTag($event)"
|
|
||||||
label="Search tag"
|
|
||||||
ng-model="$ctrl.value">
|
|
||||||
<prepend>
|
|
||||||
<vn-icon icon="search"></vn-icon>
|
|
||||||
</prepend>
|
|
||||||
<append>
|
|
||||||
<vn-icon
|
|
||||||
icon="keyboard_arrow_down"
|
|
||||||
ng-click="$ctrl.openPanel($event)"
|
|
||||||
style="cursor: pointer;">
|
|
||||||
</vn-icon>
|
|
||||||
</append>
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-vertical>
|
|
||||||
<vn-popover
|
|
||||||
vn-id="popover"
|
|
||||||
on-close="$ctrl.onPopoverClose()">
|
|
||||||
<vn-order-catalog-search-panel
|
|
||||||
filter="panelFilter"
|
|
||||||
on-submit="$ctrl.onPanelSubmit($filter)">
|
|
||||||
</vn-order-catalog-search-panel>
|
|
||||||
</vn-popover>
|
|
||||||
<div class="chips">
|
|
||||||
<vn-chip
|
|
||||||
ng-if="category.selection"
|
|
||||||
removable="true"
|
|
||||||
translate-attr="{title: 'Category'}"
|
|
||||||
on-remove="$ctrl.categoryId = null"
|
|
||||||
class="colored">
|
|
||||||
<span translate>{{category.selection.name}}</span>
|
|
||||||
</vn-chip>
|
|
||||||
<vn-chip
|
|
||||||
ng-if="type.selection"
|
|
||||||
removable="true"
|
|
||||||
translate-attr="{title: 'Type'}"
|
|
||||||
on-remove="$ctrl.typeId = null"
|
|
||||||
class="colored">
|
|
||||||
<span translate>{{type.selection.name}}</span>
|
|
||||||
</vn-chip>
|
|
||||||
<vn-chip
|
|
||||||
ng-repeat="tag in $ctrl.tags"
|
|
||||||
removable="true"
|
|
||||||
translate-attr="{title: 'Tag'}"
|
|
||||||
on-remove="$ctrl.remove($index)"
|
|
||||||
class="colored">
|
|
||||||
<span translate>{{::tag.value}}</span>
|
|
||||||
</vn-chip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,175 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class Controller {
|
|
||||||
constructor($element, $http, $scope, $state, $compile, $transitions) {
|
|
||||||
this.$element = $element;
|
|
||||||
this.$http = $http;
|
|
||||||
this.$ = $scope;
|
|
||||||
this.$state = $state;
|
|
||||||
this.$stateParams = $state.params;
|
|
||||||
this.$compile = $compile;
|
|
||||||
this.$transitions = $transitions;
|
|
||||||
this.itemTypes = [];
|
|
||||||
this.tags = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get order() {
|
|
||||||
return this._order;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets filter values from state params
|
|
||||||
*
|
|
||||||
* @param {Object} value - Order data
|
|
||||||
*/
|
|
||||||
set order(value) {
|
|
||||||
this._order = value;
|
|
||||||
|
|
||||||
if (!value) return;
|
|
||||||
|
|
||||||
this.$.$applyAsync(() => {
|
|
||||||
if (this.$stateParams.categoryId)
|
|
||||||
this.categoryId = this.$stateParams.categoryId;
|
|
||||||
|
|
||||||
if (this.$stateParams.typeId)
|
|
||||||
this.typeId = this.$stateParams.typeId;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get categoryId() {
|
|
||||||
return this._categoryId;
|
|
||||||
}
|
|
||||||
|
|
||||||
set categoryId(value) {
|
|
||||||
if (!value || (this.categoryId == value))
|
|
||||||
value = null;
|
|
||||||
|
|
||||||
this._categoryId = value;
|
|
||||||
this.itemTypes = [];
|
|
||||||
this.typeId = null;
|
|
||||||
|
|
||||||
this.updateStateParams();
|
|
||||||
|
|
||||||
if (this.tags.length > 0)
|
|
||||||
this.applyFilters();
|
|
||||||
|
|
||||||
if (value)
|
|
||||||
this.updateItemTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
get typeId() {
|
|
||||||
return this._typeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
set typeId(value) {
|
|
||||||
this._typeId = value;
|
|
||||||
|
|
||||||
this.updateStateParams();
|
|
||||||
|
|
||||||
if ((value) || this.tags.length > 0)
|
|
||||||
this.applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refreshes item type dropdown data
|
|
||||||
*/
|
|
||||||
updateItemTypes() {
|
|
||||||
let params = {
|
|
||||||
itemCategoryId: this.categoryId
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = `Orders/${this.order.id}/getItemTypeAvailable`;
|
|
||||||
this.$http.get(query, {params}).then(res =>
|
|
||||||
this.itemTypes = res.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearchById(event) {
|
|
||||||
const hasValue = this.tags.length > 0 || this.itemId || this.typeId;
|
|
||||||
if (event.key === 'Enter' && hasValue)
|
|
||||||
this.applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearchByTag(event) {
|
|
||||||
if (event.key !== 'Enter' || !this.value) return;
|
|
||||||
this.tags.push({
|
|
||||||
value: this.value,
|
|
||||||
});
|
|
||||||
this.$.search.value = null;
|
|
||||||
this.applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(index) {
|
|
||||||
this.tags.splice(index, 1);
|
|
||||||
|
|
||||||
if (this.tags.length >= 0 || this.itemId || this.typeId)
|
|
||||||
this.applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
applyFilters() {
|
|
||||||
let newParams = {};
|
|
||||||
let newFilter = {};
|
|
||||||
const model = this.catalog.$scope.model;
|
|
||||||
|
|
||||||
if (this.categoryId)
|
|
||||||
newFilter.categoryFk = this.categoryId;
|
|
||||||
|
|
||||||
if (this.typeId)
|
|
||||||
newFilter.typeFk = this.typeId;
|
|
||||||
|
|
||||||
if (this.itemId)
|
|
||||||
newFilter = {'i.id': this.itemId};
|
|
||||||
|
|
||||||
newParams = {
|
|
||||||
orderFk: this.order.id,
|
|
||||||
orderBy: this.catalog.getOrderBy(),
|
|
||||||
tags: this.tags,
|
|
||||||
};
|
|
||||||
|
|
||||||
model.applyFilter({where: newFilter}, newParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
openPanel(event) {
|
|
||||||
if (event.defaultPrevented) return;
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.panelFilter = {};
|
|
||||||
this.$.popover.show(this.$.search.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPanelSubmit(filter) {
|
|
||||||
this.$.popover.hide();
|
|
||||||
this.tags.push(filter);
|
|
||||||
this.applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates url state params from filter values
|
|
||||||
*/
|
|
||||||
updateStateParams() {
|
|
||||||
const params = {};
|
|
||||||
|
|
||||||
if (this.categoryId)
|
|
||||||
params.categoryId = this.categoryId;
|
|
||||||
else params.categoryId = undefined;
|
|
||||||
|
|
||||||
if (this.typeId)
|
|
||||||
params.typeId = this.typeId;
|
|
||||||
else params.typeId = undefined;
|
|
||||||
|
|
||||||
this.$state.go(this.$state.current.name, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$http', '$scope', '$state', '$compile', '$transitions'];
|
|
||||||
|
|
||||||
ngModule.component('vnCatalogFilter', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
require: {
|
|
||||||
catalog: '^vnOrderCatalog'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
order: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,172 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
import crudModel from 'core/mocks/crud-model';
|
|
||||||
|
|
||||||
describe('Order', () => {
|
|
||||||
describe('Component vnCatalogFilter', () => {
|
|
||||||
let $scope;
|
|
||||||
let $state;
|
|
||||||
let controller;
|
|
||||||
let $httpBackend;
|
|
||||||
|
|
||||||
beforeEach(ngModule('order'));
|
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_, $rootScope) => {
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$scope.model = crudModel;
|
|
||||||
$scope.search = {};
|
|
||||||
$state = _$state_;
|
|
||||||
$state.params.categoryId = 1;
|
|
||||||
$state.params.typeId = 2;
|
|
||||||
$state.current.name = 'my.current.state';
|
|
||||||
controller = $componentController('vnCatalogFilter', {$element: null, $scope, $state});
|
|
||||||
controller.catalog = {
|
|
||||||
$scope: $scope,
|
|
||||||
getOrderBy: () => {
|
|
||||||
return {field: 'relevancy DESC, name', way: 'DESC'};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('order() setter', () => {
|
|
||||||
it(`should call scope $applyAsync() method and apply filters from state params`, () => {
|
|
||||||
$httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
|
|
||||||
controller.order = {id: 4};
|
|
||||||
|
|
||||||
$scope.$apply();
|
|
||||||
|
|
||||||
expect(controller.categoryId).toEqual(1);
|
|
||||||
expect(controller.typeId).toEqual(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('categoryId() setter', () => {
|
|
||||||
it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
|
||||||
spyOn(controller, 'updateStateParams');
|
|
||||||
controller.categoryId = null;
|
|
||||||
|
|
||||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
|
||||||
spyOn(controller, 'updateStateParams');
|
|
||||||
controller._order = {id: 4};
|
|
||||||
controller.categoryId = 2;
|
|
||||||
|
|
||||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('typeId() setter', () => {
|
|
||||||
it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
|
||||||
spyOn(controller, 'updateStateParams');
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.typeId = null;
|
|
||||||
|
|
||||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
|
||||||
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
|
||||||
spyOn(controller, 'updateStateParams');
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.typeId = 2;
|
|
||||||
|
|
||||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
|
||||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onSearchByTag()', () => {
|
|
||||||
it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.order = {id: 4};
|
|
||||||
controller.value = 'Color';
|
|
||||||
controller.onSearchByTag({key: 'Tab'});
|
|
||||||
|
|
||||||
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.order = {id: 4};
|
|
||||||
controller.value = 'Color';
|
|
||||||
|
|
||||||
controller.onSearchByTag({key: 'Enter'});
|
|
||||||
|
|
||||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onSearchById()', () => {
|
|
||||||
it(`should not filter by id if the event key code doesn't equals to 'Enter'`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.itemId = 1;
|
|
||||||
controller.onSearchById({key: 'Tab'});
|
|
||||||
|
|
||||||
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should filter by id if the event key code equals to 'Enter' an then call applyFilters()`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.itemId = 1;
|
|
||||||
|
|
||||||
controller.onSearchById({key: 'Enter'});
|
|
||||||
|
|
||||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('applyFilters()', () => {
|
|
||||||
it(`should call model applyFilter() method with a new filter`, () => {
|
|
||||||
let model = controller.catalog.$scope.model;
|
|
||||||
spyOn(model, 'applyFilter');
|
|
||||||
controller._categoryId = 2;
|
|
||||||
controller._typeId = 4;
|
|
||||||
controller._order = {id: 4};
|
|
||||||
|
|
||||||
controller.applyFilters();
|
|
||||||
|
|
||||||
expect(model.applyFilter).toHaveBeenCalledWith(
|
|
||||||
{where: {categoryFk: 2, typeFk: 4}},
|
|
||||||
{orderFk: 4, orderBy: controller.catalog.getOrderBy(), tags: []});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('remove()', () => {
|
|
||||||
it(`should remove a tag from tags property`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller.tags = [{tagFk: 1, value: 'Blue'}, {tagFk: 2, value: '70'}];
|
|
||||||
controller.remove(0);
|
|
||||||
|
|
||||||
expect(controller.tags.length).toEqual(1);
|
|
||||||
expect(controller.tags[0].tagFk).toEqual(2);
|
|
||||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => {
|
|
||||||
spyOn(controller, 'applyFilters');
|
|
||||||
controller._categoryId = 1;
|
|
||||||
controller._typeId = 1;
|
|
||||||
controller.tags = [{tagFk: 1, value: 'Blue'}];
|
|
||||||
controller.remove(0);
|
|
||||||
|
|
||||||
expect(controller.tags.length).toEqual(0);
|
|
||||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('updateStateParams()', () => {
|
|
||||||
it(`should call state go() method passing category and type state params`, () => {
|
|
||||||
spyOn(controller.$state, 'go');
|
|
||||||
controller._categoryId = 2;
|
|
||||||
controller._typeId = 4;
|
|
||||||
let result = {categoryId: 2, typeId: 4};
|
|
||||||
controller.updateStateParams();
|
|
||||||
|
|
||||||
expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
vn-catalog-filter > div {
|
|
||||||
& > .input {
|
|
||||||
padding-left: $spacing-md;
|
|
||||||
padding-right: $spacing-md;
|
|
||||||
border-color: $color-spacer;
|
|
||||||
border-bottom: $border-thin;
|
|
||||||
}
|
|
||||||
.item-category {
|
|
||||||
padding: $spacing-sm;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
vn-autocomplete[vn-id="category"] {
|
|
||||||
display: none
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-one {
|
|
||||||
padding: $spacing-sm;
|
|
||||||
min-width: 33.33%;
|
|
||||||
text-align: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
& > vn-icon {
|
|
||||||
padding: $spacing-sm;
|
|
||||||
background-color: $color-font-secondary;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: $color-main;
|
|
||||||
color: #FFF
|
|
||||||
}
|
|
||||||
& > i:before {
|
|
||||||
font-size: 32pt;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.chips {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: $spacing-md;
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
vn-autocomplete[vn-id="type"] .list {
|
|
||||||
max-height: 20em
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,9 +6,9 @@ import './card';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './search-panel';
|
import './search-panel';
|
||||||
import './catalog-search-panel';
|
import './catalog-search-panel';
|
||||||
import './filter';
|
import './catalog-view';
|
||||||
import './summary';
|
|
||||||
import './catalog';
|
import './catalog';
|
||||||
|
import './summary';
|
||||||
import './line';
|
import './line';
|
||||||
import './prices-popover';
|
import './prices-popover';
|
||||||
import './volume';
|
import './volume';
|
||||||
|
|
Loading…
Reference in New Issue