Automatic pagination with vnPagination, improved vnSearchbar

This commit is contained in:
Juan 2018-06-07 23:47:19 +02:00
parent a015b790b5
commit 9fa12d4baf
59 changed files with 973 additions and 751 deletions

View File

@ -75,20 +75,20 @@
</vn-autocomplete>
<vn-textfield
vn-two
margin-large-right
label="Description"
model="observation.description"
rule="addressObservation.description">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
<vn-none>
<vn-icon-button
medium-grey
margin-medium-v
vn-tooltip="Remove note"
icon="remove_circle_outline"
ng-click="$ctrl.removeObservation($index)">
</vn-icon>
</vn-one>
ng-click="$ctrl.removeObservation($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
</div>
<vn-icon-button

View File

@ -1,25 +1,30 @@
<mg-ajax path="/client/api/Clients/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-crud-model
vn-id="model"
url="/item/api/Clients"
filter="::$ctrl.filter"
limit="4"
data="clients"
auto-load="false">
</vn-crud-model>
<div margin-medium>
<div class="vn-list">
<vn-card>
<vn-horizontal>
<vn-searchbar vn-one
index="index"
on-search="$ctrl.search(index)"
advanced="true"
popover="vn-client-search-panel"
ignore-keys = "['page', 'size', 'search']">
</vn-searchbar>
</vn-horizontal>
<vn-card pad-medium-h>
<vn-searchbar
panel="vn-client-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-card>
<vn-card margin-medium-top>
<vn-card margin-medium-v>
<vn-item-client
ng-repeat="client in index.model.instances track by client.id"
client="client">
ng-repeat="client in clients track by client.id"
client="::client">
</vn-item-client>
</vn-card>
<vn-paging index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging index="index" total="index.model.count" items="$ctrl.clients"></vn-auto-paging> -->
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</div>
</div>
<a ui-sref="client.create" vn-bind="+" fixed-bottom-right>
@ -30,4 +35,4 @@
<tpl-body>
<vn-client-summary client="$ctrl.clientSelected"></vn-client-summary>
</tpl-body>
</vn-dialog>
</vn-dialog>

View File

