Merge branch 'dev' of https://git.verdnatura.es/salix into dev
This commit is contained in:
commit
64da648a2f
|
@ -45,7 +45,7 @@ vn-login > div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 0;
|
width: 0;
|
||||||
top: .3em;
|
top: .3em;
|
||||||
right: 4em;
|
right: 3em;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<form name="form" ng-submit="watcher.submitGo('client.card.address.index')">
|
<form name="form" ng-submit="watcher.submitGo('client.card.address.index')">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Address</vn-title>
|
<vn-title>Address</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check vn-one label="Default" field="$ctrl.address.isDefaultAddress"></vn-check>
|
<vn-check vn-one label="Default" field="$ctrl.address.isDefaultAddress"></vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -15,41 +15,9 @@ describe('Client', () => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$state.params.addressId = '1';
|
$state.params.addressId = '1';
|
||||||
controller = $componentController('vnClientAddressEdit', {$state: $state});
|
controller = $componentController('vnClientAddressEdit', {$state: $state});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should define and set address property', () => {
|
|
||||||
expect(controller.address.id).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_observationsEquals', () => {
|
|
||||||
it('should return true if two observations are equals independent of control attributes', () => {
|
|
||||||
let ob1 = {id: 1, observationTypeFk: 1, description: 'Spiderman rocks', showAddIcon: true};
|
|
||||||
let ob2 = {id: 1, observationTypeFk: 1, description: 'Spiderman rocks', showAddIcon: false};
|
|
||||||
let equals = controller.equalObservations(ob2, ob1);
|
|
||||||
|
|
||||||
expect(equals).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if two observations are not equals independent of control attributes', () => {
|
|
||||||
let ob1 = {id: 1, observationTypeFk: 1, description: 'Spiderman rocks', showAddIcon: true};
|
|
||||||
let ob2 = {id: 1, observationTypeFk: 1, description: 'Spiderman sucks', showAddIcon: true};
|
|
||||||
let equals = controller.equalObservations(ob2, ob1);
|
|
||||||
|
|
||||||
expect(equals).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('$onInit()', () => {
|
|
||||||
it('should perform a GET query to receive the address observations', () => {
|
|
||||||
let filter = {where: {addressFk: 1}, include: {relation: 'observationType'}};
|
|
||||||
let res = ['some notes'];
|
|
||||||
$httpBackend.whenGET(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).respond(res);
|
|
||||||
$httpBackend.expectGET(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`);
|
|
||||||
controller.$onInit();
|
|
||||||
$httpBackend.flush();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,32 @@
|
||||||
|
|
||||||
<mg-ajax
|
<mg-ajax
|
||||||
path="/client/api/Addresses/{{edit.params.addressId}}"
|
path="/client/api/Addresses/{{edit.params.addressId}}"
|
||||||
actions="$ctrl.address = edit.model; $ctrl._setIconAdd();"
|
actions="$ctrl.address = edit.model"
|
||||||
options="mgEdit">
|
options="mgEdit">
|
||||||
</mg-ajax>
|
</mg-ajax>
|
||||||
<vn-watcher
|
<vn-watcher
|
||||||
vn-id="watcher"
|
vn-id="watcher"
|
||||||
|
data="$ctrl.address"
|
||||||
url="/client/api/Addresses"
|
url="/client/api/Addresses"
|
||||||
id-field="id"
|
id-field="id"
|
||||||
data="$ctrl.address"
|
form="form">
|
||||||
form="addressForm">
|
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<form name="addressForm" ng-submit="$ctrl.submit()">
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="/client/api/AddressObservations"
|
||||||
|
fields="['id', 'addressFk', 'observationTypeFk', 'description']"
|
||||||
|
link="{addressFk: $ctrl.$stateParams.addressId}"
|
||||||
|
data="observations">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
url="/client/api/ObservationTypes"
|
||||||
|
fields="['id', 'description']"
|
||||||
|
data="types">
|
||||||
|
</vn-crud-model>
|
||||||
|
<form name="form" ng-submit="$ctrl.submit()">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Address</vn-title>
|
<vn-title>Address</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check vn-one label="Enabled" field="$ctrl.address.isActive"></vn-check>
|
<vn-check vn-one label="Enabled" field="$ctrl.address.isActive"></vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one label="Is equalizated"
|
vn-one label="Is equalizated"
|
||||||
|
@ -49,60 +62,43 @@
|
||||||
<vn-textfield vn-one label="Phone" field="$ctrl.address.phone"></vn-textfield>
|
<vn-textfield vn-one label="Phone" field="$ctrl.address.phone"></vn-textfield>
|
||||||
<vn-textfield vn-one label="Mobile" field="$ctrl.address.mobile"></vn-textfield>
|
<vn-textfield vn-one label="Mobile" field="$ctrl.address.mobile"></vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-one margin-medium-top>
|
|
||||||
</vn-card>
|
|
||||||
</form>
|
|
||||||
<vn-watcher form="notesForm"></vn-watcher>
|
|
||||||
<form name="notesForm" ng-submit="$ctrl.submit()">
|
|
||||||
<vn-card pad-large>
|
|
||||||
<vn-title>Notes</vn-title>
|
<vn-title>Notes</vn-title>
|
||||||
<mg-ajax path="/client/api/ObservationTypes" options="mgIndex as observationsTypes"></mg-ajax>
|
<div name="observations">
|
||||||
<vn-horizontal ng-repeat="observation in $ctrl.observations track by $index">
|
<vn-horizontal ng-repeat="observation in observations">
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
ng-if="!observation.id"
|
|
||||||
vn-one
|
vn-one
|
||||||
vn-focus
|
vn-focus
|
||||||
initial-data="observation.observationType"
|
data="types"
|
||||||
field="observation.observationTypeFk"
|
field="observation.observationTypeFk"
|
||||||
data="observationsTypes.model"
|
|
||||||
show-field="description"
|
show-field="description"
|
||||||
label="Observation type">
|
label="Observation type">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
|
||||||
ng-if="observation.id"
|
|
||||||
vn-one
|
|
||||||
label="Observation type"
|
|
||||||
model="observation.observationType.description"
|
|
||||||
disabled="true">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-two
|
vn-two
|
||||||
margin-large-right
|
|
||||||
label="Description"
|
label="Description"
|
||||||
model="observation.description"
|
model="observation.description"
|
||||||
rule="addressObservation.description">
|
rule="addressObservation.description">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-auto pad-medium-top>
|
<vn-none>
|
||||||
<vn-icon
|
<vn-icon-button
|
||||||
pointer
|
|
||||||
medium-grey
|
medium-grey
|
||||||
|
margin-medium-v
|
||||||
vn-tooltip="Remove note"
|
vn-tooltip="Remove note"
|
||||||
icon="remove_circle_outline"
|
icon="remove_circle_outline"
|
||||||
ng-click="$ctrl.removeObservation($index)">
|
ng-click="$ctrl.removeObservation($index)"
|
||||||
</vn-icon>
|
tabindex="-1">
|
||||||
</vn-one>
|
</vn-icon-button>
|
||||||
|
</vn-none>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-one>
|
</div>
|
||||||
<vn-one>
|
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
pointer
|
pointer
|
||||||
vn-bind="+"
|
vn-bind="+"
|
||||||
vn-tooltip="Add note"
|
vn-tooltip="Add note"
|
||||||
icon="add_circle"
|
icon="add_circle"
|
||||||
ng-if="observationsTypes.model.length > $ctrl.observations.length"
|
ng-if="types.length > observations.length"
|
||||||
ng-click="$ctrl.addObservation()">
|
ng-click="model.insert()">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-one>
|
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit label="Save"></vn-submit>
|
<vn-submit label="Save"></vn-submit>
|
||||||
|
|
|
@ -1,151 +1,30 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
constructor($state, $scope, $http, $q, $translate, vnApp) {
|
constructor($scope, $state) {
|
||||||
|
this.$ = $scope;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$scope = $scope;
|
this.$stateParams = $state.params;
|
||||||
this.$http = $http;
|
|
||||||
this.$q = $q;
|
|
||||||
this.$translate = $translate;
|
|
||||||
this.vnApp = vnApp;
|
|
||||||
|
|
||||||
this.address = {
|
|
||||||
id: parseInt($state.params.addressId)
|
|
||||||
};
|
|
||||||
this.observations = [];
|
|
||||||
this.oldObservations = {};
|
|
||||||
this.removedObservations = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_setDirtyForm() {
|
|
||||||
if (this.$scope.form) {
|
|
||||||
this.$scope.form.$setDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_unsetDirtyForm() {
|
|
||||||
if (this.$scope.form) {
|
|
||||||
this.$scope.form.$setPristine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addObservation() {
|
|
||||||
this.observations.push({observationTypeFk: null, addressFk: this.address.id, description: null});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeObservation(index) {
|
removeObservation(index) {
|
||||||
let item = this.observations[index];
|
this.$.watcher.setDirty();
|
||||||
if (item) {
|
this.$.model.remove(index);
|
||||||
this.observations.splice(index, 1);
|
|
||||||
if (item.id) {
|
|
||||||
this.removedObservations.push(item.id);
|
|
||||||
this._setDirtyForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.observations.length === 0 && Object.keys(this.oldObservations).length === 0) {
|
|
||||||
this._unsetDirtyForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_submitObservations(observationsObject) {
|
|
||||||
return this.$http.post(`/client/api/AddressObservations/crudAddressObservations`, observationsObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
equalObservations(oldObj, newObj) {
|
|
||||||
return oldObj.id === newObj.id && oldObj.observationTypeFk === newObj.observationTypeFk && oldObj.description === newObj.description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
if (this.$scope.addressForm.$invalid || this.$scope.notesForm.$invalid) {
|
this.$.watcher.check();
|
||||||
this.vnApp.showMessage(
|
this.$.watcher.realSubmit()
|
||||||
this.$translate.instant('Some fields are invalid')
|
.then(() => this.$.model.save(true))
|
||||||
);
|
.then(() => {
|
||||||
return false;
|
this.$.watcher.setPristine();
|
||||||
}
|
this.$.watcher.notifySaved();
|
||||||
|
|
||||||
let canSubmitWatcher = this.$scope.watcher.dataChanged();
|
|
||||||
let canSubmitObservations;
|
|
||||||
let repeatedTypes = false;
|
|
||||||
let emptyFields = false;
|
|
||||||
let types = [];
|
|
||||||
let observationsObj = {
|
|
||||||
delete: this.removedObservations,
|
|
||||||
create: [],
|
|
||||||
update: []
|
|
||||||
};
|
|
||||||
|
|
||||||
this.observations.forEach(observation => {
|
|
||||||
let isNewObservation = observation.id === undefined;
|
|
||||||
|
|
||||||
if (observation.observationTypeFk && types.indexOf(observation.observationTypeFk) !== -1) {
|
|
||||||
repeatedTypes = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observation.observationTypeFk === null || observation.description === null) {
|
|
||||||
emptyFields = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observation.observationTypeFk)
|
|
||||||
types.push(observation.observationTypeFk);
|
|
||||||
|
|
||||||
if (isNewObservation && observation.observationTypeFk && observation.description) {
|
|
||||||
observationsObj.create.push(observation);
|
|
||||||
} else if (!isNewObservation && !this.equalObservations(this.oldObservations[observation.id], observation)) {
|
|
||||||
observationsObj.update.push(observation);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
canSubmitObservations = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0;
|
|
||||||
|
|
||||||
if (repeatedTypes) {
|
|
||||||
this.vnApp.showMessage(
|
|
||||||
this.$translate.instant('The observation type must be unique')
|
|
||||||
);
|
|
||||||
} else if (emptyFields) {
|
|
||||||
this.vnApp.showMessage(
|
|
||||||
this.$translate.instant('No field can be blank')
|
|
||||||
);
|
|
||||||
} else if (canSubmitWatcher && !canSubmitObservations) {
|
|
||||||
this.$scope.watcher.submit().then(() => {
|
|
||||||
this.$state.go('client.card.address.index', {id: this.$state.params.id});
|
|
||||||
this.card.reload();
|
this.card.reload();
|
||||||
});
|
this.$state.go('client.card.address.index', {id: this.$stateParams.id});
|
||||||
} else if (!canSubmitWatcher && canSubmitObservations) {
|
|
||||||
this._submitObservations(observationsObj).then(() => {
|
|
||||||
this.$state.go('client.card.address.index', {id: this.$state.params.id});
|
|
||||||
});
|
|
||||||
} else if (canSubmitWatcher && canSubmitObservations) {
|
|
||||||
this.$q.all([this.$scope.watcher.submit(), this._submitObservations(observationsObj)]).then(() => {
|
|
||||||
this.$state.go('client.card.address.index', {id: this.$state.params.id});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.vnApp.showMessage(
|
|
||||||
this.$translate.instant('No changes to save')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this._unsetDirtyForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
_getAddressNotes() {
|
|
||||||
let filter = {
|
|
||||||
where: {addressFk: this.address.id},
|
|
||||||
include: {relation: 'observationType'}
|
|
||||||
};
|
|
||||||
this.$http.get(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).then(res => {
|
|
||||||
this.observations = res.data;
|
|
||||||
res.data.forEach(item => {
|
|
||||||
this.oldObservations[item.id] = Object.assign({}, item);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
this._getAddressNotes();
|
|
||||||
}
|
}
|
||||||
}
|
Controller.$inject = ['$scope', '$state'];
|
||||||
Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp'];
|
|
||||||
|
|
||||||
ngModule.component('vnClientAddressEdit', {
|
ngModule.component('vnClientAddressEdit', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<vn-title vn-one>Addresses</vn-title>
|
<vn-title vn-one>Addresses</vn-title>
|
||||||
<vn-horizontal ng-repeat="address in index.model.items track by address.id" class="pad-medium-top" style="align-items: center;">
|
<vn-horizontal ng-repeat="address in index.model.items track by address.id" class="pad-medium-top" style="align-items: center;">
|
||||||
<vn-one border-radius class="pad-small border-solid"
|
<vn-one border-radius class="pad-small border-solid"
|
||||||
ng-class="{'bg-dark-item': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}">
|
ng-class="{'bg-main': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}">
|
||||||
<vn-horizontal style="align-items: center;">
|
<vn-horizontal style="align-items: center;">
|
||||||
<vn-none pad-medium-h>
|
<vn-none pad-medium-h>
|
||||||
<i class="material-icons"
|
<i class="material-icons"
|
||||||
|
@ -27,11 +27,22 @@
|
||||||
</i>
|
</i>
|
||||||
</vn-none>
|
</vn-none>
|
||||||
<vn-one border-solid-right>
|
<vn-one border-solid-right>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-one>
|
||||||
<div><b>{{::address.nickname}}</b></div>
|
<div><b>{{::address.nickname}}</b></div>
|
||||||
<div>{{::address.street}}</div>
|
<div name="street">{{::address.street}}</div>
|
||||||
<div>{{::address.city}}, {{::address.province}}</div>
|
<div>{{::address.city}}, {{::address.province}}</div>
|
||||||
<div>{{::address.phone}}, {{::address.mobile}}</div>
|
<div>{{::address.phone}}, {{::address.mobile}}</div>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
<vn-one>
|
||||||
|
<vn-check
|
||||||
|
vn-one label="Is equalizated"
|
||||||
|
field="address.isEqualizated"
|
||||||
|
disabled="true">
|
||||||
|
</vn-check>
|
||||||
|
</vn-one>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-one>
|
||||||
<vn-vertical vn-one pad-medium-h>
|
<vn-vertical vn-one pad-medium-h>
|
||||||
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'pad-small-top': $index}">
|
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'pad-small-top': $index}">
|
||||||
<b margin-medium-right>{{::observation.observationType.description}}:</b>
|
<b margin-medium-right>{{::observation.observationType.description}}:</b>
|
||||||
|
|
|
@ -10,17 +10,19 @@
|
||||||
<vn-title>Basic data</vn-title>
|
<vn-title>Basic data</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-one label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
<vn-textfield vn-one label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||||
<vn-textfield vn-one label="Contact" field="$ctrl.client.contact"></vn-textfield>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-one label="Phone" field="$ctrl.client.phone"></vn-textfield>
|
<vn-textfield vn-one label="Contact" field="$ctrl.client.contact"></vn-textfield>
|
||||||
<vn-textfield vn-one label="Mobile" field="$ctrl.client.mobile"></vn-textfield>
|
|
||||||
<vn-textfield vn-one
|
<vn-textfield vn-one
|
||||||
label="Email"
|
label="Email"
|
||||||
field="$ctrl.client.email"
|
field="$ctrl.client.email"
|
||||||
info="You can save multiple emails">
|
info="You can save multiple emails">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield vn-one label="Phone" field="$ctrl.client.phone"></vn-textfield>
|
||||||
|
<vn-textfield vn-one label="Mobile" field="$ctrl.client.mobile"></vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
initial-data="$ctrl.client.salesPerson"
|
initial-data="$ctrl.client.salesPerson"
|
||||||
|
|
|
@ -9,26 +9,31 @@
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Pay method</vn-title>
|
<vn-title>Pay method</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-two
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
label="Pay method"
|
||||||
vn-acl="administrative, salesAssistant"
|
vn-acl="administrative, salesAssistant"
|
||||||
field="$ctrl.client.payMethodFk"
|
field="$ctrl.client.payMethodFk"
|
||||||
url="/client/api/PayMethods"
|
url="/client/api/PayMethods"
|
||||||
select-fields="ibanRequired"
|
select-fields="ibanRequired"
|
||||||
initial-data="$ctrl.client.payMethod"
|
initial-data="$ctrl.client.payMethod">
|
||||||
label="Pay method">
|
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-two label="IBAN"
|
vn-one
|
||||||
field="$ctrl.client.iban"
|
label="Due day"
|
||||||
vn-acl="administrative, salesAssistant">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one label="Due day"
|
|
||||||
field="$ctrl.client.dueDay"
|
field="$ctrl.client.dueDay"
|
||||||
vn-acl="administrative, salesAssistant">
|
vn-acl="administrative, salesAssistant">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal margin-medium-bottom>
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
label="IBAN"
|
||||||
|
field="$ctrl.client.iban"
|
||||||
|
vn-acl="administrative, salesAssistant">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal pad-small-v>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Received core VNH"
|
label="Received core VNH"
|
||||||
|
|
|
@ -6,22 +6,11 @@
|
||||||
save="post">
|
save="post">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
||||||
<div style="max-width: 70em; margin: 0 auto;">
|
<div style="max-width: 50em; margin: 0 auto;">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Create client</vn-title>
|
<vn-title>Create client</vn-title>
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield vn-two label="Business name" field="$ctrl.client.socialName"></vn-textfield>
|
|
||||||
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi"></vn-textfield>
|
|
||||||
<vn-check
|
|
||||||
vn-one
|
|
||||||
label="Is equalizated"
|
|
||||||
field="$ctrl.client.isEqualizated">
|
|
||||||
</vn-check>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-two label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
<vn-textfield vn-two label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||||
<vn-textfield vn-one label="Web user" field="$ctrl.client.userName"></vn-textfield>
|
|
||||||
<vn-textfield vn-two label="Email" field="$ctrl.client.email" info="You can save multiple emails"></vn-textfield>
|
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
field="$ctrl.client.salesPersonFk"
|
field="$ctrl.client.salesPersonFk"
|
||||||
url="/client/api/Clients/activeSalesPerson"
|
url="/client/api/Clients/activeSalesPerson"
|
||||||
|
@ -31,6 +20,17 @@
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield vn-two label="Business name" field="$ctrl.client.socialName"></vn-textfield>
|
||||||
|
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi"></vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
vn-two
|
||||||
|
label="Street"
|
||||||
|
field="$ctrl.client.street">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
|
@ -38,18 +38,22 @@
|
||||||
field="$ctrl.client.postcode">
|
field="$ctrl.client.postcode">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-two
|
vn-one
|
||||||
label="Street"
|
|
||||||
field="$ctrl.client.street">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-two
|
|
||||||
label="City"
|
label="City"
|
||||||
field="$ctrl.client.city">
|
field="$ctrl.client.city">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
field="$ctrl.client.countryFk"
|
||||||
|
url="/client/api/Countries"
|
||||||
|
show-field="country"
|
||||||
|
value-field="id"
|
||||||
|
label="Country">
|
||||||
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
initial-data="$ctrl.client.province"
|
|
||||||
field="$ctrl.client.provinceFk"
|
field="$ctrl.client.provinceFk"
|
||||||
url="/client/api/Provinces"
|
url="/client/api/Provinces"
|
||||||
show-field="name"
|
show-field="name"
|
||||||
|
@ -57,15 +61,30 @@
|
||||||
label="Province">
|
label="Province">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
label="Web user"
|
||||||
|
field="$ctrl.client.userName">
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
label="Email"
|
||||||
|
field="$ctrl.client.email"
|
||||||
|
info="You can save multiple emails">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal pad-small-v>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="Is equalizated"
|
||||||
|
field="$ctrl.client.isEqualizated">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit label="Create"></vn-submit>
|
<vn-submit label="Create"></vn-submit>
|
||||||
<button
|
<vn-button ui-sref="client.index" label="Cancel"></vn-button>
|
||||||
class="mdl-button mdl-button--raised mdl-button--colored"
|
|
||||||
translate
|
|
||||||
ui-sref="client.index"
|
|
||||||
>Cancel
|
|
||||||
</button>
|
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
margin-medium-right
|
|
||||||
label="Credit"
|
label="Credit"
|
||||||
model="$ctrl.creditClassification.credit",
|
model="$ctrl.creditClassification.credit",
|
||||||
rule="CreditInsurance.credit"
|
rule="CreditInsurance.credit"
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
argin-medium-right
|
|
||||||
label="Grade"
|
label="Grade"
|
||||||
model="$ctrl.creditClassification.grade"
|
model="$ctrl.creditClassification.grade"
|
||||||
rule="CreditInsurance.grade">
|
rule="CreditInsurance.grade">
|
||||||
|
|
|
@ -13,6 +13,7 @@ describe('Client', () => {
|
||||||
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
controller = $componentController('vnClientCreditInsuranceIndex');
|
controller = $componentController('vnClientCreditInsuranceIndex');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ describe('Client', () => {
|
||||||
it('should define the classificationId property of the controller and then call the show method()', () => {
|
it('should define the classificationId property of the controller and then call the show method()', () => {
|
||||||
controller.$scope.closeContract = {show: () => {}};
|
controller.$scope.closeContract = {show: () => {}};
|
||||||
spyOn(controller.$scope.closeContract, 'show');
|
spyOn(controller.$scope.closeContract, 'show');
|
||||||
|
|
||||||
expect(controller.classificationId).toBeFalsy();
|
expect(controller.classificationId).toBeFalsy();
|
||||||
controller.closeContract({id: 1});
|
controller.closeContract({id: 1});
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title vn-one>Contract credit insurance</vn-title>
|
<vn-title vn-one>Contract credit insurance</vn-title>
|
||||||
<vn-horizontal ng-repeat="classification in $ctrl.classifications track by classification.id" class="pad-medium-top" style="align-items: center;">
|
<vn-horizontal ng-repeat="classification in $ctrl.classifications track by classification.id" class="pad-medium-top" style="align-items: center;">
|
||||||
<vn-one border-radius class="pad-small border-solid" ng-class="{'bg-dark-item': !classification.finished,'bg-opacity-item': classification.finished}">
|
<vn-one border-radius class="pad-small border-solid" ng-class="{'bg-main': !classification.finished,'bg-opacity-item': classification.finished}">
|
||||||
<vn-horizontal style="align-items: center;">
|
<vn-horizontal style="align-items: center;">
|
||||||
<vn-none pad-medium-h style="color:#FFA410;">
|
<vn-none pad-medium-h orange>
|
||||||
<i class="material-icons pointer"
|
<i class="material-icons pointer"
|
||||||
ng-if="!classification.finished"
|
ng-if="!classification.finished"
|
||||||
vn-tooltip="Close contract"
|
vn-tooltip="Close contract"
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
<a vn-auto ui-sref="client.card.creditInsurance.insurance.index({classificationId: {{classification.id}}})">
|
<a vn-auto ui-sref="client.card.creditInsurance.insurance.index({classificationId: {{classification.id}}})">
|
||||||
<vn-icon-button icon="desktop_windows" vn-tooltip="List classifications"></vn-icon-button>
|
<vn-icon-button icon="desktop_windows" vn-tooltip="View credits"></vn-icon-button>
|
||||||
</a>
|
</a>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
|
|
@ -2,5 +2,6 @@ Contract credit insurance: Contratos de seguro de crédito
|
||||||
New contract: Nuevo contrato
|
New contract: Nuevo contrato
|
||||||
Close contract: Cerrar contrato
|
Close contract: Cerrar contrato
|
||||||
Edit contract: Modificar contrato
|
Edit contract: Modificar contrato
|
||||||
List classifications: Ver clasificaciones
|
View credits: Ver créditos
|
||||||
Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato?
|
Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato?
|
||||||
|
Grade: Grado
|
|
@ -8,7 +8,7 @@
|
||||||
<form name="form"
|
<form name="form"
|
||||||
ng-submit="watcher.submitGo('client.card.creditInsurance.insurance.index', {classificationId: post.params.classificationId})">
|
ng-submit="watcher.submitGo('client.card.creditInsurance.insurance.index', {classificationId: post.params.classificationId})">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>New classification</vn-title>
|
<vn-title>New credit</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
|
@ -37,5 +37,9 @@
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit label="Save"></vn-submit>
|
<vn-submit label="Save"></vn-submit>
|
||||||
|
<button
|
||||||
|
class="mdl-button mdl-button--raised mdl-button--colored"
|
||||||
|
translate
|
||||||
|
ui-sref="client.card.creditInsurance.insurance.index({classificationId: post.params.classificationId})">Cancel</button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
|
@ -1,15 +1,14 @@
|
||||||
import ngModule from '../../../module';
|
import ngModule from '../../../module';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($state, $filter) {
|
constructor($filter) {
|
||||||
this.insurance = {
|
this.insurance = {
|
||||||
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
|
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
|
||||||
};
|
};
|
||||||
this.classificationId = $state.params.classificationId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$state', '$filter'];
|
Controller.$inject = ['$filter'];
|
||||||
|
|
||||||
ngModule.component('vnClientCreditInsuranceInsuranceCreate', {
|
ngModule.component('vnClientCreditInsuranceInsuranceCreate', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
New classification: Nueva clasificación
|
New credit: Nuevo crédito
|
|
@ -14,6 +14,7 @@ describe('Client', () => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
let $state = {params: {classificationId: 1}};
|
let $state = {params: {classificationId: 1}};
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
controller = $componentController('vnClientCreditInsuranceInsuranceIndex', {$state: $state});
|
controller = $componentController('vnClientCreditInsuranceInsuranceIndex', {$state: $state});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,38 @@
|
||||||
<mg-ajax path="/client/api/CreditInsurances/filter" options="vnIndexNonAuto"></mg-ajax>
|
<mg-ajax path="/client/api/CreditClassifications/{{index.params.classificationId}}/creditInsurances" options="vnIndex"></mg-ajax>
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-title>Requested credits</vn-title>
|
<vn-title>Requested credits</vn-title>
|
||||||
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
|
<table class="vn-grid">
|
||||||
<vn-column-header vn-one pad-medium-h field="credit" text="Credit"></vn-column-header>
|
<thead>
|
||||||
<vn-column-header vn-one pad-medium-h field="grade" text="Grade"></vn-column-header>
|
<tr>
|
||||||
<vn-column-header vn-two pad-medium-h field="created" text="Created" default-order="DESC"></vn-column-header>
|
<th number translate>Credit</th>
|
||||||
</vn-grid-header>
|
<th number translate>Grade</th>
|
||||||
<vn-one class="list list-content">
|
<th translate>Created</th>
|
||||||
<vn-horizontal
|
</tr>
|
||||||
vn-one class="list list-element text-center"
|
</thead>
|
||||||
pad-small-bottom
|
<tbody>
|
||||||
ng-repeat="insurance in index.model.instances track by insurance.id">
|
<tr ng-repeat="insurance in index.model track by insurance.id" class="list list-element">
|
||||||
<vn-one pad-medium-h>{{::insurance.credit}}</vn-one>
|
<td number>{{::insurance.credit | currency: '€': 2}}</td>
|
||||||
<vn-one pad-medium-h>{{::insurance.grade}}</vn-one>
|
<td number>{{::insurance.grade}}</td>
|
||||||
<vn-two pad-medium-h>{{::insurance.created | date: 'dd/MM/yyyy'}}</vn-two>
|
<td>{{::insurance.created | date: 'dd/MM/yyyy'}}</td>
|
||||||
</vn-horizontal>
|
</tr>
|
||||||
</vn-one>
|
<tr ng-if="index.model.count === 0" class="list list-element">
|
||||||
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
|
<td colspan="6" style="text-align: center" translate>No results</td>
|
||||||
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<button
|
||||||
|
class="mdl-button mdl-button--raised mdl-button--colored"
|
||||||
|
translate
|
||||||
|
ui-sref="client.card.creditInsurance.index">Back</button>
|
||||||
|
</vn-button-bar>
|
||||||
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
|
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
|
||||||
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
|
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
<a ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{index.params.classificationId}}})"
|
<a ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{index.params.classificationId}}})"
|
||||||
fixed-bottom-right vn-tooltip="New classification" vn-bind="+" ng-if="!$ctrl.isClosed">
|
fixed-bottom-right vn-tooltip="New credit" vn-bind="+" ng-if="!$ctrl.isClosed">
|
||||||
<vn-float-button icon="add"></vn-float-button>
|
<vn-float-button icon="add"></vn-float-button>
|
||||||
</a>
|
</a>
|
|
@ -34,6 +34,7 @@ describe('Client', () => {
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
$state.params.id = 101;
|
$state.params.id = 101;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
controller = $componentController('vnClientCreditCreate', {$scope: $scope}, {$state: $state});
|
controller = $componentController('vnClientCreditCreate', {$scope: $scope}, {$state: $state});
|
||||||
}));
|
}));
|
||||||
describe('onSubmit()', () => {
|
describe('onSubmit()', () => {
|
||||||
|
|
|
@ -13,6 +13,7 @@ describe('Descriptor', () => {
|
||||||
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
controller = $componentController('vnClientDescriptor');
|
controller = $componentController('vnClientDescriptor');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$http'];
|
Controller.$inject = ['$http'];
|
||||||
|
|
||||||
ngModule.component('vnClientDescriptor', {
|
ngModule.component('vnClientDescriptor', {
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('Client', () => {
|
||||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
controller = $componentController('vnClientFiscalData', {$scope: $scope});
|
controller = $componentController('vnClientFiscalData', {$scope: $scope});
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -24,13 +24,6 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-check
|
|
||||||
vn-one
|
|
||||||
label="Is equalizated"
|
|
||||||
field="$ctrl.client.isEqualizated"
|
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
|
||||||
</vn-check>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -40,13 +33,6 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="City"
|
|
||||||
field="$ctrl.client.city"
|
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -56,17 +42,15 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
initial-data="$ctrl.client.province"
|
label="City"
|
||||||
field="$ctrl.client.provinceFk"
|
field="$ctrl.client.city"
|
||||||
url="/client/api/Provinces"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
label="Province"
|
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-autocomplete>
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
initial-data="$ctrl.client.country"
|
initial-data="$ctrl.client.country"
|
||||||
|
@ -78,8 +62,19 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
initial-data="$ctrl.client.province"
|
||||||
|
field="$ctrl.client.provinceFk"
|
||||||
|
url="/client/api/Provinces"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
label="Province"
|
||||||
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal margin-small-bottom>
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Active"
|
label="Active"
|
||||||
|
@ -94,21 +89,8 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
|
||||||
vn-two
|
|
||||||
label="Invoice by address"
|
|
||||||
field="$ctrl.client.hasToInvoiceByAddress"
|
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
|
||||||
</vn-check>
|
|
||||||
<vn-check
|
|
||||||
vn-two
|
|
||||||
label="Verified data"
|
|
||||||
field="$ctrl.client.isTaxDataChecked"
|
|
||||||
vn-acl="administrative, salesAssistant, salesAssistant">
|
|
||||||
</vn-check>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Has to invoice"
|
label="Has to invoice"
|
||||||
|
@ -116,6 +98,15 @@
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="Vies"
|
||||||
|
field="$ctrl.client.isVies"
|
||||||
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Invoice by mail"
|
label="Invoice by mail"
|
||||||
|
@ -125,12 +116,27 @@
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Vies"
|
label="Invoice by address"
|
||||||
field="$ctrl.client.isVies"
|
field="$ctrl.client.hasToInvoiceByAddress"
|
||||||
vn-acl="administrative, salesAssistant, salesPerson"
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal pad-small-v>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="Is equalizated"
|
||||||
|
field="$ctrl.client.isEqualizated"
|
||||||
|
vn-acl="administrative, salesAssistant, salesPerson"
|
||||||
|
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
|
||||||
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="Verified data"
|
||||||
|
field="$ctrl.client.isTaxDataChecked"
|
||||||
|
vn-acl="administrative, salesAssistant, salesAssistant">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit
|
<vn-submit
|
||||||
|
|
|
@ -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="8"
|
||||||
|
data="clients"
|
||||||
|
auto-load="false">
|
||||||
|
</vn-crud-model>
|
||||||
<div margin-medium>
|
<div margin-medium>
|
||||||
<div class="vn-list">
|
<div class="vn-list">
|
||||||
<vn-card>
|
<vn-card pad-medium-h>
|
||||||
<vn-horizontal>
|
<vn-searchbar
|
||||||
<vn-searchbar vn-one
|
panel="vn-client-search-panel"
|
||||||
index="index"
|
model="model"
|
||||||
on-search="$ctrl.search(index)"
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
advanced="true"
|
|
||||||
popover="vn-client-search-panel"
|
|
||||||
ignore-keys = "['page', 'size', 'search']">
|
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-horizontal>
|
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card margin-medium-top>
|
<vn-card margin-medium-v>
|
||||||
<vn-item-client
|
<vn-item-client
|
||||||
ng-repeat="client in index.model.instances track by client.id"
|
ng-repeat="client in clients track by client.id"
|
||||||
client="client">
|
client="::client">
|
||||||
</vn-item-client>
|
</vn-item-client>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-paging index="index" total="index.model.count"></vn-paging>
|
<vn-pagination
|
||||||
<!-- <vn-auto-paging index="index" total="index.model.count" items="$ctrl.clients"></vn-auto-paging> -->
|
model="model"
|
||||||
|
scroll-selector="ui-view">
|
||||||
|
</vn-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a ui-sref="client.create" vn-bind="+" fixed-bottom-right>
|
<a ui-sref="client.create" vn-bind="+" fixed-bottom-right>
|
||||||
|
|
|
@ -2,23 +2,42 @@ import ngModule from '../module';
|
||||||
import './item-client';
|
import './item-client';
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
|
|
||||||
constructor($scope) {
|
constructor($scope) {
|
||||||
this.$scope = $scope;
|
this.$ = $scope;
|
||||||
this.clientSelected = null;
|
this.clientSelected = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(index) {
|
exprBuilder(param, value) {
|
||||||
index.accept();
|
switch (param) {
|
||||||
/* this.clients = [];
|
case 'search':
|
||||||
index.accept().then(res => {
|
return {
|
||||||
this.clients = res.instances;
|
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) {
|
openSummary(client) {
|
||||||
this.clientSelected = client;
|
this.clientSelected = client;
|
||||||
this.$scope.dialogSummaryClient.show();
|
this.$.dialogSummaryClient.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Controller.$inject = ['$scope'];
|
Controller.$inject = ['$scope'];
|
||||||
|
|
|
@ -3,3 +3,4 @@ Phone: Teléfono
|
||||||
Town/City: Ciudad
|
Town/City: Ciudad
|
||||||
Email: Correo electrónico
|
Email: Correo electrónico
|
||||||
Create client: Crear cliente
|
Create client: Crear cliente
|
||||||
|
View client: Ver cliente
|
|
@ -22,3 +22,4 @@ Credit contracts: Contratos de crédito
|
||||||
Verified data: Datos comprobados
|
Verified data: Datos comprobados
|
||||||
Mandate: Mandato
|
Mandate: Mandato
|
||||||
Amount: Importe
|
Amount: Importe
|
||||||
|
Back: Volver
|
|
@ -1,22 +1,22 @@
|
||||||
<div pad-large style="min-width: 30em">
|
<div pad-large style="min-width: 30em">
|
||||||
<form ng-submit="$ctrl.onSearch()">
|
<form ng-submit="$ctrl.onSearch()">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-one label="Client id" model="$ctrl.filter.id" vn-focus></vn-textfield>
|
<vn-textfield vn-one label="Client id" model="filter.id" vn-focus></vn-textfield>
|
||||||
<vn-textfield vn-one label="Tax number" model="$ctrl.filter.fi"></vn-textfield>
|
<vn-textfield vn-one label="Tax number" model="filter.fi"></vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<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-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-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-one label="Town/City" model="$ctrl.filter.city"></vn-textfield>
|
<vn-textfield vn-one label="Town/City" model="filter.city"></vn-textfield>
|
||||||
<vn-textfield vn-one label="Postcode" model="$ctrl.filter.postcode"></vn-textfield>
|
<vn-textfield vn-one label="Postcode" model="filter.postcode"></vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield vn-one label="Email" model="$ctrl.filter.email"></vn-textfield>
|
<vn-textfield vn-one label="Email" model="filter.email"></vn-textfield>
|
||||||
<vn-textfield vn-one label="Phone" model="$ctrl.filter.phone"></vn-textfield>
|
<vn-textfield vn-one label="Phone" model="filter.phone"></vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal margin-large-top>
|
<vn-horizontal margin-large-top>
|
||||||
<vn-submit label="Search"></vn-submit>
|
<vn-submit label="Search"></vn-submit>
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
import SearchPanel from 'core/src/components/searchbar/search-panel';
|
||||||
export default class Controller {
|
|
||||||
constructor() {
|
|
||||||
// onSubmit() is defined by @vnSearchbar
|
|
||||||
this.onSubmit = () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearch() {
|
|
||||||
this.onSubmit(this.filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Controller.$inject = [];
|
|
||||||
|
|
||||||
ngModule.component('vnClientSearchPanel', {
|
ngModule.component('vnClientSearchPanel', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller
|
controller: SearchPanel
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<form name="form" ng-submit="watcher.submit()">
|
<form name="form" ng-submit="watcher.submit()">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Web access</vn-title>
|
<vn-title>Web access</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal pad-small-v>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Enable web access"
|
label="Enable web access"
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-focus
|
vn-focus
|
||||||
vn-one
|
vn-one
|
||||||
margin-medium-top
|
|
||||||
label="User"
|
label="User"
|
||||||
field="$ctrl.account.name">
|
field="$ctrl.account.name">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
|
|
@ -62,6 +62,15 @@ export default class Autocomplete extends Input {
|
||||||
this.refreshDisplayed();
|
this.refreshDisplayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set data(value) {
|
||||||
|
this._data = value;
|
||||||
|
this.refreshSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
get data() {
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
selectionIsValid(selection) {
|
selectionIsValid(selection) {
|
||||||
return selection
|
return selection
|
||||||
&& selection[this.valueField] == this._field
|
&& selection[this.valueField] == this._field
|
||||||
|
@ -212,7 +221,7 @@ export default class Autocomplete extends Input {
|
||||||
showDropDown(search) {
|
showDropDown(search) {
|
||||||
Object.assign(this.$.dropDown.$.model, {
|
Object.assign(this.$.dropDown.$.model, {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
staticData: this.data
|
staticData: this._data
|
||||||
});
|
});
|
||||||
|
|
||||||
asignProps(this, this.$.dropDown, [
|
asignProps(this, this.$.dropDown, [
|
||||||
|
@ -220,9 +229,9 @@ export default class Autocomplete extends Input {
|
||||||
'showField',
|
'showField',
|
||||||
'where',
|
'where',
|
||||||
'order',
|
'order',
|
||||||
|
'limit',
|
||||||
'showFilter',
|
'showFilter',
|
||||||
'multiple',
|
'multiple',
|
||||||
'limit',
|
|
||||||
'$transclude'
|
'$transclude'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ describe('Component vnAutocomplete', () => {
|
||||||
};
|
};
|
||||||
let json = encodeURIComponent(JSON.stringify(filter));
|
let json = encodeURIComponent(JSON.stringify(filter));
|
||||||
|
|
||||||
|
$httpBackend.whenGET(`localhost?filter=${json}`).respond({});
|
||||||
$httpBackend.expectGET(`localhost?filter=${json}`);
|
$httpBackend.expectGET(`localhost?filter=${json}`);
|
||||||
controller.field = data.id;
|
controller.field = data.id;
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "colors";
|
@import "effects";
|
||||||
|
|
||||||
vn-autocomplete > div > .mdl-textfield {
|
vn-autocomplete > div > .mdl-textfield {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -44,13 +44,12 @@ ul.vn-autocomplete {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
@extend %clickable;
|
||||||
display: block;
|
display: block;
|
||||||
padding: .8em;
|
padding: .8em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.active,
|
&.active {
|
||||||
&:hover {
|
|
||||||
background-color: $hover;
|
background-color: $hover;
|
||||||
}
|
}
|
||||||
&.load-more {
|
&.load-more {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<div ng-transclude></div>
|
<div></div>
|
||||||
|
|
|
@ -7,8 +7,12 @@ export default function directive(vnTemplate) {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
transclude: true,
|
transclude: true,
|
||||||
template: require('./card.html'),
|
template: require('./card.html'),
|
||||||
link: function(_, $element) {
|
link: function($scope, $element, $attrs, $ctrl, $transclude) {
|
||||||
$element.addClass('demo-card-wide mdl-shadow--2dp bg-panel');
|
$element.addClass('demo-card-wide mdl-shadow--2dp bg-panel');
|
||||||
|
|
||||||
|
$transclude($scope, function(clone) {
|
||||||
|
angular.element($element[0].querySelector('div')).append(clone);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,18 @@ import './style.scss';
|
||||||
* @property {HTMLElement} buttons The dialog HTML buttons
|
* @property {HTMLElement} buttons The dialog HTML buttons
|
||||||
*/
|
*/
|
||||||
export default class Dialog extends Component {
|
export default class Dialog extends Component {
|
||||||
constructor($element, $transclude) {
|
constructor($element, $scope, $transclude) {
|
||||||
super($element);
|
super($element, $scope);
|
||||||
this.shown = false;
|
this.shown = false;
|
||||||
this.$element.addClass('vn-dialog');
|
this.$element.addClass('vn-dialog');
|
||||||
this.element.addEventListener('mousedown',
|
this.element.addEventListener('mousedown',
|
||||||
e => this.onBackgroundMouseDown(e));
|
e => this.onBackgroundMouseDown(e));
|
||||||
|
|
||||||
if ($transclude) {
|
if ($transclude) {
|
||||||
$transclude(tClone => {
|
$transclude($scope.$parent, tClone => {
|
||||||
this.body = tClone[0];
|
this.body = tClone[0];
|
||||||
}, null, 'body');
|
}, null, 'body');
|
||||||
$transclude(tClone => {
|
$transclude($scope.$parent, tClone => {
|
||||||
this.buttons = tClone[0];
|
this.buttons = tClone[0];
|
||||||
}, null, 'buttons');
|
}, null, 'buttons');
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ export default class Dialog extends Component {
|
||||||
clearTimeout(this.transitionTimeout);
|
clearTimeout(this.transitionTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dialog.$inject = ['$element', '$transclude'];
|
Dialog.$inject = ['$element', '$scope', '$transclude'];
|
||||||
|
|
||||||
ngModule.component('vnDialog', {
|
ngModule.component('vnDialog', {
|
||||||
template: require('./dialog.html'),
|
template: require('./dialog.html'),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "colors";
|
@import "effects";
|
||||||
|
|
||||||
.vn-dialog {
|
.vn-dialog {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: rgba(1, 1, 1, .6);
|
background-color: rgba(0, 0, 0, .6);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 300ms ease-in-out;
|
transition: opacity 300ms ease-in-out;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
& > div {
|
& > div {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 0 .4em rgba(1,1,1,.4);
|
box-shadow: 0 0 .4em rgba(0, 0, 0, .4);
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: .2em;
|
border-radius: .2em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -34,16 +34,11 @@
|
||||||
input[type="button"],
|
input[type="button"],
|
||||||
input[type="submit"],
|
input[type="submit"],
|
||||||
input[type="reset"] {
|
input[type="reset"] {
|
||||||
|
@extend %clickable;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 250ms;
|
|
||||||
border-radius: .1em;
|
border-radius: .1em;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(1,1,1,.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
& > button.close {
|
& > button.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<vn-model
|
<vn-rest-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
on-data-change="$ctrl.onModelDataChange()">
|
on-data-change="$ctrl.onModelDataChange()">
|
||||||
</vn-model>
|
</vn-rest-model>
|
||||||
<vn-popover
|
<vn-popover
|
||||||
vn-id="popover"
|
vn-id="popover"
|
||||||
on-open="$ctrl.onOpen()"
|
on-open="$ctrl.onOpen()"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import Component from '../../lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import './model';
|
|
||||||
|
|
||||||
export default class DropDown extends Component {
|
export default class DropDown extends Component {
|
||||||
constructor($element, $scope, $transclude, $timeout, $http) {
|
constructor($element, $scope, $transclude, $timeout, $http) {
|
||||||
|
@ -366,14 +365,18 @@ ngModule.component('vnDropDown', {
|
||||||
template: require('./drop-down.html'),
|
template: require('./drop-down.html'),
|
||||||
controller: DropDown,
|
controller: DropDown,
|
||||||
bindings: {
|
bindings: {
|
||||||
|
showField: '@?',
|
||||||
|
valueField: '@?',
|
||||||
|
where: '@?',
|
||||||
|
order: '@?',
|
||||||
|
limit: '<?',
|
||||||
|
showFilter: '<?',
|
||||||
|
multiple: '<?',
|
||||||
field: '=?',
|
field: '=?',
|
||||||
data: '<?',
|
data: '<?',
|
||||||
selection: '=?',
|
selection: '=?',
|
||||||
search: '<?',
|
search: '<?',
|
||||||
limit: '<?',
|
|
||||||
showFilter: '<?',
|
|
||||||
parent: '<?',
|
parent: '<?',
|
||||||
multiple: '<?',
|
|
||||||
onSelect: '&?'
|
onSelect: '&?'
|
||||||
},
|
},
|
||||||
transclude: {
|
transclude: {
|
||||||
|
|
|
@ -32,7 +32,7 @@ describe('Component vnDropDown', () => {
|
||||||
$scope.popover = $componentController('vnPopover', {$element: $popover, $scope, $timeout, $transitions});
|
$scope.popover = $componentController('vnPopover', {$element: $popover, $scope, $timeout, $transitions});
|
||||||
$scope.popover.$postLink();
|
$scope.popover.$postLink();
|
||||||
|
|
||||||
$scope.model = $componentController('vnModel', {$httpBackend, $q, $filter});
|
$scope.model = $componentController('vnRestModel', {$httpBackend, $q, $filter});
|
||||||
controller = $componentController('vnDropDown', {$element, $scope, $transclude: null, $timeout, $httpBackend, $translate: null});
|
controller = $componentController('vnDropDown', {$element, $scope, $transclude: null, $timeout, $httpBackend, $translate: null});
|
||||||
controller.$postLink();
|
controller.$postLink();
|
||||||
controller.parent = angular.element('<vn-parent></vn-parent>')[0];
|
controller.parent = angular.element('<vn-parent></vn-parent>')[0];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "colors";
|
@import "effects";
|
||||||
|
|
||||||
vn-drop-down {
|
vn-drop-down {
|
||||||
.dropdown {
|
.dropdown {
|
||||||
|
@ -47,19 +47,15 @@ vn-drop-down {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
li, .status {
|
li, .status {
|
||||||
|
@extend %clickable;
|
||||||
padding: .6em;
|
padding: .6em;
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: background-color 250ms ease-out;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
& > input[type=checkbox] {
|
& > input[type=checkbox] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-right: .6em;
|
margin-right: .6em;
|
||||||
}
|
}
|
||||||
&:hover {
|
|
||||||
background-color: rgba(0, 0, 0, .1);
|
|
||||||
}
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: #3D3A3B;
|
background-color: #3D3A3B;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
|
|
||||||
vn-grid-header {
|
vn-grid-header {
|
||||||
border-bottom: 3px solid $main-header;
|
border-bottom: 3px solid $lines;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
.orderly{
|
.orderly{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "colors";
|
@import "effects";
|
||||||
|
|
||||||
.vn-grid {
|
.vn-grid {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
@ -19,20 +19,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > thead, & > tbody {
|
& > thead, & > tbody {
|
||||||
border-bottom: 3px solid $main-header;
|
border-bottom: 3px solid $lines;
|
||||||
}
|
}
|
||||||
& > tbody > tr {
|
& > tbody > tr {
|
||||||
border-bottom: 1px solid $main-header;
|
border-bottom: 1px solid $lines;
|
||||||
transition: background-color 200ms ease-in-out;
|
transition: background-color 200ms ease-in-out;
|
||||||
|
|
||||||
&.clickable {
|
&.clickable {
|
||||||
cursor: pointer;
|
@extend %clickable;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $hover;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
background-color: rgba(163, 209, 49, 0.3);
|
background-color: rgba(163, 209, 49, 0.3);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import './style.scss';
|
||||||
|
|
||||||
export default class IconButton {
|
export default class IconButton {
|
||||||
constructor($element) {
|
constructor($element) {
|
||||||
|
if ($element[0].getAttribute('tabindex') == null)
|
||||||
$element[0].tabIndex = 0;
|
$element[0].tabIndex = 0;
|
||||||
$element.on("keyup", event => this.onKeyDown(event, $element));
|
$element.on("keyup", event => this.onKeyDown(event, $element));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,31 +2,16 @@
|
||||||
|
|
||||||
vn-icon-button {
|
vn-icon-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
|
||||||
color: rgba($main-01, 0.7);
|
color: rgba($main-01, 0.7);
|
||||||
|
font-size: 18pt;
|
||||||
transition: color 200ms ease-in-out;
|
transition: color 200ms ease-in-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&.button {
|
|
||||||
background-color: $main-01;
|
& > vn-icon {
|
||||||
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 {
|
|
||||||
display: block;
|
display: block;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
&:not(.button):hover {
|
&:not(.button):hover {
|
||||||
color: $main-01;
|
color: $main-01;
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
import './icon-menu.js';
|
|
||||||
|
|
||||||
describe('Component vnIconMenu', () => {
|
|
||||||
let $componentController;
|
|
||||||
let $element;
|
|
||||||
let $httpBackend;
|
|
||||||
let $timeout;
|
|
||||||
let $scope;
|
|
||||||
let controller;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
angular.mock.module('client');
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_) => {
|
|
||||||
$componentController = _$componentController_;
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
|
||||||
$timeout = _$timeout_;
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$element = angular.element('<div></div>');
|
|
||||||
controller = $componentController('vnIconMenu', {$scope, $element, $httpBackend, $timeout}, {url: 'test.com'});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('component vnIconMenu', () => {
|
|
||||||
describe('findItem()', () => {
|
|
||||||
it(`should return items empty array if the controller does not provide a url and have no items defined`, () => {
|
|
||||||
controller.url = undefined;
|
|
||||||
controller.items = undefined;
|
|
||||||
let result = controller.findItems('some search value');
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should return items array if the controller does not provide a url`, () => {
|
|
||||||
controller.url = undefined;
|
|
||||||
controller.items = ['Batman', 'Bruce Wayne'];
|
|
||||||
controller.findItems('some search value');
|
|
||||||
|
|
||||||
expect(controller.items.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should perform a search and store the result in controller items`, () => {
|
|
||||||
let search = 'The Joker';
|
|
||||||
let json = JSON.stringify({where: {name: {regexp: search}}});
|
|
||||||
$httpBackend.whenGET(`${controller.url}?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
|
|
||||||
$httpBackend.expectGET(`${controller.url}?filter=${json}`);
|
|
||||||
controller.findItems(search);
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.items[0]).toEqual({id: 3, name: 'The Joker'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should call getItems function if there's no search value`, () => {
|
|
||||||
spyOn(controller, 'getItems');
|
|
||||||
controller.findItems();
|
|
||||||
|
|
||||||
expect(controller.getItems).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getItems()', () => {
|
|
||||||
it(`should perform a query and then push elements found into controller.items`, () => {
|
|
||||||
controller.items = [];
|
|
||||||
$httpBackend.whenGET(`${controller.url}?filter={}`).respond([{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}]);
|
|
||||||
$httpBackend.expectGET(`${controller.url}?filter={}`);
|
|
||||||
controller.getItems();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.items).toEqual([{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should perform a query and then set controller.maxRow to false if there are no items in the controller`, () => {
|
|
||||||
controller.items = [];
|
|
||||||
controller.maxRow = true;
|
|
||||||
$httpBackend.whenGET(`${controller.url}?filter={"skip":0,"limit":true,"order":"name ASC"}`).respond(controller.items);
|
|
||||||
$httpBackend.expectGET(`${controller.url}?filter={"skip":0,"limit":true,"order":"name ASC"}`);
|
|
||||||
controller.getItems();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.maxRow).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,6 +1,8 @@
|
||||||
import './textfield/textfield';
|
import './model-proxy/model-proxy';
|
||||||
|
import './rest-model/crud-model';
|
||||||
|
import './rest-model/rest-model';
|
||||||
import './watcher/watcher';
|
import './watcher/watcher';
|
||||||
import './paging/paging';
|
import './textfield/textfield';
|
||||||
import './icon/icon';
|
import './icon/icon';
|
||||||
import './dialog/dialog';
|
import './dialog/dialog';
|
||||||
import './confirm/confirm';
|
import './confirm/confirm';
|
||||||
|
@ -31,4 +33,7 @@ import './switch/switch';
|
||||||
import './float-button/float-button';
|
import './float-button/float-button';
|
||||||
import './step-control/step-control';
|
import './step-control/step-control';
|
||||||
import './label-value/label-value';
|
import './label-value/label-value';
|
||||||
|
import './paging/paging';
|
||||||
import './auto-paging/auto-paging';
|
import './auto-paging/auto-paging';
|
||||||
|
import './pagination/pagination';
|
||||||
|
import './searchbar/searchbar';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
ngModule.component('vnLabelValue', {
|
ngModule.component('vnLabelValue', {
|
||||||
template: require('../label-value/label-value.html'),
|
template: require('./label-value.html'),
|
||||||
replace: true,
|
replace: true,
|
||||||
transclude: true,
|
transclude: true,
|
||||||
bindings: {
|
bindings: {
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
|
||||||
|
export default class ModelProxy {
|
||||||
|
constructor() {
|
||||||
|
this._data = [];
|
||||||
|
this.resetChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
get orgData() {
|
||||||
|
return this._orgData;
|
||||||
|
}
|
||||||
|
|
||||||
|
set orgData(value) {
|
||||||
|
this._orgData = value;
|
||||||
|
// this._data.splice(0, this._data.length);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
get data() {
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
set data(value) {}
|
||||||
|
|
||||||
|
get fields() {
|
||||||
|
return this._fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
set fields(value) {
|
||||||
|
this._fields = value;
|
||||||
|
|
||||||
|
let Row = function(data, index) {
|
||||||
|
this.$data = data;
|
||||||
|
this.$index = index;
|
||||||
|
this.$oldData = null;
|
||||||
|
this.$isNew = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let prop of value) {
|
||||||
|
Object.defineProperty(Row.prototype, prop, {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
set: function(value) {
|
||||||
|
if (!this.$isNew) {
|
||||||
|
if (!this.$oldData)
|
||||||
|
this.$oldData = {};
|
||||||
|
if (!this.$oldData[prop])
|
||||||
|
this.$oldData[prop] = this.$data[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$data[prop] = value;
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
return this.$data[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Row = Row;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(index) {
|
||||||
|
let data = this._data;
|
||||||
|
|
||||||
|
let item;
|
||||||
|
[item] = data.splice(index, 1);
|
||||||
|
|
||||||
|
for (let i = index; i < data.length; i++)
|
||||||
|
data[i].$index = i;
|
||||||
|
|
||||||
|
if (!item.$isNew)
|
||||||
|
this.removed.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(data) {
|
||||||
|
data = Object.assign(data || {}, this.link);
|
||||||
|
let newRow = new this.Row(data, this._data.length);
|
||||||
|
newRow.$isNew = true;
|
||||||
|
return this._data.push(newRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetChanges() {
|
||||||
|
this.removed = [];
|
||||||
|
|
||||||
|
for (let row of this._data) {
|
||||||
|
row.$oldData = null;
|
||||||
|
row.$isNew = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undoChanges() {
|
||||||
|
let data = this._data;
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let row = data[i];
|
||||||
|
|
||||||
|
if (row.$oldData)
|
||||||
|
Object.assign(row.$data, row.$oldData);
|
||||||
|
if (row.$isNew)
|
||||||
|
data.splice(i--, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let row of this.removed)
|
||||||
|
data.splice(row.$index, 0, row);
|
||||||
|
|
||||||
|
this.resetChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
dataChanged() {
|
||||||
|
if (this.onDataChange)
|
||||||
|
this.onDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.component('vnModelProxy', {
|
||||||
|
controller: ModelProxy,
|
||||||
|
bindings: {
|
||||||
|
orgData: '<?',
|
||||||
|
data: '=?',
|
||||||
|
fields: '<?',
|
||||||
|
link: '<?',
|
||||||
|
onDataChange: '&?'
|
||||||
|
}
|
||||||
|
});
|
|
@ -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>
|
|
@ -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
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
vn-pagination {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& > div > vn-icon-button {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import ModelProxy from '../model-proxy/model-proxy';
|
||||||
|
|
||||||
|
export default class CrudModel extends ModelProxy {
|
||||||
|
constructor($http, $q) {
|
||||||
|
super();
|
||||||
|
this.$http = $http;
|
||||||
|
this.$q = $q;
|
||||||
|
this.primaryKey = 'id';
|
||||||
|
this.autoLoad = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLoading() {
|
||||||
|
return this.canceler != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
if (this.autoLoad)
|
||||||
|
this.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRequest() {
|
||||||
|
if (this.canceler) {
|
||||||
|
this.canceler.resolve();
|
||||||
|
this.canceler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
let create = [];
|
||||||
|
let update = [];
|
||||||
|
let remove = [];
|
||||||
|
|
||||||
|
for (let row of this.removed)
|
||||||
|
remove.push(row.$data[this.primaryKey]);
|
||||||
|
|
||||||
|
for (let row of this._data) {
|
||||||
|
if (row.$isNew)
|
||||||
|
create.push(row.$data);
|
||||||
|
else if (row.$oldData)
|
||||||
|
update.push(row.$data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let isChanged =
|
||||||
|
create.length > 0 ||
|
||||||
|
update.length > 0 ||
|
||||||
|
remove.length > 0;
|
||||||
|
|
||||||
|
if (!isChanged)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let changes = {
|
||||||
|
create: create,
|
||||||
|
update: update,
|
||||||
|
delete: remove
|
||||||
|
};
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(ignoreChanges) {
|
||||||
|
let changes = this.getChanges();
|
||||||
|
|
||||||
|
if (!changes)
|
||||||
|
return this.$q.resolve();
|
||||||
|
|
||||||
|
let url = this.saveUrl ? this.saveUrl : `${this.url}/crud`;
|
||||||
|
return this.$http.post(url, changes)
|
||||||
|
.then(() => this.resetChanges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CrudModel.$inject = ['$http', '$q'];
|
||||||
|
|
||||||
|
ngModule.component('vnCrudModel', {
|
||||||
|
controller: CrudModel,
|
||||||
|
bindings: {
|
||||||
|
orgData: '<?',
|
||||||
|
data: '=?',
|
||||||
|
fields: '<?',
|
||||||
|
link: '<?',
|
||||||
|
url: '@?',
|
||||||
|
saveUrl: '@?',
|
||||||
|
where: '<?',
|
||||||
|
include: '<?',
|
||||||
|
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;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
|
|
||||||
export default class Model {
|
export default class RestModel {
|
||||||
constructor($http, $q, $filter) {
|
constructor($http, $q, $filter) {
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
|
@ -104,10 +104,10 @@ export default class Model {
|
||||||
this.onDataChange();
|
this.onDataChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Model.$inject = ['$http', '$q', '$filter'];
|
RestModel.$inject = ['$http', '$q', '$filter'];
|
||||||
|
|
||||||
ngModule.component('vnModel', {
|
ngModule.component('vnRestModel', {
|
||||||
controller: Model,
|
controller: RestModel,
|
||||||
bindings: {
|
bindings: {
|
||||||
url: '@?',
|
url: '@?',
|
||||||
staticData: '<?',
|
staticData: '<?',
|
|
@ -1,6 +1,6 @@
|
||||||
import './model.js';
|
import './rest-model.js';
|
||||||
|
|
||||||
describe('Component vnModel', () => {
|
describe('Component vnRestModel', () => {
|
||||||
let $componentController;
|
let $componentController;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
let controller;
|
let controller;
|
||||||
|
@ -11,7 +11,7 @@ describe('Component vnModel', () => {
|
||||||
|
|
||||||
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
controller = $componentController('vnModel', {$httpBackend});
|
controller = $componentController('vnRestModel', {$httpBackend});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('set url', () => {
|
describe('set url', () => {
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,11 @@
|
||||||
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
|
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
|
||||||
style="cursor: pointer; padding-top: 23px">
|
style="cursor: pointer; padding-top: 23px">
|
||||||
</vn-icon-button>
|
</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
|
<vn-icon
|
||||||
pad-medium-top
|
pad-medium-top
|
||||||
ng-if="$ctrl.advanced"
|
ng-if="$ctrl.panel"
|
||||||
ng-click="$ctrl.onpenFilters($event)"
|
ng-click="$ctrl.openPanel($event)"
|
||||||
icon="keyboard_arrow_down"
|
icon="keyboard_arrow_down"
|
||||||
style="cursor: pointer; color: #aaa">
|
style="cursor: pointer; color: #aaa">
|
||||||
</vn-icon>
|
</vn-icon>
|
|
@ -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
|
||||||
|
});
|
|
@ -1,7 +1,6 @@
|
||||||
vn-searchbar {
|
vn-searchbar {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
padding-left: 16px;
|
display: block;
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
& > form > vn-horizontal > vn-icon-button {
|
& > form > vn-horizontal > vn-icon-button {
|
||||||
color: black;
|
color: black;
|
|
@ -41,9 +41,8 @@ vn-snackbar > div {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $main-01;
|
color: $main-01;
|
||||||
padding: 1em;
|
padding: .5em;
|
||||||
margin: -1em;
|
margin: -.5em;
|
||||||
padding-left: 1.5em;
|
margin-left: .5em;
|
||||||
margin-left: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -26,7 +26,8 @@ export default class Tooltip extends Component {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.$element.addClass('show');
|
this.$element.addClass('show');
|
||||||
this.relocate();
|
this.relocate();
|
||||||
this.relocateTimeout = this.$timeout(() => this.relocate(), 200);
|
this.cancelTimeout();
|
||||||
|
this.relocateTimeout = this.$timeout(() => this.relocate(), 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +35,10 @@ export default class Tooltip extends Component {
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
this.$element.removeClass('show');
|
this.$element.removeClass('show');
|
||||||
|
this.cancelTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelTimeout() {
|
||||||
if (this.relocateTimeout) {
|
if (this.relocateTimeout) {
|
||||||
this.$timeout.cancel(this.relocateTimeout);
|
this.$timeout.cancel(this.relocateTimeout);
|
||||||
this.relocateTimeout = null;
|
this.relocateTimeout = null;
|
||||||
|
@ -102,6 +106,8 @@ export default class Tooltip extends Component {
|
||||||
}
|
}
|
||||||
calcCoords();
|
calcCoords();
|
||||||
|
|
||||||
|
// Overflow
|
||||||
|
|
||||||
let axisOverflow =
|
let axisOverflow =
|
||||||
axis == 'x' && (left < min || left > maxLeft) ||
|
axis == 'x' && (left < min || left > maxLeft) ||
|
||||||
axis == 'y' && (top < min || top > maxTop);
|
axis == 'y' && (top < min || top > maxTop);
|
||||||
|
@ -124,8 +130,6 @@ export default class Tooltip extends Component {
|
||||||
calcCoords();
|
calcCoords();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overflow
|
|
||||||
|
|
||||||
function range(coord, min, max) {
|
function range(coord, min, max) {
|
||||||
return Math.min(Math.max(coord, min), max);
|
return Math.min(Math.max(coord, min), max);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +190,7 @@ export default class Tooltip extends Component {
|
||||||
this.arrow = arrow;
|
this.arrow = arrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
$destroy() {
|
$onDestroy() {
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import getModifiedData from '../../lib/modified';
|
||||||
import copyObject from '../../lib/copy';
|
import copyObject from '../../lib/copy';
|
||||||
import isEqual from '../../lib/equals';
|
import isEqual from '../../lib/equals';
|
||||||
import isFullEmpty from '../../lib/full-empty';
|
import isFullEmpty from '../../lib/full-empty';
|
||||||
|
import UserError from '../../lib/user-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that checks for changes on a specific model property and
|
* Component that checks for changes on a specific model property and
|
||||||
|
@ -12,14 +13,15 @@ import isFullEmpty from '../../lib/full-empty';
|
||||||
* properties are provided.
|
* properties are provided.
|
||||||
*/
|
*/
|
||||||
export default class Watcher extends Component {
|
export default class Watcher extends Component {
|
||||||
constructor($element, $scope, $state, $transitions, $http, vnApp, $translate, $attrs) {
|
constructor($element, $scope, $state, $transitions, $http, vnApp, $translate, $attrs, $q) {
|
||||||
super($element);
|
super($element);
|
||||||
this.$scope = $scope;
|
this.$ = $scope;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$translate = $translate;
|
this._ = $translate;
|
||||||
this.$attrs = $attrs;
|
this.$attrs = $attrs;
|
||||||
this.vnApp = vnApp;
|
this.vnApp = vnApp;
|
||||||
|
this.$q = $q;
|
||||||
|
|
||||||
this.state = null;
|
this.state = null;
|
||||||
this.deregisterCallback = $transitions.onStart({},
|
this.deregisterCallback = $transitions.onStart({},
|
||||||
|
@ -28,34 +30,38 @@ export default class Watcher extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
if (this.get && this.url) {
|
if (this.get && this.url)
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
} else if (this.get && !this.url) {
|
else if (this.get && !this.url)
|
||||||
throw new Error('Error: Parameter url ommitted');
|
throw new Error('URL parameter ommitted');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$onChanges(changes) {
|
$onChanges(changes) {
|
||||||
if (this.data) {
|
if (this.data)
|
||||||
this.updateOriginalData();
|
this.updateOriginalData();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$onDestroy() {
|
$onDestroy() {
|
||||||
this.deregisterCallback();
|
this.deregisterCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get dirty() {
|
||||||
|
return this.form && this.form.$dirty || this.dataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
dataChanged() {
|
||||||
|
let data = this.copyInNewObject(this.data);
|
||||||
|
return !isEqual(data, this.orgData);
|
||||||
|
}
|
||||||
|
|
||||||
fetchData() {
|
fetchData() {
|
||||||
let id = this.data[this.idField];
|
let id = this.data[this.idField];
|
||||||
// return new Promise((resolve, reject) => {
|
return this.$http.get(`${this.url}/${id}`).then(
|
||||||
this.$http.get(`${this.url}/${id}`).then(
|
|
||||||
json => {
|
json => {
|
||||||
this.data = copyObject(json.data);
|
this.data = copyObject(json.data);
|
||||||
this.updateOriginalData();
|
this.updateOriginalData();
|
||||||
}
|
}
|
||||||
// json => reject(json)
|
|
||||||
);
|
);
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,9 +70,10 @@ export default class Watcher extends Component {
|
||||||
* @return {Promise} The request promise
|
* @return {Promise} The request promise
|
||||||
*/
|
*/
|
||||||
submitBack() {
|
submitBack() {
|
||||||
return this.submit().then(
|
return this.submit().then(res => {
|
||||||
() => this.window.history.back()
|
this.window.history.back();
|
||||||
);
|
return res;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,9 +84,10 @@ export default class Watcher extends Component {
|
||||||
* @return {Promise} The request promise
|
* @return {Promise} The request promise
|
||||||
*/
|
*/
|
||||||
submitGo(state, params) {
|
submitGo(state, params) {
|
||||||
return this.submit().then(
|
return this.submit().then(res => {
|
||||||
() => this.$state.go(state, params || {})
|
this.$state.go(state, params || {});
|
||||||
);
|
return res;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,89 +96,107 @@ export default class Watcher extends Component {
|
||||||
* @return {Promise} The http request promise
|
* @return {Promise} The http request promise
|
||||||
*/
|
*/
|
||||||
submit() {
|
submit() {
|
||||||
if (this.form) {
|
try {
|
||||||
|
this.check();
|
||||||
|
} catch (err) {
|
||||||
|
return this.$q.reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.realSubmit().then(res => {
|
||||||
|
this.notifySaved();
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the data without checking data validity or changes.
|
||||||
|
*
|
||||||
|
* @return {Promise} The http request promise
|
||||||
|
*/
|
||||||
|
realSubmit() {
|
||||||
|
if (this.form)
|
||||||
this.form.$setSubmitted();
|
this.form.$setSubmitted();
|
||||||
|
|
||||||
if (!this.form.$valid)
|
|
||||||
return new Promise(
|
|
||||||
(resolve, reject) => this.invalidForm(reject)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!this.dataChanged()) {
|
if (!this.dataChanged()) {
|
||||||
return new Promise(
|
this.updateOriginalData();
|
||||||
(resolve, reject) => this.noChanges(reject)
|
return this.$q.resolve();
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isPost = (this.$attrs.save && this.$attrs.save.toLowerCase() === 'post');
|
let isPost = (this.$attrs.save && this.$attrs.save.toLowerCase() === 'post');
|
||||||
let changedData = isPost
|
let changedData = isPost
|
||||||
? this.data
|
? this.data
|
||||||
: getModifiedData(this.data, this.orgData);
|
: getModifiedData(this.data, this.orgData);
|
||||||
|
|
||||||
if (this.requiredField && !changedData[this.requiredField]) {
|
|
||||||
let required = this.data[this.requiredField] || this.orgData[this.requiredField];
|
|
||||||
if (required === undefined) {
|
|
||||||
return new Promise(
|
|
||||||
(resolve, reject) => this.invalidForm(reject)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
changedData[this.requiredField] = required;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.save && this.save.accept) {
|
|
||||||
this.save.model = changedData; // this.copyInNewObject(changedData);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.save.accept().then(
|
|
||||||
json => this.writeData({data: json}, resolve),
|
|
||||||
json => reject(json)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: Alternative when mgCrud is not used
|
|
||||||
|
|
||||||
let id = this.idField ? this.orgData[this.idField] : null;
|
let id = this.idField ? this.orgData[this.idField] : null;
|
||||||
|
|
||||||
if (id) {
|
// If watcher is associated to mgCrud
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.$http.patch(`${this.url}/${id}`, changedData).then(
|
if (this.save && this.save.accept) {
|
||||||
json => this.writeData(json, resolve),
|
if (id)
|
||||||
json => reject(json)
|
changedData[this.idField] = id;
|
||||||
|
|
||||||
|
this.save.model = changedData;
|
||||||
|
return this.$q((resolve, reject) => {
|
||||||
|
this.save.accept().then(
|
||||||
|
json => this.writeData({data: json}, resolve),
|
||||||
|
reject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
// When mgCrud is not used
|
||||||
this.$http.post(this.url, changedData).then(
|
|
||||||
|
if (id) {
|
||||||
|
return this.$q((resolve, reject) => {
|
||||||
|
this.$http.patch(`${this.url}/${id}`, changedData).then(
|
||||||
json => this.writeData(json, resolve),
|
json => this.writeData(json, resolve),
|
||||||
json => reject(json)
|
reject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.$q((resolve, reject) => {
|
||||||
|
this.$http.post(this.url, changedData).then(
|
||||||
|
json => this.writeData(json, resolve),
|
||||||
|
reject
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if data is ready to send.
|
||||||
|
*/
|
||||||
|
check() {
|
||||||
|
if (this.form && this.form.$invalid)
|
||||||
|
throw new UserError(this._.instant('Some fields are invalid'));
|
||||||
|
if (!this.dirty)
|
||||||
|
throw new UserError(this._.instant('No changes to save'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the user that the data has been saved.
|
||||||
|
*/
|
||||||
|
notifySaved() {
|
||||||
|
this.vnApp.showMessage(this._.instant('Data saved!'));
|
||||||
|
}
|
||||||
|
|
||||||
writeData(json, resolve) {
|
writeData(json, resolve) {
|
||||||
Object.assign(this.data, json.data);
|
Object.assign(this.data, json.data);
|
||||||
this.updateOriginalData();
|
this.updateOriginalData();
|
||||||
resolve(json);
|
resolve(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
noChanges(reject) {
|
|
||||||
this.vnApp.showMessage(
|
|
||||||
this.$translate.instant('No changes to save')
|
|
||||||
);
|
|
||||||
reject(new Error('No changes to save'));
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidForm(reject) {
|
|
||||||
this.vnApp.showMessage(
|
|
||||||
this.$translate.instant('Some fields are invalid')
|
|
||||||
);
|
|
||||||
reject(new Error('Some fields are invalid'));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOriginalData() {
|
updateOriginalData() {
|
||||||
this.orgData = this.copyInNewObject(this.data);
|
this.orgData = this.copyInNewObject(this.data);
|
||||||
if (this.form && this.form.$dirty)
|
this.setPristine();
|
||||||
this.form.$setPristine();
|
}
|
||||||
|
|
||||||
|
setPristine() {
|
||||||
|
if (this.form) this.form.$setPristine();
|
||||||
|
}
|
||||||
|
|
||||||
|
setDirty() {
|
||||||
|
if (this.form) this.form.$setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
copyInNewObject(data) {
|
copyInNewObject(data) {
|
||||||
|
@ -192,22 +218,15 @@ export default class Watcher extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(transition) {
|
callback(transition) {
|
||||||
let dataChanged = this.dataChanged();
|
if (!this.state && this.dirty) {
|
||||||
if (!this.state && dataChanged) {
|
|
||||||
this.state = transition.to().name;
|
this.state = transition.to().name;
|
||||||
this.$scope.confirm.show();
|
this.$.confirm.show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataChanged() {
|
|
||||||
let newData = this.copyInNewObject(this.data);
|
|
||||||
if (this.form && this.form.$dirty) return !isEqual(newData, this.orgData);
|
|
||||||
return !isEqual(newData, this.orgData);
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfirmResponse(response) {
|
onConfirmResponse(response) {
|
||||||
if (response === 'ACCEPT') {
|
if (response === 'ACCEPT') {
|
||||||
if (this.data)
|
if (this.data)
|
||||||
|
@ -218,18 +237,17 @@ export default class Watcher extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Watcher.$inject = ['$element', '$scope', '$state', '$transitions', '$http', 'vnApp', '$translate', '$attrs'];
|
Watcher.$inject = ['$element', '$scope', '$state', '$transitions', '$http', 'vnApp', '$translate', '$attrs', '$q'];
|
||||||
|
|
||||||
ngModule.component('vnWatcher', {
|
ngModule.component('vnWatcher', {
|
||||||
template: require('./watcher.html'),
|
template: require('./watcher.html'),
|
||||||
bindings: {
|
bindings: {
|
||||||
url: '@?',
|
url: '@?',
|
||||||
idField: '@?',
|
idField: '@?',
|
||||||
requiredField: '@?',
|
|
||||||
data: '<',
|
data: '<',
|
||||||
form: '<',
|
form: '<',
|
||||||
save: '<',
|
save: '<',
|
||||||
get: '=?'
|
get: '<?'
|
||||||
},
|
},
|
||||||
controller: Watcher
|
controller: Watcher
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import getModifiedData from '../../lib/modified';
|
||||||
|
|
||||||
describe('Component vnWatcher', () => {
|
describe('Component vnWatcher', () => {
|
||||||
let $componentController;
|
let $componentController;
|
||||||
|
let $rootScope;
|
||||||
let $scope;
|
let $scope;
|
||||||
let $element;
|
let $element;
|
||||||
let $state;
|
let $state;
|
||||||
|
@ -12,25 +13,28 @@ describe('Component vnWatcher', () => {
|
||||||
let $translate;
|
let $translate;
|
||||||
let controller;
|
let controller;
|
||||||
let $attrs;
|
let $attrs;
|
||||||
|
let $q;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
angular.mock.module('client');
|
angular.mock.module('client');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_, _$transitions_, _$httpBackend_, _vnApp_, _$translate_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$rootScope_, _$state_, _$transitions_, _$httpBackend_, _vnApp_, _$translate_, _$q_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
|
$rootScope = _$rootScope_;
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$element = angular.element('<div></div>');
|
$element = angular.element('<div></div>');
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
vnApp = _vnApp_;
|
vnApp = _vnApp_;
|
||||||
$transitions = _$transitions_;
|
$transitions = _$transitions_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
|
||||||
$translate = _$translate_;
|
$translate = _$translate_;
|
||||||
|
$q = _$q_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$attrs = {
|
$attrs = {
|
||||||
save: "patch"
|
save: "patch"
|
||||||
};
|
};
|
||||||
controller = $componentController('vnWatcher', {$scope, $element, $state, vnApp, $transitions, $httpBackend, $translate, $attrs});
|
controller = $componentController('vnWatcher', {$scope, $element, $state, vnApp, $transitions, $httpBackend, $translate, $attrs, $q});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('$onInit()', () => {
|
describe('$onInit()', () => {
|
||||||
|
@ -48,26 +52,7 @@ describe('Component vnWatcher', () => {
|
||||||
|
|
||||||
expect(function() {
|
expect(function() {
|
||||||
controller.$onInit();
|
controller.$onInit();
|
||||||
}).toThrow(new Error('Error: Parameter url ommitted'));
|
}).toThrowError(/parameter/);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('$onChanges()', () => {
|
|
||||||
it(`should call updateOriginalData() if controllers data is defined`, () => {
|
|
||||||
controller.data = [];
|
|
||||||
spyOn(controller, 'updateOriginalData');
|
|
||||||
controller.$onChanges();
|
|
||||||
|
|
||||||
expect(controller.updateOriginalData).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('$onDestroy()', () => {
|
|
||||||
it(`should call deregisterCallback()`, () => {
|
|
||||||
spyOn(controller, 'deregisterCallback');
|
|
||||||
controller.$onDestroy();
|
|
||||||
|
|
||||||
expect(controller.deregisterCallback).toHaveBeenCalledWith();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -115,61 +100,51 @@ describe('Component vnWatcher', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('submit()', () => {
|
describe('check()', () => {
|
||||||
|
it(`should throw error if controller.form is invalid`, () => {
|
||||||
|
controller.form = {$invalid: true};
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
controller.check();
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should throw error if controller.dirty is true`, () => {
|
||||||
|
controller.form = {$invalid: true};
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
controller.check();
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('realSubmit()', () => {
|
||||||
describe('when controller.form', () => {
|
describe('when controller.form', () => {
|
||||||
it(`should call controller.form.setSubminted if controller.form is defined`, () => {
|
it(`should call controller.form.setSubmited if controller.form is defined`, () => {
|
||||||
controller.form = {$setSubmitted: () => {}};
|
controller.form = {
|
||||||
|
$setSubmitted: () => {},
|
||||||
|
$setPristine: () => {}
|
||||||
|
};
|
||||||
spyOn(controller.form, '$setSubmitted');
|
spyOn(controller.form, '$setSubmitted');
|
||||||
controller.submit();
|
controller.realSubmit();
|
||||||
|
|
||||||
expect(controller.form.$setSubmitted).toHaveBeenCalledWith();
|
expect(controller.form.$setSubmitted).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should call controller.invalidForm if controller.form.$valid is not defined`, () => {
|
|
||||||
controller.form = {$setSubmitted: () => {}};
|
|
||||||
spyOn(controller, 'invalidForm');
|
|
||||||
controller.submit();
|
|
||||||
|
|
||||||
expect(controller.invalidForm).toHaveBeenCalledWith(jasmine.any(Function));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when !controller.dataChanged()', () => {
|
|
||||||
it(`should call controller.noChanges()`, () => {
|
|
||||||
spyOn(controller, 'noChanges');
|
|
||||||
controller.submit();
|
|
||||||
|
|
||||||
expect(controller.noChanges).toHaveBeenCalledWith(jasmine.any(Function));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when controller.save()', () => {
|
describe('when controller.save()', () => {
|
||||||
it(`should set controller.save.model property`, () => {
|
it(`should set controller.save.model property`, () => {
|
||||||
controller.save = {accept: () => {}};
|
controller.save = {accept: () => $q.resolve()};
|
||||||
controller.data = {originalInfo: 'original data', info: 'new data'};
|
controller.data = {originalInfo: 'original data', info: 'new data'};
|
||||||
controller.orgData = {originalInfo: 'original data'};
|
controller.orgData = {originalInfo: 'original data'};
|
||||||
controller.submit();
|
controller.realSubmit();
|
||||||
|
|
||||||
expect(controller.save.model).toEqual({info: 'new data'});
|
expect(controller.save.model).toEqual({info: 'new data'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should call controller.save.accept() then controller.writeData`, done => {
|
|
||||||
controller.save = {accept: () => {}};
|
|
||||||
controller.data = {originalInfo: 'original data', info: 'new data'};
|
|
||||||
controller.orgData = {originalInfo: 'original data'};
|
|
||||||
spyOn(controller.save, 'accept').and.returnValue(Promise.resolve());
|
|
||||||
spyOn(controller, 'writeData').and.callThrough();
|
|
||||||
controller.submit()
|
|
||||||
.then(() => {
|
|
||||||
expect(controller.save.accept).toHaveBeenCalledWith();
|
|
||||||
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when id is defined', () => {
|
describe('when id is defined', () => {
|
||||||
it(`should perform a query then call controller.writeData()`, () => {
|
it(`should perform a query then call controller.writeData()`, done => {
|
||||||
controller.dataChanged = () => {
|
controller.dataChanged = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -182,7 +157,7 @@ describe('Component vnWatcher', () => {
|
||||||
spyOn(controller, 'writeData').and.callThrough();
|
spyOn(controller, 'writeData').and.callThrough();
|
||||||
$httpBackend.whenPATCH(`${controller.url}/1`, changedData).respond(json);
|
$httpBackend.whenPATCH(`${controller.url}/1`, changedData).respond(json);
|
||||||
$httpBackend.expectPATCH(`${controller.url}/1`);
|
$httpBackend.expectPATCH(`${controller.url}/1`);
|
||||||
controller.submit()
|
controller.realSubmit()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
|
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
|
||||||
done();
|
done();
|
||||||
|
@ -191,7 +166,7 @@ describe('Component vnWatcher', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should perform a POST query then call controller.writeData()`, () => {
|
it(`should perform a POST query then call controller.writeData()`, done => {
|
||||||
controller.dataChanged = () => {
|
controller.dataChanged = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -202,7 +177,7 @@ describe('Component vnWatcher', () => {
|
||||||
spyOn(controller, 'writeData').and.callThrough();
|
spyOn(controller, 'writeData').and.callThrough();
|
||||||
$httpBackend.whenPOST(`${controller.url}`, controller.data).respond(json);
|
$httpBackend.whenPOST(`${controller.url}`, controller.data).respond(json);
|
||||||
$httpBackend.expectPOST(`${controller.url}`, controller.data);
|
$httpBackend.expectPOST(`${controller.url}`, controller.data);
|
||||||
controller.submit()
|
controller.realSubmit()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
|
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
|
||||||
done();
|
done();
|
||||||
|
@ -224,18 +199,9 @@ describe('Component vnWatcher', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('copyInNewObject()', () => {
|
|
||||||
it(`should return newCopy object if data was an object`, () => {
|
|
||||||
let data = {id: 1, Heroname: 'Batman', name: 'Bruce Wayne'};
|
|
||||||
let result = controller.copyInNewObject(data);
|
|
||||||
|
|
||||||
expect(result).toEqual(data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('callback()', () => {
|
describe('callback()', () => {
|
||||||
describe(`when dataChanged() returns true and there's no state in the controller`, () => {
|
describe(`when dataChanged() returns true and there's no state in the controller`, () => {
|
||||||
it(`should define controller.state, call controller.$scope.confirm.show() and return false`, () => {
|
it(`should define controller.state, call controller.$.confirm.show() and return false`, () => {
|
||||||
$scope.confirm = {show: jasmine.createSpy('show')};
|
$scope.confirm = {show: jasmine.createSpy('show')};
|
||||||
controller.dataChanged = () => {
|
controller.dataChanged = () => {
|
||||||
return true;
|
return true;
|
||||||
|
@ -247,7 +213,7 @@ describe('Component vnWatcher', () => {
|
||||||
let result = controller.callback(transition);
|
let result = controller.callback(transition);
|
||||||
|
|
||||||
expect(controller.state).toEqual('Batman');
|
expect(controller.state).toEqual('Batman');
|
||||||
expect(controller.$scope.confirm.show).toHaveBeenCalledWith();
|
expect(controller.$.confirm.show).toHaveBeenCalledWith();
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,9 +9,10 @@ describe('Directive zoomImage', () => {
|
||||||
angular.mock.module('client');
|
angular.mock.module('client');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($compile, $rootScope) => {
|
beforeEach(angular.mock.inject(($compile, $rootScope, $httpBackend) => {
|
||||||
compile = $compile;
|
compile = $compile;
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
@ -7,9 +7,9 @@ import ngModule from '../module';
|
||||||
* @property {Snackbar} snackbar The main object to show messages.
|
* @property {Snackbar} snackbar The main object to show messages.
|
||||||
*/
|
*/
|
||||||
export default class App {
|
export default class App {
|
||||||
constructor($rootScope) {
|
constructor() {
|
||||||
this.loaderStatus = 0;
|
this.loaderStatus = 0;
|
||||||
this.$rootScope = $rootScope;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
if (this.snackbar)
|
if (this.snackbar)
|
||||||
|
@ -17,19 +17,18 @@ export default class App {
|
||||||
}
|
}
|
||||||
showError(message) {
|
showError(message) {
|
||||||
if (this.snackbar)
|
if (this.snackbar)
|
||||||
this.snackbar.showError({message: `Error: ${message}`});
|
this.snackbar.showError({message: message});
|
||||||
}
|
}
|
||||||
pushLoader() {
|
pushLoader() {
|
||||||
this.loaderStatus++;
|
this.loaderStatus++;
|
||||||
if (this.loaderStatus === 1)
|
if (this.loaderStatus === 1)
|
||||||
this.$rootScope.loading = true;
|
this.loading = true;
|
||||||
}
|
}
|
||||||
popLoader() {
|
popLoader() {
|
||||||
this.loaderStatus--;
|
this.loaderStatus--;
|
||||||
if (this.loaderStatus === 0)
|
if (this.loaderStatus === 0)
|
||||||
this.$rootScope.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
App.$inject = ['$rootScope'];
|
|
||||||
|
|
||||||
ngModule.service('vnApp', App);
|
ngModule.service('vnApp', App);
|
||||||
|
|
|
@ -12,10 +12,10 @@ export function toJsonDate(date) {
|
||||||
let year = date.getFullYear();
|
let year = date.getFullYear();
|
||||||
|
|
||||||
if (day < 10)
|
if (day < 10)
|
||||||
day = `0${day}`
|
day = `0${day}`;
|
||||||
|
|
||||||
if (month < 10)
|
if (month < 10)
|
||||||
month = `0${month}`
|
month = `0${month}`;
|
||||||
|
|
||||||
return new Date(`${year}-${month}-${day}`);
|
return new Date(`${year}-${month}-${day}`);
|
||||||
}
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* Wraps $http error responses. This class is mainly used to
|
||||||
|
* avoid the default AngularJS behaviour, that is, stringifying all
|
||||||
|
* unhandled rejections that aren't @Error objects. More info at:
|
||||||
|
* - https://github.com/angular/angular.js/issues/14631
|
||||||
|
*/
|
||||||
|
export default class HttpError extends Error {
|
||||||
|
constructor(message, fileName, lineNumber) {
|
||||||
|
super(message, fileName, lineNumber);
|
||||||
|
this.name = 'HttpError';
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,3 +13,5 @@ import './equals';
|
||||||
import './modified';
|
import './modified';
|
||||||
import './key-codes';
|
import './key-codes';
|
||||||
import './get-watchers';
|
import './get-watchers';
|
||||||
|
import './http-error';
|
||||||
|
import './user-error';
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
import HttpError from './http-error';
|
||||||
|
|
||||||
interceptor.$inject = ['$q', '$window', 'vnApp', '$translate', '$cookies'];
|
interceptor.$inject = ['$q', 'vnApp', '$cookies'];
|
||||||
function interceptor($q, $window, vnApp, $translate, $cookies) {
|
function interceptor($q, vnApp, $cookies) {
|
||||||
return {
|
return {
|
||||||
request: function(config) {
|
request: function(config) {
|
||||||
vnApp.pushLoader();
|
vnApp.pushLoader();
|
||||||
|
@ -16,42 +17,14 @@ function interceptor($q, $window, vnApp, $translate, $cookies) {
|
||||||
return $q.reject(rejection);
|
return $q.reject(rejection);
|
||||||
},
|
},
|
||||||
response: function(response) {
|
response: function(response) {
|
||||||
switch (response.config.method) {
|
|
||||||
case 'PUT':
|
|
||||||
case 'POST':
|
|
||||||
case 'PATCH':
|
|
||||||
vnApp.showMessage($translate.instant('Data saved!'));
|
|
||||||
}
|
|
||||||
vnApp.popLoader();
|
vnApp.popLoader();
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
responseError: function(rejection) {
|
responseError: function(rejection) {
|
||||||
vnApp.popLoader();
|
vnApp.popLoader();
|
||||||
let data = rejection.data;
|
let err = new HttpError(rejection.statusText);
|
||||||
let error;
|
Object.assign(err, rejection);
|
||||||
|
return $q.reject(err);
|
||||||
switch (rejection.xhrStatus) {
|
|
||||||
case 'timeout':
|
|
||||||
case 'abort':
|
|
||||||
return $q.reject(rejection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data && data.error instanceof Object)
|
|
||||||
error = data.error.message;
|
|
||||||
else if (rejection.status === -1)
|
|
||||||
error = $translate.instant(`Can't contact with server`);
|
|
||||||
else
|
|
||||||
error = `${rejection.status}: ${rejection.statusText}`;
|
|
||||||
|
|
||||||
if (rejection.status === 401) {
|
|
||||||
let location = $window.location;
|
|
||||||
let continueUrl = location.pathname + location.search + location.hash;
|
|
||||||
continueUrl = encodeURIComponent(continueUrl);
|
|
||||||
$window.location = `/auth/?apiKey=${vnApp.name}&continue=${continueUrl}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
vnApp.showError(error);
|
|
||||||
return $q.reject(rejection);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Errors that can be visible and understood by the end user.
|
||||||
|
* Used mainly in the global error handler.
|
||||||
|
*/
|
||||||
|
export default class UserError extends Error {
|
||||||
|
constructor(message, fileName, lineNumber) {
|
||||||
|
super(message, fileName, lineNumber);
|
||||||
|
this.name = 'UserError';
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,4 +11,5 @@ Hide: Hide
|
||||||
Next: Next
|
Next: Next
|
||||||
Finalize: Finalize
|
Finalize: Finalize
|
||||||
Previous: Back
|
Previous: Back
|
||||||
|
Load more: Load more
|
||||||
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search
|
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search
|
|
@ -11,4 +11,5 @@ Hide: Ocultar
|
||||||
Next: Siguiente
|
Next: Siguiente
|
||||||
Finalize: Finalizar
|
Finalize: Finalizar
|
||||||
Previous: Anterior
|
Previous: Anterior
|
||||||
|
Load more: Cargar más
|
||||||
Auto-scroll interrupted, please adjust the search: Auto-scroll interrumpido, por favor ajusta la búsqueda
|
Auto-scroll interrupted, please adjust the search: Auto-scroll interrumpido, por favor ajusta la búsqueda
|
|
@ -20,5 +20,5 @@
|
||||||
.icon-ticket:before { content: '\e821'; } /* '' */
|
.icon-ticket:before { content: '\e821'; } /* '' */
|
||||||
.icon-tax:before { content: '\e822'; } /* '' */
|
.icon-tax:before { content: '\e822'; } /* '' */
|
||||||
.icon-no036:before { content: '\e823'; } /* '' */
|
.icon-no036:before { content: '\e823'; } /* '' */
|
||||||
.icon-mana:before { content: '\e824'; } /* '' */
|
.icon-transaction:before { content: '\e826'; } /* '' */
|
||||||
.icon-solunion:before { content: '\e827'; } /* '' */
|
.icon-solunion:before { content: '\e827'; } /* '' */
|
Binary file not shown.
|
@ -2,4 +2,4 @@ import './mdl-override.scss';
|
||||||
import './mdi-override.css';
|
import './mdi-override.css';
|
||||||
import './zoom-image.scss';
|
import './zoom-image.scss';
|
||||||
import './fontello-head.css';
|
import './fontello-head.css';
|
||||||
import './fontello-icons.css';
|
import './fontello-codes.css';
|
||||||
|
|
|
@ -109,6 +109,18 @@
|
||||||
"params": {
|
"params": {
|
||||||
"item": "$ctrl.item"
|
"item": "$ctrl.item"
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
"url" : "/diary",
|
||||||
|
"state": "item.card.diary",
|
||||||
|
"component": "vn-item-diary",
|
||||||
|
"params": {
|
||||||
|
"item": "$ctrl.item"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"description": "Diary",
|
||||||
|
"icon": "icon-transaction"
|
||||||
|
},
|
||||||
|
"acl": ["employee"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -15,8 +15,12 @@ describe('Item', () => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$state.params.id = '1';
|
$state.params.id = '1';
|
||||||
controller = $componentController('vnItemBarcode', {$state: $state});
|
controller = $componentController('vnItemBarcode', {$state: $state});
|
||||||
|
controller.$scope.watcher = {
|
||||||
|
notifySaved: () => {}
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('add / remove barcode()', () => {
|
describe('add / remove barcode()', () => {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
form="form">
|
||||||
|
</vn-watcher>
|
||||||
<form name="form" ng-submit="$ctrl.submit()">
|
<form name="form" ng-submit="$ctrl.submit()">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>Item barcode</vn-title>
|
<vn-title>Item barcode</vn-title>
|
||||||
|
|
|
@ -95,6 +95,7 @@ export default class Controller {
|
||||||
return this.$http.post(`/item/api/ItemBarcodes/crudItemBarcodes`, barcodesObj).then(() => {
|
return this.$http.post(`/item/api/ItemBarcodes/crudItemBarcodes`, barcodesObj).then(() => {
|
||||||
this.getBarcodes();
|
this.getBarcodes();
|
||||||
this._unsetDirtyForm();
|
this._unsetDirtyForm();
|
||||||
|
this.$scope.watcher.notifySaved();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
|
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('ItemBotanical', () => {
|
||||||
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$state = {
|
$state = {
|
||||||
params: {
|
params: {
|
||||||
id: 123
|
id: 123
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
vn-id="watcher"
|
vn-id="watcher"
|
||||||
data="$ctrl.botanical"
|
data="$ctrl.botanical"
|
||||||
id-field="itemFk"
|
id-field="itemFk"
|
||||||
required-field="itemFk"
|
|
||||||
form="form"
|
form="form"
|
||||||
save="patch">
|
save="patch">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
|
|
|
@ -4,6 +4,9 @@ class Controller {
|
||||||
constructor($http, $state) {
|
constructor($http, $state) {
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
|
this.botanical = {
|
||||||
|
itemFk: this.$state.params.id
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getBotanical() {
|
_getBotanical() {
|
||||||
|
@ -13,16 +16,8 @@ class Controller {
|
||||||
};
|
};
|
||||||
this.$http.get(`/item/api/ItemBotanicals?filter=${JSON.stringify(filter)}`)
|
this.$http.get(`/item/api/ItemBotanicals?filter=${JSON.stringify(filter)}`)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.data.length) {
|
if (res.data.length)
|
||||||
this.botanical = res.data[0];
|
this.botanical = res.data[0];
|
||||||
} else {
|
|
||||||
this.botanical = {
|
|
||||||
itemFk: this.$state.params.id,
|
|
||||||
botanical: null,
|
|
||||||
genusFk: null,
|
|
||||||
specieFk: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('Item', () => {
|
||||||
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
|
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
$state = {
|
$state = {
|
||||||
params: {
|
params: {
|
||||||
id: 123
|
id: 123
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
save="post">
|
save="post">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
||||||
<div style="max-width: 70em; margin: 0 auto;">
|
<div style="max-width: 50em; margin: 0 auto;">
|
||||||
<vn-card pad-large>
|
<vn-card pad-large>
|
||||||
<vn-title>New item</vn-title>
|
<vn-title>New item</vn-title>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
@ -20,7 +20,10 @@
|
||||||
value-field="id"
|
value-field="id"
|
||||||
field="$ctrl.item.typeFk"
|
field="$ctrl.item.typeFk"
|
||||||
where="{or: [{code: {regexp: 'search'}}, {name: {regexp: 'search'}}]}">
|
where="{or: [{code: {regexp: 'search'}}, {name: {regexp: 'search'}}]}">
|
||||||
<tpl-item>{{code}} : {{name}}</tpl-item>
|
<tpl-item style="display: flex;">
|
||||||
|
<div style="width: 3em; padding-right: 1em;">{{::code}}</div>
|
||||||
|
<div>{{::name}}</div>
|
||||||
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
url="/item/api/Intrastats"
|
url="/item/api/Intrastats"
|
||||||
|
@ -29,7 +32,10 @@
|
||||||
value-field="id"
|
value-field="id"
|
||||||
field="$ctrl.item.intrastatFk"
|
field="$ctrl.item.intrastatFk"
|
||||||
where="{or: [{id: {regexp: 'search'}}, {description: {regexp: 'search'}}]}">
|
where="{or: [{id: {regexp: 'search'}}, {description: {regexp: 'search'}}]}">
|
||||||
<tpl-item>{{id}} : {{description}}</tpl-item>
|
<tpl-item style="display: flex;">
|
||||||
|
<div style="width: 6em; text-align: right; padding-right: 1em;">{{::id}}</div>
|
||||||
|
<div>{{::description}}</div>
|
||||||
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -32,7 +32,10 @@
|
||||||
field="$ctrl.item.intrastatFk"
|
field="$ctrl.item.intrastatFk"
|
||||||
where="{or: [{id: {regexp: 'search'}}, {description: {regexp: 'search'}}]}"
|
where="{or: [{id: {regexp: 'search'}}, {description: {regexp: 'search'}}]}"
|
||||||
initial-data="$ctrl.item.intrastat">
|
initial-data="$ctrl.item.intrastat">
|
||||||
<tpl-item>{{id}} : {{description}}</tpl-item>
|
<tpl-item style="display: flex;">
|
||||||
|
<div style="width: 6em; text-align: right; padding-right: 1em;">{{::id}}</div>
|
||||||
|
<div>{{::description}}</div>
|
||||||
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield vn-one label="Relevancy" field="$ctrl.item.relevancy" type="number"></vn-textfield>
|
<vn-textfield vn-one label="Relevancy" field="$ctrl.item.relevancy" type="number"></vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
@ -45,13 +48,19 @@
|
||||||
field="$ctrl.item.originFk"
|
field="$ctrl.item.originFk"
|
||||||
initial-data="$ctrl.item.origin">
|
initial-data="$ctrl.item.origin">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
label="Reference"
|
||||||
|
field="$ctrl.item.description">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
url="/item/api/Expences"
|
url="/item/api/Expences"
|
||||||
label="Expence"
|
label="Expence"
|
||||||
field="$ctrl.item.expenceFk"
|
field="$ctrl.item.expenceFk"
|
||||||
initial-data="$ctrl.item.expence">
|
initial-data="$ctrl.item.expence">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield vn-one label="Reference" field="$ctrl.item.description"></vn-textfield>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-card pad-large>
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-title>Item diary</vn-title>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-focus
|
||||||
|
url="/item/api/Warehouses"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
initial-data="$ctrl.warehouseFk"
|
||||||
|
field="$ctrl.warehouseFk"
|
||||||
|
label="Select warehouse">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
<table class="vn-grid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th number translate>Date</th>
|
||||||
|
<th number translate>State</th>
|
||||||
|
<th number translate>Origin</th>
|
||||||
|
<th number translate>Reference</th>
|
||||||
|
<th style="text-align: center" translate>Name</th>
|
||||||
|
<th number translate>In</th>
|
||||||
|
<th number translate>Out</th>
|
||||||
|
<th number translate>Balance</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="diary in $ctrl.diary">
|
||||||
|
<td number>{{diary.date | date:'dd/MM/yyyy HH:mm' }}</td>
|
||||||
|
<td number>{{diary.alertLevel | dashIfEmpty}}</td>
|
||||||
|
<td number>{{diary.origin | dashIfEmpty}}</td>
|
||||||
|
<td number>{{diary.reference | dashIfEmpty}}</td>
|
||||||
|
<td style="text-align: center">{{diary.name | dashIfEmpty}}</td>
|
||||||
|
<td number>{{diary.in | dashIfEmpty}}</td>
|
||||||
|
<td number>{{diary.out | dashIfEmpty}}</td>
|
||||||
|
<td number>{{diary.balance | dashIfEmpty}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="$ctrl.diary.length === 0" class="list list-element">
|
||||||
|
<td colspan="8" style="text-align: center" translate>No results</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</vn-vertical>
|
||||||
|
</vn-card>
|
||||||
|
<vn-paging margin-large-top vn-one index="$ctrl.diary" total="$ctrl.diary.count"></vn-paging>
|
||||||
|
<!-- <vn-auto-paging margin-large-top vn-one index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
|
||||||
|
</vn-vertical>
|
|
@ -0,0 +1,38 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
constructor($scope, $http) {
|
||||||
|
this.$ = $scope;
|
||||||
|
this.$http = $http;
|
||||||
|
this.diary = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
set warehouseFk(value) {
|
||||||
|
this._getItemDiary(value);
|
||||||
|
this._warehouseFk = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get warehouseFk() {
|
||||||
|
return this._warehouseFk;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getItemDiary(warehouse) {
|
||||||
|
if (warehouse == null)
|
||||||
|
return;
|
||||||
|
let params = {itemFk: this.item.id, warehouseFk: warehouse};
|
||||||
|
this.$http.get(`/item/api/Items/getDiary?params=${JSON.stringify(params)}`).then(res => {
|
||||||
|
this.diary = res.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$scope', '$http'];
|
||||||
|
|
||||||
|
ngModule.component('vnItemDiary', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
item: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('Item', () => {
|
||||||
|
describe('Component vnItemDiary', () => {
|
||||||
|
let $componentController;
|
||||||
|
let $scope;
|
||||||
|
let controller;
|
||||||
|
let $httpBackend;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
angular.mock.module('item');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
|
||||||
|
$componentController = _$componentController_;
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
controller = $componentController('vnItemDiary', {$scope: $scope});
|
||||||
|
controller.item = {id: 3};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('set warehouseFk()', () => {
|
||||||
|
it(`should call _getItemDiary() with 2 and set warehouseFk`, () => {
|
||||||
|
spyOn(controller, '_getItemDiary');
|
||||||
|
controller.warehouseFk = 2;
|
||||||
|
|
||||||
|
expect(controller._getItemDiary).toHaveBeenCalledWith(2);
|
||||||
|
expect(controller.warehouseFk).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_getItemDiary()', () => {
|
||||||
|
it(`should make a request to get the diary hwen is called with a number`, () => {
|
||||||
|
$httpBackend.whenGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}').respond({data: 'item'});
|
||||||
|
$httpBackend.expectGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}');
|
||||||
|
controller._getItemDiary(2);
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
vn-item-diary {
|
||||||
|
& vn-horizontal {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
& vn-autocomplete > div{
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
|
@ -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
|
|
||||||
});
|
|
|
@ -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="8"
|
||||||
|
data="items"
|
||||||
|
auto-load="false">
|
||||||
|
</vn-crud-model>
|
||||||
<div margin-medium>
|
<div margin-medium>
|
||||||
<div class="vn-list">
|
<div class="vn-list">
|
||||||
<vn-card>
|
<vn-card pad-medium-h>
|
||||||
<vn-horizontal>
|
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
vn-one
|
panel="vn-item-search-panel"
|
||||||
index="index"
|
model="model"
|
||||||
on-search="$ctrl.search(index)"
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
advanced="true"
|
|
||||||
popover="vn-item-filter-panel"
|
|
||||||
ignore-keys = "['page', 'size', 'search']">
|
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-horizontal>
|
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card margin-medium-top>
|
<vn-card margin-medium-v>
|
||||||
<vn-item-product
|
<vn-item-product
|
||||||
ng-repeat="item in index.model.instances track by item.id"
|
ng-repeat="item in items track by item.id"
|
||||||
item="item">
|
item="::item">
|
||||||
</vn-item-product>
|
</vn-item-product>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-paging index="index" total="index.model.count"></vn-paging>
|
<vn-pagination
|
||||||
<!-- <vn-auto-paging index="index" total="index.model.count" items="$ctrl.items"></vn-auto-paging> -->
|
model="model"
|
||||||
|
scroll-selector="ui-view">
|
||||||
|
</vn-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a ui-sref="item.create" vn-bind="+" fixed-bottom-right>
|
<a ui-sref="item.create" vn-bind="+" fixed-bottom-right>
|
||||||
|
|
|
@ -3,46 +3,68 @@ import './product';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
|
|
||||||
constructor($http, $state, $scope) {
|
constructor($http, $state, $scope) {
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$scope = $scope;
|
this.$ = $scope;
|
||||||
this.model = {};
|
|
||||||
this.itemSelected = null;
|
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) {
|
exprBuilder(param, value) {
|
||||||
index.accept();
|
switch (param) {
|
||||||
/* this.items = [];
|
case 'search':
|
||||||
index.accept().then(res => {
|
return {
|
||||||
this.items = res.instances;
|
or: [
|
||||||
}); */
|
{id: value},
|
||||||
|
{name: {regexp: value}}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
case 'name':
|
||||||
|
case 'description':
|
||||||
|
return {[param]: {regexp: value}};
|
||||||
|
case 'id':
|
||||||
|
case 'typeFk':
|
||||||
|
return {[param]: value};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cloneItem(item) {
|
cloneItem(item) {
|
||||||
this.itemSelected = item;
|
this.itemSelected = item;
|
||||||
this.$scope.clone.show();
|
this.$.clone.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
onCloneAccept(response) {
|
onCloneAccept(response) {
|
||||||
if (response == 'ACCEPT' && this.itemSelected) {
|
if (!(response == 'ACCEPT' && this.itemSelected))
|
||||||
|
return;
|
||||||
|
|
||||||
this.$http.post(`/item/api/Items/${this.itemSelected.id}/clone`).then(res => {
|
this.$http.post(`/item/api/Items/${this.itemSelected.id}/clone`).then(res => {
|
||||||
if (res && res.data && res.data.id) {
|
if (res && res.data && res.data.id)
|
||||||
this.$state.go('item.card.tags', {id: res.data.id});
|
this.$state.go('item.card.tags', {id: res.data.id});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
this.itemSelected = null;
|
this.itemSelected = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
showItemPreview(item) {
|
showItemPreview(item) {
|
||||||
this.itemSelected = item;
|
this.itemSelected = item;
|
||||||
this.$scope.preview.show();
|
this.$.preview.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$http', '$state', '$scope'];
|
Controller.$inject = ['$http', '$state', '$scope'];
|
||||||
|
|
||||||
ngModule.component('vnItemIndex', {
|
ngModule.component('vnItemIndex', {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import "./colors";
|
||||||
|
|
||||||
vn-item-product {
|
vn-item-product {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -2,7 +2,8 @@ export * from './module';
|
||||||
|
|
||||||
import './index';
|
import './index';
|
||||||
import './filter-item-list';
|
import './filter-item-list';
|
||||||
import './filter-panel';
|
import './search-panel';
|
||||||
|
import './diary';
|
||||||
import './create';
|
import './create';
|
||||||
import './card';
|
import './card';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
|
|
|
@ -29,10 +29,16 @@ Value: Valor
|
||||||
Priority: Prioridad
|
Priority: Prioridad
|
||||||
Item tax: Tasas del artículo
|
Item tax: Tasas del artículo
|
||||||
Country: País
|
Country: País
|
||||||
|
Select warehouse: Selecione almacén
|
||||||
Class: Clase
|
Class: Clase
|
||||||
Item niches: Nichos del artículo
|
Item niches: Nichos del artículo
|
||||||
|
Item diary: Registro de compra-venta
|
||||||
|
Diary: Registro
|
||||||
Warehouse: Almacén
|
Warehouse: Almacén
|
||||||
Code: Código
|
Code: Código
|
||||||
|
State: Estado
|
||||||
|
In: Entrada
|
||||||
|
Out: Salida
|
||||||
Botanical: Botánico
|
Botanical: Botánico
|
||||||
Species: Especie
|
Species: Especie
|
||||||
Add tag: Añadir etiqueta
|
Add tag: Añadir etiqueta
|
||||||
|
@ -43,3 +49,4 @@ Add barcode: Añadir código de barras
|
||||||
Remove barcode: Quitar código de barras
|
Remove barcode: Quitar código de barras
|
||||||
Buyer: Comprador
|
Buyer: Comprador
|
||||||
No results: Sin resultados
|
No results: Sin resultados
|
||||||
|
Tag: Etiqueta
|
|
@ -14,6 +14,11 @@ export default class Controller {
|
||||||
this.oldNiches = {};
|
this.oldNiches = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.getNiches();
|
||||||
|
this.getWarehouses();
|
||||||
|
}
|
||||||
|
|
||||||
_setIconAdd() {
|
_setIconAdd() {
|
||||||
if (this.niches.length) {
|
if (this.niches.length) {
|
||||||
this.niches.map(element => {
|
this.niches.map(element => {
|
||||||
|
@ -130,15 +135,11 @@ export default class Controller {
|
||||||
return this.$http.post(`/item/api/ItemNiches/crudItemNiches`, nichesObj).then(() => {
|
return this.$http.post(`/item/api/ItemNiches/crudItemNiches`, nichesObj).then(() => {
|
||||||
this.getNiches();
|
this.getNiches();
|
||||||
this._unsetDirtyForm();
|
this._unsetDirtyForm();
|
||||||
|
this.$scope.watcher.notifySaved();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
|
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
this.getNiches();
|
|
||||||
this.getWarehouses();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnApp'];
|
Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnApp'];
|
||||||
|
|
|
@ -15,7 +15,11 @@ describe('Item', () => {
|
||||||
$componentController = _$componentController_;
|
$componentController = _$componentController_;
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
|
||||||
controller = $componentController('vnItemNiche', {$state: $state});
|
controller = $componentController('vnItemNiche', {$state: $state});
|
||||||
|
controller.$scope.watcher = {
|
||||||
|
notifySaved: () => {}
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('add / remove niche', () => {
|
describe('add / remove niche', () => {
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue