This commit is contained in:
Carlos 2017-11-09 11:43:06 +01:00
commit 9d67a622fc
352 changed files with 75233 additions and 2745 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -16,3 +16,5 @@ rules:
camelcase: 0
default-case: 0
no-eq-null: 0
no-console: 0
no-warning-comments: 0

4
.gitignore vendored
View File

@ -1,7 +1,7 @@
.DS_Store
node_modules
spliting.js
build
npm-debug.log
debug.log
datasources.development.json
.idea
.idea

2
.vscode/launch.json vendored
View File

@ -5,7 +5,7 @@
"name": "Iniciar",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/services/client/server/server.js",
"program": "${workspaceRoot}/services/auth/server/server.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",

87
Jenkinsfile vendored
View File

@ -1,28 +1,25 @@
#!/usr/bin/env groovy
import groovy.json.*;
def image;
def branchName = "${env.BRANCH_NAME}";
def branchNameTest = "preprod";
def branchNameProd = "master";
def prefixDocker = "test";
def dockerNginxName = ["nginx", "-p 80:80 --privileged --link test-auth:auth --link test-salix:salix --link test-client:client --link test-mailer:mailer --link test-production:production"]
def dockerAuthName = ["auth", "-p 3000:3000"]
def dockerSalixName = ["salix", "-p 3001:3001"]
def dockerClientName = ["client", "-p 3002:3002"]
def dockerMailerName = ["mailer", "-p 3003:3003"]
def dockerProductionName = ["production", "-p 3004:3004"]
def branchProduction = "master"
def branchTest = "test";
def dockerHost = "${env.DOCKER_HOST}";
def buildNumber = "${env.BUILD_NUMBER}";
env.NODE_ENV = branchName;
env.BRANCH_NAME = branchName;
env.TAG = "${env.BUILD_NUMBER}";
def dockers = [dockerAuthName, dockerSalixName, dockerClientName, dockerMailerName, dockerProductionName, dockerNginxName]
if (branchName == "test")
env.NODE_ENV = "development";
node {
if (branchName == branchNameProd)
prefixDocker = "prod";
if (branchName == branchProduction)
dockerHost = "tcp://172.16.255.29:2375";
node
{
stage ('Print environment variables'){
echo "Branch ${branchName}, Build ${env.TAG}, NODE_ENV ${env.NODE_ENV} en docker Host ${dockerHost}"
}
stage ('Checkout') {
checkout scm
}
@ -31,51 +28,25 @@ node {
sh "npm install"
}
stage ('build Project Salix'){
stage ('build Project'){
sh "gulp build"
}
for (int i = 0; i < dockers.size(); i++)
stage ("docker")
{
def element = dockers[i][0]
def ports = dockers[i][1]
stage ("docker ${element}")
stage ("install modules loopback service")
{
stage ("Stopping ${prefixDocker}-${element} actual")
{
try
{
def returnDocker = sh (script: "docker stop ${prefixDocker}-${element}", returnStdout: true).trim();
echo "${returnDocker}";
returnDocker = sh (script: "docker rm ${prefixDocker}-${element}", returnStdout: false).trim();
//echo "${returnDocker}";
returnDocker = sh (script: "docker rmi ${prefixDocker}-${element}:${buildNumber-3}", returnStdout: true).trim();
echo "borran ${prefixDocker}-${element}:${buildNumber-3}";
//echo "${returnDocker}";
}catch(Exception _){
echo "Error Stage Stopping"
}
}
stage ("Create Docker Image ${element}") {
dir("./services/${element}"){
stage ("Install modules service ${element}"){
if (fileExists('./package.json'))
sh "npm i"
}
stage("Build image ${element}"){
image = docker.build("${prefixDocker}-${element}:${buildNumber}", ".")
}
}
}
stage ("Run Docker ${element}"){
image.run ("${ports} --name ${prefixDocker}-${element}")
}
/*
stage('docker registry'){
docker.withServer('tcp://harbor.verdnatura.es','docker-registry')
}
*/
sh "cd ./services/loopback && npm install"
}
stage ("Stopping/Removing Docker")
{
sh "docker-compose down --rmi all"
}
stage ("Generar dockers")
{
sh "docker-compose up -d --build"
}
}
}

View File

@ -43,6 +43,10 @@ npm run testWatch or test for single run
on root run:
gulp docker
wait 10 secs for db to be ready
npm run e2e
## Built With

View File

@ -7,7 +7,8 @@
{
"url": "/clients",
"state": "clients",
"component": "vn-client-index"
"component": "vn-client-index",
"acl": ["employee"]
}, {
"url": "/create",
"state": "create",
@ -22,7 +23,7 @@
"state": "clientCard.basicData",
"component": "vn-client-basic-data",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Datos básicos",
@ -33,7 +34,7 @@
"state": "clientCard.fiscalData",
"component": "vn-client-fiscal-data",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Datos fiscales",
@ -44,7 +45,7 @@
"state": "clientCard.billingData",
"component": "vn-client-billing-data",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Datos facturación",
@ -60,7 +61,7 @@
"state": "clientCard.addresses.list",
"component": "vn-client-addresses",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Consignatarios",
@ -79,7 +80,7 @@
"state": "clientCard.webAccess",
"component": "vn-client-web-access",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Acceso web",
@ -95,7 +96,7 @@
"state": "clientCard.notes.list",
"component": "vn-client-notes",
"params": {
"client": "card.client"
"client": "$ctrl.client"
},
"menu": {
"description": "Notas",

View File

@ -30,7 +30,7 @@
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.address.agencyFk"
url="/client/api/AgencyServices"
url="/client/api/AgencyModes"
show-field="name"
value-field="id"
label="Agency">

View File

@ -2,6 +2,7 @@ import './address-create.js';
describe('Client', () => {
describe('Component vnAddressCreate', () => {
let controller;
let $componentController;
let $state;
@ -13,11 +14,10 @@ describe('Client', () => {
$componentController = _$componentController_;
$state = _$state_;
$state.params.id = '1234';
controller = $componentController('vnAddressCreate', {$state});
}));
it('should define and set address property', () => {
let controller = $componentController('vnAddressCreate', {$state});
expect(controller.address.clientFk).toBe(1234);
expect(controller.address.isEnabled).toBe(true);
});

View File

@ -11,8 +11,12 @@
<vn-vertical pad-large>
<vn-title>Address</vn-title>
<vn-horizontal>
<vn-check vn-one label="enabled" field="$ctrl.address.isEnabled"></vn-check>
<vn-check vn-one label="Is equalizated" field="$ctrl.address.isEqualizated"></vn-check>
<vn-one>
<vn-check label="enabled" field="$ctrl.address.isEnabled"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Is equalizated" field="$ctrl.address.isEqualizated"></vn-check>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Consignee" field="$ctrl.address.consignee" vn-focus></vn-textfield>
@ -33,7 +37,7 @@
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.address.defaultAgencyFk"
url="/client/api/AgencyServices"
url="/client/api/AgencyModes"
show-field="name"
value-field="id"
label="Agency">

View File

@ -4,6 +4,7 @@ describe('Client', () => {
describe('Component vnAddressEdit', () => {
let $componentController;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -13,11 +14,10 @@ describe('Client', () => {
$componentController = _$componentController_;
$state = _$state_;
$state.params.addressId = '1234';
controller = $componentController('vnAddressEdit', {$state: $state});
}));
it('should define and set address property', () => {
let controller = $componentController('vnAddressEdit', {$state: $state});
expect(controller.address.id).toBe(1234);
});
});

View File

@ -27,8 +27,12 @@
<vn-textfield vn-one label="Crédito asegurado" field="$ctrl.client.creditInsurance" vn-acl="administrative"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-check vn-one label="Recargo de equivalencia" field="$ctrl.client.equalizationTax" vn-acl="administrative"></vn-check>
<vn-check vn-one label="Vies" field="$ctrl.client.vies" vn-acl="administrative"></vn-check>
<vn-one>
<vn-check label="Recargo de equivalencia" field="$ctrl.client.equalizationTax" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Vies" field="$ctrl.client.vies" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
@ -36,9 +40,15 @@
<vn-vertical pad-large>
<vn-title>Documentación</vn-title>
<vn-horizontal>
<vn-check vn-one label="Recibido core VNH" field="$ctrl.client.coreVnh" vn-acl="administrative"></vn-check>
<vn-check vn-one label="Recibido core VNL" field="$ctrl.client.coreVnl" vn-acl="administrative"></vn-check>
<vn-check vn-one label="Recibido B2B VNL" field="$ctrl.client.sepaVnl" vn-acl="administrative"></vn-check>
<vn-one>
<vn-check label="Recibido core VNH" field="$ctrl.client.coreVnh" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Recibido core VNL" field="$ctrl.client.coreVnl" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Recibido B2B VNL" field="$ctrl.client.sepaVnl" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
@ -59,4 +69,18 @@
<button response="CANCEL" translate>No</button>
<button response="ACCEPT" translate>Yes, notify</button>
</tpl-buttons>
</vn-dialog>
<vn-dialog
vn-id="propagate-equalizationTax"
on-response="$ctrl.returnDialogRE(response)">
<tpl-body>
<vn-vertical>
<vn-one text-center translate>You changes the equivalent tax</vn-one>
<vn-one text-center translate>Do you want to spread the change to their consignees?</vn-one>
</vn-vertical>
</tpl-body>
<tpl-buttons>
<button response="CANCEL" translate>No</button>
<button response="ACCEPT" translate>Yes, propagate</button>
</tpl-buttons>
</vn-dialog>

View File

@ -20,6 +20,7 @@ export default class Controller {
this.billData.discount = this.client.discount;
this.billData.credit = this.client.credit;
this.billData.creditInsurance = this.client.creditInsurance;
this.equalizationTax = this.client.equalizationTax;
}
}
submit() {
@ -36,13 +37,41 @@ export default class Controller {
}
}
);
if (!equals)
if (equals) {
this.checkREChanges();
} else {
this.$.sendMail.show();
}
}
returnDialog(response) {
if (response === 'ACCEPT') {
this.$http.post(`/mailer/manuscript/payment-update/${this.client.id}`).then(
() => this.vnApp.showMessage(this.translate.instant('Notification sent!'))
() => {
this.vnApp.showMessage(this.translate.instant('Notification sent!'));
this.checkREChanges();
}
);
} else {
this.checkREChanges();
}
}
checkREChanges() {
let equals = this.equalizationTax == this.client.equalizationTax;
this.equalizationTax = this.client.equalizationTax;
if (!equals)
this.$.propagateEqualizationTax.show();
}
returnDialogRE(response) {
if (response === 'ACCEPT') {
this.$http.patch(`/client/api/Clients/${this.client.id}/addressesPropagateRe`, {isEqualizated: this.client.equalizationTax}).then(
res => {
if (res.data)
this.vnApp.showMessage(this.translate.instant('Equivalent tax spreaded'));
}
);
}
}

View File

@ -5,6 +5,7 @@ describe('Client', () => {
let $componentController;
let $httpBackend;
let $scope;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -18,11 +19,11 @@ describe('Client', () => {
$scope.watcher = {submit};
let show = jasmine.createSpy('show');
$scope.sendMail = {show};
controller = $componentController('vnClientBillingData', {$scope: $scope});
}));
describe('copyData()', () => {
it(`should define billData using client's data`, () => {
let controller = $componentController('vnClientBillingData', {$scope: $scope});
controller.client = {
credit: 1000000000000,
creditInsurance: null,
@ -40,7 +41,6 @@ describe('Client', () => {
describe('submit()', () => {
it(`should call submit() on the watcher then receive a callback`, done => {
let controller = $componentController('vnClientBillingData', {$scope: $scope});
spyOn(controller, 'checkPaymentChanges');
controller.submit()
.then(() => {
@ -53,7 +53,6 @@ describe('Client', () => {
describe('checkPaymentChanges()', () => {
it(`should not call sendMail.show() if there are no changes on billing data`, () => {
let controller = $componentController('vnClientBillingData', {$scope: $scope});
controller.billData = {marvelHero: 'Silver Surfer'};
controller.client = {marvelHero: 'Silver Surfer'};
controller.checkPaymentChanges();
@ -62,7 +61,6 @@ describe('Client', () => {
});
it(`should call sendMail.show() if there are changes on billing data object`, () => {
let controller = $componentController('vnClientBillingData', {$scope: $scope});
controller.billData = {marvelHero: 'Silver Surfer'};
controller.client = {marvelHero: 'Spider-Man'};
controller.checkPaymentChanges();
@ -73,7 +71,6 @@ describe('Client', () => {
describe('returnDialog()', () => {
it('should request to send notification email', () => {
let controller = $componentController('vnClientBillingData', {$scope: $scope});
controller.client = {id: '123'};
$httpBackend.when('POST', `/mailer/manuscript/payment-update/${controller.client.id}`).respond('done');
$httpBackend.expectPOST(`/mailer/manuscript/payment-update/${controller.client.id}`);

View File

@ -4,5 +4,9 @@
"No": "No",
"Yes, notify": "Sí, notificar",
"Notification sent!": "¡Notificación enviada!",
"Notification error": "Error al enviar notificación"
"Notification error": "Error al enviar notificación",
"You changes the equivalent tax": "Has cambiado el recargo de equivalencia",
"Do you want to spread the change to their consignees?" : "¿Deseas propagar el cambio a sus consignatarios?",
"Yes, propagate": "Si, propagar",
"Equivalent tax spreaded": "Recargo de equivalencia propagado"
}

View File

@ -1,13 +1,13 @@
<vn-horizontal>
<mg-ajax
path="/client/api/Clients/{{edit.params.id}}/card"
actions="card.client = edit.model"
actions="$ctrl.client = edit.model"
options="mgEdit">
</mg-ajax>
<vn-empty style="min-width: 18em; padding-left: 1em; padding-bottom: 1em;">
<vn-descriptor
client="card.client"
active="card.client.active"
client="$ctrl.client"
active="$ctrl.client.active"
class="display-block" >
</vn-descriptor>
<vn-left-menu></vn-left-menu>

View File

@ -9,6 +9,5 @@ export default class Controller {
ngModule.component('vnClientCard', {
template: require('./card.html'),
controller: Controller,
controllerAs: 'card'
controller: Controller
});

View File

@ -4,6 +4,7 @@ describe('Client', () => {
describe('Component vnClientCard', () => {
let $componentController;
let $scope;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -12,11 +13,10 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
$componentController = _$componentController_;
$scope = $rootScope;
controller = $componentController('vnClientCard', {$scope: $scope});
}));
it('should define and set client property to null in the module instance', () => {
let controller = $componentController('vnClientCard', {$scope: $scope});
expect(controller.client).toBeDefined();
expect(controller.client).toBe(null);
});

View File

@ -5,6 +5,7 @@ describe('Client', () => {
let $componentController;
let $scope;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -23,11 +24,10 @@ describe('Client', () => {
};
}
};
controller = $componentController('vnClientCreate', {$scope: $scope});
}));
it('should define and set scope, state and client properties', () => {
let controller = $componentController('vnClientCreate', {$scope: $scope});
expect(controller.$).toBe($scope);
expect(controller.$state).toBe($state);
expect(controller.client.active).toBe(true);
@ -35,7 +35,6 @@ describe('Client', () => {
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
let controller = $componentController('vnClientCreate', {$scope: $scope});
spyOn($state, 'go');
controller.onSubmit();

View File

@ -1,9 +1,9 @@
<vn-card>
<vn-vertical class="margin-medium" pad-medium-top pad-medium-bottom>
<vn-horizontal>
<vn-one>
<a vn-one ui-sref="clients">
<i class="material-icons descriptor-icon">person</i>
</vn-one>
</a>
<vn-vertical vn-two>
<div class="margin-none">{{::$ctrl.client.id}}</div>
<div class="margin-none">{{$ctrl.client.name}}</div>

View File

@ -1,5 +1,4 @@
import ngModule from '../module';
import './style.css';
export default class Controller {
constructor($scope, $http) {

View File

@ -4,6 +4,7 @@ describe('Client', () => {
describe('Component vnDescriptor', () => {
let $componentController;
let $scope;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -12,11 +13,11 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
controller = $componentController('vnDescriptor', {$scope: $scope});
}));
describe('set active', () => {
it('should check if active is defined and diferent from the new value', () => {
let controller = $componentController('vnDescriptor', {$scope: $scope});
controller.client = {id: 1};
expect(controller._active).toBe(undefined);

View File

@ -1,3 +0,0 @@
.descriptor-icon{
font-size:60px;
}

View File

@ -10,8 +10,12 @@
<vn-vertical pad-large>
<vn-title>Fiscal data</vn-title>
<vn-horizontal>
<vn-check vn-one label="Facturar" field="$ctrl.client.hasToInvoice"></vn-check>
<vn-check vn-one label="Factura impresa" field="$ctrl.client.invoiceByEmail"></vn-check>
<vn-one>
<vn-check label="Has to invoice" field="$ctrl.client.hasToInvoice"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Invoice by mail" field="$ctrl.client.invoiceByEmail"></vn-check>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-two label="Domicilio fiscal" field="$ctrl.client.street" vn-focus></vn-textfield>
@ -33,7 +37,7 @@
url="/client/api/Countries"
show-field="name"
value-field="id"
label="País">
label="CountryFk">
</vn-autocomplete>
</vn-horizontal>
</vn-vertical>

View File

@ -3,6 +3,7 @@ import './index.js';
describe('Client', () => {
describe('Component vnClientIndex', () => {
let $componentController;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -10,17 +11,15 @@ describe('Client', () => {
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnClientIndex');
}));
it('should define and set model property as an empty object', () => {
let controller = $componentController('vnClientIndex');
expect(controller.model).toEqual({});
});
describe('search()', () => {
it(`should set model's search to the search input`, () => {
let controller = $componentController('vnClientIndex');
controller.model.search = 'batman';
let index = {
filter: {},

View File

@ -1,5 +1,8 @@
{
"Client": "Cliente",
"Clients": "Clientes",
"Fiscal data": "Datos Fiscales"
"Fiscal data": "Datos Fiscales",
"Has to invoice": "Factura",
"Invoice by mail": "Factura impresa",
"CountryFk": "País"
}

View File

@ -4,6 +4,7 @@ describe('Client', () => {
describe('Component vnNoteCreate', () => {
let $componentController;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -13,11 +14,10 @@ describe('Client', () => {
$componentController = _$componentController_;
$state = _$state_;
$state.params.id = '1234';
controller = $componentController('vnNoteCreate', {$state: $state});
}));
it('should define clientFk using $state.params.id', () => {
let controller = $componentController('vnNoteCreate', {$state: $state});
expect(controller.note.clientFk).toBe(1234);
expect(controller.note.client).toBe(undefined);
});

View File

@ -5,6 +5,7 @@ describe('Client', () => {
let $componentController;
let $state;
let $httpBackend;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -14,11 +15,11 @@ describe('Client', () => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
}));
describe('$onChanges()', () => {
it(`should call getObservation() with the client id`, () => {
let controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
controller.client = {
id: 1234
};
@ -31,19 +32,20 @@ describe('Client', () => {
describe('$getObservation()', () => {
it(`should request to GET the client notes`, () => {
let controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
controller.client = {id: '1234'};
let json = JSON.stringify({where: {clientFk: '1234'}, order: 'created DESC'});
$httpBackend.when('GET', `/client/api/clientObservations?filter=${json}`).respond('ok');
$httpBackend.expectGET(`/client/api/clientObservations?filter=${json}`, {Accept: 'application/json, text/plain, */*'});
let jsonString = JSON.stringify({where: {clientFk: '1234'}, order: 'created DESC'});
let json = {data: 'some data'};
$httpBackend.when('GET', `/client/api/clientObservations?filter=${jsonString}`).respond(json);
$httpBackend.expectGET(`/client/api/clientObservations?filter=${jsonString}`, {Accept: 'application/json, text/plain, */*'});
controller.getObservation();
$httpBackend.flush();
expect(controller.observations).toEqual(json);
});
});
describe('$newObservation()', () => {
it(`should redirect the user to the newObservation view`, () => {
let controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
controller.client = {id: '1234'};
spyOn(controller.$state, 'go');
controller.newObservation();

View File

@ -4,6 +4,7 @@ describe('Client', () => {
describe('Component vnClientSearchPanel', () => {
let $componentController;
let $window;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -12,11 +13,11 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, _$window_) => {
$componentController = _$componentController_;
$window = _$window_;
controller = $componentController('vnClientSearchPanel', {$window: $window});
}));
describe('onSearch()', () => {
it(`should call setStorageValue() and onSubmit()`, () => {
let controller = $componentController('vnClientSearchPanel', {$window: $window});
spyOn(controller, 'setStorageValue');
spyOn(controller, 'onSubmit');
controller.setStorageValue();
@ -29,8 +30,6 @@ describe('Client', () => {
describe('$onChanges()', () => {
it(`should set filter properties using the search values`, () => {
let controller = $componentController('vnClientSearchPanel', {$window: $window});
expect(controller.filter).not.toBeDefined();
spyOn(JSON, 'parse').and.returnValue({data: 'data'});
controller.$onChanges();

View File

@ -8,9 +8,15 @@
<form name="form" ng-submit="watcher.submit()" pad-medium>
<vn-card>
<vn-vertical pad-large>
<vn-title>Web access</vn-title>
<vn-check label="Enable web access" field="$ctrl.account.active"></vn-check>
<vn-textfield label="User" class="margin-medium-top" field="$ctrl.account.name" vn-focus></vn-textfield>
<vn-one>
<vn-title>Web access</vn-title>
</vn-one>
<vn-one>
<vn-check label="Enable web access" field="$ctrl.account.active"></vn-check>
</vn-one>
<vn-one>
<vn-textfield label="User" class="margin-medium-top" field="$ctrl.account.name" vn-focus></vn-textfield>
</vn-one>
</vn-vertical>
</vn-card>
<vn-button-bar>

View File

@ -5,6 +5,7 @@ describe('Component VnClientWebAccess', () => {
let $httpBackend;
let $scope;
let vnApp;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -16,11 +17,11 @@ describe('Component VnClientWebAccess', () => {
$httpBackend = _$httpBackend_;
vnApp = _vnApp_;
spyOn(vnApp, 'showError');
controller = $componentController('vnClientWebAccess', {$scope: $scope});
}));
describe('$onChanges()', () => {
it(`should pass client's account data to account then call isCustomer function`, () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
spyOn(controller, 'isCustomer');
controller.client = {client: 'Bruce Wayne', account: 'Wayne Industries'};
controller.account = {};
@ -33,7 +34,6 @@ describe('Component VnClientWebAccess', () => {
describe('isCustomer()', () => {
it(`should perform a query if client is defined with an ID`, () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
controller.client = {id: '1234'};
controller.isCustomer();
@ -45,7 +45,6 @@ describe('Component VnClientWebAccess', () => {
describe('onPassOpen()', () => {
it('should set passwords to empty values', () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
controller.newPassword = 'm24x8';
controller.repeatPassword = 'm24x8';
controller.onPassOpen();
@ -57,7 +56,6 @@ describe('Component VnClientWebAccess', () => {
describe('onPassChange()', () => {
it('should request to update the password', () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
controller.client = {id: '1234'};
controller.newPassword = 'm24x8';
controller.repeatPassword = 'm24x8';
@ -70,7 +68,6 @@ describe('Component VnClientWebAccess', () => {
describe(`when password is empty`, () => {
it(`should throw Passwords can't be empty error`, () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
controller.client = {id: '1234'};
controller.newPassword = '';
controller.canChangePassword = true;
@ -82,7 +79,6 @@ describe('Component VnClientWebAccess', () => {
describe(`when passwords don't match`, () => {
it(`should throw Passwords don't match error`, () => {
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
controller.client = {id: '1234'};
controller.newPassword = 'm24x8';
controller.canChangePassword = true;

View File

@ -7,6 +7,7 @@
filter="true"
load-more="$ctrl.getItems()"
show-load-more="$ctrl.maxRow"
remove-load-more="$ctrl.removeLoadMore"
filter-action="$ctrl.findItems(search)"
item-width="$ctrl.width"
multiple="$ctrl.multiple"

View File

@ -1,14 +1,16 @@
import {module} from '../module';
import Component from '../lib/component';
import copyObject from '../lib/copy';
import './style.scss';
class Autocomplete extends Component {
constructor($element, $scope, $http, $timeout) {
constructor($element, $scope, $http, $timeout, $filter) {
super($element);
this.$element = $element;
this.$scope = $scope;
this.$http = $http;
this.$timeout = $timeout;
this.$filter = $filter;
this._showDropDown = false;
this.finding = false;
@ -19,10 +21,12 @@ class Autocomplete extends Component {
this.maxRow = 10;
this.showField = this.showField || 'name';
this.valueField = this.valueField || 'id';
this.items = this.data || [];
this.order = this.order || 'name ASC';
this.items = copyObject(this.data) || [];
this.displayValueMultiCheck = [];
this._multiField = [];
this.readonly = true;
this.removeLoadMore = false;
}
get showDropDown() {
@ -33,6 +37,10 @@ class Autocomplete extends Component {
this._preLoad = true;
this.getItems();
}
if (value && !this.width) {
let rectangle = this.$element[0].getBoundingClientRect();
this.width = Math.round(rectangle.width) - 10;
}
this._showDropDown = value;
}
@ -70,28 +78,36 @@ class Autocomplete extends Component {
return this.multiple ? this._multiField : this._field;
}
set field(value) {
this.finding = true;
if (value && value.hasOwnProperty(this.valueField)) {
this._field = value[this.valueField];
if (this.multiple) {
this.setMultiField(value[this.valueField]);
if (!angular.equals(value, this.field)) {
this.finding = true;
if (value && value.hasOwnProperty(this.valueField)) {
this._field = value[this.valueField];
if (this.multiple) {
this.setMultiField(value[this.valueField]);
}
} else {
this.setValue(value);
}
} else {
this.setValue(value);
if (value && value.hasOwnProperty(this.showField))
this.displayValue = value[this.showField];
this.finding = false;
if (this.onChange)
this.onChange({item: this._field});
}
if (value && value.hasOwnProperty(this.showField))
this.displayValue = value[this.showField];
this.finding = false;
if (this.onChange)
this.onChange({item: this._field});
}
set initialData(value) {
if (value) {
this.field = value;
if (value && value.hasOwnProperty(this.valueField)) {
this._field = value[this.valueField];
if (this.multiple) {
this._multiField = [value[this.valueField]]
}
if (value.hasOwnProperty(this.showField)) {
this.displayValue = value[this.showField];
}
}
}
@ -173,12 +189,12 @@ class Autocomplete extends Component {
}
findItems(search) {
if (!this.url)
return this.items ? this.items : [];
if (search && !this.finding) {
if (this.url && search && !this.finding) {
this.maxRow = false;
let filter = {where: {name: {regexp: search}}};
if (this.filter && this.filter.where) {
Object.assign(filter.where, this.filter.where);
}
let json = JSON.stringify(filter);
this.finding = true;
this.$http.get(`${this.url}?filter=${json}`).then(
@ -198,43 +214,65 @@ class Autocomplete extends Component {
this.finding = false;
}
);
} else if (search && !this.url && this.data) {
this.items = this.$filter('filter')(this.data, search);
} else if (!search && !this.finding) {
this.maxRow = 10;
this.items = [];
this.getItems();
}
}
getItems() {
let filter = {};
if (this.url === undefined) {
this.items = copyObject(this.data);
this.maxRow = false;
this.removeLoadMore = true;
} else {
let filter = {};
if (!this.finding) {
this.finding = true;
if (this.maxRow) {
if (this.items) {
filter.skip = this.items.length;
}
filter.limit = this.maxRow;
filter.order = 'name ASC';
}
if (this.maxRow) {
if (this.items) {
filter.skip = this.items.length;
}
filter.limit = this.maxRow;
filter.order = this.order;
}
if (this.filter) {
Object.assign(filter, this.filter);
}
let json = JSON.stringify(filter);
let json = JSON.stringify(filter);
this.$http.get(`${this.url}?filter=${json}`).then(
json => {
if (json.data.length)
json.data.forEach(
el => {
if (this.multiple) {
el.checked = this.field.indexOf(el[this.valueField]) !== -1;
this.removeLoadMore = false;
this.$http.get(`${this.url}?filter=${json}`).then(
json => {
if (json.data.length) {
json.data.forEach(
el => {
if (this.multiple) {
el.checked = this.field.indexOf(el[this.valueField]) !== -1;
}
this.items.push(el);
}
);
if (filter.skip === 0 && this.maxRow && json.data.length < this.maxRow) {
this.removeLoadMore = true;
}
this.items.push(el);
} else {
this.maxRow = false;
}
);
else
this.maxRow = false;
this.finding = false;
},
() => {
this.finding = false;
}
);
}
);
}
}
$onInit() {
this.findMore = this.url && this.maxRow;
this.mouseFocus = false;
@ -265,9 +303,6 @@ class Autocomplete extends Component {
this.showDropDown = this.mouseFocus;
});
});
let rectangle = this.$element[0].getBoundingClientRect();
this.width = Math.round(rectangle.width) - 10;
}
$onDestroy() {
@ -277,9 +312,17 @@ class Autocomplete extends Component {
this.$element.unbind('focusout');
}
$onChanges(objectChange) {
if (objectChange.data && objectChange.data.currentValue && objectChange.data.currentValue.length) {
this.items = copyObject(objectChange.data.currentValue);
this.maxRow = false;
this.removeLoadMore = true;
}
}
}
Autocomplete.$inject = ['$element', '$scope', '$http', '$timeout'];
Autocomplete.$inject = ['$element', '$scope', '$http', '$timeout', '$filter'];
module.component('vnAutocomplete', {
template: require('./autocomplete.html'),
@ -292,11 +335,11 @@ module.component('vnAutocomplete', {
initialData: '<?',
onChange: '&?',
data: '<?',
itemAs: '@?',
field: '=',
label: '@',
itemTemplate: '@?',
multiple: '@?'
multiple: '@?',
order: '@?',
filter: '<?'
},
transclude: {
tplItem: '?tplItem'

View File

@ -6,6 +6,7 @@ describe('Component vnAutocomplete', () => {
let $httpBackend;
let $timeout;
let $element;
let controller;
beforeEach(() => {
angular.mock.module('client');
@ -17,11 +18,11 @@ describe('Component vnAutocomplete', () => {
$httpBackend = _$httpBackend_;
$timeout = _$timeout_;
$element = angular.element('<div></div>');
controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
}));
describe('showDropDown() setter', () => {
it(`should set _showDropDown value`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller._showDropDown = '';
controller.showDropDown = 'some value';
@ -29,7 +30,6 @@ describe('Component vnAutocomplete', () => {
});
it(`should set _showDropDown value`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller._showDropDown = '';
controller.showDropDown = 'some value';
@ -39,7 +39,6 @@ describe('Component vnAutocomplete', () => {
describe('displayValue() setter', () => {
it(`should display value in a formated way`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
let value = 'some value';
controller.displayValue = value;
@ -48,7 +47,6 @@ describe('Component vnAutocomplete', () => {
describe('when the autocomeplete is multiple', () => {
it(`should display values separated with commas`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.multiple = true;
controller.displayValue = 'some value';
controller.displayValue = 'another value';
@ -61,14 +59,12 @@ describe('Component vnAutocomplete', () => {
describe('field() setter', () => {
describe('when value is an object', () => {
it(`should set _field controllers property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.field = {id: 1, name: 'Bruce Wayne'};
expect(controller._field).toEqual(1);
});
it(`should set _multifield controllers property `, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.multiple = true;
controller.field = {id: 1, name: 'Bruce Wayne'};
@ -82,7 +78,6 @@ describe('Component vnAutocomplete', () => {
});
it(`should set _multifield value and remove it if called a second type with same value`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.multiple = true;
controller.field = {id: 1, name: 'Bruce Wayne'};
@ -96,7 +91,6 @@ describe('Component vnAutocomplete', () => {
});
it(`should set displayValue finding an existing item in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
controller.field = {id: 2, name: 'Bruce Wayne'};
@ -106,7 +100,6 @@ describe('Component vnAutocomplete', () => {
describe('when value is a number', () => {
it(`should set _field controller property finding an existing item in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}];
controller.field = 2;
@ -114,7 +107,6 @@ describe('Component vnAutocomplete', () => {
});
it(`should set _multifield value and remove it if called a second type with same value finding an existing item in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}];
controller.multiple = true;
controller.field = 2;
@ -127,7 +119,7 @@ describe('Component vnAutocomplete', () => {
});
it(`should perform a query if the item id isn't present in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout}, {url: 'test.com'});
controller.url = 'test.com';
$httpBackend.whenGET('test.com?filter={"fields":{"id":true,"name":true},"where":{"id":3}}').respond();
$httpBackend.expectGET('test.com?filter={"fields":{"id":true,"name":true},"where":{"id":3}}');
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
@ -136,7 +128,6 @@ describe('Component vnAutocomplete', () => {
});
it(`should set displayValue finding an existing item in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
controller.field = 2;
@ -144,7 +135,7 @@ describe('Component vnAutocomplete', () => {
});
it(`should set field performing a query as the item id isn't present in the controller.items property`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout}, {url: 'test.com'});
controller.url = 'test.com';
$httpBackend.whenGET('test.com?filter={"fields":{"id":true,"name":true},"where":{"id":3}}').respond();
$httpBackend.expectGET('test.com?filter={"fields":{"id":true,"name":true},"where":{"id":3}}');
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
@ -154,25 +145,10 @@ describe('Component vnAutocomplete', () => {
});
});
describe('findItem()', () => {
it(`should return items array if the controller does not provide a url and nither it has items`, () => {
describe('findItems()', () => {
it(`should perform a search and store the result in controller items`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = ['Batman', 'Bruce Wayne'];
controller.findItems('some search value');
expect(controller.items.length).toEqual(2);
});
it(`should perform a query with the search value if the finding flag is false`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.items = ['Batman', 'Bruce Wayne'];
controller.findItems('Gotham');
expect(controller.items.length).toEqual(2);
});
it(`should perform a query with the search value if the finding flag is false`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout}, {url: 'test.com'});
controller.url = 'test.com';
let search = 'The Joker';
let json = JSON.stringify({where: {name: {regexp: search}}});
$httpBackend.whenGET(`test.com?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
@ -183,18 +159,37 @@ describe('Component vnAutocomplete', () => {
expect(controller.items[0]).toEqual({id: 3, name: 'The Joker'});
});
it(`should perform a query with the search value if the finding flag is false`, () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout}, {url: 'test.com'});
let search = 'The Joker';
it(`should perform a search with multiple true and store the result in controller items with the checked property defined`, () => {
controller.url = 'test.com';
let search = 'Joker';
controller.multiple = true;
let json = JSON.stringify({where: {name: {regexp: search}}});
$httpBackend.whenGET(`test.com?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
$httpBackend.whenGET(`test.com?filter=${json}`).respond([{id: 3, name: 'The Joker'}, {id: 4, name: 'Joker'}]);
$httpBackend.expectGET(`test.com?filter=${json}`);
controller.findItems(search);
$httpBackend.flush();
expect(controller.items[0]).toEqual({id: 3, name: 'The Joker'});
expect(controller.items).toEqual([{id: 3, name: 'The Joker', checked: false}, {id: 4, name: 'Joker', checked: false}]);
});
// siguiente test el de Multiple!
it(`should call getItems function if there's no search value`, () => {
controller.url = 'test.com';
spyOn(controller, 'getItems');
controller.findItems();
expect(controller.getItems).toHaveBeenCalledWith();
});
});
describe('getItems()', () => {
it(`should perfom a query to fill the items without filter`, () => {
controller.url = 'test.com';
$httpBackend.whenGET(`test.com?filter={"skip":0,"limit":10,"order":"name ASC"}`).respond([{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}]);
$httpBackend.expectGET(`test.com?filter={"skip":0,"limit":10,"order":"name ASC"}`);
controller.getItems();
$httpBackend.flush();
expect(controller.items).toEqual([{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce Wayne'}]);
});
});
});

View File

@ -2,6 +2,7 @@ import {module as _module} from '../module';
import * as resolveFactory from '../lib/resolveDefaultComponents';
import * as normalizerFactory from '../lib/inputAttrsNormalizer';
import * as util from '../lib/util';
import './style.css';
const _NAME = 'check';
export const NAME = util.getName(_NAME);

View File

@ -0,0 +1,3 @@
vn-check {
float: left;
}

View File

@ -21,6 +21,12 @@ export default class ColumnHeader {
}
return showArrow;
}
$onInit() {
if (this.defaultOrder) {
this.order = this.defaultOrder;
this.onClick();
}
}
}
ColumnHeader.$inject = [];
@ -29,7 +35,8 @@ module.component('vnColumnHeader', {
bindings: {
field: '@?',
text: '@?',
className: '@?'
className: '@?',
defaultOrder: '@?'
},
require: {
gridHeader: '^^vnGridHeader'

View File

@ -0,0 +1,88 @@
import './column-header.js';
describe('Component vnColumnHeader', () => {
let $componentController;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnColumnHeader', {});
}));
describe('onClick()', () => {
it(`should change the ordenation to DESC (descendant) if it was ASC (ascendant)`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'ASC';
controller.onClick();
expect(controller.order).toEqual('DESC');
});
it(`should change the ordenation to ASC (ascendant) if it wasnt ASC`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'DESC or any other value that might occur';
controller.onClick();
expect(controller.order).toEqual('ASC');
});
it(`should call the selectColum() function after changing a value`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'Change me!';
spyOn(controller.gridHeader, 'selectColum');
controller.onClick();
expect(controller.gridHeader.selectColum).toHaveBeenCalledWith(controller);
});
});
describe('showArrow()', () => {
it(`should return true when the type is DESC and MouseIsOver`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.mouseIsOver = true;
let result = controller.showArrow('DESC');
expect(result).toEqual(true);
});
it(`should return true if many conditions are true`, () => {
controller.gridHeader = {currentColumn: {field: 'fields should be identical'}};
controller.field = 'fields should be identical';
controller.order = 'ASC';
let result = controller.showArrow('ASC');
expect(result).toEqual(true);
});
it(`should return false without type being DESC or any other values being true`, () => {
controller.gridHeader = {currentColumn: {field: 'fields should be identical'}};
controller.field = 'I am the controllers field';
controller.order = 'ASC';
let result = controller.showArrow('ASC');
expect(result).toEqual(false);
});
});
describe('onInit()', () => {
it(`should never call onClick()`, () => {
spyOn(controller, 'onClick');
controller.$onInit();
expect(controller.onClick).not.toHaveBeenCalledWith();
});
it(`should define controllers order as per defaultOrder then call onClick()`, () => {
controller.defaultOrder = 'ASC';
spyOn(controller, 'onClick');
controller.$onInit();
expect(controller.order).toEqual('ASC');
expect(controller.onClick).toHaveBeenCalledWith();
});
});
});

View File

@ -38,8 +38,6 @@ export {NAME as SUBMIT, directive as SubmitDirective} from './submit/submit';
export {NAME as SUBMIT_MDL, factory as submitMdl} from './submit/submit.mdl';
export {NAME as COMBO, directive as ComboDirective} from './combo/combo';
export {NAME as COMBO_MDL, factory as comboMdl} from './combo/combo.mdl';
/* export {NAME as DATE_PICKER, directive as DatePickerDirective} from './date-picker/date-picker';
export {NAME as DATE_PICKER_MDL, factory as datePickerMdl} from './date-picker/date-picker.mdl';*/
export {NAME as CARD, directive as CardDirective} from './card/card';
export {NAME as CARD_MDL, factory as cardMdl} from './card/card.mdl';
export {NAME as SWITCH, directive as SwitchDirective} from './switch/switch';

View File

@ -1,51 +0,0 @@
import {module as _module} from '../module';
import * as resolveFactory from '../lib/resolveDefaultComponents';
import * as normalizerFactory from '../lib/inputAttrsNormalizer';
import * as util from '../lib/util';
import Flatpickr from 'vendor/src/flatpickr';
const _NAME = 'datePicker';
export const NAME = util.getName(_NAME);
directive.$inject = [resolveFactory.NAME, normalizerFactory.NAME, '$translate'];
export function directive(resolve, normalizer, $translate) {
return {
restrict: 'E',
template: function(_, attrs) {
normalizer.normalize(attrs);
return resolve.getTemplate(_NAME, attrs);
},
link: function(scope, element, attrs) {
let input = element[0];
let flatpickrInstance;
let initOptions = {};
if (attrs.iniOpts)
initOptions = scope.$eval(attrs.iniOpts);
if (!initOptions.locale)
initOptions.locale = $translate.use();
/*if (!initOptions.dateFormat && initOptions.locale === 'es')
initOptions.dateFormat = 'd-m-Y';*/
if (!input.matches('input'))
input = input.querySelector('input');
if (input)
flatpickrInstance = new Flatpickr(input, initOptions);
scope.$watch(attrs.model, () => {
let mdlField = element[0].firstChild.MaterialCheckbox;
if (mdlField)
mdlField.updateClasses_();
});
componentHandler.upgradeElement(element[0].firstChild);
element.on('$destroy', function() {
flatpickrInstance.destroy();
});
}
};
}
_module.directive(NAME, directive);

View File

@ -1,4 +0,0 @@
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input *[className]*" type="text" name="*[name]*" ng-model="*[model]*" rule="*[rule]*" *[enabled]*/>
<label class="mdl-textfield__label" translate>*[label]*</label>
</div>

View File

@ -1,14 +0,0 @@
import {module} from '../module';
import template from './date-picker.mdl.html';
export const NAME = 'vnDatePickerMdlFactory';
export function factory() {
return {
template: template,
default: {
label: 'Label',
enabled: 'enabled'
}
};
}
module.factory(NAME, factory);

View File

@ -1,19 +1,21 @@
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<div
class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"
tabindex="1"
ng-focus="$ctrl.hasFocus = true"
ng-blur="$ctrl.hasFocus = false"
ng-mouseenter="$ctrl.hasMouseIn = true"
ng-mouseleave="$ctrl.hasMouseIn = false"
>
<input type="text"
class="mdl-textfield__input"
name="{{::$ctrl.name}}"
ng-model="$ctrl.modelView"
ng-disabled="{{!$ctrl.enabled}}"
rule="{{::$ctrl.rule}}"/>
<button
type="button"
class="mdl-chip__action ng-hide"
tabindex="-1"
translate-attr="{title: 'Clear'}"
ng-show="$ctrl.modelView"
ng-click="$ctrl.onClear()"
>
<i class="material-icons">clear</i>
</button>
<label class="mdl-textfield__label">{{::$ctrl.label | translate}}</label>
rule="{{::$ctrl.rule}}"/>
<div class="mdl-chip__action">
<i class="material-icons" ng-if="$ctrl.isTimePicker" ng-click="$ctrl.vp.open()">query_builder</i>
<i class="material-icons pointer" ng-show="$ctrl.modelView&&($ctrl.hasFocus||$ctrl.hasMouseIn)" ng-click="$ctrl.onClear()">clear</i>
</div>
<label class="mdl-textfield__label" translate>{{$ctrl.label}}</label>
</div>

View File

@ -27,7 +27,9 @@ class DatePicker extends Component {
this.enabled = true;
this._modelView = null;
this._model = undefined;
this._optionsChecked = false;
this.hasFocus = false;
this.hasMouseIn = false;
componentHandler.upgradeElement($element[0].firstChild);
}
@ -37,7 +39,9 @@ class DatePicker extends Component {
set model(value) {
this._model = value;
if (value && !this.modelView) {
let format = this._formatFlat2Angular(this.iniOptions.dateFormat || 'Y-m-d');
let options = this._getOptions();
let initialDateFormat = (options && options.dateFormat) ? options.dateFormat : 'Y-m-d';
let format = this._formatFlat2Angular(initialDateFormat);
this.modelView = this.$filter('date')(value, format);
}
}
@ -48,9 +52,8 @@ class DatePicker extends Component {
this._modelView = value;
this.input.value = value;
this._setModel(value);
this.$timeout(
() => {
this.mdlUpdate();
this.$timeout(() => {
this.mdlUpdate();
}, 500);
}
onClear() {
@ -76,16 +79,15 @@ class DatePicker extends Component {
return `${dates} ${hours}`.trim();
} else if (string.indexOf(':') !== -1) { // only time format
return parts.join(':');
} else { // only date format
return parts.join('-');
}
} // only date format
return parts.join('-');
}
_setModel(value) {
let model;
if (!value) {
model = undefined;
} else if (!this.iniOptions.dateFormat || (this.iniOptions.dateFormat && this.iniOptions.dateFormat.startsWith('Y-m-d'))) {
} else if (!this.iniOptions || (this.iniOptions.dateFormat && this.iniOptions.dateFormat.startsWith('Y-m-d'))) {
model = value;
} else {
let formats = this.iniOptions.dateFormat.split(/[ZT.,/ :-]/);
@ -136,9 +138,12 @@ class DatePicker extends Component {
}
}
$onInit() {
if (!this.iniOptions)
_getOptions() {
if (this.iniOptions && this._optionsChecked) {
return this.iniOptions;
} else if (!this.iniOptions) {
this.iniOptions = {};
}
if (!this.iniOptions.locale)
this.iniOptions.locale = this.$translate.use();
@ -158,9 +163,14 @@ class DatePicker extends Component {
}
);
}
this._optionsChecked = true;
return this.iniOptions;
}
if (this.input)
this.vp = new Flatpickr(this.input, this.iniOptions);
$onInit() {
this.iniOptions = this._getOptions();
this.isTimePicker = (this.iniOptions && this.iniOptions.enableTime && this.iniOptions.noCalendar);
this.vp = new Flatpickr(this.input, this.iniOptions);
}
$onDestroy() {
if (this.vp)

View File

@ -0,0 +1,44 @@
import './datePicker.js';
describe('Component vnDatePicker', () => {
let $componentController;
let $scope;
let $timeout;
let $element;
let $translate;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$timeout_, _$translate_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$timeout = _$timeout_;
$element = angular.element(`<div><input></div>`);
$translate = _$translate_;
controller = $componentController('vnDatePicker', {$scope, $element, $translate, $timeout});
}));
describe('_formatFlat2Angular()', () => {
it(`should format date from Y-m-d to yyyy-MM-dd`, () => {
let formatedDate = controller._formatFlat2Angular(`Y-m-d`);
expect(formatedDate).toBe('yyyy-MM-dd');
});
it(`should format date from d-m-Y to dd-MM-yyyy`, () => {
let formatedDate = controller._formatFlat2Angular(`d-m-Y`);
expect(formatedDate).toBe('dd-MM-yyyy');
});
it(`should split the given string into parts`, () => {
controller.iniOptions = {dateFormat: 'd/m/Y'};
controller.model = '2017-12-23';
expect(controller.modelView).toBe('23-12-2017');
});
});
});

View File

@ -1,12 +1,18 @@
vn-date-picker {
div {
outline: none; //remove chrome outline
}
.mdl-chip__action {
position: absolute;
position: absolute;
width: auto;
top: 0px;
right: -6px;
margin: 22px 0px;
background-color: white;
}
.material-icons {
font-size: 18px;
font-size: 18px;
float: right;
margin-right: 5px;
}
}

View File

@ -13,21 +13,20 @@ export default class Dialog extends Component {
super($element);
$element.addClass('vn-dialog');
this.dialog = $element[0].firstChild;
this.element.addEventListener('mousedown',
event => this.onBackgroundMouseDown(event));
this.element.addEventListener('mousedown', event => this.onBackgroundMouseDown(event));
}
/**
* Displays the dialog to the user.
*/
show() {
let style = this.dialog.style;
let screenMargin = 20;
let window = this.window;
let innerWidth = window.innerWidth;
let innerHeight = window.innerHeight;
let width = this.dialog.offsetWidth;
let height = this.dialog.offsetHeight;
let screenMargin = 20;
let dblMargin = screenMargin * 2;
if (width + screenMargin > innerWidth) {
width = innerWidth - dblMargin;
@ -38,10 +37,8 @@ export default class Dialog extends Component {
style.height = height + 'px';
}
this.keypressHandler =
event => this.onKeypress(event);
this.document.addEventListener('keypress',
this.keypressHandler);
this.keypressHandler = event => this.onKeypress(event);
this.document.addEventListener('keypress', this.keypressHandler);
this.element.style.display = 'block';
if (this.onOpen)
@ -66,17 +63,17 @@ export default class Dialog extends Component {
cancel = this.onResponse({response: response});
return cancel;
}
realHide() {
this.element.style.display = 'none';
this.document.removeEventListener('keypress',
this.keypressHandler);
this.document.removeEventListener('keypress', this.keypressHandler);
this.lastEvent = null;
}
onButtonClick(event) {
let buttonBar = this.element.querySelector('.button-bar');
let buttons = buttonBar.querySelector('tpl-buttons');
let node = event.target;
while (node.parentNode != buttons) {
if (node == buttonBar) return;
node = node.parentNode;
@ -86,13 +83,16 @@ export default class Dialog extends Component {
let cancel = this.fireResponse(response);
if (cancel !== false) this.realHide();
}
onDialogMouseDown(event) {
this.lastEvent = event;
}
onBackgroundMouseDown(event) {
if (event != this.lastEvent)
this.hide();
}
onKeypress(event) {
if (event.keyCode == 27) // Esc
this.hide();
@ -107,8 +107,8 @@ module.component('vnDialog', {
tplButtons: 'tplButtons'
},
bindings: {
onOpen: '&',
onResponse: '&'
onOpen: '&?',
onResponse: '&?'
},
controller: Dialog
});

View File

@ -0,0 +1,176 @@
describe('Component vnDialog', () => {
let $componentController;
let $element;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
$element = angular.element('<div></div>');
controller = $componentController('vnDialog', {$element});
}));
describe('show()', () => {
it(`should define keypressHandler function, call addEventListener function and define element.style.display to block then call onOpen function`, () => {
window.innerHeight = 600;
window.innerWidth = 800;
controller.onOpen = () => {};
controller.dialog = {style: {}, offsetWidth: 780, offsetHeight: 581};
spyOn(controller.document, 'addEventListener');
spyOn(controller, 'onOpen');
controller.show();
expect(controller.keypressHandler).toBeDefined();
expect(controller.document.addEventListener).toHaveBeenCalledWith('keypress', controller.keypressHandler);
expect(controller.element.style.display).toEqual('block');
expect(controller.onOpen).toHaveBeenCalledWith();
});
it(`should define keypressHandler function, call addEventListener function and define element.style.display to block and never call onOpen function`, () => {
window.innerHeight = 600;
window.innerWidth = 800;
controller.dialog = {style: {}, offsetWidth: 781, offsetHeight: 581};
spyOn(controller.document, 'addEventListener');
controller.show();
expect(controller.keypressHandler).toBeDefined();
expect(controller.document.addEventListener).toHaveBeenCalledWith('keypress', controller.keypressHandler);
expect(controller.element.style.display).toEqual('block');
expect(controller.onOpen).not.toBeDefined();
});
});
describe('hide()', () => {
it(`should call fireResponse() and realHide()`, () => {
spyOn(controller, 'fireResponse');
spyOn(controller, 'realHide');
controller.hide();
expect(controller.fireResponse).toHaveBeenCalledWith();
expect(controller.realHide).toHaveBeenCalledWith();
});
});
describe('fireResponse()', () => {
it(`should return cancel as false`, () => {
let result = controller.fireResponse('I am the answer!');
expect(controller.onResponse).not.toBeDefined();
expect(result).toEqual(false);
});
it(`should return onResponse()`, () => {
let text = 'I am the answer!';
controller.onResponse = () => {
return {response: text};
};
let result = controller.fireResponse(text);
expect(result.response).toEqual(text);
});
});
describe('realHide()', () => {
it(`should set element.style.display and lastEvent properties and call removeEvenListener()`, () => {
spyOn(controller.document, 'removeEventListener');
expect(controller.element.style.display).not.toEqual('none');
expect(controller.lastEvent).not.toBeDefined();
controller.realHide();
expect(controller.element.style.display).toEqual('none');
expect(controller.document.removeEventListener).toHaveBeenCalledWith('keypress', controller.keypressHandler);
expect(controller.lastEvent).toEqual(null);
});
});
describe('onButtonClick()', () => {
it(`should call realHide if cancel isn't false`, () => {
controller.element = document.createElement('div');
controller.element.className = 'tpl-buttons';
let childElement = document.createElement('div');
childElement.className = 'button-bar';
controller.element.appendChild(childElement);
let event = {target: controller.element, attribute: true};
spyOn(controller, 'realHide');
spyOn(controller, 'fireResponse').and.returnValue(true);
controller.onButtonClick(event);
expect(controller.realHide).toHaveBeenCalledWith();
});
it(`should call fireResponse with the value of response`, () => {
controller.element = document.createElement('div');
controller.element.className = 'tpl-buttons';
let childElement = document.createElement('div');
childElement.className = 'button-bar';
controller.element.appendChild(childElement);
let attribute = document.createAttribute('response');
attribute.value = 'I am the response!';
controller.element.setAttributeNode(attribute);
spyOn(controller, 'fireResponse');
let event = {target: controller.element};
controller.onButtonClick(event);
expect(controller.fireResponse).toHaveBeenCalledWith('I am the response!');
});
});
describe('onDialogMouseDown()', () => {
it(`should set controller's lastEvent property`, () => {
controller.element = document.createElement('div');
let event = {target: controller.element};
controller.onDialogMouseDown(event);
expect(controller.lastEvent).toEqual(event);
});
});
describe('onBackgroundMouseDown()', () => {
it(`shouldn't call hide() function as event equals lastEvent`, () => {
controller.element = document.createElement('div');
let event = {target: controller.element};
controller.lastEvent = event;
spyOn(controller, 'hide');
controller.onBackgroundMouseDown(event);
expect(controller.hide).not.toHaveBeenCalledWith();
});
it(`should call hide() function as event doesn't equal lastEvent`, () => {
controller.element = document.createElement('div');
let event = {target: controller.element};
controller.lastEvent = event;
controller.lastEvent = 'the singularity event!';
spyOn(controller, 'hide');
controller.onBackgroundMouseDown(event);
expect(controller.hide).toHaveBeenCalledWith();
});
});
describe('onKeypress()', () => {
it(`should call hide() if the key pressed equal the code 27`, () => {
controller.element = document.createElement('div');
let event = {target: controller.element};
event.keyCode = 27;
spyOn(controller, 'hide');
controller.onKeypress(event);
expect(controller.hide).toHaveBeenCalledWith();
});
it(`should't call hide() as the key pressed equal the code 999`, () => {
controller.element = document.createElement('div');
let event = {target: controller.element};
event.keyCode = 999;
spyOn(controller, 'hide');
controller.onKeypress(event);
expect(controller.hide).not.toHaveBeenCalledWith();
});
});
});

View File

@ -19,8 +19,8 @@ function vnAcl(aclService, $timeout) {
$timeout(() => {
input.setAttribute("disabled", "true");
});
$element[0].querySelectorAll('i, vn-drop-down').forEach(i => {
i.parentNode.removeChild(i);
$element[0].querySelectorAll('i, vn-drop-down').forEach(element => {
element.parentNode.removeChild(element);
});
}
} else {

View File

@ -11,7 +11,8 @@ export function directive() {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.on('click', function(event) {
let dialog = $scope[kebabToCamel($attrs.vnDialog)];
let dialogKey = kebabToCamel($attrs.vnDialog);
let dialog = $scope[dialogKey];
if (dialog instanceof Dialog)
dialog.show();
event.preventDefault();

View File

@ -2,6 +2,8 @@ import {module} from '../module';
/**
* Sets the focus and selects the text on the input.
*
* @return {Object} The directive
*/
export function directive() {
return {

View File

@ -1,16 +0,0 @@
import {module} from '../module';
directive.$inject = ['$compile'];
function directive($compile) {
return {
restrict: 'A',
priority: 9999,
link: function(scope, element, attrs) {
element.removeAttr('vn-repeat');
element.attr('ng-repeat', attrs.vnRepeat);
$compile(element)(scope);
}
};
}
module.directive('vnRepeat', directive);

View File

@ -0,0 +1,54 @@
describe('Directive acl', () => {
let scope;
let element;
let compile;
let $timeout;
beforeEach(() => {
angular.mock.module('client');
});
compile = (hasPermissions, _element) => {
inject(($compile, $rootScope, aclService, _$timeout_) => {
spyOn(aclService, 'aclPermission').and.returnValue(hasPermissions);
scope = $rootScope.$new();
$timeout = _$timeout_;
element = angular.element(_element);
$compile(element)(scope);
scope.$digest();
});
};
it('should not disable the input element as the user has permision', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><input/></div>`;
compile(true, html);
let input = element.find('input');
expect(input).toBeDefined();
expect(input.attr('disabled')).toBeFalsy();
});
it('should delete the element as the user does not have permission and there is no action', () => {
let html = `<container><div vn-acl="administrative,client" vn-acl-action="anything but disabled"><input/></div></container>`;
compile(false, html);
expect(element.children().length).toEqual(0);
});
it('should disable the element as the action is to disable it but the user has no permission but present', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><input/></div>`;
compile(false, html);
let input = element.find('input');
$timeout.flush();
expect(input).toBeDefined();
expect(input.attr('disabled')).toBeTruthy();
});
it('should delete any element with the tag i and vn-drop-down', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><label><i/></label><input/></div>`;
compile(false, html);
expect(element.find('i').length).toBe(0);
});
});

View File

@ -0,0 +1,37 @@
describe('Directive dialog', () => {
let $scope;
let $element;
let element;
let compile;
let $componentController;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
compile = _element => {
inject(($compile, $rootScope) => {
$scope = $rootScope.$new();
$scope.myDialog = controller;
element = angular.element(_element);
$compile(element)($scope);
$scope.$digest();
});
};
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
$element = angular.element('<div></div>');
controller = $componentController('vnDialog', {$element});
}));
it('should call show() function if dialog is a instance of vnDialog', () => {
let html = `<div vn-dialog="myDialog"></div>`;
spyOn(controller, 'show');
compile(html);
element[0].click();
expect(controller.show).toHaveBeenCalledWith();
});
});

View File

@ -0,0 +1,55 @@
describe('Directive focus', () => {
let $scope;
let $element;
let compile;
beforeEach(() => {
angular.mock.module('client');
});
compile = (_element, _childElement) => {
inject(($compile, $rootScope) => {
$scope = $rootScope.$new();
$element = angular.element(_element);
if (_childElement) {
let childElement = angular.element(_childElement);
$element[0] < childElement;
$element[0].firstChild.focus = jasmine.createSpy(focus);
}
$element[0].focus = jasmine.createSpy('focus');
$element[0].select = jasmine.createSpy('select');
$compile($element)($scope);
$scope.$digest();
});
};
it('should call the querySelector function upon the input to redefine it with the expected selector then call focus', () => {
let html = `<div vn-focus><input></input></div>`;
let childHtml = '<input></input>';
compile(html, childHtml);
expect($element[0].firstChild.focus).toHaveBeenCalled();
});
it('should print a warning message on console', () => {
let html = `<potato vn-focus></potato>`;
console.warn = jasmine.createSpy('warn');
compile(html);
expect(console.warn).toHaveBeenCalledWith(`vnFocus: Can't find a focusable element`);
});
it('should call focus function on the element', () => {
let html = `<input vn-focus></input>`;
compile(html);
expect($element[0].focus).toHaveBeenCalledWith();
});
it('should call select function on the element', () => {
let html = `<input vn-focus></input>`;
compile(html);
expect($element[0].select).toHaveBeenCalledWith();
});
});

View File

@ -0,0 +1,43 @@
describe('Directive vnId', () => {
let $scope;
let $element;
let compile;
beforeEach(() => {
angular.mock.module('client');
});
compile = _element => {
inject(($compile, $rootScope) => {
$scope = $rootScope.$new();
$element = angular.element(_element);
$compile($element)($scope);
$scope.$digest();
});
};
it(`should throw an error when there's no id defined`, () => {
let html = `<form vn-id=""></form>`;
expect(() => {
compile(html);
}).toThrow(new Error(`vnId: Attribute can't be null`));
});
it(`should throw an error when these's no controller defined in $element[0]`, () => {
let html = `<div vn-id="1"></div>`;
expect(() => {
compile(html);
}).toThrow(new Error(`vnId: Can't find controller for element '1'`));
});
it(`should set the controller into the $scope as there are no errors being thrown`, () => {
let html = `<form vn-id="1"></form>`;
expect($scope['1']).not.toBeDefined();
compile(html);
expect($scope['1']).toBeDefined();
});
});

View File

@ -16,7 +16,14 @@
<input type="checkbox" ng-checked="item.checked" ng-if="$ctrl.multiple">
<div ng-transclude="vnItem">{{item.name}}</div>
</li>
<li ng-if="$ctrl.showLoadMore" class="dropdown__loadMore" tabIndex="-1" ng-class="{'active': $ctrl.itemsFiltered.length === $ctrl.activeOption}" ng-click="$ctrl.loadMore();$ctrl.show = true;" translate="Load More"></li>
<li
ng-if="$ctrl.loadMore&&!$ctrl.removeLoadMore"
class="dropdown__loadMore"
tabIndex="-1"
ng-class="{'active': $ctrl.itemsFiltered.length === $ctrl.activeOption, 'noMore' : !$ctrl.showLoadMore}"
ng-click="$ctrl.loadItems()"
translate="{{$ctrl.showLoadMore ? 'Show More' : 'No more results'}}"
></li>
</ul>
</vn-one>
</vn-vertical>

View File

@ -16,6 +16,7 @@ export default class DropDown {
get show() {
return this._show;
}
set show(value) {
let oldValue = this.show;
this._show = value;
@ -27,12 +28,14 @@ export default class DropDown {
this._focusingFilter = false;
}, 250);
}
}
}
get search() {
return this._search;
}
set search(value) {
let val = (value === undefined && value === '') ? null : value;
let val = (value === undefined || value === '') ? null : value;
this._search = val;
if (this.filterAction)
@ -40,9 +43,11 @@ export default class DropDown {
else
this.filterItems();
}
get activeOption() {
return this._activeOption;
}
set activeOption(value) {
if (value < 0) {
value = 0;
@ -51,6 +56,10 @@ export default class DropDown {
}
this.$timeout(() => {
this._activeOption = value;
// AutoLoad items with "scroll" (1st version):
if (value && value >= this.items.length - 3 && !this.removeLoadMore) {
this.loadItems();
}
});
}
@ -59,7 +68,9 @@ export default class DropDown {
}
onFilterRest() {
this.filterAction({search: this.search});
if (this.filterAction) {
this.filterAction({search: this.search});
}
}
$onChanges(changesObj) {
@ -117,6 +128,7 @@ export default class DropDown {
}
}
}
setScrollPosition() {
let dropdown = this.$element[0].querySelector('ul.dropdown');
let child = dropdown ? dropdown.childNodes[this.activeOption] : null;
@ -135,6 +147,13 @@ export default class DropDown {
}
}
loadItems() {
if (this.showLoadMore && this.loadMore) {
this.loadMore();
}
this.show = true;
}
$onInit() {
if (this.parent)
this.parent.addEventListener('keydown', e => this.onKeydown(e));
@ -144,6 +163,7 @@ export default class DropDown {
this.parent.removeEventListener('keydown', e => this.onKeydown(e));
}
}
DropDown.$inject = ['$element', '$filter', '$timeout'];
module.component('vnDropDown', {
@ -156,8 +176,9 @@ module.component('vnDropDown', {
selected: '=',
search: '=?',
loadMore: '&?',
removeLoadMore: '<?',
filterAction: '&?',
showLoadMore: '=?',
showLoadMore: '<?',
top: '<?',
itemWidth: '<?',
parent: '<?',

View File

@ -0,0 +1,335 @@
import './drop-down.js';
describe('Component vnDropDown', () => {
let $componentController;
let $timeout;
let $element;
let $filter;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$timeout_, _$filter_) => {
$componentController = _$componentController_;
$element = angular.element('<div><ul><li></li></ul></div>');
$timeout = _$timeout_;
$filter = _$filter_;
controller = $componentController('vnDropDown', {$element, $timeout, $filter});
}));
describe('show() setter', () => {
it(`should define controllers _show using the value received as argument`, () => {
controller._show = 'old value';
controller.show = 'new value';
expect(controller._show).toEqual('new value');
});
});
describe('search()', () => {
it(`should set controllers _search property with the value received`, () => {
controller.search = 'some filter valiue';
expect(controller._search).toEqual('some filter valiue');
});
it(`should set controllers _search property to null as the value received is an empty string`, () => {
controller.search = '';
expect(controller._search).toEqual(null);
});
it(`should call onFilterRest() if controllers filterAction is defined`, () => {
controller.filterAction = true;
spyOn(controller, 'onFilterRest');
controller.search = 'some filter valiue';
expect(controller.onFilterRest).toHaveBeenCalledWith();
});
it(`should call filterItems() if controllers filterAction is undefined`, () => {
controller.filterAction = undefined;
spyOn(controller, 'filterItems');
controller.search = 'some filter valiue';
expect(controller.filterItems).toHaveBeenCalledWith();
});
});
describe('activeOption() setter', () => {
it(`should set _activeOption as items.length if showLoadMore is defined if activeOption is bigger than items.length then call loadItems()`, () => {
spyOn(controller, 'loadItems');
controller.showLoadMore = true;
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.activeOption = 10;
$timeout.flush();
expect(controller._activeOption).toEqual(4);
expect(controller.loadItems).toHaveBeenCalledWith();
});
it(`should set _activeOption as activeOption if showLoadMore is defined if activeOption is smaller than items.length then call loadItems()`, () => {
spyOn(controller, 'loadItems');
controller.showLoadMore = true;
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.activeOption = 2;
$timeout.flush();
expect(controller._activeOption).toEqual(2);
expect(controller.loadItems).toHaveBeenCalledWith();
});
it(`should set _activeOption as items.length -1 if showLoadMore is not defined then call loadItems()`, () => {
spyOn(controller, 'loadItems');
controller.showLoadMore = undefined;
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.activeOption = 10;
$timeout.flush();
expect(controller._activeOption).toEqual(3);
expect(controller.loadItems).toHaveBeenCalledWith();
});
it(`should define _activeOption as activeOption and never call loadItems()`, () => {
spyOn(controller, 'loadItems');
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}, {id: 5, name: 'Doctor X'}];
controller.activeOption = 1;
$timeout.flush();
expect(controller._activeOption).toEqual(1);
expect(controller.loadItems).not.toHaveBeenCalledWith();
});
});
describe('filterItems() setter', () => {
it(`should set _itemsFiltered using the value of items`, () => {
controller.items = [{id: 1, name: 'Batman'}];
controller.filterItems();
expect(controller.itemsFiltered).toEqual(controller.items);
});
it(`should set _itemsFiltered with the filtered value of items`, () => {
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.search = 'Batman';
controller.filterItems();
expect(controller.itemsFiltered).toEqual([Object({id: 1, name: 'Batman'})]);
});
it(`should set _itemsFiltered an empty array`, () => {
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.search = 'the Joker';
expect(controller.itemsFiltered.length).toEqual(0);
});
});
describe('onFilterRest()', () => {
it(`should call the filterAction() with a constructed object as argument`, () => {
controller.filterAction = () => {};
controller.search = 'Batman';
spyOn(controller, 'filterAction');
controller.onFilterRest();
expect(controller.filterAction).toHaveBeenCalledWith({search: controller.search});
});
});
describe('$onChanges()', () => {
it(`should set the top css of the $element`, () => {
let argumentObject = {show: true, top: {currentValue: 100}};
spyOn(controller.$element, 'css');
controller.$onChanges(argumentObject);
expect(controller.$element.css).toHaveBeenCalledWith('top', '100px');
});
it(`should set the width css of the $element`, () => {
let argumentObject = {show: true, itemWidth: {currentValue: 100}};
spyOn(controller.$element, 'css');
controller.$onChanges(argumentObject);
expect(controller.$element.css).toHaveBeenCalledWith('width', '100px');
});
it(`should set the width css of the $element`, () => {
let argumentObject = {items: {id: 1, name: 'Batman'}};
spyOn(controller, 'filterItems');
controller.$onChanges(argumentObject);
expect(controller.filterItems).toHaveBeenCalledWith();
});
});
describe('clearSearch()', () => {
it(`should set the controllers search property to null`, () => {
controller.search = true;
controller.clearSearch();
expect(controller.search).toEqual(null);
});
});
describe('selectOption()', () => {
it(`should set controllers selected and show properties then call clearSearch() as _activeOption is smaller than items.length`, () => {
spyOn(controller, 'clearSearch');
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller._activeOption = 0;
controller.selectOption();
expect(controller.selected).toEqual(controller.items[controller._activeOption]);
expect(controller._show).toEqual(false);
expect(controller.clearSearch).toHaveBeenCalledWith();
});
it(`should not set controllers selected, show and never call clearSearch() as _activeOption is bigger than items.length`, () => {
spyOn(controller, 'clearSearch');
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller._activeOption = 100;
controller.selectOption();
expect(controller.selected).not.toBeDefined();
expect(controller._show).not.toBeDefined();
expect(controller.clearSearch).not.toHaveBeenCalledWith();
});
it(`should call loadMore() if the activeValue equals items.length`, () => {
controller.loadMore = () => {};
spyOn(controller, 'loadMore');
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller._activeOption = 4;
controller.showLoadMore = 4;
controller.selectOption();
expect(controller.loadMore).toHaveBeenCalledWith();
});
});
describe('onKeydown()', () => {
it(`should call selectOption() and preventDefault() if Enter key is pressed`, () => {
spyOn(controller, 'selectOption');
controller._show = true;
controller.element = document.createElement('div');
let event = {target: controller.element, preventDefault: () => {}};
event.keyCode = 13;
spyOn(event, 'preventDefault');
controller.onKeydown(event);
$timeout.flush();
expect(controller.selectOption).toHaveBeenCalledWith();
expect(event.preventDefault).toHaveBeenCalledWith();
});
it(`should call clearSearch() Esc key is pressed`, () => {
spyOn(controller, 'clearSearch');
controller._show = true;
controller.element = document.createElement('div');
let event = {target: controller.element};
event.keyCode = 27;
controller.onKeydown(event);
expect(controller.clearSearch).toHaveBeenCalledWith();
});
it(`should call clearSearch() Esc key is pressed and take off 1 from _activeOption`, () => {
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
spyOn(controller, 'setScrollPosition');
controller._show = true;
controller.element = document.createElement('div');
let event = {target: controller.element};
event.keyCode = 38;
controller._activeOption = 1;
controller.onKeydown(event);
$timeout.flush();
expect(controller.setScrollPosition).toHaveBeenCalledWith();
expect(controller._activeOption).toEqual(0);
});
it(`should call clearSearch() Esc key is pressed and add up 1 to _activeOption`, () => {
controller.items = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
spyOn(controller, 'setScrollPosition');
controller._show = true;
controller.element = document.createElement('div');
let event = {target: controller.element};
event.keyCode = 40;
controller._activeOption = 1;
controller.onKeydown(event);
$timeout.flush();
expect(controller.setScrollPosition).toHaveBeenCalledWith();
expect(controller._activeOption).toEqual(2);
});
});
describe('setScrollPosition()', () => {
it(`should call child.scrollIntoView if defined `, () => {
$element[0].firstChild.setAttribute('class', 'dropdown');
let child = $element[0].firstChild.firstChild;
child.scrollIntoView = () => {};
spyOn(child, 'scrollIntoView');
controller._activeOption = 0;
controller.setScrollPosition();
expect(child.scrollIntoView).toHaveBeenCalledWith();
});
});
describe('selectItem()', () => {
it(`should pass item to selected and set controller._show to false`, () => {
let item = {id: 1, name: 'Batman'};
controller.selectItem(item);
expect(controller.selected).toEqual(item);
expect(controller._show).toEqual(false);
});
it(`should pass item to selected and set controller._show to true if the controller.multiple is defined`, () => {
let item = {id: 1, name: 'Batman'};
controller.multiple = true;
controller.selectItem(item);
expect(controller.selected).toEqual(item);
expect(controller._show).toEqual(true);
});
});
describe('loadItems()', () => {
it(`should set controller._show to true`, () => {
controller.loadItems();
expect(controller._show).toEqual(true);
});
it(`should call loadMore() and then set controller._show to true`, () => {
controller.showLoadMore = () => {};
controller.loadMore = () => {};
spyOn(controller, 'loadMore');
controller.loadItems();
expect(controller._show).toEqual(true);
expect(controller.loadMore).toHaveBeenCalledWith();
});
});
describe('$onInit()', () => {
it(`should add an event listener to the parent element`, () => {
spyOn(controller.parent, 'addEventListener');
controller.$onInit();
expect(controller.parent.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));
});
});
describe('$onDestroy()', () => {
it(`should remove an event listener from the parent element`, () => {
spyOn(controller.parent, 'removeEventListener');
controller.$onDestroy();
expect(controller.parent.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function));
});
});
});

View File

@ -39,6 +39,11 @@ vn-drop-down {
color: rgb(255,171,64);
font-weight: 700;
}
&.dropdown__loadMore.noMore{
color:#424242;
font-weight: 700;
opacity: 0.7;
}
input[type=checkbox]{
float: left;
margin: 5px 5px 0 0;

View File

@ -0,0 +1,43 @@
import './grid-header.js';
describe('Component vnGridHeader', () => {
let $componentController;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnGridHeader', {});
}));
describe('selectColum()', () => {
it(`should set controller currentColumn to equal the argument received`, () => {
let col = {columnStuff: 'some stuff'};
controller.selectColum(col);
expect(controller.currentColumn).toEqual(col);
});
it(`should set controller currentColumn.order to undefined then set currentColumn to equal the argument received`, () => {
controller.currentColumn = {field: 'some field', order: 'ordered'};
let col = {columnStuff: 'some stuff'};
controller.selectColum(col);
expect(controller.currentColumn.order).not.toBeDefined();
expect(controller.currentColumn).toEqual(col);
});
it(`should set controller currentColumn.order to undefined then call onOrder passing currentColumn as argument`, () => {
controller.onOrder = () => {};
spyOn(controller, 'onOrder');
let col = {columnStuff: 'some stuff'};
controller.selectColum(col);
expect(controller.currentColumn).toEqual(col);
expect(controller.onOrder).toHaveBeenCalledWith(col);
});
});
});

View File

@ -1,5 +1,8 @@
<div class="icon-menu">
<vn-icon-button icon="{{::$ctrl.icon}}"></vn-icon-button>
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored icon-square icon-menu__button">
<vn-icon vn-none icon="{{::$ctrl.icon}}"></vn-icon>
<vn-icon vn-none class="icon-menu__arrow_down" icon="keyboard_arrow_down" ng-click="$ctrl.showDropDown = true"></vn-icon>
</button>
<div ng-if="!$ctrl.findMore">
<vn-drop-down
items="$ctrl.items"

View File

@ -11,12 +11,15 @@ export default class IconMenu {
this.findMore = false;
this.element = $element[0];
}
get showDropDown() {
return this._showDropDown;
}
set showDropDown(value) {
this._showDropDown = value;
}
findItems(search) {
if (!this.url)
return this.items ? this.items : [];
@ -40,6 +43,7 @@ export default class IconMenu {
this.getItems();
}
}
getItems() {
let filter = {};
@ -66,6 +70,7 @@ export default class IconMenu {
}
);
}
$onInit() {
if (!this.items && this.url) {
this.items = [];
@ -104,6 +109,7 @@ export default class IconMenu {
this.$element.unbind('focusout');
}
}
IconMenu.$inject = ['$element', '$http', '$timeout'];
module.component('vnIconMenu', {

View File

@ -0,0 +1,84 @@
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_;
$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();
});
});
});
});

View File

@ -1,3 +1,12 @@
vn-icon-menu{
position: relative;
.icon-menu__button {
padding: 0 10px;
}
vn-icon{
float: left;
}
vn-icon.icon-menu__arrow_down{
margin:2px 0 0 5px;
}
}

View File

@ -1,4 +1,7 @@
import ngModule from './module';
import {module} from '../module';
var acl = window.salix ? window.salix.acl : {};
module.constant('aclConstant', acl);
aclService.$inject = ['aclConstant'];
function aclService(aclConstant) {
@ -29,4 +32,4 @@ function aclService(aclConstant) {
};
}
ngModule.service('aclService', aclService);
module.service('aclService', aclService);

View File

@ -1,6 +1,7 @@
import {module} from '../module';
const isEqual = angular.equals;
export default isEqual;
export const NAME = 'equalsObject';

View File

@ -4,6 +4,7 @@ import './template';
import './getTemplate';
import './app';
import './interceptor';
import './aclService';
export * from './util';
export {default as splitingRegister} from './splitingRegister';

View File

@ -5,7 +5,7 @@ export default function getModifiedData(object, objectOld) {
var newObject = {};
for (var k in object) {
var val = object[k];
var valOld = objectOld[k];
var valOld = objectOld[k] === undefined ? null : objectOld[k];
if (!isEqual(val, valOld)) {
if (val instanceof Object) {

View File

@ -6,5 +6,6 @@
"Save": "Guardar",
"Add": "Añadir",
"Search": "Buscar",
"Load More": "Cargar más"
"Show More": "Ver más",
"No more results" : "No hay más resultados"
}

View File

@ -1,4 +1,4 @@
<vn-vertical class="multi-check" vn-none class="multi-check {{$ctrl.className}}" tabindex="-1" ng-blur="$ctrl.showDropDown = false">
<vn-vertical class="multi-check" vn-none class="multi-check {{$ctrl.className}}" tabindex="1" ng-blur="$ctrl.onBlur()">
<vn-one>
<vn-horizontal>
<vn-none class="primaryCheckbox" ng-if="$ctrl.checkAll===0">

View File

@ -2,58 +2,28 @@ import {module} from '../module';
import './multi-check.scss';
/**
* Draw checkbox with a drop-down and multi options
* @param {SmallInt} checkAll Primary input-check state: 0 -> uncheck, 1 -> checked, 2 -> indeterminate checked
* @param {Array} options List of options shown in drop-down
* @param {Array} models Elements to check / unCheck
* @param {String} className Optional css class name
*/
export default class MultiCheck {
constructor() {
constructor($timeout) {
this.$timeout = $timeout;
this._checkAll = 0;
this._models = [];
this.type = {};
this._type = {};
this.showDropDown = false;
}
get models() {
return this._models;
get type() {
return this._type;
}
set models(value) {
this._models = value;
}
get checkAll() {
return this._checkAll;
}
set checkAll(value) {
this._checkAll = value;
this.switchChecks();
}
switchChecks() {
if (this.models)
this.models.forEach(
el => {
let checked;
if (this.type.id && this.type.id !== 'all' && this.type.id !== 'any') {
if (this.type.id.length > 3 && this.type.id.substr(0, 3) === 'no-') {
checked = el[this.type.id.replace('no-', '')] == null;
} else if (this.type.id.length > 6 && this.type.id.substr(0, 6) === 'equal-') {
let label = this.type.id.replace('equal-', '');
checked = (el[label] && el[label] === this.type.name);
} else {
checked = el[this.type.id] != null;
}
} else {
checked = this.checkAll === 1;
}
el.checked = checked;
}
);
}
$onChanges() {
this.type = {};
this.checkAll = 0;
}
$doCheck() {
if (this.type && this.type.id) {
switch (this.type.id) {
set type(value) {
if (value && value.id) {
this._type = value;
switch (value.id) {
case 'all':
this.checkAll = 1;
break;
@ -64,11 +34,64 @@ export default class MultiCheck {
this.checkAll = 2;
break;
}
this.type = {};
}
this._type = {};
this.showDropDown = false;
}
get models() {
return this._models;
}
set models(value) {
this._models = value;
}
get checkAll() {
return this._checkAll;
}
set checkAll(value) {
this._checkAll = value;
this.switchChecks();
}
switchChecks() {
if (this.models)
this.models.forEach(
el => {
let checked;
if (this.type.id && this.type.id !== 'all' && this.type.id !== 'any') {
if (this.type.id.length > 3 && this.type.id.substr(0, 3) === 'no-') {
let label = this.type.id.replace('no-', '');
checked = Boolean(el[label]) === false;
} else if (this.type.id.length > 6 && this.type.id.substr(0, 6) === 'equal-') {
let label = this.type.id.replace('equal-', '');
checked = (el[label] && el[label] === this.type.name);
} else {
checked = Boolean(el[this.type.id]) === true;
}
} else {
checked = this.checkAll === 1;
}
el.checked = checked;
}
);
}
$onChanges() {
this.type = {};
this.checkAll = 0;
}
onBlur() {
this.$timeout(() => {
this.showDropDown = false;
}, 200);
}
}
MultiCheck.$inject = [];
MultiCheck.$inject = ['$timeout'];
module.component('vnMultiCheck', {
template: require('./multi-check.html'),
@ -76,7 +99,7 @@ module.component('vnMultiCheck', {
bindings: {
checkAll: '=',
options: '<',
models: '=',
models: '<',
className: '@?'
}
});

View File

@ -0,0 +1,163 @@
import './multi-check.js';
describe('Component vnMultiCheck', () => {
let $componentController;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnMultiCheck', {});
}));
describe('models()', () => {
it(`should set controller _models property with the argument received`, () => {
let argument = 'I am the model';
controller.models = argument;
expect(controller._models).toEqual(argument);
});
});
describe('checkAll()', () => {
it(`should set controller _checkAll property with the argument received then call switchChecks()`, () => {
let argument = 'I am the model';
spyOn(controller, 'switchChecks');
controller.checkAll = argument;
expect(controller._checkAll).toEqual(argument);
expect(controller.switchChecks).toHaveBeenCalledWith();
});
});
describe('switchChecks()', () => {
it(`should set checked property inside each existing elemenet when id begings with no-`, () => {
controller.type = {id: 'no-name'};
controller.models = [
{name: 'name'},
{name: null}
];
expect(controller._models[0].checked).not.toBeDefined();
expect(controller._models[1].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(controller._models[0].checked).toBeTruthy();
expect(controller._models[1].checked).toBeTruthy();
});
it(`should set checked property inside each existing elemenet when id begings with equal-`, () => {
controller.type = {id: 'equal-name', name: 'name'};
controller.models = [
{name: null},
{name: 'name'}
];
expect(controller._models[0].checked).not.toBeDefined();
expect(controller._models[1].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(controller._models[0].checked).toBeTruthy();
expect(controller._models[1].checked).toBeTruthy();
});
it(`should set checked property inside each existing elemenet when begings with anything but any, all, no- or equal-`, () => {
controller.type = {id: 'name'};
controller.models = [
{name: null},
{name: 'name'}
];
expect(controller._models[0].checked).not.toBeDefined();
expect(controller._models[1].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(controller._models[0].checked).toBeTruthy();
expect(controller._models[1].checked).toBeTruthy();
});
describe('when id is any', () => {
it('should set element checked property based on controller._checkAll', () => {
controller.type = {id: 'any'};
controller.models = [
{name: 'name'}
];
expect(controller._models[0].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(controller._models[0].checked).toBeTruthy();
controller._checkAll = 0;
controller.switchChecks();
expect(controller._models[0].checked).toBeFalsy();
controller._checkAll = 2;
controller.switchChecks();
expect(controller._models[0].checked).toBeFalsy();
});
});
describe('when id is all', () => {
it('should set element checked property based on controller._checkAll property', () => {
controller.type = {id: 'all'};
controller.models = [
{name: 'name'}
];
expect(controller._models[0].checked).not.toBeDefined();
controller._checkAll = 1;
controller.switchChecks();
expect(controller._models[0].checked).toBeTruthy();
controller._checkAll = 0;
controller.switchChecks();
expect(controller._models[0].checked).toBeFalsy();
controller._checkAll = 2;
controller.switchChecks();
expect(controller._models[0].checked).toBeFalsy();
});
});
});
describe('$onChanges()', () => {
it('should set controller.type to empty object and checkAll to zero', () => {
controller.type = {id: 'all'};
controller._checkAll = 1;
controller.$onChanges();
expect(controller.type).toEqual({});
expect(controller._checkAll).toEqual(0);
});
});
describe('type setter', () => {
it('should set controller.type to empty object and checkAll based on controller.type.id', () => {
let value = {id: 'all'};
controller._checkAll = 0;
controller.type = value;
expect(controller.type).toEqual({});
expect(controller._checkAll).toEqual(1);
value = {id: 'any'};
controller.type = value;
expect(controller.type).toEqual({});
expect(controller._checkAll).toEqual(0);
value = {id: 'any other id name'};
controller.type = value;
expect(controller.type).toEqual({});
expect(controller._checkAll).toEqual(2);
});
});
});

View File

@ -1,6 +1,7 @@
<paging
page="$ctrl.currentPage"
page-size="$ctrl.numPerPage"
page-size="$ctrl.numPerPage"
ng-if="$ctrl.numPages>1"
total="$ctrl.numItems"
show-prev-next="true"
show-first-last="false"

View File

@ -5,6 +5,7 @@ export default class Paging {
get numPages() {
return Math.ceil(this.numItems / this.numPerPage);
}
constructor($http, $scope) {
this.$http = $http;
this.$scope = $scope;
@ -13,6 +14,7 @@ export default class Paging {
this.numItems = 0;
$scope.$watch('$ctrl.index.model.length', () => this.onModelUpdated());
}
$onChanges(changes) {
if (!this.index) return;
this.numPerPage = this.index.filter.size;
@ -20,14 +22,15 @@ export default class Paging {
if (changes.total)
this.numItems = changes.total.currentValue;
}
onModelUpdated() {
let index = this.index;
let filter = index.filter;
if (filter.page >= this.numPages
&& index.model.length >= this.numPerPage)
if (filter.page >= this.numPages && index.model.length >= this.numPerPage)
this.numItems = filter.page * filter.size + 1;
}
onPageChange(page) {
this.index.filter.page = page;
if (typeof this.pageChange === 'undefined') {
@ -37,6 +40,7 @@ export default class Paging {
}
}
}
Paging.$inject = ['$http', '$scope'];
export const NAME = 'vnPaging';
@ -49,4 +53,5 @@ export const COMPONENT = {
},
controller: Paging
};
module.component(NAME, COMPONENT);

View File

@ -0,0 +1,69 @@
import './paging.js';
describe('Component vnPaging', () => {
let $componentController;
let $scope;
let $httpBackend;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
controller = $componentController('vnPaging', {$scope, $httpBackend});
}));
describe('$onChanges()', () => {
it(`should define numberPage and currentPage based on index.filter properties`, () => {
controller.index = {filter: {size: 'something', page: 'something else'}};
controller.$onChanges({index: 'simpleChange', currentValue: 'current value', previousValue: 'previous value'});
expect(controller.numPerPage).toBe(controller.index.filter.size);
expect(controller.currentPage).toEqual(controller.index.filter.page);
});
it(`should define numItems based on changes.total.currentValue`, () => {
controller.index = {filter: {size: 'something', page: 'something else'}};
controller.$onChanges({total: {currentValue: 'current value'}});
expect(controller.numItems).toEqual('current value');
});
});
describe('onModelUpdated()', () => {
it(`should define controllers numItems as the result of page times size plus one`, () => {
controller.numPerPage = 2;
controller.index = {
filter: {size: 10, page: 10},
model: ['one mother..', 'another model..', 'last model..']
};
controller.onModelUpdated();
expect(controller.numItems).toBe(101);
});
});
describe('onPageChange()', () => {
it(`should call accept() since pageChange property is undefined`, () => {
controller.index = {accept: () => {}, filter: {page: 0}};
spyOn(controller.index, 'accept');
controller.onPageChange(100);
expect(controller.index.accept).toHaveBeenCalledWith();
});
it(`should call pageChange() since pageChange property isn't undefined`, () => {
controller.index = {accept: () => {}, filter: {page: 0}};
controller.pageChange = true;
spyOn(controller, 'pageChange');
controller.onPageChange(100);
expect(controller.pageChange).toHaveBeenCalledWith();
});
});
});

View File

@ -13,6 +13,7 @@ export function directive(vnPopover) {
}
};
}
module.directive('vnPopover', directive);
export class Popover {
@ -69,6 +70,7 @@ export class Popover {
width = innerWidth - dblMargin;
style.width = width + 'px';
}
if (height + dblMargin > innerHeight) {
height = innerHeight - dblMargin;
style.height = height + 'px';
@ -82,11 +84,13 @@ export class Popover {
if (left + width > innerWidth)
left -= (left + width) - innerWidth + margin;
if (top + height > innerHeight)
top -= height + parentNode.offsetHeight + spacing * 2;
if (left < 0)
left = screenMargin;
if (top < 0)
top = screenMargin;
@ -102,6 +106,7 @@ export class Popover {
}
return popoverId;
}
showComponent(childComponent, $scope, parent) {
let childElement = this.document.createElement(childComponent);
let id = 'popover-' + this.popOpens;
@ -111,12 +116,14 @@ export class Popover {
this.show(childElement, parent, id);
return childElement;
}
_checkOpens() {
this.popOpens = this.document.querySelectorAll('*[id^="popover-"]').length;
if (this.popOpens === 0) {
this._destroy();
}
}
_removeElement(val) {
if (!val) return;
let element = angular.element(val);
@ -136,6 +143,7 @@ export class Popover {
}
this._checkOpens();
}
hideChilds(id) {
let popovers = this.document.querySelectorAll('*[id^="popover-"]');
let idNumber = parseInt(id.split('-')[1], 10);
@ -147,6 +155,7 @@ export class Popover {
);
this._checkOpens();
}
hideAll() {
let popovers = this.document.querySelectorAll('*[id^="popover-"]');
popovers.forEach(
@ -156,6 +165,7 @@ export class Popover {
);
this._checkOpens();
}
_findPopOver(node) {
while (node != null) {
if (node.id && node.id.startsWith('popover-')) {
@ -165,6 +175,7 @@ export class Popover {
}
return null;
}
onDocMouseDown(event) {
let targetId = this._findPopOver(event.target);
if (targetId) {
@ -173,6 +184,7 @@ export class Popover {
this.hideAll();
}
}
onDocKeyDown(event) {
if (event.keyCode === 27) {
let targetId = this._findPopOver(this.lastTarget);
@ -184,9 +196,12 @@ export class Popover {
this.lastTarget = null;
}
}
onPopoverMouseDown(event) {
this.lastTarget = event.target;
}
}
Popover.$inject = ['$document', '$compile', '$transitions'];
module.service('vnPopover', Popover);

View File

@ -7,12 +7,12 @@ export const NAME = util.getName(_NAME);
directive.$inject = [resolveFactory.NAME];
export function directive(resolve) {
return{
return {
restrict: 'E',
template: function(_, attrs) {
return resolve.getTemplate(_NAME, attrs);
return resolve.getTemplate(_NAME, attrs);
}
}
};
}
_module.directive(NAME, directive);

View File

@ -15,7 +15,7 @@ export function factory() {
enabled: 'true',
className: DEFAULT_CLASS
}
}
};
}
_module.factory(NAME, factory);

View File

@ -0,0 +1,63 @@
import './spinner.js';
describe('Component vnSpinner', () => {
let $componentController;
let $scope;
let $element;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$element = angular.element('<div><div></div></div>');
controller = $componentController('vnSpinner', {$scope, $element});
}));
describe('enable()', () => {
it(`should call start() based on a boolean value passed as argument`, () => {
spyOn(controller, 'start');
spyOn(controller, 'stop');
controller.enable = true;
expect(controller.start).toHaveBeenCalledWith();
expect(controller.stop).not.toHaveBeenCalledWith();
});
it(`should call stop() based on a boolean value passed as argument`, () => {
spyOn(controller, 'start');
spyOn(controller, 'stop');
controller.enable = false;
expect(controller.start).not.toHaveBeenCalledWith();
expect(controller.stop).toHaveBeenCalledWith();
});
});
describe('start()', () => {
it(`should call start() on the controller.materialSpinner then set controllers._enable to be truthy`, () => {
controller.spinner = {MaterialSpinner: {start: () => {}}};
spyOn(controller.spinner.MaterialSpinner, 'start');
controller._enable = false;
controller.start();
expect(controller.spinner.MaterialSpinner.start).toHaveBeenCalledWith();
expect(controller._enable).toBeTruthy();
});
});
describe('stop()', () => {
it(`should call stop() on the controller.materialSpinner then set controllers._enable to be truthy`, () => {
controller.spinner = {MaterialSpinner: {stop: () => {}}};
spyOn(controller.spinner.MaterialSpinner, 'stop');
controller._enable = true;
controller.stop();
expect(controller.spinner.MaterialSpinner.stop).toHaveBeenCalledWith();
expect(controller._enable).toBeFalsy();
});
});
});

View File

@ -16,8 +16,8 @@
ng-readonly="$ctrl.readonly"
/>
<div class="mdl-chip__action">
<i class="material-icons pointer" ng-show="$ctrl.hasValue&&($ctrl.hasFocus||$ctrl.hasMouseIn)" ng-click="$ctrl.clear()">clear</i>
<i class="material-icons" ng-if="$ctrl.hasInfo" vn-tooltip="{{$ctrl.info}}" tooltip-position="up">info_outline</i>
<i class="material-icons pointer" ng-show="$ctrl.hasValue&&($ctrl.hasFocus||$ctrl.hasMouseIn)" ng-click="$ctrl.clear()">clear</i>
</div>
<label class="mdl-textfield__label" translate>{{$ctrl.label}}</label>
</div>

View File

@ -36,7 +36,7 @@ export default class TextfieldController extends Component {
this.hasValue = Boolean(this._value);
this.mdlUpdate();
}
mdlUpdate() {
let mdlField = this.$element[0].firstChild.MaterialTextfield;
if (mdlField)
@ -48,6 +48,7 @@ export default class TextfieldController extends Component {
this.input.focus();
}
}
TextfieldController.$inject = ['$element', '$scope', '$attrs', '$timeout', normalizerFactory.NAME];
module.component('vnTextfield', {

View File

@ -0,0 +1,47 @@
import './textfield.js';
describe('Component vnTextfield', () => {
let $componentController;
let $scope;
let $attrs;
let $timeout;
let $element;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$attrs = {};
$timeout = _$timeout_;
$element = angular.element('<div><input></div>');
controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout});
}));
describe('value() setter', () => {
it(`should set _value, input.value and hasValue properties to null, '' and false then call mdlUpdate()`, () => {
spyOn(controller, 'mdlUpdate');
let testValue = '';
controller.value = testValue;
expect(controller._value).toEqual(null);
expect(controller.input.value).toEqual(testValue);
expect(controller.hasValue).toEqual(Boolean(testValue));
expect(controller.mdlUpdate).toHaveBeenCalledWith();
});
it(`should set _value, input.value and hasValue propertiest to test, test and true then call mdlUpdate()`, () => {
spyOn(controller, 'mdlUpdate');
let testValue = 'test';
controller.value = testValue;
expect(controller._value).toEqual(testValue);
expect(controller.input.value).toEqual(testValue);
expect(controller.hasValue).toEqual(Boolean(testValue));
expect(controller.mdlUpdate).toHaveBeenCalledWith();
});
});
});

View File

@ -1,7 +1,7 @@
import {module} from '../module';
import Component from '../lib/component';
import getModifiedData from '../lib/modified';
// import copyObject from '../lib/copy';
import copyObject from '../lib/copy';
import isEqual from '../lib/equals';
/**
@ -22,8 +22,9 @@ export default class Watcher extends Component {
this.state = null;
this.deregisterCallback = $transitions.onStart({},
transition => this.callback(transition));
this.copyData();
this.updateOriginalData();
}
$onInit() {
if (this.get && this.url) {
this.fetchData();
@ -31,21 +32,24 @@ export default class Watcher extends Component {
throw new Error('Error: Parameter url ommitted');
}
}
$onChanges(changes) {
if (this.data) {
this.copyData();
this.updateOriginalData();
}
}
$onDestroy() {
this.deregisterCallback();
}
fetchData() {
let id = this.data[this.idField];
// return new Promise((resolve, reject) => {
this.$http.get(`${this.url}/${id}`).then(
json => {
this.data = this.copyObject(json.data);
this.copyData();
this.data = copyObject(json.data);
this.updateOriginalData();
}
// json => reject(json)
);
@ -55,7 +59,7 @@ export default class Watcher extends Component {
* Submits the data and goes back in the history.
*/
submitBack() {
this.submit().then(
return this.submit().then(
() => this.window.history.back()
);
}
@ -65,7 +69,7 @@ export default class Watcher extends Component {
* @param {String} state The state name
*/
submitGo(state) {
this.submit().then(
return this.submit().then(
() => this.$state.go(state)
);
}
@ -120,43 +124,49 @@ export default class Watcher extends Component {
);
});
}
writeData(json, resolve) {
Object.keys(this.data).forEach(
key => {
this.data[key] = json.data[key];
}
);
this.copyData();
writeData(json, resolve) {
Object.assign(this.data, json.data);
this.updateOriginalData();
resolve(json);
}
noChanges(resolve) {
this.vnApp.showMessage(
this.$translate.instant('No changes to save')
);
resolve();
}
invalidForm(resolve) {
this.vnApp.showMessage(
this.$translate.instant('Some fields are invalid')
);
resolve();
}
copyData() {
this.orgData = this.copyObject(this.data);
updateOriginalData() {
this.orgData = this.copyInNewObject(this.data);
}
copyObject(data) {
let copy = {};
if (data) {
copyInNewObject(data) {
let newCopy = {};
if (data && typeof data === 'object') {
Object.keys(data).forEach(
val => {
if (data[val] !== "" && data[val] !== undefined && data[val] !== null)
copy[val] = data[val];
if (data[val] !== "" && data[val] !== undefined && data[val] !== null) {
if (typeof data[val] === 'object') {
newCopy[val] = this.copyInNewObject(data[val]);
} else {
newCopy[val] = data[val];
}
}
}
);
}
return copy;
return newCopy;
}
callback(transition) {
let dataChanged = this.dataChanged();
if (!this.state && dataChanged) {
@ -167,19 +177,22 @@ export default class Watcher extends Component {
return true;
}
dataChanged() {
let newData = this.copyObject(this.data);
let newData = this.copyInNewObject(this.data);
return !isEqual(newData, this.orgData);
}
onConfirmResponse(response) {
if (response === 'ACCEPT') {
this.data = this.copyObject(this.orgData);
Object.assign(this.data, this.orgData);
this.$state.go(this.state);
} else {
this.state = null;
}
}
}
Watcher.$inject = ['$element', '$scope', '$state', '$transitions', '$http', 'vnApp', '$translate'];
module.component('vnWatcher', {

View File

@ -0,0 +1,293 @@
import './watcher.js';
import getModifiedData from '../lib/modified';
describe('Component vnWatcher', () => {
let $componentController;
let $scope;
let $element;
let $state;
let $transitions;
let $httpBackend;
let vnApp;
let $translate;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_, _$transitions_, _$httpBackend_, _vnApp_, _$translate_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$element = angular.element('<div></div>');
$state = _$state_;
vnApp = _vnApp_;
$transitions = _$transitions_;
$httpBackend = _$httpBackend_;
$translate = _$translate_;
controller = $componentController('vnWatcher', {$scope, $element, $state, vnApp, $transitions, $httpBackend, $translate});
}));
describe('$onInit()', () => {
it(`should call fetchData() if controllers get and url properties are defined`, () => {
controller.get = () => {};
controller.url = 'test.com';
spyOn(controller, 'fetchData');
controller.$onInit();
expect(controller.fetchData).toHaveBeenCalledWith();
});
it(`should throw an error if $onInit is called without url defined`, () => {
controller.get = () => {};
expect(function() {
controller.$onInit();
}).toThrow(new Error('Error: Parameter url ommitted'));
});
});
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();
});
});
describe('fetchData()', () => {
it(`should perform a query then store the received data into controller.data and call updateOriginalData()`, () => {
spyOn(controller, 'updateOriginalData');
let json = {data: 'some data'};
controller.data = [1];
controller.idField = 0;
controller.url = 'test.com';
$httpBackend.whenGET('test.com/1').respond(json);
$httpBackend.expectGET('test.com/1');
controller.fetchData();
$httpBackend.flush();
expect(controller.data).toEqual({data: 'some data'});
expect(controller.updateOriginalData).toHaveBeenCalledWith();
});
});
describe('submitBack()', () => {
it(`should call controller.window.history.back() function after calling controllers submit() function`, done => {
spyOn(controller, 'submit').and.returnValue(Promise.resolve());
spyOn(controller.window.history, 'back');
controller.submitBack()
.then(() => {
expect(controller.submit).toHaveBeenCalledWith();
expect(controller.window.history.back).toHaveBeenCalledWith();
done();
});
});
});
describe('submitGo()', () => {
it(`should call controller.$state.go() function after calling controllers submit() function`, done => {
spyOn(controller, 'submit').and.returnValue(Promise.resolve());
spyOn(controller.$state, 'go');
let state = 'the state';
controller.submitGo(state)
.then(() => {
expect(controller.submit).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith(state);
done();
});
});
});
describe('submit()', () => {
describe('when controller.form', () => {
it(`should call controller.form.setSubminted if controller.form is defined`, () => {
controller.form = {$setSubmitted: () => {}};
spyOn(controller.form, '$setSubmitted');
controller.submit();
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()', () => {
it(`should set controller.save.model property`, () => {
controller.save = {};
controller.data = {originalInfo: 'original data', info: 'new data'};
controller.orgData = {originalInfo: 'original data'};
controller.submit();
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', () => {
it(`should perform a query then call controller.writeData()`, () => {
controller.dataChanged = () => {
return true;
};
controller.data = {id: 2};
controller.orgData = {id: 1};
let changedData = getModifiedData(controller.data, controller.orgData);
controller.idField = 'id';
controller.url = 'test.com';
let json = {data: 'some data'};
spyOn(controller, 'writeData').and.callThrough();
$httpBackend.whenPATCH(`${controller.url}/1`, changedData).respond(json);
$httpBackend.expectPATCH(`${controller.url}/1`);
controller.submit()
.then(() => {
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
done();
});
$httpBackend.flush();
});
});
it(`should perform a POST query then call controller.writeData()`, () => {
controller.dataChanged = () => {
return true;
};
controller.data = {id: 2};
controller.orgData = {id: 1};
controller.url = 'test.com';
let json = {data: 'some data'};
spyOn(controller, 'writeData').and.callThrough();
$httpBackend.whenPOST(`${controller.url}`, controller.data).respond(json);
$httpBackend.expectPOST(`${controller.url}`, controller.data);
controller.submit()
.then(() => {
expect(controller.writeData).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function));
done();
});
$httpBackend.flush();
});
});
describe('writeData()', () => {
it(`should call Object.asssign() function over controllers.data with json.data, then call updateOriginalData function and finally call resolve() function`, () => {
spyOn(controller, 'updateOriginalData');
controller.data = {};
let json = {data: 'some data'};
let resolve = jasmine.createSpy('resolve');
controller.writeData(json, resolve);
expect(controller.updateOriginalData).toHaveBeenCalledWith();
expect(resolve).toHaveBeenCalledWith(json);
});
});
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(`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`, () => {
$scope.confirm = {show: jasmine.createSpy('show')};
controller.dataChanged = () => {
return true;
};
controller.state = undefined;
let transition = {to: () => {
return {name: 'Batman'};
}};
let result = controller.callback(transition);
expect(controller.state).toEqual('Batman');
expect(controller.$scope.confirm.show).toHaveBeenCalledWith();
expect(result).toBeFalsy();
});
});
describe(`when dataChanged() returns false and/or there's a state in the controller`, () => {
it(`should return true`, () => {
$scope.confirm = {show: jasmine.createSpy('show')};
controller.dataChanged = () => {
return false;
};
controller.state = 'the state';
let transition = {to: () => {
return {name: 'Batman'};
}};
let result = controller.callback(transition);
expect(result).toBeTruthy();
});
});
});
describe(`onConfirmResponse()`, () => {
describe(`when response is ACCEPT`, () => {
it(`should call Object.assing on controlle.data with controller.orgData then call go() on state`, () => {
let response = 'ACCEPT';
controller.data = {};
controller.orgData = {name: 'Batman'};
controller.$state = {go: jasmine.createSpy('go')};
controller.state = 'Batman';
controller.onConfirmResponse(response);
expect(controller.data).toEqual(controller.orgData);
expect(controller.$state.go).toHaveBeenCalledWith(controller.state);
});
});
describe(`when response is not ACCEPT`, () => {
it(`should set controller.state to null`, () => {
let response = 'anything but ACCEPT';
controller.state = 'Batman';
controller.onConfirmResponse(response);
expect(controller.state).toBeFalsy();
});
});
});
});
// 309

1
client/locator/index.js Normal file
View File

@ -0,0 +1 @@
export * from './src/locator';

View File

@ -0,0 +1,14 @@
{
"module": "locator",
"name": "Locator",
"icon": "add_location",
"validations" : false,
"routes": [
{
"url": "/locator",
"state": "locator",
"component": "vn-locator-index",
"acl": ["developer"]
}
]
}

View File

@ -0,0 +1,39 @@
<vn-card margin-large>
<vn-vertical pad-medium>
<vn-horizontal vn-one margin-large-bottom class="locator-header">
<vn-title vn-one>Routes locator</vn-title>
<form vn-two vn-horizontal class="filterPanel" ng-submit="$ctrl.searchRoutes()">
<vn-textfield vn-one label="Filter" model="$ctrl.search"></vn-textfield>
<vn-icon
vn-none
margin-medium-right
pad-medium-top
icon="keyboard_arrow_down"
ng-click="$ctrl.moreFilters($event)">
</vn-icon>
<vn-button vn-none pad-small-top label="Filtrar" ng-click="$ctrl.searchRoutes()"></vn-button>
</form>
<vn-one vn-horizontal>
<vn-one></vn-one>
<vn-autocomplete vn-two
initial-value="$ctrl.filter.warehouseFk"
show-field="name"
value-field="id"
field="$ctrl.filter.warehouseFk"
url="/production/api/Warehouses/production"
on-change = "$ctrl.onChangeWareHouse(item)"
label="Store">
</vn-autocomplete>
<vn-icon-button vn-none pad-ten-top margin-medium-left icon="refresh" ng-click="$ctrl.refreshRoutes()"></vn-icon-button>
</vn-one>
</vn-horizontal>
<vn-horizontal vn-one margin-large-bottom>
<vn-one>
<vn-locator-actions routes="$ctrl.routes" states="$ctrl.states" hour-items="$ctrl.hourItems"></vn-locator-actions>
</vn-one>
<vn-two></vn-two>
</vn-horizontal>
<vn-locator-table check-all="$ctrl.checkAll" routes="$ctrl.routes" footer="$ctrl.footer"></vn-locator-table>
</vn-vertical>
</vn-card>

View File

@ -0,0 +1,33 @@
import ngModule from '../module';
class LocatorIndex {
constructor($state) {
this.$state = $state;
this.routes = [];
for (let i = 1; i < 100; i++) {
let route = {
id: i,
zoneFk: Math.floor(Math.random() * 6) + 1,
postalcode: 46006,
order: Math.floor(Math.random() * 3) + 1,
preparado: '25/08',
entrada: '26/08',
ticket: 1547890 + i,
routeFk: Math.floor(Math.random() * 9999) + 1000,
alias: `Flores X${Math.floor(Math.random() * 3) + 1}`,
bultos: Math.floor(Math.random() * 20) + 10,
m3: (Math.random()).toFixed(2),
error: (Math.floor(Math.random() * 3) + 1) === 1
};
route.success = (!route.error && (Math.floor(Math.random() * 3) + 1) === 1);
this.routes.push(route);
}
}
}
LocatorIndex.$inject = ['$state'];
ngModule.component('vnLocatorIndex', {
template: require('./index.html'),
controller: LocatorIndex
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,6 @@
{
"Routes locator": "Localizador de rutas",
"Filter": "Filtro",
"Store": "Almacén",
"Address": "Dirección"
}

View File

@ -0,0 +1,14 @@
<vn-horizontal class="actionPanel">
<vn-none margin-medium-right>
<vn-icon-button icon="local_shipping"></vn-icon-button>
</vn-none>
<vn-none margin-medium-right>
<vn-icon-menu icon="local_shipping" selected="$ctrl.actionRoute" items="[{id:1,name:'test_01'},{id:2,name:'test_02'}]"></vn-icon-menu>
</vn-none>
<vn-none margin-medium-right>
</vn-none>
<vn-none margin-medium-right>
</vn-none>
</vn-horizontal>

View File

@ -0,0 +1,13 @@
import ngModule from '../module';
class LocatorActions {
constructor($state) {
this.$state = $state;
}
}
LocatorActions.$inject = ['$state'];
ngModule.component('vnLocatorActions', {
template: require('./locator-actions.html'),
controller: LocatorActions
});

View File

@ -0,0 +1,60 @@
<vn-vertical>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h>
<vn-multi-check
check-all="$ctrl.checkAll"
models="$ctrl.routes"
options="[{id:'all',name:'Todos'},{id:'any',name:'Ninguno'}, {id:'error', name:'Con problema'}, {id:'no-error', name:'Sin problema'}]"
></vn-multi-check>
</vn-column-header>
<vn-column-header vn-two pad-medium-h field="zoneFk" text="Zona"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="postalcode" text="Postal Code"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="order" text="Order" default-order="DESC"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="preparado" text="Preparado"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="entrada" text="Entrada"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="ticket" text="Ticket"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="routeFk" text="ID de ruta"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="alias" text="Alias"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="bultos" text="Bultos"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="m3" text="m3"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-vertical
class="list list-element text-center"
ng-repeat="route in $ctrl.pageTable.model track by route.id"
ng-class="{warning: route.error, success: route.success}"
>
<vn-horizontal>
<vn-one pad-medium-h></vn-one>
<vn-two pad-medium-h>{{::route.zoneFk}}</vn-two>
<vn-two pad-medium-h>{{::route.postalcode}}</vn-two>
<vn-two pad-medium-h>{{::route.order}}</vn-two>
<vn-two pad-medium-h>{{::route.preparado}}</vn-two>
<vn-two pad-medium-h>{{::route.entrada}}</vn-two>
<vn-two pad-medium-h>{{::route.ticket}}</vn-two>
<vn-two pad-medium-h>{{::route.routeFk}}</vn-two>
<vn-two pad-medium-h>{{::route.alias}}</vn-two>
<vn-two pad-medium-h>{{::route.bultos}}</vn-two>
<vn-two pad-medium-h>{{::route.m3}}</vn-two>
</vn-horizontal>
<vn-horizontal margin-small-top>
<vn-none pad-medium-h margin--small-top>
<vn-check model="route.checked"></vn-check>
</vn-none>
<vn-none pad-medium-h></vn-none>
<vn-one text-left pad-small border-solid>
<strong translate>Address</strong>: {{::route.address}}
</vn-one>
<vn-none pad-medium-h>
<vn-icon icon="more" vn-tooltip tooltip-template="/static/templates/tooltip.routes.tpl.html" tooltip-position="left"></vn-icon>
</vn-none>
</vn-horizontal>
</vn-vertical>
</vn-one>
<vn-horizontal vn-one class="list list-footer">
</vn-horizontal>
<vn-one>
<vn-paging page-change="$ctrl.paginate()" index="$ctrl.pageTable" total="$ctrl.totalFilter"></vn-paging>
</vn-one>
</vn-vertical>

View File

@ -0,0 +1,46 @@
import ngModule from '../module';
class LocatorTable {
constructor($filter) {
this.$filter = $filter;
this.itemsDisplayedInList = 7;
this.pageTable = {
filter: {
page: 1,
size: this.itemsDisplayedInList
},
model: []
};
this._routes = [];
}
set routes(value) {
this._routes = value;
this.totalFilter = this._routes.length;
this.pageTable.filter.page = 1;
this.paginate();
}
get routes() {
return this._routes;
}
onOrder(field, order) {
let reverse = order === 'DESC';
this.routes = this.$filter('orderBy')(this.routes, field, reverse);
this.paginate();
}
paginate() {
let init = (this.pageTable.filter.page - 1) * this.itemsDisplayedInList;
let fin = this.pageTable.filter.page * this.itemsDisplayedInList;
this.pageTable.model = this.routes.slice(init, fin);
}
}
LocatorTable.$inject = ['$filter'];
ngModule.component('vnLocatorTable', {
template: require('./locator-table.html'),
bindings: {
routes: '<'
},
controller: LocatorTable
});

View File

@ -0,0 +1,5 @@
export * from './module';
import './index/index';
import './locator-actions/locator-actions';
import './locator-table/locator-table';

View File

@ -0,0 +1,5 @@
import {ng} from 'vendor';
import 'core';
const ngModule = ng.module('locator', []);
export default ngModule;

9
client/modules.json Normal file
View File

@ -0,0 +1,9 @@
{
"salix": [],
"auth": [],
"core": [],
"client": [],
"production": [],
"route": [],
"locator": []
}

View File

@ -7,7 +7,8 @@
{
"url": "/production",
"state": "production",
"component": "vn-production-index"
"component": "vn-production-index",
"acl": ["employee"]
}
]
}

Some files were not shown because too many files have changed in this diff Show More