@ -2,23 +2,42 @@ import ngModule from '../module';
import './item-client';
export default class Controller {
constructor($scope) {
this.$scope = $scope;
this.$ = $scope;
this.clientSelected = null;
}
search(index) {
index.accept();
/* this.clients = [];
index.accept().then(res => {
this.clients = res.instances;
}); */
exprBuilder(param, value) {
switch (param) {
case 'search':
return {
or: [
{id: value},
{name: {regexp: value}}
]
};
case 'phone':
return {
or: [
{phone: value},
{mobile: value}
]
};
case 'name':
case 'socialName':
case 'city':
return {[param]: {regexp: value}};
case 'id':
case 'fi':
case 'postcode':
case 'email':
return {[param]: value};
}
}
openSummary(client) {
this.clientSelected = client;
this.$scope.dialogSummaryClient.show();
this.$.dialogSummaryClient.show();
}
}
Controller.$inject = ['$scope'];

View File

@ -2,4 +2,5 @@ Client id: Id cliente
Phone: Teléfono
Town/City: Ciudad
Email: Correo electrónico
Create client: Crear cliente
Create client: Crear cliente
View client: Ver cliente

View File

@ -1,22 +1,22 @@
<div pad-large style="min-width: 30em">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield vn-one label="Client id" model="$ctrl.filter.id" vn-focus></vn-textfield>
<vn-textfield vn-one label="Tax number" model="$ctrl.filter.fi"></vn-textfield>
<vn-textfield vn-one label="Client id" model="filter.id" vn-focus></vn-textfield>
<vn-textfield vn-one label="Tax number" model="filter.fi"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Name" model="$ctrl.filter.name"></vn-textfield>
<vn-textfield vn-one label="Name" model="filter.name"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Social name" model="$ctrl.filter.socialName"></vn-textfield>
<vn-textfield vn-one label="Social name" model="filter.socialName"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Town/City" model="$ctrl.filter.city"></vn-textfield>
<vn-textfield vn-one label="Postcode" model="$ctrl.filter.postcode"></vn-textfield>
<vn-textfield vn-one label="Town/City" model="filter.city"></vn-textfield>
<vn-textfield vn-one label="Postcode" model="filter.postcode"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Email" model="$ctrl.filter.email"></vn-textfield>
<vn-textfield vn-one label="Phone" model="$ctrl.filter.phone"></vn-textfield>
<vn-textfield vn-one label="Email" model="filter.email"></vn-textfield>
<vn-textfield vn-one label="Phone" model="filter.phone"></vn-textfield>
</vn-horizontal>
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>

View File

@ -1,18 +1,7 @@
import ngModule from '../module';
export default class Controller {
constructor() {
// onSubmit() is defined by @vnSearchbar
this.onSubmit = () => {};
}
onSearch() {
this.onSubmit(this.filter);
}
}
Controller.$inject = [];
import SearchPanel from 'core/src/components/searchbar/search-panel';
ngModule.component('vnClientSearchPanel', {
template: require('./index.html'),
controller: Controller
controller: SearchPanel
});

View File

@ -3,7 +3,8 @@ import './style.scss';
export default class IconButton {
constructor($element) {
$element[0].tabIndex = 0;
if ($element[0].getAttribute('tabindex') == null)
$element[0].tabIndex = 0;
$element.on("keyup", event => this.onKeyDown(event, $element));
}

View File

@ -2,32 +2,16 @@
vn-icon-button {
display: inline-block;
text-align: center;
color: rgba($main-01, 0.7);
font-size: 18pt;
transition: color 200ms ease-in-out;
cursor: pointer;
&.button {
background-color: $main-01;
color: white;
width: 64px;
height: 36px;
box-shadow: rgba(0, 0, 0, 0.14) 0px 2px 2px 0px, rgba(0, 0, 0, 0.2) 0px 3px 1px -2px, rgba(0, 0, 0, 0.12) 0px 1px 5px 0px;
border-radius: 2px;
}
&.button:focus {
will-change: box-shadow;
box-shadow: 0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);
transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
}
&.button i {
margin-top: 6px;
}
& > i,
& > i.material-icons {
& > vn-icon {
display: block;
font-size: inherit;
color: inherit;
margin: 0 auto;
}
&:not(.button):hover {
color: $main-01;

View File

@ -3,7 +3,6 @@ import './rest-model/crud-model';
import './rest-model/rest-model';
import './watcher/watcher';
import './textfield/textfield';
import './paging/paging';
import './icon/icon';
import './dialog/dialog';
import './confirm/confirm';
@ -34,4 +33,7 @@ import './switch/switch';
import './float-button/float-button';
import './step-control/step-control';
import './label-value/label-value';
import './paging/paging';
import './auto-paging/auto-paging';
import './pagination/pagination';
import './searchbar/searchbar';

View File

@ -2,7 +2,7 @@ import ngModule from '../../module';
import './style.scss';
ngModule.component('vnLabelValue', {
template: require('../label-value/label-value.html'),
template: require('./label-value.html'),
replace: true,
transclude: true,
bindings: {

View File

@ -12,13 +12,17 @@ export default class ModelProxy {
set orgData(value) {
this._orgData = value;
this._data = [];
// this._data.splice(0, this._data.length);
for (let i = 0; i < value.length; i++) {
let row = new this.Row(value[i], i);
this._data.push(row);
}
if (this.Row) {
this._data = [];
for (let i = 0; i < value.length; i++) {
let row = new this.Row(value[i], i);
this._data.push(row);
}
} else
this._data = value;
this.resetChanges();
}
@ -112,6 +116,11 @@ export default class ModelProxy {
this.resetChanges();
}
dataChanged() {
if (this.onDataChange)
this.onDataChange();
}
}
ngModule.component('vnModelProxy', {
@ -120,6 +129,7 @@ ngModule.component('vnModelProxy', {
orgData: '<?',
data: '=?',
fields: '<?',
link: '<?'
link: '<?',
onDataChange: '&?'
}
});

View File

@ -0,0 +1,13 @@
<div
ng-if="$ctrl.model.moreRows">
<vn-icon-button
ng-if="!$ctrl.model.isLoading"
icon="more_horiz"
vn-tooltip="Load more"
ng-click="$ctrl.model.loadMore()">
</vn-icon-button>
<vn-spinner
ng-if="$ctrl.model.isLoading"
enable="$ctrl.model.isLoading">
</vn-spinner>
</div>

View File

@ -0,0 +1,75 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
/**
* Pagination component that automatically loads more rows when
* the user scrolls down an element.
*
* @property {CrudModel} model The model used for pagination
* @property {String} scrollSelector The the scrollable element selector
* @property {HTMLElement} scrollElement The scrollable element
* @property {Number} scrollOffset The distance, in pixels, until the end that activates the loading of the next rows
*/
class Pagination extends Component {
constructor($element, $scope) {
super($element, $scope);
this.scrollOffset = 20;
this.scrollHandler = e => this.onScroll(e);
}
$onInit() {
if (!this._scrollElement)
this.scrollElement = document.body;
}
set scrollSelector(value) {
this._scrollSelector = value;
this.scrollElement = document.querySelector(value);
}
get scrollSelector() {
return this._scrollSelector;
}
set scrollElement(value) {
if (this._scrollElement)
this._scrollElement.removeEventListener('scroll', this.scrollHandler);
this._scrollElement = value;
if (value)
this._scrollElement.addEventListener('scroll', this.scrollHandler);
}
get scrollElement() {
return this._scrollElement;
}
onScroll() {
let scrollElement = this.scrollElement;
let shouldLoad =
scrollElement.scrollTop + scrollElement.clientHeight >= (scrollElement.scrollHeight - this.scrollOffset)
&& !this.model.isLoading;
if (shouldLoad) {
this.model.loadMore();
this.$.$apply();
}
}
$onDestroy() {
this.scrollElement = null;
}
}
ngModule.component('vnPagination', {
template: require('./pagination.html'),
bindings: {
model: '<',
scrollSelector: '@?',
scrollElement: '<?',
scrollOffset: '<?'
},
controller: Pagination
});

View File

@ -0,0 +1,9 @@
vn-pagination {
display: block;
text-align: center;
& > div > vn-icon-button {
font-size: 2em;
}
}

View File

@ -10,32 +10,80 @@ export default class CrudModel extends ModelProxy {
this.autoLoad = true;
}
get isLoading() {
return this.canceler != null;
}
$onInit() {
if (this.autoLoad)
this.refresh();
}
refresh() {
refresh(usFilter, usData) {
if (!this.url) return;
let myFilter = {
fields: this.fields,
where: mergeWhere(this.link, this.where),
include: this.include,
order: this.order,
limit: this.limit,
userData: this.userData
};
let filter = this.filter;
filter = mergeFilters(myFilter, filter);
filter = mergeFilters(usFilter, filter);
return this.sendRequest(filter);
}
if (!filter) {
let where = Object.assign({}, this.link, this.where);
filter = {
fields: this.fields,
where: where,
include: this.include,
order: this.order,
limit: this.limit
};
cancelRequest() {
if (this.canceler) {
this.canceler.resolve();
this.canceler = null;
}
}
let urlFilter = encodeURIComponent(JSON.stringify(filter));
sendRequest(filter, append) {
this.cancelRequest();
this.canceler = this.$q.defer();
let options = {timeout: this.canceler.promise};
let json = encodeURIComponent(JSON.stringify(filter));
return this.$http.get(`${this.url}?filter=${json}`, options).then(
json => this.onRemoteDone(json, filter, append),
json => this.onRemoteError(json)
);
}
return this.$http.get(`${this.url}?filter=${urlFilter}`).then(res => {
this.orgData = res.data;
});
onRemoteDone(json, filter, append) {
let data = json.data;
if (append)
this.orgData = this.orgData.concat(data);
else
this.orgData = data;
this.currentFilter = filter;
this.moreRows = filter.limit && data.length == filter.limit;
this.onRequestEnd();
this.dataChanged();
}
onRemoteError(err) {
this.onRequestEnd();
throw err;
}
onRequestEnd() {
this.canceler = null;
}
loadMore() {
if (this.moreRows) {
let filter = Object.assign({}, this.currentFilter);
filter.skip = (filter.skip || 0) + filter.limit;
this.sendRequest(filter, true);
}
}
getChanges() {
@ -97,7 +145,87 @@ ngModule.component('vnCrudModel', {
order: '@?',
limit: '<?',
filter: '<?',
userData: '<?',
primaryKey: '@?',
autoLoad: '<?'
}
});
/**
* Passes a loopback fields filter to an object.
*
* @param {Object} fields The fields object or array
* @return {Object} The fields as object
*/
function fieldsToObject(fields) {
let fieldsObj = {};
if (Array.isArray(fields))
for (let field of fields)
fieldsObj[field] = true;
else if (typeof fields == 'object')
for (let field in fields)
if (fields[field])
fieldsObj[field] = true;
return fieldsObj;
}
/**
* Merges two loopback fields filters.
*
* @param {Object|Array} src The source fields
* @param {Object|Array} dst The destination fields
* @return {Array} The merged fields as an array
*/
function mergeFields(src, dst) {
let fields = {};
Object.assign(fields,
fieldsToObject(src),
fieldsToObject(dst)
);
return Object.keys(fields);
}
/**
* Merges two loopback where filters.
*
* @param {Object|Array} src The source where
* @param {Object|Array} dst The destination where
* @return {Array} The merged wheres
*/
function mergeWhere(src, dst) {
let and = [];
if (src) and.push(src);
if (dst) and.push(dst);
return and.length > 1 ? {and} : and[0];
}
/**
* Merges two loopback filters returning the merged filter.
*
* @param {Object} src The source filter
* @param {Object} dst The destination filter
* @return {Object} The result filter
*/
function mergeFilters(src, dst) {
let res = Object.assign({}, dst);
if (!src)
return res;
if (src.fields)
res.fields = mergeFields(src.fields, res.fields);
if (src.where)
res.where = mergeWhere(res.where, src.where);
if (src.include)
res.include = src.include;
if (src.order)
res.order = src.order;
if (src.limit)
res.limit = src.limit;
if (src.userData)
res.userData = src.userData;
return res;
}

View File

@ -0,0 +1,18 @@
import Component from '../../lib/component';
export default class extends Component {
set filter(value) {
this.$.filter = value;
}
get filter() {
return this.$.filter;
}
onSearch() {
if (!this.onSubmit)
throw new Error('SearchPanel::onSubmit() method not defined');
this.onSubmit(this.filter);
}
}

View File

@ -5,11 +5,11 @@
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
style="cursor: pointer; padding-top: 23px">
</vn-icon-button>
<vn-textfield vn-one label="Search" model="$ctrl.stringSearch"></vn-textfield>
<vn-textfield vn-one label="Search" model="$ctrl.searchString"></vn-textfield>
<vn-icon
pad-medium-top
ng-if="$ctrl.advanced"
ng-click="$ctrl.onpenFilters($event)"
ng-if="$ctrl.panel"
ng-click="$ctrl.openPanel($event)"
icon="keyboard_arrow_down"
style="cursor: pointer; color: #aaa">
</vn-icon>

View File

@ -0,0 +1,200 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
/**
* An input specialized to perform searches, it allows to use a panel
* for advanced searches when the panel property is defined.
* When model and exprBuilder properties are used, the model is updated
* automatically with an and-filter exprexion in which each operand is built
* by calling the exprBuilder function for each non-null parameter.
*
* @property {Object} filter A key-value object with filter parameters
* @property {Function} onSearch Function to call when search is submited
* @property {SearchPanel} panel The panel used for advanced searches
* @property {CrudModel} model The model used for searching
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
*/
export default class Controller extends Component {
constructor($element, $scope, $compile, $state, $transitions) {
super($element, $scope);
this.$compile = $compile;
this.$state = $state;
this.deregisterCallback = $transitions.onStart({},
transition => this.changeState(transition));
this.filter = {};
this.searchString = '';
}
$onInit() {
if (this.$state.params.q)
this.filter = JSON.parse(decodeURIComponent(this.$state.params.q));
this.refreshString();
this.doSearch();
}
changeState(transition) {
return transition._targetState._identifier.name !== this.$state.current.name;
}
openPanel(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.$panel = this.$compile(`<${this.panel}/>`)(this.$.$new());
let panel = this.$panel.isolateScope().$ctrl;
panel.filter = this.filter;
panel.onSubmit = filter => this.onPanelSubmit(filter);
this.$.popover.parent = this.element;
this.$.popover.child = this.$panel[0];
this.$.popover.show();
}
onPopoverClose() {
this.$panel.scope().$destroy();
this.$panel.remove();
this.$panel = null;
}
onPanelSubmit(filter) {
this.$.popover.hide();
for (let param in filter)
if (filter[param] == null)
delete filter[param];
this.filter = filter;
this.refreshString();
this.doSearch();
}
refreshString() {
this.searchString = this.getStringFromObject(this.filter);
}
onSubmit() {
this.filter = this.getObjectFromString(this.searchString);
this.doSearch();
}
doSearch() {
this.pushFilterToState(this.filter);
if (this.onSearch)
this.onSearch({filter: this.filter});
if (this.model) {
if (!this.exprBuilder)
throw new Error('exprBuilder property should be defined when model is assigned');
let and = [];
for (let param in this.filter) {
let value = this.filter[param];
if (value == null) continue;
let expr = this.exprBuilder({param, value});
if (expr) and.push(expr);
}
let lbFilter = and.length > 0 ? {where: {and}} : null;
this.model.refresh(lbFilter);
}
}
pushFilterToState(filter) {
let history = window.history || {pushState: () => {
console.error('Error in history.pushState(): Browser incompatibility error');
}};
let aux = window.location.hash.split('?q=');
if (Object.keys(filter).length)
history.pushState({}, null, `${aux[0]}?q=${encodeURIComponent(JSON.stringify(filter))}`);
else
history.pushState({}, null, aux[0]);
}
/**
* Finds pattern key:value or key:(extra value) and passes it to object.
*
* @param {String} searchString The search string
* @return {Object} The parsed object
*/
getObjectFromString(searchString) {
let result = {};
if (searchString) {
let regex = /((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi;
let findPattern = searchString.match(regex);
let remnantString = searchString.replace(regex, '').trim();
if (findPattern) {
for (let i = 0; i < findPattern.length; i++) {
let aux = findPattern[i].split(':');
let property = aux[0];
let value = aux[1].replace(/\(|\)/g, '');
result[property] = value.trim();
}
}
if (remnantString)
result.search = remnantString;
}
return result;
}
/**
* Passes an object to pattern key:value or key:(extra value).
*
* @param {Object} searchObject The search object
* @return {String} The passed string
*/
getStringFromObject(searchObject) {
let search = [];
if (searchObject) {
let keys = Object.keys(searchObject);
keys.forEach(key => {
if (key == 'search') return;
let value = searchObject[key];
let valueString;
if (typeof value === 'string' && value.indexOf(' ') !== -1)
valueString = `(${value})`;
else if (value instanceof Date)
valueString = value.toJSON();
else
switch (typeof value) {
case 'number':
case 'string':
case 'boolean':
valueString = `${value}`;
}
if (valueString)
search.push(`${key}:${valueString}`);
});
if (searchObject.search)
search.unshift(searchObject.search);
}
return search.length ? search.join(' ') : '';
}
$onDestroy() {
this.deregisterCallback();
}
}
Controller.$inject = ['$element', '$scope', '$compile', '$state', '$transitions'];
ngModule.component('vnSearchbar', {
template: require('./searchbar.html'),
bindings: {
filter: '<?',
onSearch: '&',
panel: '@',
model: '<?',
exprBuilder: '&?'
},
controller: Controller
});

View File

@ -1,7 +1,6 @@
vn-searchbar {
padding-top: 6px;
padding-left: 16px;
padding-right: 16px;
display: block;
& > form > vn-horizontal > vn-icon-button {
color: black;

View File

@ -11,4 +11,5 @@ Hide: Hide
Next: Next
Finalize: Finalize
Previous: Back
Load more: Load more
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search

View File

@ -11,4 +11,5 @@ Hide: Ocultar
Next: Siguiente
Finalize: Finalizar
Previous: Anterior
Load more: Cargar más
Auto-scroll interrupted, please adjust the search: Auto-scroll interrumpido, por favor ajusta la búsqueda

View File

@ -1,77 +0,0 @@
<div pad-large style="min-width: 30em">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="Id"
model="$ctrl.filter.id"
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Name"
model="$ctrl.filter.name"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Description"
model="$ctrl.filter.description">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
url="/item/api/ItemTypes"
label="Type"
show-field="name"
value-field="id"
field="$ctrl.filter.typeFk">
</vn-autocomplete>
<vn-autocomplete
vn-one
url="/item/api/Producers"
label="Producer"
show-field="name"
value-field="id"
field="$ctrl.filter.producerFk">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
url="/item/api/Origins"
label="Origin"
show-field="name"
value-field="id"
field="$ctrl.filter.originFk">
</vn-autocomplete>
<vn-autocomplete
vn-one
url="/item/api/Inks"
label="Ink"
show-field="name"
value-field="id"
field="$ctrl.filter.inkFk">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Category"
model="$ctrl.filter.category">
</vn-textfield>
<vn-textfield
vn-one
label="Size"
type="number"
model="$ctrl.filter.itemSize">
</vn-textfield>
</vn-horizontal>
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,16 +0,0 @@
import ngModule from '../module';
class Controller {
constructor() {
this.onSubmit = () => {};
}
onSearch() {
this.onSubmit(this.filter);
}
}
ngModule.component('vnItemFilterPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,26 +1,30 @@
<mg-ajax path="/item/api/Items/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-crud-model
vn-id="model"
url="/item/api/Items"
filter="::$ctrl.filter"
limit="4"
data="items"
auto-load="false">
</vn-crud-model>
<div margin-medium>
<div class="vn-list">
<vn-card>
<vn-horizontal>
<vn-searchbar
vn-one
index="index"
on-search="$ctrl.search(index)"
advanced="true"
popover="vn-item-filter-panel"
ignore-keys = "['page', 'size', 'search']">
</vn-searchbar>
</vn-horizontal>
<vn-card pad-medium-h>
<vn-searchbar
panel="vn-item-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-card>
<vn-card margin-medium-top>
<vn-card margin-medium-v>
<vn-item-product
ng-repeat="item in index.model.instances track by item.id"
item="item">
ng-repeat="item in items track by item.id"
item="::item">
</vn-item-product>
</vn-card>
<vn-paging index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging index="index" total="index.model.count" items="$ctrl.items"></vn-auto-paging> -->
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</div>
</div>
<a ui-sref="item.create" vn-bind="+" fixed-bottom-right>

View File

@ -3,46 +3,68 @@ import './product';
import './style.scss';
class Controller {
constructor($http, $state, $scope) {
this.$http = $http;
this.$state = $state;
this.$scope = $scope;
this.model = {};
this.$ = $scope;
this.itemSelected = null;
this.items = [];
this.filter = {
include: [
{relation: 'itemType',
scope: {
fields: ['name', 'workerFk'],
include: {
relation: 'worker',
fields: ['firstName', 'name']
}
}
}
],
order: 'name ASC'
};
}
search(index) {
index.accept();
/* this.items = [];
index.accept().then(res => {
this.items = res.instances;
}); */
exprBuilder(param, value) {
switch (param) {
case 'search':
return {
or: [
{id: value},
{name: {regexp: value}}
]
};
case 'name':
case 'description':
return {[param]: {regexp: value}};
case 'id':
case 'typeFk':
return {[param]: value};
}
}
cloneItem(item) {
this.itemSelected = item;
this.$scope.clone.show();
this.$.clone.show();
}
onCloneAccept(response) {
if (response == 'ACCEPT' && this.itemSelected) {
this.$http.post(`/item/api/Items/${this.itemSelected.id}/clone`).then(res => {
if (res && res.data && res.data.id) {
this.$state.go('item.card.tags', {id: res.data.id});
}
});
}
if (!(response == 'ACCEPT' && this.itemSelected))
return;
this.$http.post(`/item/api/Items/${this.itemSelected.id}/clone`).then(res => {
if (res && res.data && res.data.id)
this.$state.go('item.card.tags', {id: res.data.id});
});
this.itemSelected = null;
}
showItemPreview(item) {
this.itemSelected = item;
this.$scope.preview.show();
this.$.preview.show();
}
}
Controller.$inject = ['$http', '$state', '$scope'];
ngModule.component('vnItemIndex', {

View File

@ -1,3 +1,4 @@
@import "./colors";
vn-item-product {
display: block;
@ -12,4 +13,4 @@ vn-item-product {
border-radius: .2em;
}
}
}
}

View File

@ -2,7 +2,7 @@ export * from './module';
import './index';
import './filter-item-list';
import './filter-panel';
import './search-panel';
import './create';
import './card';
import './descriptor';

View File

@ -42,4 +42,5 @@ Remove niche: Quitar nicho
Add barcode: Añadir código de barras
Remove barcode: Quitar código de barras
Buyer: Comprador
No results: Sin resultados
No results: Sin resultados
Tag: Etiqueta

View File

@ -0,0 +1,84 @@
<mg-ajax path="/item/api/Tags" options="mgIndex as tags"></mg-ajax>
<div style="min-width: 30em; max-height: 540px; overflow: auto;">
<form pad-large ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="Id"
model="filter.id"
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Name"
model="filter.name">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
vn-focus
url="/item/api/ItemTypes"
label="Type"
show-field="name"
value-field="id"
field="filter.typeFk">
</vn-autocomplete>
<vn-textfield
vn-one
label="Description"
model="filter.description">
</vn-textfield>
</vn-horizontal>
<!--
<vn-horizontal ng-repeat="itemTag in filter.tags">
<vn-autocomplete
vn-id="tag"
vn-one
field="itemTag.tagFk"
data="tags.model"
show-field="name"
label="Tag"
on-change="itemTag.value = null">
</vn-autocomplete>
<vn-textfield
vn-two
ng-show="tag.selection.isFree !== false"
vn-id="text"
label="Value"
model="itemTag.value">
</vn-textfield>
<vn-autocomplete
vn-two
ng-show="tag.selection.isFree === false"
url="{{$ctrl.getSourceTable(tag.selection)}}"
label="Value"
field="itemTag.value"
show-field="name"
value-field="name">
</vn-autocomplete>
<vn-none>
<vn-icon-button
medium-grey
margin-medium-v
vn-tooltip="Remove tag"
icon="remove_circle_outline"
ng-click="filter.tags.splice($index, 1)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-horizontal>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add tag"
icon="add_circle"
ng-click="filter.tags.push({})">
</vn-icon-button>
</vn-horizontal>
-->
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,32 @@
import ngModule from '../module';
import SearchPanel from 'core/src/components/searchbar/search-panel';
class Controller extends SearchPanel {
set filter(value) {
if (!value.tags)
value.tags = [{}];
this.$.filter = value;
}
get filter() {
return this.$.filter;
}
getSourceTable(selection) {
if (!selection || selection.isFree === true)
return null;
if (selection.sourceTable)
return '/api/'
+ selection.sourceTable.charAt(0).toUpperCase()
+ selection.sourceTable.substring(1) + 's';
else if (selection.sourceTable == null)
return `/api/ItemTags/filterItemTags/${selection.id}`;
}
}
ngModule.component('vnItemSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -48,14 +48,16 @@
rule="itemTag.priority"
vn-acl="buyer">
</vn-textfield>
<vn-icon
pad-medium-top
pointer
<vn-none>
<vn-icon-button
medium-grey
margin-medium-v
vn-tooltip="Remove tag"
icon="remove_circle_outline"
ng-click="$ctrl.removeTag($index)">
</vn-icon>
ng-click="$ctrl.removeTag($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button

View File

@ -4,4 +4,3 @@ import './main-menu/main-menu';
import './left-menu/left-menu';
import './left-menu/menu-item';
import './topbar/topbar';
import './searchbar/searchbar';

View File

@ -1,160 +0,0 @@
import ngModule from '../../module';
import './style.scss';
export default class Controller {
constructor($element, $scope, $compile, $timeout, $state, $transitions) {
this.element = $element[0];
this.$ = $scope;
this.$compile = $compile;
this.$timeout = $timeout;
this.stringSearch = '';
this.$state = $state;
this.deregisterCallback = $transitions.onStart({},
transition => this.changeState(transition));
}
clearFilter() {
this.index.filter = {
page: 1,
size: 20
};
}
setFilter(filterObject) {
this.clearFilter();
Object.assign(this.index.filter, filterObject);
}
getFiltersFromString(stringSearch) {
let result = {};
if (stringSearch) {
// find pattern key:value or key:(extra value) and returns array
let findPattern = stringSearch.match(/((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi);
let remnantString = (stringSearch.replace(/((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi, '')).trim();
if (findPattern) {
for (let i = 0; i < findPattern.length; i++) {
let aux = findPattern[i].split(':');
let property = aux[0];
let value = aux[1].replace(/\(|\)/g, '');
result[property] = value.trim();
}
}
if (remnantString) {
result.search = remnantString;
}
}
return result;
}
createStringFromObject(filterObject) {
let search = [];
let keys = Object.keys(filterObject);
if (keys.length) {
keys.forEach(k => {
let ignore = (this.ignoreKeys && this.ignoreKeys instanceof Array && this.ignoreKeys.indexOf(k) !== -1);
if (!ignore) {
let value = filterObject[k];
if (typeof value === 'string' && value.indexOf(' ') !== -1) {
search.push(`${k}:(${value})`);
} else if (typeof value !== 'object') {
search.push(`${k}:${value}`);
}
}
});
if (filterObject.search)
search.unshift(filterObject.search);
}
return (search.length) ? search.join(' ') : '';
}
changeState(transition) {
return !(transition._targetState._identifier.name === this.$state.current.name);
}
pushFiltersToState(filters) {
let history = window.history || {pushState: () => {
console.error('Error in history.pushState(): Browser incompatibility error');
}};
let aux = window.location.hash.split('?q=');
if (Object.keys(filters).length)
history.pushState({}, null, `${aux[0]}?q=${encodeURIComponent(JSON.stringify(filters))}`);
else
history.pushState({}, null, aux[0]);
}
onpenFilters(event) {
let filter = {};
if (this.stringSearch) {
filter = this.getFiltersFromString(this.stringSearch);
}
this.$child = this.$compile(`<${this.popover}/>`)(this.$.$new());
var childCtrl = this.$child.isolateScope().$ctrl;
childCtrl.filter = Object.assign({}, filter);
childCtrl.onSubmit = filter => this.onChildSubmit(filter);
if (this.data)
childCtrl.data = Object.assign({}, this.data);
event.preventDefault();
this.$.popover.parent = this.element;
this.$.popover.child = this.$child[0];
this.$.popover.show();
}
onPopoverClose() {
this.$child.scope().$destroy();
this.$child.remove();
this.$child = null;
}
onChildSubmit(filter) {
this.$.popover.hide();
this.stringSearch = this.createStringFromObject(filter);
this.clearFilter();
this.$timeout(() => this.onSubmit());
}
onSubmit() {
let filter = {};
if (this.stringSearch) {
filter = this.getFiltersFromString(this.stringSearch);
}
this.setFilter(filter);
if (this.onSearch)
this.onSearch();
this.pushFiltersToState(filter);
}
$onDestroy() {
this.clearFilter();
this.deregisterCallback();
}
$onInit() {
if (this.$state.params.q) {
let filter = JSON.parse(decodeURIComponent(this.$state.params.q));
this.stringSearch = this.createStringFromObject(filter);
}
this.$timeout(() => this.onSubmit());
}
}
Controller.$inject = ['$element', '$scope', '$compile', '$timeout', '$state', '$transitions'];
ngModule.component('vnSearchbar', {
template: require('./searchbar.html'),
bindings: {
index: '<',
onSearch: '&',
advanced: '=',
popover: '@',
ignoreKeys: '<?',
data: '<?'
},
controller: Controller
});

View File

@ -64,7 +64,6 @@ html [vn-auto], vn-auto, .vn-auto {
flex-basis: auto;
}
html [vn-none], vn-none, .vn-none {
flex: 1;
flex: none;
}
html [vn-one], vn-one, .vn-one {

View File

@ -1,70 +1,69 @@
<mg-ajax path="/ticket/api/Tickets/filter" options="vnIndexNonAuto"></mg-ajax>
<div margin-large>
<div>
<vn-card pad-medium>
<vn-title>TICKETS</vn-title>
<vn-horizontal class="vn-list">
<vn-searchbar vn-one
index="index"
on-search="$ctrl.search(index)"
ignore-keys = "['page', 'size', 'search']">
</vn-searchbar>
</vn-horizontal>
<vn-horizontal>
<table class="vn-grid">
<thead>
<tr>
<th></th>
<th number translate>ID Ticket</th>
<th translate>Comercial</th>
<th translate>Date</th>
<th translate>Hora</th>
<th translate>Alias</th>
<th translate>Provincia</th>
<th translate>Estado</th>
<th translate>Agencia</th>
<th translate>Almacen</th>
<th number translate>Factura</th>
<th number translate>Ruta</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ticket in index.model.instances track by ticket.id"
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<td>
<!-- <i pointer
class="material-icons"
vn-tooltip="delete expedition"
ng-click="$ctrl.deleteExpedition(expedition)">warning</i> -->
</td>
<th number>{{::ticket.id}}</th>
<td translate>{{::ticket.client.salesPerson.name | dashIfEmpty}}</td>
<td translate>{{::ticket.shipped | date:'dd/MM/yyyy'}}</td>
<td translate>{{::ticket.shipped | date:'HH:MM'}}</td>
<td translate>{{::ticket.nickname}}</td>
<td translate>{{::ticket.address.province.name}}</td>
<td translate>{{::ticket.tracking.state.name}}</td>
<td translate>{{::ticket.agencyMode.name}}</td>
<td translate>{{::ticket.warehouse.name}}</td>
<td number translate>{{::ticket.refFk | dashIfEmpty}}</td>
<td number translate>{{::ticket.routeFk | dashIfEmpty}}</td>
<td>
<vn-icon-button
ng-click="$ctrl.preview($event, ticket)"
vn-tooltip="Preview"
icon="desktop_windows">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</vn-horizontal>
<vn-crud-model
vn-id="model"
url="/ticket/api/Tickets"
filter="::$ctrl.filter"
limit="10"
data="tickets"
auto-load="false">
</vn-crud-model>
<div margin-medium>
<div class="vn-list">
<vn-card pad-medium-h>
<vn-searchbar
panel="vn-ticket-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-card>
<vn-paging vn-one index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one index="index" total="index.model.count" items="$ctrl.tickets"></vn-auto-paging> -->
</div>
<vn-card margin-medium-v pad-medium>
<table class="vn-grid">
<thead>
<tr>
<th translate number>Id</th>
<th translate>Salesperson</th>
<th translate>Date</th>
<th translate>Hour</th>
<th translate>Alias</th>
<th translate>Province</th>
<th translate>State</th>
<th translate>Agency</th>
<th translate>Warehouse</th>
<th translate number>Invoice</th>
<th translate number>Route</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ticket in tickets"
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<th number>{{::ticket.id}}</th>
<td >{{::ticket.client.salesPerson.name | dashIfEmpty}}</td>
<td >{{::ticket.shipped | date:'dd/MM/yyyy'}}</td>
<td >{{::ticket.shipped | date:'HH:MM'}}</td>
<td >{{::ticket.nickname}}</td>
<td >{{::ticket.address.province.name}}</td>
<td >{{::ticket.tracking.state.name}}</td>
<td >{{::ticket.agencyMode.name}}</td>
<td >{{::ticket.warehouse.name}}</td>
<td number >{{::ticket.refFk | dashIfEmpty}}</td>
<td number >{{::ticket.routeFk | dashIfEmpty}}</td>
<td>
<vn-icon-button
ng-click="$ctrl.preview($event, ticket)"
vn-tooltip="Preview"
icon="desktop_windows">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</vn-card>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</div>
<a ui-sref="ticket.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>

View File

@ -1,11 +1,82 @@
import ngModule from '../module';
import './ticket-item';
import './style.scss';
export default class Controller {
constructor($scope) {
this.$scope = $scope;
this.$ = $scope;
this.ticketSelected = null;
this.filter = {
include: [
{
relation: 'address',
scope: {
fields: ['provinceFk'],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'warehouse',
scope: {
fields: ['name']
}
}, {
relation: 'agencyMode',
scope: {
fields: ['name']
}
}, {
relation: 'tracking',
scope: {
fields: ['stateFk'],
include: {
relation: 'state',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'client',
scope: {
fields: ['salesPersonFk'],
include: {
relation: 'salesPerson',
scope: {
fields: ['name']
}
}
}
}
],
order: 'shipped DESC'
};
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return {
or: [
{id: value},
{nickname: {regexp: value}}
]
};
case 'from':
return {shipped: {gte: value}};
case 'to':
return {shipped: {lte: value}};
case 'nickname':
return {[param]: {regexp: value}};
case 'id':
case 'clientFk':
case 'agencyModeFk':
case 'warehouseFk':
return {[param]: value};
}
}
compareDate(date) {
@ -26,17 +97,9 @@ export default class Controller {
preview(event, ticket) {
event.preventDefault();
event.stopImmediatePropagation();
this.$scope.dialogSummaryTicket.show();
this.$.dialogSummaryTicket.show();
this.ticketSelected = ticket;
}
search(index) {
index.accept();
/* this.tickets = [];
index.accept().then(res => {
this.tickets = res.instances;
}); */
}
}
Controller.$inject = ['$scope'];

View File

@ -34,14 +34,14 @@ describe('ticket', () => {
it('should call preventDefault and stopImmediatePropagation from event and show', () => {
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopImmediatePropagation']);
controller.$scope = {dialogSummaryTicket: {show: () => {}}};
spyOn(controller.$scope.dialogSummaryTicket, 'show');
controller.$ = {dialogSummaryTicket: {show: () => {}}};
spyOn(controller.$.dialogSummaryTicket, 'show');
let ticket = {};
controller.preview(event, ticket);
expect(event.preventDefault).toHaveBeenCalledWith();
expect(event.stopImmediatePropagation).toHaveBeenCalledWith();
expect(controller.$scope.dialogSummaryTicket.show).toHaveBeenCalledWith();
expect(controller.$.dialogSummaryTicket.show).toHaveBeenCalledWith();
});
});
});

View File

@ -1,3 +0,0 @@
vn-ticket-item {
display: block;
}

View File

@ -1,20 +0,0 @@
<a
ui-sref="ticket.card.summary({ id: {{::$ctrl.ticket.id}} })"
translate-attr="{title: 'View ticket'}"
class="vn-list-item">
<vn-horizontal ng-click="$ctrl.onClick($event)">
<vn-one>
<h6>{{::$ctrl.ticket.nickname}}</h6>
<vn-label-value label="Id"
value="{{::$ctrl.ticket.id}}">
</vn-label-value>
</vn-one>
<vn-horizontal class="buttons">
<vn-icon
ng-click="$ctrl.preview($event)"
vn-tooltip="Preview"
icon="desktop_windows">
</vn-icon>
</vn-horizontal>
</vn-horizontal>
</a>

View File

@ -1,24 +0,0 @@
import ngModule from '../module';
class Controller {
onClick(event) {
if (event.defaultPrevented)
event.stopImmediatePropagation();
}
preview(event) {
event.preventDefault();
this.index.openSummary(this.ticket);
}
}
ngModule.component('vnTicketItem', {
controller: Controller,
template: require('./ticket-item.html'),
bindings: {
ticket: '<'
},
require: {
index: '^vnTicketIndex'
}
});

View File

@ -0,0 +1,58 @@
<div pad-large style="min-width: 30em">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="Nickname"
model="filter.nickname"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Ticket id"
model="filter.id">
</vn-textfield>
<vn-textfield
vn-one
label="Client id"
model="filter.clientFk">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="From"
model="filter.from">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
model="filter.to">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Agency"
field="filter.agencyModeFk"
url="/api/AgencyModes"
show-field="name"
value-field="id">
<tpl-item>{{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
label="Warehouse"
field="filter.warehouseFk"
url="/api/Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import SearchPanel from 'core/src/components/searchbar/search-panel';
ngModule.component('vnTicketSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -0,0 +1,7 @@
Ticket id: Id ticket
Client id: Id cliente
Nickname: Alias
From: Desde
To: Hasta
Agency: Agencia
Warehouse: Almacén

View File

@ -1,5 +1,6 @@
export * from './module';
import './search-panel';
import './index';
import './create';
import './card';

View File

@ -192,7 +192,7 @@ export default {
itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
tagsButton: `vn-menu-item a[ui-sref="item.card.tags"]`,
firstRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(2) > vn-icon[icon="remove_circle_outline"]`,
firstRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(2) vn-icon-button[icon="remove_circle_outline"]`,
firstTagSelect: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] input`,
firstTagDisabled: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete > div > div > input`,
firstTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
@ -218,7 +218,7 @@ export default {
fifthTagSelectOptionFive: `vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(5)`,
fifthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] > div > input`,
fifthRelevancyInput: `vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] > div > input`,
addItemTagButton: `vn-icon[icon="add_circle"]`,
addItemTagButton: `vn-icon-button[icon="add_circle"]`,
submitItemTagsButton: `${components.vnSubmit}`
},
itemTax: {

View File

@ -23,7 +23,7 @@ describe('Ticket', () => {
it('should search for the ticket with id 1', () => {
return nightmare
.wait(selectors.ticketsIndex.searchTicketInput)
.type(selectors.ticketsIndex.searchTicketInput, '1')
.type(selectors.ticketsIndex.searchTicketInput, 'id:1')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countSearchResults(selectors.ticketsIndex.searchResult)

View File

@ -23,7 +23,7 @@ describe('Ticket', () => {
it('should search for the ticket with id 1', () => {
return nightmare
.wait(selectors.ticketsIndex.searchTicketInput)
.type(selectors.ticketsIndex.searchTicketInput, '1')
.type(selectors.ticketsIndex.searchTicketInput, 'id:1')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countSearchResults(selectors.ticketsIndex.searchResult)

View File

@ -25,7 +25,7 @@ describe('Ticket', () => {
it('should search for the ticket 1', () => {
return nightmare
.wait(selectors.ticketsIndex.searchResult)
.type(selectors.ticketsIndex.searchTicketInput, 1)
.type(selectors.ticketsIndex.searchTicketInput, 'id:1')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countSearchResults(selectors.ticketsIndex.searchResult)

View File

@ -25,7 +25,7 @@
// it('should search for the ticket 1', () => {
// return nightmare
// .wait(selectors.ticketsIndex.searchResult)
// .type(selectors.ticketsIndex.searchTicketInput, 1)
// .type(selectors.ticketsIndex.searchTicketInput, 'id:1')
// .click(selectors.ticketsIndex.searchButton)
// .waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
// .countSearchResults(selectors.ticketsIndex.searchResult)

View File

@ -25,7 +25,7 @@ describe('Ticket', () => {
it('should search for the ticket 1', () => {
return nightmare
.wait(selectors.ticketsIndex.searchResult)
.type(selectors.ticketsIndex.searchTicketInput, 1)
.type(selectors.ticketsIndex.searchTicketInput, 'id:1')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countSearchResults(selectors.ticketsIndex.searchResult)

View File

@ -17,6 +17,7 @@
"angular-translate-loader-partial": "^2.18.1",
"flatpickr": "^4.4.6",
"fs-extra": "^5.0.0",
"js-yaml": "^3.10.0",
"material-design-lite": "^1.3.0",
"mg-crud": "^1.1.2",
"npm": "^5.8.0",
@ -55,7 +56,6 @@
"html-loader": "^0.4.4",
"jasmine": "^2.9.0",
"jasmine-spec-reporter": "^4.2.1",
"js-yaml": "^3.10.0",
"karma": "^1.7.1",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.1.0",

View File

@ -40,6 +40,7 @@ module.exports = function(Self) {
street: data.street,
city: data.city,
provinceFk: data.provinceFk,
countryFk: data.countryFk,
isEqualizated: data.isEqualizated
};
newClient = await Self.create(client);

View File

@ -1,61 +0,0 @@
module.exports = function(Client) {
Client.installMethod('filter', filterClients);
function filterClients(params) {
let filters = {
where: {},
skip: (params.page - 1) * params.size,
limit: params.size
};
delete params.page;
delete params.size;
if (params.search) {
filters.where.and = [
{
or: [
{id: params.search},
{name: {regexp: params.search}}
]
}
];
delete params.search;
}
if (params.phone) {
let phones = [
{phone: params.phone},
{mobile: params.phone}
];
if (filters.where.and) {
filters.where.and.push(
{
or: phones
}
);
} else {
filters.where.or = phones;
}
delete params.phone;
}
let properties = Object.keys(params);
if (properties.length) {
properties.forEach(
property => {
let propertyToBeEqual = (property === 'postcode' || property === 'fi' || property === 'id');
if (filters.where.and) {
let filter = {};
filter[property] = propertyToBeEqual ? params[property] : {regexp: params[property]};
filters.where.and.push(filter);
} else {
filters.where[property] = propertyToBeEqual ? params[property] : {regexp: params[property]};
}
}
);
}
return filters;
}
};

View File

@ -1,62 +0,0 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
let filter = {
where: {},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'name ASC', // name, relevancy DESC
include: [
{relation: 'itemType',
scope: {
fields: ['name', 'workerFk'],
include: {
relation: 'worker',
fields: ['firstName', 'name']
}
}
},
{relation: 'origin'},
{relation: 'ink'},
{relation: 'producer'},
{relation: 'intrastat'},
{relation: 'expence'}
]
};
delete params.page;
delete params.size;
delete params.order;
if (params.search) {
filter.where.and = [
{
or: [
{id: params.search},
{name: {regexp: params.search}}
]
}
];
delete params.search;
}
if (params.itemSize) {
params.size = params.itemSize;
delete params.itemSize;
}
Object.keys(params).forEach(
key => {
if (filter.where.and) {
let filter = {};
filter[key] = (key === 'description' || key === 'name') ? {regexp: params[key]} : params[key];
filter.where.and.push(filter);
} else {
filter.where[key] = (key === 'description' || key === 'name') ? {regexp: params[key]} : params[key];
}
}
);
return filter;
}
};

View File

@ -1,87 +0,0 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
let filters = {
where: {},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'created DESC',
include: [
{
relation: 'address',
scope: {
fields: ['provinceFk'],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'warehouse',
scope: {
fields: ['name']
}
}, {
relation: 'agencyMode',
scope: {
fields: ['name']
}
}, {
relation: 'tracking',
scope: {
fields: ['stateFk'],
include: {
relation: 'state',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'client',
scope: {
fields: ['salesPersonFk'],
include: {
relation: 'salesPerson',
scope: {
fields: ['name']
}
}
}
}
]
};
delete params.page;
delete params.size;
delete params.order;
if (params.search) {
filters.where.and = [
{
or: [
{id: params.search},
{name: {regexp: params.search}}
]
}
];
delete params.search;
}
Object.keys(params).forEach(
key => {
if (filters.where.and) {
let filter = {};
filter[key] = (key === 'nickname') ? {regexp: params[key]} : params[key];
filters.where.and.push(filter);
} else {
filters.where[key] = (key === 'nickname') ? {regexp: params[key]} : params[key];
}
}
);
return filters;
}
};

View File

@ -10,7 +10,6 @@ module.exports = Self => {
require('../methods/client/card')(Self);
require('../methods/client/createWithUser')(Self);
require('../methods/client/listWorkers')(Self);
require('../methods/client/filter')(Self);
require('../methods/client/hasCustomerRole')(Self);
require('../methods/client/isValidClient')(Self);
require('../methods/client/activeSalesPerson')(Self);

View File

@ -1,7 +1,6 @@
let UserError = require('../helpers').UserError;
module.exports = Self => {
require('../methods/item/filter')(Self);
require('../methods/item/clone')(Self);
require('../methods/item/updateTaxes')(Self);

View File

@ -1,7 +1,6 @@
module.exports = Self => {
require('../methods/ticket/changeTime')(Self);
require('../methods/ticket/changeWorker')(Self);
require('../methods/ticket/filter')(Self);
require('../methods/ticket/getVolume')(Self);
require('../methods/ticket/getTotalVolume')(Self);
require('../methods/ticket/summary')(Self);