diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js
index b50aaf824..41d551f2b 100644
--- a/back/methods/dms/uploadFile.js
+++ b/back/methods/dms/uploadFile.js
@@ -94,7 +94,7 @@ module.exports = Self => {
const models = Self.app.models;
const storageConnector = Self.app.dataSources.storage.connector;
const myUserId = ctx.req.accessToken.userId;
- const myWorker = await models.Worker.findOne({where: {userFk: myUserId}});
+ const myWorker = await models.Worker.findOne({where: {userFk: myUserId}}, myOptions);
const args = ctx.args;
const newDms = await Self.create({
diff --git a/db/changes/10091-iberflora/00-department.sql b/db/changes/10091-iberflora/00-department.sql
new file mode 100644
index 000000000..5c2889985
--- /dev/null
+++ b/db/changes/10091-iberflora/00-department.sql
@@ -0,0 +1,83 @@
+ALTER TABLE `vn2008`.`department`
+ADD COLUMN `parentFk` INT UNSIGNED NULL AFTER `sons`,
+ADD COLUMN `path` VARCHAR(255) NULL AFTER `parentFk`,
+CHANGE COLUMN `sons` `sons` DECIMAL(10,0) NOT NULL DEFAULT '0' ;
+
+USE `vn`;
+CREATE
+ OR REPLACE ALGORITHM = UNDEFINED
+ DEFINER = `root`@`%`
+ SQL SECURITY DEFINER
+VIEW `department` AS
+ SELECT
+ `b`.`department_id` AS `id`,
+ `b`.`name` AS `name`,
+ `b`.`production` AS `isProduction`,
+ `b`.`parentFk` AS `parentFk`,
+ `b`.`path` AS `path`,
+ `b`.`lft` AS `lft`,
+ `b`.`rgt` AS `rgt`,
+ `b`.`isSelected` AS `isSelected`,
+ `b`.`depth` AS `depth`,
+ `b`.`sons` AS `sons`
+ FROM
+ `vn2008`.`department` `b`;
+
+DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_DELETE`;
+
+DELIMITER $$
+USE `vn2008`$$
+CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_DELETE`
+ AFTER DELETE ON `department` FOR EACH ROW
+BEGIN
+ UPDATE vn.department_recalc SET isChanged = TRUE;
+END$$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS `vn2008`.`department_BEFORE_INSERT`;
+
+DELIMITER $$
+USE `vn2008`$$
+CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_BEFORE_INSERT`
+ BEFORE INSERT ON `department` FOR EACH ROW
+BEGIN
+ UPDATE vn.department_recalc SET isChanged = TRUE;
+END$$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_UPDATE`;
+
+DELIMITER $$
+USE `vn2008`$$
+CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_UPDATE`
+ AFTER UPDATE ON `department` FOR EACH ROW
+BEGIN
+ IF !(OLD.parentFk <=> NEW.parentFk) THEN
+ UPDATE vn.department_recalc SET isChanged = TRUE;
+ END IF;
+END$$
+DELIMITER ;
+
+CREATE TABLE `vn`.`department_recalc` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `isChanged` TINYINT(4) NOT NULL,
+ PRIMARY KEY (`id`));
+
+INSERT INTO `vn`.`department_recalc` (`id`, `isChanged`) VALUES ('1', '0');
+
+
+ALTER TABLE `vn2008`.`department`
+CHANGE COLUMN `lft` `lft` INT(11) NULL ,
+CHANGE COLUMN `rgt` `rgt` INT(11) NULL ;
+
+ALTER TABLE `vn2008`.`department`
+DROP INDEX `rgt_UNIQUE` ,
+DROP INDEX `lft_UNIQUE` ;
+;
+
+ALTER TABLE `vn2008`.`department`
+ADD INDEX `lft_rgt_depth_idx` (`lft` ASC, `rgt` ASC, `depth` ASC);
+;
+
+
+UPDATE vn.department SET lft = NULL, rgt = NULL;
diff --git a/db/changes/10091-iberflora/00-department_calcTree.sql b/db/changes/10091-iberflora/00-department_calcTree.sql
new file mode 100644
index 000000000..5f518d2e2
--- /dev/null
+++ b/db/changes/10091-iberflora/00-department_calcTree.sql
@@ -0,0 +1,37 @@
+USE `vn`;
+DROP procedure IF EXISTS `department_calcTree`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTree`()
+BEGIN
+/**
+ * Calculates the #path, #lft, #rgt, #sons and #depth columns of
+ * the #department table. To build the tree, it uses the #parentFk
+ * column.
+ */
+ DECLARE vIndex INT DEFAULT 0;
+ DECLARE vSons INT;
+
+ DROP TEMPORARY TABLE IF EXISTS tNestedTree;
+ CREATE TEMPORARY TABLE tNestedTree
+ SELECT id, path, lft, rgt, depth, sons
+ FROM department LIMIT 0;
+
+ SET max_sp_recursion_depth = 5;
+ CALL department_calcTreeRec(NULL, '/', 0, vIndex, vSons);
+ SET max_sp_recursion_depth = 0;
+
+ UPDATE department z
+ JOIN tNestedTree t ON t.id = z.id
+ SET z.path = t.path,
+ z.lft = t.lft,
+ z.rgt = t.rgt,
+ z.depth = t.depth,
+ z.sons = t.sons;
+
+ DROP TEMPORARY TABLE tNestedTree;
+END$$
+
+DELIMITER ;
+
diff --git a/db/changes/10091-iberflora/00-department_calcTreeRec.sql b/db/changes/10091-iberflora/00-department_calcTreeRec.sql
new file mode 100644
index 000000000..1f24270e2
--- /dev/null
+++ b/db/changes/10091-iberflora/00-department_calcTreeRec.sql
@@ -0,0 +1,75 @@
+USE `vn`;
+DROP procedure IF EXISTS `department_calcTreeRec`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTreeRec`(
+ vSelf INT,
+ vPath VARCHAR(255),
+ vDepth INT,
+ INOUT vIndex INT,
+ OUT vSons INT
+)
+BEGIN
+/**
+ * Calculates and sets the #path, #lft, #rgt, #sons and #depth
+ * columns for all children of the passed node. Once calculated
+ * the last node rgt index and the number of sons are returned.
+ * To update it's children, this procedure calls itself recursively
+ * for each one.
+ *
+ * @vSelf The node identifier
+ * @vPath The initial path
+ * @vDepth The initial depth
+ * @vIndex The initial lft index
+ * @vSons The number of direct sons
+ */
+ DECLARE vChildFk INT;
+ DECLARE vLft INT;
+ DECLARE vMySons INT;
+ DECLARE vDone BOOL;
+ DECLARE vChildren CURSOR FOR
+ SELECT id FROM department
+ WHERE (vSelf IS NULL AND parentFk IS NULL)
+ OR (vSelf IS NOT NULL AND parentFk = vSelf);
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ SET vSons = 0;
+
+ OPEN vChildren;
+ myLoop: LOOP
+ SET vDone = FALSE;
+ FETCH vChildren INTO vChildFk;
+
+ IF vDone THEN
+ LEAVE myLoop;
+ END IF;
+
+ SET vIndex = vIndex + 1;
+ SET vLft = vIndex;
+ SET vSons = vSons + 1;
+
+ CALL department_calcTreeRec(
+ vChildFk,
+ CONCAT(vPath, vChildFk, '/'),
+ vDepth + 1,
+ vIndex,
+ vMySons
+ );
+
+ SET vIndex = vIndex + 1;
+
+ INSERT INTO tNestedTree
+ SET id = vChildFk,
+ path = vPath,
+ lft = vLft,
+ rgt = vIndex,
+ depth = vDepth,
+ sons = vMySons;
+ END LOOP;
+ CLOSE vChildren;
+END$$
+
+DELIMITER ;
+
diff --git a/db/changes/10091-iberflora/00-department_doCalc.sql b/db/changes/10091-iberflora/00-department_doCalc.sql
new file mode 100644
index 000000000..ba5c16c48
--- /dev/null
+++ b/db/changes/10091-iberflora/00-department_doCalc.sql
@@ -0,0 +1,35 @@
+USE `vn`;
+DROP procedure IF EXISTS `department_doCalc`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `department_doCalc`()
+proc: BEGIN
+/**
+ * Recalculates the department tree.
+ */
+ DECLARE vIsChanged BOOL;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ DO RELEASE_LOCK('vn.department_doCalc');
+ RESIGNAL;
+ END;
+
+ IF !GET_LOCK('vn.department_doCalc', 0) THEN
+ LEAVE proc;
+ END IF;
+
+ SELECT isChanged INTO vIsChanged
+ FROM department_recalc;
+
+ IF vIsChanged THEN
+ UPDATE department_recalc SET isChanged = FALSE;
+ CALL vn.department_calcTree;
+ END IF;
+
+ DO RELEASE_LOCK('vn.department_doCalc');
+END$$
+
+DELIMITER ;
+
diff --git a/db/changes/10091-iberflora/00-department_getLeaves.sql b/db/changes/10091-iberflora/00-department_getLeaves.sql
new file mode 100644
index 000000000..436f67dbf
--- /dev/null
+++ b/db/changes/10091-iberflora/00-department_getLeaves.sql
@@ -0,0 +1,84 @@
+USE `vn`;
+DROP procedure IF EXISTS `department_getLeaves`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `department_getLeaves`(
+ vParentFk INT,
+ vSearch VARCHAR(255)
+)
+BEGIN
+ DECLARE vIsNumber BOOL;
+ DECLARE vIsSearch BOOL DEFAULT vSearch IS NOT NULL AND vSearch != '';
+
+ DROP TEMPORARY TABLE IF EXISTS tNodes;
+ CREATE TEMPORARY TABLE tNodes
+ (UNIQUE (id))
+ ENGINE = MEMORY
+ SELECT id FROM department LIMIT 0;
+
+ IF vIsSearch THEN
+ SET vIsNumber = vSearch REGEXP '^[0-9]+$';
+
+ INSERT INTO tNodes
+ SELECT id FROM department
+ WHERE (vIsNumber AND `name` = vSearch)
+ OR (!vIsNumber AND `name` LIKE CONCAT('%', vSearch, '%'))
+ LIMIT 1000;
+ END IF;
+
+ IF vParentFk IS NULL THEN
+ DROP TEMPORARY TABLE IF EXISTS tChilds;
+ CREATE TEMPORARY TABLE tChilds
+ ENGINE = MEMORY
+ SELECT id FROM tNodes;
+
+ DROP TEMPORARY TABLE IF EXISTS tParents;
+ CREATE TEMPORARY TABLE tParents
+ ENGINE = MEMORY
+ SELECT id FROM department LIMIT 0;
+
+ myLoop: LOOP
+ DELETE FROM tParents;
+ INSERT INTO tParents
+ SELECT parentFk id
+ FROM department g
+ JOIN tChilds c ON c.id = g.id
+ WHERE g.parentFk IS NOT NULL;
+
+ INSERT IGNORE INTO tNodes
+ SELECT id FROM tParents;
+
+ IF ROW_COUNT() = 0 THEN
+ LEAVE myLoop;
+ END IF;
+
+ DELETE FROM tChilds;
+ INSERT INTO tChilds
+ SELECT id FROM tParents;
+ END LOOP;
+
+ DROP TEMPORARY TABLE
+ tChilds,
+ tParents;
+ END IF;
+
+ IF !vIsSearch THEN
+ INSERT IGNORE INTO tNodes
+ SELECT id FROM department
+ WHERE parentFk <=> vParentFk;
+ END IF;
+
+ SELECT d.id,
+ d.`name`,
+ d.parentFk,
+ d.sons
+ FROM department d
+ JOIN tNodes n ON n.id = d.id
+ ORDER BY depth, `name`;
+
+ DROP TEMPORARY TABLE tNodes;
+END$$
+
+DELIMITER ;
+
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index f701c4121..f00a98759 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -471,7 +471,7 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
(14, 1, 2, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()),
(15, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()),
(16, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
- (17, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
+ (17, 1, 7, 2, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
(18, 1, 4, 4, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 108, 'Cerebro', 128, NULL, 0, 12, CURDATE()),
(19, 1, 5, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 1, 13, CURDATE()),
(20, 1, 5, 5, 3, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
diff --git a/e2e/helpers/components_selectors.js b/e2e/helpers/components_selectors.js
index 4cd8241ab..3e2715ba2 100644
--- a/e2e/helpers/components_selectors.js
+++ b/e2e/helpers/components_selectors.js
@@ -1,6 +1,6 @@
export default {
- vnTextfield: 'vn-textfield > div > div > div > input',
- vnInputNumber: 'vn-input-number > div > div > div > input',
+ vnTextfield: 'vn-textfield input',
+ vnInputNumber: 'vn-input-number input',
vnSubmit: 'vn-submit > input',
vnFloatButton: 'vn-float-button > button'
};
diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js
index 081332337..fb36e3279 100644
--- a/e2e/helpers/extensions.js
+++ b/e2e/helpers/extensions.js
@@ -18,8 +18,11 @@ let actions = {
clearInput: function(selector, done) {
this.wait(selector)
- .evaluate(inputSelector => {
- return document.querySelector(inputSelector).closest('*[model], *[field], *[value]').$ctrl.value = '';
+ .evaluate(selector => {
+ let $ctrl = document.querySelector(selector).closest('.vn-field').$ctrl;
+ $ctrl.field = null;
+ $ctrl.$.$apply();
+ $ctrl.input.dispatchEvent(new Event('change'));
}, selector)
.then(done)
.catch(done);
@@ -31,6 +34,7 @@ let actions = {
let doLogin = () => {
this.wait(`vn-login input[name=user]`)
+ .clearInput(`vn-login input[name=user]`)
.write(`vn-login input[name=user]`, userName)
.write(`vn-login input[name=password]`, 'nightmare')
.click(`vn-login input[type=submit]`)
@@ -75,7 +79,7 @@ let actions = {
},
changeLanguageToEnglish: function(done) {
- let langSelector = '.user-configuration vn-autocomplete[field="$ctrl.lang"]';
+ let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]';
this.waitToClick('#user')
.wait(langSelector)
@@ -167,8 +171,8 @@ let actions = {
focusElement: function(selector, done) {
this.wait(selector)
- .evaluate_now(elemenetSelector => {
- let element = document.querySelector(elemenetSelector);
+ .evaluate_now(selector => {
+ let element = document.querySelector(selector);
element.focus();
}, done, selector)
.then(done)
@@ -401,8 +405,7 @@ let actions = {
},
autocompleteSearch: function(autocompleteSelector, searchValue, done) {
- this.wait(`${autocompleteSelector} input`)
- .waitToClick(`${autocompleteSelector} input`)
+ this.waitToClick(`${autocompleteSelector} input`)
.write(`.vn-popover.shown .vn-drop-down input`, searchValue)
.waitToClick(`.vn-popover.shown .vn-drop-down li.active`)
.wait((autocompleteSelector, searchValue) => {
@@ -412,7 +415,7 @@ let actions = {
}, autocompleteSelector, searchValue)
.then(done)
.catch(() => {
- done(new Error(`.autocompleteSearch() for ${autocompleteSelector}, timed out`));
+ done(new Error(`.autocompleteSearch() for value ${searchValue} in ${autocompleteSelector} timed out`));
});
},
@@ -427,25 +430,22 @@ let actions = {
.catch(done);
},
- datePicker: function(datePickerSelector, changeMonth, day, done) {
- this.wait(datePickerSelector)
- .mousedown(datePickerSelector)
- .wait('div.flatpickr-calendar.open');
+ datePicker: function(selector, changeMonth, day, done) {
+ this.wait(selector)
+ .mousedown(`${selector} input`)
+ .wait('.flatpickr-calendar.open');
+
if (changeMonth > 0)
- this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-next-month > svg');
-
-
+ this.mousedown(`.flatpickr-calendar.open .flatpickr-next-month`);
if (changeMonth < 0)
- this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-prev-month > svg');
+ this.mousedown(`.flatpickr-calendar.open .flatpickr-prev-month`);
let daySelector;
if (!day)
- daySelector = 'div.flatpickr-calendar.open span.flatpickr-day:nth-child(16)';
-
+ daySelector = `.flatpickr-calendar.open .flatpickr-day:nth-child(16)`;
if (day)
- daySelector = `span.flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`;
-
+ daySelector = `.flatpickr-calendar.open .flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`;
this.wait(selector => {
return document.querySelector(selector);
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 351053a02..81ad3a0dc 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -11,14 +11,14 @@ export default {
claimsButton: '.modules-menu > li[ui-sref="claim.index"]',
returnToModuleIndexButton: 'a[ui-sref="order.index"]',
userMenuButton: 'vn-topbar #user',
- userLocalWarehouse: '.user-configuration vn-autocomplete[field="$ctrl.localWarehouseFk"]',
- userLocalBank: '.user-configuration vn-autocomplete[field="$ctrl.localBankFk"]',
- userLocalCompany: '.user-configuration vn-autocomplete[field="$ctrl.localCompanyFk"]',
- userWarehouse: '.user-configuration vn-autocomplete[field="$ctrl.warehouseFk"]',
- userCompany: '.user-configuration vn-autocomplete[field="$ctrl.companyFk"]',
- userConfigFirstAutocompleteClear: '#localWarehouse > div > div > div > vn-icon.clear',
- userConfigSecondAutocompleteClear: '#localBank > div > div > div > vn-icon.clear',
- userConfigThirdAutocompleteClear: '#localCompany > div > div > div > vn-icon.clear',
+ userLocalWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.localWarehouseFk"]',
+ userLocalBank: '.user-popover vn-autocomplete[ng-model="$ctrl.localBankFk"]',
+ userLocalCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.localCompanyFk"]',
+ userWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
+ userCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.companyFk"]',
+ userConfigFirstAutocompleteClear: '#localWarehouse .icons > vn-icon[icon=clear]',
+ userConfigSecondAutocompleteClear: '#localBank .icons > vn-icon[icon=clear]',
+ userConfigThirdAutocompleteClear: '#localCompany .icons > vn-icon[icon=clear]',
acceptButton: 'vn-confirm button[response=ACCEPT]'
},
clientsIndex: {
@@ -35,11 +35,11 @@ export default {
street: `${components.vnTextfield}[name="street"]`,
postcode: `${components.vnTextfield}[name="postcode"]`,
city: `${components.vnTextfield}[name="city"]`,
- province: `vn-autocomplete[field="$ctrl.client.provinceFk"]`,
- country: `vn-autocomplete[field="$ctrl.client.countryFk"]`,
+ province: `vn-autocomplete[ng-model="$ctrl.client.provinceFk"]`,
+ country: `vn-autocomplete[ng-model="$ctrl.client.countryFk"]`,
userName: `${components.vnTextfield}[name="userName"]`,
email: `${components.vnTextfield}[name="email"]`,
- salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
+ salesPersonAutocomplete: `vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]`,
createButton: `${components.vnSubmit}`,
cancelButton: 'vn-button[href="#!/client/index"]'
},
@@ -49,13 +49,13 @@ export default {
},
clientBasicData: {
basicDataButton: 'vn-left-menu a[ui-sref="client.card.basicData"]',
- nameInput: 'vn-textfield[field="$ctrl.client.name"] input',
- contactInput: 'vn-textfield[field="$ctrl.client.contact"] input',
- phoneInput: 'vn-textfield[field="$ctrl.client.phone"] input',
- mobileInput: 'vn-textfield[field="$ctrl.client.mobile"] input',
- emailInput: 'vn-textfield[field="$ctrl.client.email"] input',
- salesPersonAutocomplete: 'vn-autocomplete[field="$ctrl.client.salesPersonFk"]',
- channelAutocomplete: 'vn-autocomplete[field="$ctrl.client.contactChannelFk"]',
+ nameInput: 'vn-textfield[ng-model="$ctrl.client.name"] input',
+ contactInput: 'vn-textfield[ng-model="$ctrl.client.contact"] input',
+ phoneInput: 'vn-textfield[ng-model="$ctrl.client.phone"] input',
+ mobileInput: 'vn-textfield[ng-model="$ctrl.client.mobile"] input',
+ emailInput: 'vn-textfield[ng-model="$ctrl.client.email"] input',
+ salesPersonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
+ channelAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.contactChannelFk"]',
saveButton: `${components.vnSubmit}`
},
clientFiscalData: {
@@ -67,8 +67,8 @@ export default {
addressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postcode"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
- provinceAutocomplete: 'vn-autocomplete[field="$ctrl.client.provinceFk"]',
- countryAutocomplete: 'vn-autocomplete[field="$ctrl.client.countryFk"]',
+ provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.provinceFk"]',
+ countryAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
activeCheckbox: 'vn-check[label="Active"]',
frozenCheckbox: 'vn-check[label="Frozen"]',
invoiceByAddressCheckbox: 'vn-check[label="Invoice by address"]',
@@ -79,14 +79,14 @@ export default {
saveButton: `${components.vnSubmit}`
},
clientBillingData: {
- payMethodAutocomplete: 'vn-autocomplete[field="$ctrl.client.payMethodFk"]',
- IBANInput: `${components.vnTextfield}[name="iban"]`,
- dueDayInput: `${components.vnInputNumber}[name="dueDay"]`,
- receivedCoreLCRCheckbox: 'vn-check[label="Received LCR"]',
- receivedCoreVNLCheckbox: 'vn-check[label="Received core VNL"]',
- receivedB2BVNLCheckbox: 'vn-check[label="Received B2B VNL"]',
- swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"]',
- clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"] > div > div > div > vn-icon > i',
+ payMethodAutocomplete: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.payMethodFk"]',
+ IBANInput: `vn-client-billing-data ${components.vnTextfield}[name="iban"]`,
+ dueDayInput: `vn-client-billing-data ${components.vnInputNumber}[name="dueDay"]`,
+ receivedCoreLCRCheckbox: 'vn-client-billing-data vn-check[label="Received LCR"]',
+ receivedCoreVNLCheckbox: 'vn-client-billing-data vn-check[label="Received core VNL"]',
+ receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]',
+ swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"]',
+ clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"] .icons > vn-icon[icon=clear]',
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input',
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
@@ -102,8 +102,8 @@ export default {
streetAddressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
- provinceAutocomplete: 'vn-autocomplete[field="$ctrl.address.provinceFk"]',
- agencyAutocomplete: 'vn-autocomplete[field="$ctrl.address.agencyModeFk"]',
+ provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.provinceFk"]',
+ agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.agencyModeFk"]',
phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`,
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
@@ -112,10 +112,10 @@ export default {
secondEditAddress: 'vn-client-address-index div:nth-child(2) > a',
activeCheckbox: 'vn-check[label="Enabled"]',
equalizationTaxCheckbox: 'vn-client-address-edit vn-check[label="Is equalizated"]',
- firstObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(1) [field="observation.observationTypeFk"]',
- firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [model="observation.description"] input',
- secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [field="observation.observationTypeFk"]',
- secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [model="observation.description"] input',
+ firstObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(1) [ng-model="observation.observationTypeFk"]',
+ firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [ng-model="observation.description"] input',
+ secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [ng-model="observation.observationTypeFk"]',
+ secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [ng-model="observation.description"] input',
addObservationButton: 'vn-client-address-edit div[name="observations"] vn-icon-button[icon="add_circle"]',
saveButton: `${components.vnSubmit}`,
cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
@@ -143,7 +143,7 @@ export default {
addGreugeFloatButton: `${components.vnFloatButton}`,
amountInput: `${components.vnInputNumber}[name="amount"]`,
descriptionInput: `${components.vnTextfield}[name="description"]`,
- typeAutocomplete: 'vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]',
+ typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.greuge.greugeTypeFk"]',
saveButton: `${components.vnSubmit}`,
firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr'
},
@@ -162,10 +162,10 @@ export default {
},
clientBalance: {
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
- companyAutocomplete: 'vn-client-balance-index vn-autocomplete[field="$ctrl.companyFk"]',
+ companyAutocomplete: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyFk"]',
newPaymentButton: `${components.vnFloatButton}`,
- newPaymentBank: 'vn-client-balance-create vn-autocomplete[field="$ctrl.receipt.bankFk"]',
- newPaymentAmountInput: 'vn-client-balance-create vn-input-number[field="$ctrl.receipt.amountPaid"] input',
+ newPaymentBank: 'vn-client-balance-create vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]',
+ newPaymentAmountInput: 'vn-client-balance-create vn-input-number[ng-model="$ctrl.receipt.amountPaid"] input',
saveButton: 'vn-client-balance-create vn-button[label="Save"]',
firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
@@ -209,9 +209,9 @@ export default {
},
itemCreateView: {
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
- typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
- intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
- originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
+ typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
+ intrastatAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
+ originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
createButton: `${components.vnSubmit}`,
cancelButton: 'button[ui-sref="item.index"]'
},
@@ -219,8 +219,8 @@ export default {
goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]',
moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon',
moreMenuRegularizeButton: '.vn-popover.shown .vn-drop-down li[name="Regularize stock"]',
- regularizeQuantityInput: 'vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input',
- regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]',
+ regularizeQuantityInput: 'vn-item-descriptor vn-dialog tpl-body > div > vn-textfield input',
+ regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]',
regularizeSaveButton: 'vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button',
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]',
@@ -229,13 +229,13 @@ export default {
itemBasicData: {
basicDataButton: 'vn-left-menu a[ui-sref="item.card.basicData"]',
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
- typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
- intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
+ typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
+ intrastatAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
nameInput: 'vn-textfield[label="Name"] input',
- relevancyInput: 'vn-input-number[label="Relevancy"] input',
- originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
- expenceAutocomplete: 'vn-autocomplete[field="$ctrl.item.expenceFk"]',
- longNameInput: 'vn-textfield[field="$ctrl.item.longName"] input',
+ relevancyInput: 'vn-input-number[ng-model="$ctrl.item.relevancy"] input',
+ originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
+ expenceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.expenceFk"]',
+ longNameInput: 'vn-textfield[ng-model="$ctrl.item.longName"] input',
isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
submitBasicDataButton: `${components.vnSubmit}`
@@ -243,26 +243,27 @@ export default {
itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
tagsButton: 'vn-left-menu a[ui-sref="item.card.tags"]',
- fourthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"]',
+ fourthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[ng-model="itemTag.tagFk"]',
fourthValueInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input',
fourthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input',
fourthRemoveTagButton: 'vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]',
- fifthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"]',
+ fifthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[ng-model="itemTag.tagFk"]',
fifthValueInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input',
fifthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input',
- sixthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"]',
+ sixthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[ng-model="itemTag.tagFk"]',
sixthValueInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input',
sixthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input',
- seventhTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[field="itemTag.tagFk"]',
+ seventhTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[ng-model="itemTag.tagFk"]',
seventhValueInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Value"] input',
seventhRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input',
addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
},
itemTax: {
- firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[field="tax.taxClassFk"]',
- secondClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[field="tax.taxClassFk"]',
- thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[field="tax.taxClassFk"]',
+ undoChangesButton: 'vn-item-tax vn-button-bar > vn-button[label="Undo changes"]',
+ firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[ng-model="tax.taxClassFk"]',
+ secondClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[ng-model="tax.taxClassFk"]',
+ thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="tax.taxClassFk"]',
submitTaxButton: `vn-item-tax ${components.vnSubmit}`
},
itemBarcodes: {
@@ -273,19 +274,19 @@ export default {
},
itemNiches: {
addNicheButton: 'vn-item-niche vn-icon[icon="add_circle"]',
- firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[field="niche.warehouseFk"]',
+ firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[ng-model="niche.warehouseFk"]',
firstCodeInput: 'vn-item-niche vn-horizontal:nth-child(1) > vn-textfield[label="Code"] input',
- secondWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[field="niche.warehouseFk"]',
+ secondWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[ng-model="niche.warehouseFk"]',
secondCodeInput: 'vn-item-niche vn-horizontal:nth-child(2) > vn-textfield[label="Code"] input',
secondNicheRemoveButton: 'vn-item-niche vn-horizontal:nth-child(2) > vn-none > vn-icon-button[icon="delete"]',
- thirdWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[field="niche.warehouseFk"]',
+ thirdWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="niche.warehouseFk"]',
thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input',
submitNichesButton: `vn-item-niche ${components.vnSubmit}`
},
itemBotanical: {
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
- genusAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]',
- speciesAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]',
+ genusAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.genusFk"]',
+ speciesAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.specieFk"]',
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
},
itemSummary: {
@@ -300,7 +301,7 @@ export default {
secondTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > span',
firstBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(1) > vn-td.balance',
fourthBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(4) > vn-td.balance',
- warehouseAutocomplete: 'vn-item-diary vn-autocomplete[field="$ctrl.warehouseFk"]',
+ warehouseAutocomplete: 'vn-item-diary vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
},
itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
@@ -323,31 +324,31 @@ export default {
setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button'
},
ticketsIndex: {
- openAdvancedSearchButton: 'vn-ticket-index vn-searchbar t-right-icons > vn-icon[icon="keyboard_arrow_down"]',
- advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[model="filter.refFk"] input',
+ openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
+ advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input',
newTicketButton: 'vn-ticket-index > a',
searchResult: 'vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
searchWeeklyTicketInput: `vn-ticket-weekly-index ${components.vnTextfield}`,
- searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar i[class="material-icons clear"]',
+ searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]',
advancedSearchButton: 'vn-ticket-search-panel vn-submit[label="Search"] input',
searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]',
searchWeeklyButton: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon="search"]',
moreMenu: 'vn-ticket-index vn-icon-menu[vn-id="more-button"] > div > vn-icon',
moreMenuWeeklyTickets: '.vn-popover.shown .vn-drop-down li:nth-child(2)',
- sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[field="weekly.weekDay"] input',
+ sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[ng-model="weekly.weekDay"] input',
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
acceptDeleteTurn: 'vn-ticket-weekly-index > vn-confirm[vn-id="deleteWeekly"] button[response="ACCEPT"]'
},
createTicketView: {
- clientAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.clientFk"]',
- addressAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.addressFk"]',
- deliveryDateInput: 'vn-ticket-create > div > div > vn-card > div > vn-ticket-create-card > vn-date-picker > div > input',
- warehouseAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.warehouseFk"]',
- agencyAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]',
+ clientAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.clientFk"]',
+ addressAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.addressFk"]',
+ deliveryDateInput: 'vn-ticket-create vn-date-picker[ng-model="$ctrl.landed"]',
+ warehouseAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
+ agencyAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.ticket.agencyModeFk"]',
createButton: `${components.vnSubmit}`
},
ticketDescriptor: {
@@ -376,7 +377,7 @@ export default {
ticketNotes: {
firstNoteRemoveButton: 'vn-icon[icon="delete"]',
addNoteButton: 'vn-icon[icon="add_circle"]',
- firstNoteTypeAutocomplete: 'vn-autocomplete[field="observation.observationTypeFk"]',
+ firstNoteTypeAutocomplete: 'vn-autocomplete[ng-model="observation.observationTypeFk"]',
firstDescriptionInput: 'vn-textfield[label="Description"] input',
submitNotesButton: `${components.vnSubmit}`
},
@@ -389,10 +390,10 @@ export default {
ticketPackages: {
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
- firstQuantityInput: 'vn-input-number[label="Quantity"] input',
+ firstQuantityInput: 'vn-input-number[ng-model="package.quantity"] input',
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
- clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] > div > div > div > vn-icon > i',
+ clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] .icons > vn-icon[icon=clear]',
savePackagesButton: `${components.vnSubmit}`
},
ticketSales: {
@@ -408,7 +409,7 @@ export default {
moreMenuReserve: '.vn-popover.shown .vn-drop-down li[name="Mark as reserved"]',
moreMenuUnmarkReseved: '.vn-popover.shown .vn-drop-down li[name="Unmark as reserved"]',
moreMenuUpdateDiscount: '.vn-popover.shown .vn-drop-down li[name="Update discount"]',
- moreMenuUpdateDiscountInput: 'vn-ticket-sale vn-dialog form vn-ticket-sale-edit-discount vn-input-number[model="$ctrl.newDiscount"] input',
+ moreMenuUpdateDiscountInput: 'vn-ticket-sale vn-dialog form vn-ticket-sale-edit-discount vn-input-number[ng-model="$ctrl.newDiscount"] input',
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
firstSaleClaimIcon: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) vn-icon[icon="icon-claims"]',
@@ -416,11 +417,11 @@ export default {
firstSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(1)',
firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img',
firstSaleZoomedImage: 'body > div > div > img',
- firstSaleQuantity: 'vn-input-number[model="sale.quantity"]:nth-child(1) input',
+ firstSaleQuantity: 'vn-input-number[ng-model="sale.quantity"]:nth-child(1) input',
firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(5)',
- firstSaleQuantityClearInput: 'vn-textfield[model="sale.quantity"] div.suffix > i',
- firstSaleIdInput: 'body > vn-app > div > ui-view > vn-ticket-card > vn-main-block > div > vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete > div > div > input',
- firstSaleIdAutocomplete: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete',
+ firstSaleQuantityClearInput: 'vn-textfield[ng-model="sale.quantity"] div.suffix > i',
+ firstSaleIdInput: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete input',
+ firstSaleIdAutocomplete: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete',
idAutocompleteFirstResult: '.vn-popover.shown .vn-drop-down li',
firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
firstSalePriceInput: '.vn-popover.shown vn-input-number input',
@@ -430,7 +431,7 @@ export default {
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
firstSaleColour: 'vn-tr:nth-child(1) vn-fetched-tags section',
firstSaleLength: 'vn-ticket-sale vn-tr:nth-child(1) vn-td-editable:nth-child(6) section:nth-child(3)',
- firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"]',
+ firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[ng-model="sale.checked"]',
secondSaleColour: 'vn-tr:nth-child(2) vn-fetched-tags section',
secondSalePrice: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span',
secondSaleDiscount: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)',
@@ -438,18 +439,18 @@ export default {
secondSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(2)',
secondSaleId: 'vn-ticket-sale:nth-child(2) vn-td-editable:nth-child(4) text > span',
secondSaleIdCell: 'vn-ticket-sale vn-tr:nth-child(2) > vn-td-editable:nth-child(4)',
- secondSaleIdInput: 'body > vn-app > div > ui-view > vn-ticket-card > vn-main-block > div > vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete > div > div > input',
- secondSaleIdAutocomplete: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete',
+ secondSaleIdInput: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete input',
+ secondSaleIdAutocomplete: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete',
secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number input',
- secondSaleConceptCell: 'vn-ticket-sale vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)',
+ secondSaleConceptCell: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)',
secondSaleConceptInput: 'vn-ticket-sale vn-table vn-tr:nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield input',
totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong',
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',
- secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[field="sale.checked"]',
- thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[field="sale.checked"]',
+ secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[ng-model="sale.checked"]',
+ thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[ng-model="sale.checked"]',
deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]',
transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]',
- moveToTicketInput: '.vn-popover.shown vn-textfield[model="$ctrl.transfer.ticketId"] input',
+ moveToTicketInput: '.vn-popover.shown vn-textfield[ng-model="$ctrl.transfer.ticketId"] input',
moveToTicketInputClearButton: '.vn-popover.shown i[title="Clear"]',
moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]',
moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]',
@@ -460,36 +461,34 @@ export default {
ticketTracking: {
trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]',
createStateButton: `${components.vnFloatButton}`,
- stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.stateFk"]',
+ stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[ng-model="$ctrl.stateFk"]',
saveButton: `${components.vnSubmit}`,
cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]'
},
ticketBasicData: {
basicDataButton: 'vn-left-menu a[ui-sref="ticket.card.basicData.stepOne"]',
- clientAutocomplete: 'vn-autocomplete[field="$ctrl.clientFk"]',
- addressAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.addressFk"]',
- agencyAutocomplete: 'vn-autocomplete[field="$ctrl.agencyModeId"]',
- zoneAutocomplete: 'vn-autocomplete[field="$ctrl.zoneId"]',
+ clientAutocomplete: 'vn-autocomplete[ng-model="$ctrl.clientFk"]',
+ addressAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.addressFk"]',
+ agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.agencyModeId"]',
+ zoneAutocomplete: 'vn-autocomplete[ng-model="$ctrl.zoneId"]',
nextStepButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-button',
finalizeButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-submit',
stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)',
- chargesReasonAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.option"]',
+ chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]',
},
ticketComponents: {
- base: 'vn-ticket-components tfoot > tr:nth-child(1) > td',
- margin: 'vn-ticket-components tfoot > tr:nth-child(2) > td',
- total: 'vn-ticket-components tfoot > tr:nth-child(3) > td'
+ base: 'vn-ticket-components [name="base-sum"]'
},
ticketRequests: {
addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button',
- request: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr',
- descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield > div > div > div.infix > input',
- atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[field="$ctrl.ticketRequest.atenderFk"]',
- quantityInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(1) > div > div > div.infix > input',
- priceInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(2) > div > div > div.infix > input',
+ request: 'vn-ticket-request-index vn-table vn-tr',
+ descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield input',
+ atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[ng-model="$ctrl.ticketRequest.atenderFk"]',
+ quantityInput: 'vn-ticket-request-create vn-input-number input[name=quantity]',
+ priceInput: 'vn-ticket-request-create vn-input-number input[name=price]',
firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
saveButton: 'vn-ticket-request-create > form > div > vn-button-bar > vn-submit[label="Create"] input',
- firstDescription: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2)',
+ firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)',
},
ticketLog: {
@@ -501,20 +500,20 @@ export default {
ticketService: {
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button',
firstAddDescriptionButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"] > button',
- firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[field="service.description"]',
+ firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[ng-model="service.description"]',
firstQuantityInput: 'vn-ticket-service vn-input-number[label="Quantity"] input',
firstPriceInput: 'vn-ticket-service vn-input-number[label="Price"] input',
firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
- fistDeleteServiceButton: 'vn-ticket-card > vn-main-block > div.content-block.ng-scope > vn-ticket-service > form > vn-card > div > vn-one:nth-child(1) > vn-horizontal:nth-child(1) > vn-auto > vn-icon-button[icon="delete"]',
- newDescriptionInput: 'vn-ticket-service > vn-dialog vn-textfield[model="$ctrl.newServiceType.name"] input',
+ fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
+ newDescriptionInput: 'vn-ticket-service > vn-dialog vn-textfield[ng-model="$ctrl.newServiceType.name"] input',
serviceLine: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(2) > vn-horizontal',
saveServiceButton: `${components.vnSubmit}`,
saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button'
},
createStateView: {
- stateAutocomplete: 'vn-autocomplete[field="$ctrl.stateFk"]',
- workerAutocomplete: 'vn-autocomplete[field="$ctrl.workerFk"]',
- clearStateInputButton: 'vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i',
+ stateAutocomplete: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
+ workerAutocomplete: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',
+ clearStateInputButton: 'vn-autocomplete[ng-model="$ctrl.stateFk"] .icons > vn-icon[icon=clear]',
saveStateButton: `${components.vnSubmit}`
},
claimsIndex: {
@@ -530,7 +529,7 @@ export default {
claimSummary: {
header: 'vn-claim-summary > vn-card > div > h5',
state: 'vn-claim-summary vn-label-value[label="State"] > section > span',
- observation: 'vn-claim-summary vn-textarea[model="$ctrl.summary.claim.observation"] > div > textarea',
+ observation: 'vn-claim-summary vn-textarea[ng-model="$ctrl.summary.claim.observation"] textarea',
firstSaleItemId: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(4) > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(1) > span',
firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img',
itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor',
@@ -541,45 +540,45 @@ export default {
firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor'
},
claimBasicData: {
- claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[field="$ctrl.claim.claimStateFk"]',
+ claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
responsabilityInputRange: 'vn-input-range',
- observationInput: 'vn-textarea[field="$ctrl.claim.observation"] textarea',
+ observationInput: 'vn-textarea[ng-model="$ctrl.claim.observation"] textarea',
saveButton: `${components.vnSubmit}`
},
claimDetail: {
secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span',
- discountInput: '.vn-popover.shown vn-input-number[model="$ctrl.newDiscount"] > div > div > div.infix > input',
+ discountInput: '.vn-popover.shown vn-input-number[ng-model="$ctrl.newDiscount"] input',
discoutPopoverMana: '.vn-popover.shown .content > div > vn-horizontal > h5',
addItemButton: 'vn-claim-detail a vn-float-button',
firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr',
claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr',
- firstItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(1) vn-input-number[model="saleClaimed.quantity"] input',
+ firstItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(1) vn-input-number[ng-model="saleClaimed.quantity"] input',
totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span',
secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i'
},
claimDevelopment: {
addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > vn-one > vn-icon-button > button > vn-icon',
firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon',
- firstClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimReasonFk"]',
- firstClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimResultFk"]',
- firstClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimResponsibleFk"]',
- firstClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.workerFk"]',
- firstClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimRedeliveryFk"]',
- secondClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimReasonFk"]',
- secondClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimResultFk"]',
- secondClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimResponsibleFk"]',
- secondClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.workerFk"]',
- secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimRedeliveryFk"]',
+ firstClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
+ firstClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
+ firstClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
+ firstClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
+ firstClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
+ secondClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
+ secondClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
+ secondClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
+ secondClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
+ secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
saveDevelopmentButton: `${components.vnSubmit}`
},
claimAction: {
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
importTicketButton: 'vn-claim-action vn-button[label="Import ticket"]',
secondImportableTicket: '.vn-popover.shown .content > div > vn-table > div > vn-tbody > vn-tr:nth-child(2)',
- firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
- secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
+ firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
+ secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
- isPaidWithManaCheckbox: 'vn-check[field="$ctrl.claim.isChargedToMana"]'
+ isPaidWithManaCheckbox: 'vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
},
ordersIndex: {
searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
@@ -597,7 +596,7 @@ export default {
clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
- landedDatePicker: 'vn-date-picker[label="Landed"] input',
+ landedDatePicker: 'vn-date-picker[label="Landed"]',
createButton: `${components.vnSubmit}`,
cancelButton: 'vn-button[href="#!/client/index"]'
},
@@ -605,14 +604,14 @@ export default {
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
typeAutocomplete: 'vn-autocomplete[data="$ctrl.itemTypes"]',
- itemIdInput: 'vn-order-catalog > vn-side-menu vn-catalog-filter vn-textfield[model="$ctrl.itemFk"] input',
- itemTagValueInput: 'vn-order-catalog > vn-side-menu vn-catalog-filter vn-textfield[model="$ctrl.value"] input',
- openTagSearch: 'vn-order-catalog > vn-side-menu > div > vn-catalog-filter > div > vn-vertical > vn-textfield[model="$ctrl.value"] > div > div > div.rightIcons > t-right-icons > i',
- tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[field="filter.tagFk"]',
- tagValueInput: 'vn-order-catalog-search-panel vn-textfield[model="filter.value"] input',
+ itemIdInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.itemFk"] input',
+ itemTagValueInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.value"] input',
+ openTagSearch: 'vn-catalog-filter > div > vn-vertical > vn-textfield[ng-model="$ctrl.value"] .append i',
+ tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
+ tagValueInput: 'vn-order-catalog-search-panel vn-textfield[ng-model="filter.value"] input',
searchTagButton: 'vn-order-catalog-search-panel > div > form > vn-horizontal:nth-child(3) > vn-submit > input',
- thirdFilterRemoveButton: 'vn-order-catalog > vn-side-menu vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(3) button',
- fourthFilterRemoveButton: 'vn-order-catalog > vn-side-menu vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(4) button',
+ thirdFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(3) button',
+ fourthFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(4) button',
},
orderBasicData: {
clientAutocomplete: 'vn-autocomplete[label="Client"]',
@@ -632,11 +631,11 @@ export default {
addNewRouteButton: 'vn-route-index > a[ui-sref="route.create"]'
},
createRouteView: {
- workerAutocomplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.workerFk"]',
- createdDatePicker: 'vn-route-create vn-date-picker[model="$ctrl.route.created"] > div > input',
- vehicleAutoComplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.vehicleFk"]',
- agencyAutoComplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.agencyModeFk"]',
- descriptionInput: 'vn-route-create vn-textfield[field="$ctrl.route.description"] input',
+ workerAutocomplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
+ createdDatePicker: 'vn-route-create vn-date-picker[ng-model="$ctrl.route.created"]',
+ vehicleAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
+ agencyAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
+ descriptionInput: 'vn-route-create vn-textfield[ng-model="$ctrl.route.description"] input',
submitButton: 'vn-route-create vn-submit > input[type="submit"]'
},
routeDescriptor: {
@@ -646,29 +645,29 @@ export default {
routeId: 'vn-route-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(1) > section > span'
},
routeBasicData: {
- workerAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.workerFk"]',
- vehicleAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.vehicleFk"]',
- agencyAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.agencyModeFk"]',
- kmStartInput: 'vn-route-basic-data vn-input-number[field="$ctrl.route.kmStart"] input',
- kmEndInput: 'vn-route-basic-data vn-input-number[model="$ctrl.route.kmEnd"] input',
- createdDateInput: 'vn-route-basic-data vn-date-picker[model="$ctrl.route.created"] > div > input',
- startedHourInput: 'vn-route-basic-data vn-input-time[model="$ctrl.route.started"] input',
- finishedHourInput: 'vn-route-basic-data vn-input-time[model="$ctrl.route.finished"] input',
+ workerAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
+ vehicleAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
+ agencyAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
+ kmStartInput: 'vn-route-basic-data vn-input-number[ng-model="$ctrl.route.kmStart"] input',
+ kmEndInput: 'vn-route-basic-data vn-input-number[ng-model="$ctrl.route.kmEnd"] input',
+ createdDateInput: 'vn-route-basic-data vn-date-picker[ng-model="$ctrl.route.created"]',
+ startedHourInput: 'vn-route-basic-data vn-input-time[ng-model="$ctrl.route.started"] input',
+ finishedHourInput: 'vn-route-basic-data vn-input-time[ng-model="$ctrl.route.finished"] input',
saveButton: 'vn-route-basic-data vn-submit[label="Save"] input'
},
routeTickets: {
- firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[model="ticket.priority"] input',
- secondTicketPriority: 'vn-route-tickets vn-tr:nth-child(2) vn-textfield[model="ticket.priority"] input',
- thirdTicketPriority: 'vn-route-tickets vn-tr:nth-child(3) vn-textfield[model="ticket.priority"] input',
- fourthTicketPriority: 'vn-route-tickets vn-tr:nth-child(4) vn-textfield[model="ticket.priority"] input',
- eleventhTicketPriority: 'vn-route-tickets vn-tr:nth-child(11) vn-textfield[model="ticket.priority"] input',
+ firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[ng-model="ticket.priority"] input',
+ secondTicketPriority: 'vn-route-tickets vn-tr:nth-child(2) vn-textfield[ng-model="ticket.priority"] input',
+ thirdTicketPriority: 'vn-route-tickets vn-tr:nth-child(3) vn-textfield[ng-model="ticket.priority"] input',
+ fourthTicketPriority: 'vn-route-tickets vn-tr:nth-child(4) vn-textfield[ng-model="ticket.priority"] input',
+ eleventhTicketPriority: 'vn-route-tickets vn-tr:nth-child(11) vn-textfield[ng-model="ticket.priority"] input',
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
confirmButton: 'vn-route-tickets > vn-confirm button[response="ACCEPT"]'
},
workerPbx: {
- extensionInput: 'vn-worker-pbx vn-textfield[model="$ctrl.worker.sip.extension"] input',
+ extensionInput: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"] input',
saveButton: 'vn-worker-pbx vn-submit[label="Save"] input'
},
workerTimeControl: {
diff --git a/e2e/paths/02-client-module/12_lock_of_verified_data.spec.js b/e2e/paths/02-client-module/12_lock_of_verified_data.spec.js
index a4c97eb62..52e2db237 100644
--- a/e2e/paths/02-client-module/12_lock_of_verified_data.spec.js
+++ b/e2e/paths/02-client-module/12_lock_of_verified_data.spec.js
@@ -176,7 +176,7 @@ describe('Client lock verified data path', () => {
.wait(selectors.clientFiscalData.socialNameInput)
.evaluate(selector => {
return document.querySelector(selector).disabled;
- }, 'vn-textfield[model="$ctrl.client.socialName"] > div');
+ }, 'vn-textfield[ng-model="$ctrl.client.socialName"] > div');
expect(result).toBeFalsy();
});
diff --git a/e2e/paths/04-item-module/03_tax.spec.js b/e2e/paths/04-item-module/03_tax.spec.js
index 38861f1bf..903d05f10 100644
--- a/e2e/paths/04-item-module/03_tax.spec.js
+++ b/e2e/paths/04-item-module/03_tax.spec.js
@@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
-// #1702 Autocomplete no siempre refresca al cancelar formulario
-xdescribe('Item edit tax path', () => {
+describe('Item edit tax path', () => {
const nightmare = createNightmare();
beforeAll(() => {
@@ -14,9 +13,9 @@ xdescribe('Item edit tax path', () => {
it(`should add the item tax to all countries`, async() => {
const result = await nightmare
- .autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'Reduced VAT')
+ .autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'General VAT')
.autocompleteSearch(selectors.itemTax.secondClassAutocomplete, 'General VAT')
- .autocompleteSearch(selectors.itemTax.thirdClassAutocomplete, 'Reduced VAT')
+ .autocompleteSearch(selectors.itemTax.thirdClassAutocomplete, 'General VAT')
.waitToClick(selectors.itemTax.submitTaxButton)
.waitForLastSnackbar();
@@ -28,7 +27,7 @@ xdescribe('Item edit tax path', () => {
.reloadSection('item.card.tax')
.waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
- expect(firstVatType).toEqual('Reduced VAT');
+ expect(firstVatType).toEqual('General VAT');
});
it(`should confirm the second item tax class was edited`, async() => {
@@ -42,6 +41,22 @@ xdescribe('Item edit tax path', () => {
const thirdVatType = await nightmare
.waitToGetProperty(`${selectors.itemTax.thirdClassAutocomplete} input`, 'value');
- expect(thirdVatType).toEqual('Reduced VAT');
+ expect(thirdVatType).toEqual('General VAT');
+ });
+
+ it(`should edit the first class without saving the form`, async() => {
+ const firstVatType = await nightmare
+ .autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'Reduced VAT')
+ .waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
+
+ expect(firstVatType).toEqual('Reduced VAT');
+ });
+
+ it(`should now click the undo changes button and see the changes works`, async() => {
+ const firstVatType = await nightmare
+ .waitToClick(selectors.itemTax.undoChangesButton)
+ .waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
+
+ expect(firstVatType).toEqual('General VAT');
});
});
diff --git a/e2e/paths/05-ticket-module/09_weekly.spec.js b/e2e/paths/05-ticket-module/09_weekly.spec.js
index bba4a20e7..a7a301d79 100644
--- a/e2e/paths/05-ticket-module/09_weekly.spec.js
+++ b/e2e/paths/05-ticket-module/09_weekly.spec.js
@@ -160,7 +160,7 @@ describe('Ticket descriptor path', () => {
it('should confirm the sixth weekly ticket was deleted', async() => {
const result = await nightmare
- .waitToClick('vn-ticket-weekly-index vn-searchbar i[class="material-icons clear"]')
+ .waitToClick('vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]')
.waitToClick(selectors.ticketsIndex.searchWeeklyButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchWeeklyResult, 5)
.countElement(selectors.ticketsIndex.searchWeeklyResult);
diff --git a/e2e/paths/05-ticket-module/13_services.spec.js b/e2e/paths/05-ticket-module/13_services.spec.js
index 31d262778..f9deb6c93 100644
--- a/e2e/paths/05-ticket-module/13_services.spec.js
+++ b/e2e/paths/05-ticket-module/13_services.spec.js
@@ -78,7 +78,7 @@ describe('Ticket services path', () => {
.waitToClick(selectors.ticketService.saveDescriptionButton)
.waitForLastSnackbar();
- expect(result).toEqual(`Name can't be empty`);
+ expect(result).toEqual(`can't be blank`);
});
it('should create a new description then add price then create the service', async() => {
diff --git a/e2e/paths/08-route-module/03_tickets.spec.js b/e2e/paths/08-route-module/03_tickets.spec.js
index c1a0350b3..24a3ebbcf 100644
--- a/e2e/paths/08-route-module/03_tickets.spec.js
+++ b/e2e/paths/08-route-module/03_tickets.spec.js
@@ -49,7 +49,7 @@ xdescribe('Route basic Data path', () => {
it('should count how many tickets are in route', async() => {
const result = await nightmare
- .countElement('vn-route-tickets vn-textfield[model="ticket.priority"]');
+ .countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
expect(result).toEqual(11);
});
@@ -74,7 +74,7 @@ xdescribe('Route basic Data path', () => {
it('should now count how many tickets are in route to find one less', async() => {
const result = await nightmare
- .countElement('vn-route-tickets vn-textfield[model="ticket.priority"]');
+ .countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
expect(result).toEqual(9);
});
diff --git a/front/core/components/autocomplete/autocomplete.html b/front/core/components/autocomplete/autocomplete.html
deleted file mode 100755
index 4caca44bc..000000000
--- a/front/core/components/autocomplete/autocomplete.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
- {{::$ctrl.label}}
- *
-
-
-
-
-
\ No newline at end of file
diff --git a/front/core/components/autocomplete/index.html b/front/core/components/autocomplete/index.html
new file mode 100755
index 000000000..2e5cc39f6
--- /dev/null
+++ b/front/core/components/autocomplete/index.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{::$ctrl.label}}
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/core/components/autocomplete/autocomplete.js b/front/core/components/autocomplete/index.js
similarity index 77%
rename from front/core/components/autocomplete/autocomplete.js
rename to front/core/components/autocomplete/index.js
index 469bb9e93..69114d028 100755
--- a/front/core/components/autocomplete/autocomplete.js
+++ b/front/core/components/autocomplete/index.js
@@ -1,5 +1,5 @@
import ngModule from '../../module';
-import Input from '../../lib/input';
+import Field from '../field';
import assignProps from '../../lib/assign-props';
import {mergeWhere} from 'vn-loopback/util/filter';
import './style.scss';
@@ -16,23 +16,18 @@ import './style.scss';
*
* @event change Thrown when value is changed
*/
-export default class Autocomplete extends Input {
- constructor($element, $scope, $http, $transclude, $translate, $interpolate) {
- super($element, $scope);
- this.$http = $http;
- this.$interpolate = $interpolate;
- this.$transclude = $transclude;
- this.$translate = $translate;
- this._field = undefined;
+export default class Autocomplete extends Field {
+ constructor($element, $scope, $compile, $http, $transclude, $translate, $interpolate) {
+ super($element, $scope, $compile);
+ Object.assign(this, {
+ $http,
+ $interpolate,
+ $transclude,
+ $translate
+ });
+
this._selection = null;
- this.readonly = true;
- this.form = null;
- this.input = this.element.querySelector('.mdl-textfield__input');
-
- componentHandler.upgradeElement(
- this.element.querySelector('.mdl-textfield'));
-
- this.registerEvents();
+ this.input = this.element.querySelector('input');
}
$postLink() {
@@ -44,12 +39,16 @@ export default class Autocomplete extends Input {
}
/**
- * Registers all event emitters
+ * @type {any} The autocomplete value.
*/
- registerEvents() {
- this.input.addEventListener('focus', event => {
- this.emit('focus', {event});
- });
+ get field() {
+ return super.field;
+ }
+
+ set field(value) {
+ super.field = value;
+ this.refreshSelection();
+ this.emit('change', {value});
}
get model() {
@@ -83,20 +82,6 @@ export default class Autocomplete extends Input {
Object.assign(this.$.dropDown, props);
}
- /**
- * @type {any} The autocomplete value.
- */
- get field() {
- return this._field;
- }
-
- set field(value) {
- this._field = value;
-
- this.refreshSelection();
- this.emit('change', {value});
- }
-
/**
* @type {Object} The selected data object, you can use this property
* to prevent requests to display the initial value.
@@ -136,8 +121,8 @@ export default class Autocomplete extends Input {
return;
const selection = this.fetchSelection();
- if (!this.selection)
- this.selection = selection;
+
+ this.selection = selection;
}
fetchSelection() {
@@ -216,39 +201,21 @@ export default class Autocomplete extends Input {
if (this.translateFields.indexOf(this.showField) > -1)
this.input.value = this.$translate.instant(display);
}
-
- this.mdlUpdate();
- }
-
- mdlUpdate() {
- let field = this.element.querySelector('.mdl-textfield');
- let mdlField = field.MaterialTextfield;
- if (mdlField) mdlField.updateClasses_();
- }
-
- setValue(value) {
- this.field = value;
- if (this.form) this.form.$setDirty();
}
onDropDownSelect(item) {
const value = item[this.valueField];
this.selection = item;
- this.setValue(value);
+ this.field = value;
}
- onClearClick(event) {
- event.preventDefault();
- this.setValue(null);
- }
+ onContainerKeyDown(event) {
+ if (event.defaultPrevented) return;
- onKeyDown(event) {
- // if (event.defaultPrevented) return;
-
- switch (event.keyCode) {
- case 38: // Up
- case 40: // Down
- case 13: // Enter
+ switch (event.code) {
+ case 'ArrowUp':
+ case 'ArrowDown':
+ case 'Enter':
this.showDropDown();
break;
default:
@@ -261,7 +228,8 @@ export default class Autocomplete extends Input {
event.preventDefault();
}
- onMouseDown(event) {
+ onContainerMouseDown(event) {
+ if (event.defaultPrevented) return;
event.preventDefault();
this.showDropDown();
}
@@ -293,7 +261,7 @@ export default class Autocomplete extends Input {
showDropDown(search) {
this.assignDropdownProps();
- this.$.dropDown.show(this.input, search);
+ this.$.dropDown.show(this.container, search);
}
get fetchFunction() {
@@ -307,16 +275,12 @@ export default class Autocomplete extends Input {
this.refreshSelection();
}
}
-Autocomplete.$inject = ['$element', '$scope', '$http', '$transclude', '$translate', '$interpolate'];
+Autocomplete.$inject = ['$element', '$scope', '$compile', '$http', '$transclude', '$translate', '$interpolate'];
-ngModule.component('vnAutocomplete', {
- template: require('./autocomplete.html'),
+ngModule.vnComponent('vnAutocomplete', {
+ template: require('./index.html'),
controller: Autocomplete,
bindings: {
- label: '@',
- field: '=?',
- disabled: '',
- required: '@?',
showField: '@?',
valueField: '@?',
initialData: '',
@@ -336,8 +300,5 @@ ngModule.component('vnAutocomplete', {
},
transclude: {
tplItem: '?tplItem'
- },
- require: {
- form: '?^form'
}
});
diff --git a/front/core/components/autocomplete/autocomplete.spec.js b/front/core/components/autocomplete/index.spec.js
similarity index 100%
rename from front/core/components/autocomplete/autocomplete.spec.js
rename to front/core/components/autocomplete/index.spec.js
diff --git a/front/core/components/autocomplete/style.scss b/front/core/components/autocomplete/style.scss
index c283ee09d..af45902cc 100755
--- a/front/core/components/autocomplete/style.scss
+++ b/front/core/components/autocomplete/style.scss
@@ -1,66 +1,23 @@
@import "effects";
-vn-autocomplete {
+vn-autocomplete.vn-field {
overflow: hidden;
- & > div > .mdl-textfield {
- position: relative;
- width: 100%;
+ & > .container {
+ cursor: pointer;
- & > input {
- cursor: pointer;
- height: 30px;
- text-overflow: ellipsis;
- white-space: nowrap;
+ & > .infix > .control {
overflow: hidden;
- }
- & > .icons {
- display: none;
- position: absolute;
- right: 0;
- top: 1.3em;
- height: 1em;
- color: $color-font-secondary;
- border-radius: .2em;
-
- & > vn-icon {
+
+ & > input {
cursor: pointer;
- font-size: 18px;
-
- &:hover {
- color: $color-font;
- }
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ text-align: left;
+ padding-left: 0;
+ padding-right: 0;
}
}
- &:hover > .icons,
- & > input:focus + .icons {
- display: block;
- }
- }
-
- label span:nth-child(2) {
- color: $color-alert
}
}
-
-ul.vn-autocomplete {
- list-style-type: none;
- padding: 1em;
- margin: 0;
- padding: 0;
- overflow: auto;
- max-height: 300px;
-
- li {
- @extend %clickable;
- display: block;
- padding: .8em;
- margin: 0;
-
- &.load-more {
- color: $color-main;
- font-family: vn-font-bold;
- padding: .4em .8em;
- }
- }
-}
\ No newline at end of file
diff --git a/front/core/components/check/index.js b/front/core/components/check/index.js
index 35ff07e8a..b9bd1bb2f 100644
--- a/front/core/components/check/index.js
+++ b/front/core/components/check/index.js
@@ -12,13 +12,13 @@ import './style.scss';
*/
export default class Check extends Toggle {
set field(value) {
- this._field = value;
+ super.field = value;
this.element.classList.toggle('checked', Boolean(value));
this.indeterminate = Boolean(value == null && this.tripleState);
}
get field() {
- return this._field;
+ return super.field;
}
set checked(value) {
@@ -64,14 +64,10 @@ export default class Check extends Toggle {
}
}
-ngModule.component('vnCheck', {
+ngModule.vnComponent('vnCheck', {
template: require('./index.html'),
controller: Check,
bindings: {
- label: '@?',
- field: '=?',
- disabled: '',
- checked: '',
tripleState: '',
indeterminate: '',
info: '@?'
diff --git a/front/core/components/check/index.spec.js b/front/core/components/check/index.spec.js
index e170804d2..f7c2f6aac 100644
--- a/front/core/components/check/index.spec.js
+++ b/front/core/components/check/index.spec.js
@@ -8,7 +8,7 @@ describe('Component vnCheck', () => {
}));
beforeEach(inject(($compile, $rootScope) => {
- $element = $compile(` `)($rootScope);
$ctrl = $element.controller('vnCheck');
element = $element[0];
}));
diff --git a/front/core/components/date-picker/date-picker.html b/front/core/components/date-picker/date-picker.html
deleted file mode 100644
index 67fcd880b..000000000
--- a/front/core/components/date-picker/date-picker.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
- clear
-
-
-
{{$ctrl.label}}
-
\ No newline at end of file
diff --git a/front/core/components/date-picker/date-picker.js b/front/core/components/date-picker/date-picker.js
deleted file mode 100644
index 1156ab09a..000000000
--- a/front/core/components/date-picker/date-picker.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import ngModule from '../../module';
-import Component from '../../lib/component';
-import {Flatpickr} from '../../vendor';
-import './style.scss';
-
-class DatePicker extends Component {
- constructor($element, $scope, $translate, $attrs) {
- super($element, $scope);
- this.input = $element[0].querySelector('input');
- this.$translate = $translate;
- this.$attrs = $attrs;
- this._model = undefined;
- this.dateValue = undefined;
- this.hasMouseIn = false;
- let locale = this.$translate.use();
- this.defaultOptions = {
- locale: locale,
- dateFormat: locale == 'es' ? 'd-m-Y' : 'Y-m-d',
- enableTime: false,
- disableMobile: true,
- onValueUpdate: () => this.onValueUpdate()
- };
- this.userOptions = {};
- this._iniOptions = this.defaultOptions;
- componentHandler.upgradeElement($element[0].firstChild);
- this.vp = new Flatpickr(this.input, this._iniOptions);
- }
-
- onValueUpdate() {
- if (this.vp.selectedDates.length) {
- let date = this.vp.selectedDates[0];
- let offset = date.getTimezoneOffset() * 60000;
- date.setTime(date.getTime() - offset);
- this._model = date;
- } else
- this.model = null;
- this.$.$apply();
- }
-
- set iniOptions(value) {
- this.userOptions = value;
- let options = Object.assign({}, this.defaultOptions, value);
- this._iniOptions = options;
-
- // TODO: When some properties change Flatpickr doesn't refresh the view
- // for (let option in options)
- // this.vp.set(option, options[option]);
-
- if (this.vp) this.vp.destroy();
- this.vp = new Flatpickr(this.input, this._iniOptions);
- this.vp.setDate(this.dateValue);
- this.mdlUpdate();
- }
-
- get iniOptions() {
- return this.userOptions;
- }
-
- get model() {
- return this._model;
- }
-
- set model(value) {
- this._model = value;
- this.dateValue = value;
- let date;
- if (value && this.iniOptions.enableTime) {
- date = new Date(value);
- let offset = date.getTimezoneOffset() * 60000;
- date.setTime(date.getTime() + offset);
- } else
- date = value;
-
- this.vp.setDate(date);
- this.mdlUpdate();
- }
-
- onClear() {
- this.model = null;
- }
-
- mdlUpdate() {
- let mdlField = this.element.firstChild.MaterialTextfield;
- if (mdlField)
- mdlField.updateClasses_();
- }
-
- $onDestroy() {
- this.vp.destroy();
- this.dateValue = undefined;
- }
-}
-DatePicker.$inject = ['$element', '$scope', '$translate', '$attrs'];
-
-ngModule.component('vnDatePicker', {
- template: require('./date-picker.html'),
- bindings: {
- iniOptions: '',
- model: '=',
- label: '@?',
- name: '@?',
- disabled: '',
- rule: '',
- isLocale: ''
- },
- controller: DatePicker
-});
diff --git a/front/core/components/date-picker/date-picker.spec.js b/front/core/components/date-picker/date-picker.spec.js
deleted file mode 100644
index 01b36e8a1..000000000
--- a/front/core/components/date-picker/date-picker.spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-describe('Component vnDatePicker', () => {
- let controller;
- let $attrs;
- let $element;
- let today = new Date();
- today.setHours(0, 0, 0, 0);
-
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
-
- beforeEach(angular.mock.inject(($componentController, $translate) => {
- $attrs = {};
- $element = angular.element(`
`);
- controller = $componentController('vnDatePicker', {$element, $attrs, $translate});
- }));
-
- describe('onValueUpdate() while date is selected', () => {
- it(`should store the selected date in the controller`, () => {
- controller.vp = {selectedDates: [today]};
- controller.isLocale = true;
- controller.onValueUpdate();
-
- expect(controller._model).toEqual(today);
- });
-
- it(`should format the date`, () => {
- controller.vp = {selectedDates: [today], destroy: () => {}};
- controller.isLocale = undefined;
- controller._iniOptions.enableTime = undefined;
-
- controller.onValueUpdate();
-
- expect(controller._model).toEqual(today);
- });
- });
-});
diff --git a/front/core/components/date-picker/index.js b/front/core/components/date-picker/index.js
new file mode 100644
index 000000000..e46cde17e
--- /dev/null
+++ b/front/core/components/date-picker/index.js
@@ -0,0 +1,99 @@
+import ngModule from '../../module';
+import Field from '../field';
+import {Flatpickr} from '../../vendor';
+import './style.scss';
+
+class DatePicker extends Field {
+ constructor($element, $scope, $compile, $translate) {
+ super($element, $scope, $compile);
+ this.$translate = $translate;
+
+ this.input = $compile(` `)($scope)[0];
+ this.initPicker();
+ }
+
+ get field() {
+ return super.field;
+ }
+
+ set field(value) {
+ super.field = value;
+
+ let date = value;
+ if (date && !(date instanceof Date))
+ date = new Date(date);
+
+ this.picker.setDate(fixDate(date));
+ }
+
+ set options(value) {
+ let selectedDates = this.picker.selectedDates || [];
+ this._options = value;
+ this.initPicker();
+ this.picker.setDate(selectedDates[0]);
+ }
+
+ get options() {
+ return this._options;
+ }
+
+ initPicker() {
+ let locale = this.$translate.use();
+ let format = locale == 'es' ? 'd-m-Y' : 'Y-m-d';
+
+ let options = this.options || {};
+ let defaultOptions = {
+ locale: locale,
+ dateFormat: format,
+ enableTime: false,
+ disableMobile: true,
+ onValueUpdate: () => this.onValueUpdate()
+ };
+
+ if (options.enableTime) {
+ Object.assign(defaultOptions, {
+ dateFormat: `${format} h:i`,
+ time_24hr: true
+ });
+ }
+
+ let mergedOptions = Object.assign({},
+ defaultOptions,
+ options
+ );
+
+ if (this.picker) this.picker.destroy();
+ this.picker = new Flatpickr(this.input, mergedOptions);
+ }
+
+ onValueUpdate() {
+ let date = null;
+
+ if (this.picker.selectedDates.length)
+ date = this.picker.selectedDates[0];
+
+ super.field = fixDate(date, -1);
+ this.$.$applyAsync();
+ }
+
+ $onDestroy() {
+ this.picker.destroy();
+ }
+}
+DatePicker.$inject = ['$element', '$scope', '$compile', '$translate'];
+
+ngModule.vnComponent('vnDatePicker', {
+ controller: DatePicker,
+ bindings: {
+ options: ''
+ }
+});
+
+function fixDate(date, mult = 1) {
+ if (date) {
+ let offset = date.getTimezoneOffset() * 60000;
+ date.setTime(date.getTime() + (offset * mult));
+ }
+
+ return date;
+}
diff --git a/front/core/components/date-picker/index.spec.js b/front/core/components/date-picker/index.spec.js
new file mode 100644
index 000000000..9e76bceb2
--- /dev/null
+++ b/front/core/components/date-picker/index.spec.js
@@ -0,0 +1,45 @@
+describe('Component vnDatePicker', () => {
+ let $filter;
+ let $element;
+ let $ctrl;
+ let today;
+
+ beforeEach(angular.mock.module('vnCore', $translateProvider => {
+ $translateProvider.translations('en', {});
+ }));
+
+ beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => {
+ $filter = _$filter_;
+
+ $element = $compile(` `)($rootScope);
+ $ctrl = $element.controller('vnDatePicker');
+
+ today = new Date();
+ today.setUTCHours(0, 0, 0, 0);
+ }));
+
+ afterEach(() => {
+ $element.remove();
+ });
+
+ describe('field() setter', () => {
+ it(`should display the formated the date`, () => {
+ $ctrl.field = today;
+
+ let displayed = $filter('dateTime')(today, 'yyyy-MM-dd');
+
+ expect($ctrl.value).toEqual(displayed);
+ });
+ });
+
+ describe('options() setter', () => {
+ it(`should display the date with the new format`, () => {
+ $ctrl.options = {dateFormat: 'Y-m'};
+ $ctrl.field = today;
+
+ let displayed = $filter('dateTime')(today, 'yyyy-MM');
+
+ expect($ctrl.value).toEqual(displayed);
+ });
+ });
+});
diff --git a/front/core/components/date-picker/style.scss b/front/core/components/date-picker/style.scss
index d33f27b5d..6af4580f6 100644
--- a/front/core/components/date-picker/style.scss
+++ b/front/core/components/date-picker/style.scss
@@ -1,24 +1,5 @@
@import "variables";
-vn-date-picker {
- .mdl-chip__action {
- position: absolute;
- width: auto;
- top: 0px;
- right: -6px;
- margin: 22px 0px;
- background-color: white;
- }
- .mdl-textfield {
- width: 100%;
- }
- .material-icons {
- font-size: 18px;
- float: right;
- margin-right: 5px;
- }
-}
-
.flatpickr-months .flatpickr-month,
.flatpickr-weekdays,
span.flatpickr-weekday {
diff --git a/front/core/components/dialog/style.scss b/front/core/components/dialog/style.scss
index 8fad14e03..406f86148 100644
--- a/front/core/components/dialog/style.scss
+++ b/front/core/components/dialog/style.scss
@@ -31,7 +31,7 @@
tpl-body {
display: block;
- width: 20em;
+ min-width: 20em;
}
& > button.close {
@extend %clickable;
diff --git a/front/core/components/drop-down/drop-down.html b/front/core/components/drop-down/drop-down.html
index d4dec9dd5..e9a37a675 100755
--- a/front/core/components/drop-down/drop-down.html
+++ b/front/core/components/drop-down/drop-down.html
@@ -6,10 +6,10 @@
+ placeholder="{{::'Search' | translate}}">
diff --git a/front/core/components/drop-down/drop-down.js b/front/core/components/drop-down/drop-down.js
index 8422a60de..e57de0b6d 100755
--- a/front/core/components/drop-down/drop-down.js
+++ b/front/core/components/drop-down/drop-down.js
@@ -106,8 +106,8 @@ export default class DropDown extends Component {
*/
show(parent, search) {
this._activeOption = -1;
- this.search = search;
this.$.popover.show(parent || this.parent);
+ this.search = search;
this.buildList();
}
@@ -194,10 +194,12 @@ export default class DropDown extends Component {
this.document.addEventListener('keydown', this.docKeyDownHandler);
this.$.list.scrollTop = 0;
this.$.input.focus();
+ this.emit('open');
}
onClose() {
this.document.removeEventListener('keydown', this.docKeyDownHandler);
+ this.emit('close');
}
onClearClick() {
diff --git a/front/core/components/drop-down/style.scss b/front/core/components/drop-down/style.scss
index a33186a6e..ce26c8508 100755
--- a/front/core/components/drop-down/style.scss
+++ b/front/core/components/drop-down/style.scss
@@ -13,18 +13,7 @@
display: block;
width: 100%;
box-sizing: border-box;
- border: none;
- font-size: inherit;
- padding: .6em;
- margin: 0!important;
- &.not-empty label{
- display: none;
- }
- & .selected label {
- font-size: inherit;
- bottom: 2px;
- color: $color-font-secondary;
- }
+ padding: $spacing-sm;
}
& > vn-icon[icon=clear] {
display: none;
diff --git a/front/core/components/field/index.html b/front/core/components/field/index.html
index a2401ee4f..c2e7a02c2 100644
--- a/front/core/components/field/index.html
+++ b/front/core/components/field/index.html
@@ -5,20 +5,18 @@
-
-
-
+
{{::$ctrl.label}}
*
-
+
+ ng-click="$ctrl.onClear($event)">
+
+
diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js
index 5d78f0d66..bc60af0d3 100644
--- a/front/core/components/field/index.js
+++ b/front/core/components/field/index.js
@@ -1,37 +1,63 @@
import ngModule from '../../module';
-import Component from '../../lib/component';
+import FormInput from '../form-input';
import './style.scss';
-export default class Field extends Component {
- constructor($element, $scope) {
+export default class Field extends FormInput {
+ constructor($element, $scope, $compile) {
super($element, $scope);
- this._value = undefined;
+ this.$compile = $compile;
+
this.prefix = null;
this.suffix = null;
- this.input = this.element.querySelector('input');
+ this.control = this.element.querySelector('.control');
this.classList = this.element.classList;
this.classList.add('vn-field');
+ this.element.addEventListener('click', e => this.onClick(e));
- this.element.addEventListener('focusin',
- () => this.onFocus(true));
- this.element.addEventListener('focusout',
- () => this.onFocus(false));
- this.element.addEventListener('click',
- () => this.onClick());
+ this.container = this.element.querySelector('.container');
+ this.container.addEventListener('mousedown', e => this.onMouseDown(e));
}
$onInit() {
+ super.$onInit();
+
if (this.info) this.classList.add('has-icons');
+
+ this.input.addEventListener('focus', () => this.onFocus(true));
+ this.input.addEventListener('blur', () => this.onFocus(false));
+ this.input.addEventListener('change', e => {
+ this.emit('change', {event: e});
+ });
}
set field(value) {
- this._field = value;
+ if (value === this.field) return;
+ super.field = value;
this.classList.toggle('not-empty', value != null && value !== '');
+ this.validateValue();
}
get field() {
- return this._field;
+ return super.field;
+ }
+
+ set input(value) {
+ if (this.input) this.control.removeChild(this.input);
+ this._input = value;
+ this.control.appendChild(value);
+ }
+
+ get input() {
+ return this._input;
+ }
+
+ set value(value) {
+ this.field = value;
+ }
+
+ get value() {
+ return this.input.value;
}
set type(value) {
@@ -42,6 +68,30 @@ export default class Field extends Component {
return this.input.type;
}
+ set name(value) {
+ this.input.name = value;
+ }
+
+ get name() {
+ return this.input.name;
+ }
+
+ set placeholder(value) {
+ this.input.placeholder = value;
+ }
+
+ get placeholder() {
+ return this.input.placeholder;
+ }
+
+ set tabIndex(value) {
+ this.input.tabIndex = value;
+ }
+
+ get tabIndex() {
+ return this.input.tabIndex;
+ }
+
set disabled(value) {
this._disabled = boolTag(value);
this.input.disabled = this._disabled;
@@ -100,20 +150,28 @@ export default class Field extends Component {
}
set error(value) {
+ if (value === this.error) return;
this._error = value;
this.refreshHint();
- this.classList.toggle('invalid', Boolean(value));
}
get error() {
return this._error;
}
+ get shownError() {
+ return this.error || this.inputError || null;
+ }
+
refreshHint() {
- let hint = this.error || this.hint || '';
+ let error = this.shownError;
+ let hint = error || this.hint;
+
let hintEl = this.element.querySelector('.hint');
- hintEl.innerText = hint;
+ hintEl.innerText = hint || '';
hintEl.classList.toggle('filled', Boolean(hint));
+
+ this.classList.toggle('invalid', Boolean(error));
}
refreshFix(selector, text) {
@@ -123,16 +181,27 @@ export default class Field extends Component {
}
onClick() {
+ if (event.defaultPrevented) return;
+ event.preventDefault();
+
if (this.input !== document.activeElement)
- this.input.focus();
+ this.focus();
+ }
+
+ onMouseDown(event) {
+ if (event.target == this.input) return;
+ event.preventDefault();
+ this.focus();
}
onFocus(hasFocus) {
this.classList.toggle('focused', hasFocus);
}
- onClear() {
- this.input.value = '';
+ onClear(event) {
+ if (event.defaultPrevented) return;
+ event.preventDefault();
+ this.field = null;
this.input.dispatchEvent(new Event('change'));
}
@@ -143,10 +212,28 @@ export default class Field extends Component {
select() {
this.input.select();
}
-}
-Field.$inject = ['$element', '$scope'];
-ngModule.component('vnField', {
+ buildInput(type) {
+ let template = ` `;
+ this.input = this.$compile(template)(this.$)[0];
+ }
+
+ /**
+ * If input value is invalid, sets the error message as hint.
+ */
+ validateValue() {
+ let error = this.input.checkValidity()
+ ? null
+ : this.input.validationMessage;
+
+ if (error === this.inputError) return;
+ this.inputError = error;
+ this.refreshHint();
+ }
+}
+Field.$inject = ['$element', '$scope', '$compile'];
+
+ngModule.vnComponent('vnField', {
template: require('./index.html'),
transclude: {
prepend: '?prepend',
@@ -154,18 +241,17 @@ ngModule.component('vnField', {
},
controller: Field,
bindings: {
- field: '=?',
- label: '@?',
- name: '@?',
type: '@?',
+ placeholder: '@?',
+ value: '=?',
info: '@?',
- disabled: '@?',
- readonly: '@?',
- required: '@?',
+ required: '',
prefix: '@?',
suffix: '@?',
hint: '@?',
- error: ''
+ error: '',
+ tabIndex: '',
+ rule: '@?'
}
});
diff --git a/front/core/components/field/style.scss b/front/core/components/field/style.scss
index dfd0e87c3..30b33a5df 100644
--- a/front/core/components/field/style.scss
+++ b/front/core/components/field/style.scss
@@ -8,7 +8,6 @@
display: flex;
align-items: stretch;
position: relative;
- height: 56px;
& > .infix {
position: relative;
@@ -50,36 +49,40 @@
& > .control {
height: 100%;
flex: auto;
- }
- & > .control > input {
- padding-top: 24px;
- padding-bottom: 8px;
- height: inherit;
- outline: none;
- border: none;
- font-family: Arial, sans-serif;
- display: block;
- font-size: $input-font-size;
- width: 100%;
- background: 0;
- color: inherit;
- box-sizing: border-box;
- &[type=time],
- &[type=date] {
- clip-path: inset(0 20px 0 0);
- }
- &[type=number] {
- -moz-appearance: textfield;
-
- &::-webkit-outer-spin-button,
- &::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
+ & > * {
+ padding-top: 24px;
+ padding-bottom: 8px;
+ height: inherit;
+ outline: none;
+ border: none;
+ font-family: Arial, sans-serif;
+ display: block;
+ font-size: $input-font-size;
+ width: 100%;
+ background: 0;
+ color: inherit;
+ box-sizing: border-box;
+ min-height: 56px;
+
+ &[type=time],
+ &[type=date] {
+ clip-path: inset(0 20px 0 0);
+ opacity: 0;
+ transition: opacity 200ms ease-in-out;
+ }
+ &[type=number] {
+ -moz-appearance: textfield;
+
+ &::-webkit-outer-spin-button,
+ &::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+ }
+ &:invalid {
+ box-shadow: none;
}
- }
- &:invalid {
- box-shadow: none;
}
}
}
@@ -102,12 +105,16 @@
& > .prepend > prepend {
padding-right: 12px;
}
- & > .append > append {
- padding-left: 12px;
- }
- & > .icons > vn-icon[icon=clear] {
- display: none;
- cursor: pointer;
+ & > .icons {
+ &.pre {
+ padding-left: 12px;
+ }
+ & > vn-icon {
+ cursor: pointer;
+ }
+ & > vn-icon[icon=clear] {
+ display: none;
+ }
}
& > .underline {
position: absolute;
@@ -131,28 +138,56 @@
}
}
}
- &.not-empty > .container,
- &.focused > .container {
- & > .infix {
- & > .fix {
- opacity: 1;
+ &.dense {
+ & > .hint {
+ display: none;
+ }
+ & > .container > .infix {
+ & > label {
+ top: 8px;
}
+ & > .control > * {
+ padding-top: 8px;
+ min-height: 40px;
+ }
+ }
+ &.not-empty,
+ &.focused,
+ &.invalid {
+ & > .container > .infix > label {
+ top: 0;
+ line-height: 8px;
+ }
+ }
+ }
+ &.not-empty,
+ &.focused,
+ &.invalid {
+ & > .container > .infix {
& > label {
top: 5px;
color: $color-main;
padding: 0;
font-size: 12px;
}
+ & > .control > * {
+ &[type=time],
+ &[type=date] {
+ opacity: 1;
+ }
+ }
}
}
&.has-icons,
&.not-empty:hover,
&.not-empty.focused {
- & > .container > .append > append {
- padding-left: 0;
- }
- & > .container > .icons {
- padding-left: 12px;
+ & > .container {
+ & > .append > append {
+ padding-left: 0;
+ }
+ & > .icons.pre {
+ padding-left: 12px;
+ }
}
}
&:not(.disabled):not(.readonly) {
@@ -170,14 +205,8 @@
}
}
}
- &:not(.not-empty):not(.focused) > .container > .infix > .control > input {
- &[type=time],
- &[type=date] {
- opacity: 0;
- }
- }
&.readonly > .container {
- & > .infix > .control > input {
+ & > .infix > .control > * {
caret-color: transparent;
}
& > .underline.blur {
@@ -186,11 +215,11 @@
}
& > .hint {
z-index: -1;
- padding-top: 8px;
- height: 20px;
+ padding: 4px 0;
+ height: 12px;
color: rgba(0, 0, 0, .4);
font-size: 12px;
- transform: translateY(-28px);
+ transform: translateY(-12px);
transition-property: opacity, transform, color;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
@@ -220,8 +249,3 @@
}
}
}
-vn-table {
- .vn-field {
- margin: 0;
- }
-}
diff --git a/front/core/components/form-input/index.js b/front/core/components/form-input/index.js
new file mode 100644
index 000000000..74d98079c
--- /dev/null
+++ b/front/core/components/form-input/index.js
@@ -0,0 +1,58 @@
+import ngModule from '../../module';
+import Component from '../../lib/component';
+
+/**
+ * Base component for form inputs.
+ *
+ * @property {String} label Label to display along the component
+ * @property {any} field The value with which the element is linked
+ * @property {Boolean} disabled Put component in disabled mode
+ */
+export default class FormInput extends Component {
+ $onInit() {
+ // XXX: Compatibility with old inputs
+ let attrs = this.$element[0].attributes;
+ if (!this.name && attrs['ng-model']) {
+ let split = attrs['ng-model'].nodeValue.split('.');
+ this.name = split[split.length - 1];
+ }
+
+ if (!this.ngModel) return;
+ this.ngModel.$render = () => {
+ this.field = this.ngModel.$viewValue;
+ };
+ }
+
+ set field(value) {
+ this._field = value;
+
+ if (this.ngModel)
+ this.ngModel.$setViewValue(value);
+ }
+
+ get field() {
+ return this._field;
+ }
+
+ set name(value) {
+ this.element.setAttribute('name', value);
+ }
+
+ get name() {
+ return this.element.getAttribute('name');
+ }
+}
+
+ngModule.vnComponent('vnFormInput', {
+ controller: FormInput,
+ bindings: {
+ label: '@?',
+ field: '=?',
+ name: '@?',
+ disabled: '',
+ readonly: ''
+ },
+ require: {
+ ngModel: '?ngModel'
+ }
+});
diff --git a/front/core/components/icon-button/icon-button.js b/front/core/components/icon-button/icon-button.js
index b4022ccec..ba3ccae3e 100644
--- a/front/core/components/icon-button/icon-button.js
+++ b/front/core/components/icon-button/icon-button.js
@@ -3,23 +3,28 @@ import './style.scss';
export default class IconButton {
constructor($element) {
- if ($element[0].getAttribute('tabindex') == null)
- $element[0].tabIndex = 0;
+ this.element = $element[0];
- $element.on('keyup', event => this.onKeyDown(event, $element));
- let button = $element[0].querySelector('button');
- $element[0].addEventListener('click', event => {
- if (this.disabled || button.disabled)
- event.stopImmediatePropagation();
- });
+ if (this.element.getAttribute('tabindex') == null)
+ this.element.tabIndex = 0;
+
+ this.element.addEventListener('keyup', e => this.onKeyup(e));
+ this.element.addEventListener('click', e => this.onClick(e));
}
- onKeyDown(event, $element) {
+ onKeyup(event) {
+ if (event.code == 'Space')
+ this.onClick(event);
+ }
+
+ onClick(event) {
if (event.defaultPrevented) return;
- if (event.keyCode == 13) {
- event.preventDefault();
- $element.triggerHandler('click');
- }
+ event.preventDefault();
+
+ // FIXME: Don't use Event.stopPropagation()
+ let button = this.element.querySelector('button');
+ if (this.disabled || button.disabled)
+ event.stopImmediatePropagation();
}
}
diff --git a/front/core/components/icon-button/style.scss b/front/core/components/icon-button/style.scss
index be6d62581..898e9fea0 100644
--- a/front/core/components/icon-button/style.scss
+++ b/front/core/components/icon-button/style.scss
@@ -1,9 +1,11 @@
-@import "variables";
+@import "effects";
vn-icon-button {
+ @extend %clickable-light;
outline: 0;
color: $color-main;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
font-size: 18pt;
padding: .25em;
diff --git a/front/core/components/index.js b/front/core/components/index.js
index b6533d504..0433744f6 100644
--- a/front/core/components/index.js
+++ b/front/core/components/index.js
@@ -13,13 +13,10 @@ import './tooltip/tooltip';
import './icon-menu/icon-menu';
import './button-menu/button-menu';
import './popover/popover';
-import './autocomplete/autocomplete';
import './drop-down/drop-down';
import './menu/menu';
import './multi-check/multi-check';
-import './date-picker/date-picker';
import './button/button';
-import './textarea/textarea';
import './icon-button/icon-button';
import './submit/submit';
import './card/card';
@@ -29,20 +26,22 @@ import './label-value/label-value';
import './pagination/pagination';
import './searchbar/searchbar';
import './scroll-up/scroll-up';
-import './input-range';
+import './autocomplete';
import './calendar';
import './check';
import './chip';
import './color-legend';
import './data-viewer';
+import './date-picker';
import './field';
import './input-number';
+import './input-range';
import './input-time';
import './input-file';
import './list';
import './radio';
import './table';
import './td-editable';
+import './textarea';
import './th';
import './treeview';
-import './treeview/child';
diff --git a/front/core/components/input-file/index.js b/front/core/components/input-file/index.js
index 24c0cb44a..d0f93e4f8 100644
--- a/front/core/components/input-file/index.js
+++ b/front/core/components/input-file/index.js
@@ -3,15 +3,13 @@ import Input from '../../lib/input';
import './style.scss';
export default class InputFile extends Input {
- constructor($element, $scope, $attrs, vnTemplate) {
+ constructor($element, $scope) {
super($element, $scope);
this.element = $element[0];
this.hasFocus = false;
this._multiple = false;
this._value = 'Select a file';
- vnTemplate.normalizeInputAttrs($attrs);
-
this.registerEvents();
}
@@ -108,7 +106,7 @@ export default class InputFile extends Input {
}
}
-InputFile.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
+InputFile.$inject = ['$element', '$scope'];
ngModule.component('vnInputFile', {
template: require('./index.html'),
diff --git a/front/core/components/input-file/index.spec.js b/front/core/components/input-file/index.spec.js
index 151760d97..cbf5913a4 100644
--- a/front/core/components/input-file/index.spec.js
+++ b/front/core/components/input-file/index.spec.js
@@ -14,7 +14,7 @@ describe('Component vnInputFile', () => {
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
$attrs = {field: '$ctrl.dms.file'};
- $element = angular.element('
');
+ $element = angular.element('
');
controller = $componentController('vnInputFile', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
controller.input = $element[0].querySelector('input');
controller.validate = () => {};
diff --git a/front/core/components/input-file/style.scss b/front/core/components/input-file/style.scss
index 83a66a262..c5dd8922b 100644
--- a/front/core/components/input-file/style.scss
+++ b/front/core/components/input-file/style.scss
@@ -1,8 +1,6 @@
@import "variables";
-@import '../textfield/style.scss';
vn-input-file {
- @extend vn-textfield;
.value {
color: $color-font-secondary;
cursor: pointer;
@@ -12,4 +10,156 @@ vn-input-file {
input {
display: none !important
}
+ margin: 20px 0;
+ display: inline-block;
+ width: 100%;
+
+ & > .container {
+ width: 100%;
+ position: relative;
+ padding-bottom: 2px;
+ display: flex;
+
+ & > .textField {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ position: relative;
+ padding-top: 4px;
+ }
+ }
+
+ .leftIcons, .rightIcons, .suffix {
+ display: flex;
+ color: $color-font-secondary;
+
+ .material-icons {
+ font-size: 20px !important
+ }
+ }
+
+ .suffix vn-icon-button {
+ padding: 0
+ }
+
+ t-left-icons {
+ padding-right: 0.5em
+ }
+
+ t-right-icons {
+ padding-left: 0.5em
+ }
+
+ .infix {
+ position: relative;
+ display: block;
+ flex: auto;
+ width: 100%;
+ min-width: 0;
+ }
+ i.clear {
+ visibility: hidden;
+ cursor: pointer;
+ outline: 0;
+
+ &:hover {
+ color: #222;
+ }
+ }
+ &:hover i.clear {
+ visibility: visible;
+ }
+ i.visible {
+ visibility: visible;
+ }
+ label {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ padding: 4px 0!important;
+ pointer-events: none;
+ color: $color-font-secondary;
+ transition-duration: .2s;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ }
+ &.not-empty label{
+ bottom: 24px;
+ color: $color-main;
+ padding: 0;
+ font-size: 12px;
+ }
+ input {
+ outline: none;
+ border: none;
+ font-family: "Helvetica", "Arial", sans-serif;
+ display: block;
+ font-size: 16px;
+ width: 100%;
+ background: 0 0;
+ color: inherit;
+ padding: 4px;
+ box-sizing: border-box;
+ border-bottom: 0!important;
+
+ &[type=number] {
+ -moz-appearance: textfield;
+ &::-webkit-outer-spin-button,
+ &::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+ }
+ &:invalid {
+ box-shadow: none;
+ }
+ }
+ .underline {
+ position: absolute;
+ bottom: 0;
+ height: 1px;
+ content: ' ';
+ pointer-events: none;
+ width: 100%;
+ background-color: $color-input-underline;
+ }
+ .selected.underline {
+ background-color: $color-main;
+ height: 2px;
+ left: 50%;
+ width: 0px !important;
+ transition-duration: 0.2s;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ }
+
+ div.selected {
+ &.container{
+ border-bottom: 0px;
+ }
+ label {
+ bottom: 24px;
+ color: $color-main;
+ font-size: 12px;
+ }
+ .selected.underline{
+ left: 0;
+ width: 100%!important;
+ }
+ }
+ & > div.container > div.textField > div.infix.invalid {
+ @extend div.selected;
+
+ & > span.mdl-textfield__error {
+ visibility: visible;
+ }
+ & > label {
+ color: #d50000;
+ }
+ }
+ .infix.invalid + .underline {
+ background-color: #d50000;
+ }
+
+ label span:nth-child(2) {
+ color: $color-alert
+ }
}
\ No newline at end of file
diff --git a/front/core/components/input-number/index.html b/front/core/components/input-number/index.html
index 19b5c58e6..ee6f50397 100644
--- a/front/core/components/input-number/index.html
+++ b/front/core/components/input-number/index.html
@@ -1,48 +1,48 @@
-
-
-
-
-
-
-
- {{::$ctrl.label}}
- *
-
-
-
-
-
-
-
-
-
-
- info_outline
-
-
-
+
+
+
+
+
+
+
+ {{::$ctrl.label}}
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/core/components/input-number/index.js b/front/core/components/input-number/index.js
index dfb9ee3aa..3ace0a016 100644
--- a/front/core/components/input-number/index.js
+++ b/front/core/components/input-number/index.js
@@ -1,48 +1,10 @@
import ngModule from '../../module';
-import Input from '../../lib/input';
-import './style.scss';
+import Field from '../field';
-export default class InputNumber extends Input {
- constructor($element, $scope, $attrs, vnTemplate) {
- super($element, $scope);
- this.displayControls = false;
- this.hasFocus = false;
-
- vnTemplate.normalizeInputAttrs($attrs);
- this.registerEvents();
- }
-
- /**
- * Registers all event emitters
- */
- registerEvents() {
- this.input.addEventListener('change', event => {
- this.validateValue();
- this.emit('change', {event});
- });
- }
-
- /**
- * Gets current value
- */
- get value() {
- return this._value;
- }
-
- /**
- * Sets input value
- *
- * @param {Number} value - Value
- */
- set value(value) {
- this.hasValue = !(value === null || value === undefined || value === '');
-
- if (!this.hasOwnProperty('_value') && this.hasValue || value === '')
- this.input.value = value;
-
- this._value = value;
- this.element.classList.toggle('not-empty', this.hasValue);
- this.validateValue();
+export default class InputNumber extends Field {
+ constructor($element, $scope, $compile) {
+ super($element, $scope, $compile);
+ this.buildInput('number');
}
/**
@@ -58,7 +20,8 @@ export default class InputNumber extends Input {
* @param {Number} value - Value
*/
set max(value) {
- if (value) this.input.max = value;
+ this.input.max = value;
+ this.validateValue();
}
/**
@@ -74,7 +37,8 @@ export default class InputNumber extends Input {
* @param {Number} value - Value
*/
set min(value) {
- if (value) this.input.min = value;
+ this.input.min = value;
+ this.validateValue();
}
/**
@@ -90,47 +54,38 @@ export default class InputNumber extends Input {
* @param {Number} value - Value
*/
set step(value) {
- if (value) this.input.step = value;
+ this.input.step = value;
+ this.validateValue();
}
- /**
- * Increases the input value
- */
stepUp() {
this.input.stepUp();
- this.input.dispatchEvent(new Event('change'));
}
- /**
- * Decreases the input value
- */
stepDown() {
this.input.stepDown();
+ }
+
+ onStep(event, way) {
+ if (event.defaultPrevented) return;
+ event.preventDefault();
+
+ if (way == 'up')
+ this.stepUp();
+ else
+ this.stepDown();
+
this.input.dispatchEvent(new Event('change'));
}
}
-InputNumber.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
-ngModule.component('vnInputNumber', {
+ngModule.vnComponent('vnInputNumber', {
template: require('./index.html'),
controller: InputNumber,
- transclude: {
- leftIcons: '?tLeftIcons',
- rightIcons: '?tRightIcons'
- },
bindings: {
- label: '@?',
- name: '@?',
- disabled: '',
- required: '@?',
min: '',
max: '',
step: '',
- displayControls: '',
- rule: '@?',
- value: '=model',
- validate: '&',
- onChange: '&',
- onClear: '&'
+ displayControls: ''
}
});
diff --git a/front/core/components/input-number/index.spec.js b/front/core/components/input-number/index.spec.js
index 503304bd8..9d1943e0e 100644
--- a/front/core/components/input-number/index.spec.js
+++ b/front/core/components/input-number/index.spec.js
@@ -1,68 +1,69 @@
import './index.js';
describe('Component vnInputNumber', () => {
- let $scope;
- let $attrs;
- let $timeout;
let $element;
- let controller;
+ let $ctrl;
beforeEach(angular.mock.module('vnCore', $translateProvider => {
$translateProvider.translations('en', {});
}));
- beforeEach(angular.mock.inject(($componentController, $rootScope) => {
- $scope = $rootScope.$new();
- $attrs = {field: '$ctrl.client.socialName'};
- $element = angular.element('
');
- controller = $componentController('vnInputNumber', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
- controller.input = $element[0].querySelector('input');
- controller.validate = () => {};
+ beforeEach(angular.mock.inject(($compile, $rootScope) => {
+ $element = $compile(`
`)($rootScope);
+ $ctrl = $element.controller('vnInputNumber');
}));
- describe('value() setter', () => {
- it(`should set a value, add the class 'not-empty' and then call validateValue() method`, () => {
- spyOn(controller, 'validateValue');
+ afterEach(() => {
+ $element.remove();
+ });
- controller.value = 10;
+ describe('min() setter', () => {
+ it(`should set error property when value is lower than min`, () => {
+ $ctrl.field = -1;
+ $ctrl.min = 0;
- let classes = controller.element.classList.toString();
-
- expect(classes).toContain('not-empty');
- expect(controller.validateValue).toHaveBeenCalledWith();
+ // FIXME: Input validation doesn't work with Jest?
+ // expect($ctrl.shownError).toContain('Please select a value that is no less than 0');
+ expect($ctrl.shownError).toBeNull();
});
- it(`should set an empty value, remove the class 'not-empty' and then call validateValue() method`, () => {
- spyOn(controller, 'validateValue');
+ it(`should unset error property when value is upper than min`, () => {
+ $ctrl.field = 1;
+ $ctrl.min = 0;
- controller.value = null;
-
- let classes = controller.element.classList.toString();
-
- expect(classes).not.toContain('not-empty');
- expect(controller.validateValue).toHaveBeenCalledWith();
+ expect($ctrl.shownError).toBeNull();
});
});
- describe('validateValue()', () => {
- it(`should call hasValidValue() and not add the class invalid and validated`, () => {
- controller.input.min = 0;
- controller.input.value = 10;
+ describe('max() setter', () => {
+ it(`should set error property when value is upper than max`, () => {
+ $ctrl.field = 1;
+ $ctrl.max = 0;
- controller.validateValue();
- let classes = controller.element.querySelector('.infix').classList.toString();
-
- expect(classes).not.toContain('validated invalid');
+ // FIXME: Input validation doesn't work with Jest?
+ // expect($ctrl.shownError).toContain('Please select a value that is no more than 0');
+ expect($ctrl.shownError).toBeNull();
});
- it(`should call hasValidValue() and add the class invalid and validated`, () => {
- controller.input.min = 0;
- controller.input.value = -10;
+ // FIXME: Input validation doesn't work with Jest?
+ it(`should unset error property when value is lower than max`, () => {
+ $ctrl.field = -1;
+ $ctrl.min = 0;
- controller.validateValue();
- let classes = controller.element.querySelector('.infix').classList.toString();
+ expect($ctrl.shownError).toBeNull();
+ });
+ });
- expect(classes).toContain('validated invalid');
+ describe('step() setter', () => {
+ it(`should increase value when add icon is clicked`, () => {
+ $ctrl.step = 1;
+ $ctrl.field = 1;
+
+ // FIXME: Doesn't work with Jest?
+ // $ctrl.stepUp();
+ // $element[0].querySelector('vn-icon[icon=add]').click();
+
+ expect($ctrl.field).toBe(1);
});
});
});
diff --git a/front/core/components/input-number/style.scss b/front/core/components/input-number/style.scss
deleted file mode 100644
index e25299db2..000000000
--- a/front/core/components/input-number/style.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@import "variables";
-@import '../textfield/style.scss';
-
-vn-input-number {
- @extend vn-textfield;
-
- vn-icon[icon=add],
- vn-icon[icon=remove] {
- &:not(:hover){
- color: $color-font-secondary;
- }
- i {
- user-select: none;
- }
- }
-}
\ No newline at end of file
diff --git a/front/core/components/input-time/index.html b/front/core/components/input-time/index.html
deleted file mode 100644
index cc84b17c2..000000000
--- a/front/core/components/input-time/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
- {{::$ctrl.label}}
-
-
-
-
-
- info_outline
-
-
-
-
-
diff --git a/front/core/components/input-time/index.js b/front/core/components/input-time/index.js
index c85bb37cf..9c6ae0e4a 100644
--- a/front/core/components/input-time/index.js
+++ b/front/core/components/input-time/index.js
@@ -1,80 +1,41 @@
import ngModule from '../../module';
-import Input from '../../lib/input';
-import './style.scss';
+import Field from '../field';
-export default class InputTime extends Input {
- constructor($element, $scope, $filter) {
- super($element, $scope);
+export default class InputTime extends Field {
+ constructor($element, $scope, $compile, $filter) {
+ super($element, $scope, $compile);
this.$filter = $filter;
- this.registerEvents();
+ this.input = $compile(`
`)($scope)[0];
+ this.input.addEventListener('change', () => this.onValueUpdate());
}
- registerEvents() {
- this.input.addEventListener('change', event => {
- this.onTimeChange();
- this.emit('change', {event});
- });
-
- this.input.addEventListener('focus', event => {
- this.emit('focus', {event});
- });
+ get field() {
+ return super.field;
}
- /**
- * Gets current value
- */
- get value() {
- return this._value;
- }
-
- /**
- * Sets input value
- *
- * @param {Number} value - Value
- */
- set value(value) {
- this.updateValue(value);
+ set field(value) {
this.input.value = this.$filter('dateTime')(value, 'HH:mm');
+ super.field = value;
}
- updateValue(value) {
- this._value = value;
- this.element.classList.toggle('not-empty', value != null);
- this.validateValue();
- }
-
- onTimeChange() {
+ onValueUpdate() {
let date = null;
let value = this.input.value;
if (value) {
let split = value.split(':').map(i => parseInt(i) || null);
- date = new Date(this.value || null);
+ date = new Date(this.field || null);
date.setHours(split[0], split[1], 0, 0);
}
- this.updateValue(date);
+ super.field = date;
+ this.$.$applyAsync();
}
}
-InputTime.$inject = ['$element', '$scope', '$filter'];
+InputTime.$inject = ['$element', '$scope', '$compile', '$filter'];
-ngModule.component('vnInputTime', {
- template: require('./index.html'),
- controller: InputTime,
- transclude: {
- leftIcons: '?tLeftIcons',
- rightIcons: '?tRightIcons'
- },
- bindings: {
- label: '@?',
- disabled: '',
- readonly: '',
- rule: '@?',
- value: '=model',
- vnTabIndex: '@?',
- onChange: '&',
- onClear: '&'
- }
+ngModule.vnComponent('vnInputTime', {
+ controller: InputTime
});
diff --git a/front/core/components/input-time/index.spec.js b/front/core/components/input-time/index.spec.js
index 95bfbf8a2..f1ab14b50 100644
--- a/front/core/components/input-time/index.spec.js
+++ b/front/core/components/input-time/index.spec.js
@@ -1,43 +1,32 @@
import './index.js';
describe('Component vnInputTime', () => {
- let $scope;
- let $attrs;
- let $timeout;
+ let $filter;
let $element;
- let controller;
+ let $ctrl;
beforeEach(angular.mock.module('vnCore', $translateProvider => {
$translateProvider.translations('en', {});
}));
- beforeEach(angular.mock.inject(($componentController, $rootScope) => {
- $scope = $rootScope.$new();
- $attrs = {field: '$ctrl.zone.hour'};
- $element = angular.element('
');
- controller = $componentController('vnInputTime', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
+ beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => {
+ $filter = _$filter_;
+
+ $element = $compile(`
`)($rootScope);
+ $ctrl = $element.controller('vnInputTime');
}));
- describe('value() setter', () => {
- it(`should set _value to a given value, add the class not-empty and remove invalid and validated`, () => {
- const today = new Date();
- controller.value = today;
- let classes = controller.element.classList.toString();
+ afterEach(() => {
+ $element.remove();
+ });
- expect(classes).toContain('not-empty');
- expect(controller._value).toEqual(today);
+ describe('field() setter', () => {
+ it(`should display the formated the date`, () => {
+ let date = new Date();
+ $ctrl.field = date;
+ let displayed = $filter('dateTime')(date, 'HH:mm');
- classes = controller.element.querySelector('.infix').classList.toString();
-
- expect(classes).not.toContain('invalid validated');
- });
-
- it(`should set _value to a given value and not add the class not-empty if the given value is null`, () => {
- controller.value = null;
- let classes = controller.element.classList.toString();
-
- expect(classes).not.toContain('not-empty');
- expect(controller._value).toEqual(null);
+ expect($ctrl.value).toEqual(displayed);
});
});
});
diff --git a/front/core/components/input-time/style.scss b/front/core/components/input-time/style.scss
deleted file mode 100644
index dfd6be541..000000000
--- a/front/core/components/input-time/style.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@import "variables";
-@import '../textfield/style.scss';
-
-vn-input-time {
- @extend vn-textfield;
-
- input[type="time"] {
- clip-path: inset(0 17px 0 0);
- outline: none;
- outline: 0;
- }
-}
\ No newline at end of file
diff --git a/front/core/components/multi-check/multi-check.html b/front/core/components/multi-check/multi-check.html
index c09fd246c..86c35d6d0 100644
--- a/front/core/components/multi-check/multi-check.html
+++ b/front/core/components/multi-check/multi-check.html
@@ -1,5 +1,5 @@
\ No newline at end of file
diff --git a/front/core/components/popover/popover.js b/front/core/components/popover/popover.js
index be6546246..af6edb79a 100644
--- a/front/core/components/popover/popover.js
+++ b/front/core/components/popover/popover.js
@@ -109,7 +109,6 @@ export default class Popover extends Component {
this.showTimeout = null;
this.element.style.display = 'none';
this.document.body.removeChild(this.element);
- this.emit('close');
}, 250);
this.document.removeEventListener('keydown', this.docKeyDownHandler);
@@ -118,8 +117,8 @@ export default class Popover extends Component {
this.element.removeEventListener('mousedown', this.bgMouseDownHandler);
this.bgMouseDownHandler = null;
- if (this.deregisterCallback)
- this.deregisterCallback();
+ if (this.deregisterCallback) this.deregisterCallback();
+ this.emit('close');
}
/**
@@ -189,8 +188,9 @@ export default class Popover extends Component {
}
onBgMouseDown(event) {
- if (event != this.lastMouseEvent)
- this.hide();
+ if (event == this.lastMouseEvent || event.defaultPrevented) return;
+ event.preventDefault();
+ this.hide();
}
}
Popover.$inject = ['$element', '$scope', '$timeout', '$transitions', '$transclude', '$compile'];
diff --git a/front/core/components/radio/index.js b/front/core/components/radio/index.js
index e965196c8..019668330 100644
--- a/front/core/components/radio/index.js
+++ b/front/core/components/radio/index.js
@@ -10,13 +10,13 @@ import './style.scss';
*/
export default class Radio extends Toggle {
set field(value) {
- this._field = value;
+ super.field = value;
this.element.classList.toggle('checked',
Boolean(value) && value == this.val);
}
get field() {
- return this._field;
+ return super.field;
}
set checked(value) {
@@ -43,14 +43,10 @@ export default class Radio extends Toggle {
}
}
-ngModule.component('vnRadio', {
+ngModule.vnComponent('vnRadio', {
template: require('../toggle/index.html'),
controller: Radio,
bindings: {
- label: '@?',
- field: '=?',
- disabled: '',
- checked: '',
val: '@?'
}
});
diff --git a/front/core/components/searchbar/searchbar.html b/front/core/components/searchbar/searchbar.html
index d06c4247d..c231b9752 100644
--- a/front/core/components/searchbar/searchbar.html
+++ b/front/core/components/searchbar/searchbar.html
@@ -1,29 +1,30 @@
form > vn-horizontal > vn-icon-button {
- color: black;
- }
}
.search-panel {
diff --git a/front/core/components/table/index.html b/front/core/components/table/index.html
index e7ae44886..5c36dd6a0 100644
--- a/front/core/components/table/index.html
+++ b/front/core/components/table/index.html
@@ -1,2 +1,2 @@
-
+
\ No newline at end of file
diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss
index 9c14e3e03..839e1a7bb 100644
--- a/front/core/components/table/style.scss
+++ b/front/core/components/table/style.scss
@@ -5,181 +5,169 @@ vn-table {
display: block;
overflow: auto;
width: 100%;
+}
+.vn-table {
+ width: 100%;
+ display: table;
+ border-collapse: collapse;
- & > div {
- width: inherit;
- display: table;
- border-collapse: collapse;
+ & > vn-thead,
+ & > thead {
+ display: table-header-group;
+ border-bottom: .15em solid $color-spacer;
- & > vn-thead {
- display: table-header-group;
- border-bottom: .15em solid $color-spacer;
+ & > * > th {
+ font-weight: normal;
+ }
+ & > * > vn-th[field] {
+ position: relative;
+ overflow: visible;
+ cursor: pointer;
- & > * > vn-th[field] {
- position: relative;
- overflow: visible;
- cursor: pointer;
-
- &.active > :after {
- color: $color-font;
- opacity: 1;
- }
- &.desc > :after {
- content: 'arrow_drop_down';
- }
- &.asc > :after {
- content: 'arrow_drop_up';
- }
- & > :after {
- font-family: 'Material Icons';
- content: 'arrow_drop_down';
- position: absolute;
- color: $color-spacer;
- opacity: 0;
- }
- &:hover > :after {
- opacity: 1;
- }
+ &.active > :after {
+ color: $color-font;
+ opacity: 1;
+ }
+ &.desc > :after {
+ content: 'arrow_drop_down';
+ }
+ &.asc > :after {
+ content: 'arrow_drop_up';
+ }
+ & > :after {
+ font-family: 'Material Icons';
+ content: 'arrow_drop_down';
+ position: absolute;
+ color: $color-spacer;
+ opacity: 0;
+ }
+ &:hover > :after {
+ opacity: 1;
}
}
- & > vn-tbody {
- display: table-row-group;
- }
- & > vn-tfoot {
- border-top: .15em solid $color-spacer;
- display: table-footer-group
- }
- & > * > vn-tr,
- & > * > a.vn-tr {
+ }
+ & > vn-tbody,
+ & > tbody {
+ display: table-row-group;
+ }
+ & > vn-tfoot,
+ & > tfoot {
+ border-top: .15em solid $color-spacer;
+ display: table-footer-group
+ }
+ & > * > vn-tr,
+ & > * > a.vn-tr,
+ & > * > tr {
+ display: table-row;
+ height: 3em;
+ }
+ vn-thead, vn-tbody, vn-tfoot,
+ thead, tbody, tfoot {
+ & > * {
display: table-row;
- height: 3em;
- }
- vn-thead, vn-tbody, vn-tfoot {
- & > * {
- display: table-row;
- & > vn-th {
- color: $color-font-light;
- padding-top: 1em;
- padding-bottom: .8em;
+ & > vn-th,
+ & > th {
+ color: $color-font-light;
+ padding-top: 1em;
+ padding-bottom: .8em;
+ }
+ & > vn-th,
+ & > vn-td,
+ & > th,
+ & > td {
+ overflow: hidden;
+ }
+ & > vn-th,
+ & > vn-td,
+ & > vn-td-editable,
+ & > th,
+ & > td {
+ vertical-align: middle;
+ display: table-cell;
+ text-align: left;
+ padding: .6em .5em;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 5em;
+
+ &[number] {
+ text-align: right;
+ width: 6em;
}
- & > vn-th,
- & > vn-td {
- overflow: hidden;
+ &[center] {
+ text-align: center;
}
- & > vn-th,
- & > vn-td,
- & > vn-td-editable {
- vertical-align: middle;
- display: table-cell;
- text-align: left;
- padding: .6em .5em;
- white-space: nowrap;
- text-overflow: ellipsis;
- max-width: 5em;
-
- &[number] {
- text-align: right;
- width: 6em;
- }
- &[center] {
- text-align: center;
- }
- &[shrink] {
- width: 1px;
- text-align: center;
- }
- &[expand] {
- max-width: 10em;
- min-width: 0;
- }
- vn-icon.bright, i.bright {
- color: #f7931e;
- }
+ &[shrink] {
+ width: 1px;
+ text-align: center;
}
- & > :last-child {
- padding-right: 1.4em;
+ &[expand] {
+ max-width: 10em;
+ min-width: 0;
}
- & > :first-child {
- padding-left: 1.4em;
+ vn-icon.bright, i.bright {
+ color: #f7931e;
}
}
- & > a.vn-tr {
- color: inherit;
+ & > :last-child {
+ padding-right: 1.4em;
+ }
+ & > :first-child {
+ padding-left: 1.4em;
}
}
- vn-tbody > * {
- border-bottom: .1em solid $color-spacer-light;
+ & > a.vn-tr {
+ color: inherit;
+ }
+ }
+ vn-tbody > *,
+ tbody > * {
+ border-bottom: .1em solid $color-spacer-light;
- &:last-child {
- border-bottom: none;
- }
- &.clickable {
- @extend %clickable;
- }
- & > vn-td .chip {
+ &:last-child {
+ border-bottom: none;
+ }
+ &.clickable {
+ @extend %clickable;
+ }
+ & > vn-td,
+ & > td {
+ .chip {
padding: .3em;
- border-radius: .3em
- }
-
- & > vn-td .chip.notice {
+ border-radius: .3em;
color: $color-font-bg;
- background-color: $color-notice-medium
- }
- & > vn-td .chip.success {
- color: $color-font-bg;
- background-color: $color-success-medium
+ &.notice {
+ background-color: $color-notice-medium
+ }
+ &.success {
+ background-color: $color-success-medium;
+ }
+ &.warning {
+ background-color: $color-main-medium;
+ }
+ &.alert {
+ background-color: $color-alert-medium;
+ }
+ &.message {
+ color: $color-font-dark;
+ background-color: $color-bg-dark
+ }
}
-
- & > vn-td .chip.warning {
- color: $color-font-bg;
- background-color: $color-main-medium;
- }
-
- & > vn-td .chip.alert {
- color: $color-font-bg;
- background-color: $color-alert-medium;
- }
-
- & > vn-td .chip.message {
- color: $color-font-dark;
- background-color: $color-bg-dark
- }
-
- & > vn-td vn-icon-menu {
+ vn-icon-menu {
display: inline-block;
color: $color-main;
padding: .25em
}
+ }
+ & > [actions] {
+ width: 1px;
- & > [actions] {
- width: 1px;
-
- & > * {
- vertical-align: middle;
- }
+ & > * {
+ vertical-align: middle;
}
}
- & > vn-empty-rows {
- display: table-caption;
- caption-side: bottom;
- text-align: center;
- padding: 1.5em;
- width: 100%;
- box-sizing: border-box;
- }
- }
- vn-autocomplete {
- div.mdl-textfield {
- padding: 0 !important;
- }
- label.mdl-textfield__label:after {
- bottom: 0;
- }
- div.icons {
- display: none !important;
- }
}
vn-textfield {
float: right;
diff --git a/front/core/components/textarea/index.js b/front/core/components/textarea/index.js
new file mode 100644
index 000000000..fc0834566
--- /dev/null
+++ b/front/core/components/textarea/index.js
@@ -0,0 +1,26 @@
+import ngModule from '../../module';
+import Field from '../field';
+
+export default class Textarea extends Field {
+ constructor($element, $scope, $compile) {
+ super($element, $scope, $compile);
+
+ let html = `
`;
+ this.input = $compile(html)($scope)[0];
+ }
+
+ set rows(value) {
+ this.input.rows = typeof value == 'number' && value > 0 ? value : 3;
+ }
+
+ get rows() {
+ return this.input.rows;
+ }
+}
+
+ngModule.vnComponent('vnTextarea', {
+ controller: Textarea,
+ bindings: {
+ rows: ''
+ }
+});
diff --git a/front/core/components/textarea/index.spec.js b/front/core/components/textarea/index.spec.js
new file mode 100644
index 000000000..1d246cfe0
--- /dev/null
+++ b/front/core/components/textarea/index.spec.js
@@ -0,0 +1,39 @@
+import './index.js';
+
+describe('Component vnTextarea', () => {
+ let $element;
+ let $ctrl;
+
+ beforeEach(angular.mock.module('vnCore', $translateProvider => {
+ $translateProvider.translations('en', {});
+ }));
+
+ beforeEach(angular.mock.inject(($compile, $rootScope) => {
+ $element = $compile(`
`)($rootScope);
+ $ctrl = $element.controller('vnTextarea');
+ }));
+
+ afterEach(() => {
+ $element.remove();
+ });
+
+ describe('rows() setter', () => {
+ it(`should set rows property of the element to the given value if it's a valid number`, () => {
+ $ctrl.rows = 27;
+
+ expect($ctrl.rows).toEqual(27);
+ });
+
+ it(`should set rows property of the element to 3 if the given value if it's null`, () => {
+ $ctrl.rows = null;
+
+ expect($ctrl.rows).toEqual(3);
+ });
+
+ it(`should set rows property of the element to 3 if the given value if it's not a valid number`, () => {
+ $ctrl.rows = 'a';
+
+ expect($ctrl.rows).toEqual(3);
+ });
+ });
+});
diff --git a/front/core/components/textarea/style.scss b/front/core/components/textarea/style.scss
deleted file mode 100644
index 2f6b521b3..000000000
--- a/front/core/components/textarea/style.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-@import "variables";
-
-vn-textarea {
- & > .mdl-textfield {
- width: initial;
- display: block;
- }
- label {
- position: absolute;
- bottom: 0;
- pointer-events: none;
- color: $color-font-secondary;
- transition-duration: .2s;
- transition-timing-function: cubic-bezier(.4,0,.2,1);
- }
- & textarea.ng-not-empty+label.placeholder{
- top: 5px;
- color: $color-main;
- padding: 0;
- font-size: 12px;
- visibility: visible!important;
- content: normal!important;
- }
-}
\ No newline at end of file
diff --git a/front/core/components/textarea/textarea.html b/front/core/components/textarea/textarea.html
deleted file mode 100644
index 6efe39f7a..000000000
--- a/front/core/components/textarea/textarea.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- {{$ctrl.label}}
-
diff --git a/front/core/components/textarea/textarea.js b/front/core/components/textarea/textarea.js
deleted file mode 100644
index 9b30546c3..000000000
--- a/front/core/components/textarea/textarea.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import ngModule from '../../module';
-import './style.scss';
-
-export default class Textarea {
- constructor($element, $scope, $attrs, vnTemplate) {
- this.$ = $scope;
- this.$attrs = $attrs;
- this.element = $element;
- vnTemplate.normalizeInputAttrs($attrs);
- this.textarea = this.element[0].querySelector('textarea');
- this.rows = null;
- }
- set model(value) {
- this._model = value;
- this.mdlUpdate();
- }
- set rows(value) {
- this.textarea.rows = value ? value : 3;
- }
- get model() {
- return this._model;
- }
- mdlUpdate() {
- let mdlField = this.element[0].firstChild.MaterialTextfield;
- if (mdlField)
- mdlField.updateClasses_();
- componentHandler.upgradeElement(this.element[0].firstChild);
- }
-}
-Textarea.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
-
-ngModule.component('vnTextarea', {
- template: require('./textarea.html'),
- controller: Textarea,
- bindings: {
- model: '=model',
- label: '@?',
- rows: '@?',
- name: '@?',
- disabled: '',
- rule: '@?'
- }
-});
diff --git a/front/core/components/textarea/textarea.spec.js b/front/core/components/textarea/textarea.spec.js
deleted file mode 100644
index d5a274e72..000000000
--- a/front/core/components/textarea/textarea.spec.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import './textarea.js';
-
-describe('Component vnTextarea', () => {
- let $scope;
- let $attrs;
- let $element;
- let controller;
-
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
-
- beforeEach(angular.mock.inject(($componentController, $rootScope) => {
- $scope = $rootScope.$new();
- $attrs = {field: '$ctrl.claim.observation'};
- $element = angular.element('
');
- $element[0].firstChild.MaterialTextfield = {updateClasses_: () => {}};
- controller = $componentController('vnTextarea', {$scope, $element, $attrs});
- }));
-
- describe('model() setter', () => {
- it(`should set model and call mdlUpdate`, () => {
- spyOn(controller, 'mdlUpdate');
- let model = 'model';
- controller.model = model;
-
- expect(controller._model).toEqual(model);
- expect(controller.mdlUpdate).toHaveBeenCalledWith();
- });
- });
-
- describe('rows() setter', () => {
- it(`should set rows property of the element to the given value if its not null`, () => {
- controller.rows = 27;
-
- expect(controller.textarea.rows).toEqual(27);
- });
-
- it(`should set rows property of the element to 3 if the given value if its null`, () => {
- controller.rows = null;
-
- expect(controller.textarea.rows).toEqual(3);
- });
- });
-
- describe('mdlUpdate()', () => {
- it(`should should call mdlField.updateClasses if theres an mdlField defined and call upgradeElement with the textarea element`, () => {
- spyOn($element[0].firstChild.MaterialTextfield, 'updateClasses_');
- spyOn(componentHandler, 'upgradeElement');
- controller.mdlUpdate();
-
- expect($element[0].firstChild.MaterialTextfield.updateClasses_).toHaveBeenCalledWith();
- expect(componentHandler.upgradeElement).toHaveBeenCalledWith(controller.element[0].firstChild);
- });
- });
-});
diff --git a/front/core/components/textfield/style.scss b/front/core/components/textfield/style.scss
deleted file mode 100644
index 96ea21345..000000000
--- a/front/core/components/textfield/style.scss
+++ /dev/null
@@ -1,162 +0,0 @@
-@import "variables";
-
-vn-textfield {
- margin: 20px 0;
- display: inline-block;
- width: 100%;
-
- & > .container {
- width: 100%;
- position: relative;
- padding-bottom: 2px;
- display: flex;
-
- & > .textField {
- width: 100%;
- display: flex;
- align-items: center;
- position: relative;
- padding-top: 4px;
- }
- }
-
- .leftIcons, .rightIcons, .suffix {
- display: flex;
- color: $color-font-secondary;
-
- .material-icons {
- font-size: 20px !important
- }
- }
-
- .suffix vn-icon-button {
- padding: 0
- }
-
- t-left-icons {
- padding-right: 0.5em
- }
-
- t-right-icons {
- padding-left: 0.5em
- }
-
- .infix {
- position: relative;
- display: block;
- flex: auto;
- width: 100%;
- min-width: 0;
- }
- i.clear {
- visibility: hidden;
- cursor: pointer;
- outline: 0;
-
- &:hover {
- color: #222;
- }
- }
- &:hover i.clear {
- visibility: visible;
- }
- i.visible {
- visibility: visible;
- }
- label {
- position: absolute;
- bottom: 0;
- left: 0;
- padding: 4px 0!important;
- pointer-events: none;
- color: $color-font-secondary;
- transition-duration: .2s;
- transition-timing-function: cubic-bezier(.4,0,.2,1);
- }
- &.not-empty label{
- bottom: 24px;
- color: $color-main;
- padding: 0;
- font-size: 12px;
- }
- input {
- outline: none;
- border: none;
- font-family: "Helvetica", "Arial", sans-serif;
- display: block;
- font-size: 16px;
- width: 100%;
- background: 0 0;
- color: inherit;
- padding: 4px;
- box-sizing: border-box;
- border-bottom: 0!important;
-
- &[type=number] {
- -moz-appearance: textfield;
- &::-webkit-outer-spin-button,
- &::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
- }
- }
- &:invalid {
- box-shadow: none;
- }
- }
- .underline {
- position: absolute;
- bottom: 0;
- height: 1px;
- content: ' ';
- pointer-events: none;
- width: 100%;
- background-color: $color-input-underline;
- }
- .selected.underline {
- background-color: $color-main;
- height: 2px;
- left: 50%;
- width: 0px !important;
- transition-duration: 0.2s;
- transition-timing-function: cubic-bezier(.4,0,.2,1);
- }
-
- div.selected {
- &.container{
- border-bottom: 0px;
- }
- label {
- bottom: 24px;
- color: $color-main;
- font-size: 12px;
- }
- .selected.underline{
- left: 0;
- width: 100%!important;
- }
- }
- & > div.container > div.textField > div.infix.invalid {
- @extend div.selected;
-
- & > span.mdl-textfield__error {
- visibility: visible;
- }
- & > label {
- color: #d50000;
- }
- }
- .infix.invalid + .underline {
- background-color: #d50000;
- }
-
- label span:nth-child(2) {
- color: $color-alert
- }
-}
-
-vn-table {
- vn-textfield {
- margin: 0;
- }
-}
\ No newline at end of file
diff --git a/front/core/components/textfield/textfield.html b/front/core/components/textfield/textfield.html
deleted file mode 100644
index 066ecfa56..000000000
--- a/front/core/components/textfield/textfield.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
- {{::$ctrl.label}}
- *
-
-
-
-
-
-
- clear
-
-
- info_outline
-
-
-
-
-
diff --git a/front/core/components/textfield/textfield.js b/front/core/components/textfield/textfield.js
index d4bb945ef..f166dae1c 100644
--- a/front/core/components/textfield/textfield.js
+++ b/front/core/components/textfield/textfield.js
@@ -1,94 +1,13 @@
import ngModule from '../../module';
-import Input from '../../lib/input';
-import './style.scss';
+import Field from '../field';
-export default class Textfield extends Input {
- constructor($element, $scope, $attrs, vnTemplate) {
- super($element, $scope);
- vnTemplate.normalizeInputAttrs($attrs);
- this._value = null;
- this.type = $attrs.type;
- this.showActions = false;
- this.hasInfo = Boolean($attrs.info);
- this.hasFocus = false;
- this.hasMouseIn = false;
-
- this.input.addEventListener('keyup', e => {
- if (e.defaultPrevented || e.key != 'Escape')
- return;
-
- this.value = this.oldValue;
- this.cancelled = true;
- e.preventDefault();
- });
- this.input.addEventListener('change', e => {
- if (this.onChange && !this.cancelled && (this.oldValue != this.value)) {
- this.onChange();
- this.saveOldValue();
- } else
- this.cancelled = false;
- });
- }
-
- $postLink() {
- this.saveOldValue();
- }
-
- saveOldValue() {
- this.oldValue = this.value;
- }
-
- set value(value) {
- this._value = (value === undefined || value === '') ? null : value;
- this.input.value = this._value;
- this.hasValue = this._value != null;
- this.element.classList.toggle('not-empty', this.hasValue);
- }
-
- get value() {
- return this._value;
- }
-
- set type(value) {
- this._type = value || 'text';
- }
-
- get type() {
- return this._type;
- }
-
- set vnTabIndex(value) {
- this.input.tabindex = value;
- }
-
- clear() {
- this.saveOldValue();
- this.value = null;
- if (this.onClear) this.onClear();
- this.input.focus();
+export default class Textfield extends Field {
+ constructor($element, $scope, $compile) {
+ super($element, $scope, $compile);
+ this.buildInput('text');
}
}
-Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
-ngModule.component('vnTextfield', {
- template: require('./textfield.html'),
- transclude: {
- leftIcons: '?tLeftIcons',
- rightIcons: '?tRightIcons'
- },
- controller: Textfield,
- bindings: {
- value: '=model',
- label: '@?',
- name: '@?',
- disabled: '',
- required: '@?',
- readonly: '',
- rule: '@?',
- type: '@?',
- vnTabIndex: '@?',
- onChange: '&',
- onClear: '&',
- info: '@?'
- }
+ngModule.vnComponent('vnTextfield', {
+ controller: Textfield
});
diff --git a/front/core/components/textfield/textfield.spec.js b/front/core/components/textfield/textfield.spec.js
deleted file mode 100644
index bd44d11e5..000000000
--- a/front/core/components/textfield/textfield.spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import './textfield.js';
-
-describe('Component vnTextfield', () => {
- let $scope;
- let $attrs;
- let $timeout;
- let $element;
- let controller;
-
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
-
- beforeEach(angular.mock.inject(($componentController, $rootScope) => {
- $scope = $rootScope.$new();
- $attrs = {field: '$ctrl.client.phone'};
- $element = angular.element('
');
- controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout, $transclude: () => {}});
- }));
-
- describe('saveOldValue()', () => {
- it(`should equal oldValue property to the actual input value`, () => {
- controller.value = 'pepino';
- controller.saveOldValue();
-
- expect(controller.oldValue).toEqual('pepino');
- });
- });
-
- describe('value() setter', () => {
- it(`should set _value, input.value and hasValue properties to null, '' and false`, () => {
- let testValue = '';
- controller.value = testValue;
-
- expect(controller._value).toEqual(null);
- expect(controller.input.value).toEqual(testValue);
- expect(controller.hasValue).toEqual(Boolean(testValue));
- });
-
- it(`should set _value, input.value and hasValue propertiest to test, test and true`, () => {
- let testValue = 'test';
- controller.value = testValue;
-
- expect(controller._value).toEqual(testValue);
- expect(controller.input.value).toEqual(testValue);
- expect(controller.hasValue).toEqual(Boolean(testValue));
- });
-
- it(`should add the class not-empty to the div`, () => {
- let testValue = 'test';
- controller.value = testValue;
-
- expect(controller.element.classList).toContain('not-empty');
- });
- });
-
- describe('type() setter', () => {
- it(`should set _type to 'text' if theres not given value`, () => {
- controller.type = null;
-
- expect(controller._type).toEqual('text');
- });
- });
-
- describe('vnTabIndex() setter', () => {
- it(`should set input.tabindex to a given value`, () => {
- controller.vnTabIndex = 9;
-
- expect(controller.input.tabindex).toEqual(9);
- });
- });
-
- describe('clear()', () => {
- it(`should set value property to null, call focus and saveOldValue`, () => {
- spyOn(controller.input, 'focus');
- spyOn(controller, 'saveOldValue');
- controller.clear();
-
- expect(controller.value).toEqual(null);
- expect(controller.saveOldValue).toHaveBeenCalledWith();
- expect(controller.input.focus).toHaveBeenCalledWith();
- });
- });
-});
diff --git a/front/core/components/toggle/index.js b/front/core/components/toggle/index.js
index 7642c8973..d9b4e05bd 100644
--- a/front/core/components/toggle/index.js
+++ b/front/core/components/toggle/index.js
@@ -1,16 +1,13 @@
import ngModule from '../../module';
-import Component from '../../lib/component';
+import FormInput from '../form-input';
import './style.scss';
/**
* Base component with common logic and styles for checkbox and radio button.
*
- * @property {String} label Label to display along the component
- * @property {any} field The value with which the element is linked
* @property {Boolean} checked Whether the checkbox is checked
- * @property {Boolean} disabled Put component in disabled mode
*/
-export default class Toggle extends Component {
+export default class Toggle extends FormInput {
constructor($element, $) {
super($element, $);
@@ -49,14 +46,10 @@ export default class Toggle extends Component {
this.emit('change', {value: this.field});
}
}
-Toggle.$inject = ['$element', '$scope'];
-ngModule.component('vnToggle', {
+ngModule.vnComponent('vnToggle', {
controller: Toggle,
bindings: {
- label: '@?',
- field: '=?',
- disabled: '',
checked: ''
}
});
diff --git a/front/core/components/toggle/style.scss b/front/core/components/toggle/style.scss
index 8671eddec..72f99cb35 100644
--- a/front/core/components/toggle/style.scss
+++ b/front/core/components/toggle/style.scss
@@ -3,7 +3,8 @@
.vn-toggle {
position: relative;
cursor: pointer;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
outline: none;
&.disabled {
@@ -19,8 +20,9 @@
vertical-align: middle;
width: 20px;
height: 20px;
+ min-width: 20px;
margin: 6px 0;
- margin-right: .4em;
+ margin-right: .6em;
border: 2px solid #666;
}
&.checked > .btn {
diff --git a/front/core/components/treeview/child.html b/front/core/components/treeview/child.html
deleted file mode 100644
index bb0bcb569..000000000
--- a/front/core/components/treeview/child.html
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create new one
-
-
-
-
\ No newline at end of file
diff --git a/front/core/components/treeview/child.js b/front/core/components/treeview/child.js
deleted file mode 100644
index 4441963f0..000000000
--- a/front/core/components/treeview/child.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import ngModule from '../../module';
-import Component from '../../lib/component';
-
-class Controller extends Component {
- constructor($element, $scope) {
- super($element, $scope);
- this.$scope = $scope;
- }
-
- toggle(event, item) {
- if (event.defaultPrevented || !item.sons) return;
- event.preventDefault();
- this.treeview.onToggle(item);
- }
-
- select(item, value) {
- this.treeview.onSelection(item, value);
- }
-
- onIconClick(icon, item, parent, index) {
- let parentController = this.parentScope.$ctrl;
- icon.callback.call(parentController, item, parent, index);
- }
-
- onCreate(parent) {
- this.treeview.onCreate(parent);
- }
-
- onDrop(item, dragged, dropped) {
- this.treeview.onDrop(item, dragged, dropped);
- }
-
- get isInsertable() {
- return Array.isArray(this.parent) || this.parent.childs;
- }
-}
-
-ngModule.component('vnTreeviewChild', {
- template: require('./child.html'),
- controller: Controller,
- bindings: {
- items: '<',
- parent: '<',
- icons: '',
- disabled: '',
- selectable: '',
- editable: '',
- draggable: '',
- droppable: '',
- aclRole: '',
- parentScope: '<'
- },
- require: {
- treeview: '^vnTreeview'
- }
-});
diff --git a/front/core/components/treeview/childs.html b/front/core/components/treeview/childs.html
new file mode 100644
index 000000000..2dd7e77ef
--- /dev/null
+++ b/front/core/components/treeview/childs.html
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/front/core/components/treeview/childs.js b/front/core/components/treeview/childs.js
new file mode 100644
index 000000000..a9bd42077
--- /dev/null
+++ b/front/core/components/treeview/childs.js
@@ -0,0 +1,26 @@
+import ngModule from '../../module';
+import Component from '../../lib/component';
+
+class Controller extends Component {
+ onClick(event, item) {
+ if (event.defaultPrevented || !item.sons) return;
+ event.preventDefault();
+ this.treeview.onToggle(item);
+ }
+
+ onDrop(item, dragged, dropped) {
+ this.treeview.onDrop(item, dragged, dropped);
+ }
+}
+
+ngModule.component('vnTreeviewChilds', {
+ template: require('./childs.html'),
+ controller: Controller,
+ bindings: {
+ items: '<',
+ parent: ''
+ },
+ require: {
+ treeview: '^vnTreeview'
+ }
+});
diff --git a/front/core/components/treeview/content.js b/front/core/components/treeview/content.js
new file mode 100644
index 000000000..506117d4f
--- /dev/null
+++ b/front/core/components/treeview/content.js
@@ -0,0 +1,39 @@
+import ngModule from '../../module';
+
+class Controller {
+ constructor($element, $scope, $compile) {
+ this.$element = $element;
+ this.$scope = $scope;
+ this.$compile = $compile;
+ }
+
+ $onInit() {
+ if (this.item.parent) {
+ this.treeview.$transclude(($clone, $scope) => {
+ this.$contentScope = $scope;
+ $scope.item = this.item;
+ this.$element.append($clone);
+ });
+ } else {
+ let template = `
{{$ctrl.treeview.rootLabel}} `;
+ let $clone = this.$compile(template)(this.$scope);
+ this.$element.append($clone);
+ }
+ }
+
+ $onDestroy() {
+ if (this.$contentScope)
+ this.$contentScope.$destroy();
+ }
+}
+Controller.$inject = ['$element', '$scope', '$compile'];
+
+ngModule.component('vnTreeviewContent', {
+ controller: Controller,
+ bindings: {
+ item: '<'
+ },
+ require: {
+ treeview: '^vnTreeview'
+ }
+});
diff --git a/front/core/components/treeview/index.html b/front/core/components/treeview/index.html
index a200c913a..31e6a88a9 100644
--- a/front/core/components/treeview/index.html
+++ b/front/core/components/treeview/index.html
@@ -1,13 +1,3 @@
-
-
+
+
diff --git a/front/core/components/treeview/index.js b/front/core/components/treeview/index.js
index ac4186c58..d9da39215 100644
--- a/front/core/components/treeview/index.js
+++ b/front/core/components/treeview/index.js
@@ -2,73 +2,122 @@ import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
+import './childs';
+import './content';
+
/**
* Treeview
- *
- * @property {String} position The relative position to the parent
*/
export default class Treeview extends Component {
- constructor($element, $scope) {
+ constructor($element, $scope, $transclude) {
super($element, $scope);
- this.$scope = $scope;
- this.data = [];
+ this.$transclude = $transclude;
+ this.readOnly = true;
}
- $onInit() {
- this.refresh();
+ get data() {
+ return this._data;
}
- refresh() {
- this.model.refresh().then(() => {
- this.data = this.model.data;
+ set data(value) {
+ this._data = value;
+
+ const sons = value.length;
+ const rootElement = [{
+ childs: value,
+ active: true,
+ sons: sons
+ }];
+
+ this.setParent(rootElement[0], value);
+
+ this.items = rootElement;
+ }
+
+ fetch() {
+ return this.fetchFunc().then(res =>
+ this.data = res
+ );
+ }
+
+ setParent(parent, childs) {
+ childs.forEach(child => {
+ child.parent = parent;
+
+ if (child.childs)
+ this.setParent(parent, child.childs);
});
}
- /**
- * Emits selection event
- * @param {Object} item - Selected item
- * @param {Boolean} value - Changed value
- */
- onSelection(item, value) {
- this.emit('selection', {item, value});
- }
-
- onCreate(parent) {
- this.emit('create', {parent});
- }
-
onToggle(item) {
if (item.active)
- item.childs = undefined;
- else {
- this.model.applyFilter({}, {parentFk: item.id}).then(() => {
- const newData = this.model.data;
+ this.fold(item);
+ else
+ this.unfold(item);
+ }
- if (item.childs) {
- let childs = item.childs;
- childs.forEach(child => {
- let index = newData.findIndex(newChild => {
- return newChild.id == child.id;
- });
- newData[index] = child;
+ fold(item) {
+ item.childs = undefined;
+ item.active = false;
+ }
+
+ unfold(item) {
+ return this.fetchFunc({$item: item}).then(newData => {
+ this.setParent(item, newData);
+
+ const childs = item.childs;
+ if (childs) {
+ childs.forEach(child => {
+ let index = newData.findIndex(newChild => {
+ return newChild.id == child.id;
});
- }
-
- item.childs = newData.sort((a, b) => {
- if (b.selected !== a.selected) {
- if (a.selected == null)
- return 1;
- if (b.selected == null)
- return -1;
- return b.selected - a.selected;
- }
-
- return a.name.localeCompare(b.name);
+ newData[index] = child;
});
- });
+ }
+
+ if (this.sortFunc) {
+ item.childs = newData.sort((a, b) =>
+ this.sortFunc({$a: a, $b: b})
+ );
+ }
+ }).then(() => item.active = true);
+ }
+
+ onRemove(item) {
+ if (this.removeFunc)
+ this.removeFunc({$item: item});
+ }
+
+ remove(item) {
+ const parent = item.parent;
+ let childs = parent.childs;
+
+ if (!childs) childs = [];
+
+ let index = childs.indexOf(item);
+ childs.splice(index, 1);
+ if (parent) parent.sons--;
+ }
+
+ onCreate(parent) {
+ if (this.createFunc)
+ this.createFunc({$parent: parent});
+ }
+
+ create(item) {
+ const parent = item.parent;
+ let childs = parent.childs;
+ if (!childs) childs = [];
+
+ childs.push(item);
+
+ if (this.sortFunc) {
+ childs = childs.sort((a, b) =>
+ this.sortFunc({$a: a, $b: b})
+ );
}
- item.active = !item.active;
+ if (parent) parent.sons++;
}
onDrop(item, dragged, dropped) {
@@ -76,19 +125,21 @@ export default class Treeview extends Component {
}
}
-Treeview.$inject = ['$element', '$scope'];
+Treeview.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnTreeview', {
template: require('./index.html'),
controller: Treeview,
bindings: {
- model: '<',
- icons: '',
- disabled: '',
- selectable: '',
- editable: '',
+ rootLabel: '@',
+ data: '',
draggable: '',
droppable: '',
- aclRole: '@?'
- }
+ fetchFunc: '&',
+ removeFunc: '&?',
+ createFunc: '&?',
+ sortFunc: '&?',
+ readOnly: ''
+ },
+ transclude: true
});
diff --git a/front/core/components/treeview/style.scss b/front/core/components/treeview/style.scss
index 0ea1074a9..b3724a9f1 100644
--- a/front/core/components/treeview/style.scss
+++ b/front/core/components/treeview/style.scss
@@ -1,9 +1,8 @@
@import "effects";
-vn-treeview {
- vn-treeview-child {
- display: block
- }
+vn-treeview-childs {
+ display: block;
+
ul {
padding: 0;
margin: 0;
@@ -11,37 +10,43 @@ vn-treeview {
li {
list-style: none;
- & > div > .arrow {
- min-width: 24px;
- margin-right: 10px;
- transition: transform 200ms;
- }
- &.expanded > div > .arrow {
- transform: rotate(180deg);
- }
& > .node {
@extend %clickable;
display: flex;
padding: 5px;
align-items: center;
-
- & > vn-check:not(.indeterminate) {
- color: $color-main;
-
- & > .btn {
- border-color: $color-main;
- }
- }
- & > vn-check.checked {
- color: $color-main;
- }
+ }
+
+ & > div > .arrow {
+ min-width: 24px;
+ margin-right: 10px;
+ transition: transform 200ms;
+ }
+
+ & > div.expanded > .arrow {
+ transform: rotate(180deg);
}
ul {
padding-left: 2.2em;
}
}
}
+
vn-icon-button {
- padding: 0
+ display: table-cell;
+ vertical-align: middle;
+ padding: 0;
+ }
+
+ .node > .buttons {
+ display: none
+ }
+
+ .node:hover > .buttons {
+ display: block
}
}
+
+vn-treeview-content {
+ flex-grow: 1
+}
\ No newline at end of file
diff --git a/front/core/components/watcher/watcher.js b/front/core/components/watcher/watcher.js
index a085ee45f..02b92dda6 100644
--- a/front/core/components/watcher/watcher.js
+++ b/front/core/components/watcher/watcher.js
@@ -223,11 +223,8 @@ export default class Watcher extends Component {
}
loadOriginalData() {
- Object.keys(this.data).forEach(key => {
- delete this.data[key];
- });
-
- this.data = Object.assign(this.data, this.orgData);
+ const orgData = JSON.parse(JSON.stringify(this.orgData));
+ this.data = Object.assign(this.data, orgData);
this.setPristine();
}
diff --git a/front/core/directives/index.js b/front/core/directives/index.js
index 08adfac07..05f21b5cd 100644
--- a/front/core/directives/index.js
+++ b/front/core/directives/index.js
@@ -2,7 +2,7 @@ import './id';
import './focus';
import './dialog';
import './popover';
-import './validation';
+import './rule';
import './acl';
import './on-error-src';
import './zoom-image';
diff --git a/front/core/directives/rule.js b/front/core/directives/rule.js
new file mode 100644
index 000000000..85b6041f8
--- /dev/null
+++ b/front/core/directives/rule.js
@@ -0,0 +1,72 @@
+import ngModule from '../module';
+import {validateAll} from '../lib/validator';
+import {firstUpper} from '../lib/string';
+
+directive.$inject = ['$translate', '$window'];
+export function directive($translate, $window) {
+ return {
+ restrict: 'A',
+ link: link,
+ require: {
+ ngModel: 'ngModel',
+ form: '^^?form'
+ }
+ };
+
+ function link($scope, $element, $attrs, $ctrl) {
+ let vnValidations = $window.validations;
+ if (!vnValidations) return;
+
+ if (!/^([A-Z]\w+(\.[a-z]\w*)?)?$/.test($attrs.rule))
+ throw new Error(`rule: Attribute must have this syntax: [ModelName[.fieldName]]`);
+
+ let rule = $attrs.rule.split('.');
+ let modelName = rule.shift();
+ let fieldName = rule.shift();
+
+ let split = $attrs.ngModel.split('.');
+ if (!fieldName) fieldName = split.pop() || null;
+ if (!modelName) modelName = firstUpper(split.pop() || '');
+
+ if (!modelName || !fieldName)
+ throw new Error(`rule: Cannot retrieve model or field attribute`);
+
+ let modelValidations = vnValidations[modelName];
+
+ if (!modelValidations)
+ throw new Error(`rule: Model '${modelName}' doesn't exist`);
+
+ let validations = modelValidations.validations[fieldName];
+ if (!validations || validations.length == 0)
+ return;
+
+ let ngModel = $ctrl.ngModel;
+ let form = $ctrl.form;
+ let field = $element[0].$ctrl;
+ let error;
+
+ function refreshError() {
+ if (!field) return;
+ let canShow = ngModel.$dirty || (form && form.$submitted);
+ field.error = error && canShow ? error.message : null;
+ }
+
+ ngModel.$options.$$options.allowInvalid = true;
+ ngModel.$validators.entity = value => {
+ try {
+ error = null;
+ validateAll($translate, value, validations);
+ } catch (e) {
+ error = e;
+ }
+
+ refreshError();
+ return error == null;
+ };
+
+ if (form)
+ $scope.$watch(form.$submitted, refreshError);
+ }
+}
+ngModule.directive('rule', directive);
+
diff --git a/front/core/directives/specs/rule.spec.js b/front/core/directives/specs/rule.spec.js
new file mode 100644
index 000000000..f3671a5e2
--- /dev/null
+++ b/front/core/directives/specs/rule.spec.js
@@ -0,0 +1,115 @@
+describe('Directive rule', () => {
+ let $scope;
+ let $element;
+ let element;
+
+ beforeEach(angular.mock.module('vnCore', $translateProvider => {
+ $translateProvider.translations('en', {});
+ }));
+
+ function compile(html, value) {
+ inject(($compile, $rootScope, $window) => {
+ $window.validations = {Model:
+ {validations: {field: [{validation: 'absence'}]}}
+ };
+
+ $scope = $rootScope.$new();
+
+ $element = angular.element(html);
+ $compile($element)($scope);
+ element = $element[0];
+
+ $scope.model = {field: value};
+ $scope.$digest();
+ });
+ }
+
+ afterEach(() => {
+ $scope.$destroy();
+ $element.remove();
+ });
+
+ describe('Errors', () => {
+ it(`should throw an error if the rule doesn't have the right syntax`, () => {
+ expect(() => {
+ compile(`
+
+ `);
+ }).toThrow(new Error(`rule: Attribute must have this syntax: [ModelName[.fieldName]]`));
+ });
+
+ it('should throw an error if cannot retrieve model or field', () => {
+ expect(() => {
+ compile(`
+
+ `);
+ }).toThrow(new Error(`rule: Cannot retrieve model or field attribute`));
+ });
+
+ it('should throw an error if the model is not defined', () => {
+ expect(() => {
+ compile(`
+
+ `);
+ }).toThrow(new Error(`rule: Model 'NonExistentModel' doesn't exist`));
+ });
+ });
+
+ describe('Validator extended', () => {
+ let html = `
+
+ `;
+
+ it('should not validate the entity as it has a wrong value', () => {
+ compile(html, 'invalidValue');
+
+ expect(element.classList).toContain('ng-invalid');
+ });
+
+ it('should validate the entity as it has a valid value', () => {
+ compile(html, '');
+
+ expect(element.classList).toContain('ng-valid');
+ });
+ });
+
+ describe('Validator minimal', () => {
+ let html = `
+
+
+ `;
+
+ it('should validate with empty rule and without specifying a parent form', () => {
+ compile(html, 'invalidValue');
+
+ expect(element.classList).toContain('ng-invalid');
+ });
+ });
+});
diff --git a/front/core/directives/specs/validation.spec.js b/front/core/directives/specs/validation.spec.js
deleted file mode 100644
index 9f8567df7..000000000
--- a/front/core/directives/specs/validation.spec.js
+++ /dev/null
@@ -1,186 +0,0 @@
-describe('Directive validation', () => {
- let scope;
- let element;
- let compile;
-
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
-
- compile = (_element, validations, value) => {
- inject(($compile, $rootScope, $window) => {
- $window.validations = validations;
- scope = $rootScope.$new();
- scope.user = {name: value};
- element = angular.element(_element);
- $compile(element)(scope);
- scope.$digest();
- });
- };
-
- it(`should throw an error if the vnValidation doesn't have the right syntax`, () => {
- let html = `
`;
-
- expect(() => {
- compile(html, {});
- }).toThrow(new Error(`vnValidation: Attribute must have this syntax: [entity].[field]`));
- });
-
- it('should throw an error if the window.validations aint defined', () => {
- let html = `
`;
-
- expect(() => {
- compile(html, {});
- }).toThrow(new Error(`vnValidation: Entity 'User' doesn't exist`));
- });
-
- describe('Validator presence()', () => {
- it('should not validate the user name as it is an empty string', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'presence'}]}}};
- compile(html, validations, 'Spiderman');
- scope.user.name = '';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
- });
-
- describe('Validator absence()', () => {
- it('should not validate the entity as it should be an empty string', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'absence'}]}}};
- compile(html, validations, 'Spiderman');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
-
- it('should validate the entity as it is an empty string', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'absence'}]}}};
- compile(html, validations, '');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-valid');
- expect(element[0].classList).not.toContain('ng-invalid');
- });
- });
-
- describe('Validator length()', () => {
- it('should not validate the user name as it should have min length of 15', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'length', min: 10, max: 50, is: 15}]}}};
- compile(html, validations, 'fifteen!');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
-
- it('should validate the user name as it has length of 15', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'length', min: 10, max: 50, is: 15}]}}};
- compile(html, validations, 'fifteen length!');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-valid');
- expect(element[0].classList).not.toContain('ng-invalid');
- });
-
- it('should not validate the user name as it should have min length of 10', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'length', min: 10}]}}};
- compile(html, validations, 'shortname');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
-
- it('should validate the user name as its length is greater then the minimum', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'length', min: 10}]}}};
- compile(html, validations, 'verylongname');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-valid');
- expect(element[0].classList).not.toContain('ng-invalid');
- });
-
- it('should not validate the user name as its length is greater then the maximum', () => {
- let html = `
`;
- let validations = {User: {validations: {name: [{validation: 'length', max: 10}]}}};
- compile(html, validations, 'toolongname');
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
- });
-
- describe('Validator numericality()', () => {
- it('should not validate the phone number as it should a integer', () => {
- let html = `
`;
- let validations = {User: {validations: {phone: [{validation: 'numericality', is: 'what is this?'}]}}};
- compile(html, validations, 'spiderman');
- scope.user.phone = 'this is not a phone number!';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
-
- it('should validate the phone number as it an integer', () => {
- let html = `
`;
- let validations = {User: {validations: {phone: [{validation: 'numericality', is: 'what is this?'}]}}};
- compile(html, validations, 'spiderman');
- scope.user.phone = '555555555';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-valid');
- expect(element[0].classList).not.toContain('ng-invalid');
- });
- });
-
- describe('Validator inclusion()', () => {
- it('should not validate the phone number as it is not an integer', () => {
- let html = `
`;
- let validations = {User: {validations: {phone: [{validation: 'inclusion'}]}}};
- compile(html, validations, 'spiderman');
- scope.user.phone = 'this is not a phone number!';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
- });
-
- describe('Validator exclusion()', () => {
- it('should validate the phone number as it is an integer', () => {
- let html = `
`;
- let validations = {User: {validations: {phone: [{validation: 'exclusion'}]}}};
- compile(html, validations, 'spiderman');
- scope.user.phone = '555555555';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-valid');
- expect(element[0].classList).not.toContain('ng-invalid');
- });
- });
-
- describe('Validator format()', () => {
- it('should not validate the email number as it doesnt contain @', () => {
- let html = `
`;
- let validations = {User: {validations: {email: [{validation: 'format', with: '@'}]}}};
- compile(html, validations, 'spiderman');
- scope.user.email = 'userverdnatura.es';
- scope.$digest();
-
- expect(element[0].classList).toContain('ng-invalid');
- expect(element[0].classList).not.toContain('ng-valid');
- });
- });
-});
diff --git a/front/core/directives/uvc.html b/front/core/directives/uvc.html
index 907ea6f2a..49b59b406 100644
--- a/front/core/directives/uvc.html
+++ b/front/core/directives/uvc.html
@@ -15,7 +15,7 @@
+ ng-model="tableConfiguration.configuration[field]">
diff --git a/front/core/directives/validation.js b/front/core/directives/validation.js
deleted file mode 100644
index d234f09bf..000000000
--- a/front/core/directives/validation.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import ngModule from '../module';
-import {validateAll} from '../lib/validator';
-import {firstUpper} from '../lib/string';
-
-directive.$inject = ['$interpolate', '$compile', '$translate', '$window'];
-export function directive(interpolate, compile, $translate, $window) {
- return {
- restrict: 'A',
- require: ['ngModel', '^^?form'],
- link: link
- };
-
- function link(scope, element, attrs, ctrl) {
- let vnValidations = $window.validations;
-
- if (!attrs.vnValidation || !vnValidations)
- return;
-
- let split = attrs.vnValidation.split('.');
-
- if (split.length !== 2)
- throw new Error(`vnValidation: Attribute must have this syntax: [entity].[field]`);
-
- let entityName = firstUpper(split[0]);
- let fieldName = split[1];
- let entity = vnValidations[entityName];
-
- if (!entity)
- throw new Error(`vnValidation: Entity '${entityName}' doesn't exist`);
-
- let validations = entity.validations[fieldName];
- if (!validations || validations.length == 0)
- return;
-
- let ngModel = ctrl[0];
- let form = ctrl[1];
- let errorSpan = angular.element(' ');
- let errorMsg;
- let errorShown = false;
-
- ngModel.$options.$$options.allowInvalid = true;
- ngModel.$validators.entity = value => {
- try {
- validateAll($translate, value, validations);
- return true;
- } catch (e) {
- errorMsg = e.message;
- if (errorShown) changeError();
- return false;
- }
- };
-
- scope.$watch(() => {
- return (form.$submitted || ngModel.$dirty) && ngModel.$invalid;
- }, value => {
- let parent = element.parent();
-
- if (value) {
- changeError();
- parent.addClass('invalid');
- element.after(errorSpan);
- } else if (errorShown) {
- parent.removeClass('invalid');
- parent.removeAttr('title');
- errorSpan.remove();
- errorSpan.empty();
- }
-
- errorShown = value;
- });
-
- function changeError() {
- let parent = element.parent();
- errorSpan.text(errorMsg);
- parent.attr('title', errorMsg);
- }
- }
-}
-ngModule.directive('vnValidation', directive);
-
diff --git a/front/core/lib/index.js b/front/core/lib/index.js
index dac8e460d..2682dfdf9 100644
--- a/front/core/lib/index.js
+++ b/front/core/lib/index.js
@@ -1,7 +1,5 @@
import './module-loader';
import './crud';
-import './acl-service';
-import './template';
import './copy';
import './equals';
import './modified';
diff --git a/front/core/lib/input.js b/front/core/lib/input.js
index 44bc890d0..b29d3fe37 100644
--- a/front/core/lib/input.js
+++ b/front/core/lib/input.js
@@ -30,79 +30,13 @@ export default class Input extends Component {
if (this.mdlElement)
this.mdlElement.updateClasses_();
}
-
- get validationError() {
- return this.input.validationMessage;
- }
-
- /**
- * Validates a valid input value
- *
- * @return {Boolean} - True if has a valid value
- */
- hasValidValue() {
- return this.input.checkValidity();
- }
-
- /**
- * Changes the input element
- * if has a validation error
- */
- validateValue() {
- if (!this.hasValidValue()) {
- this.hideError();
- this.showError();
- } else
- this.hideError();
- }
-
- /**
- * Shows the input validation error
- */
- showError() {
- const infixElement = this.element.querySelector('.infix');
- const infixClassList = infixElement.classList;
-
- const errorSpan = document.createElement('span');
- errorSpan.className = 'mdl-textfield__error';
-
- const errorText = document.createTextNode(this.validationError);
-
- errorSpan.append(errorText);
- infixElement.append(errorSpan);
-
- infixClassList.add('validated', 'invalid');
- }
-
- /**
- * Hides the input validation error
- */
- hideError() {
- const infixElement = this.element.querySelector('.infix');
- const infixClassList = infixElement.classList;
- const errorElement = this.element.querySelector('.infix span.mdl-textfield__error');
-
- if (errorElement)
- errorElement.remove();
-
- infixClassList.remove('validated', 'invalid');
- }
}
Input.$inject = ['$element', '$scope'];
-export const component = {
- transclude: {
- leftIcons: '?tLeftIcons',
- rightIcons: '?tRightIcons'
- },
+export const $options = {
bindings: {
label: '@?',
disabled: '',
- readonly: '',
- rule: '@?',
- value: '=model',
- vnTabIndex: '@?',
- onChange: '&',
- onClear: '&'
+ readonly: ''
}
};
diff --git a/front/core/lib/specs/validator.spec.js b/front/core/lib/specs/validator.spec.js
new file mode 100644
index 000000000..c61faa793
--- /dev/null
+++ b/front/core/lib/specs/validator.spec.js
@@ -0,0 +1,186 @@
+import {validate} from '../validator.js';
+
+describe('Validator', () => {
+ let $translate;
+
+ beforeEach(angular.mock.module('vnCore', $translateProvider => {
+ $translateProvider.translations('en', {});
+ }));
+
+ beforeEach(inject(_$translate_ => {
+ $translate = _$translate_;
+ }));
+
+ describe('presence', () => {
+ let conf = {validation: 'presence'};
+
+ it('should not validate the value as it should be defined', () => {
+ expect(() => {
+ validate($translate, '', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should be defined', () => {
+ expect(() => {
+ validate($translate, 'aDefinedValue', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('absence', () => {
+ let conf = {validation: 'absence'};
+
+ it('should not validate the value as it should be undefined', () => {
+ expect(() => {
+ validate($translate, 'aDefinedValue', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should be undefined', () => {
+ expect(() => {
+ validate($translate, '', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('length', () => {
+ it('should not validate the value as it should have an specific valid length', () => {
+ let conf = {
+ validation: 'length',
+ is: 25
+ };
+
+ expect(() => {
+ validate($translate, 'invalidSpecificLengthString', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should have an specific valid length', () => {
+ let conf = {
+ validation: 'length',
+ is: 25
+ };
+
+ expect(() => {
+ validate($translate, 'validSpecificLengthString', conf);
+ }).not.toThrowError();
+ });
+
+ it('should not validate the value as it should have a min length', () => {
+ let conf = {
+ validation: 'length',
+ min: 10
+ };
+
+ expect(() => {
+ validate($translate, 'shortName', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should have a min length', () => {
+ let conf = {
+ validation: 'length',
+ min: 10
+ };
+
+ expect(() => {
+ validate($translate, 'veryLongName', conf);
+ }).not.toThrowError();
+ });
+
+ it('should not validate the value as it should be smaller than the maximum', () => {
+ let conf = {
+ validation: 'length',
+ max: 20
+ };
+
+ expect(() => {
+ validate($translate, 'chainThatExceedsMaxLength', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should be smaller than the maximum', () => {
+ let conf = {
+ validation: 'length',
+ max: 20
+ };
+
+ expect(() => {
+ validate($translate, 'shortString', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('numericality', () => {
+ let conf = {validation: 'numericality'};
+
+ it('should not validate the value as it should be an integer', () => {
+ expect(() => {
+ validate($translate, 'notANumber', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should be an integer', () => {
+ expect(() => {
+ validate($translate, '123456789', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('inclusion', () => {
+ let conf = {
+ validation: 'inclusion',
+ in: ['firstValue', 'seekValue', 'lastValue']
+ };
+
+ it('should not validate the value as it should be in array', () => {
+ expect(() => {
+ validate($translate, 'notIncludedValue', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should be in array', () => {
+ expect(() => {
+ validate($translate, 'seekValue', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('exclusion', () => {
+ let conf = {
+ validation: 'exclusion',
+ in: ['firstValue', 'seekValue', 'lastValue']
+ };
+
+ it('should not validate the value as it should not be in array', () => {
+ expect(() => {
+ validate($translate, 'seekValue', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should not be in array', () => {
+ expect(() => {
+ validate($translate, 'notIncludedValue', conf);
+ }).not.toThrowError();
+ });
+ });
+
+ describe('format', () => {
+ let conf = {
+ validation: 'format',
+ with: /[a-z-]+@[a-z-]+\.[a-z]+/
+ };
+
+ it('should not validate the value as it should match regexp', () => {
+ expect(() => {
+ validate($translate, 'wrongValue', conf);
+ }).toThrowError();
+ });
+
+ it('should validate the value as it should match regexp', () => {
+ expect(() => {
+ validate($translate, 'valid-value@domain.com', conf);
+ }).not.toThrowError();
+ });
+ });
+});
diff --git a/front/core/lib/string.js b/front/core/lib/string.js
index 920640a95..582485cb1 100644
--- a/front/core/lib/string.js
+++ b/front/core/lib/string.js
@@ -6,7 +6,7 @@
* @return {String} The camelized string
*/
export function kebabToCamel(str) {
- var camelCased = str.replace(/-([a-z])/g, function(g) {
+ let camelCased = str.replace(/-([a-z])/g, function(g) {
return g[1].toUpperCase();
});
return camelCased;
diff --git a/front/core/lib/template.js b/front/core/lib/template.js
deleted file mode 100644
index ae2f1b188..000000000
--- a/front/core/lib/template.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import ngModule from '../module';
-
-export default class Template {
- getNormalized(template, $attrs, defaults) {
- this.normalizeInputAttrs($attrs);
- return this.get(template, $attrs, defaults);
- }
- normalizeInputAttrs($attrs) {
- const field = $attrs.field || $attrs.model;
- const split = field.split('.');
- const len = split.length;
-
- let i = len - 1;
- const fieldName = split[i--];
- const entity = i >= 0 ? split[i--] : 'model';
- const ctrl = i >= 0 ? split[i--] : '$ctrl';
-
- if ($attrs.field) {
- if (len == 0)
- throw new Error(`Attribute 'field' can not be empty`);
- if (len > 3)
- throw new Error(`Attribute 'field' must have this syntax: [ctrl].[entity].[field]`);
-
- if ($attrs.model === undefined)
- $attrs.model = `${ctrl}.${entity}.${fieldName}`;
- if ($attrs.rule === undefined && len >= 2)
- $attrs.rule = `${entity}.${fieldName}`;
- if ($attrs.label === undefined && len >= 2)
- $attrs.label = `${entity}.${fieldName}`;
- }
-
- if ($attrs.name === undefined)
- $attrs.name = fieldName;
-
- if ($attrs.focus !== undefined)
- $attrs.focus = 'vn-focus';
- }
-}
-
-ngModule.service('vnTemplate', Template);
diff --git a/front/core/module.js b/front/core/module.js
index 96685092d..0ce7f7619 100644
--- a/front/core/module.js
+++ b/front/core/module.js
@@ -4,6 +4,43 @@ const ngModule = ng.module('vnCore', ngDeps);
ngModule.constant('moment', require('moment-timezone'));
export default ngModule;
+/**
+ * Acts like native Module.component() function but merging component options
+ * with parent component options. This method establishes the $options property
+ * to the component controller class with the merged component options. To
+ * retrieve parent options, it reads the same property of the parent class, so
+ * for the parent options to be copied, it must have been declared using this
+ * same function. If any of the options (template, transclude, bindings ...) is
+ * redeclared in the child component, it has preference.
+ *
+ * @param {String} name Coponent name in camelCase
+ * @param {Object} options The component options
+ * @return {angularModule} The same angular module
+ */
+ngModule.vnComponent = function(name, options) {
+ let controller = options.controller;
+ let parent = Object.getPrototypeOf(controller);
+ let parentOptions = parent.$options || {};
+
+ let mergedOptions = Object.assign({},
+ parentOptions,
+ options,
+ {
+ transclude: Object.assign({},
+ parentOptions.transclude,
+ options.transclude
+ ),
+ bindings: Object.assign({},
+ parentOptions.bindings,
+ options.bindings
+ )
+ }
+ );
+ controller.$options = mergedOptions;
+
+ return this.component(name, mergedOptions);
+};
+
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
export function config($translateProvider, $translatePartialLoaderProvider) {
$translatePartialLoaderProvider.addPart('core');
diff --git a/front/core/lib/acl-service.js b/front/core/services/acl-service.js
similarity index 100%
rename from front/core/lib/acl-service.js
rename to front/core/services/acl-service.js
diff --git a/front/core/services/config.js b/front/core/services/config.js
new file mode 100644
index 000000000..e2d3035fb
--- /dev/null
+++ b/front/core/services/config.js
@@ -0,0 +1,79 @@
+import ngModule from '../module';
+
+/**
+ * Saves and loads the application configuration.
+ */
+export default class Config {
+ constructor($http, vnApp, $translate, $window) {
+ Object.assign(this, {
+ $http,
+ vnApp,
+ $translate,
+ storage: $window.localStorage,
+ user: {},
+ local: {}
+ });
+
+ this.params = [
+ 'warehouseFk',
+ 'companyFk',
+ 'bankFk'
+ ];
+ }
+
+ initialize() {
+ for (let param of this.params)
+ this.local[param] = this.getItem(param);
+
+ return this.$http.get('api/UserConfigs/getUserConfig')
+ .then(res => {
+ for (let param of this.params)
+ this.user[param] = res.data[param];
+ })
+ .finally(() => this.mergeParams());
+ }
+
+ mergeParams() {
+ for (let param of this.params) {
+ let local = this.local[param];
+ this[param] = local != null && local != ''
+ ? local
+ : this.user[param];
+ }
+ }
+
+ setUser(param, value) {
+ this.user[param] = value;
+ this.mergeParams();
+
+ let params = {[param]: value};
+ return this.$http.post('api/UserConfigs/setUserConfig', params)
+ .then(() => this.showSaved());
+ }
+
+ setLocal(param, value) {
+ this.setItem(param, value);
+ this.local[param] = value;
+ this.mergeParams();
+ this.showSaved();
+ }
+
+ showSaved() {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ }
+
+ getItem(key) {
+ let value = this.storage[key];
+ return value !== undefined ? JSON.parse(value) : value;
+ }
+
+ setItem(key, value) {
+ if (value == null)
+ this.storage.removeItem(key);
+ else
+ this.storage[key] = JSON.stringify(value);
+ }
+}
+Config.$inject = ['$http', 'vnApp', '$translate', '$window'];
+
+ngModule.service('vnConfig', Config);
diff --git a/front/core/services/index.js b/front/core/services/index.js
index 6cffd4501..8ff95188a 100644
--- a/front/core/services/index.js
+++ b/front/core/services/index.js
@@ -1,6 +1,8 @@
+import './acl-service';
import './app';
import './auth';
import './token';
import './modules';
import './interceptor';
+import './config';
diff --git a/front/salix/components/app/style.scss b/front/salix/components/app/style.scss
index efeceff89..29c14fe0b 100644
--- a/front/salix/components/app/style.scss
+++ b/front/salix/components/app/style.scss
@@ -64,14 +64,6 @@ vn-app {
box-sizing: border-box;
padding: $spacing-md;
height: inherit;
-
- form vn-horizontal {
- align-items: center;
-
- & > * {
- padding: .2em;
- }
- }
}
vn-main-block {
display: block;
@@ -102,11 +94,6 @@ vn-app {
.content-block {
margin-left: 0;
margin-right: 0;
-
- form vn-horizontal {
- flex-direction: column;
- align-items: initial;
- }
}
vn-main-block {
padding-left: 0;
@@ -117,3 +104,37 @@ vn-app {
}
}
}
+form vn-horizontal {
+ align-items: center;
+
+ & > * {
+ box-sizing: border-box;
+ min-height: 2.8em;
+ padding: 0 $spacing-sm;
+
+ &:first-child {
+ padding-left: 0;
+ padding-right: $spacing-xs;
+ }
+ &:last-child {
+ padding-left: $spacing-xs;
+ padding-right: 0;
+ }
+ &:first-child:last-child {
+ padding: 0;
+ }
+ }
+
+ @media screen and (max-width: $mobile-width) {
+ flex-direction: column;
+ align-items: initial;
+
+ & > * {
+ &,
+ &:first-child,
+ &:last-child {
+ padding: 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/front/salix/components/index.js b/front/salix/components/index.js
index a61b037b0..ca37b21d1 100644
--- a/front/salix/components/index.js
+++ b/front/salix/components/index.js
@@ -6,6 +6,6 @@ import './background/background';
import './side-menu/side-menu';
import './left-menu/left-menu';
import './topbar/topbar';
-import './user-configuration-popover';
+import './user-popover';
import './descriptor';
import './summary';
diff --git a/front/salix/components/login/login.html b/front/salix/components/login/login.html
index 95a0044d8..73154222c 100644
--- a/front/salix/components/login/login.html
+++ b/front/salix/components/login/login.html
@@ -3,20 +3,20 @@
diff --git a/modules/agency/front/summary/index.html b/modules/agency/front/summary/index.html
index f6e41abf1..32db7802e 100644
--- a/modules/agency/front/summary/index.html
+++ b/modules/agency/front/summary/index.html
@@ -27,7 +27,7 @@
+ ng-model="$ctrl.summary.isVolumetric">
diff --git a/modules/agency/front/warehouses/index.html b/modules/agency/front/warehouses/index.html
index d40e1139c..063c6993b 100644
--- a/modules/agency/front/warehouses/index.html
+++ b/modules/agency/front/warehouses/index.html
@@ -7,7 +7,7 @@
{{::row.warehouse.name}}
-
+
@@ -86,7 +86,7 @@
-
+
ID
diff --git a/modules/claim/front/basic-data/index.html b/modules/claim/front/basic-data/index.html
index 8588d562a..953441581 100644
--- a/modules/claim/front/basic-data/index.html
+++ b/modules/claim/front/basic-data/index.html
@@ -11,7 +11,7 @@
+ ng-model="$ctrl.claim.created"
+ options="{enableTime: true}">
-
+
-
+
+ ng-model="$ctrl.claim.observation"
+ rule>
-
+
diff --git a/modules/claim/front/detail/index.html b/modules/claim/front/detail/index.html
index 768242c06..90e7b5914 100644
--- a/modules/claim/front/detail/index.html
+++ b/modules/claim/front/detail/index.html
@@ -37,8 +37,10 @@
{{::saleClaimed.sale.ticket.landed | dateTime:'dd/MM/yyyy'}}
{{::saleClaimed.sale.quantity}}
-
@@ -138,13 +140,11 @@
-
- €
-
+ on-change="$ctrl.updateDiscount()"
+ suffix="€">
New price
diff --git a/modules/claim/front/development/index.html b/modules/claim/front/development/index.html
index 23de22b3f..6c2441647 100644
--- a/modules/claim/front/development/index.html
+++ b/modules/claim/front/development/index.html
@@ -45,7 +45,7 @@
vn-one
vn-focus
label="Reason"
- field="claimDevelopment.claimReasonFk"
+ ng-model="claimDevelopment.claimReasonFk"
data="claimReasons"
fields="['id', 'description']"
show-field="description"
@@ -54,7 +54,7 @@
Drag & Drop files here...
-
-
-
-
-
- Drag & Drop files here...
-
-
-
+
+
diff --git a/modules/claim/front/dms/index/index.js b/modules/claim/front/dms/index/index.js
index f60feab6c..12d22bb2a 100644
--- a/modules/claim/front/dms/index/index.js
+++ b/modules/claim/front/dms/index/index.js
@@ -2,13 +2,14 @@ import ngModule from '../../module';
import './style.scss';
class Controller {
- constructor($stateParams, $scope, $http, $translate, vnToken, vnApp) {
+ constructor($stateParams, $scope, $http, $translate, vnToken, vnApp, vnConfig) {
this.$stateParams = $stateParams;
this.$ = $scope;
this.$http = $http;
this.$translate = $translate;
this.accessToken = vnToken.token;
this.vnApp = vnApp;
+ this.vnConfig = vnConfig;
}
showWorkerDescriptor(event, workerFk) {
@@ -49,8 +50,8 @@ class Controller {
}};
return this.$http.get('/api/DmsTypes/findOne', {params}).then(res => {
const dmsTypeId = res.data && res.data.id;
- const companyId = window.localStorage.defaultCompanyFk;
- const warehouseId = window.localStorage.defaultWarehouseFk;
+ const companyId = this.vnConfig.companyFk;
+ const warehouseId = this.vnConfig.warehouseFk;
this.dms = {
hasFile: false,
hasFileAttached: false,
@@ -92,7 +93,7 @@ class Controller {
}
}
-Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnToken', 'vnApp'];
+Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnToken', 'vnApp', 'vnConfig'];
ngModule.component('vnClaimDmsIndex', {
template: require('./index.html'),
diff --git a/modules/claim/front/locale/es.yml b/modules/claim/front/locale/es.yml
index 27a9eb9a9..f636b43ee 100644
--- a/modules/claim/front/locale/es.yml
+++ b/modules/claim/front/locale/es.yml
@@ -1,6 +1,7 @@
#Ordenar alfabeticamente
Add sale: Añadir linea
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
+Are you sure you want to delete this claim?: ¿Seguro que quieres eliminar esta reclamación?
Client Id: Id cliente
Claimed ticket: Ticket reclamado
Delete claim: Eliminar reclamación
diff --git a/modules/claim/front/search-panel/index.html b/modules/claim/front/search-panel/index.html
index fa9db0885..917d2d280 100644
--- a/modules/claim/front/search-panel/index.html
+++ b/modules/claim/front/search-panel/index.html
@@ -4,7 +4,7 @@
@@ -13,25 +13,25 @@
+ ng-model="filter.client">
+ ng-model="filter.id">
+ ng-model="filter.clientFk">
@@ -62,7 +62,7 @@
+ ng-model="filter.created">
diff --git a/modules/claim/front/summary/index.html b/modules/claim/front/summary/index.html
index 65d08777b..314d71bc9 100644
--- a/modules/claim/front/summary/index.html
+++ b/modules/claim/front/summary/index.html
@@ -26,7 +26,7 @@
vn-three
disabled="true"
label="Observation"
- model="$ctrl.summary.claim.observation">
+ ng-model="$ctrl.summary.claim.observation">
diff --git a/modules/client/front/address/create/index.html b/modules/client/front/address/create/index.html
index 79cf4c94f..8c30f9929 100644
--- a/modules/client/front/address/create/index.html
+++ b/modules/client/front/address/create/index.html
@@ -16,24 +16,42 @@
@@ -23,7 +23,7 @@
@@ -22,7 +22,7 @@
label="Type"
fields="['code', 'name']"
value-field="id"
- field="$ctrl.item.typeFk"
+ ng-model="$ctrl.item.typeFk"
search-function="{or: [{code: {like: $search +'%'}}, {name: {like: '%'+ $search +'%'}}]}">
{{::code}}
@@ -34,7 +34,7 @@
label="Intrastat"
show-field="description"
value-field="id"
- field="$ctrl.item.intrastatFk"
+ ng-model="$ctrl.item.intrastatFk"
search-function="{or: [{id: {like: $search +'%'}}, {description: {like: '%'+ $search +'%'}}]}">
{{::id}}
@@ -48,7 +48,7 @@
label="Origin"
show-field="name"
value-field="id"
- field="$ctrl.item.originFk">
+ ng-model="$ctrl.item.originFk">
diff --git a/modules/item/front/descriptor/index.html b/modules/item/front/descriptor/index.html
index c139ad3d9..cd12c253f 100644
--- a/modules/item/front/descriptor/index.html
+++ b/modules/item/front/descriptor/index.html
@@ -103,7 +103,7 @@
@@ -112,7 +112,7 @@
vn-one
label="Warehouse"
id="warehouse"
- field="$ctrl.warehouseFk"
+ ng-model="$ctrl.warehouseFk"
url="/api/Warehouses"
select-fields="['id', 'name']"
show-field="name"
diff --git a/modules/item/front/descriptor/index.js b/modules/item/front/descriptor/index.js
index da94aacb6..0e3f83f1d 100644
--- a/modules/item/front/descriptor/index.js
+++ b/modules/item/front/descriptor/index.js
@@ -2,12 +2,13 @@ import ngModule from '../module';
import './style.scss';
class Controller {
- constructor($state, $scope, $http, vnApp, $translate) {
+ constructor($state, $scope, $http, vnApp, $translate, vnConfig) {
this.$state = $state;
this.$scope = $scope;
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
+ this.vnConfig = vnConfig;
this.moreOptions = [
{callback: this.showRegularizeDialog, name: 'Regularize stock'}
];
@@ -27,7 +28,7 @@ class Controller {
}
get warehouseFk() {
if (!this._warehouseFk)
- this._warehouseFk = parseInt(window.localStorage.defaultWarehouseFk);
+ this._warehouseFk = this.vnConfig.warehouseFk;
return this._warehouseFk;
}
@@ -93,7 +94,7 @@ class Controller {
}
}
-Controller.$inject = ['$state', '$scope', '$http', 'vnApp', '$translate'];
+Controller.$inject = ['$state', '$scope', '$http', 'vnApp', '$translate', 'vnConfig'];
ngModule.component('vnItemDescriptor', {
template: require('./index.html'),
diff --git a/modules/item/front/diary/index.html b/modules/item/front/diary/index.html
index 53710942c..cf8de3236 100644
--- a/modules/item/front/diary/index.html
+++ b/modules/item/front/diary/index.html
@@ -15,7 +15,7 @@
show-field="name"
value-field="id"
initial-data="$ctrl.warehouseFk"
- field="$ctrl.warehouseFk"
+ ng-model="$ctrl.warehouseFk"
label="Select warehouse">
diff --git a/modules/item/front/index/index.html b/modules/item/front/index/index.html
index 5ff8e63ab..aae3b0660 100644
--- a/modules/item/front/index/index.html
+++ b/modules/item/front/index/index.html
@@ -107,7 +107,7 @@
+ ng-model="::item.isActive">
diff --git a/modules/item/front/last-entries/index.html b/modules/item/front/last-entries/index.html
index 1dc4d02c3..78f10c3af 100644
--- a/modules/item/front/last-entries/index.html
+++ b/modules/item/front/last-entries/index.html
@@ -12,8 +12,7 @@
+ ng-model="$ctrl.date">
@@ -40,7 +39,7 @@
diff --git a/modules/item/front/niche/index.html b/modules/item/front/niche/index.html
index 52017b4f4..04399777e 100644
--- a/modules/item/front/niche/index.html
+++ b/modules/item/front/niche/index.html
@@ -18,15 +18,15 @@
url="/item/api/Warehouses"
show-field="name"
value-field="id"
- field="niche.warehouseFk"
+ ng-model="niche.warehouseFk"
label="Warehouse"
vn-acl="buyer,replenisher" vn-one vn-focus>
diff --git a/modules/item/front/request-search-panel/index.html b/modules/item/front/request-search-panel/index.html
index 8f9f71632..8e15be8fb 100644
--- a/modules/item/front/request-search-panel/index.html
+++ b/modules/item/front/request-search-panel/index.html
@@ -4,7 +4,7 @@
@@ -12,11 +12,11 @@
+ ng-model="filter.ticketFk">
+ ng-model="filter.clientFk">
@@ -42,12 +42,12 @@
+ ng-model="filter.from">
+ ng-model="filter.to">
@@ -57,7 +57,7 @@
vn-one
triple-state="true"
label="Confirmed"
- field="filter.isOk">
+ ng-model="filter.isOk">
diff --git a/modules/item/front/request/index.html b/modules/item/front/request/index.html
index 3670cd469..20af5653a 100644
--- a/modules/item/front/request/index.html
+++ b/modules/item/front/request/index.html
@@ -24,7 +24,7 @@
- Ticket ID
+ Ticket ID
Shipped
Warehouse
SalesPerson
@@ -73,8 +73,8 @@
{{request.itemFk}}
-
@@ -90,8 +90,8 @@
{{request.saleQuantity}}
-
@@ -136,7 +136,7 @@
+ ng-model="$ctrl.denyObservation">
diff --git a/modules/item/front/request/style.scss b/modules/item/front/request/style.scss
index ec540e853..0af32c309 100644
--- a/modules/item/front/request/style.scss
+++ b/modules/item/front/request/style.scss
@@ -5,15 +5,13 @@ vn-item-request {
button.close {
display: none
}
- & vn-button {
+ vn-button {
margin: 0 auto
}
-
vn-textarea {
width: 100%
}
}
-
vn-icon[icon=insert_drive_file]{
color: $color-font-secondary;
}
diff --git a/modules/item/front/search-panel/index.html b/modules/item/front/search-panel/index.html
index 5b2cc82db..3917013d9 100644
--- a/modules/item/front/search-panel/index.html
+++ b/modules/item/front/search-panel/index.html
@@ -5,7 +5,7 @@
@@ -18,7 +18,7 @@
label="Category"
show-field="name"
value-field="id"
- field="filter.categoryFk">
+ ng-model="filter.categoryFk">
+ ng-model="filter.typeFk">
@@ -54,7 +54,7 @@
+ ng-model="itemTag.value">
@@ -105,7 +105,7 @@
+ ng-model="field.value">
+ ng-model="itemTag.value"
+ rule>
+ ng-model="itemTag.priority"
+ rule>
@@ -11,17 +11,18 @@
-
-
+
-
diff --git a/modules/item/front/tax/index.js b/modules/item/front/tax/index.js
index f4cc6f73e..84a847e05 100644
--- a/modules/item/front/tax/index.js
+++ b/modules/item/front/tax/index.js
@@ -1,11 +1,11 @@
import ngModule from '../module';
export default class Controller {
- constructor($stateParams, $http, $translate, vnApp) {
+ constructor($stateParams, $http, $translate, $scope) {
+ this.$ = $scope;
this.$http = $http;
this.$stateParams = $stateParams;
this._ = $translate;
- this.vnApp = vnApp;
}
$onInit() {
@@ -21,9 +21,8 @@ export default class Controller {
}]
};
- let urlFilter = encodeURIComponent(JSON.stringify(filter));
- let url = `/item/api/Items/${this.$stateParams.id}/taxes?filter=${urlFilter}`;
- this.$http.get(url).then(json => {
+ let url = `api/Items/${this.$stateParams.id}/taxes`;
+ this.$http.get(url, {params: {filter}}).then(json => {
this.taxes = json.data;
});
}
@@ -33,14 +32,16 @@ export default class Controller {
for (let tax of this.taxes)
data.push({id: tax.id, taxClassFk: tax.taxClassFk});
- let url = `/item/api/Items/updateTaxes`;
- this.$http.post(url, data).then(
- () => this.vnApp.showSuccess(this._.instant('Data saved!'))
- );
+ this.$.watcher.check();
+ let url = `api/Items/updateTaxes`;
+ this.$http.post(url, data).then(() => {
+ this.$.watcher.notifySaved();
+ this.$.watcher.updateOriginalData();
+ });
}
}
-Controller.$inject = ['$stateParams', '$http', '$translate', 'vnApp'];
+Controller.$inject = ['$stateParams', '$http', '$translate', '$scope'];
ngModule.component('vnItemTax', {
template: require('./index.html'),
diff --git a/modules/item/front/tax/index.spec.js b/modules/item/front/tax/index.spec.js
index 56c3b9306..59d8ad892 100644
--- a/modules/item/front/tax/index.spec.js
+++ b/modules/item/front/tax/index.spec.js
@@ -2,65 +2,65 @@ import './index.js';
describe('Item', () => {
describe('Component vnItemTax', () => {
+ let $element;
let $stateParams;
let controller;
let $httpBackend;
- let vnApp;
beforeEach(angular.mock.module('item', $translateProvider => {
$translateProvider.translations('en', {});
}));
- beforeEach(angular.mock.inject(($componentController, _$httpBackend_, _$stateParams_, _vnApp_) => {
+ beforeEach(angular.mock.inject((_$httpBackend_, $rootScope, _$stateParams_, $compile) => {
$stateParams = _$stateParams_;
$stateParams.id = 1;
$httpBackend = _$httpBackend_;
- vnApp = _vnApp_;
- spyOn(vnApp, 'showSuccess');
- controller = $componentController('vnItemTax', {$state: $stateParams});
+
+ $httpBackend.whenGET(url => url.startsWith(`api/TaxClasses`))
+ .respond([
+ {id: 1, description: 'Reduced VAT', code: 'R'},
+ {id: 2, description: 'General VAT', code: 'G'}
+ ]);
+
+ $httpBackend.whenGET(url => url.startsWith(`api/Items/${$stateParams.id}/taxes`))
+ .respond([
+ {id: 1, taxClassFk: 1}
+ ]);
+
+ $element = $compile(` {
+ $element.remove();
+ });
+
describe('getTaxes()', () => {
it('should perform a query to set the array of taxes into the controller', () => {
- let filter = {
- fields: ['id', 'countryFk', 'taxClassFk'],
- include: [{
- relation: 'country',
- scope: {fields: ['country']}
- }]
- };
- let response = [{id: 1, taxClassFk: 1}];
- filter = encodeURIComponent(JSON.stringify(filter));
- $httpBackend.when('GET', `/item/api/Items/1/taxes?filter=${filter}`).respond(response);
- $httpBackend.expect('GET', `/item/api/Items/1/taxes?filter=${filter}`);
- controller.$onInit();
$httpBackend.flush();
- expect(controller.taxes).toEqual(response);
+ expect(controller.taxes[0].id).toEqual(1);
+ expect(controller.taxes[0].taxClassFk).toEqual(1);
});
});
describe('submit()', () => {
it('should perform a post to update taxes', () => {
- let filter = {
- fields: ['id', 'countryFk', 'taxClassFk'],
- include: [{
- relation: 'country',
- scope: {fields: ['country']}
- }]
- };
- let response = [{id: 1, taxClassFk: 1}];
- filter = encodeURIComponent(JSON.stringify(filter));
- $httpBackend.when('GET', `/item/api/Items/1/taxes?filter=${filter}`).respond(response);
- controller.$onInit();
- $httpBackend.flush();
+ spyOn(controller.$.watcher, 'notifySaved');
+ spyOn(controller.$.watcher, 'updateOriginalData');
+ controller.taxes = [
+ {id: 37, countryFk: 1, taxClassFk: 1, country: {id: 1, country: 'España'}}
+ ];
+ controller.$.watcher.data = [
+ {id: 37, countryFk: 1, taxClassFk: 2, country: {id: 1, country: 'España'}}
+ ];
- $httpBackend.when('POST', `/item/api/Items/updateTaxes`).respond('ok');
- $httpBackend.expect('POST', `/item/api/Items/updateTaxes`);
+ $httpBackend.whenPOST(`api/Items/updateTaxes`).respond('oki doki');
controller.submit();
$httpBackend.flush();
- expect(vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
+ expect(controller.$.watcher.notifySaved).toHaveBeenCalledWith();
+ expect(controller.$.watcher.updateOriginalData).toHaveBeenCalledWith();
});
});
});
diff --git a/modules/order/front/basic-data/index.html b/modules/order/front/basic-data/index.html
index 1a6fcb9dd..54dc470c5 100644
--- a/modules/order/front/basic-data/index.html
+++ b/modules/order/front/basic-data/index.html
@@ -22,7 +22,7 @@
search-function="{or: [{id: $search}, {name: {like: '%'+$search+'%'}}]}"
show-field="name"
value-field="id"
- field="$ctrl.order.clientFk"
+ ng-model="$ctrl.order.clientFk"
selection="$ctrl.selection"
fields="['defaultAddressFk']">
{{::id}}: {{::name}}
@@ -35,7 +35,7 @@
search-function="$search"
show-field="nickname"
value-field="id"
- field="$ctrl.order.addressFk">
+ ng-model="$ctrl.order.addressFk">
{{::nickname}}
@@ -43,8 +43,7 @@
+ ng-model="$ctrl.order.landed">
+ ng-model="$ctrl.order.agencyModeFk">
{{::id}}: {{::name}}
+ ng-model="$ctrl.order.note"
+ rule>
diff --git a/modules/order/front/catalog-search-panel/index.html b/modules/order/front/catalog-search-panel/index.html
index 4c3c1989a..5f9566e0f 100644
--- a/modules/order/front/catalog-search-panel/index.html
+++ b/modules/order/front/catalog-search-panel/index.html
@@ -4,7 +4,7 @@
@@ -15,7 +15,7 @@
+ ng-model="filter.value">
diff --git a/modules/order/front/catalog/index.html b/modules/order/front/catalog/index.html
index 2f52b73e8..b40044527 100644
--- a/modules/order/front/catalog/index.html
+++ b/modules/order/front/catalog/index.html
@@ -18,7 +18,7 @@
{{id}}: {{name}}
@@ -14,7 +14,7 @@
disabled="!$ctrl.clientFk"
url="{{ $ctrl.clientFk ? '/api/Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
fields="['nickname', 'street', 'city']"
- field="$ctrl.addressFk"
+ ng-model="$ctrl.addressFk"
show-field="nickname"
value-field="id"
label="Address">
@@ -22,8 +22,7 @@
+ ng-model="$ctrl.landed">
+ ng-model="$ctrl.order.agencyModeFk">
diff --git a/modules/order/front/filter/index.html b/modules/order/front/filter/index.html
index d4d8fd751..7a935120e 100644
--- a/modules/order/front/filter/index.html
+++ b/modules/order/front/filter/index.html
@@ -25,46 +25,48 @@
id: value,
value: type.selection.name
}"
- field="$ctrl.type.id"
+ ng-model="$ctrl.type.id"
show-field="name"
value-field="id"
label="Type">
-
+
search
-
-
+
+
keyboard_arrow_down
-
+
-
-
+ ng-model="$ctrl.itemFk">
+
-
+
-
-
+ ng-model="$ctrl.value">
+
search
-
-
+
+
keyboard_arrow_down
-
+
diff --git a/modules/order/front/prices-popover/index.html b/modules/order/front/prices-popover/index.html
index 971d70a22..3a7f34462 100644
--- a/modules/order/front/prices-popover/index.html
+++ b/modules/order/front/prices-popover/index.html
@@ -55,10 +55,11 @@
-
diff --git a/modules/order/front/search-panel/index.html b/modules/order/front/search-panel/index.html
index 3b8ca238b..395dcf7d5 100644
--- a/modules/order/front/search-panel/index.html
+++ b/modules/order/front/search-panel/index.html
@@ -4,7 +4,7 @@
@@ -13,26 +13,26 @@
+ ng-model="filter.id">
+ ng-model="filter.clientFk">
-
+ ng-model="filter.from">
-
+ ng-model="filter.to">
+ ng-model="filter.ticketFk">
@@ -70,14 +72,14 @@
+ ng-model="filter.isConfirmed">
diff --git a/modules/order/front/summary/index.html b/modules/order/front/summary/index.html
index 4f3aee712..939a43244 100644
--- a/modules/order/front/summary/index.html
+++ b/modules/order/front/summary/index.html
@@ -9,7 +9,7 @@
value="{{$ctrl.summary.address.nickname}}">
+ ng-model="$ctrl.summary.isConfirmed">
@@ -39,7 +39,7 @@
Total {{$ctrl.summary.total | currency: 'EUR':2}}
-
+
diff --git a/modules/route/front/basic-data/index.html b/modules/route/front/basic-data/index.html
index 973b20a40..daad13a56 100644
--- a/modules/route/front/basic-data/index.html
+++ b/modules/route/front/basic-data/index.html
@@ -10,7 +10,7 @@
+ ng-model="$ctrl.route.created">
+ ng-model="$ctrl.route.kmStart"
+ rule>
+ ng-model="$ctrl.route.kmEnd"
+ rule>
+ ng-model="$ctrl.route.started">
+ ng-model="$ctrl.route.finished">
-
+ ng-model="$ctrl.route.description"
+ rule
+ vn-focus>
+
diff --git a/modules/route/front/create/index.html b/modules/route/front/create/index.html
index b4a45b91e..0c6ec17ea 100644
--- a/modules/route/front/create/index.html
+++ b/modules/route/front/create/index.html
@@ -11,7 +11,7 @@
+ ng-model="$ctrl.route.created">
+ ng-model="$ctrl.route.description"
+ rule>
diff --git a/modules/route/front/index/index.js b/modules/route/front/index/index.js
index 169e6b5b8..823ef8bf4 100644
--- a/modules/route/front/index/index.js
+++ b/modules/route/front/index/index.js
@@ -1,9 +1,10 @@
import ngModule from '../module';
export default class Controller {
- constructor($scope, vnToken) {
+ constructor($scope, vnToken, vnConfig) {
this.accessToken = vnToken.token;
this.$ = $scope;
+ this.vnConfig = vnConfig;
this.setDefaultFilter();
}
@@ -16,7 +17,7 @@ export default class Controller {
let from = new Date();
from.setHours(0, 0, 0, 0);
- this.filter = {from, to, warehouseFk: localStorage.defaultWarehouseFk};
+ this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk};
}
showWorkerDescriptor(event, workerFk) {
@@ -45,7 +46,7 @@ export default class Controller {
}
}
-Controller.$inject = ['$scope', 'vnToken'];
+Controller.$inject = ['$scope', 'vnToken', 'vnConfig'];
ngModule.component('vnRouteIndex', {
template: require('./index.html'),
diff --git a/modules/route/front/search-panel/index.html b/modules/route/front/search-panel/index.html
index 1b495c640..f72f13878 100644
--- a/modules/route/front/search-panel/index.html
+++ b/modules/route/front/search-panel/index.html
@@ -4,7 +4,7 @@
@@ -12,7 +12,7 @@
-
+ ng-model="filter.from">
-
+ ng-model="filter.to">
@@ -51,14 +53,14 @@
+ ng-model="filter.m3">
@@ -66,7 +68,7 @@
+ ng-model="filter.description">
diff --git a/modules/route/front/summary/index.html b/modules/route/front/summary/index.html
index f4b559d1c..85a045106 100644
--- a/modules/route/front/summary/index.html
+++ b/modules/route/front/summary/index.html
@@ -43,10 +43,9 @@
+ ng-model="$ctrl.summary.route.description">
diff --git a/modules/route/front/tickets/index.html b/modules/route/front/tickets/index.html
index 560a34cc8..92bf7a2aa 100644
--- a/modules/route/front/tickets/index.html
+++ b/modules/route/front/tickets/index.html
@@ -49,16 +49,16 @@
+ ng-model="ticket.checked">
+ ng-model="ticket.priority"
+ rule="Ticket">
diff --git a/modules/ticket/back/methods/ticket/getPossibleStowaways.js b/modules/ticket/back/methods/ticket/getPossibleStowaways.js
index f692c9f23..3565d64ab 100644
--- a/modules/ticket/back/methods/ticket/getPossibleStowaways.js
+++ b/modules/ticket/back/methods/ticket/getPossibleStowaways.js
@@ -45,7 +45,7 @@ module.exports = Self => {
clientFk: ship.clientFk,
addressFk: ship.addressFk,
agencyModeFk: ship.agencyModeFk,
- warehouse: {neq: ship.warehouseFk},
+ warehouseFk: {neq: ship.warehouseFk},
shipped: {
between: [lowestDate.toJSON(), highestDate.toJSON()]
}
diff --git a/modules/ticket/back/models/stowaway.js b/modules/ticket/back/models/stowaway.js
index aa21e8680..780b6b31c 100644
--- a/modules/ticket/back/models/stowaway.js
+++ b/modules/ticket/back/models/stowaway.js
@@ -14,7 +14,7 @@ module.exports = function(Self) {
code: 'BOARDING'
};
let state = await models.State.findOne({where});
- let params = {ticketFk: ctx.instance.id, stateFk: state.id};
+ let params = {ticketFk: ctx.instance.shipFk, stateFk: state.id};
const loopBackContext = LoopBackContext.getCurrentContext();
let httpCtx = {req: loopBackContext.active};
diff --git a/modules/ticket/front/basic-data/step-one/index.html b/modules/ticket/front/basic-data/step-one/index.html
index 85cc72bbd..f545e7131 100644
--- a/modules/ticket/front/basic-data/step-one/index.html
+++ b/modules/ticket/front/basic-data/step-one/index.html
@@ -15,7 +15,7 @@
show-field="name"
value-field="id"
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
- field="$ctrl.clientId"
+ ng-model="$ctrl.clientId"
initial-data="$ctrl.clientId"
order="id">
@@ -24,7 +24,7 @@
label="Address"
show-field="nickname"
value-field="id"
- field="$ctrl.addressId"
+ ng-model="$ctrl.addressId"
order="isActive DESC">
{{::isActive ? '' : 'INACTIVE'}} {{::nickname}}
@@ -36,25 +36,27 @@
label="Warehouse"
show-field="name"
value-field="id"
- field="$ctrl.warehouseId">
+ ng-model="$ctrl.warehouseId">
-
+ ng-model="$ctrl.shipped"
+ options="{enableTime: true}">
-
+ ng-model="$ctrl.landed">
@@ -64,14 +66,14 @@
label="Agency"
show-field="name"
value-field="id"
- field="$ctrl.agencyModeId">
+ ng-model="$ctrl.agencyModeId">
{{::name}} - {{::warehouse.name}} - Max. {{::hour | dateTime: 'HH:mm'}} h.
diff --git a/modules/ticket/front/basic-data/step-three/index.html b/modules/ticket/front/basic-data/step-three/index.html
index 0bd5bdc2a..24d5c8ba5 100644
--- a/modules/ticket/front/basic-data/step-three/index.html
+++ b/modules/ticket/front/basic-data/step-three/index.html
@@ -6,14 +6,14 @@
label="Charge difference to"
show-field="description"
value-field="id"
- field="$ctrl.ticket.option"
+ ng-model="$ctrl.ticket.option"
initial-data="$ctrl.ticket.option">
diff --git a/modules/ticket/front/basic-data/step-two/index.html b/modules/ticket/front/basic-data/step-two/index.html
index 7713555ae..aa5a23212 100644
--- a/modules/ticket/front/basic-data/step-two/index.html
+++ b/modules/ticket/front/basic-data/step-two/index.html
@@ -1,7 +1,7 @@
-
+
Item
diff --git a/modules/ticket/front/component/index.html b/modules/ticket/front/component/index.html
index d79bd524f..2c072f0a3 100644
--- a/modules/ticket/front/component/index.html
+++ b/modules/ticket/front/component/index.html
@@ -6,72 +6,64 @@
data="components"
auto-load="true">
-
-
-
-
-
-
- Item
- Description
- Quantity
- Serie
- Components
- Import
- Total import
-
-
-
-
-
- Base to commission {{$ctrl.base() | currency: 'EUR':3}}
-
-
-
-
-
-
-
- {{sale.itemFk | zeroFill:6}}
-
-
-
-
-
-
-
- {{::sale.quantity}}
-
-
-
-
- {{::component.componentRate.componentType.type}}
-
-
- {{::component.componentRate.name}}
-
-
- {{::component.value | currency: 'EUR':3}}
-
-
- {{::sale.quantity * component.value | currency: 'EUR':3}}
-
-
-
- No results
-
-
-
-
+
+
+ Base to commission {{$ctrl.base() | currency: 'EUR':3}}
-
+
+
+
+
+ Item
+ Description
+ Quantity
+ Serie
+ Components
+ Import
+ Total
+
+
+
+
+
+
+ {{sale.itemFk | zeroFill:6}}
+
+
+
+
+
+
+
+ {{::sale.quantity}}
+
+
+
+
+ {{::component.componentRate.componentType.type}}
+
+
+ {{::component.componentRate.name}}
+
+
+ {{::component.value | currency: 'EUR':3}}
+
+
+ {{::sale.quantity * component.value | currency: 'EUR':3}}
+
+
+
+
+
+
diff --git a/modules/ticket/front/component/style.scss b/modules/ticket/front/component/style.scss
index 85bb673e0..c20b8f720 100644
--- a/modules/ticket/front/component/style.scss
+++ b/modules/ticket/front/component/style.scss
@@ -1,29 +1,27 @@
+@import "variables";
+
vn-ticket-components {
- .vn-grid {
- tbody:not(:last-child) {
+ .vn-table > tbody {
+ &:not(:last-child) {
+ border-bottom: .1em solid $color-spacer-light;
+ }
+ & > tr {
border-bottom: none;
- }
- tfoot tr:first-child td {
- padding-top: 10px !important;
- }
-
- tr {
- td {
- padding-top: .1em !important;
- padding-bottom: .1em !important;
+ &.components {
+ height: 1em;
+
+ & > td {
+ padding-top: .1em;
+ padding-bottom: .1em;
+ }
+ &:nth-child(2) > td {
+ padding-top: 1em;
+ }
+ &:last-child > td {
+ padding-bottom: 1em;
+ }
}
-
- td.first {
- padding-top: 10px !important;
- }
-
- td.last {
- padding-bottom: 10px !important;
- }
- }
- tr:not(:first-child):not(:last-child), {
- border-bottom: none;
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/ticket/front/create/card.html b/modules/ticket/front/create/card.html
index 0629f460f..59a22c569 100644
--- a/modules/ticket/front/create/card.html
+++ b/modules/ticket/front/create/card.html
@@ -6,7 +6,7 @@
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
show-field="name"
value-field="id"
- field="$ctrl.clientFk"
+ ng-model="$ctrl.clientFk"
order="id">
{{id}}: {{name}}
@@ -14,7 +14,7 @@
disabled="!$ctrl.clientFk"
url="{{ $ctrl.clientFk ? '/api/Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
fields="['nickname', 'street', 'city']"
- field="$ctrl.addressFk"
+ ng-model="$ctrl.addressFk"
show-field="nickname"
value-field="id"
label="Address">
@@ -22,12 +22,11 @@
+ ng-model="$ctrl.landed">
+ ng-model="$ctrl.ticket.agencyModeFk">
\ No newline at end of file
diff --git a/modules/ticket/front/create/card.js b/modules/ticket/front/create/card.js
index c0321e3d5..5cdc61cd7 100644
--- a/modules/ticket/front/create/card.js
+++ b/modules/ticket/front/create/card.js
@@ -1,11 +1,12 @@
import ngModule from '../module';
class Controller {
- constructor($http, vnApp, $translate, $state, $stateParams) {
+ constructor($http, vnApp, $translate, $state, $stateParams, vnConfig) {
this.$stateParams = $stateParams;
this.$http = $http;
this.translate = $translate;
this.vnApp = vnApp;
+ this.vnConfig = vnConfig;
this.ticket = {};
this.$state = $state;
this.clientFk = $stateParams.clientFk;
@@ -15,8 +16,7 @@ class Controller {
if (this.$stateParams && this.$stateParams.clientFk)
this.clientFk = this.$stateParams.clientFk;
- if (window.localStorage && window.localStorage.defaultWarehouseFk)
- this.warehouseFk = parseInt(window.localStorage.defaultWarehouseFk);
+ this.warehouseFk = this.vnConfig.warehouseFk;
}
set ticket(value) {
@@ -116,7 +116,7 @@ class Controller {
}
}
-Controller.$inject = ['$http', 'vnApp', '$translate', '$state', '$stateParams'];
+Controller.$inject = ['$http', 'vnApp', '$translate', '$state', '$stateParams', 'vnConfig'];
ngModule.component('vnTicketCreateCard', {
template: require('./card.html'),
diff --git a/modules/ticket/front/descriptor/addStowaway.html b/modules/ticket/front/descriptor/addStowaway.html
index c51d9d0d3..0c8c3d6b0 100644
--- a/modules/ticket/front/descriptor/addStowaway.html
+++ b/modules/ticket/front/descriptor/addStowaway.html
@@ -7,28 +7,28 @@
class="modal-form"
on-open="model.refresh()">
-