version para Jenkins
This commit is contained in:
commit
58a80d2062
|
@ -1,5 +1,9 @@
|
|||
extends: google
|
||||
extends: [eslint:recommended, google, plugin:jasmine/recommended]
|
||||
installedESLint: true
|
||||
plugins:
|
||||
- jasmine
|
||||
env:
|
||||
jasmine: true
|
||||
rules:
|
||||
indent: [error, 4]
|
||||
require-jsdoc: 0
|
||||
|
@ -9,3 +13,6 @@ rules:
|
|||
operator-linebreak: 0
|
||||
radix: 0
|
||||
guard-for-in: 0
|
||||
camelcase: 0
|
||||
default-case: 0
|
||||
no-eq-null: 0
|
||||
|
|
|
@ -4,3 +4,4 @@ build
|
|||
npm-debug.log
|
||||
debug.log
|
||||
datasources.development.json
|
||||
.idea
|
|
@ -5,7 +5,7 @@
|
|||
"name": "Iniciar",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/app.js",
|
||||
"program": "${workspaceRoot}/services/client/server/server.js",
|
||||
"stopOnEntry": false,
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
|
@ -37,50 +37,10 @@
|
|||
"name": "Asociar al proceso",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"processId": "${command.PickProcess}",
|
||||
"processId": "${command:PickProcess}",
|
||||
"port": 5858,
|
||||
"sourceMaps": false,
|
||||
"outFiles": []
|
||||
},
|
||||
{
|
||||
"name": "Loopback",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/services/client/server/server.js",
|
||||
"stopOnEntry": false,
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": null,
|
||||
"runtimeExecutable": null,
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"console": "internalConsole",
|
||||
"sourceMaps": false,
|
||||
"outFiles": []
|
||||
},
|
||||
{
|
||||
"name": "gulp debug",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}\\@salix\\node_modules\\gulp\\bin\\gulp.js",
|
||||
"stopOnEntry": false,
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}\\@salix",
|
||||
"preLaunchTask": null,
|
||||
"runtimeExecutable": null,
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"console": "internalConsole",
|
||||
"sourceMaps": false,
|
||||
"outFiles": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#!/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 buildNumber = "${env.BUILD_NUMBER}";
|
||||
|
||||
def dockers = [dockerAuthName, dockerSalixName, dockerClientName, dockerMailerName, dockerProductionName, dockerNginxName]
|
||||
|
||||
node {
|
||||
|
||||
if (branchName == branchNameProd)
|
||||
prefixDocker = "prod";
|
||||
|
||||
stage ('Checkout') {
|
||||
checkout scm
|
||||
}
|
||||
|
||||
stage ('install modules'){
|
||||
sh "npm install"
|
||||
}
|
||||
|
||||
stage ('build Project Salix'){
|
||||
sh "gulp build"
|
||||
}
|
||||
|
||||
for (int i = 0; i < dockers.size(); i++)
|
||||
{
|
||||
def element = dockers[i][0]
|
||||
def ports = dockers[i][1]
|
||||
|
||||
stage ("docker ${element}")
|
||||
{
|
||||
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')
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
# Project Title
|
||||
|
||||
One Paragraph of project description goes here
|
||||
|
||||
### Prerequisites
|
||||
|
||||
For testing purposes you will need to install globally the following items:
|
||||
npm install -g karma
|
||||
npm install -g karma-cli
|
||||
|
||||
## Getting Started // ### Installing
|
||||
|
||||
Pull from repo.
|
||||
|
||||
install nodejs v6 or above.
|
||||
|
||||
install nginx globally.
|
||||
|
||||
Ask a senior dev for the datasources.development.json files required to run the project.
|
||||
|
||||
on root run:
|
||||
npm install
|
||||
gulp install
|
||||
|
||||
lauching nginx:
|
||||
./dev.sh
|
||||
|
||||
launching frontend:
|
||||
gulp client
|
||||
|
||||
launching backend:
|
||||
gulp services
|
||||
|
||||
## Running the tests
|
||||
|
||||
for client-side unit tests run from project's root:
|
||||
karma start
|
||||
|
||||
for server-side unit tests run from project's root:
|
||||
npm run testWatch or test for single run
|
||||
|
||||
### Break down into end to end tests
|
||||
|
||||
on root run:
|
||||
|
||||
npm run e2e
|
||||
|
||||
## Built With
|
||||
|
||||
* [angularjs](https://angularjs.org/)
|
||||
* [nodejs](https://nodejs.org/)
|
||||
* [webpack](https://webpack.js.org/)
|
||||
* [loopback](https://loopback.io/)
|
||||
* [docker](https://www.docker.com/)
|
||||
* [gulp.js](https://gulpjs.com/)
|
||||
|
||||
## Versioning
|
||||
|
||||
We use [SourceTree](https://www.sourcetreeapp.com/) for versioning. For the versions available, see the [salix project](https://git.verdnatura.es).
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License
|
||||
|
|
@ -5,6 +5,6 @@
|
|||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://git.verdnatura.es:/salix"
|
||||
"url": "https://git.verdnatura.es/salix"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './module';
|
||||
import './ngModule';
|
||||
import './config';
|
||||
|
||||
export {component as Login} from './login/login';
|
||||
import './login/login';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {module} from './module';
|
||||
import ngModule from './ngModule';
|
||||
|
||||
config.$inject = ['$translatePartialLoaderProvider', '$httpProvider'];
|
||||
export function config($translatePartialLoaderProvider, $httpProvider) {
|
||||
|
@ -7,4 +7,4 @@ export function config($translatePartialLoaderProvider, $httpProvider) {
|
|||
$httpProvider.defaults.useXDomain = true;
|
||||
delete $httpProvider.defaults.headers.common['X-Requested-With'];
|
||||
}
|
||||
module.config(config);
|
||||
ngModule.config(config);
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<div>
|
||||
<div class="box-wrapper">
|
||||
<div class="box">
|
||||
<img src="./logo.svg"/>
|
||||
<form name="form" ng-submit="$ctrl.submit()">
|
||||
<vn-textfield label="User" field="$ctrl.model.email"></vn-textfield>
|
||||
<vn-password label="Password" field="$ctrl.model.password"></vn-password>
|
||||
<div class="footer">
|
||||
<vn-submit label="Enter"></vn-submit>
|
||||
<div class="spinner-wrapper">
|
||||
<vn-spinner enable="$ctrl.loading"></vn-spinner>
|
||||
</div>
|
||||
<div class="box">
|
||||
<img src="./logo.svg"/>
|
||||
<form name="form" ng-submit="$ctrl.submit()">
|
||||
<vn-textfield vn-id="userField" label="User" model="$ctrl.user" vn-focus></vn-textfield>
|
||||
<vn-textfield label="Password" model="$ctrl.password" type="password"></vn-textfield>
|
||||
<div class="footer">
|
||||
<vn-submit label="Enter"></vn-submit>
|
||||
<div class="spinner-wrapper">
|
||||
<vn-spinner enable="$ctrl.loading"></vn-spinner>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<vn-snackbar></vn-snackbar>
|
||||
</div>
|
||||
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
||||
</div>
|
||||
|
|
|
@ -1,57 +1,81 @@
|
|||
import {module} from '../module';
|
||||
import './login.scss';
|
||||
import ngModule from '../ngModule';
|
||||
import './style.scss';
|
||||
|
||||
export const component = {
|
||||
template: require('./login.html'),
|
||||
controller: controller
|
||||
};
|
||||
module.component('vnLogin', component);
|
||||
|
||||
controller.$inject = ['$http', '$element', '$window'];
|
||||
function controller($http, $element, $window) {
|
||||
Object.assign(this, {
|
||||
submit: function() {
|
||||
let model = this.model;
|
||||
|
||||
if (!(model && model.email && model.password)) {
|
||||
this.showMessage('Please insert your email and password');
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
model.appId = $window.location.href;
|
||||
$http.post('/auth', this.model).then(
|
||||
json => this.onLoginOk(json),
|
||||
json => this.onLoginErr(json)
|
||||
);
|
||||
},
|
||||
onLoginOk: function(json) {
|
||||
this.loading = false;
|
||||
let data = json.data;
|
||||
$window.location = `${data.location}?access_token=${data.location}`;
|
||||
},
|
||||
onLoginErr: function(json) {
|
||||
this.loading = false;
|
||||
this.model.password = '';
|
||||
|
||||
let message;
|
||||
|
||||
switch (json.status) {
|
||||
case 401:
|
||||
message = 'Invalid credentials';
|
||||
break;
|
||||
case -1:
|
||||
message = 'Can\'t contact with server';
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
|
||||
this.showMessage(message);
|
||||
},
|
||||
showMessage: function(message) {
|
||||
let snackbar = $element.find('vn-snackbar').controller('vnSnackbar');
|
||||
snackbar.show({message: message});
|
||||
export default class Controller {
|
||||
constructor($element, $scope, $window, $http) {
|
||||
this.$element = $element;
|
||||
this.$ = $scope;
|
||||
this.$window = $window;
|
||||
this.$http = $http;
|
||||
}
|
||||
submit() {
|
||||
if (!(this.user && this.password)) {
|
||||
this.focusUser();
|
||||
this.showMessage('Please insert your user and password');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
this.loading = true;
|
||||
let params = {
|
||||
user: this.user,
|
||||
password: this.password,
|
||||
location: this.$window.location.href
|
||||
};
|
||||
this.$http.post('/auth/login', params).then(
|
||||
json => this.onLoginOk(json),
|
||||
json => this.onLoginErr(json)
|
||||
);
|
||||
}
|
||||
onLoginOk(json) {
|
||||
this.loading = false;
|
||||
let data = json.data;
|
||||
let params = {
|
||||
token: data.token,
|
||||
continue: data.continue
|
||||
};
|
||||
this.$window.location = `${data.loginUrl}?${this.encodeUri(params)}`;
|
||||
}
|
||||
encodeUri(object) {
|
||||
let uri = '';
|
||||
for (var key in object)
|
||||
if (object[key]) {
|
||||
if (uri.length > 0)
|
||||
uri += '&';
|
||||
uri += encodeURIComponent(key) + '=' + encodeURIComponent(object[key]);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
onLoginErr(json) {
|
||||
this.loading = false;
|
||||
this.password = '';
|
||||
this.focusUser();
|
||||
|
||||
let message;
|
||||
|
||||
switch (json.status) {
|
||||
case 401:
|
||||
message = 'Invalid credentials';
|
||||
break;
|
||||
case -1:
|
||||
message = 'Can\'t contact with server';
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
|
||||
this.showMessage(message);
|
||||
}
|
||||
focusUser() {
|
||||
this.$.userField.select();
|
||||
this.$.userField.focus();
|
||||
}
|
||||
showMessage(message) {
|
||||
this.$.snackbar.show({message: message});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$scope', '$window', '$http'];
|
||||
|
||||
ngModule.component('vnLogin', {
|
||||
template: require('./login.html'),
|
||||
controller: Controller
|
||||
});
|
||||
|
|
|
@ -44,4 +44,3 @@ vn-login > div {
|
|||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import {ng} from 'vendor';
|
||||
import * as core from 'core';
|
||||
|
||||
export const module = ng.module('vnAuth', [core.NAME]);
|
|
@ -0,0 +1,5 @@
|
|||
import {ng} from 'vendor';
|
||||
import 'core';
|
||||
|
||||
let ngModule = ng.module('vnAuth', ['vnCore']);
|
||||
export default ngModule;
|
|
@ -5,6 +5,6 @@
|
|||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://git.verdnatura.es:/salix"
|
||||
"url": "https://git.verdnatura.es/salix"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"module": "client",
|
||||
"routes": [
|
||||
{
|
||||
"url": "/clients",
|
||||
"state": "clients",
|
||||
"component": "vn-client-index"
|
||||
}, {
|
||||
"url": "/clients/:id",
|
||||
"state": "clientCard",
|
||||
"component": "vn-client-card"
|
||||
}, {
|
||||
"url": "/basic-data",
|
||||
"state": "clientCard.basicData",
|
||||
"component": "vn-client-basic-data",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"description": "Datos básicos",
|
||||
"icon": "person"
|
||||
}, {
|
||||
"url": "/fiscal-data",
|
||||
"state": "clientCard.fiscalData",
|
||||
"component": "vn-client-fiscal-data",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"description": "Datos facturación",
|
||||
"icon": "assignment"
|
||||
}, {
|
||||
"url": "/addresses",
|
||||
"state": "clientCard.addresses",
|
||||
"component": "vn-client-addresses",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"description": "Consignatarios",
|
||||
"icon": "local_shipping"
|
||||
}, {
|
||||
"url": "/web-access",
|
||||
"state": "clientCard.webAccess",
|
||||
"component": "vn-client-web-access",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"description": "Acceso web",
|
||||
"icon": "language"
|
||||
}, {
|
||||
"url": "/notes",
|
||||
"state": "clientCard.notes",
|
||||
"component": "vn-client-notes",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"description": "Notas",
|
||||
"icon": "insert_drive_file"
|
||||
}, {
|
||||
"url": "/new-note",
|
||||
"state": "clientCard.newNote",
|
||||
"component": "vn-new-note"
|
||||
},{
|
||||
"url": "/create",
|
||||
"state": "create",
|
||||
"component": "vn-client-create"
|
||||
}, {
|
||||
"url": "/address/create",
|
||||
"state": "clientCard.addressCreate",
|
||||
"component": "vn-address-create"
|
||||
}, {
|
||||
"url": "/address/:addressId",
|
||||
"state": "clientCard.addressEdit",
|
||||
"component": "vn-address-edit"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
{
|
||||
"module": "client",
|
||||
"name": "Clients",
|
||||
"icon": "/static/images/icon_client.png",
|
||||
"validations" : true,
|
||||
"routes": [
|
||||
{
|
||||
"url": "/clients",
|
||||
"state": "clients",
|
||||
"component": "vn-client-index"
|
||||
}, {
|
||||
"url": "/create",
|
||||
"state": "create",
|
||||
"component": "vn-client-create"
|
||||
}, {
|
||||
"url": "/clients/:id",
|
||||
"state": "clientCard",
|
||||
"abstract": true,
|
||||
"component": "vn-client-card"
|
||||
}, {
|
||||
"url": "/basic-data",
|
||||
"state": "clientCard.basicData",
|
||||
"component": "vn-client-basic-data",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Datos básicos",
|
||||
"icon": "person"
|
||||
}
|
||||
}, {
|
||||
"url": "/fiscal-data",
|
||||
"state": "clientCard.fiscalData",
|
||||
"component": "vn-client-fiscal-data",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Datos fiscales",
|
||||
"icon": "account_balance"
|
||||
}
|
||||
}, {
|
||||
"url": "/billing-data",
|
||||
"state": "clientCard.billingData",
|
||||
"component": "vn-client-billing-data",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Datos facturación",
|
||||
"icon": "assignment"
|
||||
}
|
||||
}, {
|
||||
"url": "/addresses",
|
||||
"state": "clientCard.addresses",
|
||||
"component": "ui-view",
|
||||
"abstract": true
|
||||
}, {
|
||||
"url": "/list",
|
||||
"state": "clientCard.addresses.list",
|
||||
"component": "vn-client-addresses",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Consignatarios",
|
||||
"icon": "local_shipping"
|
||||
}
|
||||
}, {
|
||||
"url": "/create",
|
||||
"state": "clientCard.addresses.create",
|
||||
"component": "vn-address-create"
|
||||
}, {
|
||||
"url": "/:addressId/edit",
|
||||
"state": "clientCard.addresses.edit",
|
||||
"component": "vn-address-edit"
|
||||
}, {
|
||||
"url": "/web-access",
|
||||
"state": "clientCard.webAccess",
|
||||
"component": "vn-client-web-access",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Acceso web",
|
||||
"icon": "language"
|
||||
}
|
||||
}, {
|
||||
"url": "/notes",
|
||||
"state": "clientCard.notes",
|
||||
"component": "ui-view",
|
||||
"abstract": true
|
||||
}, {
|
||||
"url": "/list",
|
||||
"state": "clientCard.notes.list",
|
||||
"component": "vn-client-notes",
|
||||
"params": {
|
||||
"client": "card.client"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Notas",
|
||||
"icon": "insert_drive_file"
|
||||
}
|
||||
}, {
|
||||
"url": "/create",
|
||||
"state": "clientCard.notes.create",
|
||||
"component": "vn-note-create"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Addresses"
|
||||
id-field="id"
|
||||
data="$ctrl.address"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submitGo('clientCard.addresses')" pad-medium>
|
||||
<vn-card >
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Address</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-check vn-one label="Default" field="$ctrl.address.isDefaultAddress"></vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Consignee" field="$ctrl.address.consignee" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Street address" field="$ctrl.address.street"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Postcode" field="$ctrl.address.postcode"></vn-textfield>
|
||||
<vn-textfield vn-one label="Town/City" field="$ctrl.address.city"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
field="$ctrl.address.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Province">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
field="$ctrl.address.agencyFk"
|
||||
url="/client/api/AgencyServices"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Agency">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one label="Phone" field="$ctrl.address.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Mobile" field="$ctrl.address.mobile"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,16 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($state) {
|
||||
this.address = {
|
||||
clientFk: parseInt($state.params.id),
|
||||
isEnabled: true
|
||||
};
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$state'];
|
||||
|
||||
ngModule.component('vnAddressCreate', {
|
||||
template: require('./address-create.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
import './address-create.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnAddressCreate', () => {
|
||||
let $componentController;
|
||||
let $state;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$state = _$state_;
|
||||
$state.params.id = '1234';
|
||||
}));
|
||||
|
||||
it('should define and set address property', () => {
|
||||
let controller = $componentController('vnAddressCreate', {$state});
|
||||
|
||||
expect(controller.address.clientFk).toBe(1234);
|
||||
expect(controller.address.isEnabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,46 +0,0 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Addresses"
|
||||
id-field="id"
|
||||
data="addressData.address"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="addressData.onSubmit()" pad-medium>
|
||||
<vn-card >
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Consignatario</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-check vn-one label="Predeterminado" field="addressData.address.default"></vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Consignatario" field="addressData.address.consignee" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Domicilio" field="addressData.address.street"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Código Postal" field="addressData.address.postcode"></vn-textfield>
|
||||
<vn-textfield vn-one label="Municipio" field="addressData.address.city"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
field="addressData.address.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Provincia">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
field="addressData.address.agencyFk"
|
||||
url="/client/api/AgencyServices"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Agencia">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one label="Teléfono" field="addressData.address.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Móvil" field="addressData.address.mobile"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,26 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($scope, $state) {
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.address = {
|
||||
client: parseInt($state.params.id),
|
||||
enabled: true
|
||||
};
|
||||
}
|
||||
onSubmit() {
|
||||
this.$scope.watcher.submit().then(
|
||||
() => this.$state.go('clientCard.addresses')
|
||||
);
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$state'];
|
||||
|
||||
export const NAME = 'vnAddressCreate';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controllerAs: 'addressData',
|
||||
controller: Controller
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"Street address": "Dirección postal",
|
||||
"Default": "Predeterminado",
|
||||
"Consignee": "Consignatario",
|
||||
"Postcode": "Código postal",
|
||||
"Town/City": "Ciudad",
|
||||
"Province": "Provincia",
|
||||
"Agency": "Agencia",
|
||||
"Phone": "Teléfono",
|
||||
"Mobile": "Móvil"
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<mg-ajax path="/client/api/Addresses/{{edit.params.addressId}}" actions="$ctrl.address=edit.model" options="mgEdit"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Addresses"
|
||||
id-field="id"
|
||||
data="$ctrl.address"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submitBack()" pad-medium>
|
||||
<vn-card>
|
||||
<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-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Consignee" field="$ctrl.address.consignee" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Street" field="$ctrl.address.street"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Postcode" field="$ctrl.address.postcode"></vn-textfield>
|
||||
<vn-textfield vn-one label="City" field="$ctrl.address.city"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.address.province"
|
||||
field="$ctrl.address.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Province">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
field="$ctrl.address.defaultAgencyFk"
|
||||
url="/client/api/AgencyServices"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Agency">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one label="Phone" field="$ctrl.address.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Mobile" field="$ctrl.address.mobile"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,15 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($state) {
|
||||
this.address = {
|
||||
id: parseInt($state.params.addressId)
|
||||
};
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$state'];
|
||||
|
||||
ngModule.component('vnAddressEdit', {
|
||||
template: require('./address-edit.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import './address-edit.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnAddressEdit', () => {
|
||||
let $componentController;
|
||||
let $state;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$state = _$state_;
|
||||
$state.params.addressId = '1234';
|
||||
}));
|
||||
|
||||
it('should define and set address property', () => {
|
||||
let controller = $componentController('vnAddressEdit', {$state: $state});
|
||||
|
||||
expect(controller.address.id).toBe(1234);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
get="true"
|
||||
url="/client/api/Addresses"
|
||||
id-field="id"
|
||||
data="addressData.address"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submitBack()" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Consignatario</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-check vn-one label="Activo" field="addressData.address.enabled"></vn-check>
|
||||
<vn-check vn-one label="Predeterminado" field="addressData.address.default"></vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Consignatario" field="addressData.address.consignee" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Domicilio" field="addressData.address.street"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Código Postal" field="addressData.address.postcode"></vn-textfield>
|
||||
<vn-textfield vn-one label="Municipio" field="addressData.address.city"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
field="addressData.address.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Provincia">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
field="addressData.address.defaultAgencyFk"
|
||||
url="/client/api/AgencyServices"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Agencia">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one label="Teléfono" field="addressData.address.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Móvil" field="addressData.address.mobile"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,18 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($stateParams) {
|
||||
this.address = {
|
||||
id: $stateParams.addressId
|
||||
};
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$stateParams'];
|
||||
|
||||
export const NAME = 'vnAddressEdit';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controllerAs: 'addressData',
|
||||
controller: Controller
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Enabled": "Activo",
|
||||
"Is equalizated": "Recargo de equivalencia"
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<mg-ajax path="/client/api/Clients/{{index.params.id}}/addressesList" options="mgIndex"></mg-ajax>
|
||||
<vn-vertical pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-horizontal>
|
||||
<vn-title vn-one>Addresses</vn-title>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-repeat="i in index.model track by i.id" class="pad-medium-top" style="align-items: center;">
|
||||
<vn-auto style="border-radius: .5em;" class="pad-small border-solid"
|
||||
ng-class="{'bg-dark-item': i.isDefaultAddress,'bg-opacity-item': !i.isEnabled && !i.isDefaultAddress}">
|
||||
<vn-horizontal style="align-items: center;">
|
||||
<vn-none pad-medium-h style="color:#FFA410;">
|
||||
<i class="material-icons" ng-if="i.isDefaultAddress">star</i>
|
||||
<i class="material-icons pointer" ng-if="!i.isDefaultAddress&&i.isEnabled" vn-tooltip="Set as default" tooltip-position="left" ng-click="$ctrl.setDefault(i.id)">star_border</i>
|
||||
</vn-none>
|
||||
<vn-auto>
|
||||
<div><b>{{::i.consignee}}</b></div>
|
||||
<div>{{::i.street}}</div>
|
||||
<div>{{::i.city}}, {{::i.province}}</div>
|
||||
<div>{{::i.phone}}, {{::i.mobile}}</div>
|
||||
</vn-auto>
|
||||
<a vn-empty ui-sref="clientCard.addresses.edit({addressId: {{i.id}}})">
|
||||
<vn-icon-button icon="edit"></vn-icon-button>
|
||||
</a>
|
||||
</vn-horizontal>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-paging index="index"></vn-paging>
|
||||
<vn-float-button
|
||||
fixed-bottom-right
|
||||
ui-sref="clientCard.addresses.create"
|
||||
icon="add"
|
||||
label="Add">
|
||||
</vn-float-button>
|
||||
</vn-vertical>
|
|
@ -0,0 +1,19 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class ClientAddresses {
|
||||
constructor($http, $scope) {
|
||||
this.$http = $http;
|
||||
this.$scope = $scope;
|
||||
}
|
||||
setDefault(id) {
|
||||
this.$http.patch(`/client/api/Addresses/${id}`, {id: id, isDefaultAddress: true}).then(() => {
|
||||
this.$scope.index.accept();
|
||||
});
|
||||
}
|
||||
}
|
||||
ClientAddresses.$inject = ['$http', '$scope'];
|
||||
|
||||
ngModule.component('vnClientAddresses', {
|
||||
template: require('./addresses.html'),
|
||||
controller: ClientAddresses
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
<mg-ajax path="/client/api/Clients/{{index.params.id}}/addressesList" options="vnIndex"></mg-ajax>
|
||||
<vn-vertical pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-horizontal>
|
||||
<vn-title vn-one>Consignatario</vn-title>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-repeat="i in index.model" class="pad-medium-top" style="align-items: center;">
|
||||
<vn-auto style="border-radius: .5em;" class="pad-small border-solid"
|
||||
ng-class="{'bg-dark-item': i.default,'bg-opacity-item': !i.enabled && !i.default}">
|
||||
<vn-horizontal style="align-items: center;">
|
||||
<vn-auto>
|
||||
<div><b>{{i.consignee}}</b></div>
|
||||
<div>{{i.street}}</div>
|
||||
<div>{{i.city}}, {{i.province}}</div>
|
||||
<div>{{i.phone}}, {{i.mobile}}</div>
|
||||
</vn-auto>
|
||||
<a vn-empty ui-sref="clientCard.addressEdit({addressId: {{i.id}}})">
|
||||
<vn-icon-button icon="edit"></vn-icon-button>
|
||||
</a>
|
||||
</vn-horizontal>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-paging index="index"></vn-paging>
|
||||
<vn-float-button
|
||||
fixed-bottom-right
|
||||
ui-sref="clientCard.addressCreate"
|
||||
icon="add">
|
||||
</vn-float-button>
|
||||
</vn-vertical>
|
|
@ -1,6 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
export const component = {
|
||||
template: require('./index.html')
|
||||
};
|
||||
module.component('vnClientAddresses', component);
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Addresses": "Consignatarios",
|
||||
"Set as default": "Establecer como predeterminado"
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Basic data</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi"></vn-textfield>
|
||||
<vn-textfield autofocus vn-one label="Social name" field="$ctrl.client.socialName"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Phone" field="$ctrl.client.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Mobile" field="$ctrl.client.mobile"></vn-textfield>
|
||||
<vn-textfield vn-one label="Fax" field="$ctrl.client.fax"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Email" field="$ctrl.client.email" info="You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.client.salesPerson"
|
||||
field="$ctrl.client.salesPersonFk"
|
||||
url="/client/api/Clients/activeSalesPerson"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
select-fields="surname"
|
||||
label="Salesperson">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.client.contactChannel"
|
||||
field="$ctrl.client.contactChannelFk"
|
||||
url="/client/api/ContactChannels"
|
||||
label="Channel">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
ngModule.component('vnClientBasicData', {
|
||||
template: require('./basic-data.html'),
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
|
@ -1,44 +0,0 @@
|
|||
<mg-ajax path="/client/api/Clients/{{put.params.id}}" options="vnPut"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="put">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Datos básicos</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Nombre" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="NIF/CIF" field="$ctrl.client.fi"></vn-textfield>
|
||||
<vn-textfield autofocus vn-one label="Razón social" field="$ctrl.client.socialName"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Teléfono" field="$ctrl.client.phone"></vn-textfield>
|
||||
<vn-textfield vn-one label="Móvil" field="$ctrl.client.mobile"></vn-textfield>
|
||||
<vn-textfield vn-one label="Fax" field="$ctrl.client.fax"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Email" field="$ctrl.client.email"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
field="$ctrl.client.salesPersonFk"
|
||||
url="/client/api/Employees"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Comercial">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
field="$ctrl.client.chanelFK"
|
||||
url="/client/api/Chanels"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Canal">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,9 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
export const component = {
|
||||
template: require('./index.html'),
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
};
|
||||
module.component('vnClientBasicData', component);
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"Basic data": "Datos básicos",
|
||||
"Name": "Nombre",
|
||||
"Tax number": "NIF/CIF",
|
||||
"Social name": "Razón social",
|
||||
"Phone": "Teléfono",
|
||||
"Mobile": "Móvil",
|
||||
"Fax": "Fax",
|
||||
"Email": "Correo electrónico",
|
||||
"Salesperson": "Comercial",
|
||||
"Channel": "Canal",
|
||||
"You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main": "Puede guardar varios correos electrónicos encadenándolos mediante comas sin espacios, ejemplo: user@dominio.com,user2@dominio.com siendo el primer correo electrónico el principal"
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="$ctrl.submit()" pad-medium>
|
||||
<vn-card margin-small-bottom>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Billing information</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-two
|
||||
vn-acl="administrative"
|
||||
field="$ctrl.client.payMethodFk"
|
||||
url="/client/api/PayMethods"
|
||||
select-fields="ibanRequired"
|
||||
initial-data="$ctrl.client.payMethod"
|
||||
label="Forma de pago">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-two label="IBAN" field="$ctrl.client.iban" vn-acl="administrative"></vn-textfield>
|
||||
<vn-textfield vn-one label="Vencimiento" field="$ctrl.client.dueDay" vn-acl="administrative"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Descuento" field="$ctrl.client.discount" vn-acl="administrative"></vn-textfield>
|
||||
<vn-textfield vn-one label="Crédito" field="$ctrl.client.credit" vn-acl="administrative"></vn-textfield>
|
||||
<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-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-card margin-small-bottom>
|
||||
<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-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar" vn-acl="administrative"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
<vn-dialog
|
||||
vn-id="send-mail"
|
||||
on-response="$ctrl.returnDialog(response)">
|
||||
<tpl-body>
|
||||
<vn-vertical>
|
||||
<vn-one text-center translate>Changed terms</vn-one>
|
||||
<vn-one text-center translate>Notify customer?</vn-one>
|
||||
</vn-vertical>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<button response="CANCEL" translate>No</button>
|
||||
<button response="ACCEPT" translate>Yes, notify</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
|
@ -0,0 +1,58 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $http, vnApp, $translate) {
|
||||
this.$ = $scope;
|
||||
this.$http = $http;
|
||||
this.vnApp = vnApp;
|
||||
this.translate = $translate;
|
||||
this.billData = {};
|
||||
this.copyData();
|
||||
}
|
||||
$onChanges() {
|
||||
this.copyData();
|
||||
}
|
||||
copyData() {
|
||||
if (this.client) {
|
||||
this.billData.payMethodFk = this.client.payMethodFk;
|
||||
this.billData.iban = this.client.iban;
|
||||
this.billData.dueDay = this.client.dueDay;
|
||||
this.billData.discount = this.client.discount;
|
||||
this.billData.credit = this.client.credit;
|
||||
this.billData.creditInsurance = this.client.creditInsurance;
|
||||
}
|
||||
}
|
||||
submit() {
|
||||
return this.$.watcher.submit().then(
|
||||
() => this.checkPaymentChanges());
|
||||
}
|
||||
checkPaymentChanges() {
|
||||
let equals = true;
|
||||
Object.keys(this.billData).forEach(
|
||||
val => {
|
||||
if (this.billData[val] !== this.client[val]) {
|
||||
this.billData[val] = this.client[val];
|
||||
equals = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
if (!equals)
|
||||
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!'))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];
|
||||
|
||||
ngModule.component('vnClientBillingData', {
|
||||
template: require('./billing-data.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
import './billing-data.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientBillingData', () => {
|
||||
let $componentController;
|
||||
let $httpBackend;
|
||||
let $scope;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
|
||||
$componentController = _$componentController_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
let submit = jasmine.createSpy('submit').and.returnValue(Promise.resolve());
|
||||
$scope.watcher = {submit};
|
||||
let show = jasmine.createSpy('show');
|
||||
$scope.sendMail = {show};
|
||||
}));
|
||||
|
||||
describe('copyData()', () => {
|
||||
it(`should define billData using client's data`, () => {
|
||||
let controller = $componentController('vnClientBillingData', {$scope: $scope});
|
||||
controller.client = {
|
||||
credit: 1000000000000,
|
||||
creditInsurance: null,
|
||||
discount: 99,
|
||||
dueDay: 0,
|
||||
iban: null,
|
||||
payMethodFk: 1
|
||||
};
|
||||
controller.billData = {};
|
||||
controller.copyData(controller.client);
|
||||
|
||||
expect(controller.billData).toEqual(controller.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(() => {
|
||||
expect(controller.$.watcher.submit).toHaveBeenCalledWith();
|
||||
expect(controller.checkPaymentChanges).toHaveBeenCalledWith();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
expect(controller.$.sendMail.show).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
expect(controller.$.sendMail.show).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
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}`);
|
||||
controller.returnDialog('ACCEPT');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Changed terms": "Has modificado las condiciones de pago",
|
||||
"Notify customer?" : "¿Deseas notificar al cliente de dichos cambios?",
|
||||
"No": "No",
|
||||
"Yes, notify": "Sí, notificar",
|
||||
"Notification sent!": "¡Notificación enviada!",
|
||||
"Notification error": "Error al enviar notificación"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<vn-horizontal>
|
||||
<mg-ajax
|
||||
path="/client/api/Clients/{{edit.params.id}}/card"
|
||||
actions="card.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"
|
||||
class="display-block" >
|
||||
</vn-descriptor>
|
||||
<vn-left-menu></vn-left-menu>
|
||||
</vn-empty>
|
||||
<vn-auto>
|
||||
<vn-vertical style="max-width: 70em; margin: 0 auto;" ui-view></vn-vertical>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
|
@ -0,0 +1,14 @@
|
|||
import ngModule from '../module';
|
||||
import './style.css';
|
||||
|
||||
export default class Controller {
|
||||
constructor() {
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnClientCard', {
|
||||
template: require('./card.html'),
|
||||
controller: Controller,
|
||||
controllerAs: 'card'
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import './card.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientCard', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope;
|
||||
}));
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
<vn-horizontal>
|
||||
<vn-empty style="min-width: 18em; padding-left: 1em; padding-bottom: 1em;">
|
||||
<vn-descriptor client="card.client" active="card.client.active" class="display-block" ></vn-descriptor>
|
||||
<vn-left-menu items="card.items"></vn-left-menu>
|
||||
</vn-empty>
|
||||
<vn-auto>
|
||||
<vn-vertical style="max-width: 70em; margin: 0 auto;" ui-view></vn-vertical>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
|
@ -1,26 +0,0 @@
|
|||
import {module} from '../module';
|
||||
import './style.css';
|
||||
|
||||
export const NAME = 'vnClientCard';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controllerAs: 'card',
|
||||
controller: function($http, $stateParams) {
|
||||
this.client = null;
|
||||
$http.get(`/client/api/Clients/${$stateParams.id}?filter[include][accountFk]`).then(
|
||||
json => this.client = json.data
|
||||
);
|
||||
|
||||
this.items = [];
|
||||
routes.client.routes.forEach(i => {
|
||||
if (i.description)
|
||||
this.items.push({
|
||||
description: i.description,
|
||||
icon: i.icon,
|
||||
href: i.state
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
COMPONENT.controller.$inject = ['$http', '$stateParams'];
|
||||
module.component(NAME, COMPONENT);
|
|
@ -1,25 +1,16 @@
|
|||
export * from './module';
|
||||
|
||||
export {NAME as CLIENT_CARD,
|
||||
COMPONENT as CLIENT_CARD_COMPONENT} from './card/index';
|
||||
export {NAME as CLIENTS,
|
||||
COMPONENT as CLIENTS_COMPONENT} from './index/index';
|
||||
export {NAME as CLIENT_FISCAL_DATA_INDEX,
|
||||
COMPONENT as CLIENT_FISCAL_DATA_INDEX_COMPONENT} from './fiscal-data/index';
|
||||
export {NAME as CLIENT_DESCRIPTOR,
|
||||
COMPONENT as CLIENT_DESCRIPTOR_COMPONENT} from './descriptor/index';
|
||||
export {NAME as CLIENT_NOTES,
|
||||
COMPONENT as CLIENT_NOTES_COMPONENT} from './notes/index';
|
||||
export {NAME as CLIENT_SEARCH_PANEL,
|
||||
COMPONENT as CLIENT_SEARCH_PANEL_COMPONENT} from './search-panel/index';
|
||||
export {NAME as CLIENT_CREATE,
|
||||
COMPONENT as CLIENT_CREATE_COMPONENT} from './create/index';
|
||||
export {NAME as CLIENT_ADDRESS_EDIT_INDEX,
|
||||
COMPONENT as CLIENT_ADDRESS_EDIT_INDEX_COMPONENT} from './address-edit/index';
|
||||
export {NAME as NEW_NOTE_INDEX,
|
||||
COMPONENT as NEW_NOTE_INDEX_COMPONENT} from './new-note/index';
|
||||
|
||||
import './addresses/index';
|
||||
import './address-create/index';
|
||||
import './basic-data/index';
|
||||
import './web-access/index';
|
||||
import './index/index';
|
||||
import './card/card';
|
||||
import './create/create';
|
||||
import './basic-data/basic-data';
|
||||
import './fiscal-data/fiscal-data';
|
||||
import './billing-data/billing-data';
|
||||
import './descriptor/descriptor';
|
||||
import './search-panel/search-panel';
|
||||
import './addresses/addresses';
|
||||
import './address-create/address-create';
|
||||
import './address-edit/address-edit';
|
||||
import './notes/notes';
|
||||
import './note-create/note-create';
|
||||
import './web-access/web-access';
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<mg-ajax path="/client/api/Clients/createUserProfile" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="post">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
||||
<div style="max-width: 70em; margin: 0 auto;">
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Create client</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Name" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Business name" field="$ctrl.client.socialName"></vn-textfield>
|
||||
<vn-textfield vn-one label="User name" field="$ctrl.client.userName"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Email" field="$ctrl.client.email" info="You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main"></vn-textfield>
|
||||
<vn-one></vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Create and edit"></vn-submit>
|
||||
<vn-button label="Create" ng-click="watcher.submitBack()"></vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
|
@ -1,23 +1,22 @@
|
|||
import {module} from '../module';
|
||||
import ngModule from '../module';
|
||||
|
||||
class Controller {
|
||||
export default class Controller {
|
||||
constructor($scope, $state) {
|
||||
this.$scope = $scope;
|
||||
this.$ = $scope;
|
||||
this.$state = $state;
|
||||
this.client = {
|
||||
active: true
|
||||
};
|
||||
}
|
||||
onSubmit() {
|
||||
this.$scope.watcher.submit().then(
|
||||
this.$.watcher.submit().then(
|
||||
json => this.$state.go('clientCard.basicData', {id: json.data.id})
|
||||
);
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$state'];
|
||||
|
||||
export const component = {
|
||||
template: require('./index.html'),
|
||||
ngModule.component('vnClientCreate', {
|
||||
template: require('./create.html'),
|
||||
controller: Controller
|
||||
};
|
||||
module.component('vnClientCreate', component);
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import './create.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientCreate', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
let $state;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope.$new();
|
||||
$state = _$state_;
|
||||
$scope.watcher = {
|
||||
submit: () => {
|
||||
return {
|
||||
then: callback => {
|
||||
callback({data: {id: '1234'}});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should call submit() on the watcher then expect a callback`, () => {
|
||||
let controller = $componentController('vnClientCreate', {$scope: $scope});
|
||||
spyOn($state, 'go');
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('clientCard.basicData', {id: '1234'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
<mg-ajax path="/client/api/Clients" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="post">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
||||
<div style="max-width: 70em; margin: 0 auto;">
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Crear Cliente</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Nombre" field="$ctrl.client.name" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="NIF/CIF" field="$ctrl.client.fi"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Razón social" field="$ctrl.client.socialName"></vn-textfield>
|
||||
<vn-one></vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Crear y continuar"></vn-submit>
|
||||
<vn-button label="Crear" ng-click="watcher.submitBack()"></vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Name": "Nombre",
|
||||
"Tax number": "NIF/CIF",
|
||||
"Business name": "Razón social",
|
||||
"User name": "Nombre de usuario",
|
||||
"Email": "Correo electrónico",
|
||||
"Create and edit": "Crear y editar",
|
||||
"Create": "Crear",
|
||||
"You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main": "Puede guardar varios correos electrónicos encadenándolos mediante comas sin espacios, ejemplo: user@dominio.com,user2@dominio.com siendo el primer correo electrónico el principal"
|
||||
}
|
|
@ -5,10 +5,10 @@
|
|||
<i class="material-icons descriptor-icon">person</i>
|
||||
</vn-one>
|
||||
<vn-vertical vn-two>
|
||||
<div class="margin-none">{{descriptor.client.id}}</div>
|
||||
<div class="margin-none">{{descriptor.client.name}}</div>
|
||||
<div class="margin-none">{{descriptor.client.phone}}</div>
|
||||
<vn-switch label="Activo" model="descriptor.active"></vn-switch>
|
||||
<div class="margin-none">{{::$ctrl.client.id}}</div>
|
||||
<div class="margin-none">{{$ctrl.client.name}}</div>
|
||||
<div class="margin-none">{{$ctrl.client.phone}}</div>
|
||||
<vn-switch label="Activo" model="$ctrl.active"></vn-switch>
|
||||
</vn-vertical>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
|
@ -0,0 +1,27 @@
|
|||
import ngModule from '../module';
|
||||
import './style.css';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $http) {
|
||||
this.$http = $http;
|
||||
}
|
||||
set active(value) {
|
||||
if (this._active !== value && this._active !== undefined)
|
||||
this.$http.put(`/client/api/Clients/${this.client.id}/activate`);
|
||||
|
||||
this._active = value;
|
||||
}
|
||||
get active() {
|
||||
return this._active;
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$http'];
|
||||
|
||||
ngModule.component('vnDescriptor', {
|
||||
template: require('./descriptor.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<',
|
||||
active: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import './descriptor.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnDescriptor', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope.$new();
|
||||
}));
|
||||
|
||||
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);
|
||||
controller.active = false;
|
||||
|
||||
expect(controller._active).toBe(false);
|
||||
controller.active = true;
|
||||
|
||||
expect(controller._active).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import {module} from '../module';
|
||||
import './style.css';
|
||||
|
||||
export const NAME = 'vnDescriptor';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controllerAs: 'descriptor',
|
||||
bindings: {
|
||||
client: '<',
|
||||
active: '<'
|
||||
},
|
||||
controller: function($http, $scope) {
|
||||
var self = this;
|
||||
$scope.$watch('descriptor.active', function(newValue, oldValue) {
|
||||
if (oldValue !== undefined)
|
||||
$http.put(`/client/api/Clients/${self.client.id}/activate`);
|
||||
});
|
||||
}
|
||||
};
|
||||
COMPONENT.controller.$inject = ['$http', '$scope'];
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,45 @@
|
|||
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.client"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" pad-medium>
|
||||
<vn-card margin-small-bottom>
|
||||
<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-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-two label="Domicilio fiscal" field="$ctrl.client.street" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Municipio" field="$ctrl.client.city"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Código postal" field="$ctrl.client.postcode"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.client.province"
|
||||
field="$ctrl.client.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Provincia">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
initial-data="$ctrl.client.country"
|
||||
field="$ctrl.client.countryFk"
|
||||
url="/client/api/Countries"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="País">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
ngModule.component('vnClientFiscalData', {
|
||||
template: require('./fiscal-data.html'),
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
<mg-ajax path="/client/api/Clients/{{put.params.id}}" options="vnPut"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="fiscal.client"
|
||||
form="form"
|
||||
save="put">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" pad-medium>
|
||||
<vn-card margin-small-bottom>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Datos fiscales y de facturación</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-check vn-one label="Facturar" field="fiscal.client.hasToInvoice"></vn-check>
|
||||
<vn-check vn-one label="Factura impresa" field="fiscal.client.invoiceByEmail"></vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-two label="Domicilio fiscal" field="fiscal.client.street" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Municipio" field="fiscal.client.city"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Código postal" field="fiscal.client.postcode"></vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
field="fiscal.client.provinceFk"
|
||||
url="/client/api/Provinces"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Provincia">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
field="fiscal.client.countryFk"
|
||||
url="/client/api/Countries"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="País">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-card margin-small-bottom>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Información de facturación</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-two label="IBAN" field="fiscal.client.iban"></vn-textfield>
|
||||
<vn-autocomplete vn-two
|
||||
field="fiscal.client.payMethodFk"
|
||||
url="/client/api/PayMethods"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Forma de pago">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one label="Vencimiento" field="fiscal.client.dueDay"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Crédito" field="fiscal.client.credit"></vn-textfield>
|
||||
<vn-textfield vn-one label="Crédito asegurado" field="fiscal.client.creditInsurance"></vn-textfield>
|
||||
<vn-check vn-three label="Recargo de equivalencia" field="fiscal.client.surcharge"></vn-check>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-card margin-small-bottom>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Documentación</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-check vn-one label="Recibido core VNH" field="fiscal.client.coreVnh"></vn-check>
|
||||
<vn-check vn-one label="Recibido core VNL" field="fiscal.client.coreVnl"></vn-check>
|
||||
<vn-check vn-one label="Recibido B2B VNL" field="fiscal.client.sepaVnl"></vn-check>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,11 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
export const NAME = 'vnClientFiscalData';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controllerAs: 'fiscal',
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
|
@ -1,21 +1,21 @@
|
|||
<mg-ajax path="/client/api/Clients/filter" options="vnIndex"></mg-ajax>
|
||||
<mg-ajax path="/client/api/Clients/filter" options="mgIndex"></mg-ajax>
|
||||
<div margin-medium>
|
||||
<div style="max-width: 40em; margin: 0 auto;">
|
||||
<vn-card>
|
||||
<vn-horizontal pad-medium>
|
||||
<vn-searchbar
|
||||
vn-auto
|
||||
<vn-searchbar vn-auto
|
||||
index="index"
|
||||
on-search="index.accept()"
|
||||
on-search="$ctrl.search(index)"
|
||||
advanced="true"
|
||||
search="$ctrl.model.search"
|
||||
popover="vn-client-search-panel">
|
||||
</vn-searchbar>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-card margin-medium-top>
|
||||
<vn-item-client ng-repeat="client in index.model" title="View client" client="client"></vn-item-client>
|
||||
<vn-item-client ng-repeat="client in index.model.instances" title="View client" client="client"></vn-item-client>
|
||||
</vn-card>
|
||||
<vn-paging index="index"></vn-paging>
|
||||
<vn-paging index="index" total="index.model.count"></vn-paging>
|
||||
</div>
|
||||
<a ui-sref="create" fixed-bottom-right>
|
||||
<vn-float-button icon="person_add"></vn-float-button>
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
import {module} from '../module';
|
||||
import ngModule from '../module';
|
||||
import './style.css';
|
||||
import './item-client';
|
||||
|
||||
export const NAME = 'vnClientIndex';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html')
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
||||
export default class Controller {
|
||||
constructor() {
|
||||
this.model = {};
|
||||
}
|
||||
search(index) {
|
||||
index.filter.search = this.model.search;
|
||||
index.accept();
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnClientIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientIndex', () => {
|
||||
let $componentController;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject(_$componentController_ => {
|
||||
$componentController = _$componentController_;
|
||||
}));
|
||||
|
||||
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: {},
|
||||
accept: () => {
|
||||
return 'accepted';
|
||||
}
|
||||
};
|
||||
controller.search(index);
|
||||
|
||||
expect(index.filter.search).toBe('batman');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
<a ui-sref="clientCard.basicData({ id: {{itemClient.client.id}} })" pad-medium border-solid-bottom>
|
||||
<div class="vn-item-client-name">{{itemClient.client.name}}</div>
|
||||
<div>Id Cliente: <b>{{itemClient.client.id}}</b></div>
|
||||
<div>Teléfono: <b>{{itemClient.client.phone | phone}}</b></div>
|
||||
<div>Población: <b>{{itemClient.client.city}}</b></div>
|
||||
<div>email: <b>{{itemClient.client.email}}</b></div>
|
||||
<a ui-sref="clientCard.basicData({ id: {{$ctrl.client.id}} })" pad-medium border-solid-bottom>
|
||||
<div class="vn-item-client-name">{{$ctrl.client.name}}</div>
|
||||
<div><span translate>Client id</span>: <b>{{$ctrl.client.id}}</b></div>
|
||||
<div><span translate>Phone</span>: <b>{{$ctrl.client.phone | phone}}</b></div>
|
||||
<div><span translate>Town/City</span>: <b>{{$ctrl.client.city}}</b></div>
|
||||
<div><span translate>Email</span>: <b>{{$ctrl.client.email}}</b></div>
|
||||
</a>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import {module} from '../module';
|
||||
import ngModule from '../module';
|
||||
|
||||
export const NAME = 'vnItemClient';
|
||||
export const COMPONENT = {
|
||||
ngModule.component('vnItemClient', {
|
||||
template: require('./item-client.html'),
|
||||
controllerAs: 'itemClient',
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Client id": "Id cliente",
|
||||
"Phone": "Teléfono",
|
||||
"Town/City": "Ciudad",
|
||||
"Email": "Correo electrónico",
|
||||
"Create client": "Crear cliente"
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
{}
|
||||
{
|
||||
"Client": "Client",
|
||||
"Clients": "Clients"
|
||||
}
|
|
@ -1 +1,5 @@
|
|||
{}
|
||||
{
|
||||
"Client": "Cliente",
|
||||
"Clients": "Clientes",
|
||||
"Fiscal data": "Datos Fiscales"
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {ng} from 'vendor';
|
||||
import * as core from 'core';
|
||||
import 'core';
|
||||
|
||||
export const NAME = 'client';
|
||||
export const module = ng.module(NAME, []);
|
||||
const ngModule = ng.module('client', ['vnCore']);
|
||||
export default ngModule;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/ClientObservations"
|
||||
id-field="id"
|
||||
data="newNote.note"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="newNote.onSubmit()" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Nueva nota</vn-title>
|
||||
<vn-textarea label="Nueva nota" model="newNote.note.text" vn-focus padd-medium-top></vn-textarea>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,27 +0,0 @@
|
|||
import template from './index.html';
|
||||
import {module} from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($element, $state) {
|
||||
this.element = $element[0];
|
||||
this.$state = $state;
|
||||
this.note = {
|
||||
client: $state.params.id,
|
||||
text: null
|
||||
};
|
||||
}
|
||||
onSubmit() {
|
||||
this.element.querySelector('vn-watcher').$ctrl.submit().then(
|
||||
() => this.$state.go('clientCard.notes')
|
||||
);
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$state'];
|
||||
|
||||
export const NAME = 'vnNewNote';
|
||||
export const COMPONENT = {
|
||||
template: template,
|
||||
controllerAs: 'newNote',
|
||||
controller: Controller
|
||||
};
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"New note": "Nueva nota",
|
||||
"Note": "Nota"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/ClientObservations"
|
||||
id-field="id"
|
||||
data="$ctrl.note"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submitGo('clientCard.notes.list')" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>New note</vn-title>
|
||||
<vn-textarea label="Note" model="$ctrl.note.text" vn-focus padd-medium-top></vn-textarea>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,16 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($state) {
|
||||
this.note = {
|
||||
clientFk: parseInt($state.params.id),
|
||||
text: null
|
||||
};
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$state'];
|
||||
|
||||
ngModule.component('vnNoteCreate', {
|
||||
template: require('./note-create.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
import './note-create.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnNoteCreate', () => {
|
||||
let $componentController;
|
||||
let $state;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$state = _$state_;
|
||||
$state.params.id = '1234';
|
||||
}));
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
<vn-card ng-show="observation.observations.length" pad-medium>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Notas</vn-title>
|
||||
<vn-horizontal ng-repeat="n in observation.observations" margin-small-bottom style="align-items: center;">
|
||||
<vn-auto style="border-radius: .3em;" class="pad-small border-solid">
|
||||
<div class="notes-date">{{n.creationTime | date:'dd/MM/yyyy HH:mm'}}</div>
|
||||
<div class="notes-date">{{n.employeeFk}}</div>
|
||||
<div>{{n.text}}</div>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-float-button
|
||||
fixed-bottom-right
|
||||
ng-click="observation.newObservation()"
|
||||
icon="add">
|
||||
</vn-float-button>
|
|
@ -1,34 +0,0 @@
|
|||
import './style.css';
|
||||
import template from './index.html';
|
||||
import {module} from '../module';
|
||||
|
||||
export const NAME = 'vnClientNotes';
|
||||
export const COMPONENT = {
|
||||
template: template,
|
||||
controllerAs: 'observation',
|
||||
bindings: {
|
||||
client: '<'
|
||||
},
|
||||
controller: function($http, $state) {
|
||||
this.$onChanges = function(changes) {
|
||||
if (this.client) {
|
||||
this.getObservation(this.client.id);
|
||||
}
|
||||
};
|
||||
|
||||
this.getObservation = function(clientId) {
|
||||
let json = JSON.stringify({where: {clientFk: this.client.id}, order: 'creationTime DESC'});
|
||||
$http.get(`/client/api/clientObservations?filter=${json}`).then(
|
||||
json => {
|
||||
this.observations = json.data;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.newObservation = () => {
|
||||
$state.go("clientCard.newNote", {id: this.client.id});
|
||||
};
|
||||
}
|
||||
};
|
||||
COMPONENT.controller.$inject = ['$http', '$state'];
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"Notes": "Notas"
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<vn-card ng-show="$ctrl.observations.length" pad-medium>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Notes</vn-title>
|
||||
<vn-horizontal ng-repeat="n in $ctrl.observations" margin-small-bottom style="align-items: center;">
|
||||
<vn-auto style="border-radius: .3em;" class="pad-small border-solid">
|
||||
<div class="notes-date">{{::n.created | date:'dd/MM/yyyy HH:mm'}}</div>
|
||||
<div class="notes-date">{{::n.employee.name}}</div>
|
||||
<div>{{::n.text}}</div>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-float-button
|
||||
fixed-bottom-right
|
||||
ng-click="$ctrl.newObservation()"
|
||||
icon="add">
|
||||
</vn-float-button>
|
|
@ -0,0 +1,34 @@
|
|||
import ngModule from '../module';
|
||||
import './style.css';
|
||||
|
||||
export default class Controller {
|
||||
constructor($http, $state) {
|
||||
this.$http = $http;
|
||||
this.$state = $state;
|
||||
}
|
||||
$onChanges() {
|
||||
if (this.client) {
|
||||
this.getObservation(this.client.id);
|
||||
}
|
||||
}
|
||||
getObservation(clientId) {
|
||||
let json = JSON.stringify({where: {clientFk: this.client.id}, order: 'created DESC'});
|
||||
this.$http.get(`/client/api/clientObservations?filter=${json}`).then(
|
||||
json => {
|
||||
this.observations = json.data;
|
||||
}
|
||||
);
|
||||
}
|
||||
newObservation() {
|
||||
this.$state.go("clientCard.notes.create", {id: this.client.id});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$http', '$state'];
|
||||
|
||||
ngModule.component('vnClientNotes', {
|
||||
template: require('./notes.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
import './notes.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientNotes', () => {
|
||||
let $componentController;
|
||||
let $state;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
|
||||
$componentController = _$componentController_;
|
||||
$state = _$state_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
}));
|
||||
|
||||
describe('$onChanges()', () => {
|
||||
it(`should call getObservation() with the client id`, () => {
|
||||
let controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
|
||||
controller.client = {
|
||||
id: 1234
|
||||
};
|
||||
spyOn(controller, 'getObservation').and.returnValue();
|
||||
controller.$onChanges();
|
||||
|
||||
expect(controller.getObservation).toHaveBeenCalledWith(1234);
|
||||
});
|
||||
});
|
||||
|
||||
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, */*'});
|
||||
controller.getObservation();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('clientCard.notes.create', Object({id: '1234'}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
<div pad-large style="min-width: 30em;" ng-show="$ctrl.formVisibility">
|
||||
<form name="form" ng-submit="form.$valid && $ctrl.onSearch()" ng-keyup="$ctrl.getKeyPressed($event)">
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Id Cliente" model="$ctrl.filter.id" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="NIF/CIF" model="$ctrl.filter.fi"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Nombre" model="$ctrl.filter.name"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Razon Social" model="$ctrl.filter.socialName"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Población" model="$ctrl.filter.city"></vn-textfield>
|
||||
<vn-textfield vn-one label="Código Postal" model="$ctrl.filter.postcode"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Email" model="$ctrl.filter.email"></vn-textfield>
|
||||
<vn-textfield vn-one label="Teléfono" model="$ctrl.filter.phone"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal margin-large-top>
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
export const NAME = 'vnClientSearchPanel';
|
||||
export const COMPONENT = {
|
||||
template: require('./index.html'),
|
||||
controller: function($scope, $window) {
|
||||
this.filter = {id: null, fi: null, name: null, socialName: null, city: null, postcode: null, email: null, phone: null};
|
||||
this.formVisibility = true;
|
||||
this.onSearch = () => {
|
||||
this.setStorageValue();
|
||||
this.onSubmit(this.filter);
|
||||
};
|
||||
this.getKeyPressed = function(event) {
|
||||
if (event.which === 27)
|
||||
this.formVisibility = false;
|
||||
};
|
||||
this.$onChanges = () => {
|
||||
var value = JSON.parse($window.sessionStorage.getItem('filter'));
|
||||
if (value !== undefined)
|
||||
this.filter = value;
|
||||
};
|
||||
this.setStorageValue = () => {
|
||||
$window.sessionStorage.setItem('filter', JSON.stringify(this.filter));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.component(NAME, COMPONENT);
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Client id": "Id cliente",
|
||||
"Tax number": "NIF/CIF",
|
||||
"Name": "Nombre",
|
||||
"Social name": "Razon social",
|
||||
"Town/City": "Ciudad",
|
||||
"Postcode": "Código postal",
|
||||
"Email": "Correo electrónico",
|
||||
"Phone": "Teléfono"
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<div pad-large style="min-width: 30em">
|
||||
<form ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Client id" model="$ctrl.filter.id" vn-focus></vn-textfield>
|
||||
<vn-textfield vn-one label="Tax number" model="$ctrl.filter.fi"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Name" model="$ctrl.filter.name"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Social name" model="$ctrl.filter.socialName"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Town/City" model="$ctrl.filter.city"></vn-textfield>
|
||||
<vn-textfield vn-one label="Postcode" model="$ctrl.filter.postcode"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Email" model="$ctrl.filter.email"></vn-textfield>
|
||||
<vn-textfield vn-one label="Phone" model="$ctrl.filter.phone"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal margin-large-top>
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,27 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($window) {
|
||||
this.$window = $window;
|
||||
// onSubmit() is defined by @vnSearchbar
|
||||
this.onSubmit = () => {};
|
||||
}
|
||||
onSearch() {
|
||||
this.setStorageValue();
|
||||
this.onSubmit(this.filter);
|
||||
}
|
||||
$onChanges() {
|
||||
var value = JSON.parse(this.$window.sessionStorage.getItem('filter'));
|
||||
if (value !== undefined)
|
||||
this.filter = value;
|
||||
}
|
||||
setStorageValue() {
|
||||
this.$window.sessionStorage.setItem('filter', JSON.stringify(this.filter));
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$window'];
|
||||
|
||||
ngModule.component('vnClientSearchPanel', {
|
||||
template: require('./search-panel.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
import './search-panel.js';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientSearchPanel', () => {
|
||||
let $componentController;
|
||||
let $window;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$window_) => {
|
||||
$componentController = _$componentController_;
|
||||
$window = _$window_;
|
||||
}));
|
||||
|
||||
describe('onSearch()', () => {
|
||||
it(`should call setStorageValue() and onSubmit()`, () => {
|
||||
let controller = $componentController('vnClientSearchPanel', {$window: $window});
|
||||
spyOn(controller, 'setStorageValue');
|
||||
spyOn(controller, 'onSubmit');
|
||||
controller.setStorageValue();
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.setStorageValue).toHaveBeenCalledWith();
|
||||
expect(controller.onSubmit).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
expect(controller.filter).toBe(JSON.parse({data: 'data'}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Accounts"
|
||||
id-field="id"
|
||||
data="$ctrl.client.account"
|
||||
to="$ctrl.account"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" pad-medium>
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Web access</vn-title>
|
||||
<vn-check label="Acceso Web" field="$ctrl.account.active"></vn-check>
|
||||
<vn-textfield label="Usuario" class="margin-medium-top" field="$ctrl.account.name" vn-focus></vn-textfield>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Guardar"></vn-submit>
|
||||
<vn-button label="Cambiar contraseña" vn-dialog="change-pass"></vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
<vn-dialog
|
||||
vn-id="change-pass"
|
||||
on-open="$ctrl.onPassOpen()"
|
||||
on-response="$ctrl.onPassChange(response)">
|
||||
<dbody>
|
||||
<vn-password label="New password" model="$ctrl.newPassword"></vn-password>
|
||||
<vn-password label="Repeat password" model="$ctrl.repeatPassword"></vn-password>
|
||||
</dbody>
|
||||
<buttons>
|
||||
<button response="CANCEL" translate>Cancel</button>
|
||||
<button response="ACCEPT" translate>Change password</button>
|
||||
</buttons>
|
||||
</vn-dialog>
|
|
@ -1,48 +0,0 @@
|
|||
import {module} from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($scope, $http, vnAppLogger) {
|
||||
this.$scope = $scope;
|
||||
this.$http = $http;
|
||||
this.vnAppLogger = vnAppLogger;
|
||||
}
|
||||
$onChanges() {
|
||||
if(this.client)
|
||||
this.account = this.client.account;
|
||||
}
|
||||
onPassOpen() {
|
||||
this.newPassword = '';
|
||||
this.repeatPassword = '';
|
||||
this.$scope.$apply();
|
||||
}
|
||||
onPassChange(response) {
|
||||
if(response == 'ACCEPT')
|
||||
try {
|
||||
if(!this.newPassword)
|
||||
throw new Error(`Passwords can't be empty`);
|
||||
if(this.newPassword != this.repeatPassword)
|
||||
throw new Error(`Passwords don't match`);
|
||||
|
||||
let account = {
|
||||
password: this.newPassword
|
||||
};
|
||||
|
||||
this.$http.put(`/client/api/Accounts/${this.client.id}`, account);
|
||||
}
|
||||
catch(e) {
|
||||
this.vnAppLogger.showError(e.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$http', 'vnAppLogger'];
|
||||
|
||||
module.component('vnClientWebAccess', {
|
||||
template: require('./index.html'),
|
||||
bindings: {
|
||||
client: '<'
|
||||
},
|
||||
controller: Controller
|
||||
});
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"User": "Usuario",
|
||||
"Enable web access": "Habilitar acceso web",
|
||||
"Web access": "Acceso web",
|
||||
"New password": "Nueva contraseña",
|
||||
"Repeat password": "Repetir contraseña",
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="/client/api/Accounts"
|
||||
id-field="id"
|
||||
data="$ctrl.account"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<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-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
<vn-button ng-if="$ctrl.canChangePassword" label="Change password" vn-dialog="change-pass"></vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
<vn-dialog
|
||||
vn-id="change-pass"
|
||||
on-open="$ctrl.onPassOpen()"
|
||||
on-response="$ctrl.onPassChange(response)">
|
||||
<tpl-body>
|
||||
<vn-textfield
|
||||
type="password"
|
||||
label="New password"
|
||||
model="$ctrl.newPassword">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
type="password"
|
||||
label="Repeat password"
|
||||
model="$ctrl.repeatPassword">
|
||||
</vn-textfield>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<button response="CANCEL" translate>Cancel</button>
|
||||
<button response="ACCEPT" translate>Change password</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
|
@ -0,0 +1,60 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $http, vnApp) {
|
||||
this.$ = $scope;
|
||||
this.$http = $http;
|
||||
this.vnApp = vnApp;
|
||||
this.canChangePassword = false;
|
||||
}
|
||||
$onChanges() {
|
||||
if (this.client) {
|
||||
this.account = this.client.account;
|
||||
this.isCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
isCustomer() {
|
||||
if (this.client && this.client.id) {
|
||||
this.$http.get(`/client/api/Clients/${this.client.id}/getRoleCustomer`).then(res => {
|
||||
this.canChangePassword = (res.data) ? res.data.isCustomer : false;
|
||||
});
|
||||
} else {
|
||||
this.canChangePassword = false;
|
||||
}
|
||||
}
|
||||
|
||||
onPassOpen() {
|
||||
this.newPassword = '';
|
||||
this.repeatPassword = '';
|
||||
this.$.$apply();
|
||||
}
|
||||
onPassChange(response) {
|
||||
if (response == 'ACCEPT' && this.canChangePassword)
|
||||
try {
|
||||
if (!this.newPassword)
|
||||
throw new Error(`Passwords can't be empty`);
|
||||
if (this.newPassword != this.repeatPassword)
|
||||
throw new Error(`Passwords don't match`);
|
||||
let account = {
|
||||
password: this.newPassword
|
||||
};
|
||||
|
||||
this.$http.patch(`/client/api/Accounts/${this.client.id}`, account);
|
||||
} catch (e) {
|
||||
this.vnApp.showError(e.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$http', 'vnApp'];
|
||||
|
||||
ngModule.component('vnClientWebAccess', {
|
||||
template: require('./web-access.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,96 @@
|
|||
import './web-access.js';
|
||||
|
||||
describe('Component VnClientWebAccess', () => {
|
||||
let $componentController;
|
||||
let $httpBackend;
|
||||
let $scope;
|
||||
let vnApp;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _vnApp_) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope.$new();
|
||||
$httpBackend = _$httpBackend_;
|
||||
vnApp = _vnApp_;
|
||||
spyOn(vnApp, 'showError');
|
||||
}));
|
||||
|
||||
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 = {};
|
||||
controller.$onChanges();
|
||||
|
||||
expect(controller.account).toBe('Wayne Industries');
|
||||
expect(controller.isCustomer).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
$httpBackend.when('GET', `/client/api/Clients/${controller.client.id}/getRoleCustomer`).respond('ok');
|
||||
$httpBackend.expectGET(`/client/api/Clients/${controller.client.id}/getRoleCustomer`);
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onPassOpen()', () => {
|
||||
it('should set passwords to empty values', () => {
|
||||
let controller = $componentController('vnClientWebAccess', {$scope: $scope});
|
||||
controller.newPassword = 'm24x8';
|
||||
controller.repeatPassword = 'm24x8';
|
||||
controller.onPassOpen();
|
||||
|
||||
expect(controller.newPassword).toBe('');
|
||||
expect(controller.repeatPassword).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
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';
|
||||
controller.canChangePassword = true;
|
||||
$httpBackend.when('PATCH', '/client/api/Accounts/1234').respond('done');
|
||||
$httpBackend.expectPATCH('/client/api/Accounts/1234', {password: 'm24x8'});
|
||||
controller.onPassChange('ACCEPT');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
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;
|
||||
controller.onPassChange('ACCEPT');
|
||||
|
||||
expect(vnApp.showError).toHaveBeenCalledWith(`Passwords can't be empty`);
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
controller.repeatPassword = 'notMatchingPassword';
|
||||
controller.onPassChange('ACCEPT');
|
||||
|
||||
expect(vnApp.showError).toHaveBeenCalledWith(`Passwords don't match`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,6 +5,6 @@
|
|||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://git.verdnatura.es:/salix"
|
||||
"url": "https://git.verdnatura.es/salix"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<vn-vertical ng-click="$ctrl.showDropDown = true">
|
||||
<vn-textfield vn-one label="{{$ctrl.label}}" model="$ctrl.displayValue" readonly="$ctrl.readonly"></vn-textfield>
|
||||
<vn-drop-down vn-one
|
||||
items="$ctrl.items"
|
||||
show="$ctrl.showDropDown"
|
||||
selected="$ctrl.field"
|
||||
filter="true"
|
||||
load-more="$ctrl.getItems()"
|
||||
show-load-more="$ctrl.maxRow"
|
||||
filter-action="$ctrl.findItems(search)"
|
||||
item-width="$ctrl.width"
|
||||
multiple="$ctrl.multiple"
|
||||
><vn-item ng-transclude="tplItem">{{$parent.item.name}}</vn-item></vn-drop-down>
|
||||
</vn-vertical>
|
|
@ -0,0 +1,304 @@
|
|||
import {module} from '../module';
|
||||
import Component from '../lib/component';
|
||||
import './style.scss';
|
||||
|
||||
class Autocomplete extends Component {
|
||||
constructor($element, $scope, $http, $timeout) {
|
||||
super($element);
|
||||
this.$element = $element;
|
||||
this.$scope = $scope;
|
||||
this.$http = $http;
|
||||
this.$timeout = $timeout;
|
||||
|
||||
this._showDropDown = false;
|
||||
this.finding = false;
|
||||
this.findMore = false;
|
||||
this._value = null;
|
||||
this._field = null;
|
||||
this._preLoad = false;
|
||||
this.maxRow = 10;
|
||||
this.showField = this.showField || 'name';
|
||||
this.valueField = this.valueField || 'id';
|
||||
this.items = this.data || [];
|
||||
this.displayValueMultiCheck = [];
|
||||
this._multiField = [];
|
||||
this.readonly = true;
|
||||
}
|
||||
|
||||
get showDropDown() {
|
||||
return this._showDropDown;
|
||||
}
|
||||
set showDropDown(value) {
|
||||
if (value && this.url && !this._preLoad) {
|
||||
this._preLoad = true;
|
||||
this.getItems();
|
||||
}
|
||||
this._showDropDown = value;
|
||||
}
|
||||
|
||||
get displayValue() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set displayValue(value) {
|
||||
let val = (value === undefined || value === '') ? null : value;
|
||||
if (this.multiple && val) {
|
||||
let index = this.displayValueMultiCheck.indexOf(val);
|
||||
if (index === -1)
|
||||
this.displayValueMultiCheck.push(val);
|
||||
else
|
||||
this.displayValueMultiCheck.splice(index, 1);
|
||||
|
||||
this._value = this.displayValueMultiCheck.join(', ');
|
||||
} else {
|
||||
this._value = val;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
this.field = null;
|
||||
if (this.multiple && this.items.length) {
|
||||
this.displayValueMultiCheck = [];
|
||||
this.items.map(item => {
|
||||
item.checked = false;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get field() {
|
||||
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]);
|
||||
}
|
||||
} 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});
|
||||
}
|
||||
|
||||
set initialData(value) {
|
||||
if (value) {
|
||||
this.field = value;
|
||||
}
|
||||
}
|
||||
|
||||
setMultiField(val) {
|
||||
if (val && typeof val === 'object' && val[this.valueField]) {
|
||||
val = val[this.valueField];
|
||||
}
|
||||
if (val === null) {
|
||||
this._multiField = [];
|
||||
} else {
|
||||
let index = this._multiField.indexOf(val);
|
||||
if (index === -1) {
|
||||
this._multiField.push(val);
|
||||
} else {
|
||||
this._multiField.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
if (value) {
|
||||
let data = this.items;
|
||||
|
||||
if (data && data.length)
|
||||
for (let i = 0; i < data.length; i++)
|
||||
if (data[i][this.valueField] === value) {
|
||||
this.showItem(data[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestItem(value);
|
||||
} else {
|
||||
this._field = null;
|
||||
this.setMultiField(null);
|
||||
this.displayValue = '';
|
||||
}
|
||||
}
|
||||
|
||||
requestItem(value) {
|
||||
if (!value) return;
|
||||
|
||||
let where = {};
|
||||
where[this.valueField] = value;
|
||||
|
||||
let filter = {
|
||||
fields: this.getRequestFields(),
|
||||
where: where
|
||||
};
|
||||
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => this.onItemRequest(json.data),
|
||||
json => this.onItemRequest(null)
|
||||
);
|
||||
}
|
||||
onItemRequest(data) {
|
||||
if (data && data.length > 0)
|
||||
this.showItem(data[0]);
|
||||
else
|
||||
this.showItem(null);
|
||||
}
|
||||
|
||||
showItem(item) {
|
||||
this.displayValue = item ? item[this.showField] : '';
|
||||
this.field = item;
|
||||
}
|
||||
|
||||
getRequestFields() {
|
||||
let fields = {};
|
||||
fields[this.valueField] = true;
|
||||
fields[this.showField] = true;
|
||||
|
||||
if (this._selectFields)
|
||||
for (let field of this._selectFields)
|
||||
fields[field] = true;
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
findItems(search) {
|
||||
if (!this.url)
|
||||
return this.items ? this.items : [];
|
||||
|
||||
if (search && !this.finding) {
|
||||
this.maxRow = false;
|
||||
let filter = {where: {name: {regexp: search}}};
|
||||
let json = JSON.stringify(filter);
|
||||
this.finding = true;
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => {
|
||||
this.items = [];
|
||||
json.data.forEach(
|
||||
el => {
|
||||
if (this.multiple) {
|
||||
el.checked = this.field.indexOf(el[this.valueField]) !== -1;
|
||||
}
|
||||
this.items.push(el);
|
||||
}
|
||||
);
|
||||
this.finding = false;
|
||||
},
|
||||
() => {
|
||||
this.finding = false;
|
||||
}
|
||||
);
|
||||
} else if (!search && !this.finding) {
|
||||
this.maxRow = 10;
|
||||
this.items = [];
|
||||
this.getItems();
|
||||
}
|
||||
}
|
||||
|
||||
getItems() {
|
||||
let filter = {};
|
||||
|
||||
if (this.maxRow) {
|
||||
if (this.items) {
|
||||
filter.skip = this.items.length;
|
||||
}
|
||||
filter.limit = this.maxRow;
|
||||
filter.order = 'name ASC';
|
||||
}
|
||||
|
||||
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.items.push(el);
|
||||
}
|
||||
);
|
||||
else
|
||||
this.maxRow = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.findMore = this.url && this.maxRow;
|
||||
this.mouseFocus = false;
|
||||
this.focused = false;
|
||||
|
||||
this.$element.bind('mouseover', e => {
|
||||
this.$timeout(() => {
|
||||
this.mouseFocus = true;
|
||||
this.showDropDown = this.focused;
|
||||
});
|
||||
});
|
||||
|
||||
this.$element.bind('mouseout', () => {
|
||||
this.$timeout(() => {
|
||||
this.mouseFocus = false;
|
||||
this.showDropDown = this.focused;
|
||||
});
|
||||
});
|
||||
this.$element.bind('focusin', e => {
|
||||
this.$timeout(() => {
|
||||
this.focused = true;
|
||||
this.showDropDown = true;
|
||||
});
|
||||
});
|
||||
this.$element.bind('focusout', e => {
|
||||
this.$timeout(() => {
|
||||
this.focused = false;
|
||||
this.showDropDown = this.mouseFocus;
|
||||
});
|
||||
});
|
||||
|
||||
let rectangle = this.$element[0].getBoundingClientRect();
|
||||
this.width = Math.round(rectangle.width) - 10;
|
||||
}
|
||||
|
||||
$onDestroy() {
|
||||
this.$element.unbind('mouseover');
|
||||
this.$element.unbind('mouseout');
|
||||
this.$element.unbind('focusin');
|
||||
this.$element.unbind('focusout');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Autocomplete.$inject = ['$element', '$scope', '$http', '$timeout'];
|
||||
|
||||
module.component('vnAutocomplete', {
|
||||
template: require('./autocomplete.html'),
|
||||
controller: Autocomplete,
|
||||
bindings: {
|
||||
url: '@?',
|
||||
showField: '@?',
|
||||
valueField: '@?',
|
||||
selectFields: '@?',
|
||||
initialData: '<?',
|
||||
onChange: '&?',
|
||||
data: '<?',
|
||||
itemAs: '@?',
|
||||
field: '=',
|
||||
label: '@',
|
||||
itemTemplate: '@?',
|
||||
multiple: '@?'
|
||||
},
|
||||
transclude: {
|
||||
tplItem: '?tplItem'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,200 @@
|
|||
import './autocomplete.js';
|
||||
|
||||
describe('Component vnAutocomplete', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
let $httpBackend;
|
||||
let $timeout;
|
||||
let $element;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope.$new();
|
||||
$httpBackend = _$httpBackend_;
|
||||
$timeout = _$timeout_;
|
||||
$element = angular.element('<div></div>');
|
||||
}));
|
||||
|
||||
describe('showDropDown() setter', () => {
|
||||
it(`should set _showDropDown value`, () => {
|
||||
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
|
||||
controller._showDropDown = '';
|
||||
controller.showDropDown = 'some value';
|
||||
|
||||
expect(controller._showDropDown).toEqual('some value');
|
||||
});
|
||||
|
||||
it(`should set _showDropDown value`, () => {
|
||||
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
|
||||
controller._showDropDown = '';
|
||||
controller.showDropDown = 'some value';
|
||||
|
||||
expect(controller._showDropDown).toEqual('some value');
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
expect(controller._value).toEqual(value);
|
||||
});
|
||||
|
||||
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';
|
||||
|
||||
expect(controller._value).toEqual('some value, another value');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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'};
|
||||
|
||||
expect(controller._field).toEqual(1);
|
||||
expect(controller._multiField[0]).toEqual(1);
|
||||
|
||||
controller.field = {id: 1, name: 'Bruce Wayne'};
|
||||
|
||||
expect(controller._multiField).toEqual([]);
|
||||
expect(controller._field).toEqual(1);
|
||||
});
|
||||
|
||||
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'};
|
||||
|
||||
expect(controller._field).toEqual(1);
|
||||
expect(controller._multiField[0]).toEqual(1);
|
||||
|
||||
controller.field = {id: 1, name: 'Bruce Wayne'};
|
||||
|
||||
expect(controller._multiField).toEqual([]);
|
||||
expect(controller._field).toEqual(1);
|
||||
});
|
||||
|
||||
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'};
|
||||
|
||||
expect(controller.displayValue).toEqual('Bruce Wayne');
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
expect(controller._field).toEqual(2);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
expect(controller._multiField[0]).toEqual(2);
|
||||
|
||||
controller.field = 2;
|
||||
|
||||
expect(controller._multiField).toEqual([]);
|
||||
});
|
||||
|
||||
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'});
|
||||
$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'}];
|
||||
controller.field = 3;
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
expect(controller.displayValue).toEqual('Bruce Wayne');
|
||||
});
|
||||
|
||||
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'});
|
||||
$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'}];
|
||||
controller.field = 3;
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('findItem()', () => {
|
||||
it(`should return items array if the controller does not provide a url and nither it has 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'});
|
||||
let search = 'The Joker';
|
||||
let json = JSON.stringify({where: {name: {regexp: search}}});
|
||||
$httpBackend.whenGET(`test.com?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
|
||||
$httpBackend.expectGET(`test.com?filter=${json}`);
|
||||
controller.findItems(search);
|
||||
$httpBackend.flush();
|
||||
|
||||
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';
|
||||
let json = JSON.stringify({where: {name: {regexp: search}}});
|
||||
$httpBackend.whenGET(`test.com?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
|
||||
$httpBackend.expectGET(`test.com?filter=${json}`);
|
||||
controller.findItems(search);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.items[0]).toEqual({id: 3, name: 'The Joker'});
|
||||
});
|
||||
|
||||
// siguiente test el de Multiple!
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
|
||||
<input type="text"
|
||||
class="mdl-textfield__input"
|
||||
ng-keydown="$ctrl.onKeydown($event)"
|
||||
ng-click="$ctrl.onClick($event)"
|
||||
ng-keyup="$ctrl.onKeyup($event)"
|
||||
ng-focus="$ctrl.onFocus($event)"
|
||||
ng-blur="$ctrl.onBlur($event)"/>
|
||||
<label class="mdl-textfield__label">{{$ctrl.label | translate}}</label>
|
||||
</div>
|
|
@ -1,356 +0,0 @@
|
|||
import {module} from '../module';
|
||||
import Component from '../lib/component';
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* Combobox like component with search and partial data loading features.
|
||||
*/
|
||||
export default class Autocomplete extends Component {
|
||||
constructor($element, $scope, $http, vnPopover) {
|
||||
super($element);
|
||||
this.input = $element[0].querySelector('input');
|
||||
this.item = null;
|
||||
this.data = null;
|
||||
this.popover = null;
|
||||
this.popoverData = null;
|
||||
this.timeoutId = null;
|
||||
this.lastSearch = null;
|
||||
this.lastRequest = null;
|
||||
this.currentRequest = null;
|
||||
this.moreData = false;
|
||||
this.activeOption = -1;
|
||||
this.maxRows = 10;
|
||||
this.requestDelay = 350;
|
||||
this.locked = false;
|
||||
this.$http = $http;
|
||||
this.$scope = $scope;
|
||||
this.vnPopover = vnPopover;
|
||||
|
||||
componentHandler.upgradeElement($element[0].firstChild);
|
||||
this.requestItem();
|
||||
}
|
||||
set field(value) {
|
||||
this.locked = true;
|
||||
this.setValue(value);
|
||||
this.locked = false;
|
||||
}
|
||||
get field() {
|
||||
return this.value;
|
||||
}
|
||||
mdlUpdate() {
|
||||
let mdlField = this.element.firstChild.MaterialTextfield;
|
||||
if (mdlField)
|
||||
mdlField.updateClasses_();
|
||||
}
|
||||
loadData(textFilter) {
|
||||
textFilter = textFilter ? textFilter : '';
|
||||
|
||||
if (this.lastSearch === textFilter) {
|
||||
this.popoverDataReady();
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastSearch = textFilter;
|
||||
|
||||
let lastRequest = this.lastRequest;
|
||||
let requestWillSame = lastRequest !== null
|
||||
&& !this.moreData
|
||||
&& textFilter.substr(0, lastRequest.length) === lastRequest;
|
||||
|
||||
if (requestWillSame)
|
||||
this.localFilter(textFilter);
|
||||
else
|
||||
this.requestData(textFilter, false);
|
||||
}
|
||||
getRequestFields() {
|
||||
let fields = {};
|
||||
fields[this.valueField] = true;
|
||||
fields[this.showField] = true;
|
||||
return fields;
|
||||
}
|
||||
requestData(textFilter, append) {
|
||||
let where = {};
|
||||
let skip = 0;
|
||||
|
||||
if (textFilter)
|
||||
where[this.showField] = {regexp: textFilter};
|
||||
if (append && this.data)
|
||||
skip = this.data.length;
|
||||
|
||||
let filter = {
|
||||
fields: this.getRequestFields(),
|
||||
where: where,
|
||||
order: `${this.showField} ASC`,
|
||||
skip: skip,
|
||||
limit: this.maxRows
|
||||
};
|
||||
|
||||
this.lastRequest = textFilter ? textFilter : '';
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
if (this.currentRequest)
|
||||
this.currentRequest.resolve();
|
||||
|
||||
this.currentRequest = this.$http.get(`${this.url}?filter=${json}`);
|
||||
this.currentRequest.then(
|
||||
json => this.onRequest(json.data, append),
|
||||
json => this.onRequest([])
|
||||
);
|
||||
}
|
||||
onRequest(data, append) {
|
||||
this.currentRequest = null;
|
||||
this.moreData = data.length >= this.maxRows;
|
||||
|
||||
if (!append || !this.data)
|
||||
this.data = data;
|
||||
else
|
||||
this.data = this.data.concat(data);
|
||||
|
||||
this.setPopoverData(this.data);
|
||||
}
|
||||
localFilter(textFilter) {
|
||||
let regex = new RegExp(textFilter, 'i');
|
||||
let data = this.data.filter(item => {
|
||||
return regex.test(item[this.showField]);
|
||||
});
|
||||
this.setPopoverData(data);
|
||||
}
|
||||
setPopoverData(data) {
|
||||
this.popoverData = data;
|
||||
this.popoverDataReady();
|
||||
}
|
||||
popoverDataReady() {
|
||||
if (this.hasFocus)
|
||||
this.showPopover();
|
||||
}
|
||||
showPopover() {
|
||||
if (!this.data) return;
|
||||
|
||||
let fragment = this.document.createDocumentFragment();
|
||||
let data = this.popoverData;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let li = this.document.createElement('li');
|
||||
li.appendChild(this.document.createTextNode(data[i][this.showField]));
|
||||
fragment.appendChild(li);
|
||||
}
|
||||
|
||||
if (this.moreData) {
|
||||
let li = this.document.createElement('li');
|
||||
li.appendChild(this.document.createTextNode('Load more'));
|
||||
li.className = 'load-more';
|
||||
fragment.appendChild(li);
|
||||
}
|
||||
|
||||
if (this.popover) {
|
||||
this.popover.innerHTML = '';
|
||||
this.popover.appendChild(fragment);
|
||||
} else {
|
||||
let popover = this.document.createElement('ul');
|
||||
popover.addEventListener('click',
|
||||
e => this.onPopoverClick(e));
|
||||
popover.addEventListener('mousedown',
|
||||
e => this.onPopoverMousedown(e));
|
||||
popover.className = 'vn-autocomplete';
|
||||
popover.appendChild(fragment);
|
||||
this.vnPopover.show(popover, this.input);
|
||||
this.popover = popover;
|
||||
}
|
||||
}
|
||||
hidePopover() {
|
||||
if (!this.popover) return;
|
||||
this.activeOption = -1;
|
||||
this.vnPopover.hide();
|
||||
this.popover = null;
|
||||
}
|
||||
selectPopoverOption(index) {
|
||||
if (!this.popover || index === -1) return;
|
||||
if (index < this.popoverData.length) {
|
||||
this.selectOptionByDataIndex(this.popoverData, index);
|
||||
this.hidePopover();
|
||||
} else
|
||||
this.requestData(this.lastRequest, true);
|
||||
}
|
||||
onPopoverClick(event) {
|
||||
let childs = this.popover.childNodes;
|
||||
for (let i = 0; i < childs.length; i++)
|
||||
if (childs[i] === event.target) {
|
||||
this.selectPopoverOption(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
onPopoverMousedown(event) {
|
||||
// Prevents input from loosing focus
|
||||
event.preventDefault();
|
||||
}
|
||||
onClick(event) {
|
||||
if (!this.popover)
|
||||
this.showPopover();
|
||||
}
|
||||
onFocus() {
|
||||
this.hasFocus = true;
|
||||
this.input.select();
|
||||
|
||||
if (this.data)
|
||||
this.showPopover();
|
||||
else
|
||||
this.loadData();
|
||||
}
|
||||
onBlur() {
|
||||
this.hasFocus = false;
|
||||
this.restoreShowValue();
|
||||
this.hidePopover();
|
||||
}
|
||||
onKeydown(event) {
|
||||
switch (event.keyCode) {
|
||||
case 13: // Enter
|
||||
this.selectPopoverOption(this.activeOption);
|
||||
break;
|
||||
case 27: // Escape
|
||||
this.restoreShowValue();
|
||||
this.input.select();
|
||||
break;
|
||||
case 38: // Arrow up
|
||||
this.activateOption(this.activeOption - 1);
|
||||
break;
|
||||
case 40: // Arrow down
|
||||
this.activateOption(this.activeOption + 1);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
onKeyup(event) {
|
||||
if (!this.isKeycodePrintable(event.keyCode)) return;
|
||||
if (this.timeoutId) clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(() => this.onTimeout(), this.requestDelay);
|
||||
}
|
||||
onTimeout() {
|
||||
this.loadData(this.input.value);
|
||||
this.timeoutId = null;
|
||||
}
|
||||
isKeycodePrintable(keyCode) {
|
||||
return keyCode === 32 // Spacebar
|
||||
|| keyCode === 8 // Backspace
|
||||
|| (keyCode > 47 && keyCode < 58) // Numbers
|
||||
|| (keyCode > 64 && keyCode < 91) // Letters
|
||||
|| (keyCode > 95 && keyCode < 112) // Numpad
|
||||
|| (keyCode > 185 && keyCode < 193) // ;=,-./`
|
||||
|| (keyCode > 218 && keyCode < 223); // [\]'
|
||||
}
|
||||
restoreShowValue() {
|
||||
this.putItem(this.item);
|
||||
}
|
||||
requestItem() {
|
||||
if (!this.value) return;
|
||||
|
||||
let where = {};
|
||||
where[this.valueField] = this.value;
|
||||
|
||||
let filter = {
|
||||
fields: this.getRequestFields(),
|
||||
where: where
|
||||
};
|
||||
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => this.onItemRequest(json.data),
|
||||
json => this.onItemRequest(null)
|
||||
);
|
||||
}
|
||||
onItemRequest(data) {
|
||||
if (data && data.length > 0)
|
||||
this.showItem(data[0]);
|
||||
else
|
||||
this.showItem(null);
|
||||
}
|
||||
activateOption(index) {
|
||||
if (!this.popover)
|
||||
this.showPopover();
|
||||
|
||||
let popover = this.popover;
|
||||
let childs = popover.childNodes;
|
||||
let len = this.popoverData.length;
|
||||
|
||||
if (this.activeOption >= 0)
|
||||
childs[this.activeOption].className = '';
|
||||
|
||||
if (index >= len)
|
||||
index = 0;
|
||||
else if (index < 0)
|
||||
index = len - 1;
|
||||
|
||||
if (index >= 0) {
|
||||
let opt = childs[index];
|
||||
let top = popover.scrollTop;
|
||||
let height = popover.clientHeight;
|
||||
|
||||
if (opt.offsetTop + opt.offsetHeight > top + height)
|
||||
top = opt.offsetTop + opt.offsetHeight - height;
|
||||
else if (opt.offsetTop < top)
|
||||
top = opt.offsetTop;
|
||||
|
||||
opt.className = 'active';
|
||||
popover.scrollTop = top;
|
||||
}
|
||||
|
||||
this.activeOption = index;
|
||||
}
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
|
||||
if (value) {
|
||||
let data = this.data;
|
||||
|
||||
if (data)
|
||||
for (let i = 0; i < data.length; i++)
|
||||
if (data[i][this.valueField] == value) {
|
||||
this.putItem(data[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestItem();
|
||||
} else
|
||||
this.putItem(null);
|
||||
}
|
||||
selectOptionByIndex(index) {
|
||||
this.selectOptionByDataIndex(this.data, index);
|
||||
}
|
||||
selectOptionByDataIndex(data, index) {
|
||||
if (data && index >= 0 && index < data.length)
|
||||
this.putItem(data[index]);
|
||||
else
|
||||
this.putItem(null);
|
||||
}
|
||||
putItem(item) {
|
||||
this.showItem(item);
|
||||
let value = item ? item[this.valueField] : undefined;
|
||||
|
||||
if (!this.locked) {
|
||||
this.value = value;
|
||||
setTimeout(
|
||||
() => this.$scope.$apply());
|
||||
}
|
||||
}
|
||||
showItem(item) {
|
||||
this.input.value = item ? item[this.showField] : '';
|
||||
this.item = item;
|
||||
this.mdlUpdate();
|
||||
}
|
||||
}
|
||||
Autocomplete.$inject = ['$element', '$scope', '$http', 'vnPopover'];
|
||||
|
||||
module.component('vnAutocomplete', {
|
||||
template: require('./index.html'),
|
||||
bindings: {
|
||||
url: '@',
|
||||
showField: '@',
|
||||
valueField: '@',
|
||||
field: '=',
|
||||
label: '@'
|
||||
},
|
||||
controller: Autocomplete
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue