diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js index 41d551f2b..e077eea30 100644 --- a/back/methods/dms/uploadFile.js +++ b/back/methods/dms/uploadFile.js @@ -9,25 +9,31 @@ module.exports = Self => { { arg: 'warehouseId', type: 'Number', - description: 'The warehouse id' + description: 'The warehouse id', + required: true }, { arg: 'companyId', type: 'Number', - description: 'The company id' + description: 'The company id', + required: true }, { arg: 'dmsTypeId', type: 'Number', - description: 'The dms type id' + description: 'The dms type id', + required: true }, { arg: 'reference', - type: 'String' + type: 'String', + required: true }, { arg: 'description', - type: 'String' + type: 'String', + required: true }, { arg: 'hasFile', type: 'Boolean', - description: 'True if has an attached file' + description: 'True if has an attached file', + required: true }], returns: { type: 'Object', diff --git a/back/model-config.json b/back/model-config.json index b24226507..89f517812 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -52,6 +52,15 @@ }, "Postcode": { "dataSource": "vn" + }, + "UserPhoneType": { + "dataSource": "vn" + }, + "UserPhone": { + "dataSource": "vn" + }, + "UserLog": { + "dataSource": "vn" } } diff --git a/modules/worker/back/models/user-log.json b/back/models/user-log.json similarity index 100% rename from modules/worker/back/models/user-log.json rename to back/models/user-log.json diff --git a/modules/worker/back/models/user-phone-type.json b/back/models/user-phone-type.json similarity index 100% rename from modules/worker/back/models/user-phone-type.json rename to back/models/user-phone-type.json diff --git a/back/models/user-phone.js b/back/models/user-phone.js new file mode 100644 index 000000000..6f6c20049 --- /dev/null +++ b/back/models/user-phone.js @@ -0,0 +1,9 @@ +let UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.rewriteDbError(function(err) { + if (err.code === 'ER_DUP_ENTRY') + return new UserError(`This phone already exists`); + return err; + }); +}; diff --git a/modules/worker/back/models/user-phone.json b/back/models/user-phone.json similarity index 75% rename from modules/worker/back/models/user-phone.json rename to back/models/user-phone.json index c869a808d..f264ff28f 100644 --- a/modules/worker/back/models/user-phone.json +++ b/back/models/user-phone.json @@ -2,7 +2,8 @@ "name": "UserPhone", "base": "Loggable", "log": { - "model":"UserLog" + "model":"UserLog", + "relation": "user" }, "options": { "mysql": { @@ -12,11 +13,15 @@ "properties": { "id": { "id": true, - "type": "String" + "type": "Number" }, "phone": { "type": "Number", "required": true + }, + "typeFk": { + "type": "String", + "required": true } }, "relations": { diff --git a/db/changes/10100-AllSaints/00-userLog.sql b/db/changes/10100-AllSaints/00-userLog.sql index 2e541eaf4..f6972796d 100644 --- a/db/changes/10100-AllSaints/00-userLog.sql +++ b/db/changes/10100-AllSaints/00-userLog.sql @@ -1,6 +1,6 @@ CREATE TABLE `vn`.`userLog` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `originFk` int(11) NOT NULL, + `originFk` int(10) unsigned NOT NULL, `userFk` int(10) unsigned DEFAULT NULL, `action` set('insert','update','delete') COLLATE utf8_unicode_ci NOT NULL, `creationDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP, @@ -13,6 +13,6 @@ CREATE TABLE `vn`.`userLog` ( PRIMARY KEY (`id`), KEY `originFk` (`originFk`), KEY `userFk` (`userFk`), - CONSTRAINT `userLog_ibfk_1` FOREIGN KEY (`originFk`) REFERENCES `client` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `userLog_ibfk_1` FOREIGN KEY (`originFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `userLog_ibfk_2` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; \ No newline at end of file diff --git a/db/changes/10100-AllSaints/01-userPhone.sql b/db/changes/10100-AllSaints/01-userPhone.sql index 488bbb112..e82b48000 100644 --- a/db/changes/10100-AllSaints/01-userPhone.sql +++ b/db/changes/10100-AllSaints/01-userPhone.sql @@ -2,7 +2,7 @@ CREATE TABLE `vn`.`userPhone` ( `id` INT NOT NULL AUTO_INCREMENT, `userFk` INT(10) UNSIGNED NOT NULL, `typeFk` VARCHAR(45) NOT NULL, - `phone` INT(15) NOT NULL, + `phone` VARCHAR(15) NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `UserFK_Phone` (`userFk` ASC, `phone` ASC)); @@ -22,7 +22,7 @@ ADD CONSTRAINT `fgnUserFk` ON UPDATE CASCADE; insert into vn.userPhone(userFk,typeFk,phone) - select id,'PersonalPhone', phone + select id,'personalPhone', phone from vn.client where phone is not null; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 2624f1768..2d3353f15 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -351,16 +351,6 @@ INSERT INTO `vn`.`creditInsurance`(`id`, `creditClassification`, `credit`, `crea (2, 2, 6000, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL), (3, 3, 10000, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL); -INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`) - VALUES - (1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 7, 'first route', 2.7, 10, CURDATE(), CURDATE()), - (2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 7, 'second route', 0.9, 20, CURDATE(), CURDATE()), - (3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 7, 'third route', 1.1, 30, CURDATE(), CURDATE()), - (4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 7, 'fourth route', 0.1, 40, CURDATE(), CURDATE()), - (5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 8, 'fifth route', NULL, 50, CURDATE(), CURDATE()), - (6, NULL, 57, CURDATE(), 5, 8, 'sixth route', NULL, 60, CURDATE(), CURDATE()), - (7, NULL, 57, CURDATE(), 6, NULL, 'seventh route', NULL, 70, CURDATE(), CURDATE()); - INSERT INTO `vn2008`.`empresa_grupo`(`empresa_grupo_id`, `grupo`) VALUES (1, 'Wayne Industries'); @@ -456,32 +446,42 @@ INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `warehouseFk`, `agencyModeFk`, `t (12, 'Zone entanglement', CONCAT(CURRENT_DATE(), ' ', TIME('22:00')), 4, 4, 0, 0, 0), (13, 'Zone quantum break', CONCAT(CURRENT_DATE(), ' ', TIME('22:00')), 5, 5, 0, 0, 0); +INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`) + VALUES + (1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 1, 'first route', 1.8, 10, CURDATE(), CURDATE(), 1), + (2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 2, 'second route', 0.2, 20, CURDATE(), CURDATE(), 9), + (3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 3, 'third route', 0.5, 30, CURDATE(), CURDATE(), 10), + (4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 4, 'fourth route', 0, 40, CURDATE(), CURDATE(), 12), + (5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 5, 'fifth route', 0.1, 50, CURDATE(), CURDATE(), 13), + (6, NULL, 57, CURDATE(), 5, 7, 'sixth route', 1.7, 60, CURDATE(), CURDATE(), 3), + (7, NULL, 57, CURDATE(), 6, 8, 'seventh route', 0, 70, CURDATE(), CURDATE(), 5); + INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `created`) VALUES - (1 , 3, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Bat cave', 121, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (2 , 1, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (3 , 1, 7, 1, 1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T2222222', 0, 3, DATE_ADD(CURDATE(), INTERVAL -2 MONTH)), - (4 , 3, 2, 1, 1, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T3333333', 0, 9, DATE_ADD(CURDATE(), INTERVAL -3 MONTH)), - (5 , 3, 3, 3, 1, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T4444444', 0, 10, DATE_ADD(CURDATE(), INTERVAL -4 MONTH)), - (6 , 1, 3, 3, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (7 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Mountain Drive Gotham', 1, NULL, 0, 3, CURDATE()), - (8 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Bat cave', 121, NULL, 0, 3, CURDATE()), - (9 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Stark tower', 124, NULL, 0, 3, CURDATE()), - (10, 1, 1, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'Ingram Street', 2, NULL, 0, 11, CURDATE()), - (11, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'NY roofs', 122, NULL, 0, 3, CURDATE()), - (12, 1, 1, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()), - (13, 1, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()), - (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, 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)), - (21, NULL, 5, 5, NULL, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Holland', 102, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), - (22, NULL, 5, 5, NULL, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Japan', 103, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), - (23, NULL, 10, 1, NULL, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'address 21', 121, NULL, 0, 8, CURDATE()), - (24 ,NULL, 10, 1, NULL, CURDATE(), CURDATE(), 101, 'Bruce Wayne', 1, NULL, 0, 8, CURDATE()); + (1 , 3, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Bat cave', 121, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (2 , 1, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (3 , 1, 7, 1, 6, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T2222222', 0, 3, DATE_ADD(CURDATE(), INTERVAL -2 MONTH)), + (4 , 3, 2, 1, 2, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T3333333', 0, 9, DATE_ADD(CURDATE(), INTERVAL -3 MONTH)), + (5 , 3, 3, 3, 3, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T4444444', 0, 10, DATE_ADD(CURDATE(), INTERVAL -4 MONTH)), + (6 , 1, 3, 3, 3, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (7 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Mountain Drive Gotham', 1, NULL, 0, 3, CURDATE()), + (8 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Bat cave', 121, NULL, 0, 3, CURDATE()), + (9 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Stark tower', 124, NULL, 0, 3, CURDATE()), + (10, 1, 1, 5, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'Ingram Street', 2, NULL, 0, 1, CURDATE()), + (11, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'NY roofs', 122, NULL, 0, 3, CURDATE()), + (12, 1, 1, 1, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()), + (13, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 3, CURDATE()), + (14, 1, 2, 1, NULL, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()), + (15, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()), + (16, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()), + (17, 1, 7, 2, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()), + (18, 1, 4, 4, 4, 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)), + (21, NULL, 5, 5, 5, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Holland', 102, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), + (22, NULL, 5, 5, 5, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Japan', 103, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), + (23, NULL, 8, 1, 7, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'address 21', 121, NULL, 0, 5, CURDATE()), + (24 ,NULL, 8, 1, 7, CURDATE(), CURDATE(), 101, 'Bruce Wayne', 1, NULL, 0, 5, CURDATE()); INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) VALUES @@ -1862,4 +1862,67 @@ INSERT INTO `vn`.`queuePriority`(`id`, `priority`) VALUES (1, 'Alta'), (2, 'Normal'), - (3, 'Baja'); \ No newline at end of file + (3, 'Baja'); + +INSERT INTO `vn`.`userPhone`(`id`, `userFk`, `typeFk`, `phone`) + VALUES + (1, 101, 'personalPhone', 1111111111), + (2, 102, 'personalPhone', 1111111111), + (3, 103, 'personalPhone', 1111111111), + (4, 104, 'personalPhone', 1111111111), + (5, 105, 'personalPhone', 1111111111), + (6, 106, 'personalPhone', 1111111111), + (7, 107, 'personalPhone', 1111111111), + (8, 108, 'personalPhone', 1111111111), + (9, 109, 'personalPhone', 1111111111), + (10, 110, 'personalPhone', 1111111111), + (11, 111, 'personalPhone', 1111111111), + (12, 112, 'personalPhone', 1111111111), + (13, 1, 'personalPhone', 623111111), + (14, 2, 'personalPhone', 623111111), + (15, 3, 'personalPhone', 623111111), + (16, 5, 'personalPhone', 623111111), + (17, 6, 'personalPhone', 623111111), + (18, 9, 'personalPhone', 623111111), + (19, 13, 'personalPhone', 623111111), + (20, 15, 'personalPhone', 623111111), + (21, 16, 'personalPhone', 623111111), + (22, 17, 'personalPhone', 623111111), + (23, 18, 'personalPhone', 623111111), + (24, 19, 'personalPhone', 623111111), + (25, 20, 'personalPhone', 623111111), + (26, 21, 'personalPhone', 623111111), + (27, 22, 'personalPhone', 623111111), + (28, 30, 'personalPhone', 623111111), + (29, 31, 'personalPhone', 623111111), + (30, 32, 'personalPhone', 623111111), + (31, 34, 'personalPhone', 623111111), + (32, 35, 'personalPhone', 623111111), + (33, 36, 'personalPhone', 623111111), + (34, 37, 'personalPhone', 623111111), + (35, 38, 'personalPhone', 623111111), + (36, 39, 'personalPhone', 623111111), + (37, 40, 'personalPhone', 623111111), + (38, 41, 'personalPhone', 623111111), + (39, 42, 'personalPhone', 623111111), + (40, 43, 'personalPhone', 623111111), + (41, 44, 'personalPhone', 623111111), + (42, 45, 'personalPhone', 623111111), + (43, 47, 'personalPhone', 623111111), + (44, 48, 'personalPhone', 623111111), + (45, 50, 'personalPhone', 623111111), + (46, 51, 'personalPhone', 623111111), + (47, 52, 'personalPhone', 623111111), + (48, 54, 'personalPhone', 623111111), + (49, 55, 'personalPhone', 623111111), + (50, 56, 'personalPhone', 623111111), + (51, 57, 'personalPhone', 623111111), + (52, 58, 'personalPhone', 623111111), + (53, 59, 'personalPhone', 623111111), + (54, 60, 'personalPhone', 623111111), + (55, 61, 'personalPhone', 623111111), + (56, 65, 'personalPhone', 623111111), + (57, 66, 'personalPhone', 623111111), + (65, 107, 'businessPhone', 700987987), + (67, 106, 'businessPhone', 1111111112), + (68, 106, 'personalPhone', 1111111113); diff --git a/e2e/helpers/components_selectors.js b/e2e/helpers/components_selectors.js deleted file mode 100644 index 3e2715ba2..000000000 --- a/e2e/helpers/components_selectors.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - 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 fb36e3279..4148a4a05 100644 --- a/e2e/helpers/extensions.js +++ b/e2e/helpers/extensions.js @@ -6,6 +6,121 @@ import config from './config.js'; let currentUser; +let asyncActions = { + // Generic extensions + + clickIfExists: async function(selector) { + let exists = await this.exists(selector); + if (exists) await this.click(selector); + return exists; + }, + + hasClass: async function(selector, className) { + return await this.evaluate((selector, className) => { + document.querySelector(selector).classList.contains(className); + }, selector, className); + }, + + parsedUrl: async function() { + return new URL(await this.url()); + }, + + // Salix specific extensions + + changeLanguageToEnglish: async function() { + let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]'; + + let lang = await this.waitToClick('#user') + .wait(langSelector) + .waitToGetProperty(`${langSelector} input`, 'value'); + + if (lang !== 'English') + await this.autocompleteSearch(langSelector, 'English'); + }, + + doLogin: async function(userName, password) { + if (password == null) password = 'nightmare'; + await this.wait(`vn-login [name=user]`) + .clearInput(`vn-login [name=user]`) + .write(`vn-login [name=user]`, userName) + .write(`vn-login [name=password]`, password) + .click(`vn-login button[type=submit]`); + }, + + login: async function(userName) { + if (currentUser !== userName) { + let logoutClicked = await this.clickIfExists('#logout'); + + if (logoutClicked) { + let buttonSelector = '.vn-dialog.shown button[response=ACCEPT]'; + this.wait(buttonSelector => { + return document.querySelector(buttonSelector) != null + || location.hash == '#!/login'; + }, buttonSelector); + await this.clickIfExists(buttonSelector); + } + + try { + await this.waitForURL('#!/login'); + } catch (e) { + this.goto(`${config.url}/#!/login`); + } + + await this.doLogin(userName, null) + .waitForURL('#!/') + .changeLanguageToEnglish(); + + currentUser = userName; + } else + await this.waitToClick('vn-topbar a[ui-sref="home"]'); + }, + + waitForLogin: async function(userName) { + await this.login(userName); + }, + + selectModule: async function(moduleName) { + let snakeName = moduleName.replace(/[\w]([A-Z])/g, m => { + return m[0] + '-' + m[1]; + }).toLowerCase(); + + await this.waitToClick(`vn-home a[ui-sref="${moduleName}.index"]`) + .waitForURL(snakeName); + }, + + loginAndModule: async function(userName, moduleName) { + await this.login(userName) + .selectModule(moduleName); + }, + + datePicker: async function(selector, changeMonth, day) { + let date = new Date(); + if (changeMonth) date.setMonth(date.getMonth() + changeMonth); + date.setDate(day ? day : 16); + date = date.toISOString().substr(0, 10); + + await this.wait(selector) + .evaluate((selector, date) => { + let input = document.querySelector(selector).$ctrl.input; + input.value = date; + input.dispatchEvent(new Event('change')); + }, selector, date); + }, + + pickTime: async function(selector, time) { + await this.wait(selector) + .evaluate((selector, time) => { + let input = document.querySelector(selector).$ctrl.input; + input.value = time; + input.dispatchEvent(new Event('change')); + }, selector, time); + }, + + isDisabled: async function(selector) { + return await this.hasClass(selector, 'disabled'); + } +}; + let actions = { clearTextarea: function(selector, done) { this.wait(selector) @@ -28,98 +143,6 @@ let actions = { .catch(done); }, - login: function(userName, done) { - if (currentUser) - this.waitToClick('#logout'); - - 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]`) - .then(() => { - currentUser = userName; - done(); - }) - .catch(done); - }; - - this.waitForURL('#!/login') - .then(doLogin) - .catch(() => { - this.goto(`${config.url}/#!/login`) - .then(doLogin) - .catch(done); - }); - }, - - waitForLogin: function(userName, done) { - if (currentUser === userName) { - return this.waitToClick('vn-topbar a[ui-sref="home"]') - .waitForURL('#!/') - .changeLanguageToEnglish() - .then(done) - .catch(done); - } - return this.login(userName) - .waitForURL('#!/') - .url() - .changeLanguageToEnglish() - .then(done) - .catch(done); - }, - - resetLogin: function(done) { - this.then(() => { - currentUser = undefined; - done(); - }) - .catch(done); - }, - - changeLanguageToEnglish: function(done) { - let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]'; - - this.waitToClick('#user') - .wait(langSelector) - .waitToGetProperty(`${langSelector} input`, 'value') - .then(lang => { - if (lang === 'English') { - this.then(done) - .catch(done); - } else { - this.autocompleteSearch(langSelector, 'English') - .then(done) - .catch(done); - } - }); - }, - - selectModule: function(moduleName, done) { - let snakeName = moduleName.replace(/[\w]([A-Z])/g, m => { - return m[0] + '-' + m[1]; - }).toLowerCase(); - this.waitToClick(`vn-home a[ui-sref="${moduleName}.index"]`) - .waitForURL(snakeName) - .then(done) - .catch(done); - }, - - loginAndModule: function(userName, moduleName, done) { - this.waitForLogin(userName) - .selectModule(moduleName) - .then(done) - .catch(done); - }, - - parsedUrl: function(done) { - this.url() - .then(url => { - done(null, new URL(url)); - }).catch(done); - }, - getProperty: function(selector, property, done) { this.evaluate_now((selector, property) => { return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim(); @@ -419,51 +442,6 @@ let actions = { }); }, - pickTime: function(selector, time, done) { - this.wait(selector) - .evaluate((selector, time) => { - let input = document.querySelector(selector); - input.value = time; - input.dispatchEvent(new Event('change')); - }, selector, time) - .then(done) - .catch(done); - }, - - datePicker: function(selector, changeMonth, day, done) { - this.wait(selector) - .mousedown(`${selector} input`) - .wait('.flatpickr-calendar.open'); - - if (changeMonth > 0) - this.mousedown(`.flatpickr-calendar.open .flatpickr-next-month`); - if (changeMonth < 0) - this.mousedown(`.flatpickr-calendar.open .flatpickr-prev-month`); - - let daySelector; - - if (!day) - daySelector = `.flatpickr-calendar.open .flatpickr-day:nth-child(16)`; - if (day) - daySelector = `.flatpickr-calendar.open .flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`; - - this.wait(selector => { - return document.querySelector(selector); - }, daySelector) - .evaluate(selector => { - let event = new MouseEvent('mousedown', { - bubbles: true, - cancelable: true, - view: window - }); - document.querySelector(selector).dispatchEvent(event); - }, daySelector) - .then(done) - .catch(() => { - done(new Error(`.datePicker(), for ${daySelector} timed out`)); - }); - }, - reloadSection: function(sectionRoute, done) { this.waitToClick('vn-icon[icon="desktop_windows"]') .wait('vn-card.summary') @@ -516,6 +494,16 @@ let actions = { }, }; +for (let name in asyncActions) { + let fn = asyncActions[name]; + + Nightmare.action(name, function(...args) { + let done = args[args.length - 1]; + fn.apply(this, args) + .then(res => done(null, res), done); + }); +} + Object.keys(actions).forEach(function(name) { let fn = actions[name]; Nightmare.action(name, fn); diff --git a/e2e/helpers/nightmare.js b/e2e/helpers/nightmare.js index 75794c28d..6706b6dc4 100644 --- a/e2e/helpers/nightmare.js +++ b/e2e/helpers/nightmare.js @@ -1,4 +1,6 @@ const Nightmare = require('nightmare'); +const config = require('./config.js'); + let nightmare; module.exports = function createNightmare(width = 1280, height = 720) { @@ -25,5 +27,5 @@ module.exports = function createNightmare(width = 1280, height = 720) { }); nightmare.header('Accept-Language', 'en'); - return nightmare; + return nightmare.goto(config.url); }; diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 5d8fd487c..a9a66c616 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -1,4 +1,3 @@ -import components from './components_selectors.js'; export default { globalItems: { @@ -22,29 +21,29 @@ export default { acceptButton: 'vn-confirm button[response=ACCEPT]' }, clientsIndex: { - searchClientInput: `${components.vnTextfield}`, + searchClientInput: `vn-textfield input`, searchButton: 'vn-searchbar vn-icon[icon="search"]', searchResult: 'vn-client-index .vn-list-item', - createClientButton: `${components.vnFloatButton}`, + createClientButton: `vn-float-button`, othersButton: 'vn-left-menu li[name="Others"] > a' }, createClientView: { - name: `${components.vnTextfield}[name="name"]`, - taxNumber: `${components.vnTextfield}[name="fi"]`, - socialName: `${components.vnTextfield}[name="socialName"]`, - street: `${components.vnTextfield}[name="street"]`, - postcode: `${components.vnTextfield}[name="postcode"]`, - city: `${components.vnTextfield}[name="city"]`, + name: `vn-textfield input[name="name"]`, + taxNumber: `vn-textfield input[name="fi"]`, + socialName: `vn-textfield input[name="socialName"]`, + street: `vn-textfield input[name="street"]`, + postcode: `vn-textfield input[name="postcode"]`, + city: `vn-textfield input[name="city"]`, 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"]`, + userName: `vn-textfield input[name="userName"]`, + email: `vn-textfield input[name="email"]`, salesPersonAutocomplete: `vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]`, - createButton: `${components.vnSubmit}`, + createButton: `button[type=submit]`, cancelButton: 'vn-button[href="#!/client/index"]' }, clientDescriptor: { - moreMenu: 'vn-client-descriptor vn-icon-menu > div > vn-icon', + moreMenu: 'vn-client-descriptor vn-icon-menu[icon=more_vert]', simpleTicketButton: '.vn-popover.shown .vn-drop-down li' }, clientBasicData: { @@ -56,17 +55,17 @@ export default { 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}` + saveButton: `button[type=submit]` }, clientFiscalData: { fiscalDataButton: 'vn-left-menu a[ui-sref="client.card.fiscalData"]', - socialNameInput: `${components.vnTextfield}[name="socialName"]`, - fiscalIdInput: `${components.vnTextfield}[name="fi"]`, - equalizationTaxCheckbox: 'vn-check[label="Is equalizated"]', + socialNameInput: `vn-textfield input[name="socialName"]`, + fiscalIdInput: `vn-textfield input[name="fi"]`, + equalizationTaxCheckbox: 'vn-check[ng-model="$ctrl.client.isEqualizated"]', acceptPropagationButton: 'vn-client-fiscal-data > vn-confirm button[response=ACCEPT]', - addressInput: `${components.vnTextfield}[name="street"]`, - postcodeInput: `${components.vnTextfield}[name="postcode"]`, - cityInput: `${components.vnTextfield}[name="city"]`, + addressInput: `vn-textfield input[name="street"]`, + postcodeInput: `vn-textfield input[name="postcode"]`, + cityInput: `vn-textfield input[name="city"]`, provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.provinceFk"]', countryAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.countryFk"]', activeCheckbox: 'vn-check[label="Active"]', @@ -76,12 +75,12 @@ export default { hasToInvoiceCheckbox: 'vn-check[label="Has to invoice"]', invoiceByMailCheckbox: 'vn-check[label="Invoice by mail"]', viesCheckbox: 'vn-check[label="Vies"]', - saveButton: `${components.vnSubmit}` + saveButton: `button[type=submit]` }, clientBillingData: { 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"]`, + IBANInput: `vn-client-billing-data vn-textfield input[name="iban"]`, + dueDayInput: `vn-client-billing-data vn-input-number input[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"]', @@ -92,20 +91,20 @@ export default { newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input', newBankEntityCode: 'vn-client-billing-data > vn-dialog vn-textfield[label="Entity Code"] input', acceptBankEntityButton: 'vn-client-billing-data > vn-dialog button[response="ACCEPT"]', - saveButton: `${components.vnSubmit}` + saveButton: `button[type=submit]` }, clientAddresses: { addressesButton: 'vn-left-menu a[ui-sref="client.card.address.index"]', - createAddress: `vn-client-address-index ${components.vnFloatButton}`, + createAddress: `vn-client-address-index vn-float-button`, defaultCheckboxInput: 'vn-check[label="Default"]', - consigneeInput: `${components.vnTextfield}[name="nickname"]`, - streetAddressInput: `${components.vnTextfield}[name="street"]`, - postcodeInput: `${components.vnTextfield}[name="postalCode"]`, - cityInput: `${components.vnTextfield}[name="city"]`, + consigneeInput: `vn-textfield input[name="nickname"]`, + streetAddressInput: `vn-textfield input[name="street"]`, + postcodeInput: `vn-textfield input[name="postalCode"]`, + cityInput: `vn-textfield input[name="city"]`, 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"]`, + phoneInput: `vn-textfield input[name="phone"]`, + mobileInput: `vn-textfield input[name="mobile"]`, defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]', secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]', firstEditAddress: 'vn-client-address-index div:nth-child(1) > a', @@ -117,34 +116,34 @@ export default { 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}`, + saveButton: `button[type=submit]`, cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]', cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button' }, clientWebAccess: { webAccessButton: 'vn-left-menu a[ui-sref="client.card.webAccess"]', enableWebAccessCheckbox: 'vn-check[label="Enable web access"]', - userNameInput: `${components.vnTextfield}[name="name"]`, - saveButton: `${components.vnSubmit}` + userNameInput: `vn-textfield input[name="name"]`, + saveButton: `button[type=submit]` }, clientNotes: { - addNoteFloatButton: `${components.vnFloatButton}`, + addNoteFloatButton: `vn-float-button`, noteInput: 'vn-textarea[label="Note"]', - saveButton: `${components.vnSubmit}`, + saveButton: `button[type=submit]`, firstNoteText: 'vn-client-note .text' }, clientCredit: { - addCreditFloatButton: `${components.vnFloatButton}`, - creditInput: `${components.vnInputNumber}[name="credit"]`, - saveButton: `${components.vnSubmit}`, + addCreditFloatButton: `vn-float-button`, + creditInput: `vn-input-number input[name="credit"]`, + saveButton: `button[type=submit]`, firstCreditText: 'vn-client-credit-index vn-card > div vn-table vn-tbody > vn-tr' }, clientGreuge: { - addGreugeFloatButton: `${components.vnFloatButton}`, - amountInput: `${components.vnInputNumber}[name="amount"]`, - descriptionInput: `${components.vnTextfield}[name="description"]`, + addGreugeFloatButton: `vn-float-button`, + amountInput: `vn-input-number input[name="amount"]`, + descriptionInput: `vn-textfield input[name="description"]`, typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.greuge.greugeTypeFk"]', - saveButton: `${components.vnSubmit}`, + saveButton: `button[type=submit]`, firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr' }, clientMandate: { @@ -163,7 +162,7 @@ export default { clientBalance: { balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]', companyAutocomplete: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyFk"]', - newPaymentButton: `${components.vnFloatButton}`, + newPaymentButton: `vn-float-button`, 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"]', @@ -182,7 +181,7 @@ export default { }, itemsIndex: { searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]', - createItemButton: `${components.vnFloatButton}`, + createItemButton: `vn-float-button`, searchResult: 'vn-item-index a.vn-tr', searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]', searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]', @@ -208,16 +207,16 @@ export default { saveFieldsButton: 'vn-item-index vn-dialog vn-horizontal:nth-child(16) > vn-button > button' }, itemCreateView: { - temporalName: `${components.vnTextfield}[name="provisionalName"]`, + temporalName: `vn-textfield input[name="provisionalName"]`, 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"]' + createButton: `button[type=submit]`, + cancelButton: 'vn-button[ui-sref="item.index"]' }, itemDescriptor: { goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]', - moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon', + moreMenu: 'vn-item-descriptor vn-icon-menu[icon=more_vert]', moreMenuRegularizeButton: '.vn-popover.shown .vn-drop-down li[name="Regularize stock"]', regularizeQuantityInput: 'vn-item-descriptor vn-dialog tpl-body > div > vn-textfield input', regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[ng-model="$ctrl.warehouseFk"]', @@ -238,7 +237,7 @@ export default { longNameInput: 'vn-textfield[ng-model="$ctrl.item.longName"] input', isActiveCheckbox: 'vn-check[label="Active"]', priceInKgCheckbox: 'vn-check[label="Price in kg"]', - submitBasicDataButton: `${components.vnSubmit}` + submitBasicDataButton: `button[type=submit]` }, itemTags: { goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]', @@ -257,19 +256,19 @@ export default { 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}` + submitItemTagsButton: `vn-item-tags button[type=submit]` }, itemTax: { 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}` + submitTaxButton: `vn-item-tax button[type=submit]` }, itemBarcodes: { addBarcodeButton: 'vn-item-barcode vn-icon[icon="add_circle"]', - thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > ${components.vnTextfield}`, - submitBarcodesButton: `vn-item-barcode ${components.vnSubmit}`, + thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > vn-textfield input`, + submitBarcodesButton: `vn-item-barcode button[type=submit]`, firstCodeRemoveButton: 'vn-item-barcode vn-horizontal vn-none vn-icon[icon="delete"]' }, itemNiches: { @@ -281,13 +280,13 @@ export default { 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[ng-model="niche.warehouseFk"]', thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input', - submitNichesButton: `vn-item-niche ${components.vnSubmit}` + submitNichesButton: `vn-item-niche button[type=submit]` }, itemBotanical: { - botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`, + botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > vn-textfield input`, 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}` + submitBotanicalButton: `vn-item-botanical button[type=submit]` }, itemSummary: { basicData: 'vn-item-summary [name="basicData"]', @@ -330,13 +329,13 @@ export default { 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}`, + searchTicketInput: `vn-ticket-index vn-textfield input`, + searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`, searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]', - advancedSearchButton: 'vn-ticket-search-panel vn-submit[label="Search"] input', + advancedSearchButton: 'vn-ticket-search-panel button[type=submit]', 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', + moreMenu: 'vn-ticket-index vn-icon-menu[icon=more_vert]', 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[ng-model="weekly.weekDay"] input', weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr', @@ -349,13 +348,13 @@ export default { 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}` + createButton: `button[type=submit]` }, ticketDescriptor: { idLabelValue: 'vn-ticket-descriptor vn-label-value[label="Id"]', stateLabelValue: 'vn-ticket-descriptor vn-label-value[label="State"]', goBackToModuleIndexButton: 'vn-ticket-descriptor a[ui-sref="ticket.index"]', - moreMenu: 'vn-ticket-descriptor vn-icon-menu > div > vn-icon', + moreMenu: 'vn-ticket-descriptor vn-icon-menu[icon=more_vert]', moreMenuAddStowaway: '.vn-popover.shown .vn-drop-down li[name="Add stowaway"]', moreMenuDeleteStowawayButton: '.vn-popover.shown .vn-drop-down li[name="Remove stowaway"]', moreMenuAddToTurn: '.vn-popover.shown .vn-drop-down li[name="Add turn"]', @@ -363,9 +362,9 @@ export default { moreMenuMakeInvoice: '.vn-popover.shown .vn-drop-down li[name="Make invoice"]', moreMenuChangeShippedHour: '.vn-popover.shown .vn-drop-down li[name="Change shipped hour"]', changeShippedHourDialog: 'vn-ticket-descriptor vn-dialog[vn-id="changeShippedDialog"]', - changeShippedHourInput: 'vn-ticket-descriptor vn-dialog[vn-id="changeShippedDialog"] vn-input-time[vn-id="newShipped"]', + changeShippedHourInput: 'vn-dialog[vn-id="changeShippedDialog"] [ng-model="$ctrl.newShipped"]', addStowawayDialogFirstTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tbody vn-tr', - shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]', + shipButton: 'vn-ticket-descriptor vn-icon[icon="icon-stowaway"]', thursdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(4)', saturdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)', closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]', @@ -380,7 +379,7 @@ export default { addNoteButton: 'vn-icon[icon="add_circle"]', firstNoteTypeAutocomplete: 'vn-autocomplete[ng-model="observation.observationTypeFk"]', firstDescriptionInput: 'vn-textfield[label="Description"] input', - submitNotesButton: `${components.vnSubmit}` + submitNotesButton: `button[type=submit]` }, ticketExpedition: { expeditionButton: 'vn-left-menu a[ui-sref="ticket.card.expedition"]', @@ -395,7 +394,7 @@ export default { firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]', addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]', clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] .icons > vn-icon[icon=clear]', - savePackagesButton: `${components.vnSubmit}` + savePackagesButton: `button[type=submit]` }, ticketSales: { saleButton: 'vn-left-menu a[ui-sref="ticket.card.sale"]', @@ -421,7 +420,6 @@ export default { 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[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', @@ -461,9 +459,9 @@ export default { }, ticketTracking: { trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]', - createStateButton: `${components.vnFloatButton}`, + createStateButton: `vn-float-button`, stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[ng-model="$ctrl.stateFk"]', - saveButton: `${components.vnSubmit}`, + saveButton: `button[type=submit]`, cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]' }, ticketBasicData: { @@ -472,8 +470,8 @@ export default { 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', + nextStepButton: 'vn-step-control .buttons > section:last-child vn-button', + finalizeButton: 'vn-step-control .buttons > section:last-child button[type=submit]', stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)', chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]', }, @@ -484,11 +482,11 @@ export default { addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button', 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"]', + atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[ng-model="$ctrl.ticketRequest.attenderFk"]', 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', + saveButton: 'vn-ticket-request-create button[type=submit]', firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)', }, @@ -500,7 +498,7 @@ 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', + firstAddDescriptionButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"]', 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', @@ -508,22 +506,22 @@ export default { 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}`, + saveServiceButton: `button[type=submit]`, saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button' }, createStateView: { 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}` + saveStateButton: `button[type=submit]` }, claimsIndex: { - searchClaimInput: `vn-claim-index ${components.vnTextfield}`, + searchClaimInput: `vn-claim-index vn-textfield input`, searchResult: 'vn-claim-index vn-card > div > vn-table > div > vn-tbody > a', searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]' }, claimDescriptor: { - moreMenu: 'vn-claim-descriptor vn-icon-menu[vn-id="more-button"]', + moreMenu: 'vn-claim-descriptor vn-icon-menu[icon=more_vert]', moreMenuDeleteClaim: '.vn-popover.shown .vn-drop-down li[name="Delete claim"]', acceptDeleteClaim: 'vn-claim-descriptor > vn-confirm[vn-id="confirm-delete-claim"] button[response="ACCEPT"]' }, @@ -531,20 +529,20 @@ export default { 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[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', + 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', itemDescriptorPopoverItemDiaryButton: '.vn-popover.shown vn-item-descriptor a[href="#!/item/2/diary"]', - firstDevelopmentWorker: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(5) > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > span', - firstDevelopmentWorkerGoToClientButton: '.vn-popover.shown vn-worker-descriptor div.quicklinks > a[href="#!/client/21/summary"]', - firstActionTicketId: 'vn-claim-summary > vn-card > div > vn-horizontal > vn-auto:nth-child(6) > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', + firstDevelopmentWorker: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(5) vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > span', + firstDevelopmentWorkerGoToClientButton: '.vn-popover.shown vn-worker-descriptor vn-quick-links > a[href="#!/client/21/summary"]', + firstActionTicketId: 'vn-claim-summary > vn-card > div > vn-horizontal > vn-auto:nth-child(6) vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor' }, claimBasicData: { claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]', - responsabilityInputRange: 'vn-input-range', + responsabilityInputRange: 'vn-range', observationInput: 'vn-textarea[ng-model="$ctrl.claim.observation"] textarea', - saveButton: `${components.vnSubmit}` + saveButton: `button[type=submit]` }, 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', @@ -570,7 +568,7 @@ export default { 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}` + saveDevelopmentButton: `button[type=submit]` }, claimAction: { importClaimButton: 'vn-claim-action vn-button[label="Import claim"]', @@ -585,9 +583,9 @@ export default { searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr', searchResultDate: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)', searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)', - searchOrderInput: `vn-order-index ${components.vnTextfield}`, + searchOrderInput: `vn-order-index vn-textfield input`, searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]', - createOrderButton: `${components.vnFloatButton}`, + createOrderButton: `vn-float-button`, }, orderDescriptor: { returnToModuleIndexButton: 'vn-order-descriptor a[ui-sref="order.index"]', @@ -598,7 +596,7 @@ export default { addressAutocomplete: 'vn-autocomplete[label="Address"]', agencyAutocomplete: 'vn-autocomplete[label="Agency"]', landedDatePicker: 'vn-date-picker[label="Landed"]', - createButton: `${components.vnSubmit}`, + createButton: `button[type=submit]`, cancelButton: 'vn-button[href="#!/client/index"]' }, orderCatalog: { @@ -610,16 +608,16 @@ export default { 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-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', + searchTagButton: 'vn-order-catalog-search-panel button[type=submit]', + thirdFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(3) vn-icon[icon=cancel]', + fourthFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(4) vn-icon[icon=cancel]', }, orderBasicData: { clientAutocomplete: 'vn-autocomplete[label="Client"]', addressAutocomplete: 'vn-autocomplete[label="Address"]', agencyAutocomplete: 'vn-autocomplete[label="Agency"]', observationInput: 'vn-textarea[label="Observation"] textarea', - saveButton: `${components.vnSubmit}`, + saveButton: `button[type=submit]`, acceptButton: 'vn-order-basic-data vn-confirm[vn-id="confirm"] button[response="ACCEPT"]' }, orderLine: { @@ -637,7 +635,7 @@ export default { 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"]' + submitButton: 'vn-route-create button[type=submit]' }, routeDescriptor: { volume: 'vn-route-descriptor vn-label-value[label="Volume"] > section > span' @@ -654,7 +652,7 @@ export default { 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' + saveButton: 'vn-route-basic-data button[type=submit]' }, routeTickets: { firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[ng-model="ticket.priority"] input', @@ -669,10 +667,10 @@ export default { }, workerPbx: { extensionInput: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"] input', - saveButton: 'vn-worker-pbx vn-submit[label="Save"] input' + saveButton: 'vn-worker-pbx button[type=submit]' }, workerTimeControl: { - timeDialogInput: 'vn-worker-time-control > vn-dialog input', + timeDialogInput: '.vn-dialog.shown [ng-model="$ctrl.newTime"]', mondayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button > button > vn-icon', tuesdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(2) > vn-icon-button > button > vn-icon', wednesdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(3) > vn-icon-button > button > vn-icon', @@ -721,12 +719,12 @@ export default { navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]' }, invoiceOutIndex: { - searchInvoiceOutInput: `vn-invoice-out-index ${components.vnTextfield}`, + searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`, searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]', searchResult: 'vn-invoice-out-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr', }, invoiceOutDescriptor: { - moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[vn-id="more-button"]', + moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[icon=more_vert]', moreMenuDeleteInvoiceOut: '.vn-popover.shown .vn-drop-down li[name="Delete Invoice"]', moreMenuBookInvoiceOut: '.vn-popover.shown .vn-drop-down li[name="Book invoice"]', moreMenuShowInvoiceOutPdf: '.vn-popover.shown .vn-drop-down li[name="Show invoice PDF"]', diff --git a/e2e/paths/01-login/01_login.spec.js b/e2e/paths/01-login/01_login.spec.js index a33be0c25..03acc98a8 100644 --- a/e2e/paths/01-login/01_login.spec.js +++ b/e2e/paths/01-login/01_login.spec.js @@ -1,58 +1,35 @@ import createNightmare from '../../helpers/nightmare'; -import config from '../../helpers/config.js'; - describe('Login path', () => { const nightmare = createNightmare(); it('should receive an error when the username is incorrect', async() => { - const username = 'nobody'; - const password = 'nightmare'; - const result = await nightmare - .goto(`${config.url}/#!/login`) - .wait(`vn-login input[name=user]`) - .write(`vn-login input[name=user]`, username) - .write(`vn-login input[name=password]`, password) - .click(`vn-login input[type=submit]`) + .doLogin('badUser', null) .waitForLastSnackbar(); expect(result.length).toBeGreaterThan(0); }); it('should receive an error when the username is blank', async() => { - const password = 'nightmare'; - const result = await nightmare - .clearInput(`vn-login input[name=user]`) - .write(`vn-login input[name=password]`, password) - .click(`vn-login input[type=submit]`) + .doLogin('', null) .waitForLastSnackbar(); expect(result.length).toBeGreaterThan(0); }); it('should receive an error when the password is incorrect', async() => { - const username = 'employee'; - const password = 'badpassword'; - const result = await nightmare - .write(`vn-login input[name=user]`, username) - .write(`vn-login input[name=password]`, password) - .click(`vn-login input[type=submit]`) + .doLogin('employee', 'badPassword') .waitForLastSnackbar(); expect(result.length).toBeGreaterThan(0); }); it('should log in', async() => { - const username = 'employee'; - const password = 'nightmare'; - const url = await nightmare - .write(`vn-login input[name=user]`, username) - .write(`vn-login input[name=password]`, password) - .click(`vn-login input[type=submit]`) + .doLogin('employee', null) .wait('#logout') .parsedUrl(); diff --git a/e2e/paths/02-client-module/03_edit_fiscal_data.spec.js b/e2e/paths/02-client-module/03_edit_fiscal_data.spec.js index ae3020d2a..cbe3722de 100644 --- a/e2e/paths/02-client-module/03_edit_fiscal_data.spec.js +++ b/e2e/paths/02-client-module/03_edit_fiscal_data.spec.js @@ -42,9 +42,7 @@ describe('Client Edit fiscalData path', () => { it('should not be able to edit the verified data checkbox', async() => { const result = await nightmare .wait(selectors.clientFiscalData.verifiedDataCheckbox) - .evaluate(selector => { - return document.querySelector(selector).getAttribute('disabled'); - }, selectors.clientFiscalData.verifiedDataCheckbox); + .isDisabled(selectors.clientFiscalData.verifiedDataCheckbox); expect(result).toBeTruthy(); }); 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 52e2db237..398185ba5 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 @@ -16,9 +16,7 @@ describe('Client lock verified data path', () => { const result = await nightmare .wait(200) .wait(selectors.clientFiscalData.verifiedDataCheckbox) - .evaluate(selector => { - return document.querySelector(selector).getAttribute('disabled'); - }, selectors.clientFiscalData.verifiedDataCheckbox); + .isDisabled(selectors.clientFiscalData.verifiedDataCheckbox); expect(result).toBeTruthy(); }); diff --git a/e2e/paths/03-worker-module/02_time_control.spec.js b/e2e/paths/03-worker-module/02_time_control.spec.js index 42be3e9e0..be48b13dd 100644 --- a/e2e/paths/03-worker-module/02_time_control.spec.js +++ b/e2e/paths/03-worker-module/02_time_control.spec.js @@ -358,7 +358,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym doesn't have hours set on the next months first week`, async() => { const wholeWeekHours = await nightmare .waitToClick(selectors.workerTimeControl.nextMonthButton) - .waitToClick(selectors.workerTimeControl.nextMonthButton) + .waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '00:00 Hours') .waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText'); expect(wholeWeekHours).toEqual('00:00 Hours'); diff --git a/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js b/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js index 0f02c6af7..3dcac0765 100644 --- a/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js +++ b/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js @@ -30,6 +30,7 @@ describe('Ticket Edit basic data path', () => { it(`should confirm the zone autocomplete is enabled for the role productionBoss`, async() => { const disabled = await nightmare + .waitForSpinnerLoad() .wait(selectors.ticketBasicData.zoneAutocomplete) .evaluate(selector => { return document.querySelector(selector).disabled; diff --git a/e2e/paths/05-ticket-module/12_descriptor.spec.js b/e2e/paths/05-ticket-module/12_descriptor.spec.js index 5c80bbfe8..b9c6c7c87 100644 --- a/e2e/paths/05-ticket-module/12_descriptor.spec.js +++ b/e2e/paths/05-ticket-module/12_descriptor.spec.js @@ -34,7 +34,7 @@ describe('Ticket descriptor path', () => { const result = await nightmare .waitToClick(selectors.ticketDescriptor.moreMenu) .waitToClick(selectors.ticketDescriptor.moreMenuChangeShippedHour) - .write(selectors.ticketDescriptor.changeShippedHourInput, '08:15') + .pickTime(selectors.ticketDescriptor.changeShippedHourInput, '08:15') .waitToClick(selectors.ticketDescriptor.acceptChangeHourButton) .waitForLastSnackbar(); diff --git a/e2e/paths/05-ticket-module/13_services.spec.js b/e2e/paths/05-ticket-module/13_services.spec.js index 31d262778..7172ab5a6 100644 --- a/e2e/paths/05-ticket-module/13_services.spec.js +++ b/e2e/paths/05-ticket-module/13_services.spec.js @@ -17,9 +17,7 @@ describe('Ticket services path', () => { const result = await nightmare .waitToClick(selectors.ticketService.addServiceButton) .wait(selectors.ticketService.firstAddDescriptionButton) - .evaluate(selector => { - return document.querySelector(selector).disabled; - }, selectors.ticketService.firstAddDescriptionButton); + .isDisabled(selectors.ticketService.firstAddDescriptionButton); expect(result).toBeTruthy(); }); diff --git a/e2e/paths/07-order-module/02_catalog.spec.js b/e2e/paths/07-order-module/02_catalog.spec.js index d2a862958..cad95741e 100644 --- a/e2e/paths/07-order-module/02_catalog.spec.js +++ b/e2e/paths/07-order-module/02_catalog.spec.js @@ -66,7 +66,7 @@ describe('Order catalog', () => { const result = await nightmare .waitToClick(selectors.orderCatalog.fourthFilterRemoveButton) .waitToClick(selectors.orderCatalog.thirdFilterRemoveButton) - .waitForNumberOfElements('section.product', 4) + .waitForNumberOfElements('.product', 4) .countElement('section.product'); expect(result).toEqual(4); diff --git a/front/core/components/autocomplete/index.html b/front/core/components/autocomplete/index.html index 2e5cc39f6..c0caf061b 100755 --- a/front/core/components/autocomplete/index.html +++ b/front/core/components/autocomplete/index.html @@ -1,6 +1,6 @@
+ on-close-start="$ctrl.focus()"> \ No newline at end of file diff --git a/front/core/components/autocomplete/index.js b/front/core/components/autocomplete/index.js index 69114d028..49dd14b07 100755 --- a/front/core/components/autocomplete/index.js +++ b/front/core/components/autocomplete/index.js @@ -228,7 +228,7 @@ export default class Autocomplete extends Field { event.preventDefault(); } - onContainerMouseDown(event) { + onContainerClick(event) { if (event.defaultPrevented) return; event.preventDefault(); this.showDropDown(); @@ -260,6 +260,7 @@ export default class Autocomplete extends Field { } showDropDown(search) { + if (this.readonly) return; this.assignDropdownProps(); this.$.dropDown.show(this.container, search); } diff --git a/front/core/components/autocomplete/style.scss b/front/core/components/autocomplete/style.scss index af45902cc..6be42c3bc 100755 --- a/front/core/components/autocomplete/style.scss +++ b/front/core/components/autocomplete/style.scss @@ -20,4 +20,7 @@ vn-autocomplete.vn-field { } } } + &.readonly > .container > .icons.post { + display: none; + } } diff --git a/front/core/components/button-menu/button-menu.html b/front/core/components/button-menu/button-menu.html deleted file mode 100644 index 5beeb12fb..000000000 --- a/front/core/components/button-menu/button-menu.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - -
\ No newline at end of file diff --git a/front/core/components/button-menu/index.html b/front/core/components/button-menu/index.html new file mode 100644 index 000000000..b0977bb93 --- /dev/null +++ b/front/core/components/button-menu/index.html @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/front/core/components/button-menu/button-menu.js b/front/core/components/button-menu/index.js similarity index 83% rename from front/core/components/button-menu/button-menu.js rename to front/core/components/button-menu/index.js index 99f3879ee..d31094d36 100644 --- a/front/core/components/button-menu/button-menu.js +++ b/front/core/components/button-menu/index.js @@ -1,17 +1,13 @@ import ngModule from '../../module'; -import Input from '../../lib/input'; +import Button from '../button'; import assignProps from '../../lib/assign-props'; import './style.scss'; -export default class ButtonMenu extends Input { +export default class ButtonMenu extends Button { constructor($element, $scope, $transclude) { super($element, $scope); this.$transclude = $transclude; - this.input = this.element.querySelector('.mdl-button'); - $element.on('click', e => { - if (!this.disabled) - this.onClick(e); - }); + $element.on('click', e => this.onClick(e)); } get model() { @@ -46,6 +42,7 @@ export default class ButtonMenu extends Input { } onClick(event) { + if (this.disabled) return; if (event.defaultPrevented) return; event.preventDefault(); this.emit('open'); @@ -85,15 +82,14 @@ export default class ButtonMenu extends Input { } ButtonMenu.$inject = ['$element', '$scope', '$transclude']; -ngModule.component('vnButtonMenu', { - template: require('./button-menu.html'), +ngModule.vnComponent('vnButtonMenu', { + template: require('./index.html'), + controller: ButtonMenu, bindings: { - label: '@', showField: '@?', selection: ' - {{$ctrl.label}} - - - \ No newline at end of file diff --git a/front/core/components/button/button.js b/front/core/components/button/button.js deleted file mode 100644 index 4e5cf50a2..000000000 --- a/front/core/components/button/button.js +++ /dev/null @@ -1,34 +0,0 @@ -import ngModule from '../../module'; -import Input from '../../lib/input'; -import './style.scss'; - -export default class Button extends Input { - constructor($element) { - super($element); - this.$element = $element; - this.input = this.element.querySelector('.mdl-button'); - - $element[0].addEventListener('click', event => { - if (this.disabled) - event.stopImmediatePropagation(); - }); - } - - $onInit() { - if (!this.type) - this.type = 'button'; - } -} -Button.$inject = ['$element']; - -ngModule.component('vnButton', { - controller: Button, - template: require('./button.html'), - bindings: { - label: '@?', - disabled: ' + + {{::$ctrl.label}} + + + + \ No newline at end of file diff --git a/front/core/components/button/index.js b/front/core/components/button/index.js new file mode 100644 index 000000000..e1a86eb5d --- /dev/null +++ b/front/core/components/button/index.js @@ -0,0 +1,46 @@ +import ngModule from '../../module'; +import FormInput from '../form-input'; +import './style.scss'; + +export default class Button extends FormInput { + constructor($element, $scope) { + super($element, $scope); + this.design = 'colored'; + this.input = this.element.querySelector('button'); + + let element = this.element; + element.tabIndex = 0; + element.classList.add('vn-button'); + this.element.addEventListener('keyup', e => this.onKeyup(e)); + this.element.addEventListener('click', e => this.onClick(e)); + } + + $onInit() { + this.element.classList.add(this.design); + if (!this.type) this.type = 'button'; + } + + onKeyup(event) { + if (event.code == 'Space') + this.onClick(event); + } + + onClick(event) { + if (event.defaultPrevented) return; + // event.preventDefault(); + + // FIXME: Don't stop event propagation + if (this.disabled) event.stopImmediatePropagation(); + } +} +Button.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnButton', { + controller: Button, + template: require('./index.html'), + bindings: { + icon: '@?', + type: '@?' + } +}); + diff --git a/front/core/components/button/style.scss b/front/core/components/button/style.scss index 84bd8659a..74dcbcf9b 100644 --- a/front/core/components/button/style.scss +++ b/front/core/components/button/style.scss @@ -1,5 +1,83 @@ -vn-button { - & > button > vn-icon { - vertical-align: middle; +@import "variables"; + +.vn-button { + display: inline-flex; + align-items: center; + justify-content: center; + height: 36px; + border: none; + border-radius: .1em; + font-family: vn-font-bold; + text-transform: uppercase; + font-size: 14px; + cursor: pointer; + box-sizing: border-box; + outline: none; + + & > button { + width: 100%; + padding: 0 12px; + box-sizing: border-box; + background-color: transparent; + border: none; + height: inherit; + color: inherit; + font: inherit; + display: block; + text-transform: inherit; + cursor: inherit; + outline: none; + display: flex; + align-items: center; + justify-content: center; + + & > vn-icon { + vertical-align: middle; + color: inherit; + font-size: 1.7em; + } } -} \ No newline at end of file + &.colored { + color: white; + background-color: $color-main; + box-shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3); + transition: background 200ms ease-in-out; + + &:not(.disabled) { + &:hover, + &:focus { + background-color: lighten($color-main, 10%); + } + } + } + &.flat { + color: $color-main; + background-color: transparent; + box-shadow: none; + transition: background 200ms ease-in-out; + + &:not(.disabled) { + &:hover, + &:focus { + background-color: $color-hover-cd; + } + } + } + &:hover, + &:focus { + outline: none; + } + &.round { + border-radius: 50%; + height: 3.8em; + width: 3.8em; + + & > button > span { + display: none; + } + } + &.disabled { + opacity: .7; + cursor: initial; + } +} diff --git a/front/core/components/card/card.js b/front/core/components/card/card.js index b812ccf61..a37835995 100644 --- a/front/core/components/card/card.js +++ b/front/core/components/card/card.js @@ -7,7 +7,7 @@ export default function directive() { transclude: true, template: require('./card.html'), link: function($scope, $element, $attrs, $ctrl, $transclude) { - $element.addClass('demo-card-wide mdl-shadow--2dp bg-panel'); + $element.addClass('demo-card-wide vn-shadow bg-panel'); $transclude($scope, function(clone) { angular.element($element[0].querySelector('div')).append(clone); diff --git a/front/core/components/card/style.scss b/front/core/components/card/style.scss index c2fe4e1ea..a459879df 100644 --- a/front/core/components/card/style.scss +++ b/front/core/components/card/style.scss @@ -1,3 +1,4 @@ vn-card { display: block; + box-sizing: border-box; } \ No newline at end of file diff --git a/front/core/components/chip/index.html b/front/core/components/chip/index.html index 8696fd8bb..38a56923d 100644 --- a/front/core/components/chip/index.html +++ b/front/core/components/chip/index.html @@ -1,8 +1,7 @@ - - - - - - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/front/core/components/chip/index.js b/front/core/components/chip/index.js index b35c00cbf..6a945559b 100644 --- a/front/core/components/chip/index.js +++ b/front/core/components/chip/index.js @@ -1,17 +1,12 @@ import ngModule from '../../module'; +import Component from '../../lib/component'; import './style.scss'; -export default class Chip { - -/** - * Remove chip event - */ - remove() { - if (this.onRemove) - this.onRemove(); +export default class Chip extends Component { + onRemove() { + if (!this.disabled) this.emit('remove'); } } - Chip.$inject = ['$element', '$scope', '$transclude']; ngModule.component('vnChip', { @@ -20,6 +15,6 @@ ngModule.component('vnChip', { transclude: true, bindings: { disabled: ' { controller = $componentController('vnChip', {$element, $scope, $transclude: () => {}}); })); - describe('remove()', () => { - it(`should call onRemove()`, () => { - controller.onRemove = () => {}; - spyOn(controller, 'onRemove'); - controller.remove(); + describe('onRemove()', () => { + it(`should emit remove event`, () => { + controller.emit = () => {}; + spyOn(controller, 'emit'); + controller.onRemove(); - expect(controller.onRemove).toHaveBeenCalledWith(); + expect(controller.emit).toHaveBeenCalledWith('remove'); }); }); }); diff --git a/front/core/components/chip/style.scss b/front/core/components/chip/style.scss index 81e81589c..ee7f46848 100644 --- a/front/core/components/chip/style.scss +++ b/front/core/components/chip/style.scss @@ -1,18 +1,53 @@ @import "variables"; vn-chip { + border-radius: 16px; + background-color: $color-bg; margin: 0 0.5em 0.5em 0; + color: $color-font; + font-size: 14px; + margin: .25em; + display: inline-flex; + align-items: center; + text-overflow: ellipsis; + white-space: nowrap; + height: 28px; + padding: 0 .7em; + overflow: hidden; - .mdl-chip { - background-color: rgba($color-main, 0.9); - color: #FFF + &.colored { + background-color: $color-main; + color: $color-font-dark; } - - .mdl-chip:active { - background-color: $color-main - } - - & > vn-one > span > span { + & > div { + display: flex; + align-items: center; max-width: 100%; + height: 100%; + + & > vn-avatar { + margin-left: -0.7em; + margin-right: .4em; + } } + & > vn-icon { + margin-left: .2em; + margin-right: -0.3em; + vertical-align: middle; + opacity: .6; + cursor: pointer; + transition: opacity 250ms ease-out; + + &:hover, + &:focus { + opacity: 1; + } + } +} + +vn-avatar { + display: inline-block; + height: 28px; + width: 28px; + border-radius: 50%; } \ No newline at end of file diff --git a/front/core/components/color-legend/index.html b/front/core/components/color-legend/index.html deleted file mode 100644 index 613fb2347..000000000 --- a/front/core/components/color-legend/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - {{legend.name}} - \ No newline at end of file diff --git a/front/core/components/color-legend/index.js b/front/core/components/color-legend/index.js deleted file mode 100644 index f72a501a7..000000000 --- a/front/core/components/color-legend/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import ngModule from '../../module'; -import Component from '../../lib/component'; -import './style.scss'; - -export default class Controller extends Component { - constructor($element, $scope, $attrs) { - super($element, $scope); - this.$attrs = $attrs; - } - - onClick(legend) { - this.emit('click', {legend}); - } -} - -Controller.$inject = ['$element', '$scope', '$attrs']; - -ngModule.component('vnColorLegend', { - template: require('./index.html'), - controller: Controller, - bindings: { - data: '`)($scope)[0]; - this.initPicker(); + this.input = $compile(``)($scope)[0]; + this.input.addEventListener('change', () => this.onValueUpdate()); + } + + onValueUpdate() { + let date = null; + let value = this.input.value; + + if (value) { + date = new Date(value); + + if (this.field) { + let orgDate = this.field instanceof Date + ? this.field + : new Date(this.field); + + date.setHours( + orgDate.getHours(), + orgDate.getMinutes(), + orgDate.getSeconds(), + orgDate.getMilliseconds() + ); + } + } + + super.field = date; + this.$.$applyAsync(); } get field() { @@ -18,69 +43,10 @@ class DatePicker extends 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(); + this.input.value = this.$filter('date')(value, 'yyyy-MM-dd'); } } -DatePicker.$inject = ['$element', '$scope', '$compile', '$translate']; +DatePicker.$inject = ['$element', '$scope', '$compile', '$translate', '$filter']; ngModule.vnComponent('vnDatePicker', { controller: DatePicker, @@ -88,12 +54,3 @@ ngModule.vnComponent('vnDatePicker', { options: ' { let $filter; let $element; let $ctrl; - let today; beforeEach(angular.mock.module('vnCore', $translateProvider => { $translateProvider.translations('en', {}); @@ -13,9 +12,6 @@ describe('Component vnDatePicker', () => { $element = $compile(``)($rootScope); $ctrl = $element.controller('vnDatePicker'); - - today = new Date(); - today.setUTCHours(0, 0, 0, 0); })); afterEach(() => { @@ -24,20 +20,12 @@ describe('Component vnDatePicker', () => { describe('field() setter', () => { it(`should display the formated the date`, () => { + let today; + today = new Date(); + today.setHours(0, 0, 0, 0); + $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'); + let displayed = $filter('date')(today, 'yyyy-MM-dd'); expect($ctrl.value).toEqual(displayed); }); diff --git a/front/core/components/drop-down/drop-down.html b/front/core/components/drop-down/drop-down.html index e9a37a675..2e4db921d 100755 --- a/front/core/components/drop-down/drop-down.html +++ b/front/core/components/drop-down/drop-down.html @@ -1,7 +1,8 @@ + on-close="$ctrl.onClose()" + on-close-start="$ctrl.emit('closeStart')">
this.$.input.focus()); this.emit('open'); } diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index bc60af0d3..ed99d1f5c 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -11,7 +11,6 @@ export default class Field extends FormInput { this.suffix = null; this.control = this.element.querySelector('.control'); - this.classList = this.element.classList; this.classList.add('vn-field'); this.element.addEventListener('click', e => this.onClick(e)); @@ -43,9 +42,11 @@ export default class Field extends FormInput { } set input(value) { - if (this.input) this.control.removeChild(this.input); + if (this.input) + this.control.removeChild(this.input); this._input = value; - this.control.appendChild(value); + if (value) + this.control.appendChild(value); } get input() { @@ -84,36 +85,8 @@ export default class Field extends FormInput { 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; - this.classList.toggle('disabled', this._disabled); - } - - get disabled() { - return this._disabled; - } - - set readonly(value) { - this._readonly = boolTag(value); - this.input.readOnly = this._readonly; - this.classList.toggle('readonly', this._readonly); - } - - get readonly() { - return this._readonly; - } - set required(value) { - this._required = boolTag(value); + this._required = value; let required = this.element.querySelector('.required'); display(required, this._required); } @@ -180,9 +153,13 @@ export default class Field extends FormInput { fix.innerText = text || ''; } + refreshTabIndex() { + this.input.tabIndex = this.disabled ? -1 : this.tabIndex; + } + onClick() { - if (event.defaultPrevented) return; - event.preventDefault(); + // if (event.defaultPrevented) return; + // event.preventDefault(); if (this.input !== document.activeElement) this.focus(); @@ -250,15 +227,10 @@ ngModule.vnComponent('vnField', { suffix: '@?', hint: '@?', error: ' .fix { padding-top: 24px; line-height: 24px; - font-size: $input-font-size; + font-size: $font-size; opacity: 0; transition: opacity 200ms ease-in-out; @@ -58,18 +58,39 @@ border: none; font-family: Arial, sans-serif; display: block; - font-size: $input-font-size; + font-size: $font-size; width: 100%; background: 0; color: inherit; box-sizing: border-box; min-height: 56px; - + } + & > input { + position: relative; + + &[type=time], + &[type=date], + &[type=password] { + opacity: 0; + transition: opacity 200ms ease-in-out; + cursor: pointer; + } &[type=time], &[type=date] { clip-path: inset(0 20px 0 0); - opacity: 0; - transition: opacity 200ms ease-in-out; + + &::-webkit-inner-spin-button, + &::-webkit-clear-button { + display: none; + -webkit-appearance: none; + } + &::-webkit-calendar-picker-indicator { + position: absolute; + height: 100%; + width: 100%; + opacity: 0; + cursor: pointer; + } } &[type=number] { -moz-appearance: textfield; @@ -83,6 +104,15 @@ &:invalid { box-shadow: none; } + &:-internal-autofill-selected { + &, + &:hover, + &:focus, + &:active, + &:valid { + box-shadow: 0 0 0 40px $color-bg-panel inset; + } + } } } } @@ -172,7 +202,8 @@ } & > .control > * { &[type=time], - &[type=date] { + &[type=date], + &[type=password] { opacity: 1; } } @@ -214,7 +245,6 @@ } } & > .hint { - z-index: -1; padding: 4px 0; height: 12px; color: rgba(0, 0, 0, .4); @@ -224,11 +254,12 @@ transition-duration: 200ms; transition-timing-function: ease-in-out; opacity: 0; + visibility: hidden; &.filled { - z-index: 0; opacity: 1; transform: translateY(0); + visibility: visible; } } &.invalid { diff --git a/front/core/components/float-button/float-button.html b/front/core/components/float-button/float-button.html deleted file mode 100644 index e74a1fce7..000000000 --- a/front/core/components/float-button/float-button.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/front/core/components/float-button/float-button.js b/front/core/components/float-button/float-button.js deleted file mode 100644 index beb3dd13b..000000000 --- a/front/core/components/float-button/float-button.js +++ /dev/null @@ -1,8 +0,0 @@ -import ngModule from '../../module'; - -ngModule.component('vnFloatButton', { - template: require('./float-button.html'), - bindings: { - icon: '@' - } -}); diff --git a/front/core/components/float-button/index.js b/front/core/components/float-button/index.js new file mode 100644 index 000000000..32ada7437 --- /dev/null +++ b/front/core/components/float-button/index.js @@ -0,0 +1,13 @@ +import ngModule from '../../module'; +import Button from '../button'; + +export default class FloatButton extends Button { + constructor($element, $scope) { + super($element, $scope); + this.element.classList.add('round'); + } +} + +ngModule.vnComponent('vnFloatButton', { + controller: FloatButton +}); diff --git a/front/core/components/form-input/index.js b/front/core/components/form-input/index.js index 74d98079c..0a1297788 100644 --- a/front/core/components/form-input/index.js +++ b/front/core/components/form-input/index.js @@ -9,6 +9,11 @@ import Component from '../../lib/component'; * @property {Boolean} disabled Put component in disabled mode */ export default class FormInput extends Component { + constructor($element, $scope) { + super($element, $scope); + this.classList = this.element.classList; + } + $onInit() { // XXX: Compatibility with old inputs let attrs = this.$element[0].attributes; @@ -41,6 +46,48 @@ export default class FormInput extends Component { get name() { return this.element.getAttribute('name'); } + + set disabled(value) { + this._disabled = boolTag(value); + this.input.disabled = this._disabled; + this.classList.toggle('disabled', this._disabled); + this.refreshTabIndex(); + } + + get disabled() { + return this._disabled; + } + + set readonly(value) { + this._readonly = boolTag(value); + this.input.readOnly = this._readonly; + this.classList.toggle('readonly', this._readonly); + } + + get readonly() { + return this._readonly; + } + + set tabIndex(value) { + this._tabIndex = value; + this.refreshTabIndex(); + } + + get tabIndex() { + return this._tabIndex; + } + + select() { + this.input.select(); + } + + focus() { + this.input.focus(); + } + + refreshTabIndex() { + this.element.tabIndex = this.disabled ? -1 : this.tabIndex; + } } ngModule.vnComponent('vnFormInput', { @@ -50,9 +97,14 @@ ngModule.vnComponent('vnFormInput', { field: '=?', name: '@?', disabled: ' - - \ No newline at end of file diff --git a/front/core/components/icon-button/icon-button.js b/front/core/components/icon-button/icon-button.js deleted file mode 100644 index ba3ccae3e..000000000 --- a/front/core/components/icon-button/icon-button.js +++ /dev/null @@ -1,39 +0,0 @@ -import ngModule from '../../module'; -import './style.scss'; - -export default class IconButton { - constructor($element) { - this.element = $element[0]; - - 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)); - } - - onKeyup(event) { - if (event.code == 'Space') - this.onClick(event); - } - - onClick(event) { - if (event.defaultPrevented) return; - event.preventDefault(); - - // FIXME: Don't use Event.stopPropagation() - let button = this.element.querySelector('button'); - if (this.disabled || button.disabled) - event.stopImmediatePropagation(); - } -} - -IconButton.$inject = ['$element']; -ngModule.component('vnIconButton', { - controller: IconButton, - template: require('./icon-button.html'), - bindings: { - icon: '@', - disabled: ' button { - background-color: transparent; - display: block; - color: inherit; - border: 0; - padding: 0; - font-size: inherit; - &:hover { - background-color: initial; - } - & > vn-icon { - display: block; - font-size: inherit; - } + & > button { + padding: .2em !important; + } + &:focus { + opacity: .6; } } diff --git a/front/core/components/icon-focusable/icon-focusable.html b/front/core/components/icon-focusable/icon-focusable.html deleted file mode 100644 index 4bd8ad78a..000000000 --- a/front/core/components/icon-focusable/icon-focusable.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/core/components/icon-focusable/icon-focusable.js b/front/core/components/icon-focusable/icon-focusable.js deleted file mode 100644 index 691ee570f..000000000 --- a/front/core/components/icon-focusable/icon-focusable.js +++ /dev/null @@ -1,29 +0,0 @@ -import ngModule from '../../module'; -import './style.scss'; - -export default class IconFocusable { - constructor($element) { - $element[0].tabIndex = 0; - $element.on("keyup", event => this.onKeyDown(event, $element)); - } - - onKeyDown(event, $element) { - if (event.defaultPrevented) return; - if (event.keyCode == 32 || event.keyCode == 13) { - event.preventDefault(); - $element.triggerHandler('click'); - } - } -} - -IconFocusable.$inject = ['$element']; -ngModule.component('vnIconFocusable', { - controller: IconFocusable, - template: require('./icon-focusable.html'), - bindings: { - icon: '@', - className: '@?', - enabled: ' i, - & > i.material-icons { - display: block; - font-size: inherit; - } -} \ No newline at end of file diff --git a/front/core/components/icon-menu/icon-menu.html b/front/core/components/icon-menu/icon-menu.html deleted file mode 100644 index 93de718ff..000000000 --- a/front/core/components/icon-menu/icon-menu.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - - - -
\ No newline at end of file diff --git a/front/core/components/icon-menu/icon-menu.js b/front/core/components/icon-menu/icon-menu.js deleted file mode 100644 index abc94504e..000000000 --- a/front/core/components/icon-menu/icon-menu.js +++ /dev/null @@ -1,41 +0,0 @@ -import ngModule from '../../module'; -import ButtonMenu from '../button-menu/button-menu'; -import './style.scss'; - -export default class IconMenu extends ButtonMenu { - constructor($element, $scope, $transclude) { - super($element, $scope); - this.$transclude = $transclude; - this.input = this.element.querySelector('.button'); - } -} -IconMenu.$inject = ['$element', '$scope', '$transclude']; - -ngModule.component('vnIconMenu', { - template: require('./icon-menu.html'), - bindings: { - label: '@', - showField: '@?', - selection: ' + + + + + \ No newline at end of file diff --git a/front/core/components/icon-menu/index.js b/front/core/components/icon-menu/index.js new file mode 100644 index 000000000..0549149a8 --- /dev/null +++ b/front/core/components/icon-menu/index.js @@ -0,0 +1,14 @@ +import ngModule from '../../module'; +import ButtonMenu from '../button-menu'; + +export default class IconMenu extends ButtonMenu { + constructor($element, $scope) { + super($element, $scope); + this.element.classList.add('flat'); + } +} + +ngModule.vnComponent('vnIconMenu', { + template: require('./index.html'), + controller: IconMenu +}); diff --git a/front/core/components/icon-menu/style.scss b/front/core/components/icon-menu/style.scss deleted file mode 100644 index df7a5e331..000000000 --- a/front/core/components/icon-menu/style.scss +++ /dev/null @@ -1,8 +0,0 @@ -vn-icon-menu { - cursor: pointer; - - vn-drop-down { - font-family: 'vn-font'; - outline: 0 - } -} \ No newline at end of file diff --git a/front/core/components/index.js b/front/core/components/index.js index 0433744f6..50c1a1bf8 100644 --- a/front/core/components/index.js +++ b/front/core/components/index.js @@ -10,36 +10,35 @@ import './subtitle/subtitle'; import './spinner/spinner'; import './snackbar/snackbar'; import './tooltip/tooltip'; -import './icon-menu/icon-menu'; -import './button-menu/button-menu'; import './popover/popover'; import './drop-down/drop-down'; import './menu/menu'; import './multi-check/multi-check'; -import './button/button'; -import './icon-button/icon-button'; -import './submit/submit'; import './card/card'; -import './float-button/float-button'; import './step-control/step-control'; import './label-value/label-value'; import './pagination/pagination'; import './searchbar/searchbar'; import './scroll-up/scroll-up'; import './autocomplete'; +import './button'; +import './button-menu'; import './calendar'; import './check'; import './chip'; -import './color-legend'; import './data-viewer'; import './date-picker'; import './field'; +import './float-button'; +import './icon-menu'; +import './icon-button'; import './input-number'; -import './input-range'; +import './range'; import './input-time'; import './input-file'; import './list'; import './radio'; +import './submit'; import './table'; import './td-editable'; import './textarea'; diff --git a/front/core/components/input-file/index.html b/front/core/components/input-file/index.html index 4e9c48631..58a2bea0c 100644 --- a/front/core/components/input-file/index.html +++ b/front/core/components/input-file/index.html @@ -1,42 +1,53 @@ -
-
-
-
-
+
+
+
+
+
+
+
{{$ctrl.value}}
- + type="file" + ng-model="$ctrl.files" + accept="{{$ctrl.accept}}"> +
-
-
-
- - info_outline - - - -
-
+
+
+
+ + + + + + +
+
+
+
+
+
+
+
diff --git a/front/core/components/input-file/index.js b/front/core/components/input-file/index.js index d0f93e4f8..3a0066e75 100644 --- a/front/core/components/input-file/index.js +++ b/front/core/components/input-file/index.js @@ -1,15 +1,13 @@ import ngModule from '../../module'; -import Input from '../../lib/input'; +import FormInput from '../form-input'; import './style.scss'; -export default class InputFile extends Input { +export default class InputFile extends FormInput { constructor($element, $scope) { super($element, $scope); - this.element = $element[0]; - this.hasFocus = false; this._multiple = false; this._value = 'Select a file'; - + this.input = this.element.querySelector('input'); this.registerEvents(); } @@ -106,26 +104,12 @@ export default class InputFile extends Input { } } -InputFile.$inject = ['$element', '$scope']; - -ngModule.component('vnInputFile', { +ngModule.vnComponent('vnInputFile', { template: require('./index.html'), controller: InputFile, - transclude: { - leftIcons: '?tLeftIcons', - rightIcons: '?tRightIcons' - }, bindings: { - label: '@?', - name: '@?', - disabled: ' .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 + input[type=file] { + display: none; } } \ No newline at end of file diff --git a/front/core/components/input-range/index.html b/front/core/components/input-range/index.html deleted file mode 100644 index 649e4de9f..000000000 --- a/front/core/components/input-range/index.html +++ /dev/null @@ -1,9 +0,0 @@ -
- - - - -
\ No newline at end of file diff --git a/front/core/components/input-range/index.js b/front/core/components/input-range/index.js deleted file mode 100644 index 8cfd898c7..000000000 --- a/front/core/components/input-range/index.js +++ /dev/null @@ -1,79 +0,0 @@ -import ngModule from '../../module'; -import Input from '../../lib/input'; -import './style.scss'; - -export default class inputRange extends Input { - constructor($element, $scope) { - super($element, $scope); - this.mdlElement = this.element.querySelector('.mdl-slider'); - componentHandler.upgradeElement(this.mdlElement); - this.mdlElement.addEventListener('change', () => { - this._value = this.input.value; - this.$.$applyAsync(); - if (this._value && this.onChange) - this.emit('change', {value: this._value}); - }); - } - - get value() { - return this._value; - } - - set value(value) { - this._value = value; - this.mdlElement.MaterialSlider.change(value); - } - - get max() { - return this.input.max; - } - - set max(value) { - this.input.max = value; - } - - get min() { - return this.input.min; - } - - set min(value) { - this.input.min = value; - } - - get step() { - return this.input.step; - } - - set step(value) { - this.input.step = value; - } - - get() { - return this._model; - } - - set model(value) { - this._model = value; - } - - set disabled(value) { - this.input.disabled = value; - } -} - -inputRange.$inject = ['$element', '$scope']; - -ngModule.component('vnInputRange', { - template: require('./index.html'), - controller: inputRange, - bindings: { - label: '@?', - disabled: ' parseInt(i) || null); - date = new Date(this.field || null); + + date = this.field instanceof Date + ? this.field + : new Date(this.field || null); date.setHours(split[0], split[1], 0, 0); } diff --git a/front/core/components/input-time/index.spec.js b/front/core/components/input-time/index.spec.js index f1ab14b50..0ab0fc762 100644 --- a/front/core/components/input-time/index.spec.js +++ b/front/core/components/input-time/index.spec.js @@ -24,7 +24,7 @@ describe('Component vnInputTime', () => { it(`should display the formated the date`, () => { let date = new Date(); $ctrl.field = date; - let displayed = $filter('dateTime')(date, 'HH:mm'); + let displayed = $filter('date')(date, 'HH:mm'); expect($ctrl.value).toEqual(displayed); }); diff --git a/front/core/components/list/style.scss b/front/core/components/list/style.scss index 7560d3288..0786cacf8 100644 --- a/front/core/components/list/style.scss +++ b/front/core/components/list/style.scss @@ -24,11 +24,10 @@ vn-icon-button { opacity: .4; - color: $color-main; margin-left: .5em; transition: opacity 250ms ease-out; padding: 0; - font-size: 2em; + font-size: 1.2em; &:hover { opacity: 1; diff --git a/front/core/components/multi-check/multi-check.js b/front/core/components/multi-check/multi-check.js index 63f89f84e..d354c9eef 100644 --- a/front/core/components/multi-check/multi-check.js +++ b/front/core/components/multi-check/multi-check.js @@ -1,5 +1,5 @@ import ngModule from '../../module'; -import Input from '../../lib/input'; +import FormInput from '../form-input'; import './style.scss'; /** @@ -8,7 +8,7 @@ import './style.scss'; * @param {Array} data List of options shown in drop-down * @param {Array} models Elements to check / unCheck */ -export default class MultiCheck extends Input { +export default class MultiCheck extends FormInput { constructor($element, $scope) { super($element, $scope); this._checked = false; diff --git a/front/core/components/popover/popover.js b/front/core/components/popover/popover.js index af6edb79a..39b44e206 100644 --- a/front/core/components/popover/popover.js +++ b/front/core/components/popover/popover.js @@ -109,6 +109,7 @@ 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,7 +119,7 @@ export default class Popover extends Component { this.bgMouseDownHandler = null; if (this.deregisterCallback) this.deregisterCallback(); - this.emit('close'); + this.emit('closeStart'); } /** diff --git a/front/core/components/range/index.html b/front/core/components/range/index.html new file mode 100644 index 000000000..ea35db58a --- /dev/null +++ b/front/core/components/range/index.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/front/core/components/range/index.js b/front/core/components/range/index.js new file mode 100644 index 000000000..80410875a --- /dev/null +++ b/front/core/components/range/index.js @@ -0,0 +1,50 @@ +import ngModule from '../../module'; +import FormInput from '../form-input'; +import './style.scss'; + +export default class Range extends FormInput { + constructor($element, $scope) { + super($element, $scope); + this.input = this.element.querySelector('input'); + } + + get max() { + return this.input.max; + } + + set max(value) { + this.input.max = value; + } + + get min() { + return this.input.min; + } + + set min(value) { + this.input.min = value; + } + + get step() { + return this.input.step; + } + + set step(value) { + this.input.step = value; + } + + refreshTabIndex() { + this.input.tabIndex = this.disabled ? -1 : this.tabIndex; + } +} + +ngModule.vnComponent('vnRange', { + template: require('./index.html'), + controller: Range, + bindings: { + min: ' label { + font-size: 12px; + + &.main { + color: $color-main; + } + &.min-label { + float: left; + } + &.max-label { + float: right; + } + } + & > input { + cursor: pointer; + height: 30px; + display: block; + width: 100%; + background: transparent; + border-color: transparent; + -webkit-appearance: none; + margin: .2em 0; + + &:focus { + outline: none; + } + &::-moz-focus-outer { + border: 0; + } + @include range( + "-moz-range-thumb", + "-moz-range-track" + ); + @include range( + "-webkit-slider-thumb", + "-webkit-slider-runnable-track" + ); + @include range( + "-ms-thumb", + "-ms-track" + ); + &:disabled { + cursor: initial; + } + } +} diff --git a/front/core/components/scroll-up/scroll-up.html b/front/core/components/scroll-up/scroll-up.html index 3f3b309d5..a3748acc6 100644 --- a/front/core/components/scroll-up/scroll-up.html +++ b/front/core/components/scroll-up/scroll-up.html @@ -1,5 +1,6 @@ - \ No newline at end of file + vn-tooltip="Go up" + class="round"> + \ No newline at end of file diff --git a/front/core/components/scroll-up/style.scss b/front/core/components/scroll-up/style.scss index 0d5cd6c58..1bf7ea982 100644 --- a/front/core/components/scroll-up/style.scss +++ b/front/core/components/scroll-up/style.scss @@ -1,6 +1,6 @@ vn-scroll-up { - top: 5em; - right: 2.5em; + top: 5.5em; + right: 2em; display: none; position: fixed; } \ No newline at end of file diff --git a/front/core/components/searchbar/searchbar.html b/front/core/components/searchbar/searchbar.html index c231b9752..069a9411c 100644 --- a/front/core/components/searchbar/searchbar.html +++ b/front/core/components/searchbar/searchbar.html @@ -1,6 +1,6 @@
diff --git a/front/core/components/searchbar/searchbar.js b/front/core/components/searchbar/searchbar.js index 9384b712b..b2275af0d 100644 --- a/front/core/components/searchbar/searchbar.js +++ b/front/core/components/searchbar/searchbar.js @@ -69,7 +69,7 @@ export default class Controller extends Component { this.$panelScope = this.$.$new(); this.$panel = this.$compile(`<${this.panel}/>`)(this.$panelScope); - let panel = this.$panel.isolateScope().$ctrl; + let panel = this.$panel[0].$ctrl; if (this.shownFilter) panel.filter = JSON.parse(JSON.stringify(this.shownFilter)); panel.onSubmit = filter => this.onPanelSubmit(filter); diff --git a/front/core/components/searchbar/style.scss b/front/core/components/searchbar/style.scss index b3fa4b1a2..03cc0cd8d 100644 --- a/front/core/components/searchbar/style.scss +++ b/front/core/components/searchbar/style.scss @@ -2,6 +2,7 @@ vn-searchbar { display: block; + width: 100%; } .search-panel { diff --git a/front/core/components/spinner/spinner.html b/front/core/components/spinner/spinner.html index 47b6e42fe..b6a3af8be 100644 --- a/front/core/components/spinner/spinner.html +++ b/front/core/components/spinner/spinner.html @@ -1,2 +1,12 @@ -
+
+ + + +
\ No newline at end of file diff --git a/front/core/components/spinner/spinner.js b/front/core/components/spinner/spinner.js index ca87323d5..aee973aa6 100644 --- a/front/core/components/spinner/spinner.js +++ b/front/core/components/spinner/spinner.js @@ -1,19 +1,18 @@ import ngModule from '../../module'; import Component from '../../lib/component'; -import './style.css'; +import './style.scss'; /** * A spinner to inform the user about loading process. */ export default class Spinner extends Component { constructor($element, $scope) { - super($element); + super($element, $scope); this._enable = false; this.spinner = $element[0].firstChild; - componentHandler.upgradeElement(this.spinner); } /** - * Enables/disables the spinner. + * Activates/deactivates the spinner. * * @param {Boolean} value %true to enable, %false to disable */ @@ -35,14 +34,14 @@ export default class Spinner extends Component { * Activates the spinner. */ start() { - this.spinner.MaterialSpinner.start(); + this.spinner.style.display = 'block'; this._enable = true; } /** * Deactivates the spinner. */ stop() { - this.spinner.MaterialSpinner.stop(); + this.spinner.style.display = 'none'; this._enable = false; } } diff --git a/front/core/components/spinner/spinner.spec.js b/front/core/components/spinner/spinner.spec.js index cb23783f7..437139c8c 100644 --- a/front/core/components/spinner/spinner.spec.js +++ b/front/core/components/spinner/spinner.spec.js @@ -1,7 +1,6 @@ import './spinner.js'; describe('Component vnSpinner', () => { - let $scope; let $element; let controller; @@ -9,53 +8,44 @@ describe('Component vnSpinner', () => { $translateProvider.translations('en', {}); })); - beforeEach(angular.mock.inject(($componentController, $rootScope) => { - $scope = $rootScope.$new(); - $element = angular.element('
'); - controller = $componentController('vnSpinner', {$scope, $element}); + beforeEach(angular.mock.inject(($compile, $rootScope) => { + $element = $compile(``)($rootScope); + controller = $element.controller('vnSpinner'); })); + afterEach(() => { + $element.remove(); + }); + describe('enable()', () => { - it(`should call start() based on a boolean value passed as argument`, () => { + it(`should call start() when enable is set to true`, () => { spyOn(controller, 'start'); - spyOn(controller, 'stop'); controller.enable = true; expect(controller.start).toHaveBeenCalledWith(); - expect(controller.stop).not.toHaveBeenCalledWith(); }); - it(`should call stop() based on a boolean value passed as argument`, () => { - spyOn(controller, 'start'); + it(`should call stop() when enable is set to false`, () => { spyOn(controller, 'stop'); controller.enable = false; - expect(controller.start).not.toHaveBeenCalledWith(); expect(controller.stop).toHaveBeenCalledWith(); }); }); describe('start()', () => { - it(`should call start() on the controller.materialSpinner then set controllers._enable to be truthy`, () => { - controller.spinner = {MaterialSpinner: {start: () => {}}}; - spyOn(controller.spinner.MaterialSpinner, 'start'); - controller._enable = false; + it(`should set enable to true`, () => { controller.start(); - expect(controller.spinner.MaterialSpinner.start).toHaveBeenCalledWith(); - expect(controller._enable).toBeTruthy(); + expect(controller.enable).toBeTruthy(); }); }); describe('stop()', () => { - it(`should call stop() on the controller.materialSpinner then set controllers._enable to be truthy`, () => { - controller.spinner = {MaterialSpinner: {stop: () => {}}}; - spyOn(controller.spinner.MaterialSpinner, 'stop'); - controller._enable = true; + it(`should set enable to false`, () => { controller.stop(); - expect(controller.spinner.MaterialSpinner.stop).toHaveBeenCalledWith(); - expect(controller._enable).toBeFalsy(); + expect(controller.enable).toBeFalsy(); }); }); }); diff --git a/front/core/components/spinner/style.css b/front/core/components/spinner/style.css deleted file mode 100644 index fbcd1a0bf..000000000 --- a/front/core/components/spinner/style.css +++ /dev/null @@ -1,3 +0,0 @@ -vn-spinner { - display: inline-block; -} \ No newline at end of file diff --git a/front/core/components/spinner/style.scss b/front/core/components/spinner/style.scss new file mode 100644 index 000000000..3be908caf --- /dev/null +++ b/front/core/components/spinner/style.scss @@ -0,0 +1,60 @@ +@import "variables"; + +vn-spinner { + display: inline-block; + min-height: 28px; + min-width: 28px; + + & > .loader { + position: relative; + margin: 0 auto; + width: 100%; + height: 100%; + + &:before { + content: ''; + display: block; + padding-top: 100%; + } + & > .circular { + animation: rotate 2s linear infinite; + transform-origin: center center; + height: 100%; + width: 100%; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + + & > .path { + stroke: $color-main; + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + stroke-linecap: square; + stroke-width: 6px; + animation: dash 1.5s ease-in-out infinite; + } + } + } + @keyframes rotate { + 100% { + transform: rotate(360deg); + } + } + @keyframes dash { + 0% { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 89, 200; + stroke-dashoffset: -35px; + } + 100% { + stroke-dasharray: 89, 200; + stroke-dashoffset: -124px; + } + } +} \ No newline at end of file diff --git a/front/core/components/step-control/style.scss b/front/core/components/step-control/style.scss index e1c29c9a2..9acc1f314 100644 --- a/front/core/components/step-control/style.scss +++ b/front/core/components/step-control/style.scss @@ -41,11 +41,5 @@ vn-step-control { & > .buttons > .step { display: flex } - & > .buttons > .step > .mdl-button { - line-height: 32px; - font-size: 12px; - padding: 0 12px; - height: 32px - } } } \ No newline at end of file diff --git a/front/core/components/submit/index.js b/front/core/components/submit/index.js new file mode 100644 index 000000000..ced73f0ba --- /dev/null +++ b/front/core/components/submit/index.js @@ -0,0 +1,13 @@ +import ngModule from '../../module'; +import Button from '../button'; + +export default class Controller extends Button { + constructor($element, $scope) { + super($element, $scope); + this.type = 'submit'; + } +} + +ngModule.vnComponent('vnSubmit', { + controller: Controller +}); diff --git a/front/core/components/submit/submit.html b/front/core/components/submit/submit.html deleted file mode 100644 index 6e77e4c16..000000000 --- a/front/core/components/submit/submit.html +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/front/core/components/submit/submit.js b/front/core/components/submit/submit.js deleted file mode 100644 index 41d63cf1d..000000000 --- a/front/core/components/submit/submit.js +++ /dev/null @@ -1,25 +0,0 @@ -import ngModule from '../../module'; -import Input from '../../lib/input'; - -export default class Controller extends Input { - constructor($element, $scope) { - super($element, $scope); - this.$element = $element; - this.input = $element[0].querySelector('input'); - } - - set disabled(value) { - this.input.disabled = value; - } -} - -Controller.$inject = ['$element', '$scope']; - -ngModule.component('vnSubmit', { - template: require('./submit.html'), - controller: Controller, - bindings: { - label: '@?', - disabled: ' span { - font-size: $input-font-size; + font-size: $font-size; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } & > .btn { position: relative; diff --git a/front/core/components/tooltip/tooltip.js b/front/core/components/tooltip/tooltip.js index e0bc323eb..036ec6958 100644 --- a/front/core/components/tooltip/tooltip.js +++ b/front/core/components/tooltip/tooltip.js @@ -13,7 +13,7 @@ export default class Tooltip extends Component { constructor($element, $scope, $timeout) { super($element, $scope); this.$timeout = $timeout; - $element.addClass('vn-tooltip mdl-shadow--4dp'); + $element.addClass('vn-tooltip vn-shadow'); this.position = 'down'; this.margin = 10; } diff --git a/front/core/components/treeview/child.html b/front/core/components/treeview/child.html new file mode 100644 index 000000000..1b928d5ea --- /dev/null +++ b/front/core/components/treeview/child.html @@ -0,0 +1,20 @@ +
+ + +
+
+ + + + +
+
diff --git a/front/core/components/treeview/content.js b/front/core/components/treeview/child.js similarity index 51% rename from front/core/components/treeview/content.js rename to front/core/components/treeview/child.js index 506117d4f..9e4edef35 100644 --- a/front/core/components/treeview/content.js +++ b/front/core/components/treeview/child.js @@ -2,22 +2,33 @@ import ngModule from '../../module'; class Controller { constructor($element, $scope, $compile) { - this.$element = $element; this.$scope = $scope; this.$compile = $compile; + this.element = $element[0]; + + this.element.$ctrl = this; + this.element.droppable = true; + this.dropCount = 0; + this.element.classList.add('vn-droppable'); } $onInit() { + const transcludeElement = this.element.querySelector('.content'); + const content = angular.element(transcludeElement); + if (this.item.parent) { this.treeview.$transclude(($clone, $scope) => { this.$contentScope = $scope; $scope.item = this.item; - this.$element.append($clone); + content.append($clone); }); + + this.element.draggable = true; + this.element.classList.add('vn-draggable'); } else { let template = `{{$ctrl.treeview.rootLabel}}`; let $clone = this.$compile(template)(this.$scope); - this.$element.append($clone); + content.append($clone); } } @@ -25,10 +36,21 @@ class Controller { if (this.$contentScope) this.$contentScope.$destroy(); } + + dragEnter() { + this.dropCount++; + + if (element != this.dropping) { + this.undrop(); + if (element) element.classList.add('dropping'); + this.dropping = element; + } + } } Controller.$inject = ['$element', '$scope', '$compile']; -ngModule.component('vnTreeviewContent', { +ngModule.component('vnTreeviewChild', { + template: require('./child.html'), controller: Controller, bindings: { item: '<' diff --git a/front/core/components/treeview/childs.html b/front/core/components/treeview/childs.html index 2dd7e77ef..de69ffb89 100644 --- a/front/core/components/treeview/childs.html +++ b/front/core/components/treeview/childs.html @@ -1,34 +1,11 @@
    -
  • -
    - - - - -
    - - - - -
    -
    +
  • + + + items="item.childs">
\ No newline at end of file diff --git a/front/core/components/treeview/childs.js b/front/core/components/treeview/childs.js index a9bd42077..d94c88df9 100644 --- a/front/core/components/treeview/childs.js +++ b/front/core/components/treeview/childs.js @@ -7,10 +7,6 @@ class Controller extends Component { event.preventDefault(); this.treeview.onToggle(item); } - - onDrop(item, dragged, dropped) { - this.treeview.onDrop(item, dragged, dropped); - } } ngModule.component('vnTreeviewChilds', { diff --git a/front/core/components/treeview/index.js b/front/core/components/treeview/index.js index d9da39215..728ea182e 100644 --- a/front/core/components/treeview/index.js +++ b/front/core/components/treeview/index.js @@ -1,18 +1,110 @@ import ngModule from '../../module'; import Component from '../../lib/component'; import './style.scss'; - import './childs'; -import './content'; +import './child'; /** * Treeview */ export default class Treeview extends Component { - constructor($element, $scope, $transclude) { + constructor($element, $scope, $transclude, $window) { super($element, $scope); this.$transclude = $transclude; + this.$window = $window; this.readOnly = true; + + this.element.addEventListener('dragstart', + event => this.dragStart(event)); + this.element.addEventListener('dragend', + event => this.dragEnd(event)); + + this.element.addEventListener('dragover', + event => this.dragOver(event)); + this.element.addEventListener('drop', + event => this.drop(event)); + this.element.addEventListener('dragenter', + event => this.dragEnter(event)); + this.element.addEventListener('dragleave', + event => this.dragLeave(event)); + + this.dropCount = 0; + } + + undrop() { + if (!this.dropping) return; + this.dropping.classList.remove('dropping'); + this.dropping = null; + } + + findDroppable(event) { + let element = event.target; + while (element != this.element && !element.droppable) + element = element.parentNode; + if (element == this.element) + return null; + return element; + } + + dragOver(event) { + this.dragClientY = event.clientY; + + // Prevents page reload + event.preventDefault(); + } + + onDragInterval() { + if (this.dragClientY > 0 && this.dragClientY < 75) + this.$window.scrollTo(0, this.$window.scrollY - 25); + } + + dragStart(event) { + event.target.classList.add('dragging'); + event.dataTransfer.setData('text', event.target.id); + + const element = this.findDroppable(event); + this.dragging = element; + + this.interval = setInterval(() => this.onDragInterval(), 100); + } + + dragEnd(event) { + event.target.classList.remove('dragging'); + this.undrop(); + this.dropCount = 0; + this.dragging = null; + clearInterval(this.interval); + } + + dragEnter(event) { + let element = this.findDroppable(event); + if (element) this.dropCount++; + + if (element != this.dropping) { + this.undrop(); + if (element) element.classList.add('dropping'); + this.dropping = element; + } + } + + dragLeave(event) { + let element = this.findDroppable(event); + + if (element) { + this.dropCount--; + if (this.dropCount == 0) this.undrop(); + } + } + + drop(event) { + event.preventDefault(); + this.element.classList.remove('dropping'); + + const $dropped = this.dropping.$ctrl.item; + const $dragged = this.dragging.$ctrl.item; + + if ($dropped != $dragged.parent) + this.emit('drop', {$dropped, $dragged}); } get data() { @@ -109,7 +201,10 @@ export default class Treeview extends Component { let childs = parent.childs; if (!childs) childs = []; - childs.push(item); + if (!parent.active) + this.unfold(parent); + else + childs.push(item); if (this.sortFunc) { childs = childs.sort((a, b) => @@ -120,12 +215,30 @@ export default class Treeview extends Component { if (parent) parent.sons++; } - onDrop(item, dragged, dropped) { - this.emit('drop', {item, dragged, dropped}); + move(item, newParent) { + if (newParent == item) return; + + if (item.parent) { + const parent = item.parent; + const childs = parent.childs; + const index = childs.indexOf(item); + parent.sons--; + + childs.splice(index, 1); + } + + item.parent = newParent; + + if (!newParent.active) { + this.unfold(newParent).then(() => { + item.parent.sons++; + }); + } else + this.create(item); } } -Treeview.$inject = ['$element', '$scope', '$transclude']; +Treeview.$inject = ['$element', '$scope', '$transclude', '$window']; ngModule.component('vnTreeview', { template: require('./index.html'), diff --git a/front/core/components/treeview/index.spec.js b/front/core/components/treeview/index.spec.js new file mode 100644 index 000000000..12cdc16e7 --- /dev/null +++ b/front/core/components/treeview/index.spec.js @@ -0,0 +1,260 @@ +describe('Component vnTreeview', () => { + let controller; + let $element; + + beforeEach(angular.mock.module('vnCore', $translateProvider => { + $translateProvider.translations('en', {}); + })); + + beforeEach(inject(($compile, $rootScope) => { + $element = $compile(``)($rootScope); + controller = $element.controller('vnTreeview'); + + const promise = new Promise(() => { + return {name: 'My item'}; + }); + + controller.fetchFunc = () => { + return promise; + }; + + controller.createFunc = () => { + return promise; + }; + + controller.removeFunc = () => { + return promise; + }; + + controller.sortFunc = () => { + return promise; + }; + })); + + afterEach(() => { + $element.remove(); + }); + + // See how to test DOM element in Jest + xdescribe('undrop()', () => { + it(`should reset all drop events and properties`, () => { + controller.dropping = angular.element(``); + spyOn(controller.dropping.classList, 'remove'); + + controller.undrop(); + + expect(controller.dropping).toBeNull(); + }); + }); + + describe('dragOver()', () => { + it(`should set the dragClientY property`, () => { + const event = new Event('dragover'); + event.clientY = 100; + + controller.dragOver(event); + + expect(controller.dragClientY).toEqual(100); + }); + }); + + describe('data() setter', () => { + it(`should set the items property nested into a root element`, () => { + const items = [{name: 'Item1'}, {name: 'Item2'}]; + controller.data = items; + + const rootItem = controller.items[0]; + + expect(rootItem.childs).toEqual(items); + }); + }); + + describe('fetch()', () => { + it(`should call the fetchFunc() method`, () => { + spyOn(controller, 'fetchFunc').and.returnValue( + new Promise(resolve => resolve([{name: 'My item'}])) + ); + controller.fetch().then(() => { + expect(controller.data).toBeDefined(); + }); + + expect(controller.fetchFunc).toHaveBeenCalledWith(); + }); + }); + + describe('setParent()', () => { + it(`should set the parent property recursively to each element of an item list`, () => { + spyOn(controller, 'setParent').and.callThrough(); + const items = [{name: 'Item1'}, {name: 'Item2', childs: [ + {name: 'Item3'} + ]}]; + const rootItem = {name: 'Nested tree', sons: items}; + controller.setParent(rootItem, items); + + expect(items[0].parent).toEqual(rootItem); + expect(items[1].parent).toEqual(rootItem); + expect(controller.setParent).toHaveBeenCalledWith(rootItem, items[1].childs); + }); + }); + + describe('onToggle()', () => { + it(`should call the fold() or unfold() methods`, () => { + spyOn(controller, 'fold'); + spyOn(controller, 'unfold'); + + const item = {name: 'My item'}; + + controller.onToggle(item); + item.active = true; + controller.onToggle(item); + + expect(controller.unfold).toHaveBeenCalledWith(item); + expect(controller.fold).toHaveBeenCalledWith(item); + }); + }); + + describe('fold()', () => { + it(`should remove the childs and set the active property to false`, () => { + const item = {name: 'My item', childs: [{name: 'Item 1'}], active: true}; + + controller.fold(item); + + expect(item.childs).toBeUndefined(); + expect(item.active).toBeFalsy(); + }); + }); + + describe('unfold()', () => { + it(`should unfold a parent item`, () => { + const expectedResponse = [{name: 'Item 1'}, {name: 'Item 2'}]; + spyOn(controller, 'fetchFunc').and.returnValue( + new Promise(resolve => resolve(expectedResponse)) + ); + spyOn(controller, 'setParent'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 1}; + const child = {name: 'Item 1'}; + child.parent = parent; + parent.childs = [child]; + + controller.unfold(parent).then(() => { + expect(controller.fetchFunc).toHaveBeenCalledWith({$item: parent}); + expect(controller.setParent).toHaveBeenCalledWith(parent, expectedResponse); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(parent.active).toBeTruthy(); + }); + }); + }); + + describe('onRemove()', () => { + it(`should call the removeFunc() method`, () => { + spyOn(controller, 'removeFunc'); + const item = {name: 'My item'}; + controller.onRemove(item); + + expect(controller.removeFunc).toHaveBeenCalledWith({$item: item}); + }); + }); + + describe('remove()', () => { + it(`should remove a child element`, () => { + const parent = {name: 'My item', sons: 1}; + const child = {name: 'Item 1'}; + child.parent = parent; + parent.childs = [child]; + + controller.remove(child); + + expect(parent.childs).toEqual([]); + expect(parent.sons).toEqual(0); + }); + }); + + describe('onCreate()', () => { + it(`should call the createFunc() method`, () => { + spyOn(controller, 'createFunc'); + const parent = {name: 'My item'}; + controller.onCreate(parent); + + expect(controller.createFunc).toHaveBeenCalledWith({$parent: parent}); + }); + }); + + describe('create()', () => { + it(`should unfold an inactive parent and then create a child`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 2, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.create(child); + + expect(parent.sons).toEqual(3); + expect(controller.unfold).toHaveBeenCalledWith(parent); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(controller.sortFunc).toHaveBeenCalledTimes(2); + }); + + it(`should create a child on an active parent`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 2, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ], active: true}; + const child = {name: 'Item 3'}; + child.parent = parent; + + controller.create(child); + + expect(parent.sons).toEqual(3); + expect(controller.unfold).not.toHaveBeenCalledWith(parent); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(controller.sortFunc).toHaveBeenCalledTimes(2); + }); + }); + + describe('move()', () => { + it(`should move an item to anocher parent and then unfold the parent`, () => { + spyOn(controller, 'unfold').and.returnValue( + new Promise(resolve => resolve()) + ); + const newParent = {name: 'My item 2', sons: 0}; + const parent = {name: 'My item', sons: 3, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.move(child, newParent); + + expect(parent.sons).toEqual(2); + expect(controller.unfold).toHaveBeenCalledWith(newParent); + }); + + it(`should move an item to anocher parent`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'create'); + const newParent = {name: 'My item 2', sons: 0, active: true}; + const parent = {name: 'My item', sons: 3, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.move(child, newParent); + + expect(parent.sons).toEqual(2); + expect(controller.unfold).not.toHaveBeenCalledWith(newParent); + }); + }); +}); diff --git a/front/core/components/treeview/style.scss b/front/core/components/treeview/style.scss index b3724a9f1..bd6175021 100644 --- a/front/core/components/treeview/style.scss +++ b/front/core/components/treeview/style.scss @@ -10,22 +10,6 @@ vn-treeview-childs { li { list-style: none; - & > .node { - @extend %clickable; - display: flex; - padding: 5px; - align-items: center; - } - - & > div > .arrow { - min-width: 24px; - margin-right: 10px; - transition: transform 200ms; - } - - & > div.expanded > .arrow { - transform: rotate(180deg); - } ul { padding-left: 2.2em; } @@ -45,8 +29,28 @@ vn-treeview-childs { .node:hover > .buttons { display: block } + + .content { + flex-grow: 1 + } } -vn-treeview-content { - flex-grow: 1 -} \ No newline at end of file +vn-treeview-child { + font-size: 16px; + display: block; + + .node { + @extend %clickable; + display: flex; + padding: 5px; + align-items: center; + } + & > div > .arrow { + min-width: 24px; + margin-right: 10px; + transition: transform 200ms; + } + &.expanded > div > .arrow { + transform: rotate(180deg); + } +} \ No newline at end of file diff --git a/front/core/directives/acl.js b/front/core/directives/acl.js index f9df740cf..1cebddd42 100644 --- a/front/core/directives/acl.js +++ b/front/core/directives/acl.js @@ -1,110 +1,41 @@ import ngModule from '../module'; +import FormInput from '../components/form-input'; -function vnAcl(aclService, $timeout) { +function vnAcl(aclService) { let acls = []; - function getMaterialType(className) { - let type = ''; - if (className) { - type = className.replace('mdl-', '').replace('__input', ''); - type = type.charAt(0).toUpperCase() + type.slice(1); - } - return type; - } - function updateMaterial(input) { - if (input && input.className) { - let find = input.className.match(/mdl-[\w]+input/g); - if (find && find.length && find[0]) { - let type = getMaterialType(find[0]); - if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_) - input.parentNode[`Material${type}`].updateClasses_(); - } - } - } - function getDynamicConditions($attrs) { - let atributes = $attrs.$attr; - let conditions = {}; - - Object.keys(atributes).forEach(atribute => { - if (atribute.startsWith('aclConditionalTo')) { - let role = atributes[atribute].split('-').slice(-1)[0]; - conditions[atribute] = { - role: role - }; - } - }); - return conditions; - } - - function permissionElement($element, action) { - if (!aclService.hasAny(acls)) { - if (action === 'disabled') { - let element = $element[0]; - let selector = 'input, textarea, button, submit, md-checkbox'; - - if (element.$ctrl) { - element.setAttribute('disabled', 'true'); - element.$ctrl.disabled = true; - } - - if (!element.matches(selector)) - element = element.querySelector(selector); - - if (element) { - $timeout(() => { - element.setAttribute('disabled', 'true'); - updateMaterial(element); - }); - $element[0].querySelectorAll('vn-drop-down').forEach(element => { - element.parentNode.removeChild(element); - }); - } - } else - $element.remove(); - } - } - - function updateAcls(role, toAdd) { - let position = acls.indexOf(role); - - if (!toAdd && position > -1) - acls.splice(position, 1); - // XXX: add acl and enabled element if previusly was disabled - } - return { restrict: 'A', priority: -1, - link: function($scope, $element, $attrs) { + link: function(_, $element, $attrs) { acls = $attrs.vnAcl.split(',').map(i => i.trim()); if (acls[0] == '') return; - let action = $attrs.vnAclAction || 'disabled'; - let conditions = getDynamicConditions($attrs); + let action = $attrs.vnAclAction || 'disable'; - permissionElement($element, action); + if (aclService.hasAny(acls)) return; - if (Object.keys(conditions).length) { - let watchConditions = $scope.$watch(() => { - Object.keys(conditions).forEach(attrName => { - let hasPermission = $scope.$eval($attrs[attrName]); - if (!hasPermission) { - updateAcls(conditions[attrName].role, hasPermission); - permissionElement($element, action); - delete conditions[attrName]; - } - }); + if (action === 'disable') { + let element = $element[0]; + let elementToDisable = element.$ctrl; - if (Object.keys(conditions).length === 0) { - // unWacth - watchConditions(); - } - }); - } + if (!(elementToDisable instanceof FormInput)) { + let selector = 'input, textarea, button, submit'; + + if (!element.matches(selector)) + element = element.querySelector(selector); + + elementToDisable = element; + } + + if (elementToDisable) + elementToDisable.disabled = true; + } else + $element.remove(); } }; } -vnAcl.$inject = ['aclService', '$timeout']; +vnAcl.$inject = ['aclService']; ngModule.directive('vnAcl', vnAcl); diff --git a/front/core/directives/draggable.js b/front/core/directives/draggable.js deleted file mode 100644 index 3b68a6cb6..000000000 --- a/front/core/directives/draggable.js +++ /dev/null @@ -1,43 +0,0 @@ -import ngModule from '../module'; - -/** - * Enables a draggable element and his drag events - * - * @return {Object} The directive - */ -export function directive() { - return { - restrict: 'A', - link: function($scope, $element, $attrs) { - const element = $element[0]; - const isDraggable = $attrs.vnDraggable === 'true'; - - if (!isDraggable) return; - - // Set draggable style properties - element.style.cursor = 'move'; - - - // Enable as draggable element - element.setAttribute('draggable', true); - - /** - * Fires when a drag event starts - */ - element.addEventListener('dragstart', event => { - element.style.opacity = 0.5; - event.stopPropagation(); - }); - - /** - * Fires when a drag event ends - */ - element.addEventListener('dragend', event => { - element.style.opacity = 1; - event.stopPropagation(); - }); - } - }; -} - -ngModule.directive('vnDraggable', directive); diff --git a/front/core/directives/droppable.js b/front/core/directives/droppable.js index 483282418..929b64be7 100644 --- a/front/core/directives/droppable.js +++ b/front/core/directives/droppable.js @@ -1,68 +1,43 @@ import ngModule from '../module'; import './droppable.scss'; -export function directive($parse) { - return { - restrict: 'A', - link: function($scope, $element, $attrs) { - const element = $element[0]; - const onDropEvent = $parse($attrs.onDrop); - const isDroppable = $attrs.vnDroppable === 'true'; +class Controller { + constructor($element, $, $attrs) { + this.element = $element[0]; + this.$ = $; + this.$attrs = $attrs; - if (!isDroppable) return; + this.element.addEventListener('dragover', + event => event.preventDefault()); // Prevents page reload + this.element.addEventListener('drop', + event => this.drop(event)); + this.element.addEventListener('dragenter', + event => this.dragEnter(event)); + this.element.addEventListener('dragleave', + event => this.dragLeave(event)); + } - /** - * Captures current dragging element - */ - element.addEventListener('dragstart', () => { - this.dragged = element; - }); + dragEnter(event) { + this.droppedElement = event.target; + this.element.classList.add('dropping'); + } - /** - * Enter droppable area event - */ - element.addEventListener('dragenter', event => { - element.classList.add('active'); + dragLeave(event) { + if (this.droppedElement === event.target) + this.element.classList.remove('dropping'); + } - event.stopImmediatePropagation(); - event.preventDefault(); - }, false); - - - /** - * Exit droppable area event - */ - element.addEventListener('dragleave', event => { - element.classList.remove('active'); - - event.stopImmediatePropagation(); - event.preventDefault(); - }); - - /** - * Prevent dragover for allowing - * dispatch drop event - */ - element.addEventListener('dragover', event => { - event.stopPropagation(); - event.preventDefault(); - }); - - /** - * Fires when a drop events - */ - element.addEventListener('drop', event => { - element.classList.remove('active'); - - onDropEvent($scope, {event}); - - event.stopPropagation(); - event.preventDefault(); - }); - } - }; + drop(event) { + if (event.defaultPrevented) return; + event.preventDefault(); + this.element.classList.remove('dropping'); + this.$.$eval(this.$attrs.vnDroppable, {$event: event}); + } } +Controller.$inject = ['$element', '$scope', '$attrs']; -directive.$inject = ['$parse']; - -ngModule.directive('vnDroppable', directive); +ngModule.directive('vnDroppable', () => { + return { + controller: Controller + }; +}); diff --git a/front/core/directives/droppable.scss b/front/core/directives/droppable.scss index 749bc9a12..97e6f8a19 100644 --- a/front/core/directives/droppable.scss +++ b/front/core/directives/droppable.scss @@ -1,11 +1,25 @@ @import "./variables"; + +.vn-droppable, +.vn-draggable, [vn-droppable] { border: 2px dashed transparent; + border-radius: 0.5em; transition: all 0.5s; +} - &.active { +.vn-droppable, +[vn-droppable] { + display: block; + + &.dropping { background-color: $color-hover-cd; - border: 2px dashed $color-bg-dark; + border-color: $color-bg-dark; } +} + +.vn-draggable.dragging { + background-color: $color-main-light; + border-color: $color-main; } \ No newline at end of file diff --git a/front/core/directives/focus.js b/front/core/directives/focus.js index b57241078..b9527479b 100644 --- a/front/core/directives/focus.js +++ b/front/core/directives/focus.js @@ -1,6 +1,11 @@ import ngModule from '../module'; -export function focus(input) { +const regex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i; +export const isMobile = regex.test(navigator.userAgent); + +export function focus($scope, input) { + if (isMobile) return; + const element = input; let selector = 'input, textarea, button, submit'; @@ -14,14 +19,13 @@ export function focus(input) { view: window }); element.dispatchEvent(focusEvent); - return; } input.focus(); - - if (input.select) + $scope.$applyAsync(() => { input.select(); + }); } /** @@ -33,7 +37,7 @@ export function directive() { return { restrict: 'A', link: function($scope, $element) { - $scope.$watch('', () => focus($element[0])); + $scope.$watch('', () => focus($scope, $element[0])); } }; } diff --git a/front/core/directives/index.js b/front/core/directives/index.js index 05f21b5cd..185046a3c 100644 --- a/front/core/directives/index.js +++ b/front/core/directives/index.js @@ -11,7 +11,6 @@ import './bind'; import './repeat-last'; import './title'; import './uvc'; -import './draggable'; import './droppable'; import './http-click'; import './http-submit'; diff --git a/front/core/directives/specs/acl.spec.js b/front/core/directives/specs/acl.spec.js index 523fb28bd..a7884a12d 100644 --- a/front/core/directives/specs/acl.spec.js +++ b/front/core/directives/specs/acl.spec.js @@ -1,54 +1,97 @@ describe('Directive acl', () => { - let scope; + let $scope; + let $element; let element; let compile; - let $timeout; beforeEach(angular.mock.module('vnCore', $translateProvider => { $translateProvider.translations('en', {}); })); - compile = (hasPermissions, _element) => { - inject(($compile, $rootScope, aclService, _$timeout_) => { - spyOn(aclService, 'hasAny').and.returnValue(hasPermissions); - scope = $rootScope.$new(); - $timeout = _$timeout_; - element = angular.element(_element); - $compile(element)(scope); - scope.$digest(); + beforeEach(inject(($httpBackend, aclService) => { + $httpBackend.whenGET('/api/Accounts/acl') + .respond({ + user: {id: 1, name: 'myUser'}, + roles: [ + {role: {name: 'myRole'}}, + {role: {name: 'myOtherRole'}} + ] + }); + aclService.load(); + $httpBackend.flush(); + })); + + afterEach(() => { + $element.remove(); + $scope.$destroy(); + }); + + compile = html => { + inject(($compile, $rootScope) => { + $scope = $rootScope.$new(); + $element = $compile(html)($scope); + $scope.$digest(); + element = $element[0]; }); }; - it('should not disable the input element as the user has permision', () => { - let html = `
`; - compile(true, html); - let input = element.find('input'); + it('should not disable the input element as the user owns the role', () => { + let html = ` +
+ +
+ `; + compile(html); + let input = element.querySelector('input'); - expect(input).toBeDefined(); - expect(input.attr('disabled')).toBeFalsy(); + expect(input.disabled).toBeFalsy(); }); - it('should delete the element as the user does not have permission and there is no action', () => { - let html = `
`; - compile(false, html); + it('should disable the element as the action is to disable and the user does not own the role', () => { + let html = ` +
+ +
+ `; + compile(html); + let input = element.querySelector('input'); - expect(element.children().length).toEqual(0); + expect(input.disabled).toBeTruthy(); }); - it('should disable the element as the action is to disable it but the user has no permission but present', () => { - let html = `
`; - compile(false, html); - let input = element.find('input'); - $timeout.flush(); + it('should keep the element as the action is to remove and the user owns the role', () => { + let html = ` +
+
+ +
+
+ `; + compile(html); + let div = element.querySelector('div'); - expect(input).toBeDefined(); - expect(input.attr('disabled')).toBeTruthy(); + expect(div).not.toBeNull(); }); - it('should delete any element with the tag vn-drop-down', () => { - let html = `
`; - compile(false, html); + it('should delete the element as the action is to remove and the user does not own the role', () => { + let html = ` +
+
+ +
+
+ `; + compile(html); + let div = element.querySelector('div'); - expect(element.find('vn-drop-down').length).toBe(0); + expect(div).toBeNull(); }); }); diff --git a/front/core/directives/specs/focus.spec.js b/front/core/directives/specs/focus.spec.js index ecf28a692..4034657b4 100644 --- a/front/core/directives/specs/focus.spec.js +++ b/front/core/directives/specs/focus.spec.js @@ -40,6 +40,7 @@ describe('Directive focus', () => { it('should call select function on the element', () => { let html = ``; compile(html); + $scope.$apply(); expect($element[0].select).toHaveBeenCalledWith(); }); diff --git a/front/core/filters/dateTime.js b/front/core/filters/dateTime.js deleted file mode 100644 index a4a527160..000000000 --- a/front/core/filters/dateTime.js +++ /dev/null @@ -1,21 +0,0 @@ -import ngModule from '../module'; - -/** - * Returns a formatted date based on input filter. - * - * @return {String} The string result - */ -dateTime.$inject = ['$filter']; - -export default function dateTime($filter) { - return function(input, format) { - if (typeof input === 'string' && input) { - input = new Date(input); - let offset = input.getTimezoneOffset() * 60000; - input.setTime(input.getTime() + offset); - } - - return $filter('date')(input, format); - }; -} -ngModule.filter('dateTime', dateTime); diff --git a/front/core/filters/index.js b/front/core/filters/index.js index 26c2db213..cc4dff823 100644 --- a/front/core/filters/index.js +++ b/front/core/filters/index.js @@ -1,7 +1,6 @@ import './phone'; import './ucwords'; import './dash-if-empty'; -import './dateTime'; import './percentage'; import './currency'; import './zero-fill'; diff --git a/front/core/lib/event-emitter.js b/front/core/lib/event-emitter.js index 2dede42ab..022e4e98c 100644 --- a/front/core/lib/event-emitter.js +++ b/front/core/lib/event-emitter.js @@ -24,10 +24,12 @@ export default class EventEmitter { */ off(callback) { if (!this.$events) return; - for (let event in this.$events) - for (let i = 0; i < event.length; i++) + for (let event in this.$events) { + for (let i = 0; i < event.length; i++) { if (event[i].callback === callback) event.splice(i--, 1); + } + } } /** @@ -37,10 +39,12 @@ export default class EventEmitter { */ disconnect(thisArg) { if (!this.$events) return; - for (let event in this.$events) - for (let i = 0; i < event.length; i++) + for (let event in this.$events) { + for (let i = 0; i < event.length; i++) { if (event[i].thisArg === thisArg) event.splice(i--, 1); + } + } } /** @@ -72,9 +76,10 @@ export default class EventEmitter { if (this[prop]) this[prop].disconnect(this); this[prop] = value; - if (value) + if (value) { for (let event in handlers) value.on(event, handlers[event], this); + } } } } diff --git a/front/core/lib/input.js b/front/core/lib/input.js deleted file mode 100644 index b29d3fe37..000000000 --- a/front/core/lib/input.js +++ /dev/null @@ -1,42 +0,0 @@ -import Component from './component'; - -/** - * Component that host an input. - */ -export default class Input extends Component { - constructor($element, $scope) { - super($element, $scope); - this.input = this.element.querySelector('input'); - } - - set disabled(value) { - this.input.disabled = value == true; - this.mdlUpdate(); - } - - get disabled() { - return this.input.disabled; - } - - select() { - this.input.select(); - } - - focus() { - this.input.focus(); - } - - mdlUpdate() { - if (this.mdlElement) - this.mdlElement.updateClasses_(); - } -} -Input.$inject = ['$element', '$scope']; - -export const $options = { - bindings: { - label: '@?', - disabled: '=2.8.0 <3.0.0" - } - }, "angular-translate": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz", @@ -83,26 +65,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, - "flatpickr": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.2.tgz", - "integrity": "sha512-jDy4QYGpmiy7+Qk8QvKJ4spjDdxcx9cxMydmq1x427HkKWBw0qizLYeYM2F6tMcvvqGjU5VpJS55j4LnsaBblA==" - }, - "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.10.tgz", - "integrity": "sha1-8tcgwiCS90Mih3XHXjYSYyUB8TE=" - }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", @@ -112,19 +74,6 @@ "esprima": "^4.0.0" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "material-design-lite": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/material-design-lite/-/material-design-lite-1.3.0.tgz", - "integrity": "sha1-0ATOP+6Zoe63Sni4oyUTSl8RcdM=" - }, "mg-crud": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz", @@ -133,19 +82,6 @@ "angular": "^1.6.1" } }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "moment-timezone": { - "version": "0.5.25", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.25.tgz", - "integrity": "sha512-DgEaTyN/z0HFaVcVbSyVCUU6HeFdnNC3vE4c9cgu2dgMTvjBUBdBzWfasTBmAW45u5OIMeCJtU8yNjM22DHucw==", - "requires": { - "moment": ">= 2.9.0" - } - }, "npm": { "version": "6.11.3", "resolved": "https://registry.npmjs.org/npm/-/npm-6.11.3.tgz", @@ -3229,11 +3165,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" - }, "validator": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz", diff --git a/front/package.json b/front/package.json index bfd8edfc4..e17dbd08c 100644 --- a/front/package.json +++ b/front/package.json @@ -12,17 +12,10 @@ "@babel/polyfill": "^7.2.5", "@uirouter/angularjs": "^1.0.20", "angular": "^1.7.5", - "angular-animate": "^1.7.7", - "angular-aria": "^1.7.7", - "angular-moment": "^1.3.0", "angular-translate": "^2.18.1", "angular-translate-loader-partial": "^2.18.1", - "flatpickr": "^4.5.2", - "fs-extra": "^5.0.0", "js-yaml": "^3.13.1", - "material-design-lite": "^1.3.0", "mg-crud": "^1.1.2", - "moment-timezone": "^0.5.25", "npm": "^6.11.3", "oclazyload": "^0.6.3", "require-yaml": "0.0.1", diff --git a/front/salix/components/app/style.scss b/front/salix/components/app/style.scss index 29c14fe0b..530524773 100644 --- a/front/salix/components/app/style.scss +++ b/front/salix/components/app/style.scss @@ -1,12 +1,5 @@ @import "variables"; -body { - background-color: $color-bg; - overflow: auto; - height: 100%; - font-family: vn-font; - color: $color-font; -} vn-app { height: inherit; display: block; @@ -104,37 +97,3 @@ 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/descriptor/index.html b/front/salix/components/descriptor/index.html new file mode 100644 index 000000000..3804be627 --- /dev/null +++ b/front/salix/components/descriptor/index.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/front/salix/components/descriptor/index.js b/front/salix/components/descriptor/index.js index 423b033ce..504812b15 100644 --- a/front/salix/components/descriptor/index.js +++ b/front/salix/components/descriptor/index.js @@ -1 +1,12 @@ +import ngModule from '../../module'; import './style.scss'; + +export default class QuickLinks {} + +ngModule.component('vnQuickLinks', { + template: require('./index.html'), + controller: QuickLinks, + bindings: { + links: ' * { - min-width: 1.8em; @extend %clickable; + min-width: 45px; + height: 45px; + box-sizing: border-box; display: flex; align-items: center; justify-content: center; - padding: .5em; color: inherit; & > vn-icon { + padding: 10px; + } + vn-icon { font-size: 1.8em; } } @@ -55,28 +59,25 @@ } } } - & > .quicklinks { + .quicklinks, + vn-quick-links { display: flex; align-items: center; justify-content: center; - padding: 0; & > a { - padding: $spacing-sm; - + padding: 0 $spacing-md; + margin: 0 $spacing-sm; + & > vn-icon { font-size: 1.8em; padding: 0; - - & > i { - line-height: 36px - } } } } } } -vn-popover { +.vn-popover { .vn-descriptor > .header > a:first-child { visibility: hidden; } diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html index fa9cd9d67..95bec4e5a 100644 --- a/front/salix/components/home/home.html +++ b/front/salix/components/home/home.html @@ -4,12 +4,11 @@ ng-repeat="mod in ::$ctrl.modules" ui-sref="{{::mod.route.state}}" translate-attr="::{title: mod.name}" - class="mdl-shadow--4dp"> + class="vn-shadow">

- diff --git a/front/salix/components/home/style.scss b/front/salix/components/home/style.scss index cab44ea73..b004b7336 100644 --- a/front/salix/components/home/style.scss +++ b/front/salix/components/home/style.scss @@ -49,9 +49,9 @@ vn-home { text-align: center; } & > h4 { - max-width: 7em; + max-width: 100%; text-align: center; - font-size: 13pt; + font-size: 12pt; overflow: hidden; color: inherit; margin: 0; diff --git a/front/salix/components/index.js b/front/salix/components/index.js index ca37b21d1..5724231ed 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -1,11 +1,11 @@ import './app/app'; -import './login/login'; -import './home/home'; -import './main-menu/main-menu'; import './background/background'; -import './side-menu/side-menu'; -import './left-menu/left-menu'; -import './topbar/topbar'; -import './user-popover'; import './descriptor'; +import './home/home'; +import './left-menu/left-menu'; +import './login/login'; +import './main-menu/main-menu'; +import './topbar/topbar'; +import './side-menu/side-menu'; import './summary'; +import './user-popover'; diff --git a/front/salix/components/login/login.html b/front/salix/components/login/login.html index 73154222c..b15714a23 100644 --- a/front/salix/components/login/login.html +++ b/front/salix/components/login/login.html @@ -4,14 +4,12 @@ *:hover { - color: $color-main; + & > * { + transition: color 250ms ease-out; } & > #user { vertical-align: middle; @@ -20,12 +20,16 @@ vn-main-menu { margin-right: .2em; cursor: pointer; } - & > vn-icon-button { - font-size: 2.1em; + & > .vn-button { + font-size: 1.2em; color: inherit; padding: 0; margin-left: .3em; } + & > :hover { + color: $color-main; + opacity: 1; + } } } diff --git a/front/salix/components/summary/style.scss b/front/salix/components/summary/style.scss index c9a2b74ab..558b9b758 100644 --- a/front/salix/components/summary/style.scss +++ b/front/salix/components/summary/style.scss @@ -20,6 +20,8 @@ & > vn-horizontal { flex-wrap: wrap; padding: $spacing-md; + overflow: hidden; + align-items: flex-start; h4 { margin-bottom: $spacing-md; @@ -28,7 +30,7 @@ line-height: 1; padding: 7px; padding-bottom: 4px; /* Bottom line-height fix */ - font-family: unset; + font-weight: lighter; background-color: $color-main-light; border-bottom: .1em solid $color-main; white-space: nowrap; @@ -38,7 +40,6 @@ & > * { margin: $spacing-sm; min-width: 14em; - overflow: hidden; padding: 0; } & > vn-auto { diff --git a/front/salix/styles/misc.scss b/front/salix/styles/misc.scss index 8066759ac..0983264bc 100644 --- a/front/salix/styles/misc.scss +++ b/front/salix/styles/misc.scss @@ -1,31 +1,43 @@ @import "./variables"; @import "./effects"; -html, body { - margin: 0; - padding: 0; +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; + } + } + } } -a:focus, -input:focus, -button:focus { - outline: none; -} -button::-moz-focus-inner, -input[type=submit]::-moz-focus-inner, -input[type=button]::-moz-focus-inner, -input[type=reset]::-moz-focus-inner { - border: none; -} -.unselectable { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} -a, .link { + +.link { color: $color-font-link; - text-decoration: none; - outline: 0 } .link { cursor: pointer; @@ -34,6 +46,12 @@ a, .link { text-decoration: underline; } } +.unselectable { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} vn-bg-title { display: block; text-align: center; @@ -77,9 +95,6 @@ html [pointer], .pointer{ html [noDrop], .noDrop{ cursor: no-drop; } -button { - @extend %clickable; -} vn-button-bar { display: block; margin-top: $spacing-sm; @@ -91,9 +106,6 @@ vn-tool-bar { margin-right: .6em; } } -input[type="submit"]:disabled, button:disabled { - opacity: 0.7; -} html [scrollable] { min-height: 1px; flex: 1; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index a6904074a..dffa15af6 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -109,5 +109,8 @@ "is invalid": "is invalid", "The postcode doesn't exists. Ensure you put the correct format": "El código postal no existe. Asegúrate de ponerlo con el formato correcto", "The department name can't be repeated": "El nombre del departamento no puede repetirse", + "This phone already exists": "Este teléfono ya existe", + "You cannot move a parent to any of its sons": "You cannot move a parent to any of its sons", + "You cannot move a parent to its own sons": "You cannot move a parent to its own sons", "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado" } \ No newline at end of file diff --git a/loopback/server/connectors/vn-mysql.js b/loopback/server/connectors/vn-mysql.js index 2ea30b598..5c625be85 100644 --- a/loopback/server/connectors/vn-mysql.js +++ b/loopback/server/connectors/vn-mysql.js @@ -5,37 +5,6 @@ const EnumFactory = require('loopback-connector-mysql').EnumFactory; const fs = require('fs'); class VnMySQL extends MySQL { - toColumnValue(prop, val) { - if (val == null || !prop || prop.type !== Date) - return MySQL.prototype.toColumnValue.call(this, prop, val); - - val = new Date(val); - - return val.getFullYear() + '-' + - fillZeros(val.getMonth() + 1) + '-' + - fillZeros(val.getDate()) + ' ' + - fillZeros(val.getHours()) + ':' + - fillZeros(val.getMinutes()) + ':' + - fillZeros(val.getSeconds()); - - function fillZeros(v) { - return v < 10 ? '0' + v : v; - } - } - fromColumnValue(prop, val) { - if (val == null || !prop || prop.type !== Date) - return MySQL.prototype.fromColumnValue.call(this, prop, val); - - let date = new Date(val); - - return date; - } - - isIsoDate(dateString) { - let isoRegexp = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/g; - return isoRegexp.test(dateString); - } - /** * Promisified version of execute(). * diff --git a/loopback/server/datasources.json b/loopback/server/datasources.json index 4c9e407a0..bbd8ce0d5 100644 --- a/loopback/server/datasources.json +++ b/loopback/server/datasources.json @@ -12,6 +12,8 @@ "username": "root", "password": "root", "multipleStatements": true, + "legacyUtcDateProcessing": false, + "timezone": "local", "connectTimeout": 20000, "acquireTimeout": 20000 }, diff --git a/modules/agency/back/methods/zone/clone.js b/modules/agency/back/methods/zone/clone.js index 7924415ed..3d0008a27 100644 --- a/modules/agency/back/methods/zone/clone.js +++ b/modules/agency/back/methods/zone/clone.js @@ -40,10 +40,6 @@ module.exports = Self => { where: {id} }, options); - const hour = zone.hour; - const offset = hour.getTimezoneOffset() * 60000; - hour.setTime(hour.getTime() + offset); - // Find all original included geolocations const includedGeo = await models.ZoneIncluded.find({ fields: ['geoFk', 'isIncluded'], diff --git a/modules/agency/front/descriptor/index.html b/modules/agency/front/descriptor/index.html index d6de7791e..ffed14551 100644 --- a/modules/agency/front/descriptor/index.html +++ b/modules/agency/front/descriptor/index.html @@ -31,7 +31,7 @@ value="{{$ctrl.zone.agencyMode.name}}"> + value="{{$ctrl.zone.hour | date: 'HH:mm'}}"> diff --git a/modules/agency/front/events/index.html b/modules/agency/front/events/index.html index eba9a9ac0..91864dc7c 100644 --- a/modules/agency/front/events/index.html +++ b/modules/agency/front/events/index.html @@ -15,13 +15,13 @@
- {{::row.from | dateTime:'dd/MM/yyyy'}} + {{::row.from | date:'dd/MM/yyyy'}}
- {{::row.from | dateTime:'dd/MM/yyyy'}} - {{::row.to | dateTime:'dd/MM/yyyy'}} + {{::row.from | date:'dd/MM/yyyy'}} - {{::row.to | date:'dd/MM/yyyy'}} Indefinitely @@ -32,7 +32,7 @@
+ value="{{::row.hour | date:'hh:mm'}}"> - {{::row.day | dateTime:'dd/MM/yyyy'}} + {{::row.day | date:'dd/MM/yyyy'}}
- + {{::zone.id}} {{::zone.name}} {{::zone.agencyMode.name}} - {{::zone.hour | dateTime: 'HH:mm'}} + {{::zone.hour | date: 'HH:mm'}} {{::zone.price | currency: 'EUR':2}} @@ -80,7 +80,8 @@ vn-tooltip="New zone" vn-bind="+" fixed-bottom-right> - diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index 4931e02fd..e01f1257a 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -4,8 +4,9 @@ filter="::$ctrl.filter">
- - + diff --git a/modules/agency/front/location/style.scss b/modules/agency/front/location/style.scss index a3b237b13..d1597e460 100644 --- a/modules/agency/front/location/style.scss +++ b/modules/agency/front/location/style.scss @@ -1,14 +1,14 @@ @import "variables"; -vn-treeview-content { - & > vn-check:not(.indeterminate) { +vn-treeview-child { + .content > vn-check:not(.indeterminate) { color: $color-main; & > .btn { border-color: $color-main; } } - & > vn-check.checked { + .content > vn-check.checked { color: $color-main; } } \ No newline at end of file diff --git a/modules/agency/front/summary/index.html b/modules/agency/front/summary/index.html index eeb4d495e..f0336f0dc 100644 --- a/modules/agency/front/summary/index.html +++ b/modules/agency/front/summary/index.html @@ -14,7 +14,7 @@ + value="{{$ctrl.summary.hour | date: 'HH:mm'}}"> diff --git a/modules/claim/back/methods/claim/regularizeClaim.js b/modules/claim/back/methods/claim/regularizeClaim.js index a2896174e..136016b1e 100644 --- a/modules/claim/back/methods/claim/regularizeClaim.js +++ b/modules/claim/back/methods/claim/regularizeClaim.js @@ -120,16 +120,19 @@ module.exports = Self => { } async function getTicketId(params, options) { - const currentDate = new Date(); - currentDate.setHours(null, null, null); + const minDate = new Date(); + minDate.setHours(0, 0, 0, 0); + + const maxDate = new Date(); + maxDate.setHours(23, 59, 59, 59); let ticket = await Self.app.models.Ticket.findOne({ where: { addressFk: params.addressFk, companyFk: params.companyFk, warehouseFk: params.warehouseFk, - shipped: currentDate, - landed: currentDate + shipped: {between: [minDate, maxDate]}, + landed: {between: [minDate, maxDate]} } }, options); diff --git a/modules/claim/back/methods/claim/uploadFile.js b/modules/claim/back/methods/claim/uploadFile.js index f6dad3477..c7565701d 100644 --- a/modules/claim/back/methods/claim/uploadFile.js +++ b/modules/claim/back/methods/claim/uploadFile.js @@ -7,36 +7,34 @@ module.exports = Self => { type: 'Number', description: 'The claim id', http: {source: 'path'} - }, - { + }, { arg: 'warehouseId', type: 'Number', - description: '' - }, - { + description: 'The warehouse id', + required: true + }, { arg: 'companyId', type: 'Number', - description: '' - }, - { + description: 'The company id', + required: true + }, { arg: 'dmsTypeId', type: 'Number', - description: '' - }, - { + description: 'The dms type id', + required: true + }, { arg: 'reference', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'description', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'hasFile', type: 'Boolean', - description: '' + description: 'True if has an attached file', + required: true }], returns: { type: 'Object', diff --git a/modules/claim/front/action/index.html b/modules/claim/front/action/index.html index 65467d349..04ab71997 100644 --- a/modules/claim/front/action/index.html +++ b/modules/claim/front/action/index.html @@ -1,121 +1,120 @@ - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - Id - Ticket - Destination - Landed - Quantity - Description - Price - Disc. - Total - - - - - - - {{saleClaimed.sale.itemFk | zeroFill:6}} - - - - - {{::saleClaimed.sale.ticketFk}} + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + Id + Ticket + Destination + Landed + Quantity + Description + Price + Disc. + Total + + + + + + + {{::saleClaimed.sale.itemFk | zeroFill:6}} - - - - - - {{saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} - {{saleClaimed.sale.quantity}} - {{saleClaimed.sale.concept}} - {{saleClaimed.sale.price | currency: 'EUR':2}} - {{saleClaimed.sale.discount}} % - - {{saleClaimed.sale.quantity * saleClaimed.sale.price * - ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} - - - - - - - - -
-
+ + + + {{::saleClaimed.sale.ticketFk}} + + + + + + + {{::saleClaimed.sale.ticket.landed | date: 'dd/MM/yyyy'}} + {{::saleClaimed.sale.quantity}} + {{::saleClaimed.sale.concept}} + {{::saleClaimed.sale.price | currency: 'EUR':2}} + {{::saleClaimed.sale.discount}} % + + {{saleClaimed.sale.quantity * saleClaimed.sale.price * + ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} + + + + + + + + + + - +
@@ -151,7 +146,7 @@ class="clickable" ng-click="$ctrl.addClaimedSale(sale.saleFk)"> {{sale.saleFk}} - {{sale.landed | dateTime: 'dd/MM/yyyy'}} + {{sale.landed | date: 'dd/MM/yyyy'}} {{sale.quantity}} {{sale.concept}} {{sale.price | currency: 'EUR':2}} @@ -188,7 +183,7 @@ ng-repeat="ticket in lastTickets" ng-click="$ctrl.importTicketLines(ticket.id)"> {{::ticket.id}} - {{::ticket.shipped | dateTime: 'dd/MM/yyyy'}} + {{::ticket.shipped | date: 'dd/MM/yyyy'}} {{::ticket.agencyMode.name}} {{::ticket.warehouse.name}} diff --git a/modules/claim/front/action/index.js b/modules/claim/front/action/index.js index 439b70d39..63b283f1f 100644 --- a/modules/claim/front/action/index.js +++ b/modules/claim/front/action/index.js @@ -19,7 +19,8 @@ class Controller { } } }, - {relation: 'claimBeggining'} + {relation: 'claimBeggining'}, + {relation: 'claimDestination'} ] }; this.resolvedState = 3; @@ -82,16 +83,6 @@ class Controller { this.calculateTotals(); } - setClaimDestination(id, claimDestinationFk) { - if (claimDestinationFk) { - let params = {id: id, claimDestinationFk: claimDestinationFk}; - let query = `claim/api/ClaimEnds/`; - this.$http.patch(query, params).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); - }); - } - } - calculateTotals() { this.claimedTotal = 0; this.salesClaimed.forEach(sale => { diff --git a/modules/claim/front/action/index.spec.js b/modules/claim/front/action/index.spec.js index d1f8f9766..e526680c3 100644 --- a/modules/claim/front/action/index.spec.js +++ b/modules/claim/front/action/index.spec.js @@ -82,17 +82,6 @@ describe('claim', () => { }); }); - describe('setClaimDestination(id, claimDestinationFk)', () => { - it('should make a patch and call refresh and showSuccess', () => { - spyOn(controller.vnApp, 'showSuccess'); - $httpBackend.expectPATCH(`claim/api/ClaimEnds/`).respond({}); - controller.setClaimDestination(1, 1); - $httpBackend.flush(); - - expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); - }); - }); - describe('calculateTotals()', () => { it('should calculate the total price of the items claimed', () => { controller.salesClaimed = [ diff --git a/modules/claim/front/action/style.scss b/modules/claim/front/action/style.scss index 3d54485e7..0124b716b 100644 --- a/modules/claim/front/action/style.scss +++ b/modules/claim/front/action/style.scss @@ -1,4 +1,19 @@ vn-claim-action { + .header { + display: flex; + justify-content: space-between; + align-items: center; + align-content: center; + + vn-tool-bar { + flex: 1 + } + + vn-check { + flex: none; + } + } + vn-dialog[vn-id=addSales] { tpl-body { width: 950px; diff --git a/modules/claim/front/basic-data/index.html b/modules/claim/front/basic-data/index.html index 953441581..1c68f6673 100644 --- a/modules/claim/front/basic-data/index.html +++ b/modules/claim/front/basic-data/index.html @@ -8,23 +8,18 @@ - - - + + - + field="::$ctrl.claim.created | date:'yyyy-MM-dd hh:mm'" + readonly="true"> + + value="{{$ctrl.claim.created | date: 'dd/MM/yyyy HH:mm'}}"> @@ -42,32 +42,9 @@ value="{{$ctrl.claim.ticketFk}}">
- + +
- - - - -
- - - - -
-
- - - - Landed - Quantity - Claimed - Description - Price - Disc. - Total - - - - - - {{::saleClaimed.sale.ticket.landed | dateTime:'dd/MM/yyyy'}} - {{::saleClaimed.sale.quantity}} - - - - - - - {{::saleClaimed.sale.concept}} - - - {{::saleClaimed.sale.price | currency: 'EUR':2}} - - - {{::saleClaimed.sale.discount}} % - - - - {{::$ctrl.getSaleTotal(saleClaimed.sale) | currency: 'EUR':2}} - - - - - - - - -
+ + + + + + + + + + + + Landed + Quantity + Claimed + Description + Price + Disc. + Total + + + + + + {{::saleClaimed.sale.ticket.landed | date:'dd/MM/yyyy'}} + {{::saleClaimed.sale.quantity}} + + + + + + + {{::saleClaimed.sale.concept}} + + + {{::saleClaimed.sale.price | currency: 'EUR':2}} + + + {{::saleClaimed.sale.discount}} % + + + + {{::$ctrl.getSaleTotal(saleClaimed.sale) | currency: 'EUR':2}} + + + + + + + + - - - -
+ + + + @@ -99,7 +99,7 @@ - {{sale.landed | dateTime: 'dd/MM/yyyy'}} + {{sale.landed | date: 'dd/MM/yyyy'}} {{sale.quantity}} -
+
Drag & Drop files here...
@@ -14,17 +14,18 @@ class="vn-w-lg">
-
- + tabindex="-1" + icon="delete"> +
diff --git a/modules/claim/front/dms/index/index.js b/modules/claim/front/dms/index/index.js index 12d22bb2a..0aa48b7cc 100644 --- a/modules/claim/front/dms/index/index.js +++ b/modules/claim/front/dms/index/index.js @@ -36,8 +36,9 @@ class Controller { } } - onDrop(event) { - const files = event.dataTransfer.files; + onDrop($event) { + console.log($event); + const files = $event.dataTransfer.files; this.setDefaultParams().then(() => { this.dms.files = files; this.create(); diff --git a/modules/claim/front/index/index.html b/modules/claim/front/index/index.html index e72810f6f..f9f42348f 100644 --- a/modules/claim/front/index/index.html +++ b/modules/claim/front/index/index.html @@ -7,7 +7,7 @@ auto-load="true">
- + - {{::claim.created | dateTime:'dd/MM/yyyy'}} + {{::claim.created | date:'dd/MM/yyyy'}} + data="photos"> -
{{$ctrl.summary.claim.id}} - {{$ctrl.summary.claim.client.name}}
+
{{::$ctrl.summary.claim.id}} - {{::$ctrl.summary.claim.client.name}}
+ value="{{$ctrl.summary.claim.created | date: 'dd/MM/yyyy'}}"> @@ -30,54 +28,58 @@ - - +

Detail

- - - - Item - Landed - Quantity - Claimed - Description - Price - Disc. - Total - - - - - - - {{saleClaimed.sale.itemFk | zeroFill:6}} - - - {{saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} - {{saleClaimed.sale.quantity}} - {{saleClaimed.quantity}} - {{saleClaimed.sale.concept}} - {{saleClaimed.sale.price | currency: 'EUR':2}} - {{saleClaimed.sale.discount}} % - - {{saleClaimed.sale.quantity * saleClaimed.sale.price * - ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} - - - - + + + + + Item + Landed + Quantity + Claimed + Description + Price + Disc. + Total + + + + + + + {{::saleClaimed.sale.itemFk | zeroFill:6}} + + + {{::saleClaimed.sale.ticket.landed | date: 'dd/MM/yyyy'}} + {{::saleClaimed.sale.quantity}} + {{::saleClaimed.quantity}} + {{::saleClaimed.sale.concept}} + {{::saleClaimed.sale.price | currency: 'EUR':2}} + {{::saleClaimed.sale.discount}} % + + {{saleClaimed.sale.quantity * saleClaimed.sale.price * + ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} + + + + +

Photos

@@ -92,78 +94,82 @@

Development

- - - - Reason - Result - Responsible - Worker - Redelivery - - - - - {{development.claimReason.description}} - {{development.claimResult.description}} - {{development.claimResponsible.description}} - - - {{::development.worker.user.nickname}} - - - {{development.claimRedelivery.description}} - - - + + + + + Reason + Result + Responsible + Worker + Redelivery + + + + + {{::development.claimReason.description}} + {{::development.claimResult.description}} + {{::development.claimResponsible.description}} + + + {{::development.worker.user.nickname}} + + + {{::development.claimRedelivery.description}} + + + +

Action

- - - - Item - Ticket - Destination - Landed - Quantity - Description - Price - Disc. - Total - - - - - - - {{action.sale.itemFk | zeroFill:6}} - - - - - {{action.sale.ticket.id | zeroFill:6}} - - - {{action.claimBeggining.description}} - {{action.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} - {{action.sale.quantity}} - {{action.sale.concept}} - {{action.sale.price}} - {{action.sale.discount}} % - - {{action.sale.quantity * action.sale.price * - ((100 - action.sale.discount) / 100) | currency: 'EUR':2}} - - - - + + + + + Item + Ticket + Destination + Landed + Quantity + Description + Price + Disc. + Total + + + + + + + {{::action.sale.itemFk | zeroFill:6}} + + + + + {{::action.sale.ticket.id | zeroFill:6}} + + + {{::action.claimBeggining.description}} + {{::action.sale.ticket.landed | date: 'dd/MM/yyyy'}} + {{::action.sale.quantity}} + {{::action.sale.concept}} + {{::action.sale.price}} + {{::action.sale.discount}} % + + {{action.sale.quantity * action.sale.price * + ((100 - action.sale.discount) / 100) | currency: 'EUR':2}} + + + + +
diff --git a/modules/claim/front/summary/index.js b/modules/claim/front/summary/index.js index 257be55e0..7d9e3716f 100644 --- a/modules/claim/front/summary/index.js +++ b/modules/claim/front/summary/index.js @@ -9,6 +9,26 @@ class Controller { this.accessToken = vnToken.token; } + get claim() { + return this._claim; + } + + + set claim(value) { + this._claim = value; + + // Get DMS on summary load + /* if (value) + this.$.$applyAsync(() => this.loadDms()); */ + } + + loadDms() { + this.$.model.where = { + claimFk: this.claim.id + }; + this.$.model.refresh(); + } + getSummary() { this.$http.get(`/claim/api/Claims/${this.claim.id}/getSummary`).then(response => { this.summary = response.data; diff --git a/modules/claim/front/summary/index.spec.js b/modules/claim/front/summary/index.spec.js index c56b91251..87b3caac0 100644 --- a/modules/claim/front/summary/index.spec.js +++ b/modules/claim/front/summary/index.spec.js @@ -1,4 +1,5 @@ import './index.js'; +import crudModel from 'core/mocks/crud-model'; describe('Claim', () => { describe('Component summary', () => { @@ -13,6 +14,7 @@ describe('Claim', () => { $httpBackend = _$httpBackend_; controller = $componentController('vnClaimSummary'); controller.claim = {id: 1}; + controller.$.model = crudModel; })); describe('getSummary()', () => { diff --git a/modules/client/back/methods/client/uploadFile.js b/modules/client/back/methods/client/uploadFile.js index 6b7a49459..6ec96e301 100644 --- a/modules/client/back/methods/client/uploadFile.js +++ b/modules/client/back/methods/client/uploadFile.js @@ -7,36 +7,34 @@ module.exports = Self => { type: 'Number', description: 'The client id', http: {source: 'path'} - }, - { + }, { arg: 'warehouseId', type: 'Number', - description: '' - }, - { + description: 'The warehouse id', + required: true + }, { arg: 'companyId', type: 'Number', - description: '' - }, - { + description: 'The company id', + required: true + }, { arg: 'dmsTypeId', type: 'Number', - description: '' - }, - { + description: 'The dms type id', + required: true + }, { arg: 'reference', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'description', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'hasFile', type: 'Boolean', - description: '' + description: 'True if has an attached file', + required: true }], returns: { type: 'Object', diff --git a/modules/client/back/methods/sms/send.spec.js b/modules/client/back/methods/sms/send.spec.js new file mode 100644 index 000000000..19dd8589f --- /dev/null +++ b/modules/client/back/methods/sms/send.spec.js @@ -0,0 +1,74 @@ +const app = require('vn-loopback/server/server'); +const soap = require('soap'); + +describe('sms send()', () => { + it('should return the expected message and status code', async() => { + const code = 200; + const smsConfig = await app.models.SmsConfig.findOne(); + const soapClient = await soap.createClientAsync(smsConfig.uri); + spyOn(soap, 'createClientAsync').and.returnValue(soapClient); + spyOn(soapClient, 'sendSMSAsync').and.returnValue([{ + result: { + $value: + ` + + + ${code} + + + Envio en procesamiento + + + 1 + + + + 444328681 + + ` + } + }]); + let ctx = {req: {accessToken: {userId: 1}}}; + let result = await app.models.Sms.send(ctx, 105, 'destination', 'My SMS Body'); + + expect(result.statusCode).toEqual(200); + expect(result.status).toContain('Envio en procesamiento'); + }); + + it(`should throw if the response code isn't 200`, async() => { + let error; + const code = 400; + const smsConfig = await app.models.SmsConfig.findOne(); + const soapClient = await soap.createClientAsync(smsConfig.uri); + spyOn(soap, 'createClientAsync').and.returnValue(soapClient); + spyOn(soapClient, 'sendSMSAsync').and.returnValue([{ + result: { + $value: + ` + + + ${code} + + + Envio en procesamiento + + + 1 + + + + 444328681 + + ` + } + }]); + let ctx = {req: {accessToken: {userId: 1}}}; + try { + await app.models.Sms.send(ctx, 105, 'destination', 'My SMS Body'); + } catch (err) { + error = err; + } + + expect(error.message).toEqual(`We weren't able to send this SMS`); + }); +}); diff --git a/modules/client/front/address/create/index.html b/modules/client/front/address/create/index.html index 8c30f9929..30e49590c 100644 --- a/modules/client/front/address/create/index.html +++ b/modules/client/front/address/create/index.html @@ -119,12 +119,10 @@
- + diff --git a/modules/client/front/balance/index/index.html b/modules/client/front/balance/index/index.html index 88dfe1222..2ae2aa2aa 100644 --- a/modules/client/front/balance/index/index.html +++ b/modules/client/front/balance/index/index.html @@ -62,13 +62,13 @@ - - {{::balance.payed | dateTime:'dd/MM/yyyy'}} + + {{::balance.payed | date:'dd/MM/yyyy'}} - - {{::balance.created | dateTime:'dd/MM/yyyy HH:mm'}} + + {{::balance.created | date:'dd/MM/yyyy HH:mm'}} diff --git a/modules/client/front/basic-data/index.html b/modules/client/front/basic-data/index.html index eed8277af..ce80ea5a6 100644 --- a/modules/client/front/basic-data/index.html +++ b/modules/client/front/basic-data/index.html @@ -5,70 +5,68 @@ form="form" save="patch"> -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/modules/client/front/credit-insurance/index/index.html b/modules/client/front/credit-insurance/index/index.html index 83e34d62e..192af4671 100644 --- a/modules/client/front/credit-insurance/index/index.html +++ b/modules/client/front/credit-insurance/index/index.html @@ -19,8 +19,8 @@ -
Since {{::classification.started | dateTime:'dd/MM/yyyy'}}
-
To {{classification.finished | dateTime:'dd/MM/yyyy'}}
+
Since {{::classification.started | date:'dd/MM/yyyy'}}
+
To {{classification.finished | date:'dd/MM/yyyy'}}
@@ -36,7 +36,7 @@ + value="{{::insurance.created | date:'dd/MM/yyyy' }}"> diff --git a/modules/client/front/credit-insurance/insurance/create/index.html b/modules/client/front/credit-insurance/insurance/create/index.html index f652f6011..a28fb605b 100644 --- a/modules/client/front/credit-insurance/insurance/create/index.html +++ b/modules/client/front/credit-insurance/insurance/create/index.html @@ -35,9 +35,9 @@
- + + \ No newline at end of file diff --git a/modules/client/front/credit-insurance/insurance/index/index.html b/modules/client/front/credit-insurance/insurance/index/index.html index ac2cdda48..06e963781 100644 --- a/modules/client/front/credit-insurance/insurance/index/index.html +++ b/modules/client/front/credit-insurance/insurance/index/index.html @@ -19,7 +19,7 @@ - {{::insurance.created | dateTime: 'dd/MM/yyyy'}} + {{::insurance.created | date: 'dd/MM/yyyy'}} {{::insurance.grade}} {{::insurance.credit | currency: 'EUR': 2}} diff --git a/modules/client/front/credit/index/index.html b/modules/client/front/credit/index/index.html index dd2a9a88c..d9cfe7dee 100644 --- a/modules/client/front/credit/index/index.html +++ b/modules/client/front/credit/index/index.html @@ -22,7 +22,7 @@ - {{::credit.created | dateTime:'dd/MM/yyyy HH:mm'}} + {{::credit.created | date:'dd/MM/yyyy HH:mm'}} {{::credit.worker.user.nickname}} {{::credit.amount | currency:'EUR':2}} diff --git a/modules/client/front/descriptor/index.html b/modules/client/front/descriptor/index.html index 84f3ca10e..32d785fe3 100644 --- a/modules/client/front/descriptor/index.html +++ b/modules/client/front/descriptor/index.html @@ -63,31 +63,8 @@ ng-class="{bright: $ctrl.client.isTaxDataChecked == false}">
- + +
\ No newline at end of file diff --git a/modules/client/front/descriptor/index.js b/modules/client/front/descriptor/index.js index 1eecdc15a..04d619f50 100644 --- a/modules/client/front/descriptor/index.js +++ b/modules/client/front/descriptor/index.js @@ -13,6 +13,10 @@ class Controller { callback.call(this); } + get client() { + return this._client; + } + set client(value) { this._client = value; @@ -32,10 +36,6 @@ class Controller { }; } - get client() { - return this._client; - } - set quicklinks(value = {}) { this._quicklinks = Object.assign(value, this._quicklinks); } diff --git a/modules/client/front/dms/index/index.html b/modules/client/front/dms/index/index.html index 0c936234c..1f306dc88 100644 --- a/modules/client/front/dms/index/index.html +++ b/modules/client/front/dms/index/index.html @@ -70,7 +70,7 @@ {{::document.dms.worker.user.nickname | dashIfEmpty}} - {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}} + {{::document.dms.created | date:'dd/MM/yyyy HH:mm'}} - + label="Country" + rule> - + label="Province" + rule> diff --git a/modules/client/front/greuge/index/index.html b/modules/client/front/greuge/index/index.html index 5932a66a9..51f31338f 100644 --- a/modules/client/front/greuge/index/index.html +++ b/modules/client/front/greuge/index/index.html @@ -36,7 +36,7 @@ - {{::greuge.shipped | dateTime:'dd/MM/yyyy HH:mm' }} + {{::greuge.shipped | date:'dd/MM/yyyy HH:mm' }} {{::greuge.description}} {{::greuge.greugeType.name}} {{::greuge.amount | currency: 'EUR': 2}} diff --git a/modules/client/front/index/index.html b/modules/client/front/index/index.html index 602ef8600..64be81a29 100644 --- a/modules/client/front/index/index.html +++ b/modules/client/front/index/index.html @@ -6,7 +6,7 @@ data="clients">
- + {{::mandate.id}} {{::mandate.company.code}} {{::mandate.mandateType.name}} - {{::mandate.created | dateTime:'dd/MM/yyyy HH:mm' }} - {{::mandate.finished | dateTime:'dd/MM/yyyy HH:mm' || '-'}} + {{::mandate.created | date:'dd/MM/yyyy HH:mm' }} + {{::mandate.finished | date:'dd/MM/yyyy HH:mm' || '-'}} diff --git a/modules/client/front/note/index/index.html b/modules/client/front/note/index/index.html index db9ba0818..04bd7d28e 100644 --- a/modules/client/front/note/index/index.html +++ b/modules/client/front/note/index/index.html @@ -15,7 +15,7 @@ class="note vn-pa-sm border-solid border-radius vn-mb-md"> {{::note.worker.user.nickname}} - {{::note.created | dateTime:'dd/MM/yyyy HH:mm'}} + {{::note.created | date:'dd/MM/yyyy HH:mm'}} {{::note.text}} diff --git a/modules/client/front/recovery/index/index.html b/modules/client/front/recovery/index/index.html index 9e4345025..755556f37 100644 --- a/modules/client/front/recovery/index/index.html +++ b/modules/client/front/recovery/index/index.html @@ -33,8 +33,8 @@ ng-click="$ctrl.setFinished(recovery)"> - {{::recovery.started | dateTime:'dd/MM/yyyy' }} - {{recovery.finished | dateTime:'dd/MM/yyyy' }} + {{::recovery.started | date:'dd/MM/yyyy' }} + {{recovery.finished | date:'dd/MM/yyyy' }} {{::recovery.amount | currency: 'EUR': 0}} {{::recovery.period}} diff --git a/modules/client/front/sample/index/index.html b/modules/client/front/sample/index/index.html index 2d7f63ba7..49be184f3 100644 --- a/modules/client/front/sample/index/index.html +++ b/modules/client/front/sample/index/index.html @@ -23,7 +23,7 @@ - {{::sample.created | dateTime:'dd/MM/yyyy HH:mm' }} + {{::sample.created | date:'dd/MM/yyyy HH:mm' }} {{::sample.type.description}} diff --git a/modules/client/front/summary/index.html b/modules/client/front/summary/index.html index 2fae69572..eccf45873 100644 --- a/modules/client/front/summary/index.html +++ b/modules/client/front/summary/index.html @@ -192,7 +192,7 @@ + value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}"> diff --git a/modules/client/front/web-payment/index.html b/modules/client/front/web-payment/index.html index 326945c21..7f6042759 100644 --- a/modules/client/front/web-payment/index.html +++ b/modules/client/front/web-payment/index.html @@ -36,7 +36,7 @@ {{::transaction.id}} - {{::transaction.created | dateTime:'dd/MM/yyyy HH:mm'}} + {{::transaction.created | date:'dd/MM/yyyy HH:mm'}} {{::transaction.amount | currency: 'EUR':2}} { case 'companyFk': case 'issued': case 'dued': + param = `i.${param}`; return {[param]: value}; } }); diff --git a/modules/invoiceOut/front/descriptor/index.html b/modules/invoiceOut/front/descriptor/index.html index 211560d28..f84b90d41 100644 --- a/modules/invoiceOut/front/descriptor/index.html +++ b/modules/invoiceOut/front/descriptor/index.html @@ -21,7 +21,7 @@
{{$ctrl.invoiceOut.ref}}
+ value="{{$ctrl.invoiceOut.issued | date: 'dd/MM/yyyy'}}"> @@ -33,24 +33,9 @@ value="{{$ctrl.invoiceOut.company.code}}">
-
+ +
-
- - - - -
+ + + + @@ -39,7 +37,7 @@ class="clickable vn-tr searchResult" ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})"> {{::invoiceOut.ref | dashIfEmpty}} - {{::invoiceOut.issued | dateTime:'dd/MM/yyyy' | dashIfEmpty}} + {{::invoiceOut.issued | date:'dd/MM/yyyy' | dashIfEmpty}} {{::invoiceOut.amount | currency: 'EUR': 2 | dashIfEmpty}} - {{::invoiceOut.created | dateTime:'dd/MM/yyyy' | dashIfEmpty}} + {{::invoiceOut.created | date:'dd/MM/yyyy' | dashIfEmpty}} {{::invoiceOut.companyCode | dashIfEmpty}} - {{::invoiceOut.dued | dateTime:'dd/MM/yyyy' | dashIfEmpty}} + {{::invoiceOut.dued | date:'dd/MM/yyyy' | dashIfEmpty}} + value="{{$ctrl.summary.invoiceOut.issued | date: 'dd/MM/yyyy'}}"> + value="{{$ctrl.summary.invoiceOut.dued | date: 'dd/MM/yyyy'}}"> + value="{{$ctrl.summary.invoiceOut.created | date: 'dd/MM/yyyy'}}"> + value="{{$ctrl.summary.invoiceOut.booked | date: 'dd/MM/yyyy'}}"> @@ -66,7 +66,7 @@ {{ticket.nickname}} - {{ticket.shipped | dateTime: 'dd/MM/yyyy'}} + {{ticket.shipped | date: 'dd/MM/yyyy'}} {{ticket.total | currency: 'EUR': 2}} diff --git a/modules/item/back/methods/item/regularize.js b/modules/item/back/methods/item/regularize.js index 17f1dbe1e..b9aa0d85f 100644 --- a/modules/item/back/methods/item/regularize.js +++ b/modules/item/back/methods/item/regularize.js @@ -105,15 +105,18 @@ module.exports = Self => { async function getTicketId(params, options) { - const currentDate = new Date(); - currentDate.setHours(null, null, null); + const minDate = new Date(); + minDate.setHours(0, 0, 0, 0); + + const maxDate = new Date(); + maxDate.setHours(23, 59, 59, 59); let ticket = await Self.app.models.Ticket.findOne({ where: { addressFk: params.addressFk, warehouseFk: params.warehouseFk, - shipped: currentDate, - landed: currentDate + shipped: {between: [minDate, maxDate]}, + landed: {between: [minDate, maxDate]} } }, options); diff --git a/modules/item/front/create/index.html b/modules/item/front/create/index.html index d852faab7..557ccdf77 100644 --- a/modules/item/front/create/index.html +++ b/modules/item/front/create/index.html @@ -53,13 +53,13 @@ - - + + + +
diff --git a/modules/item/front/descriptor/index.html b/modules/item/front/descriptor/index.html index cd12c253f..e273b02e4 100644 --- a/modules/item/front/descriptor/index.html +++ b/modules/item/front/descriptor/index.html @@ -64,32 +64,9 @@ ng-class="{bright: $ctrl.item.isActive == false}">
- + +
- {{::sale.date | dateTime:'dd/MM/yyyy' }} + {{::sale.date | date:'dd/MM/yyyy' }} diff --git a/modules/item/front/diary/index.js b/modules/item/front/diary/index.js index a15c32644..e128369a2 100644 --- a/modules/item/front/diary/index.js +++ b/modules/item/front/diary/index.js @@ -52,24 +52,31 @@ class Controller { get freeLineIndex() { let lines = this.$scope.model.data; - let currentDate = new Date(); - currentDate.setHours(0, 0, 0); + let minDate = new Date(); + minDate.setHours(0, 0, 0, 0); + + let maxDate = new Date(); + maxDate.setHours(23, 59, 59, 59); + for (let i = 0; i < lines.length; i++) { - let isFutureDate = new Date(lines[i].date) >= currentDate; + const dated = new Date(lines[i].date); - if (isFutureDate) + let isForFuture = dated > maxDate; + let isForToday = (dated >= minDate && dated <= maxDate); + + if (isForFuture || isForToday) return i; } } get onPreparationLineIndex() { let lines = this.$scope.model.data; - for (let i = this.freeLineIndex; i >= 0; i--) { let line = lines[i]; + let currentDate = new Date(); - currentDate.setHours(0, 0, 0); + currentDate.setHours(0, 0, 0, 0); let isPastDate = new Date(lines[i].date) < currentDate; let isPicked = line.alertLevel == 1 && line.isPicked; @@ -95,6 +102,7 @@ class Controller { let selectedTicketLineIndex = this.givenTicketIndex; let lineIndex = this.onPreparationLineIndex; + let lines = body.querySelector('vn-tbody').children; if (lineIndex == undefined || !lines.length) return; @@ -120,7 +128,6 @@ class Controller { offsetTop = onPreparationLine.offsetTop - headerHeight; this.$window.scrollTo(0, offsetTop); - this.ticketFk = null; } diff --git a/modules/item/front/index/index.html b/modules/item/front/index/index.html index aae3b0660..2f53c9c08 100644 --- a/modules/item/front/index/index.html +++ b/modules/item/front/index/index.html @@ -6,15 +6,13 @@ data="items">
- - - + + + suggested-filter="{isActive: true}"> {{entry.warehouse| dashIfEmpty}} - {{entry.landed | dateTime:'dd/MM/yyyy HH:mm'}} + {{entry.landed | date:'dd/MM/yyyy HH:mm'}} {{entry.entryFk | dashIfEmpty}} {{entry.price2 | dashIfEmpty}} {{entry.price3 | dashIfEmpty}} diff --git a/modules/item/front/last-entries/style.scss b/modules/item/front/last-entries/style.scss index 63ffcb35b..6188daabc 100644 --- a/modules/item/front/last-entries/style.scss +++ b/modules/item/front/last-entries/style.scss @@ -15,10 +15,6 @@ vn-item-last-entries { } vn-date-picker { flex: none !important; - - .mdl-textfield{ - width: 400px !important; - } } @media screen and (max-width: 1440px) { .expendable { diff --git a/modules/item/front/request-search-panel/index.html b/modules/item/front/request-search-panel/index.html index 8e15be8fb..999bfd500 100644 --- a/modules/item/front/request-search-panel/index.html +++ b/modules/item/front/request-search-panel/index.html @@ -16,7 +16,7 @@ + order="shipped DESC, isOk ASC">
- - - - - + + + - - - - - Ticket ID - Shipped - Warehouse - SalesPerson - Description - Quantity - Price - Atender - Item - Concept - Sale quantity - State - - - - - - - {{request.ticketFk}} - - - - - {{::request.shipped | dateTime: 'dd/MM/yyyy'}} - - - {{::request.warehouse}} - - - {{::request.salesPersonNickname}} - - - {{::request.description}} - {{::request.quantity}} - {{::request.price | currency: 'EUR':2}} - - - {{::request.atenderNickname}} - - - - {{request.itemFk}} - - - - - - - - {{::request.itemDescription}} - - - - {{request.saleQuantity}} - - - - - - {{::$ctrl.getState(request.isOk)}} - - - - - - - - - - - + + + + + + Ticket ID + Shipped + Warehouse + SalesPerson + Description + Requested + Price + Atender + Item + Achieved + Concept + State + + + + + + + {{request.ticketFk}} + + + + + {{::request.shipped | date: 'dd/MM/yyyy'}} + + + {{::request.warehouse}} + + + {{::request.salesPersonNickname}} + + + {{::request.description}} + {{::request.quantity}} + {{::request.price | currency: 'EUR':2}} + + + {{::request.atenderNickname}} + + + + {{request.itemFk}} + + + + + + + {{request.saleQuantity}} + + + + + + + + {{request.itemDescription}} + + + {{$ctrl.getState(request.isOk)}} + + + + + + + + + + +
+ on-response="$ctrl.denyRequest(response)"> - -
Indicate the reasons to deny this request
-
+
Specify the reasons to deny this request
- - - -
+ + + +
\ No newline at end of file diff --git a/modules/item/front/request/index.js b/modules/item/front/request/index.js index f887be9a8..c18800001 100644 --- a/modules/item/front/request/index.js +++ b/modules/item/front/request/index.js @@ -9,8 +9,23 @@ export default class Controller { this.$ = $; this.vnApp = vnApp; this._ = $translate; - if (!$stateParams.q) - this.filter = {isOk: false, mine: true}; + if (!$stateParams.q) { + const today = new Date(); + today.setHours(23, 59, 59, 59); + + const lastWeek = new Date(); + lastWeek.setHours(0, 0, 0, 0); + lastWeek.setDate(lastWeek.getDate() - 7); + + this.filter = { + where: { + isOk: false, + mine: true, + from: lastWeek, + to: today + } + }; + } } $postLink() { @@ -21,7 +36,7 @@ export default class Controller { getState(isOk) { if (isOk === null) return 'Nueva'; - else if (isOk === -1 || isOk === 1) + else if (isOk === -1 || isOk) return 'Aceptada'; else return 'Denegada'; @@ -34,14 +49,12 @@ export default class Controller { quantity: request.saleQuantity }; - let endpoint = `/api/TicketRequests/${request.id}/confirm`; + let query = `/api/TicketRequests/${request.id}/confirm`; + this.$http.post(query, params).then(res => { + request.itemDescription = res.data.concept; + request.isOk = true; - this.$http.post(endpoint, params).then(() => { this.vnApp.showSuccess(this._.instant('Data saved!')); - this.$.model.refresh(); - }).catch( e => { - this.$.model.refresh(); - throw e; }); } } @@ -56,10 +69,7 @@ export default class Controller { this.$http.patch(endpoint, params).then(() => { this.vnApp.showSuccess(this._.instant('Data saved!')); - }).catch( e => { - this.$.model.refresh(); - throw e; - }); + }).then(() => this.confirmRequest(request)); } else this.confirmRequest(request); } @@ -86,7 +96,7 @@ export default class Controller { } showDenyReason(event, requestId) { - this.denyRequestId = requestId; + this.selectedRequest = requestId; this.$.denyReason.parent = event.target; this.$.denyReason.show(); document.querySelector('vn-item-request vn-textarea textArea').focus(); @@ -96,17 +106,21 @@ export default class Controller { delete this.denyRequestId; } - denyRequest() { + denyRequest(response) { + if (response !== 'ACCEPT') return; + let params = { observation: this.denyObservation }; - let endpoint = `/api/TicketRequests/${this.denyRequestId}/deny`; + let query = `/api/TicketRequests/${this.selectedRequest.id}/deny`; + this.$http.post(query, params).then(res => { + const request = res.data; + this.selectedRequest.isOk = request.isOk; + this.selectedRequest.attenderFk = request.attenderFk; + this.selectedRequest.response = request.response; - this.$http.post(endpoint, params).then(() => { this.vnApp.showSuccess(this._.instant('Data saved!')); - this.$.model.refresh(); - this.$.denyReason.hide(); this.denyObservation = null; }); } diff --git a/modules/item/front/request/index.spec.js b/modules/item/front/request/index.spec.js index e27bc3879..1704c61a1 100644 --- a/modules/item/front/request/index.spec.js +++ b/modules/item/front/request/index.spec.js @@ -53,15 +53,15 @@ describe('Item', () => { let model = controller.$.model; spyOn(model, 'refresh'); + const expectedResult = {concept: 'Melee Weapon'}; let request = {itemFk: 1, saleQuantity: 1, id: 1}; - $httpBackend.when('POST', `/api/TicketRequests/${request.id}/confirm`).respond(); - $httpBackend.expect('POST', `/api/TicketRequests/${request.id}/confirm`).respond(); + $httpBackend.when('POST', `/api/TicketRequests/${request.id}/confirm`).respond(expectedResult); + $httpBackend.expect('POST', `/api/TicketRequests/${request.id}/confirm`).respond(expectedResult); controller.confirmRequest(request); $httpBackend.flush(); expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); - expect($scope.model.refresh).toHaveBeenCalledWith(); }); }); @@ -110,20 +110,17 @@ describe('Item', () => { describe('denyRequest()', () => { it(`should perform a query and call vnApp.showSuccess(), refresh(), hide() and set denyObservation to null in the controller`, () => { spyOn(controller.vnApp, 'showSuccess'); - let model = controller.$.model; - spyOn(model, 'refresh'); - spyOn(controller.$.denyReason, 'hide'); - controller.denyRequestId = 1; + const request = {id: 1}; + const expectedResult = {isOk: false, attenderFk: 106, response: 'Denied!'}; + controller.selectedRequest = request; - $httpBackend.when('POST', `/api/TicketRequests/${controller.denyRequestId}/deny`).respond(); - $httpBackend.expect('POST', `/api/TicketRequests/${controller.denyRequestId}/deny`).respond(); - controller.denyRequest(); + $httpBackend.when('POST', `/api/TicketRequests/${request.id}/deny`).respond(expectedResult); + $httpBackend.expect('POST', `/api/TicketRequests/${request.id}/deny`).respond(expectedResult); + controller.denyRequest('ACCEPT'); $httpBackend.flush(); expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); - expect($scope.model.refresh).toHaveBeenCalledWith(); - expect($scope.denyReason.hide).toHaveBeenCalledWith(); }); }); }); diff --git a/modules/item/front/request/locale/es.yml b/modules/item/front/request/locale/es.yml index 760f30bd3..33710a327 100644 --- a/modules/item/front/request/locale/es.yml +++ b/modules/item/front/request/locale/es.yml @@ -1,5 +1,6 @@ Discard: Descartar -Indicate the reasons to deny this request: Indique las razones para descartar esta peticion +Specify the reasons to deny this request: Especifica las razones para descartar la petición Buy requests: Peticiones de compra Search request by id or alias: Buscar peticiones por identificador o alias -Sale quantity: C. conseguida \ No newline at end of file +Requested: Solicitado +Achieved: Conseguido \ No newline at end of file diff --git a/modules/item/front/search-panel/index.js b/modules/item/front/search-panel/index.js index 458382a99..a722fb1fc 100644 --- a/modules/item/front/search-panel/index.js +++ b/modules/item/front/search-panel/index.js @@ -2,8 +2,8 @@ import ngModule from '../module'; import SearchPanel from 'core/components/searchbar/search-panel'; class Controller extends SearchPanel { - constructor($scope) { - super(); + constructor($scope, $element) { + super($scope, $element); this.$ = $scope; this.moreFields = [ {field: 'id'}, @@ -58,8 +58,6 @@ class Controller extends SearchPanel { } } -Controller.$inject = ['$scope']; - ngModule.component('vnItemSearchPanel', { template: require('./index.html'), controller: Controller diff --git a/modules/item/front/summary/index.html b/modules/item/front/summary/index.html index 4a1e4fb56..a3ab3d8ae 100644 --- a/modules/item/front/summary/index.html +++ b/modules/item/front/summary/index.html @@ -2,11 +2,9 @@
{{$ctrl.item.id}} - {{$ctrl.summary.item.name}}
- - - +

Visible

diff --git a/modules/item/front/tags/index.js b/modules/item/front/tags/index.js index b187e5baf..c5e120d82 100644 --- a/modules/item/front/tags/index.js +++ b/modules/item/front/tags/index.js @@ -66,7 +66,7 @@ class Controller { this.$scope.watcher.check(); this.$scope.model.save().then(() => { this.$scope.watcher.notifySaved(); - this.$scope.model.refresh(); + this.$scope.watcher.updateOriginalData(); this.card.reload(); }); } diff --git a/modules/order/front/descriptor/index.html b/modules/order/front/descriptor/index.html index dcc9b16de..fd83d5c71 100644 --- a/modules/order/front/descriptor/index.html +++ b/modules/order/front/descriptor/index.html @@ -31,7 +31,7 @@ value="{{$ctrl.order.client.salesPerson.user.nickname}}"> + value="{{$ctrl.order.landed | date: 'dd/MM/yyyy' }}"> @@ -46,32 +46,9 @@ value="{{$ctrl.order.total | currency: 'EUR': 2}}">
- + +
- +
+ class="colored"> {{$ctrl.category.value}} + class="colored"> {{$ctrl.type.value}} + class="colored"> {{::tag.value}} - +
\ No newline at end of file diff --git a/modules/order/front/filter/index.js b/modules/order/front/filter/index.js index 4305f2506..780bf798b 100644 --- a/modules/order/front/filter/index.js +++ b/modules/order/front/filter/index.js @@ -151,7 +151,7 @@ class Controller { this.$panelScope = this.$scope.$new(); this.$panel = this.$compile(``)(this.$panelScope); - const panel = this.$panel.isolateScope().$ctrl; + const panel = this.$panel[0].$ctrl; panel.filter = this.filter; panel.onSubmit = filter => this.onPanelSubmit(filter); diff --git a/modules/order/front/filter/style.scss b/modules/order/front/filter/style.scss index 52125ace6..0cac5765f 100644 --- a/modules/order/front/filter/style.scss +++ b/modules/order/front/filter/style.scss @@ -39,10 +39,12 @@ vn-catalog-filter > div { } } .chips { + display: flex; flex-wrap: wrap; padding: $spacing-md; + overflow: hidden; + max-width: 100%; } - vn-autocomplete[vn-id="type"] .list { max-height: 20em } diff --git a/modules/order/front/index/index.html b/modules/order/front/index/index.html index 5a01182b9..c6a817742 100644 --- a/modules/order/front/index/index.html +++ b/modules/order/front/index/index.html @@ -6,7 +6,7 @@ order="landed DESC, clientFk">
- + {{::order.sourceApp}} - {{::order.created | dateTime: 'dd/MM/yyyy HH:mm'}} + {{::order.created | date: 'dd/MM/yyyy HH:mm'}} {{::order.landed | date:'dd/MM/yyyy'}} {{::order.companyCode}} {{::order.total | currency: 'EUR': 2 | dashIfEmpty}} diff --git a/modules/order/front/line/index.html b/modules/order/front/line/index.html index cbf492dbf..cdff9b35c 100644 --- a/modules/order/front/line/index.html +++ b/modules/order/front/line/index.html @@ -48,7 +48,7 @@ {{::row.warehouse.name}} - {{::row.shipped | dateTime: 'dd/MM/yyyy'}} + {{::row.shipped | date: 'dd/MM/yyyy'}} {{::row.quantity}} {{::row.price | currency: 'EUR':2}} diff --git a/modules/order/front/prices-popover/index.html b/modules/order/front/prices-popover/index.html index 3a7f34462..a88e027ac 100644 --- a/modules/order/front/prices-popover/index.html +++ b/modules/order/front/prices-popover/index.html @@ -1,13 +1,11 @@
- - - + - +
@@ -74,32 +72,9 @@ - + + + value="{{$ctrl.summary.created | date: 'dd/MM/yyyy HH:mm'}}"> + value="{{$ctrl.summary.confirmed | date: 'dd/MM/yyyy HH:mm'}}"> diff --git a/modules/route/back/methods/route/filter.js b/modules/route/back/methods/route/filter.js index 1ea962bed..eeeef1dac 100644 --- a/modules/route/back/methods/route/filter.js +++ b/modules/route/back/methods/route/filter.js @@ -85,10 +85,13 @@ module.exports = Self => { return {'r.m3': value}; case 'description': return {'r.description': {like: `%${value}%`}}; - case 'workerFk': case 'warehouseFk': + param = `v.${param}`; + return {[param]: value}; + case 'workerFk': case 'vehicleFk': case 'agencyModeFk': + param = `r.${param}`; return {[param]: value}; } }); diff --git a/modules/route/back/methods/route/specs/filter.spec.js b/modules/route/back/methods/route/specs/filter.spec.js index ba32c956c..a4742c128 100644 --- a/modules/route/back/methods/route/specs/filter.spec.js +++ b/modules/route/back/methods/route/specs/filter.spec.js @@ -117,6 +117,6 @@ describe('Route filter()', () => { let result = await app.models.Route.filter(ctx); - expect(result.length).toEqual(4); + expect(result.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/getTickets.spec.js b/modules/route/back/methods/route/specs/getTickets.spec.js index 3888156b6..15028309f 100644 --- a/modules/route/back/methods/route/specs/getTickets.spec.js +++ b/modules/route/back/methods/route/specs/getTickets.spec.js @@ -4,6 +4,6 @@ describe('route getTickets()', () => { it('should return the tickets for a given route', async() => { let result = await app.models.Route.getTickets(2); - expect(result.length).toEqual(4); + expect(result.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/guessPriority.spec.js b/modules/route/back/methods/route/specs/guessPriority.spec.js index cc536787a..075ea05c8 100644 --- a/modules/route/back/methods/route/specs/guessPriority.spec.js +++ b/modules/route/back/methods/route/specs/guessPriority.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); describe('route guessPriority()', () => { - const targetRouteId = 2; + const targetRouteId = 7; let routeTicketsToRestore; afterAll(async done => { @@ -17,25 +17,23 @@ describe('route guessPriority()', () => { it('should confirm the tickets in the target route have no priority yet', async() => { routeTicketsToRestore = await app.models.Ticket.find({where: {routeFk: targetRouteId}}); - expect(routeTicketsToRestore.length).toEqual(4); + expect(routeTicketsToRestore.length).toEqual(2); + + expect(routeTicketsToRestore[0].id).toEqual(23); expect(routeTicketsToRestore[0].priority).toBeNull(); - expect(routeTicketsToRestore[0].id).toEqual(7); + expect(routeTicketsToRestore[1].id).toEqual(24); expect(routeTicketsToRestore[1].priority).toBeNull(); - expect(routeTicketsToRestore[1].id).toEqual(8); - expect(routeTicketsToRestore[2].priority).toBeNull(); - expect(routeTicketsToRestore[2].id).toEqual(9); }); it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => { await app.models.Route.guessPriority(targetRouteId); let routeTickets = await app.models.Ticket.find({where: {routeFk: targetRouteId}, fields: ['id', 'priority']}); - expect(routeTickets.length).toEqual(4); - expect(routeTickets[0].priority).toEqual(1); - expect(routeTickets[0].id).toEqual(7); - expect(routeTickets[1].priority).toEqual(3); - expect(routeTickets[1].id).toEqual(8); - expect(routeTickets[2].priority).toEqual(2); - expect(routeTickets[2].id).toEqual(9); + expect(routeTickets.length).toEqual(2); + + expect(routeTickets[0].id).toEqual(23); + expect(routeTickets[0].priority).toEqual(3); + expect(routeTickets[1].id).toEqual(24); + expect(routeTickets[1].priority).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/summary.spec.js b/modules/route/back/methods/route/specs/summary.spec.js index 5b31ff19f..ba976ae94 100644 --- a/modules/route/back/methods/route/specs/summary.spec.js +++ b/modules/route/back/methods/route/specs/summary.spec.js @@ -11,7 +11,7 @@ describe('route summary()', () => { const result = await app.models.Route.summary(1); const agency = result.route.agencyMode(); - expect(agency.name).toEqual('Silla247'); + expect(agency.name).toEqual('inhouse pickup'); }); it(`should return a summary object containing it's vehicle`, async() => { @@ -31,6 +31,6 @@ describe('route summary()', () => { it(`should return a summary object containing data from the tickets`, async() => { const result = await app.models.Route.summary(2); - expect(result.tickets.length).toEqual(4); + expect(result.tickets.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/updateVolume.spec.js b/modules/route/back/methods/route/specs/updateVolume.spec.js index 75ee3c139..a1ff67acc 100644 --- a/modules/route/back/methods/route/specs/updateVolume.spec.js +++ b/modules/route/back/methods/route/specs/updateVolume.spec.js @@ -10,8 +10,8 @@ describe('route updateVolume()', () => { afterAll(async done => { - await originalRoute.updateAttributes({m3: 2.7}); - await ticketToRestore.updateAttributes({routeFk: 2}); + await originalRoute.updateAttributes({m3: 1.8}); + await ticketToRestore.updateAttributes({routeFk: null}); await app.models.RouteLog.destroyById(logIdToDestroy); done(); }); @@ -19,12 +19,12 @@ describe('route updateVolume()', () => { it('should confirm the original volume of the route is the expected', async() => { originalRoute = await app.models.Route.findById(routeId); - expect(originalRoute.m3).toEqual(2.7); + expect(originalRoute.m3).toEqual(1.8); }); it('should confirm the route volume is updated when a ticket is added', async() => { - ticketToRestore = await app.models.Ticket.findById(8); - let updatedTicket = await app.models.Ticket.findById(8); + ticketToRestore = await app.models.Ticket.findById(14); + let updatedTicket = await app.models.Ticket.findById(14); await updatedTicket.updateAttributes({routeFk: routeId}); await app.models.Route.updateVolume(ctx, routeId); @@ -36,8 +36,9 @@ describe('route updateVolume()', () => { it('should confirm the change is logged', async() => { let logs = await app.models.RouteLog.find({fields: ['id', 'newInstance']}); + let m3Log = logs.filter(log => { - return log.newInstance.m3 === 3.1; + return log.newInstance.m3 === 1.9; }); logIdToDestroy = m3Log[0].id; diff --git a/modules/route/front/descriptor/index.html b/modules/route/front/descriptor/index.html index 2e141e52e..312985cb4 100644 --- a/modules/route/front/descriptor/index.html +++ b/modules/route/front/descriptor/index.html @@ -24,7 +24,7 @@ value="{{$ctrl.route.id}}"> + value="{{$ctrl.route.created | date: 'dd/MM/yyyy'}}"> @@ -46,32 +46,9 @@ ng-class="{bright: $ctrl.route.m3 > $ctrl.route.vehicle.m3 && $ctrl.route.vehicle.m3 != NULL}">
- + +
- + {{::route.agencyName | dashIfEmpty}} {{::route.vehiclePlateNumber | dashIfEmpty}} - {{::route.created | dateTime:'dd/MM/yyyy' | dashIfEmpty}} + {{::route.created | date:'dd/MM/yyyy' | dashIfEmpty}} {{::route.m3 | dashIfEmpty}} {{::route.description | dashIfEmpty}} diff --git a/modules/route/front/summary/index.html b/modules/route/front/summary/index.html index 85a045106..0ec6c47fe 100644 --- a/modules/route/front/summary/index.html +++ b/modules/route/front/summary/index.html @@ -6,7 +6,7 @@ value="{{$ctrl.summary.route.id}}"> + value="{{$ctrl.summary.route.created | date: 'dd/MM/yyyy'}}"> @@ -23,10 +23,10 @@ + value="{{$ctrl.summary.route.time | date: 'HH:MM'}}"> + value="{{$ctrl.summary.route.finished | date: 'HH:MM'}}"> diff --git a/modules/ticket/back/methods/ticket-request/confirm.js b/modules/ticket/back/methods/ticket-request/confirm.js index 92dd06dae..53fb2527c 100644 --- a/modules/ticket/back/methods/ticket-request/confirm.js +++ b/modules/ticket/back/methods/ticket-request/confirm.js @@ -38,27 +38,27 @@ module.exports = Self => { try { let options = {transaction: tx}; - let item = await models.Item.findById(ctx.args.itemFk); + let item = await models.Item.findById(ctx.args.itemFk, null, options); if (!item) throw new UserError(`That item doesn't exists`); let request = await models.TicketRequest.findById(ctx.args.id, { include: {relation: 'ticket'} - }); + }, options); let [[stock]] = await Self.rawSql(`CALL vn.getItemVisibleAvailable(?,?,?,?)`, [ ctx.args.itemFk, request.ticket().shipped, request.ticket().warehouseFk, false - ]); + ], options); if (stock.available < 0) throw new UserError(`This item is not available`); if (request.saleFk) { - sale = await models.Sale.findById(request.saleFk); + sale = await models.Sale.findById(request.saleFk, null, options); sale.updateAttributes({ itemFk: ctx.args.itemFk, quantity: ctx.args.quantity, @@ -71,7 +71,11 @@ module.exports = Self => { quantity: ctx.args.quantity, concept: item.name }, options); - request.updateAttributes({saleFk: sale.id, itemFk: sale.itemFk, isOk: true}, options); + request.updateAttributes({ + saleFk: sale.id, + itemFk: sale.itemFk, + isOk: true + }, options); } query = `CALL vn.ticketCalculateSale(?)`; @@ -86,6 +90,8 @@ module.exports = Self => { }, options); await tx.commit(); + + return sale; } catch (error) { await tx.rollback(); throw error; diff --git a/modules/ticket/back/methods/ticket-request/deny.js b/modules/ticket/back/methods/ticket-request/deny.js index 817c90782..e663ef1bc 100644 --- a/modules/ticket/back/methods/ticket-request/deny.js +++ b/modules/ticket/back/methods/ticket-request/deny.js @@ -29,7 +29,7 @@ module.exports = Self => { let params = { isOk: false, - atenderFk: worker.id, + attenderFk: worker.id, response: ctx.args.observation, }; diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js index 40c1f7652..82e1ee8e0 100644 --- a/modules/ticket/back/methods/ticket-request/filter.js +++ b/modules/ticket/back/methods/ticket-request/filter.js @@ -28,7 +28,7 @@ module.exports = Self => { type: 'Number', description: `Search by warehouse` }, { - arg: 'atenderFk', + arg: 'attenderFk', type: 'Number', description: `Search requests atended by the given worker` }, { @@ -65,7 +65,7 @@ module.exports = Self => { let worker = await Self.app.models.Worker.findOne({where: {userFk: userId}}); if (ctx.args.mine) - ctx.args.atenderFk = worker.id; + ctx.args.attenderFk = worker.id; let where = buildFilter(ctx.args, (param, value) => { switch (param) { @@ -75,7 +75,7 @@ module.exports = Self => { : {'t.nickname': {like: `%${value}%`}}; case 'ticketFk': return {'t.id': value}; - case 'atenderFk': + case 'attenderFk': return {'tr.atenderFk': value}; case 'isOk': return {'tr.isOk': value}; @@ -106,13 +106,13 @@ module.exports = Self => { tr.ticketFk, tr.quantity, tr.price, - tr.atenderFk, + tr.atenderFk attenderFk, tr.description, tr.response, tr.saleFk, tr.isOk, s.quantity AS saleQuantity, - s.itemFK, + s.itemFk, i.name AS itemDescription, t.shipped, t.nickname, diff --git a/modules/ticket/back/methods/ticket-request/specs/confirm.spec.js b/modules/ticket/back/methods/ticket-request/specs/confirm.spec.js index 574469d86..2383fe560 100644 --- a/modules/ticket/back/methods/ticket-request/specs/confirm.spec.js +++ b/modules/ticket/back/methods/ticket-request/specs/confirm.spec.js @@ -1,27 +1,14 @@ const app = require('vn-loopback/server/server'); describe('ticket-request confirm()', () => { - let request; - let sale; + let originalRequest; + let originalSale; let createdSaleId; afterAll(async done => { - const paramsForRequest = { - saleFk: request.saleFk, - isOk: request.isOk, - itemFk: request.itemFk, - ticketFk: request.ticketFk - }; - - const paramsForSale = { - itemFk: sale.itemFk, - quantity: sale.quantity, - concept: sale.concept, - }; - - await request.updateAttributes(paramsForRequest); - await sale.updateAttributes(paramsForSale); - app.models.Sale.destroyById(createdSaleId); + await originalRequest.updateAttributes(originalRequest); + await originalSale.updateAttributes(originalSale); + await app.models.Sale.destroyById(createdSaleId); done(); }); @@ -65,10 +52,11 @@ describe('ticket-request confirm()', () => { const itemId = 1; const quantity = 10; - request = await app.models.TicketRequest.findById(requestId); - sale = await app.models.Sale.findById(saleId); + originalRequest = await app.models.TicketRequest.findById(requestId); + originalSale = await app.models.Sale.findById(saleId); - request.updateAttributes({saleFk: saleId}); + const request = await app.models.TicketRequest.findById(requestId); + await request.updateAttributes({saleFk: saleId}); let ctx = {req: {accessToken: {userId: 9}}, args: { itemFk: itemId, @@ -89,7 +77,8 @@ describe('ticket-request confirm()', () => { const itemId = 1; const quantity = 10; - request.updateAttributes({saleFk: null}); + const request = await app.models.TicketRequest.findById(requestId); + await request.updateAttributes({saleFk: null}); let ctx = {req: {accessToken: {userId: 9}}, args: { itemFk: itemId, diff --git a/modules/ticket/back/methods/ticket-request/specs/deny.spec.js b/modules/ticket/back/methods/ticket-request/specs/deny.spec.js index cac63586e..04152fa21 100644 --- a/modules/ticket/back/methods/ticket-request/specs/deny.spec.js +++ b/modules/ticket/back/methods/ticket-request/specs/deny.spec.js @@ -5,7 +5,7 @@ describe('ticket-request deny()', () => { afterAll(async done => { let params = { isOk: null, - atenderFk: request.atenderFk, + attenderFk: request.attenderFk, response: null, }; diff --git a/modules/ticket/back/methods/ticket-request/specs/filter.spec.js b/modules/ticket/back/methods/ticket-request/specs/filter.spec.js index 24e74e4df..329688866 100644 --- a/modules/ticket/back/methods/ticket-request/specs/filter.spec.js +++ b/modules/ticket/back/methods/ticket-request/specs/filter.spec.js @@ -37,7 +37,7 @@ describe('ticket-request filter()', () => { }); it('should return the ticket request matching the atender ID', async() => { - let ctx = {req: {accessToken: {userId: 9}}, args: {atenderFk: 35}}; + let ctx = {req: {accessToken: {userId: 9}}, args: {attenderFk: 35}}; let result = await app.models.TicketRequest.filter(ctx); let requestId = result[0].id; diff --git a/modules/ticket/back/methods/ticket/isEmpty.js b/modules/ticket/back/methods/ticket/isEmpty.js index 7f9de98b1..0465589e6 100644 --- a/modules/ticket/back/methods/ticket/isEmpty.js +++ b/modules/ticket/back/methods/ticket/isEmpty.js @@ -40,7 +40,8 @@ module.exports = function(Self) { }, options); const hasPurchaseRequests = await models.TicketRequest.count({ - ticketFk: id + ticketFk: id, + isOk: true }, options); const isEmpty = !hasSales && !hasPackages && diff --git a/modules/ticket/back/methods/ticket/uploadFile.js b/modules/ticket/back/methods/ticket/uploadFile.js index 7c968f5c9..e5ea465cb 100644 --- a/modules/ticket/back/methods/ticket/uploadFile.js +++ b/modules/ticket/back/methods/ticket/uploadFile.js @@ -7,36 +7,34 @@ module.exports = Self => { type: 'Number', description: 'The ticket id', http: {source: 'path'} - }, - { + }, { arg: 'warehouseId', type: 'Number', - description: '' - }, - { + description: 'The warehouse id', + required: true + }, { arg: 'companyId', type: 'Number', - description: '' - }, - { + description: 'The company id', + required: true + }, { arg: 'dmsTypeId', type: 'Number', - description: '' - }, - { + description: 'The dms type id', + required: true + }, { arg: 'reference', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'description', type: 'String', - description: '' - }, - { + required: true + }, { arg: 'hasFile', type: 'Boolean', - description: '' + description: 'True if has an attached file', + required: true }], returns: { type: 'Object', diff --git a/modules/ticket/back/models/ticket-request.json b/modules/ticket/back/models/ticket-request.json index dfb609e3c..7f1cb4b02 100644 --- a/modules/ticket/back/models/ticket-request.json +++ b/modules/ticket/back/models/ticket-request.json @@ -33,9 +33,12 @@ "isOk": { "type": "Boolean" }, - "atenderFk": { + "attenderFk": { "type": "Number", - "required": true + "required": true, + "mysql": { + "columnName": "atenderFk" + } }, "response": { "type": "String" @@ -55,7 +58,7 @@ "atender": { "type": "belongsTo", "model": "Worker", - "foreignKey": "atenderFk" + "foreignKey": "attenderFk" }, "requester": { "type": "belongsTo", diff --git a/modules/ticket/front/basic-data/step-one/index.html b/modules/ticket/front/basic-data/step-one/index.html index f545e7131..cd3ef442e 100644 --- a/modules/ticket/front/basic-data/step-one/index.html +++ b/modules/ticket/front/basic-data/step-one/index.html @@ -26,9 +26,12 @@ value-field="id" ng-model="$ctrl.addressId" order="isActive DESC"> - - {{::isActive ? '' : 'INACTIVE'}} {{::nickname}} - - {{::street}} - {{::city}} - {{::province.name}} - {{::agencyMode.name}} + + {{::!isActive ? '(Inactive)' : ''}} + {{::nickname}} + + , {{::street}}, {{::city}}, {{::province.name}} - {{::agencyMode.name}} + + ng-model="$ctrl.shipped"> + + - + + - - - - - {{::name}} - {{::warehouse.name}} - Max. {{::hour | dateTime: 'HH:mm'}} h. + {{::name}} - {{::warehouse.name}} - Max. {{::hour | date: 'HH:mm'}} h. diff --git a/modules/ticket/front/basic-data/step-one/style.scss b/modules/ticket/front/basic-data/step-one/style.scss index fff383a52..756d662fd 100644 --- a/modules/ticket/front/basic-data/step-one/style.scss +++ b/modules/ticket/front/basic-data/step-one/style.scss @@ -1,13 +1,8 @@ @import "variables"; -tpl-item{ - &.notActive { - background-color: $color-bg; +.vn-popover { + .address.inactive { color: $color-font-secondary; width: 100%; } - - & > .inactive { - text-transform: uppercase; - } } diff --git a/modules/ticket/front/descriptor/addStowaway.html b/modules/ticket/front/descriptor/addStowaway.html index 963c9b6c1..e46292714 100644 --- a/modules/ticket/front/descriptor/addStowaway.html +++ b/modules/ticket/front/descriptor/addStowaway.html @@ -25,7 +25,7 @@ {{ticket.id}} - {{ticket.landed | dateTime: 'dd/MM/yyyy'}} + {{ticket.landed | date: 'dd/MM/yyyy'}} {{ticket.agencyMode.name}} {{ticket.warehouse.name}} {{ticket.state.state.name}} diff --git a/modules/ticket/front/descriptor/index.html b/modules/ticket/front/descriptor/index.html index d830f09d9..9b981f27a 100644 --- a/modules/ticket/front/descriptor/index.html +++ b/modules/ticket/front/descriptor/index.html @@ -33,7 +33,7 @@ value="{{$ctrl.ticket.client.salesPerson.user.nickname}}"> + value="{{$ctrl.ticket.shipped | date: 'dd/MM/yyyy HH:mm' }}"> @@ -72,31 +72,10 @@ ng-class="{bright: $ctrl.ticket.isDeleted == true}">
-