diff --git a/.gitignore b/.gitignore index 3ef8c28fd..8cb76a829 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ coverage node_modules -dist/* +dist e2e/dms/*/ !e2e/dms/c4c !e2e/dms/c81 diff --git a/db/changes/10081-agency/00-zone_getEvents.sql b/db/changes/10081-agency/00-zone_getEvents.sql index 199074a72..35d95f7bb 100644 --- a/db/changes/10081-agency/00-zone_getEvents.sql +++ b/db/changes/10081-agency/00-zone_getEvents.sql @@ -1,10 +1,12 @@ +USE `vn`; +DROP procedure IF EXISTS `zone_getEvents`; -DROP PROCEDURE IF EXISTS `vn`.`zone_getEvents`; DELIMITER $$ -CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`zone_getEvents`( - vAgencyModeFk INT, +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `zone_getEvents`( vProvinceFk INT, - vPostCode VARCHAR(255)) + vPostCode VARCHAR(255), + vAgencyModeFk INT) BEGIN /** * Returns available events for the passed province/postcode and agency. @@ -13,9 +15,9 @@ BEGIN * @param vProvinceFk The province id * @param vPostCode The postcode or %NULL to use the province */ - DECLARE vGeoFk INT; - - IF vPostCode IS NOT NULL THEN + DECLARE vGeoFk INT; + + IF vPostCode IS NOT NULL THEN SELECT p.geoFk INTO vGeoFk FROM postCode p JOIN town t ON t.id = p.townFk @@ -24,24 +26,27 @@ BEGIN ELSE SELECT geoFk INTO vGeoFk FROM province - WHERE id = vProvinceFk; - END IF; + WHERE id = vProvinceFk; + END IF; - CALL zone_getFromGeo(vGeoFk); - - DELETE t FROM tmp.zone t - JOIN zone z ON z.id = t.id - WHERE z.agencyModeFk != vAgencyModeFk; - - SELECT e.`from`, e.`to`, e.weekDays + CALL zone_getFromGeo(vGeoFk); + + IF vAgencyModeFk IS NOT NULL THEN + DELETE t FROM tmp.zone t + JOIN zone z ON z.id = t.id + WHERE z.agencyModeFk != vAgencyModeFk; + END IF; + + SELECT e.`from`, e.`to`, e.weekDays FROM tmp.zone t JOIN zoneEvent e ON e.zoneFk = t.id; - - SELECT DISTINCT e.`day` + + SELECT DISTINCT e.`day` FROM tmp.zone t JOIN zoneExclusion e ON e.zoneFk = t.id; DROP TEMPORARY TABLE tmp.zone; END$$ + DELIMITER ; diff --git a/db/changes/10100-AllSaints/00-ACL.sql b/db/changes/10100-allSaints/00-ACL.sql similarity index 100% rename from db/changes/10100-AllSaints/00-ACL.sql rename to db/changes/10100-allSaints/00-ACL.sql diff --git a/db/changes/10100-AllSaints/00-travelLog.sql b/db/changes/10100-allSaints/00-travelLog.sql similarity index 100% rename from db/changes/10100-AllSaints/00-travelLog.sql rename to db/changes/10100-allSaints/00-travelLog.sql diff --git a/db/changes/10100-AllSaints/00-upperGap.sql b/db/changes/10100-allSaints/00-upperGap.sql similarity index 100% rename from db/changes/10100-AllSaints/00-upperGap.sql rename to db/changes/10100-allSaints/00-upperGap.sql diff --git a/db/changes/10100-AllSaints/00-userLog.sql b/db/changes/10100-allSaints/00-userLog.sql similarity index 100% rename from db/changes/10100-AllSaints/00-userLog.sql rename to db/changes/10100-allSaints/00-userLog.sql diff --git a/db/changes/10100-AllSaints/00-userPhoneType.sql b/db/changes/10100-allSaints/00-userPhoneType.sql similarity index 100% rename from db/changes/10100-AllSaints/00-userPhoneType.sql rename to db/changes/10100-allSaints/00-userPhoneType.sql diff --git a/db/changes/10100-AllSaints/00-zone.sql b/db/changes/10100-allSaints/00-zone.sql similarity index 100% rename from db/changes/10100-AllSaints/00-zone.sql rename to db/changes/10100-allSaints/00-zone.sql diff --git a/db/changes/10100-AllSaints/01-userPhone.sql b/db/changes/10100-allSaints/01-userPhone.sql similarity index 100% rename from db/changes/10100-AllSaints/01-userPhone.sql rename to db/changes/10100-allSaints/01-userPhone.sql diff --git a/db/changes/10100-AllSaints/01-zone_getAgency.sql b/db/changes/10100-allSaints/01-zone_getAgency.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getAgency.sql rename to db/changes/10100-allSaints/01-zone_getAgency.sql diff --git a/db/changes/10100-AllSaints/01-zone_getFirstShipped.sql b/db/changes/10100-allSaints/01-zone_getFirstShipped.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getFirstShipped.sql rename to db/changes/10100-allSaints/01-zone_getFirstShipped.sql diff --git a/db/changes/10100-AllSaints/01-zone_getLanded.sql b/db/changes/10100-allSaints/01-zone_getLanded.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getLanded.sql rename to db/changes/10100-allSaints/01-zone_getLanded.sql diff --git a/db/changes/10100-AllSaints/01-zone_getShippedWarehouse.sql b/db/changes/10100-allSaints/01-zone_getShippedWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getShippedWarehouse.sql rename to db/changes/10100-allSaints/01-zone_getShippedWarehouse.sql diff --git a/db/changes/10100-AllSaints/01-zone_getWarehouse.sql b/db/changes/10100-allSaints/01-zone_getWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getWarehouse.sql rename to db/changes/10100-allSaints/01-zone_getWarehouse.sql diff --git a/db/changes/10100-AllSaints/02-catalog_componentCalculate.sql b/db/changes/10100-allSaints/02-catalog_componentCalculate.sql similarity index 100% rename from db/changes/10100-AllSaints/02-catalog_componentCalculate.sql rename to db/changes/10100-allSaints/02-catalog_componentCalculate.sql diff --git a/db/changes/10100-AllSaints/02-ticketCalculate__.sql b/db/changes/10100-allSaints/02-ticketCalculate__.sql similarity index 100% rename from db/changes/10100-AllSaints/02-ticketCalculate__.sql rename to db/changes/10100-allSaints/02-ticketCalculate__.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetAgency.sql b/db/changes/10100-allSaints/02-zoneGetAgency.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetAgency.sql rename to db/changes/10100-allSaints/02-zoneGetAgency.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetFirstShipped__.sql b/db/changes/10100-allSaints/02-zoneGetFirstShipped__.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetFirstShipped__.sql rename to db/changes/10100-allSaints/02-zoneGetFirstShipped__.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetLanded.sql b/db/changes/10100-allSaints/02-zoneGetLanded.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetLanded.sql rename to db/changes/10100-allSaints/02-zoneGetLanded.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetShipped.sql b/db/changes/10100-allSaints/02-zoneGetShipped.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetShipped.sql rename to db/changes/10100-allSaints/02-zoneGetShipped.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetShippedWarehouse.sql b/db/changes/10100-allSaints/02-zoneGetShippedWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetShippedWarehouse.sql rename to db/changes/10100-allSaints/02-zoneGetShippedWarehouse.sql diff --git a/db/changes/10100-AllSaints/03-available_calc.sql b/db/changes/10100-allSaints/03-available_calc.sql similarity index 100% rename from db/changes/10100-AllSaints/03-available_calc.sql rename to db/changes/10100-allSaints/03-available_calc.sql diff --git a/db/changes/10100-AllSaints/03-catalog_calculate.sql b/db/changes/10100-allSaints/03-catalog_calculate.sql similarity index 100% rename from db/changes/10100-AllSaints/03-catalog_calculate.sql rename to db/changes/10100-allSaints/03-catalog_calculate.sql diff --git a/db/changes/10100-AllSaints/03-order_confirmWithUser.sql b/db/changes/10100-allSaints/03-order_confirmWithUser.sql similarity index 100% rename from db/changes/10100-AllSaints/03-order_confirmWithUser.sql rename to db/changes/10100-allSaints/03-order_confirmWithUser.sql diff --git a/db/changes/10100-AllSaints/03-ticketCalculateClon.sql b/db/changes/10100-allSaints/03-ticketCalculateClon.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCalculateClon.sql rename to db/changes/10100-allSaints/03-ticketCalculateClon.sql diff --git a/db/changes/10100-AllSaints/03-ticketComponentCalculate.sql b/db/changes/10100-allSaints/03-ticketComponentCalculate.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketComponentCalculate.sql rename to db/changes/10100-allSaints/03-ticketComponentCalculate.sql diff --git a/db/changes/10100-AllSaints/03-ticketCreateWithUser.sql b/db/changes/10100-allSaints/03-ticketCreateWithUser.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCreateWithUser.sql rename to db/changes/10100-allSaints/03-ticketCreateWithUser.sql diff --git a/db/changes/10100-AllSaints/03-ticketCreateWithoutZone.sql b/db/changes/10100-allSaints/03-ticketCreateWithoutZone.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCreateWithoutZone.sql rename to db/changes/10100-allSaints/03-ticketCreateWithoutZone.sql diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForDated.sql b/db/changes/10100-allSaints/04-zone_getOptionsForDated.sql similarity index 100% rename from db/changes/10100-AllSaints/04-zone_getOptionsForDated.sql rename to db/changes/10100-allSaints/04-zone_getOptionsForDated.sql diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForLanding.sql b/db/changes/10100-allSaints/04-zone_getOptionsForLanding.sql similarity index 100% rename from db/changes/10100-AllSaints/04-zone_getOptionsForLanding.sql rename to db/changes/10100-allSaints/04-zone_getOptionsForLanding.sql diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForShipment.sql b/db/changes/10100-allSaints/04-zone_getOptionsForShipment.sql similarity index 100% rename from db/changes/10100-AllSaints/04-zone_getOptionsForShipment.sql rename to db/changes/10100-allSaints/04-zone_getOptionsForShipment.sql diff --git a/db/changes/10100-AllSaints/05-catalog_calcFromItem.sql b/db/changes/10100-allSaints/05-catalog_calcFromItem.sql similarity index 100% rename from db/changes/10100-AllSaints/05-catalog_calcFromItem.sql rename to db/changes/10100-allSaints/05-catalog_calcFromItem.sql diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js index 13aeae436..4d6285603 100644 --- a/e2e/helpers/extensions.js +++ b/e2e/helpers/extensions.js @@ -7,8 +7,6 @@ import config from './config.js'; let currentUser; let actions = { - // Generic extensions - clickIfExists: async function(selector) { let exists = await this.exists(selector); if (exists) await this.click(selector); @@ -25,8 +23,6 @@ let actions = { }, selector); }, - // Salix specific extensions - changeLanguageToEnglish: async function() { let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]'; @@ -49,14 +45,15 @@ let actions = { login: async function(userName) { if (currentUser !== userName) { - let logoutClicked = await this.clickIfExists('#logout'); + let accountClicked = await this.clickIfExists('#user'); - if (logoutClicked) { + if (accountClicked) { let buttonSelector = '.vn-dialog.shown button[response=accept]'; - await this.wait(buttonSelector => { - return document.querySelector(buttonSelector) != null - || location.hash == '#!/login'; - }, buttonSelector); + await this.waitToClick('#logout') + .wait(buttonSelector => { + return document.querySelector(buttonSelector) != null + || location.hash == '#!/login'; + }, buttonSelector); await this.clickIfExists(buttonSelector); } @@ -72,7 +69,7 @@ let actions = { currentUser = userName; } else - await this.waitToClick('vn-topbar a[ui-sref="home"]'); + await this.waitToClick('a[ui-sref=home]'); }, waitForLogin: async function(userName) { @@ -189,7 +186,7 @@ let actions = { let element = selectorMatches[0]; if (selectorMatches.length > 1) - throw new Error(`multiple matches of ${elementSelector} found`); + throw new Error(`Multiple matches of ${elementSelector} found`); let isVisible = false; if (element) { @@ -347,7 +344,7 @@ let actions = { .write('vn-searchbar input', searchValue) .click('vn-searchbar vn-icon[icon="search"]') .wait(100) - .waitForNumberOfElements('.searchResult', 1) + .waitForNumberOfElements('.search-result', 1) .evaluate(() => { return document.querySelector('ui-view vn-card vn-table') != null; }) @@ -362,15 +359,16 @@ let actions = { accessToSection: function(sectionRoute) { return this.wait(`vn-left-menu`) .evaluate(sectionRoute => { - return document.querySelector(`vn-left-menu ul li ul li > a[ui-sref="${sectionRoute}"]`) != null; + return document.querySelector(`vn-left-menu li li > a[ui-sref="${sectionRoute}"]`) != null; }, sectionRoute) .then(nested => { - if (!nested) - return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); + if (nested) { + this.waitToClick('vn-left-menu vn-item-section > vn-icon[icon=keyboard_arrow_down]') + .wait('vn-left-menu .expanded'); + } - return this.waitToClick('vn-left-menu .collapsed') - .wait('vn-left-menu .expanded') - .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); + return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`) + .waitForSpinnerLoad(); }); }, @@ -429,10 +427,7 @@ let actions = { }, waitForSpinnerLoad: function() { - return this.wait(() => { - const element = document.querySelector('vn-spinner > div'); - return element.style.display == 'none'; - }); + return this.waitUntilNotPresent('vn-topbar vn-spinner'); } }; diff --git a/e2e/helpers/nightmare.js b/e2e/helpers/nightmare.js index 6706b6dc4..01dcdd5d3 100644 --- a/e2e/helpers/nightmare.js +++ b/e2e/helpers/nightmare.js @@ -13,9 +13,7 @@ module.exports = function createNightmare(width = 1280, height = 720) { x: 0, y: 0, waitTimeout: 2000, - // openDevTools: { - // mode: 'detach' - // } + // openDevTools: {mode: 'detach'} }).viewport(width, height); nightmare.on('console', (type, message, ...args) => { diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 838878b60..d7b41bc64 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -9,7 +9,7 @@ export default { invoiceOutButton: '.modules-menu > li[ui-sref="invoiceOut.index"]', claimsButton: '.modules-menu > li[ui-sref="claim.index"]', returnToModuleIndexButton: 'a[ui-sref="order.index"]', - userMenuButton: 'vn-topbar #user', + userMenuButton: '#user', userLocalWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.localWarehouseFk"]', userLocalBank: '.user-popover vn-autocomplete[ng-model="$ctrl.localBankFk"]', userLocalCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.localCompanyFk"]', @@ -23,7 +23,7 @@ export default { clientsIndex: { searchClientInput: `vn-textfield input`, searchButton: 'vn-searchbar vn-icon[icon="search"]', - searchResult: 'vn-client-index .vn-list-item', + searchResult: 'vn-client-index .vn-item', createClientButton: `vn-float-button`, othersButton: 'vn-left-menu li[name="Others"] > a' }, @@ -180,7 +180,7 @@ export default { acceptDeleteButton: '.vn-confirm.shown button[response="accept"]' }, itemsIndex: { - searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]', + searchIcon: 'vn-searchbar vn-icon[icon="search"]', createItemButton: `vn-float-button`, searchResult: 'vn-item-index a.vn-tr', searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]', @@ -221,7 +221,7 @@ export default { moreMenuRegularizeButton: '.vn-drop-down.shown li[name="Regularize stock"]', regularizeQuantityInput: '.vn-dialog.shown tpl-body > div > vn-textfield input', regularizeWarehouseAutocomplete: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.warehouseFk"]', - editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]', + editButton: 'vn-item-descriptor vn-float-button[icon="edit"]', regularizeSaveButton: '.vn-dialog.shown tpl-buttons > button', inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]', navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]' @@ -324,20 +324,20 @@ export default { setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button' }, ticketsIndex: { - openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]', + openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input', newTicketButton: 'vn-ticket-index > a', searchResult: 'vn-ticket-index vn-card > 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 vn-textfield input`, - searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`, - searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]', + searchTicketInput: `vn-searchbar input`, + searchWeeklyTicketInput: `vn-searchbar input`, + searchWeeklyClearInput: 'vn-searchbar vn-icon[icon=clear]', 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"]', + searchButton: 'vn-searchbar vn-icon[icon="search"]', + searchWeeklyButton: 'vn-searchbar vn-icon[icon="search"]', moreMenu: 'vn-ticket-index vn-icon-menu[icon=more_vert]', - moreMenuWeeklyTickets: '.vn-drop-down.shown li:nth-child(2)', + menuWeeklyTickets: 'vn-left-menu [ui-sref="ticket.weekly.index"]', sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[ng-model="weekly.weekDay"] input', weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr', firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]', @@ -472,7 +472,7 @@ export default { zoneAutocomplete: 'vn-autocomplete[ng-model="$ctrl.zoneId"]', 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 > vn-horizontal > table > tfoot > tr > td:nth-child(4)', + stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two vn-tfoot > vn-tr > :nth-child(6)', chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]', }, ticketComponents: { @@ -516,9 +516,9 @@ export default { saveStateButton: `button[type=submit]` }, claimsIndex: { - searchClaimInput: `vn-claim-index vn-textfield input`, + searchClaimInput: `vn-searchbar input`, searchResult: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a', - searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]' + searchButton: 'vn-searchbar vn-icon[icon="search"]' }, claimDescriptor: { moreMenu: 'vn-claim-descriptor vn-icon-menu[icon=more_vert]', @@ -584,7 +584,7 @@ export default { 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 vn-textfield input`, - searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]', + searchButton: 'vn-searchbar vn-icon[icon="search"]', createOrderButton: `vn-float-button`, }, orderDescriptor: { @@ -720,8 +720,8 @@ export default { navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]' }, invoiceOutIndex: { - searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`, - searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]', + searchInvoiceOutInput: `vn-searchbar input`, + searchButton: 'vn-searchbar vn-icon[icon="search"]', searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr', }, invoiceOutDescriptor: { diff --git a/e2e/paths/01-login/01_login.spec.js b/e2e/paths/01-login/01_login.spec.js index 03acc98a8..f612ad23c 100644 --- a/e2e/paths/01-login/01_login.spec.js +++ b/e2e/paths/01-login/01_login.spec.js @@ -30,7 +30,7 @@ describe('Login path', () => { it('should log in', async() => { const url = await nightmare .doLogin('employee', null) - .wait('#logout') + .wait('#user') .parsedUrl(); expect(url.hash).toEqual('#!/'); diff --git a/e2e/paths/04-item-module/01_summary.spec.js b/e2e/paths/04-item-module/01_summary.spec.js index bd06ebef4..8f493bfac 100644 --- a/e2e/paths/04-item-module/01_summary.spec.js +++ b/e2e/paths/04-item-module/01_summary.spec.js @@ -80,7 +80,7 @@ describe('Item summary path', () => { it('should search for other item', async() => { const result = await nightmare - .clearInput('vn-item-index vn-searchbar input') + .clearInput('vn-searchbar input') .waitToClick(selectors.itemsIndex.searchButton) .write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat fist 15cm') .waitToClick(selectors.itemsIndex.searchButton) diff --git a/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js b/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js index 8e4645051..ec9b16fa4 100644 --- a/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js +++ b/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js @@ -7,7 +7,7 @@ describe('Ticket expeditions and log path', () => { beforeAll(() => { return nightmare .loginAndModule('production', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.expedition'); }); diff --git a/e2e/paths/05-ticket-module/04_packages.spec.js b/e2e/paths/05-ticket-module/04_packages.spec.js index 30324aec2..b51bb8b95 100644 --- a/e2e/paths/05-ticket-module/04_packages.spec.js +++ b/e2e/paths/05-ticket-module/04_packages.spec.js @@ -7,7 +7,7 @@ describe('Ticket Create packages path', () => { beforeAll(() => { return nightmare .loginAndModule('employee', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.package'); }); diff --git a/e2e/paths/05-ticket-module/05_tracking_state.spec.js b/e2e/paths/05-ticket-module/05_tracking_state.spec.js index 5797b6798..7c319f1c9 100644 --- a/e2e/paths/05-ticket-module/05_tracking_state.spec.js +++ b/e2e/paths/05-ticket-module/05_tracking_state.spec.js @@ -8,7 +8,7 @@ describe('Ticket Create new tracking state path', () => { beforeAll(() => { return nightmare .loginAndModule('production', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.tracking.index'); }); @@ -44,7 +44,7 @@ describe('Ticket Create new tracking state path', () => { beforeAll(() => { return nightmare .loginAndModule('salesPerson', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.tracking.index'); }); diff --git a/e2e/paths/05-ticket-module/08_components.spec.js b/e2e/paths/05-ticket-module/08_components.spec.js index 4603650ac..501c2eaad 100644 --- a/e2e/paths/05-ticket-module/08_components.spec.js +++ b/e2e/paths/05-ticket-module/08_components.spec.js @@ -7,7 +7,7 @@ describe('Ticket List components path', () => { beforeAll(() => { return nightmare .loginAndModule('employee', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.components'); }); diff --git a/e2e/paths/05-ticket-module/09_weekly.spec.js b/e2e/paths/05-ticket-module/09_weekly.spec.js index a7a301d79..ad8a8020c 100644 --- a/e2e/paths/05-ticket-module/09_weekly.spec.js +++ b/e2e/paths/05-ticket-module/09_weekly.spec.js @@ -11,8 +11,7 @@ describe('Ticket descriptor path', () => { it('should count the amount of tickets in the turns section', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .wait(selectors.ticketsIndex.weeklyTicket) .countElement(selectors.ticketsIndex.weeklyTicket); @@ -72,8 +71,7 @@ describe('Ticket descriptor path', () => { it('should confirm the ticket 11 was added on thursday', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .waitToGetProperty(selectors.ticketsIndex.sixthWeeklyTicket, 'value'); expect(result).toEqual('Thursday'); @@ -132,8 +130,7 @@ describe('Ticket descriptor path', () => { it('should confirm the ticket 11 was added on saturday', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .waitToGetProperty(selectors.ticketsIndex.sixthWeeklyTicket, 'value'); expect(result).toEqual('Saturday'); @@ -160,7 +157,7 @@ describe('Ticket descriptor path', () => { it('should confirm the sixth weekly ticket was deleted', async() => { const result = await nightmare - .waitToClick('vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]') + .waitToClick('vn-searchbar vn-icon[icon=clear]') .waitToClick(selectors.ticketsIndex.searchWeeklyButton) .waitForNumberOfElements(selectors.ticketsIndex.searchWeeklyResult, 5) .countElement(selectors.ticketsIndex.searchWeeklyResult); diff --git a/e2e/paths/05-ticket-module/10_request.spec.js b/e2e/paths/05-ticket-module/10_request.spec.js index 0a31cb75a..44dc3e1af 100644 --- a/e2e/paths/05-ticket-module/10_request.spec.js +++ b/e2e/paths/05-ticket-module/10_request.spec.js @@ -7,7 +7,7 @@ describe('Ticket purchase request path', () => { beforeAll(() => { nightmare .loginAndModule('salesPerson', 'ticket') - .accessToSearchResult('id:16') + .accessToSearchResult('16') .accessToSection('ticket.card.request.index'); }); diff --git a/e2e/paths/05-ticket-module/11_diary.spec.js b/e2e/paths/05-ticket-module/11_diary.spec.js index aeec780e7..c64c0ba7b 100644 --- a/e2e/paths/05-ticket-module/11_diary.spec.js +++ b/e2e/paths/05-ticket-module/11_diary.spec.js @@ -1,5 +1,4 @@ import selectors from '../../helpers/selectors.js'; -import config from '../../helpers/config.js'; import createNightmare from '../../helpers/nightmare'; describe('Ticket diary path', () => { @@ -31,12 +30,9 @@ describe('Ticket diary path', () => { }); it(`should navigate to the item diary from the 1st sale item id descriptor popover`, async() => { - const itemId = 2; const url = await nightmare .waitToClick(selectors.ticketSummary.firstSaleItemId) .waitToClick(selectors.ticketSummary.popoverDiaryButton) - .waitForLogin('employee') - .goto(`${config.url}#!/item/${itemId}/diary?warehouseFk=1&ticketFk=1`) .parsedUrl(); expect(url.hash).toContain('/diary'); diff --git a/e2e/paths/05-ticket-module/13_services.spec.js b/e2e/paths/05-ticket-module/13_services.spec.js index 775357bda..01da91f1b 100644 --- a/e2e/paths/05-ticket-module/13_services.spec.js +++ b/e2e/paths/05-ticket-module/13_services.spec.js @@ -21,7 +21,7 @@ describe('Ticket services path', () => { .isDisabled(selectors.ticketService.firstAddDescriptionButton); expect(result).toBeTruthy(); - }); + }, 100000); it('should receive an error if you attempt to save a service without access rights', async() => { const result = await nightmare diff --git a/front/core/components/autocomplete/index.js b/front/core/components/autocomplete/index.js index 9808fd667..e2c9b7ad4 100755 --- a/front/core/components/autocomplete/index.js +++ b/front/core/components/autocomplete/index.js @@ -208,11 +208,9 @@ export default class Autocomplete extends Field { onContainerKeyDown(event) { if (event.defaultPrevented) return; - switch (event.key) { case 'ArrowUp': case 'ArrowDown': - case 'Enter': this.showDropDown(); break; default: diff --git a/front/core/components/button-menu/index.js b/front/core/components/button-menu/index.js index 946e1fd7f..c19962b08 100644 --- a/front/core/components/button-menu/index.js +++ b/front/core/components/button-menu/index.js @@ -7,7 +7,7 @@ export default class ButtonMenu extends Button { constructor($element, $scope, $transclude) { super($element, $scope); this.$transclude = $transclude; - $element.on('click', e => this.onClick(e)); + $element.on('click', e => this.onButtonClick(e)); } get model() { @@ -41,8 +41,7 @@ export default class ButtonMenu extends Button { Object.assign(this.$.dropDown, props); } - onClick(event) { - if (this.disabled) return; + onButtonClick(event) { if (event.defaultPrevented) return; this.emit('open'); this.showDropDown(); diff --git a/front/core/components/button-menu/index.spec.js b/front/core/components/button-menu/index.spec.js index 1e8c048ff..87f677001 100644 --- a/front/core/components/button-menu/index.spec.js +++ b/front/core/components/button-menu/index.spec.js @@ -14,7 +14,7 @@ describe('Component vnButtonMenu', () => { $element.remove(); }); - describe('onClick(event)', () => { + describe('onButtonClick(event)', () => { it(`should emit the open event`, () => { spyOn(controller, 'emit'); @@ -23,7 +23,7 @@ describe('Component vnButtonMenu', () => { bubbles: true, cancelable: true }); - controller.onClick(event); + controller.onButtonClick(event); expect(controller.emit).toHaveBeenCalledWith('open'); }); diff --git a/front/core/components/button/style.scss b/front/core/components/button/style.scss index 74dcbcf9b..032c88cbb 100644 --- a/front/core/components/button/style.scss +++ b/front/core/components/button/style.scss @@ -39,19 +39,19 @@ } &.colored { color: white; - background-color: $color-main; + background-color: $color-button; 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%); + background-color: lighten($color-button, 10%); } } } &.flat { - color: $color-main; + color: $color-button; background-color: transparent; box-shadow: none; transition: background 200ms ease-in-out; diff --git a/front/core/components/check/style.scss b/front/core/components/check/style.scss index 31715a2cd..b84b61ce5 100644 --- a/front/core/components/check/style.scss +++ b/front/core/components/check/style.scss @@ -19,7 +19,7 @@ } &.checked > .btn { border-color: transparent; - background-color: $color-main; + background-color: $color-button; & > .mark { top: 0; diff --git a/front/core/components/date-picker/index.js b/front/core/components/date-picker/index.js index 17ce19406..24f8cf90f 100644 --- a/front/core/components/date-picker/index.js +++ b/front/core/components/date-picker/index.js @@ -14,7 +14,9 @@ class DatePicker extends Field { let value = this.input.value; if (value) { - date = new Date(value); + let ymd = value.split('-') + .map(e => parseInt(e)); + date = new Date(ymd[0], ymd[1] - 1, ymd[2]); if (this.field) { let orgDate = this.field instanceof Date diff --git a/front/core/components/date-picker/index.spec.js b/front/core/components/date-picker/index.spec.js index 411a92105..2dd419e07 100644 --- a/front/core/components/date-picker/index.spec.js +++ b/front/core/components/date-picker/index.spec.js @@ -3,6 +3,10 @@ describe('Component vnDatePicker', () => { let $element; let $ctrl; + let today; + today = new Date(); + today.setHours(0, 0, 0, 0); + beforeEach(ngModule('vnCore')); beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => { @@ -18,14 +22,19 @@ 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('date')(today, 'yyyy-MM-dd'); expect($ctrl.value).toEqual(displayed); }); }); + + describe('onValueUpdate()', () => { + it(`should change the picker value to selected date`, () => { + $ctrl.value = $filter('date')(today, 'yyyy-MM-dd'); + $ctrl.input.dispatchEvent(new Event('change')); + + expect($ctrl.field).toEqual(today); + }); + }); }); diff --git a/front/core/components/dialog/index.js b/front/core/components/dialog/index.js index 0891542a2..7a7cb5f9f 100644 --- a/front/core/components/dialog/index.js +++ b/front/core/components/dialog/index.js @@ -29,6 +29,10 @@ export default class Dialog extends Popup { * @return {Promise} A promise that will be resolved with response when dialog is closed */ show(data, responseHandler) { + if (this.shown) + return this.$q.reject(new Error('Dialog already shown')); + super.show(); + if (typeof data == 'function') { responseHandler = data; data = null; @@ -36,27 +40,27 @@ export default class Dialog extends Popup { this.data = data; this.showHandler = responseHandler; - super.show(); return this.$q(resolve => { this.resolve = resolve; }); } /** - * Hides the dialog. + * Hides the dialog resolving the promise returned by show(). * * @param {String} response The response */ hide(response) { if (!this.shown) return; - this.showHandler = null; super.hide(); + + this.showHandler = null; if (this.resolve) this.resolve(response); } /** - * Calls the response handler. + * Calls the response handlers. * * @param {String} response The response code * @return {Boolean} The response handler return diff --git a/front/core/components/dialog/index.spec.js b/front/core/components/dialog/index.spec.js index a898261fb..8c41bc060 100644 --- a/front/core/components/dialog/index.spec.js +++ b/front/core/components/dialog/index.spec.js @@ -28,14 +28,6 @@ describe('Component vnDialog', () => { expect(called).toBeTruthy(); }); - it(`should hide the dialog when response is given`, () => { - controller.show(); - spyOn(controller, 'hide'); - controller.respond('answer'); - - expect(controller.hide).toHaveBeenCalledWith('answer'); - }); - it(`should not hide the dialog when false is returned from response handler`, () => { controller.show(() => false); spyOn(controller, 'hide'); @@ -46,12 +38,13 @@ describe('Component vnDialog', () => { }); describe('hide()', () => { - it(`should do nothing if it's already hidden`, () => { - controller.onResponse = () => {}; - spyOn(controller, 'onResponse'); + it(`should resolve the promise returned by show`, () => { + let resolved = true; + controller.show().then(() => resolved = true); controller.hide(); + $scope.$apply(); - expect(controller.onResponse).not.toHaveBeenCalledWith(); + expect(resolved).toBeTruthy(); }); }); @@ -94,7 +87,7 @@ describe('Component vnDialog', () => { expect(controller.onAccept).toHaveBeenCalledWith({$response: 'accept'}); }); - it(`should resolve the promise returned by show() with response when hidden`, () => { + it(`should resolve the promise returned by show() with response`, () => { let response; controller.show().then(res => response = res); controller.respond('response'); diff --git a/front/core/components/dialog/style.scss b/front/core/components/dialog/style.scss index 9461a0063..2f3d9a028 100644 --- a/front/core/components/dialog/style.scss +++ b/front/core/components/dialog/style.scss @@ -36,7 +36,7 @@ background-color: transparent; border: none; border-radius: .1em; - color: $color-main; + color: $color-button; font-family: vn-font-bold; padding: .7em; margin: -0.7em; diff --git a/front/core/components/drop-down/index.js b/front/core/components/drop-down/index.js index 163553aeb..9f2dfe424 100644 --- a/front/core/components/drop-down/index.js +++ b/front/core/components/drop-down/index.js @@ -5,6 +5,7 @@ import template from './index.html'; import ArrayModel from '../array-model/array-model'; import CrudModel from '../crud-model/crud-model'; import {mergeWhere} from 'vn-loopback/util/filter'; +import focus from '../../lib/focus'; /** * @event select Thrown when model item is selected @@ -86,9 +87,11 @@ export default class DropDown extends Popover { * @param {String} search The initial search term or %null */ show(parent, search) { - this._activeOption = -1; + if (this.shown) return; super.show(parent); + this._activeOption = -1; + this.list = this.popup.querySelector('.list'); this.ul = this.popup.querySelector('ul'); @@ -102,21 +105,25 @@ export default class DropDown extends Popover { this.search = search; this.buildList(); - let input = this.popup.querySelector('input'); - setTimeout(() => input.focus()); + focus(this.popup.querySelector('input')); } - onClose() { + hide() { + if (!this.shown) return; + super.hide(); + this.document.removeEventListener('keydown', this.docKeyDownHandler); this.docKeyDownHandler = null; this.list.removeEventListener('scroll', this.listScrollHandler); this.listScrollHandler = null; + } + onClose() { + this.destroyList(); this.list = null; this.ul = null; - this.destroyList(); super.onClose(); } @@ -387,7 +394,7 @@ export default class DropDown extends Popover { let filter = { order, - limit: this.limit || 8 + limit: this.limit || 30 }; if (model instanceof CrudModel) { diff --git a/front/core/components/field/index.html b/front/core/components/field/index.html index 8d12ddd77..d614d157f 100644 --- a/front/core/components/field/index.html +++ b/front/core/components/field/index.html @@ -28,8 +28,7 @@ ng-transclude="append" class="append"> -
-
+
diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index 0155c88a8..481ccce91 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -50,7 +50,7 @@ export default class Field extends FormInput { } set value(value) { - this.field = value; + this.input.value = value; } get value() { diff --git a/front/core/components/field/style.scss b/front/core/components/field/style.scss index add3a3228..f3bc0ae04 100644 --- a/front/core/components/field/style.scss +++ b/front/core/components/field/style.scss @@ -2,6 +2,7 @@ .vn-field { display: inline-block; + box-sizing: border-box; width: 100%; & > .container { @@ -22,7 +23,7 @@ top: 18px; line-height: 20px; pointer-events: none; - color: $color-font-secondary; + color: $color-font-bg-marginal; transition-property: top, color, font-size; transition-duration: 400ms; transition-timing-function: cubic-bezier(.4, 0, .2, 1); @@ -67,7 +68,11 @@ } & > input { position: relative; + color: $color-font; + &::placeholder { + color: $color-font-bg; + } &[type=time], &[type=date], &[type=password] { @@ -121,12 +126,13 @@ & > .icons { display: flex; align-items: center; - color: $color-font-secondary; + color: $color-font-bg-marginal; } & > .prepend > prepend, & > .append > append, & > .icons { display: flex; + align-items: center; & > vn-icon { font-size: 24px; @@ -159,7 +165,7 @@ } &.focus { height: 2px; - background-color: $color-main; + background-color: $color-primary; left: 50%; width: 0; transition-property: width, left, background-color; @@ -190,13 +196,57 @@ } } } + &.standout { + border-radius: .1em; + background-color: rgba(255, 255, 255, .1); + padding: 0 12px; + transition-property: background-color, color; + transition-duration: 200ms; + transition-timing-function: ease-in-out; + + & > .container { + & > .underline { + display: none; + } + & > .infix > .control > * { + color: $color-font-dark; + + &::placeholder { + color: $color-font-bg-dark; + } + } + & > .prepend, + & > .append, + & > .icons { + color: $color-font-bg-dark-marginal; + } + } + &.focused { + background-color: $color-bg-panel; + + & > .container { + & > .infix > .control > * { + color: $color-font; + + &::placeholder { + color: $color-font-bg; + } + } + & > .prepend, + & > .append, + & > .icons { + color: $color-font-bg-marginal; + } + } + } + } &.not-empty, &.focused, &.invalid { & > .container > .infix { & > label { top: 5px; - color: $color-main; + color: $color-primary; padding: 0; font-size: 12px; } diff --git a/front/core/components/icon-button/style.scss b/front/core/components/icon-button/style.scss index 1126c5e6d..d59980a62 100644 --- a/front/core/components/icon-button/style.scss +++ b/front/core/components/icon-button/style.scss @@ -2,7 +2,7 @@ .vn-icon-button { @extend %clickable-light; - color: $color-main; + color: $color-button; & > button { padding: .2em !important; diff --git a/front/core/components/index.js b/front/core/components/index.js index bc19db642..0ebd03420 100644 --- a/front/core/components/index.js +++ b/front/core/components/index.js @@ -41,6 +41,7 @@ import './list'; import './popover'; import './popup'; import './radio'; +import './slot'; import './submit'; import './table'; import './td-editable'; diff --git a/front/core/components/list/style.scss b/front/core/components/list/style.scss index 0786cacf8..6f12ce7c6 100644 --- a/front/core/components/list/style.scss +++ b/front/core/components/list/style.scss @@ -1,45 +1,102 @@ @import "./effects"; +/* +ul.menu { + list-style-type: none; + padding: 0; + padding-top: $spacing-md; + margin: 0; + font-size: inherit; + & > li > a { + @extend %clickable; + display: block; + color: inherit; + padding: .6em 2em; + } +} +*/ + +vn-list, .vn-list { + display: block; max-width: $width-sm; margin: 0 auto; + padding: 0; + list-style-type: none; - a.vn-list-item { - @extend %clickable; + vn-list, + .vn-list { + vn-item, + .vn-item { + padding-left: $spacing-lg; + } } - .vn-list-item { - border-bottom: $border-thin-light; - display: block; - text-decoration: none; - color: inherit; - - & > vn-horizontal { + &.separated { + vn-item, + .vn-item { + border-bottom: $border-thin-light; padding: $spacing-md; - - & > vn-one { - overflow: hidden; + + &:last-child { + border-bottom: none; } - & > .buttons { - align-items: center; + } + } +} + +vn-item, +.vn-item { + @extend %clickable; + display: flex; + align-items: center; + color: inherit; + padding: $spacing-sm $spacing-md; + text-decoration: none; + min-height: 40px; + box-sizing: border-box; + + &.separated { + border-bottom: $border-thin-light; - vn-icon-button { - opacity: .4; - margin-left: .5em; - transition: opacity 250ms ease-out; - padding: 0; - font-size: 1.2em; - - &:hover { - opacity: 1; - } + &:last-child { + border-bottom: none; + } + } + &.active { + @extend %active; + } + & > vn-item-section { + overflow: hidden; + flex: 1; + + &[avatar] { + display: flex; + flex: none; + align-items: center; + margin-right: $spacing-md; + + & > .vn-icon { + font-size: 1.2em; + } + } + &[side] { + display: flex; + flex: none; + align-items: center; + + & > .vn-button { + opacity: .4; + margin-left: .5em; + transition: opacity 250ms ease-out; + padding: 0; + font-size: 1.05em; + + &:hover { + opacity: 1; } } } } - vn-empty-rows { - display: block; - text-align: center; - padding: 1.5em; - box-sizing: border-box; - } -} \ No newline at end of file +} + + diff --git a/front/core/components/popup/index.js b/front/core/components/popup/index.js index 525346819..994f4a0e7 100644 --- a/front/core/components/popup/index.js +++ b/front/core/components/popup/index.js @@ -98,8 +98,9 @@ export default class Popup extends Component { onClose() { this.closeTimeout = null; this.popup.remove(); - this.$contentScope.$destroy(); this.popup = null; + this.$contentScope.$destroy(); + this.$contentScope = null; this.windowEl = null; this.emit('close'); } diff --git a/front/core/components/radio/style.scss b/front/core/components/radio/style.scss index 787c0986e..2ee037e65 100644 --- a/front/core/components/radio/style.scss +++ b/front/core/components/radio/style.scss @@ -9,7 +9,7 @@ } } &.checked > .btn { - border-color: $color-main; + border-color: $color-button; & > .mark { position: absolute; @@ -19,7 +19,7 @@ transform: translate(-50%, -50%); width: 10px; height: 10px; - background-color: $color-main; + background-color: $color-button; } } &.disabled.checked > .btn > .mark { diff --git a/front/core/components/range/style.scss b/front/core/components/range/style.scss index a370b4c39..6898f8cda 100644 --- a/front/core/components/range/style.scss +++ b/front/core/components/range/style.scss @@ -5,7 +5,7 @@ -webkit-appearance: none; margin-top: -5px; border-radius: 50%; - background: $color-main; + background: $color-button; border: none; height: 12px; width: 12px; @@ -15,7 +15,7 @@ transition-timing-function: ease-out; } &:focus::#{$thumb-selector} { - box-shadow: 0 0 0 10px rgba($color-main, .2); + box-shadow: 0 0 0 10px rgba($color-button, .2); } &:active::#{$thumb-selector} { transform: scale(1.5); @@ -29,7 +29,7 @@ width: 100%; height: 3px; cursor: inherit; - background: $color-secondary; + background: $color-marginal; border-radius: 2px; border: none; } @@ -40,7 +40,7 @@ font-size: 12px; &.main { - color: $color-main; + color: $color-button; } &.min-label { float: left; diff --git a/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap b/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap deleted file mode 100644 index 698e1ba88..000000000 --- a/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Component vnSearchbar doSearch() should define searchString, call onSearch and the model applyFilter method 1`] = `"function search() { [native code] } 0:i 1:d 2:: 3:( ) 4:1 5:2 6:3 7:4 8:5 9:6"`; - -exports[`Component vnSearchbar onPanelSubmit() should hide the popover, and set the filter as it wasn't defined 1`] = `Object {}`; - -exports[`Component vnSearchbar onPanelSubmit() should hide the popover, compact the filter 1`] = ` -Object { - "id": 999, -} -`; - -exports[`Component vnSearchbar onSubmit() should define the controller's filter 1`] = ` -Object { - "search": "id: 999", -} -`; diff --git a/front/core/components/searchbar/searchbar.html b/front/core/components/searchbar/searchbar.html index 069a9411c..d6f4720f2 100644 --- a/front/core/components/searchbar/searchbar.html +++ b/front/core/components/searchbar/searchbar.html @@ -1,14 +1,26 @@
+
+ + + + {{param.chip}} + +
this.onStateChange()); + constructor($element, $) { + super($element, $); + this.searchState = '.'; - this._filter = null; - this.autoLoad = false; + let criteria = {}; + this.deregisterCallback = this.$transitions.onSuccess( + criteria, () => this.onStateChange()); } $postLink() { - if (this.filter === null) - this.onStateChange(); + this.onStateChange(); } - set filter(value) { - this._filter = value; - this.$state.go('.', {q: JSON.stringify(value)}, {location: 'replace'}); + $onDestroy() { + this.deregisterCallback(); } get filter() { return this._filter; } - onStateChange() { - this._filter = null; - - if (this.$state.params.q) { - try { - this._filter = JSON.parse(this.$state.params.q); - } catch (e) { - console.error(e); - } - } - - this.doSearch(); + set filter(value) { + this._filter = value; + this.toBar(value); } get shownFilter() { - return this._filter != null ? this._filter : this.suggestedFilter; + return this.filter != null + ? this.filter + : this.suggestedFilter; + } + + get searchString() { + return this._searchString; + } + + set searchString(value) { + this._searchString = value; + if (value == null) this.params = []; + } + + onStateChange() { + let filter = null; + + if (this.$state.is(this.searchState)) { + if (this.$params.q) { + try { + filter = JSON.parse(this.$params.q); + } catch (e) { + console.error(e); + } + } + + focus(this.element.querySelector('vn-textfield input')); + } + + this.filter = filter; } openPanel(event) { @@ -88,21 +99,136 @@ export default class Controller extends Component { onPanelSubmit(filter) { this.$.popover.hide(); filter = compact(filter); - this.filter = filter != null ? filter : {}; - - let element = this.element.querySelector('vn-textfield input'); - element.select(); - element.focus(); + filter = filter != null ? filter : {}; + this.doSearch(filter); } onSubmit() { - this.filter = this.getObjectFromString(this.searchString); + this.doSearch(this.fromBar()); + } + + removeParam(index) { + this.params.splice(index, 1); + this.doSearch(this.fromBar()); + } + + doSearch(filter) { + this.filter = filter; + + let opts = this.$state.is(this.searchState) + ? {location: 'replace'} : null; + this.$state.go(this.searchState, + {q: JSON.stringify(filter)}, opts); + } + + fromBar() { + let filter = {}; + + if (this.searchString) + filter.search = this.searchString; + + if (this.params) { + for (let param of this.params) + filter[param.key] = param.value; + } + + return filter; + } + + toBar(filter) { + this.params = []; + this.searchString = filter && filter.search; + if (!filter) return; + + let keys = Object.keys(filter); + keys.forEach(key => { + if (key == 'search') return; + let value = filter[key]; + let chip; + + if (typeof value == 'string' + && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) + value = new Date(value); + + switch (typeof value) { + case 'boolean': + chip = `${value ? '' : 'not '}${key}`; + break; + case 'number': + case 'string': + chip = `${key}: ${value}`; + break; + default: + if (value instanceof Date) { + let format = 'yyyy-MM-dd'; + if (value.getHours() || value.getMinutes()) + format += ' HH:mm'; + chip = `${key}: ${this.$filter('date')(value, format)}`; + } else + chip = key; + } + + this.params.push({chip, key, value}); + }); + } +} + +ngModule.vnComponent('vnSearchbar', { + controller: Controller, + template: require('./searchbar.html'), + bindings: { + searchState: '@?', + filter: ' this.onStateChange()); + + this.fetchFilter(); + } + + $postLink() { + if (this.filter !== null) + this.doSearch(); + } + + $onDestroy() { + this.deregisterCallback(); + } + + fetchFilter() { + if (this.$state.params.q) { + try { + this.filter = JSON.parse(this.$state.params.q); + } catch (e) { + console.error(e); + } + } else + this.filter = null; + } + + onStateChange() { + this.fetchFilter(); + this.doSearch(); } doSearch() { - this.searchString = this.getStringFromObject(this.shownFilter); - - let filter = this._filter; + let filter = this.filter; if (filter == null && this.autoload) filter = {}; @@ -141,94 +267,17 @@ export default class Controller extends Component { exprBuilder(param, value) { return {[param]: value}; } - - /** - * Finds pattern key:value or key:(extra value) and passes it to object. - * - * @param {String} searchString The search string - * @return {Object} The parsed object - */ - getObjectFromString(searchString) { - let result = {}; - if (searchString) { - let regex = /((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi; - let findPattern = searchString.match(regex); - let remnantString = searchString.replace(regex, '').trim(); - if (findPattern) { - for (let i = 0; i < findPattern.length; i++) { - let aux = findPattern[i].split(':'); - let property = aux[0]; - let value = aux[1].replace(/\(|\)/g, ''); - result[property] = value.trim(); - } - } - if (remnantString) - result.search = remnantString; - } - return result; - } - - /** - * Passes an object to pattern key:value or key:(extra value). - * - * @param {Object} searchObject The search object - * @return {String} The passed string - */ - getStringFromObject(searchObject) { - let search = []; - - if (searchObject) { - let keys = Object.keys(searchObject); - keys.forEach(key => { - if (key == 'search') return; - - let value = searchObject[key]; - let valueString; - - if (typeof value === 'string' && value.indexOf(' ') !== -1) - valueString = `(${value})`; - else if (value instanceof Date) - valueString = value.toJSON(); - else { - switch (typeof value) { - case 'number': - case 'string': - case 'boolean': - valueString = `${value}`; - } - } - - if (valueString) - search.push(`${key}:${valueString}`); - }); - - if (searchObject.search) - search.unshift(searchObject.search); - } - - return search.length ? search.join(' ') : ''; - } - - $onDestroy() { - this.deregisterCallback(); - } } -Controller.$inject = ['$element', '$scope', '$compile', '$state', '$transitions']; +AutoSearch.$inject = ['$state', '$transitions']; -ngModule.component('vnSearchbar', { - template: require('./searchbar.html'), +ngModule.vnComponent('vnAutoSearch', { + controller: AutoSearch, bindings: { - filter: ' { let controller; let $element; let $state; + let $params; let $scope; + let filter = {id: 1, search: 'needle'}; beforeEach(ngModule('vnCore')); - beforeEach(angular.mock.inject(($componentController, _$state_, $rootScope) => { + beforeEach(angular.mock.inject(($componentController, $rootScope, _$state_) => { $scope = $rootScope.$new(); $state = _$state_; - $element = angular.element(``); + $params = $state.params; + + $params.q = JSON.stringify(filter); + + $element = angular.element(`
`); controller = $componentController('vnSearchbar', {$element, $scope}); controller.panel = 'vn-client-search-panel'; })); describe('$postLink()', () => { - it(`should not call onStateChange() if filter is defined`, () => { - spyOn(controller, 'onStateChange'); - controller.filter = {}; - + it(`should fetch the filter from the state if it's in the filter state`, () => { controller.$postLink(); - expect(controller.onStateChange).not.toHaveBeenCalledWith(); + expect(controller.filter).toEqual(filter); + expect(controller.searchString).toBe('needle'); + expect(controller.params.length).toBe(1); }); - it(`should call onStateChange() if filter is null`, () => { - spyOn(controller, 'onStateChange'); - controller.filter = null; - + it(`should not fetch the filter from the state if not in the filter state`, () => { + controller.searchState = 'other.state'; controller.$postLink(); - expect(controller.onStateChange).toHaveBeenCalledWith(); - }); - }); - - describe('onStateChange()', () => { - it(`should set a formated _filter in the controller`, () => { - spyOn(controller, 'doSearch'); - Object.assign($state.params, {q: '{"id": 999}'}); - - controller.onStateChange(); - - expect(controller._filter).toEqual({id: 999}); - expect(controller.doSearch).toHaveBeenCalledWith(); - }); - }); - - describe('shownFilter() getter', () => { - it(`should return the _filter if not NULL`, () => { - controller.filter = '{"id": 999}'; - - let shownFilter = controller.shownFilter; - - expect(shownFilter).toEqual(controller._filter); - expect(shownFilter).not.toEqual(controller.suggestedFilter); - }); - - it(`should return the suggested filter if filter is NULL`, () => { - controller.suggestedFilter = '{"id": 888}'; - - let shownFilter = controller.shownFilter; - - expect(shownFilter).not.toEqual(controller._filter); - expect(shownFilter).toEqual(controller.suggestedFilter); + expect(controller.filter).toBeNull(); + expect(controller.searchString).toBeNull(); + expect(controller.params.length).toBe(0); }); }); describe('filter() setter', () => { - it(`should call $state.go() to replace the current state location instead of creating a new one`, () => { - controller._filter = {}; - spyOn($state, 'go'); - controller.filter = {expected: 'filter'}; + it(`should update the bar params and search`, () => { + let withoutHours = new Date(2000, 1, 1); + let withHours = new Date(withoutHours.getTime()); + withHours.setHours(12, 30, 15, 10); - expect(controller._filter).toEqual(controller.filter); - expect($state.go).toHaveBeenCalledWith('.', Object({q: '{"expected":"filter"}'}), Object({location: 'replace'})); + controller.filter = { + search: 'needle', + withHours: withHours.toJSON(), + withoutHours: withoutHours.toJSON(), + boolean: true, + negated: false, + myObjectProp: {myProp: 1} + }; + + let chips = {}; + for (let param of controller.params || []) + chips[param.key] = param.chip; + + expect(controller.searchString).toBe('needle'); + expect(chips.withHours).toBe('withHours: 2000-02-01 12:30'); + expect(chips.withoutHours).toBe('withoutHours: 2000-02-01'); + expect(chips.boolean).toBe('boolean'); + expect(chips.negated).toBe('not negated'); + expect(chips.myObjectProp).toBe('myObjectProp'); + }); + }); + + describe('shownFilter() getter', () => { + it(`should return the _filter if not null`, () => { + controller.filter = filter; + controller.suggestedFilter = {sugestedParam: 'suggestedValue'}; + + expect(controller.shownFilter).toEqual(filter); + }); + + it(`should return the suggested filter if filter is null`, () => { + controller.filter = null; + controller.suggestedFilter = {sugestedParam: 'suggestedValue'}; + + expect(controller.shownFilter).toEqual(controller.suggestedFilter); + }); + }); + + describe('searchString() setter', () => { + it(`should clear the whole filter when it's null`, () => { + controller.filter = filter; + controller.searchString = null; + + expect(controller.searchString).toBeNull(); + expect(controller.params.length).toBe(0); }); }); describe('onPanelSubmit()', () => { - it(`should hide the popover, and set the filter as it wasn't defined`, () => { + it(`should compact and define the filter`, () => { controller.$.popover = {hide: jasmine.createSpy('hide')}; - const filter = undefined; + const filter = { + id: 1, + thisKeyShouldBePurged: null, + alsoThis: [], + andThis: {emptyProp: undefined, nullProp: null}, + dontForgetThis: [null, undefined], + myObject: {keepThis: true, butNotThis: null}, + myArray: [null, undefined, true] + }; controller.onPanelSubmit(filter); - expect(controller.filter).toMatchSnapshot(); - }); - - it(`should hide the popover, compact the filter`, () => { - controller.$.popover = {hide: jasmine.createSpy('hide')}; - const filter = {id: 999, thisKeyShouldBePurged: null}; - - controller.onPanelSubmit(filter); - - expect(controller.filter).toMatchSnapshot(); + expect(controller.filter).toEqual({ + id: 1, + myObject: {keepThis: true}, + myArray: [true] + }); }); }); describe('onSubmit()', () => { - it(`should define the controller's filter`, () => { - controller.searchString = 'id: 999'; + it(`should define the filter`, () => { + controller.filter = filter; + controller.searchString = 'mySearch'; controller.onSubmit(); - expect(controller.filter).toMatchSnapshot(); + expect(controller.filter).toEqual({id: 1, search: 'mySearch'}); + }); + }); + + describe('removeParam()', () => { + it(`should remove the parameter from the filter`, () => { + controller.filter = filter; + controller.removeParam(0); + + expect(controller.filter).toEqual({search: 'needle'}); }); }); describe('doSearch()', () => { - it(`should define searchString, call onSearch and the model applyFilter method`, () => { - controller._filter = 'id: 123456'; - controller.autoload = true; - controller.onSearch = jasmine.createSpy('onSearch'); - controller.model = {applyFilter: jasmine.createSpy('applyFilter')}; - controller.paramBuilder = jasmine.createSpy('paramBuilder').and.returnValue({'param': `${controller._filter}`}); + it(`should go to the search state and pass the filter as query param`, () => { + spyOn($state, 'go'); + controller.searchState = 'search.state'; + controller.doSearch(filter); + let queryParams = {q: JSON.stringify(filter)}; - controller.doSearch(); - - expect(controller.searchString).toMatchSnapshot(); - expect(controller.onSearch).toHaveBeenCalledWith({'$params': `${controller._filter}`}); - expect(controller.model.applyFilter).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Object)); - }); - - it(`should define searchString, call onSearch and the model clear method`, () => { - controller._filter = null; - controller.autoload = false; - controller.onSearch = jasmine.createSpy('onSearch'); - controller.model = {clear: jasmine.createSpy('clear')}; - controller.paramBuilder = jasmine.createSpy('paramBuilder').and.returnValue({'param': `${controller._filter}`}); - - - controller.doSearch(); - - expect(controller.searchString).toEqual(''); - expect(controller.onSearch).toHaveBeenCalledWith({'$params': null}); - expect(controller.model.clear).toHaveBeenCalledWith(); - }); - }); - - describe('getObjectFromString()', () => { - it(`should return a formated object based on the string received for basic search`, () => { - let result = controller.getObjectFromString('Bruce Wayne'); - - expect(result).toEqual({search: 'Bruce Wayne'}); - }); - - it(`should return a formated object based on the string received for advanced search`, () => { - let result = controller.getObjectFromString('id:101 name:(Bruce Wayne)'); - - expect(result).toEqual({id: '101', name: 'Bruce Wayne'}); - }); - - it(`should format the object grouping any unmatched part of the instring of the string to the search property`, () => { - let string = 'I am the search id:101 name:(Bruce Wayne) concatenated value'; - let result = controller.getObjectFromString(string); - - expect(result).toEqual({ - id: '101', - name: 'Bruce Wayne', - search: 'I am the search concatenated value' - }); + expect($state.go).toHaveBeenCalledWith('search.state', queryParams, null); + expect(controller.filter).toEqual(filter); }); }); }); diff --git a/front/core/components/searchbar/style.scss b/front/core/components/searchbar/style.scss index 03cc0cd8d..31907792c 100644 --- a/front/core/components/searchbar/style.scss +++ b/front/core/components/searchbar/style.scss @@ -2,7 +2,40 @@ vn-searchbar { display: block; - width: 100%; + max-width: 35em; + margin: 0 auto; + + .search-params { + flex: 1; + margin: .05em 0; + overflow: visible; + display: flex; + max-width: 24em; + + & > .search-param { + color: rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, .1); + padding: .1em .4em; + margin-left: .2em; + display: inline-block; + border-radius: .8em; + max-width: 12em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + & > vn-icon { + font-size: inherit; + vertical-align: middle; + cursor: pointer; + border-radius: 50%; + + &:hover { + color: rgba(0, 0, 0, .8); + } + } + } + } } .search-panel { diff --git a/front/core/components/slot/index.js b/front/core/components/slot/index.js new file mode 100644 index 000000000..1747ed93f --- /dev/null +++ b/front/core/components/slot/index.js @@ -0,0 +1,81 @@ +import ngModule from '../../module'; +import './portal'; +import './style.scss'; + +export class Slot { + constructor($element, vnSlotService) { + this.$element = $element; + this.vnSlotService = vnSlotService; + this.$content = null; + } + + $onDestroy() { + this.unregister(); + } + + set name(value) { + this.unregister(); + this._name = value; + this.vnSlotService.slots[value] = this; + } + + get name() { + return this._name; + } + + unregister() { + if (this.name) + this.vnSlotService.slots[this.name] = undefined; + } + + setContent($content) { + if (this.$content) { + this.$content.detach(); + this.$content = null; + } + + this.$content = $content; + if (this.$content) this.$element.append($content); + this.$element[0].style.display = $content ? 'block' : 'none'; + } +} +Slot.$inject = ['$element', 'vnSlotService']; + +ngModule.vnComponent('vnSlot', { + controller: Slot, + bindings: { + name: '@?' + } +}); + +export class SlotService { + constructor() { + this.stacks = {}; + this.slots = {}; + } + + push(slot, $transclude) { + if (!this.stacks[slot]) this.stacks[slot] = []; + this.stacks[slot].unshift($transclude); + this.refreshContent(slot); + } + + pop(slot) { + let $content = this.stacks[slot].shift(); + this.refreshContent(slot); + if ($content && typeof $content == 'object') + $content.remove(); + } + + refreshContent(slot) { + if (!this.slots[slot]) return; + let $content = this.stacks[slot][0]; + if (typeof $content == 'function') { + $content(clone => { + $content = this.stacks[slot][0] = clone; + }); + } + this.slots[slot].setContent($content); + } +} +ngModule.service('vnSlotService', SlotService); diff --git a/front/core/components/slot/portal.js b/front/core/components/slot/portal.js new file mode 100644 index 000000000..f4dbecb09 --- /dev/null +++ b/front/core/components/slot/portal.js @@ -0,0 +1,28 @@ +import ngModule from '../../module'; + +/** + * Component used to fill slots with content. + */ +export default class Portal { + constructor($transclude, vnSlotService) { + this.$transclude = $transclude; + this.vnSlotService = vnSlotService; + } + + $postLink() { + this.vnSlotService.push(this.slot, this.$transclude); + } + + $onDestroy() { + this.vnSlotService.pop(this.slot); + } +} +Portal.$inject = ['$transclude', 'vnSlotService']; + +ngModule.component('vnPortal', { + controller: Portal, + transclude: true, + bindings: { + slot: '@' + } +}); diff --git a/front/core/components/slot/style.scss b/front/core/components/slot/style.scss new file mode 100644 index 000000000..825aad085 --- /dev/null +++ b/front/core/components/slot/style.scss @@ -0,0 +1,3 @@ +vn-slot { + display: block; +} \ No newline at end of file diff --git a/front/core/components/spinner/spinner.html b/front/core/components/spinner/spinner.html index b6a3af8be..440e47445 100644 --- a/front/core/components/spinner/spinner.html +++ b/front/core/components/spinner/spinner.html @@ -6,6 +6,7 @@ cy="50" r="20" fill="none" + stroke="currentColor" stroke-miterlimit="10"> diff --git a/front/core/components/spinner/style.scss b/front/core/components/spinner/style.scss index 3be908caf..eb39f64e8 100644 --- a/front/core/components/spinner/style.scss +++ b/front/core/components/spinner/style.scss @@ -4,6 +4,7 @@ vn-spinner { display: inline-block; min-height: 28px; min-width: 28px; + color: $color-main; & > .loader { position: relative; @@ -29,7 +30,6 @@ vn-spinner { margin: auto; & > .path { - stroke: $color-main; stroke-dasharray: 1, 200; stroke-dashoffset: 0; stroke-linecap: square; diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss index dc3c754bc..41d1f6db8 100644 --- a/front/core/components/table/style.scss +++ b/front/core/components/table/style.scss @@ -3,8 +3,8 @@ vn-table { display: block; - overflow: auto; width: 100%; + overflow: auto; } .vn-table { width: 100%; diff --git a/front/core/components/toggle/style.scss b/front/core/components/toggle/style.scss index af3dc3ae5..31769d2a5 100644 --- a/front/core/components/toggle/style.scss +++ b/front/core/components/toggle/style.scss @@ -40,10 +40,10 @@ background-color: rgba(0, 0, 0, .1); } &.checked > .btn { - border-color: $color-main; + border-color: $color-button; & > .focus-mark { - background-color: rgba($color-main, .15); + background-color: rgba($color-button, .15); } } &.disabled { diff --git a/front/core/components/wday-picker/style.scss b/front/core/components/wday-picker/style.scss index c6899ad6a..be610c733 100644 --- a/front/core/components/wday-picker/style.scss +++ b/front/core/components/wday-picker/style.scss @@ -20,7 +20,7 @@ background-color: rgba(0, 0, 0, .05); &.marked { - background: $color-main; + background: $color-button; color: $color-font-dark; } } diff --git a/front/core/directives/id.js b/front/core/directives/id.js index 693606b0c..8230527d6 100644 --- a/front/core/directives/id.js +++ b/front/core/directives/id.js @@ -12,14 +12,23 @@ export function directive() { restrict: 'A', link: function($scope, $element, $attrs) { let id = kebabToCamel($attrs.vnId); - let $ctrl = $element[0].$ctrl - ? $element[0].$ctrl - : $element.controller($element[0].tagName.toLowerCase()); if (!id) throw new Error(`vnId: Attribute can't be null`); - $scope[id] = $ctrl || $element[0]; + let $ctrl = $element[0].$ctrl + ? $element[0].$ctrl + : $element.controller($element[0].tagName.toLowerCase()); + let ctrl = $ctrl || $element[0]; + + $scope[id] = ctrl; + + if (!$scope.hasOwnProperty('$ctrl')) { + while ($scope && !$scope.hasOwnProperty('$ctrl')) + $scope = Object.getPrototypeOf($scope); + + if ($scope) $scope[id] = ctrl; + } } }; } diff --git a/front/core/lib/component.js b/front/core/lib/component.js index 979420b22..b9f04dba6 100644 --- a/front/core/lib/component.js +++ b/front/core/lib/component.js @@ -107,6 +107,7 @@ function runFn( $compile, $filter, $interpolate, + $window, vnApp) { Object.assign(Component.prototype, { $translate, @@ -119,6 +120,7 @@ function runFn( $compile, $filter, $interpolate, + $window, vnApp }); } @@ -133,6 +135,7 @@ runFn.$inject = [ '$compile', '$filter', '$interpolate', + '$window', 'vnApp' ]; diff --git a/front/core/lib/focus.js b/front/core/lib/focus.js new file mode 100644 index 000000000..8b997d920 --- /dev/null +++ b/front/core/lib/focus.js @@ -0,0 +1,16 @@ + +import isMobile from './is-mobile'; + +export default function focus(element) { + if (isMobile) return; + setTimeout(() => element.focus(), 10); +} + +export function select(element) { + if (isMobile) return; + setTimeout(() => { + element.focus(); + if (element.select) + element.select(); + }, 10); +} diff --git a/front/core/lib/index.js b/front/core/lib/index.js index 2682dfdf9..4d562ca05 100644 --- a/front/core/lib/index.js +++ b/front/core/lib/index.js @@ -2,8 +2,9 @@ import './module-loader'; import './crud'; import './copy'; import './equals'; +import './focus'; +import './get-main-route'; import './modified'; import './key-codes'; import './http-error'; import './user-error'; -import './get-main-route'; diff --git a/front/core/lib/specs/acl-service.spec.js b/front/core/lib/specs/acl-service.spec.js index 7756347b0..3a1460241 100644 --- a/front/core/lib/specs/acl-service.spec.js +++ b/front/core/lib/specs/acl-service.spec.js @@ -39,7 +39,7 @@ describe('Service acl', () => { expect(hasAny).toBeTruthy(); }); - it('should return true when user has not any of the passed roles', () => { + it('should return false when user has not any of the passed roles', () => { let hasAny = aclService.hasAny(['inventedRole', 'nonExistent']); expect(hasAny).toBeFalsy(); diff --git a/front/core/module.js b/front/core/module.js index 33eb68c24..0c52fd565 100644 --- a/front/core/module.js +++ b/front/core/module.js @@ -1,9 +1,6 @@ import {ng, ngDeps} from './vendor'; import {camelToKebab} from './lib/string'; -const ngModule = ng.module('vnCore', ngDeps); -export default ngModule; - /** * Acts like native Module.component() function but merging component options * with parent component options. This method establishes the $options property @@ -17,7 +14,7 @@ export default ngModule; * @param {Object} options The component options * @return {angularModule} The same angular module */ -ngModule.vnComponent = function(name, options) { +function vnComponent(name, options) { let controller = options.controller; let parent = Object.getPrototypeOf(controller); let parentOptions = parent.$options || {}; @@ -57,10 +54,21 @@ ngModule.vnComponent = function(name, options) { controller.$classNames = classNames; return this.component(name, mergedOptions); +} + +const ngModuleFn = ng.module; + +ng.module = function(...args) { + let ngModule = ngModuleFn.apply(this, args); + ngModule.vnComponent = vnComponent; + return ngModule; }; -config.$inject = ['$translateProvider', '$translatePartialLoaderProvider']; -export function config($translateProvider, $translatePartialLoaderProvider) { +const ngModule = ng.module('vnCore', ngDeps); +export default ngModule; + +config.$inject = ['$translateProvider', '$translatePartialLoaderProvider', '$animateProvider']; +export function config($translateProvider, $translatePartialLoaderProvider, $animateProvider) { // For CSS browser targeting document.documentElement.setAttribute('data-browser', navigator.userAgent); @@ -91,5 +99,8 @@ export function config($translateProvider, $translatePartialLoaderProvider) { return langAliases[locale]; return fallbackLang; }); + + $animateProvider.customFilter( + node => node.tagName == 'UI-VIEW'); } ngModule.config(config); diff --git a/front/core/services/interceptor.js b/front/core/services/interceptor.js index 448b70a34..b7cf3a0ec 100644 --- a/front/core/services/interceptor.js +++ b/front/core/services/interceptor.js @@ -13,11 +13,15 @@ function interceptor($q, vnApp, vnToken, $translate) { vnApp.pushLoader(); if (config.url.charAt(0) !== '/' && apiPath) - config.url = `${apiPath}/${config.url}`; + config.url = `${apiPath}${config.url}`; if (vnToken.token) config.headers.Authorization = vnToken.token; if ($translate.use()) config.headers['Accept-Language'] = $translate.use(); + if (config.filter) { + if (!config.params) config.params = {}; + config.params.filter = config.filter; + } return config; }, diff --git a/front/core/styles/animations.scss b/front/core/styles/animations.scss new file mode 100644 index 000000000..039f79c77 --- /dev/null +++ b/front/core/styles/animations.scss @@ -0,0 +1,30 @@ + +@keyframes nothing {} +@keyframes slideIn { + from { + transform: translate3d(-2em, 0, 0); + opacity: 0; + } + to { + transform: translate3d(0, 0, 0); + opacity: 1; + } +} +@keyframes amplify { + from { + opacity: 0; + transform: scale3d(0, 0, 0); + } + to { + opacity: 1; + transform: scale3d(1, 1, 1); + } +} +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} \ No newline at end of file diff --git a/front/core/styles/global.scss b/front/core/styles/global.scss index ff49516dc..8b3d465f1 100644 --- a/front/core/styles/global.scss +++ b/front/core/styles/global.scss @@ -40,4 +40,8 @@ button { a { color: $color-font-link; text-decoration: none; +} +.ng-leave, +.ng-enter { + transition: none; } \ No newline at end of file diff --git a/front/core/styles/index.js b/front/core/styles/index.js index 1cdc4c7e1..dd35b7bfd 100644 --- a/front/core/styles/index.js +++ b/front/core/styles/index.js @@ -1,3 +1,4 @@ +import './animations.scss'; import './background.scss'; import './border.scss'; import './font-family.scss'; diff --git a/front/core/styles/variables.scss b/front/core/styles/variables.scss index 454bc6f7c..89e487ad7 100644 --- a/front/core/styles/variables.scss +++ b/front/core/styles/variables.scss @@ -19,27 +19,36 @@ $spacing-xs: 4px; $spacing-sm: 8px; $spacing-md: 16px; $spacing-lg: 32px; -$spacing-xl: 100px; +$spacing-xl: 70px; // Light theme -$color-header: #3d3d3d; -$color-bg: #e5e5e5; -$color-bg-dark: #3d3d3d; +$color-primary: #f7931e; +$color-secondary: $color-primary; + $color-font: #222; $color-font-light: #555; $color-font-secondary: #9b9b9b; $color-font-dark: white; -$color-font-bg: rgba(0, 0, 0, .7); $color-font-link: #005a9a; +$color-font-bg: rgba(0, 0, 0, .7); +$color-font-bg-marginal: rgba(0, 0, 0, .4); +$color-font-bg-dark: rgba(255, 255, 255, .7); +$color-font-bg-dark-marginal: rgba(255, 255, 255, .4); + +$color-header: #3d3d3d; +$color-menu-header: #3d3d3d; +$color-bg: #e5e5e5; +$color-bg-dark: #3d3d3d; $color-active: #3d3d3d; -$color-active-font: white; +$color-active-font: $color-font-dark; $color-bg-panel: white; -$color-main: #f7931e; -$color-secondary: #ccc; +$color-main: $color-primary; +$color-marginal: #ccc; $color-success: #a3d131; $color-notice: #32b1ce; $color-alert: #f42121; +$color-button: $color-secondary; $color-spacer: rgba(0, 0, 0, .3); $color-spacer-light: rgba(0, 0, 0, .12); @@ -78,7 +87,7 @@ $color-active: #666; $color-active-font: white; $color-bg-panel: #3c3b3b; $color-main: #f7931e; -$color-secondary: #ccc; +$color-marginal: #ccc; $color-success: #a3d131; $color-notice: #32b1ce; $color-alert: #f42121; diff --git a/front/core/vendor.js b/front/core/vendor.js index 0cdae4e81..8cec57f4f 100644 --- a/front/core/vendor.js +++ b/front/core/vendor.js @@ -2,6 +2,7 @@ import '@babel/polyfill'; import * as ng from 'angular'; export {ng}; +import 'angular-animate'; import 'angular-translate'; import 'angular-translate-loader-partial'; import '@uirouter/angularjs'; @@ -9,6 +10,7 @@ import 'mg-crud'; import 'oclazyload'; export const ngDeps = [ + 'ngAnimate', 'pascalprecht.translate', 'ui.router', 'mgCrud', diff --git a/front/package-lock.json b/front/package-lock.json index 9d71d20cc..f546f6c65 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -31,6 +31,11 @@ "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz", "integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw==" }, + "angular-animate": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.8.tgz", + "integrity": "sha512-bINtzizq7TbJzfVrDpwLfTxVl0Qd7fRNWFb5jKYI1vaFZobQNX/QONXlLow6ySsDbZ6eLECycB7mvWtfh1YYaw==" + }, "angular-translate": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz", diff --git a/front/package.json b/front/package.json index e17dbd08c..1eb3a8b8e 100644 --- a/front/package.json +++ b/front/package.json @@ -12,6 +12,7 @@ "@babel/polyfill": "^7.2.5", "@uirouter/angularjs": "^1.0.20", "angular": "^1.7.5", + "angular-animate": "^1.7.8", "angular-translate": "^2.18.1", "angular-translate-loader-partial": "^2.18.1", "js-yaml": "^3.13.1", diff --git a/front/salix/components/app/app.html b/front/salix/components/app/app.html index d6ac05d7e..5432609c2 100644 --- a/front/salix/components/app/app.html +++ b/front/salix/components/app/app.html @@ -1,29 +1,9 @@ - - - - -
- {{$ctrl.$state.current.description}} -
- - - - -
-
- -
+ + + + \ No newline at end of file diff --git a/front/salix/components/app/app.js b/front/salix/components/app/app.js index edf482712..1f8cdb46e 100644 --- a/front/salix/components/app/app.js +++ b/front/salix/components/app/app.js @@ -1,5 +1,6 @@ import ngModule from '../../module'; import './style.scss'; +import Component from 'core/lib/component'; /** * The main graphical application component. @@ -7,28 +8,21 @@ import './style.scss'; * @property {SideMenu} leftMenu The left menu, if it's present * @property {SideMenu} rightMenu The left menu, if it's present */ -export default class App { - constructor($, $state, vnApp) { - Object.assign(this, { - $, - $state, - vnApp - }); - } - +export default class App extends Component { $postLink() { this.vnApp.logger = this; } - $onDestroy() { - this.vnApp.logger = null; - } - - get showTopbar() { + get showLayout() { let state = this.$state.current.name; return state && state != 'login'; } + $onDestroy() { + this.deregisterCallback(); + this.vnApp.logger = null; + } + showMessage(message) { this.$.snackbar.show({message: message}); } @@ -41,9 +35,8 @@ export default class App { this.$.snackbar.showError({message: message}); } } -App.$inject = ['$scope', '$state', 'vnApp']; -ngModule.component('vnApp', { +ngModule.vnComponent('vnApp', { template: require('./app.html'), controller: App }); diff --git a/front/salix/components/app/style.scss b/front/salix/components/app/style.scss index 530524773..aab37c8a3 100644 --- a/front/salix/components/app/style.scss +++ b/front/salix/components/app/style.scss @@ -1,99 +1,17 @@ @import "variables"; vn-app { - height: inherit; display: block; + height: inherit; - & > vn-topbar { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 10; - box-shadow: 0 .1em .2em $color-shadow; - height: $topbar-height; - padding: .4em; - - & > header { - & > * { - padding: .3em; - } - & > .logo > img { - height: 1.4em; - display: block; - } - & > .show-menu { - display: none; - font-size: 1.8em; - cursor: pointer; - - &:hover { - color: $color-main; - } - } - & > .main-title { - font-size: 1.6em; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - padding-left: .6em; - } - & > vn-spinner { - padding: 0 .4em; - } - & > vn-main-menu { - flex: 1; - } - } - } - & > .main-view { + ui-view { + display: block; box-sizing: border-box; height: inherit; - - &.padding { - padding-top: $topbar-height; - } - .content-block { - box-sizing: border-box; - padding: $spacing-md; - height: inherit; - } - vn-main-block { - display: block; - margin: 0 auto; - padding-left: $menu-width; - height: 100% - - } - .main-with-right-menu { - padding-right: $menu-width; - - @media screen and (max-width: 800px) { - padding-right: 0; - } - } - } - @media screen and (max-width: $mobile-width) { - & > vn-topbar > header { - & > .logo { - display: none; - } - & > .show-menu { - display: block; - } - } - & > .main-view { - .content-block { - margin-left: 0; - margin-right: 0; - } - vn-main-block { - padding-left: 0; - } - .main-with-right-menu { - padding-right: 0; - } + &.ng-enter { + animation-name: nothing, slideIn; + animation-duration: 400ms, 200ms; } } } diff --git a/front/salix/components/descriptor/style.scss b/front/salix/components/descriptor/style.scss index 16bc23b28..af719cc0d 100644 --- a/front/salix/components/descriptor/style.scss +++ b/front/salix/components/descriptor/style.scss @@ -50,7 +50,7 @@ & > vn-icon { padding: $spacing-sm; - color: $color-secondary; + color: $color-marginal; font-size: 1.5em; &.bright { diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html index 47307d22e..b3fc02d0d 100644 --- a/front/salix/components/home/home.html +++ b/front/salix/components/home/home.html @@ -1,4 +1,4 @@ -
+
{ let $httpBackend; @@ -14,8 +14,7 @@ describe('Component vnMainMenu', () => { describe('getCurrentUserName()', () => { it(`should set the user name property in the controller`, () => { - $httpBackend.when('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'}); - $httpBackend.expect('GET', `Accounts/getCurrentUserData`); + $httpBackend.expect('GET', `Accounts/getUserData`).respond({name: 'batman'}); controller.getCurrentUserName(); $httpBackend.flush(); diff --git a/front/salix/components/layout/index.html b/front/salix/components/layout/index.html new file mode 100644 index 000000000..0ad400114 --- /dev/null +++ b/front/salix/components/layout/index.html @@ -0,0 +1,77 @@ + + + +
+ +
+ {{$ctrl.$state.current.description}} +
+ + +
+ +
+ + + + + + +
+ +
    +
  • + + Home +
  • +
  • + + {{::mod.name}} +
  • +
+
+ + + + + + + + + + + + + + {{::mod.name}} + + + + + \ No newline at end of file diff --git a/front/salix/components/layout/index.js b/front/salix/components/layout/index.js new file mode 100644 index 000000000..883b010ba --- /dev/null +++ b/front/salix/components/layout/index.js @@ -0,0 +1,30 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export class Layout extends Component { + constructor($element, $, vnModules) { + super($element, $); + this.modules = vnModules.get(); + } + + $onInit() { + this.getUserData(); + } + + getUserData() { + this.$http.get('Accounts/getCurrentUserData').then(json => { + this.$.$root.user = json.data; + window.localStorage.currentUserWorkerId = json.data.workerId; + }); + } +} +Layout.$inject = ['$element', '$scope', 'vnModules']; + +ngModule.component('vnLayout', { + template: require('./index.html'), + controller: Layout, + require: { + app: '^vnApp' + } +}); diff --git a/front/salix/components/layout/index.spec.js b/front/salix/components/layout/index.spec.js new file mode 100644 index 000000000..7163acb65 --- /dev/null +++ b/front/salix/components/layout/index.spec.js @@ -0,0 +1,25 @@ +import './index.js'; + +describe('Component vnLayout', () => { + let $httpBackend; + let controller; + + beforeEach(ngModule('salix')); + + beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => { + let vnModules = {get: () => {}}; + $httpBackend = _$httpBackend_; + let $element = angular.element('
'); + controller = $componentController('vnLayout', {$element, vnModules}); + })); + + describe('getUserData()', () => { + it(`should set the user name property in the controller`, () => { + $httpBackend.expect('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'}); + controller.getUserData(); + $httpBackend.flush(); + + expect(controller.$.$root.user.name).toEqual('batman'); + }); + }); +}); diff --git a/front/salix/components/app/logo.svg b/front/salix/components/layout/logo-text.svg similarity index 100% rename from front/salix/components/app/logo.svg rename to front/salix/components/layout/logo-text.svg diff --git a/front/salix/components/layout/logo.svg b/front/salix/components/layout/logo.svg new file mode 100644 index 000000000..1a111f2f1 --- /dev/null +++ b/front/salix/components/layout/logo.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/front/salix/components/layout/style.scss b/front/salix/components/layout/style.scss new file mode 100644 index 000000000..9a9d903a6 --- /dev/null +++ b/front/salix/components/layout/style.scss @@ -0,0 +1,159 @@ +@import "effects"; + +vn-layout { + & > vn-topbar { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 10; + box-shadow: 0 .1em .2em $color-shadow; + height: $topbar-height; + padding: 0 1em; + justify-content: space-between; + + & > .side { + flex: auto; + display: flex; + align-items: center; + width: 5em; + transition: width 200ms; + } + & > .start { + padding-right: 1em; + overflow: hidden; + + & > .logo > img { + height: 2em; + display: block; + } + & > .main-title { + font-size: 1.6em; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding-left: .4em; + } + & > vn-spinner { + padding: 0 .4em; + } + } + & > vn-slot { + flex: auto; + } + & > .end { + padding-left: 1em; + justify-content: flex-end; + } + .show-menu { + display: none; + } + .vn-button { + color: inherit; + font-size: 1.05em; + padding: 0; + } + } + & > vn-side-menu > .menu { + display: flex; + flex-direction: column; + align-items: stretch; + + & > vn-slot { + flex: 1; + overflow: auto; + } + } + &.left-menu { + & > vn-topbar > .start { + width: 5em + $menu-width; + } + & > .main-view { + padding-left: $menu-width; + } + } + &.right-menu { + & > vn-topbar > .end { + width: 5em + $menu-width; + } + & > .main-view { + padding-right: $menu-width; + } + } + & > .main-view { + padding-top: $topbar-height; + } + ui-view { + & > * { + display: block; + padding: $spacing-md; + } + &.ng-enter { + vn-side-menu { + opacity: 0; + animation: fadeIn 200ms ease-out 200ms; + } + [fixed-bottom-right] { + opacity: 0; + animation: amplify 200ms ease-out 200ms; + } + } + } + @media screen and (max-width: $mobile-width) { + & > vn-topbar { + & > .start > .logo { + display: none; + } + & > .side { + flex: 1; + } + .show-menu { + display: block; + } + } + &.left-menu { + & > vn-topbar { + left: 0; + } + & > .main-view { + padding-left: 0; + } + } + &.right-menu { + & > .main-view { + padding-right: 0; + } + } + ui-view > * { + padding-left: 0; + padding-right: 0; + } + } +} +.vn-popover .modules-menu { + list-style-type: none; + margin: 0; + color: $color-font-dark; + + & > li { + @extend %clickable-light; + background-color: $color-main; + margin-bottom: .6em; + padding: .8em; + border-radius: .1em; + min-width: 8em; + white-space: nowrap; + + &:last-child { + margin-bottom: 0; + } + & > vn-icon { + padding-right: .3em; + vertical-align: middle; + } + } +} +#user { + font-size: 1.5em; + height: auto; +} diff --git a/front/salix/components/left-menu/left-menu.html b/front/salix/components/left-menu/left-menu.html index 12cea49a3..73488a24b 100644 --- a/front/salix/components/left-menu/left-menu.html +++ b/front/salix/components/left-menu/left-menu.html @@ -1,17 +1,30 @@ -
    +
    • - - - {{::item.description}} + + + + + {{::item.description}} + + + + -
        + diff --git a/front/salix/components/left-menu/left-menu.js b/front/salix/components/left-menu/left-menu.js index 576dcc2df..92adb02aa 100644 --- a/front/salix/components/left-menu/left-menu.js +++ b/front/salix/components/left-menu/left-menu.js @@ -6,24 +6,42 @@ export default class LeftMenu { this.$element = $element; this.$timeout = $timeout; this.$state = $state; + this.aclService = aclService; this.deregisterCallback = $transitions.onSuccess({}, () => this.activateItem()); + this.source = 'main'; this._depth = 3; + } + $onInit() { + this.items = this.fetchItems(); + this.activateItem(); + } + + set depth(value) { + this._depth = value; + this.activateItem(); + } + + get depth() { + return this._depth; + } + + fetchItems() { let states = this.$state.router.stateRegistry.states; let moduleIndex = this.$state.current.data.moduleIndex; let moduleFile = window.routes[moduleIndex] || []; - let menu = moduleFile.menu || []; + let menu = moduleFile.menus && moduleFile.menus[this.source] || []; let items = []; - function addItem(items, item) { + let addItem = (items, item) => { let state = states[item.state]; if (!state) return; state = state.self; let acl = state.data.acl; - if (acl && !aclService.hasAny(acl)) + if (acl && !this.aclService.hasAny(acl)) return; items.push({ @@ -31,7 +49,7 @@ export default class LeftMenu { description: state.description, state: item.state }); - } + }; for (let item of menu) { if (item.state) @@ -52,17 +70,7 @@ export default class LeftMenu { } } - this.items = items; - this.activateItem(); - } - - set depth(value) { - this._depth = value; - this.activateItem(); - } - - get depth() { - return this._depth; + return items; } activateItem() { @@ -93,9 +101,8 @@ export default class LeftMenu { item.active = !item.active; this.$timeout(() => { - let element = this.$element[0].querySelector('a[class="expanded"]'); - if (element) - element.scrollIntoView(); + let element = this.$element[0].querySelector('a.expanded'); + if (element) element.scrollIntoView(); }); } @@ -109,6 +116,7 @@ ngModule.component('vnLeftMenu', { template: require('./left-menu.html'), controller: LeftMenu, bindings: { + source: '@?', depth: ' .vn-list { + padding: $spacing-md 0; - & > li { - list-style: none; - display: block; - - & > ul > li > a { - padding-left: 2em + & > li > .vn-item { + & > [side] > vn-icon[icon="keyboard_arrow_down"] { + transition: transform 200ms; } - } - - & > li > a { - @extend %clickable; - padding: .5em 1em; - display: block; - color: inherit; - - & > vn-icon:nth-child(1) { - vertical-align: middle; - margin-right: .4em - } - - & > vn-icon:nth-child(2) { - float: right; - margin-left: .4em - } - } - - & > li > a.active { - @extend %active; - } - - & > li > a.expanded { - & > vn-icon[icon="keyboard_arrow_down"] { - transition: all 0.2s; + &.expanded > [side] > vn-icon[icon="keyboard_arrow_down"] { transform: rotate(180deg); } } - - & > li > a.collapsed { - & > vn-icon[icon="keyboard_arrow_down"] { - transition: all 0.2s; - transform: rotate(0deg); - } - } } } diff --git a/front/salix/components/login/login.js b/front/salix/components/login/login.js index 401d4a4d9..efb5e02bb 100644 --- a/front/salix/components/login/login.js +++ b/front/salix/components/login/login.js @@ -14,6 +14,7 @@ export default class Controller { remember: true }); } + submit() { this.loading = true; this.vnAuth.login(this.user, this.password, this.remember) @@ -28,6 +29,7 @@ export default class Controller { throw err; }); } + focusUser() { this.$.userField.select(); this.$.userField.focus(); diff --git a/front/salix/components/main-menu/main-menu.html b/front/salix/components/main-menu/main-menu.html deleted file mode 100644 index c583ffe0c..000000000 --- a/front/salix/components/main-menu/main-menu.html +++ /dev/null @@ -1,31 +0,0 @@ -
        -
        - {{$root.user.nickname}} -
        - - - - -
        - -
          -
        • - - {{::mod.name}} -
        • -
        -
        - - \ No newline at end of file diff --git a/front/salix/components/main-menu/main-menu.js b/front/salix/components/main-menu/main-menu.js deleted file mode 100644 index 93ff955e7..000000000 --- a/front/salix/components/main-menu/main-menu.js +++ /dev/null @@ -1,30 +0,0 @@ -import ngModule from '../../module'; -import './style.scss'; - -export default class MainMenu { - constructor($, $http, vnAuth, vnModules) { - Object.assign(this, { - $, - $http, - vnAuth, - modules: vnModules.get() - }); - } - - $onInit() { - this.getCurrentUserName(); - } - - getCurrentUserName() { - this.$http.get('Accounts/getCurrentUserData').then(json => { - this.$.$root.user = json.data; - window.localStorage.currentUserWorkerId = json.data.workerId; - }); - } -} -MainMenu.$inject = ['$scope', '$http', 'vnAuth', 'vnModules']; - -ngModule.component('vnMainMenu', { - template: require('./main-menu.html'), - controller: MainMenu -}); diff --git a/front/salix/components/main-menu/style.scss b/front/salix/components/main-menu/style.scss deleted file mode 100644 index cd38472ab..000000000 --- a/front/salix/components/main-menu/style.scss +++ /dev/null @@ -1,61 +0,0 @@ -@import "effects"; -@import "variables"; - -vn-main-menu { - display: flex; - align-items: center; - justify-content: flex-end; - - & > div { - display: flex; - align-items: center; - box-sizing: border-box; - - & > * { - transition: color 250ms ease-out; - } - & > #user { - vertical-align: middle; - font-weight: bold; - margin-right: .2em; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - cursor: pointer; - } - & > .vn-button { - font-size: 1.2em; - color: inherit; - padding: 0; - margin-left: .3em; - } - & > :hover { - color: $color-main; - opacity: 1; - } - } -} - -.vn-popover .modules-menu { - list-style-type: none; - margin: 0; - color: $color-font-dark; - - & > li { - @extend %clickable-light; - background-color: $color-main; - margin-bottom: .6em; - padding: .8em; - border-radius: .1em; - min-width: 8em; - white-space: nowrap; - - &:last-child { - margin-bottom: 0; - } - & > vn-icon { - padding-right: .3em; - vertical-align: middle; - } - } -} diff --git a/front/salix/components/module-card/index.js b/front/salix/components/module-card/index.js new file mode 100644 index 000000000..9131cc772 --- /dev/null +++ b/front/salix/components/module-card/index.js @@ -0,0 +1,29 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +/** + * Base class for module cards. + */ +export default class ModuleCard extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-module-card'); + } + + $onInit() { + this.reload(); + } + + /** + * Reloads the card data. Should be implemented or overriden by child + * classes. + */ + reload() { + throw new Error('ModuleCard::reload() method not implemented'); + } +} + +ngModule.vnComponent('vnModuleCard', { + controller: ModuleCard +}); diff --git a/front/salix/components/module-card/style.scss b/front/salix/components/module-card/style.scss new file mode 100644 index 000000000..0ae943efc --- /dev/null +++ b/front/salix/components/module-card/style.scss @@ -0,0 +1,5 @@ +@import "variables"; + +.vn-module-card { + padding: 0; +} \ No newline at end of file diff --git a/front/salix/components/module-main/index.html b/front/salix/components/module-main/index.html new file mode 100644 index 000000000..6e04f06d0 --- /dev/null +++ b/front/salix/components/module-main/index.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/front/salix/components/module-main/index.js b/front/salix/components/module-main/index.js new file mode 100644 index 000000000..39fbe42b8 --- /dev/null +++ b/front/salix/components/module-main/index.js @@ -0,0 +1,15 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class ModuleMain extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-module-main'); + } +} + +ngModule.vnComponent('vnModuleMain', { + template: require('./index.html'), + controller: ModuleMain +}); diff --git a/front/salix/components/module-main/style.scss b/front/salix/components/module-main/style.scss new file mode 100644 index 000000000..d9bdbd940 --- /dev/null +++ b/front/salix/components/module-main/style.scss @@ -0,0 +1,5 @@ +@import "variables"; + +.vn-module-main { + padding: 0; +} \ No newline at end of file diff --git a/front/salix/components/section/index.js b/front/salix/components/section/index.js new file mode 100644 index 000000000..a0c7484b4 --- /dev/null +++ b/front/salix/components/section/index.js @@ -0,0 +1,14 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class Section extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-section'); + } +} + +ngModule.vnComponent('vnSection', { + controller: Section +}); diff --git a/front/salix/components/section/style.scss b/front/salix/components/section/style.scss new file mode 100644 index 000000000..e69de29bb diff --git a/front/salix/components/side-menu/side-menu.js b/front/salix/components/side-menu/side-menu.js index dfac732a1..c2b5b421e 100644 --- a/front/salix/components/side-menu/side-menu.js +++ b/front/salix/components/side-menu/side-menu.js @@ -1,4 +1,5 @@ import ngModule from '../../module'; +import Component from 'core/lib/component'; import './style.scss'; /** @@ -7,15 +8,10 @@ import './style.scss'; * @property {String} side [left|right] The side where the menu is displayed * @property {Boolean} shown Whether the menu it's currently displayed (Only for small viewports) */ -export default class SideMenu { - constructor($, $element, $window, $transitions) { - Object.assign(this, { - $, - $element, - $window, - $transitions, - side: 'left' - }); +export default class SideMenu extends Component { + constructor($element, $) { + super($element, $); + this.side = 'left'; } $onInit() { @@ -23,18 +19,25 @@ export default class SideMenu { if (this.side == 'right') { this.menu.classList.add('right'); - this.app.rightMenu = this; + this.layout.rightMenu = this; + this.layout.element.classList.add('right-menu'); } else { this.menu.classList.add('left'); - this.app.leftMenu = this; + this.layout.leftMenu = this; + this.layout.element.classList.add('left-menu'); } } $onDestroy() { - if (this.side == 'right') - this.app.rightMenu = null; - else - this.app.leftMenu = null; + if (this.side == 'right') { + this.layout.rightMenu = null; + this.layout.element.classList.remove('right-menu'); + } else { + this.layout.leftMenu = null; + this.layout.element.classList.remove('left-menu'); + } + + this.hide(); } onEscape(event) { @@ -50,6 +53,7 @@ export default class SideMenu { } show() { + if (this.shown) return; this.shown = true; this.handler = e => this.onEscape(e); this.$window.addEventListener('keydown', this.handler); @@ -57,12 +61,12 @@ export default class SideMenu { } hide() { + if (!this.shown) return; this.$window.removeEventListener('keydown', this.handler); this.stateHandler(); this.shown = false; } } -SideMenu.$inject = ['$scope', '$element', '$window', '$transitions']; ngModule.component('vnSideMenu', { template: require('./side-menu.html'), @@ -72,6 +76,6 @@ ngModule.component('vnSideMenu', { side: '@?' }, require: { - app: '^vnApp' + layout: '^vnLayout' } }); diff --git a/front/salix/components/side-menu/style.scss b/front/salix/components/side-menu/style.scss index 0054abfa7..239d15cf6 100644 --- a/front/salix/components/side-menu/style.scss +++ b/front/salix/components/side-menu/style.scss @@ -1,5 +1,8 @@ @import "variables"; +vn-side-menu { + display: block; +} vn-side-menu > .menu { display: block; position: fixed; @@ -20,14 +23,15 @@ vn-side-menu > .menu { } @media screen and (max-width: $mobile-width) { - top: 0; transition: transform 200ms ease-out; z-index: 15; + top: 0; &.left { transform: translateZ(0) translateX(-$menu-width); } &.right { + top: 0; transform: translateZ(0) translateX($menu-width); } &.shown { diff --git a/front/salix/components/summary/style.scss b/front/salix/components/summary/style.scss index 58a613088..f0d6ae038 100644 --- a/front/salix/components/summary/style.scss +++ b/front/salix/components/summary/style.scss @@ -59,4 +59,5 @@ .vn-popup .summary { border: none; box-shadow: none; + margin: 0; } diff --git a/front/salix/components/topbar/style.scss b/front/salix/components/topbar/style.scss index dc5273dbf..d7a5173f6 100644 --- a/front/salix/components/topbar/style.scss +++ b/front/salix/components/topbar/style.scss @@ -2,15 +2,8 @@ vn-topbar { display: flex; + align-items: center; color: $color-font-dark; box-sizing: border-box; background-color: $color-header; - align-items: center; - - & > header { - height: inherit; - width: inherit; - display: flex; - align-items: center; - } } diff --git a/front/salix/components/topbar/topbar.js b/front/salix/components/topbar/topbar.js index 583354ad1..423b033ce 100644 --- a/front/salix/components/topbar/topbar.js +++ b/front/salix/components/topbar/topbar.js @@ -1,7 +1 @@ -import ngModule from '../../module'; import './style.scss'; - -ngModule.component('vnTopbar', { - template: require('./topbar.html'), - transclude: true -}); diff --git a/front/salix/components/user-popover/index.html b/front/salix/components/user-popover/index.html index 1e17313f1..f34219d26 100644 --- a/front/salix/components/user-popover/index.html +++ b/front/salix/components/user-popover/index.html @@ -13,18 +13,33 @@
        - -
        -
        -
        - {{$root.user.nickname}} -
        -
        - {{::$root.user.name}} + +
        +
        +
        +
        + {{$root.user.nickname}} +
        +
        + {{::$root.user.name}} +
        + +
        - - + + My account
        diff --git a/front/salix/components/user-popover/index.js b/front/salix/components/user-popover/index.js index 5addec4a2..764b0cae9 100644 --- a/front/salix/components/user-popover/index.js +++ b/front/salix/components/user-popover/index.js @@ -12,11 +12,12 @@ let languages = { }; class Controller { - constructor($, $translate, vnConfig) { + constructor($, $translate, vnConfig, vnAuth) { Object.assign(this, { $, $translate, vnConfig, + vnAuth, lang: $translate.use(), langs: [] }); @@ -86,7 +87,7 @@ class Controller { this.$.popover.show(event.target); } } -Controller.$inject = ['$scope', '$translate', 'vnConfig']; +Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth']; ngModule.component('vnUserPopover', { template: require('./index.html'), diff --git a/front/salix/components/user-popover/style.scss b/front/salix/components/user-popover/style.scss index 4277b98f6..c8325c9f6 100644 --- a/front/salix/components/user-popover/style.scss +++ b/front/salix/components/user-popover/style.scss @@ -8,11 +8,8 @@ align-items: center; & > vn-icon { - font-size: 60px; - border-radius: 50%; - color: $color-font-dark; - background: $color-secondary; - padding: .1em; + font-size: 80px; + color: $color-font-bg-marginal; } & > div { display: flex; @@ -20,10 +17,21 @@ justify-content: space-between; flex: 1; - & > .user { - max-width: 10em; + & > div { + display: flex; padding-bottom: .5em; + + & > .user { + flex: 1; + max-width: 8em; + } } } } +} +#logout { + float: right; + height: initial; + vertical-align: middle; + font-size: 1em; } \ No newline at end of file diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml index a7f1dc93a..2f65ab255 100644 --- a/front/salix/locale/es.yml +++ b/front/salix/locale/es.yml @@ -1,4 +1,5 @@ Applications: Aplicaciones +Account: Cuenta Language: Idioma Change language: Cambiar idioma Data saved!: ¡Datos guardados! diff --git a/front/salix/module.js b/front/salix/module.js index 58bdec105..2354e0dff 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -62,9 +62,10 @@ export function config($translatePartialLoaderProvider, $httpProvider, $compileP $translatePartialLoaderProvider.addPart(appName); $httpProvider.interceptors.push('vnInterceptor'); - // $compileProvider.debugInfoEnabled(false); - $compileProvider.commentDirectivesEnabled(false); - $compileProvider.cssClassDirectivesEnabled(false); + $compileProvider + .debugInfoEnabled(false) + .commentDirectivesEnabled(false) + .cssClassDirectivesEnabled(false); } ngModule.config(config); diff --git a/front/salix/routes.js b/front/salix/routes.js index 48a92795e..600907ff1 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -1,41 +1,23 @@ import ngModule from './module'; import getMainRoute from 'core/lib/get-main-route'; -function loader(moduleName) { - load.$inject = ['vnModuleLoader']; - function load(moduleLoader) { - return moduleLoader.load(moduleName); - } - return load; -} - config.$inject = ['$stateProvider', '$urlRouterProvider']; function config($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/'); - $stateProvider.state('home', { - url: '/', - template: '', - description: 'Home' - }); - $stateProvider.state('login', { - url: '/login?continue', - template: '', - description: 'Login' - }); - - function getParams(route) { - let params = ''; - let temporalParams = []; - - if (!route.params) - return params; - - Object.keys(route.params).forEach(key => { - temporalParams.push(`${key} = "${route.params[key]}"`); + $stateProvider + .state('login', { + url: '/login?continue', + description: 'Login', + views: { + login: {template: ''} + } + }) + .state('home', { + url: '/', + description: 'Home', + template: '' }); - return temporalParams.join(' '); - } for (let file in window.routes) { let routeFile = window.routes[file]; @@ -68,5 +50,26 @@ function config($stateProvider, $urlRouterProvider) { $stateProvider.state(route.state, configRoute); } } + + function getParams(route) { + let params = ''; + let temporalParams = []; + + if (!route.params) + return params; + + Object.keys(route.params).forEach(key => { + temporalParams.push(`${key} = "${route.params[key]}"`); + }); + return temporalParams.join(' '); + } + + function loader(moduleName) { + load.$inject = ['vnModuleLoader']; + function load(moduleLoader) { + return moduleLoader.load(moduleName); + } + return load; + } } ngModule.config(config); diff --git a/front/salix/styles/misc.scss b/front/salix/styles/misc.scss index 0ae30becd..1f2d79981 100644 --- a/front/salix/styles/misc.scss +++ b/front/salix/styles/misc.scss @@ -123,25 +123,14 @@ html [scrollable] { font-size: 0.7em } } -[compact], .compact { - margin-left: auto; - margin-right: auto; - max-width: $width-md; -} -vn-empty-rows { - display: block; - text-align: center; - padding: 1.5em; - box-sizing: border-box; -} /* XXX: Deprecated, use classes with text prefix */ [color-main] { color: $color-main; } -[color-secondary] { - color: $color-secondary; +[color-marginal] { + color: $color-marginal; } [uppercase], .uppercase { text-transform: uppercase; diff --git a/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js b/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js index 767ecff2e..3a806ce44 100644 --- a/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js +++ b/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js @@ -1,14 +1,23 @@ module.exports = Self => { Self.remoteMethod('getAgenciesWithWarehouse', { description: 'Returns a list of agencies that can land a shipment on a day for an address and a warehouse', - accepts: [{ - arg: 'filter', - type: 'object', - required: true, - description: 'addressFk' - }], + accepts: [ + { + arg: 'addressFk', + type: 'number', + required: true + }, { + arg: 'landed', + type: 'date', + required: true + }, { + arg: 'warehouseFk', + type: 'number', + required: true + } + ], returns: { - type: 'object', + type: ['object'], root: true }, http: { @@ -17,10 +26,9 @@ module.exports = Self => { } }); - Self.getAgenciesWithWarehouse = async filter => { + Self.getAgenciesWithWarehouse = async(addressFk, landed, warehouseFk) => { let query = `CALL vn.zone_getWarehouse(?, ?, ?)`; - let result = await Self.rawSql(query, [filter.addressFk, filter.landed, filter.warehouseFk]); - + let [result] = await Self.rawSql(query, [addressFk, landed, warehouseFk]); return result; }; }; diff --git a/modules/agency/back/methods/agency/landsThatDay.js b/modules/agency/back/methods/agency/landsThatDay.js index f92d2e285..87da63767 100644 --- a/modules/agency/back/methods/agency/landsThatDay.js +++ b/modules/agency/back/methods/agency/landsThatDay.js @@ -1,12 +1,17 @@ module.exports = Self => { Self.remoteMethod('landsThatDay', { description: 'Returns a list of agencies that can land a shipment on a day for an address', - accepts: [{ - arg: 'filter', - type: 'object', - required: true, - description: 'addressFk' - }], + accepts: [ + { + arg: 'addressFk', + type: 'number', + required: true + }, { + arg: 'landed', + type: 'date', + required: true + } + ], returns: { type: 'object', root: true @@ -17,14 +22,13 @@ module.exports = Self => { } }); - Self.landsThatDay = async filter => { + Self.landsThatDay = async(addressFk, landed) => { let query = ` CALL vn.zone_getAgency(?, ?); SELECT * FROM tmp.zoneGetAgency; DROP TEMPORARY TABLE tmp.zoneGetAgency; `; - - let result = await Self.rawSql(query, [filter.addressFk, filter.landed]); + let result = await Self.rawSql(query, [addressFk, landed]); return result[1]; }; diff --git a/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js b/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js index 3666ef7f2..614c33519 100644 --- a/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js +++ b/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js @@ -3,14 +3,7 @@ const app = require('vn-loopback/server/server'); describe('Agency getAgenciesWithWarehouse()', () => { const today = new Date(); it('should return the agencies that can handle the given delivery request', async() => { - let filter = { - addressFk: 101, - landed: today, - warehouseFk: 1 - }; - - let result = await app.models.Agency.getAgenciesWithWarehouse(filter); - let agencies = result[0]; + let agencies = await app.models.Agency.getAgenciesWithWarehouse(101, today, 1); expect(agencies.length).toEqual(3); expect(agencies[0].agencyMode).toEqual('inhouse pickup'); @@ -19,14 +12,7 @@ describe('Agency getAgenciesWithWarehouse()', () => { }); it('should return no agencies if the date is incorrect', async() => { - let filter = { - addressFk: 101, - landed: null, - warehouseFk: 1 - }; - - let result = await app.models.Agency.getAgenciesWithWarehouse(filter); - let agencies = result[0]; + let agencies = await app.models.Agency.getAgenciesWithWarehouse(101, null, 1); expect(agencies.length).toEqual(0); }); diff --git a/modules/agency/back/methods/agency/specs/landsThatDay.spec.js b/modules/agency/back/methods/agency/specs/landsThatDay.spec.js index 98760d5fe..4c0235118 100644 --- a/modules/agency/back/methods/agency/specs/landsThatDay.spec.js +++ b/modules/agency/back/methods/agency/specs/landsThatDay.spec.js @@ -3,12 +3,7 @@ const app = require('vn-loopback/server/server'); xdescribe('Agency landsThatDay()', () => { const today = new Date(); it('should return a list of agencies that can land a shipment on a day for an address', async() => { - let filter = { - addressFk: 101, - landed: today, - }; - - let agencies = await app.models.Agency.landsThatDay(filter); + let agencies = await app.models.Agency.landsThatDay(101, today); expect(agencies.length).toBeGreaterThanOrEqual(3); }); diff --git a/modules/agency/back/methods/zone/getEvents.js b/modules/agency/back/methods/zone/getEvents.js index b294222b5..1c70dfffd 100644 --- a/modules/agency/back/methods/zone/getEvents.js +++ b/modules/agency/back/methods/zone/getEvents.js @@ -4,19 +4,18 @@ module.exports = Self => { description: 'Returns delivery days for a postcode', accepts: [ { - arg: 'agencyModeFk', - type: 'Number', - description: 'The agency mode id', - required: true - }, { arg: 'provinceFk', type: 'Number', description: 'The province id', required: true }, { - arg: 'postCode', + arg: 'search', type: 'String', description: 'The postcode' + }, { + arg: 'agencyModeFk', + type: 'Number', + description: 'The agency mode id' } ], returns: { @@ -29,12 +28,11 @@ module.exports = Self => { } }); - Self.getEvents = async(agencyModeFk, provinceFk, postCode) => { + Self.getEvents = async(provinceFk, postCode, agencyModeFk) => { let [events, exclusions] = await Self.rawSql( `CALL zone_getEvents(?, ?, ?)`, - [agencyModeFk, provinceFk, postCode] + [provinceFk, postCode, agencyModeFk] ); - return {events, exclusions}; }; }; diff --git a/modules/agency/front/basic-data/index.html b/modules/agency/front/basic-data/index.html index b0f94ddc0..ec5cc3b20 100644 --- a/modules/agency/front/basic-data/index.html +++ b/modules/agency/front/basic-data/index.html @@ -84,14 +84,17 @@ - - + + diff --git a/modules/agency/front/card/index.html b/modules/agency/front/card/index.html index 1fd22809a..ae6a7f10a 100644 --- a/modules/agency/front/card/index.html +++ b/modules/agency/front/card/index.html @@ -1,5 +1,5 @@ - + - - -
        + + + diff --git a/modules/agency/front/card/index.js b/modules/agency/front/card/index.js index 7c53e9294..4e8ac7e8c 100644 --- a/modules/agency/front/card/index.js +++ b/modules/agency/front/card/index.js @@ -1,38 +1,21 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - this.$http = $http; - this.$stateParams = $stateParams; - } - - $onInit() { - this.getCard(); - } - - getCard() { +class Controller extends ModuleCard { + reload() { let filter = { include: { relation: 'agencyMode', scope: {fields: ['name']} } }; - let json = encodeURIComponent(JSON.stringify(filter)); - let query = `Zones/${this.$stateParams.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) - this.zone = res.data; - }); - } - reload() { - this.getCard(); + this.$http.get(`Zones/${this.$params.id}`, {filter}) + .then(res => this.zone = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - -ngModule.component('vnZoneCard', { +ngModule.vnComponent('vnZoneCard', { template: require('./index.html'), controller: Controller }); diff --git a/modules/agency/front/card/index.spec.js b/modules/agency/front/card/index.spec.js index 190b77d0d..cc53fe946 100644 --- a/modules/agency/front/card/index.spec.js +++ b/modules/agency/front/card/index.spec.js @@ -1,35 +1,26 @@ import './index.js'; describe('Agency Component vnZoneCard', () => { - let $scope; let controller; let $httpBackend; - let $stateParams; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('agency')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $stateParams = {id: 1}; - controller = $componentController('vnZoneCard', {$scope, $stateParams}); + + let $element = angular.element('
        '); + controller = $componentController('vnZoneCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Zones/:id').respond(data); })); - describe('getCard()', () => { - it(`should make a query and define zone property`, () => { - let filter = { - include: { - relation: 'agencyMode', - scope: {fields: ['name']} - } - }; - let json = encodeURIComponent(JSON.stringify(filter)); - $httpBackend.expectGET(`Zones/1?filter=${json}`).respond({id: 1}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.zone).toEqual({id: 1}); - }); + expect(controller.zone).toEqual(data); }); }); - diff --git a/modules/agency/front/create/index.html b/modules/agency/front/create/index.html index 716d5e50c..332e8a9b8 100644 --- a/modules/agency/front/create/index.html +++ b/modules/agency/front/create/index.html @@ -5,79 +5,82 @@ form="form" save="post"> -
        -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        -
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        diff --git a/modules/agency/front/delivery-days/index.html b/modules/agency/front/delivery-days/index.html index 286721068..2b8a0e423 100644 --- a/modules/agency/front/delivery-days/index.html +++ b/modules/agency/front/delivery-days/index.html @@ -1,43 +1,33 @@
        -
        - - - - - - - - -
        {{name}}
        -
        - {{country.country}} -
        -
        -
        - - -
        -
        - - - -
        + data="data">
        + +
        + + +
        {{name}}
        +
        + {{country.country}} +
        +
        +
        + + + + + +
        +
        diff --git a/modules/agency/front/delivery-days/index.js b/modules/agency/front/delivery-days/index.js index cebfdbf7e..e9c885a13 100644 --- a/modules/agency/front/delivery-days/index.js +++ b/modules/agency/front/delivery-days/index.js @@ -1,20 +1,17 @@ import ngModule from '../module'; +import Section from 'salix/components/section'; import './style.scss'; -class Controller { - constructor($, $http) { - Object.assign(this, { - $, - $http - }); +class Controller extends Section { + $onInit() { + this.$.params = {}; } onSubmit() { - this.$http.get(`Zones/getEvents`, {params: this.params}) + this.$http.get(`Zones/getEvents`, {params: this.$.params}) .then(res => this.$.data = res.data); } } -Controller.$inject = ['$scope', '$http']; ngModule.component('vnZoneDeliveryDays', { template: require('./index.html'), diff --git a/modules/agency/front/delivery-days/style.scss b/modules/agency/front/delivery-days/style.scss index 1db8edfbf..79f95a3dc 100644 --- a/modules/agency/front/delivery-days/style.scss +++ b/modules/agency/front/delivery-days/style.scss @@ -9,4 +9,8 @@ vn-zone-delivery-days { min-width: 16.5em; } } + form { + display: flex; + flex-direction: column; + } } \ No newline at end of file diff --git a/modules/agency/front/events/index.html b/modules/agency/front/events/index.html index 4764ded3c..ec1bec4e8 100644 --- a/modules/agency/front/events/index.html +++ b/modules/agency/front/events/index.html @@ -1,11 +1,9 @@ -
        - - -
        + +
        -
        + is-loading="!data.events"> + diff --git a/modules/agency/front/index/index.html b/modules/agency/front/index/index.html index 18229cd34..56a790b44 100644 --- a/modules/agency/front/index/index.html +++ b/modules/agency/front/index/index.html @@ -6,63 +6,56 @@ data="zones" auto-load="true"> -
        - - - + + + + + + + + Id + Name + Agency + Closing + Price + + + + + + {{::zone.id}} + {{::zone.name}} + {{::zone.agencyMode.name}} + {{::zone.hour | date: 'HH:mm'}} + {{::zone.price | currency: 'EUR':2}} + + + + + + + + + + + - - - - - - Id - Name - Agency - Closing - Price - - - - - - {{::zone.id}} - {{::zone.name}} - {{::zone.agencyMode.name}} - {{::zone.hour | date: 'HH:mm'}} - {{::zone.price | currency: 'EUR':2}} - - - - - - - - - - - - - -
        + diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index 7f33b840c..4b3523a1f 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -3,16 +3,17 @@ url="Zones/{{$ctrl.$params.id}}/getLeaves" filter="::$ctrl.filter"> + + + + +
        - - - - - { const data = this.$.model.data; @@ -12,9 +16,8 @@ class Controller extends Component { onFetch(item) { const params = item ? {parentId: item.id} : null; - return this.$.model.applyFilter({}, params).then(() => { - return this.$.model.data; - }); + return this.$.model.applyFilter({}, params) + .then(() => this.$.model.data); } onSort(a, b) { diff --git a/modules/agency/front/location/style.scss b/modules/agency/front/location/style.scss index d03701e42..4972a5d29 100644 --- a/modules/agency/front/location/style.scss +++ b/modules/agency/front/location/style.scss @@ -2,13 +2,13 @@ vn-treeview-child { .content > .vn-check:not(.indeterminate) { - color: $color-main; + color: $color-button; & > .btn { - border-color: $color-main; + border-color: $color-button; } } .content > .vn-check.checked { - color: $color-main; + color: $color-button; } } \ No newline at end of file diff --git a/modules/agency/front/main/index.html b/modules/agency/front/main/index.html index d695d74aa..230bf7f6d 100644 --- a/modules/agency/front/main/index.html +++ b/modules/agency/front/main/index.html @@ -1,14 +1,11 @@ - - - - -
        -
        - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/modules/agency/front/main/index.js b/modules/agency/front/main/index.js index 223b78caa..3e8890310 100644 --- a/modules/agency/front/main/index.js +++ b/modules/agency/front/main/index.js @@ -1,6 +1,9 @@ import ngModule from '../module'; -import './style.scss'; +import ModuleMain from 'salix/components/module-main'; -ngModule.component('vnZone', { +export default class Zone extends ModuleMain {} + +ngModule.vnComponent('vnZone', { + controller: Zone, template: require('./index.html') }); diff --git a/modules/agency/front/main/style.scss b/modules/agency/front/main/style.scss deleted file mode 100644 index 21e4fc40c..000000000 --- a/modules/agency/front/main/style.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import "effects"; - -vn-zone { - ul.menu { - list-style-type: none; - padding: 0; - padding-top: $spacing-md; - margin: 0; - font-size: inherit; - - & > li > a { - @extend %clickable; - display: block; - color: inherit; - padding: .6em 2em; - } - } -} \ No newline at end of file diff --git a/modules/agency/front/routes.json b/modules/agency/front/routes.json index 26d619172..7c5b1338d 100644 --- a/modules/agency/front/routes.json +++ b/modules/agency/front/routes.json @@ -4,12 +4,18 @@ "icon" : "icon-delivery", "validations" : true, "dependencies": ["worker"], - "menu": [ - {"state": "zone.card.basicData", "icon": "settings"}, - {"state": "zone.card.location", "icon": "my_location"}, - {"state": "zone.card.warehouses", "icon": "home"}, - {"state": "zone.card.events", "icon": "today"} - ], + "menus": { + "main": [ + {"state": "zone.index", "icon": "icon-delivery"}, + {"state": "zone.deliveryDays", "icon": "today"} + ], + "card": [ + {"state": "zone.card.basicData", "icon": "settings"}, + {"state": "zone.card.location", "icon": "my_location"}, + {"state": "zone.card.warehouses", "icon": "home"}, + {"state": "zone.card.events", "icon": "today"} + ] + }, "routes": [ { "url": "/zone", @@ -23,7 +29,7 @@ "component": "vn-zone-index", "description": "Zones" }, { - "url": "/delivery-days", + "url": "/delivery-days?q", "state": "zone.deliveryDays", "component": "vn-zone-delivery-days", "description": "Delivery days" diff --git a/modules/agency/front/search-panel/index.html b/modules/agency/front/search-panel/index.html index 1fd07cbf5..a97428192 100644 --- a/modules/agency/front/search-panel/index.html +++ b/modules/agency/front/search-panel/index.html @@ -22,8 +22,8 @@ label="Agency" ng-model="filter.agencyModeFk" url="AgencyModes/isActive" - show-field="name" - value-field="id"> + value-field="id" + show-field="name"> diff --git a/modules/claim/front/basic-data/index.html b/modules/claim/front/basic-data/index.html index cd8cade9f..9be04cb57 100644 --- a/modules/claim/front/basic-data/index.html +++ b/modules/claim/front/basic-data/index.html @@ -5,7 +5,7 @@ url="Claims/{{$ctrl.$stateParams.id}}/updateClaim" save="post"> -
        + diff --git a/modules/claim/front/card/index.html b/modules/claim/front/card/index.html index 3a445f0b1..1db6b38db 100644 --- a/modules/claim/front/card/index.html +++ b/modules/claim/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/claim/front/card/index.js b/modules/claim/front/card/index.js index 0a641ce89..ccd78a271 100644 --- a/modules/claim/front/card/index.js +++ b/modules/claim/front/card/index.js @@ -1,12 +1,12 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ - {relation: 'worker', + { + relation: 'worker', scope: { fields: ['userFk'], include: { @@ -16,8 +16,7 @@ class Controller { } } } - }, - { + }, { relation: 'ticket', scope: { fields: ['agencyModeFk'], @@ -25,14 +24,12 @@ class Controller { relation: 'agencyMode' } } - }, - { + }, { relation: 'claimState', scope: { fields: ['id', 'description'] } - }, - { + }, { relation: 'client', scope: { fields: ['salesPersonFk', 'name', 'email'], @@ -53,29 +50,13 @@ class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - let query = `Claims/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) - this.claim = res.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Claims/${this.$params.id}`, {filter}) + .then(res => this.claim = res.data); } } -Controller.$inject = ['$http', '$state']; - -ngModule.component('vnClaimCard', { +ngModule.vnComponent('vnClaimCard', { template: require('./index.html'), controller: Controller }); diff --git a/modules/claim/front/card/index.spec.js b/modules/claim/front/card/index.spec.js index dfd106e89..1b2344925 100644 --- a/modules/claim/front/card/index.spec.js +++ b/modules/claim/front/card/index.spec.js @@ -2,29 +2,27 @@ import './index.js'; describe('Claim', () => { describe('Component vnClaimCard', () => { - let $scope; let controller; let $httpBackend; - let $state; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('claim')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $state = {params: {id: 1}}; - controller = $componentController('vnClaimCard', {$scope, $state}); + + let $element = angular.element('
        '); + controller = $componentController('vnClaimCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Claims/:id').respond(data); })); - describe('getCard()', () => { - it(`should make a query and save the data in claim`, () => { - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Claims/${controller.$state.params.id}?filter=${json}`).respond({id: 1}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.claim).toEqual({id: 1}); - }); + expect(controller.claim).toEqual(data); }); }); }); diff --git a/modules/claim/front/development/index.html b/modules/claim/front/development/index.html index 281eb0188..cf777f31a 100644 --- a/modules/claim/front/development/index.html +++ b/modules/claim/front/development/index.html @@ -36,7 +36,7 @@ data="claimDevelopments" form="form"> - + diff --git a/modules/claim/front/index.js b/modules/claim/front/index.js index 53aa5e9da..4ea9ac6cc 100644 --- a/modules/claim/front/index.js +++ b/modules/claim/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './action'; import './basic-data'; diff --git a/modules/claim/front/index/index.html b/modules/claim/front/index/index.html index ca232b3dc..c85632571 100644 --- a/modules/claim/front/index/index.html +++ b/modules/claim/front/index/index.html @@ -6,67 +6,60 @@ order="claimStateFk ASC, created DESC" auto-load="true"> - + diff --git a/modules/claim/front/main/index.html b/modules/claim/front/main/index.html new file mode 100644 index 000000000..e733162b1 --- /dev/null +++ b/modules/claim/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/claim/front/main/index.js b/modules/claim/front/main/index.js new file mode 100644 index 000000000..77b051897 --- /dev/null +++ b/modules/claim/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Claim extends ModuleMain {} + +ngModule.vnComponent('vnClaim', { + controller: Claim, + template: require('./index.html') +}); diff --git a/modules/claim/front/routes.json b/modules/claim/front/routes.json index bda7da74b..80bc77c49 100644 --- a/modules/claim/front/routes.json +++ b/modules/claim/front/routes.json @@ -4,13 +4,18 @@ "icon": "icon-claims", "validations": true, "dependencies": ["worker", "item", "client"], - "menu": [ - {"state": "claim.card.basicData", "icon": "settings"}, - {"state": "claim.card.detail", "icon": "icon-details"}, - {"state": "claim.card.dms.index", "icon": "image"}, - {"state": "claim.card.development", "icon": "icon-traceability"}, - {"state": "claim.card.action", "icon": "icon-actions"} - ], + "menus": { + "main": [ + {"state": "claim.index", "icon": "icon-claims"} + ], + "card": [ + {"state": "claim.card.basicData", "icon": "settings"}, + {"state": "claim.card.detail", "icon": "icon-details"}, + {"state": "claim.card.dms.index", "icon": "image"}, + {"state": "claim.card.development", "icon": "icon-traceability"}, + {"state": "claim.card.action", "icon": "icon-actions"} + ] + }, "keybindings": [ {"key": "r", "state": "claim.index"} ], @@ -19,7 +24,7 @@ "url": "/claim", "state": "claim", "abstract": true, - "component": "ui-view", + "component": "vn-claim", "description": "Claims" }, { "url": "/index?q", diff --git a/modules/client/front/address/create/index.html b/modules/client/front/address/create/index.html index 3e9c4ab41..43f6e3589 100644 --- a/modules/client/front/address/create/index.html +++ b/modules/client/front/address/create/index.html @@ -14,7 +14,7 @@ data="observations" auto-load="true"> - + - + - + - - - - -
        - + + + + + diff --git a/modules/client/front/card/index.js b/modules/client/front/card/index.js index c785644d9..5aacc83ea 100644 --- a/modules/client/front/card/index.js +++ b/modules/client/front/card/index.js @@ -1,28 +1,12 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($scope, $stateParams, $http) { - this.$scope = $scope; - this.$http = $http; - this.$stateParams = $stateParams; - this.client = null; - } - - $onInit() { - this.getCard(); - } - - getCard() { - this.$http.get(`Clients/${this.$stateParams.id}/getCard`).then(response => { - this.client = response.data; - }); - } - +export default class Controller extends ModuleCard { reload() { - this.getCard(); + this.$http.get(`Clients/${this.$params.id}/getCard`) + .then(res => this.client = res.data); } } -Controller.$inject = ['$scope', '$stateParams', '$http']; ngModule.component('vnClientCard', { template: require('./index.html'), diff --git a/modules/client/front/card/index.spec.js b/modules/client/front/card/index.spec.js index 502e04677..72272fe89 100644 --- a/modules/client/front/card/index.spec.js +++ b/modules/client/front/card/index.spec.js @@ -2,21 +2,27 @@ import './index'; describe('Client', () => { describe('Component vnClientCard', () => { - let $componentController; - let $scope; let controller; + let $httpBackend; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('client')); - beforeEach(angular.mock.inject((_$componentController_, $rootScope) => { - $componentController = _$componentController_; - $scope = $rootScope; - controller = $componentController('vnClientCard', {$scope: $scope}); + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { + $httpBackend = _$httpBackend_; + + let $element = angular.element('
        '); + controller = $componentController('vnClientCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Clients/:id/getCard').respond(data); })); - it('should define and set client property to null in the module instance', () => { - expect(controller.client).toBeDefined(); - expect(controller.client).toBe(null); + it('should request data and set it on the controller', () => { + controller.$onInit(); + $httpBackend.flush(); + + expect(controller.client).toEqual(data); }); }); }); diff --git a/modules/client/front/contact/index.html b/modules/client/front/contact/index.html index fa58f27d4..2cefdd86d 100644 --- a/modules/client/front/contact/index.html +++ b/modules/client/front/contact/index.html @@ -11,7 +11,7 @@ data="contacts" form="form"> - + -
        - - - - - - - {{firstName}} {{lastName}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        \ No newline at end of file +
        + + + + + + {{firstName}} {{lastName}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + + + \ No newline at end of file diff --git a/modules/client/front/credit-insurance/create/index.html b/modules/client/front/credit-insurance/create/index.html index 5dbe258b6..d1dc081b7 100644 --- a/modules/client/front/credit-insurance/create/index.html +++ b/modules/client/front/credit-insurance/create/index.html @@ -1,4 +1,4 @@ -
        + - + - + -
        +
        diff --git a/modules/client/front/dms/edit/index.html b/modules/client/front/dms/edit/index.html index a63a58c44..dbc2e0ed1 100644 --- a/modules/client/front/dms/edit/index.html +++ b/modules/client/front/dms/edit/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        - + - + - + { - let $scope; + let $state; let controller; beforeEach(ngModule('client')); - beforeEach(angular.mock.inject(($componentController, $rootScope, $state) => { - $scope = $rootScope.$new(); - controller = $componentController('vnClientIndex', {$scope, $state}); + beforeEach(angular.mock.inject(($componentController, _$state_) => { + $state = _$state_; + controller = $componentController('vnClientIndex'); })); describe('filterTickets()', () => { it('should navigate to the ticket index using params as filter', () => { const client = {id: 101}; - const event = {preventDefault: () => {}}; - spyOn(event, 'preventDefault'); - spyOn(controller.$state, 'go'); + const event = new MouseEvent('click', {cancelable: true}); + spyOn($state, 'go'); controller.filterTickets(client, event); - expect(event.preventDefault).toHaveBeenCalledWith(); - expect(controller.$state.go).toHaveBeenCalledWith('ticket.index', jasmine.any(Object)); + expect($state.go).toHaveBeenCalledWith('ticket.index', jasmine.any(Object)); }); }); }); diff --git a/modules/client/front/main/index.html b/modules/client/front/main/index.html new file mode 100644 index 000000000..1ae1d211b --- /dev/null +++ b/modules/client/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/client/front/main/index.js b/modules/client/front/main/index.js new file mode 100644 index 000000000..890165a19 --- /dev/null +++ b/modules/client/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Client extends ModuleMain {} + +ngModule.vnComponent('vnClient', { + controller: Client, + template: require('./index.html') +}); diff --git a/modules/client/front/note/create/index.html b/modules/client/front/note/create/index.html index 00a18edba..c9c351199 100644 --- a/modules/client/front/note/create/index.html +++ b/modules/client/front/note/create/index.html @@ -6,7 +6,7 @@ save="post" form="form"> - + - + - + - + - - - - -
        - + + + + + diff --git a/modules/invoiceOut/front/card/index.js b/modules/invoiceOut/front/card/index.js index c1a934924..0ad91f4c2 100644 --- a/modules/invoiceOut/front/card/index.js +++ b/modules/invoiceOut/front/card/index.js @@ -1,11 +1,9 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.route = null; - this.filter = { +class Controller extends ModuleCard { + reload() { + const filter = { fields: [ 'id', 'ref', @@ -28,24 +26,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - const params = {filter: this.filter}; - this.$http.get(`InvoiceOuts/${this.$stateParams.id}`, {params}).then(response => { - this.invoiceOut = response.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`InvoiceOuts/${this.$params.id}`, {filter}) + .then(res => this.invoiceOut = res.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnInvoiceOutCard', { template: require('./index.html'), diff --git a/modules/invoiceOut/front/index.js b/modules/invoiceOut/front/index.js index 239bfd812..9843e188b 100644 --- a/modules/invoiceOut/front/index.js +++ b/modules/invoiceOut/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './summary'; diff --git a/modules/invoiceOut/front/index/index.html b/modules/invoiceOut/front/index/index.html index 1a74b28b4..c798b8bc6 100644 --- a/modules/invoiceOut/front/index/index.html +++ b/modules/invoiceOut/front/index/index.html @@ -5,72 +5,65 @@ data="invoiceOuts" order="issued DESC"> -
        + diff --git a/modules/invoiceOut/front/main/index.html b/modules/invoiceOut/front/main/index.html new file mode 100644 index 000000000..18e72ed5e --- /dev/null +++ b/modules/invoiceOut/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/invoiceOut/front/main/index.js b/modules/invoiceOut/front/main/index.js new file mode 100644 index 000000000..ad37e9c4b --- /dev/null +++ b/modules/invoiceOut/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class InvoiceOut extends ModuleMain {} + +ngModule.vnComponent('vnInvoiceOut', { + controller: InvoiceOut, + template: require('./index.html') +}); diff --git a/modules/invoiceOut/front/routes.json b/modules/invoiceOut/front/routes.json index eb278ebd8..8f828a634 100644 --- a/modules/invoiceOut/front/routes.json +++ b/modules/invoiceOut/front/routes.json @@ -4,12 +4,17 @@ "icon": "icon-invoices", "validations" : true, "dependencies": ["worker", "client", "ticket"], + "menus": { + "main": [ + {"state": "invoiceOut.index", "icon": "icon-invoices"} + ] + }, "routes": [ { "url": "/invoice-out", "state": "invoiceOut", "abstract": true, - "component": "ui-view", + "component": "vn-invoice-out", "description": "InvoiceOut", "acl": ["employee"] }, diff --git a/modules/item/front/barcode/index.html b/modules/item/front/barcode/index.html index 316fff80f..dba745d28 100644 --- a/modules/item/front/barcode/index.html +++ b/modules/item/front/barcode/index.html @@ -11,7 +11,7 @@ data="barcodes" form="form"> - + - + - + - - - - -
        - + + + + + diff --git a/modules/item/front/card/index.js b/modules/item/front/card/index.js index c5adb14b8..12167500f 100644 --- a/modules/item/front/card/index.js +++ b/modules/item/front/card/index.js @@ -1,29 +1,13 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - this.$http = $http; - this.$stateParams = $stateParams; - this.item = null; - } - - $onInit() { - this.getCard(); - } - - getCard() { - this.$http.get(`Items/${this.$stateParams.id}/getCard`).then(response => { - this.item = response.data; - }); - } - +class Controller extends ModuleCard { reload() { - this.getCard(); + this.$http.get(`Items/${this.$params.id}/getCard`) + .then(res => this.item = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - ngModule.component('vnItemCard', { template: require('./index.html'), controller: Controller diff --git a/modules/item/front/card/index.spec.js b/modules/item/front/card/index.spec.js index 1db1db140..c0ea80e69 100644 --- a/modules/item/front/card/index.spec.js +++ b/modules/item/front/card/index.spec.js @@ -2,30 +2,27 @@ import './index.js'; describe('Item', () => { describe('Component vnItemCard', () => { - let $httpBackend; - let $stateParams; let controller; + let $httpBackend; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('item')); - beforeEach(angular.mock.inject(($componentController, _$stateParams_, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $stateParams = { - id: 123 - }; - controller = $componentController('vnItemCard', {$stateParams}); + + let $element = angular.element('
        '); + controller = $componentController('vnItemCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Items/:id/getCard').respond(data); })); - describe('getCard()', () => { - it('should request to set the item into the controller', () => { - let response = {id: 123}; - $httpBackend.when('GET', `Items/123/getCard`).respond(response); - $httpBackend.expect('GET', `Items/123/getCard`); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.item).toEqual(response); - }); + expect(controller.item).toEqual(data); }); }); }); diff --git a/modules/item/front/create/index.html b/modules/item/front/create/index.html index 8d22d7145..0c00aa8e9 100644 --- a/modules/item/front/create/index.html +++ b/modules/item/front/create/index.html @@ -5,61 +5,59 @@ form="form" save="post"> -
        - - - - - - - - - -
        {{::code}}
        -
        {{::name}}
        -
        -
        - - -
        {{::id}}
        -
        {{::description}}
        -
        -
        -
        - - - - -
        - - - - - - - -
        +
        + + + + + + + + +
        {{::code}}
        +
        {{::name}}
        +
        +
        + + +
        {{::id}}
        +
        {{::description}}
        +
        +
        +
        + + + + +
        + + + + + + +
        diff --git a/modules/item/front/diary/index.html b/modules/item/front/diary/index.html index dd8c9ff9d..720a42fb7 100644 --- a/modules/item/front/diary/index.html +++ b/modules/item/front/diary/index.html @@ -6,7 +6,7 @@ auto-load="false"> - + - + show-fields="$ctrl.showFields" + vn-uvc="itemIndex"> + + + + Id + Grouping + Packing + Description + Stems + Size + Niche + Type + Category + Intrastat + Origin + Buyer + Density + Active + + + + + + + + + + + {{::item.id | zeroFill:6}} + + + {{::item.grouping | dashIfEmpty}} + {{::item.packing | dashIfEmpty}} + + + + + {{::item.stems}} + {{::item.size}} + {{::item.niche}} + + {{::item.type}} + + + {{::item.category}} + + + {{::item.intrastat}} + + {{::item.origin}} + + + {{::item.userNickname}} + + + {{::item.density}} + + + + + + + + + + + + + + + + + diff --git a/modules/item/front/index/index.js b/modules/item/front/index/index.js index 93a83c6be..087021577 100644 --- a/modules/item/front/index/index.js +++ b/modules/item/front/index/index.js @@ -14,22 +14,6 @@ class Controller { id: false, actions: false }; - this.moreOptions = [ - {callback: this.goToTicketRequest, name: 'Buy requests', acl: 'buyer'} - ]; - } - - onMoreOpen() { - let options = this.moreOptions.filter(o => this.aclService.hasAny([o.acl])); - this.$.moreButton.data = options; - } - - onMoreChange(callback) { - callback.call(this); - } - - goToTicketRequest() { - this.$state.go('item.request'); } stopEvent(event) { diff --git a/modules/item/front/main/index.html b/modules/item/front/main/index.html new file mode 100644 index 000000000..758bfd55e --- /dev/null +++ b/modules/item/front/main/index.html @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/item/front/main/index.js b/modules/item/front/main/index.js new file mode 100644 index 000000000..f34369982 --- /dev/null +++ b/modules/item/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Items extends ModuleMain {} + +ngModule.vnComponent('vnItems', { + controller: Items, + template: require('./index.html') +}); diff --git a/modules/item/front/niche/index.html b/modules/item/front/niche/index.html index b361ed1e2..70dabe671 100644 --- a/modules/item/front/niche/index.html +++ b/modules/item/front/niche/index.html @@ -11,7 +11,7 @@ data="niches" form="form"> -
        + - -
        - - - - - - - - - - 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)}} - - - - - - - - - - - -
        - + + + + + + + + + + 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)}} + + + + + + + + + + + diff --git a/modules/item/front/routes.json b/modules/item/front/routes.json index e50a7f112..bda4f18a9 100644 --- a/modules/item/front/routes.json +++ b/modules/item/front/routes.json @@ -4,17 +4,23 @@ "icon": "icon-item", "validations" : true, "dependencies": ["worker", "client", "ticket"], - "menu": [ - {"state": "item.card.basicData", "icon": "settings"}, - {"state": "item.card.tags", "icon": "icon-tags"}, - {"state": "item.card.tax", "icon": "icon-tax"}, - {"state": "item.card.niche", "icon": "place"}, - {"state": "item.card.botanical", "icon": "local_florist"}, - {"state": "item.card.itemBarcode", "icon": "icon-barcode"}, - {"state": "item.card.diary", "icon": "icon-transaction"}, - {"state": "item.card.last-entries", "icon": "icon-regentry"}, - {"state": "item.card.log", "icon": "history"} - ], + "menus": { + "main": [ + {"state": "item.index", "icon": "icon-item"}, + {"state": "item.request", "icon": "pan_tool"} + ], + "card": [ + {"state": "item.card.basicData", "icon": "settings"}, + {"state": "item.card.tags", "icon": "icon-tags"}, + {"state": "item.card.tax", "icon": "icon-tax"}, + {"state": "item.card.niche", "icon": "place"}, + {"state": "item.card.botanical", "icon": "local_florist"}, + {"state": "item.card.itemBarcode", "icon": "icon-barcode"}, + {"state": "item.card.diary", "icon": "icon-transaction"}, + {"state": "item.card.last-entries", "icon": "icon-regentry"}, + {"state": "item.card.log", "icon": "history"} + ] + }, "keybindings": [ {"key": "a", "state": "item.index"} ], @@ -24,7 +30,7 @@ "state": "item", "abstract": true, "description": "Items", - "component": "ui-view" + "component": "vn-items" }, { "url": "/index?q", "state": "item.index", diff --git a/modules/item/front/tags/index.html b/modules/item/front/tags/index.html index 7958f9e1e..b7b61107d 100644 --- a/modules/item/front/tags/index.html +++ b/modules/item/front/tags/index.html @@ -19,7 +19,7 @@ data="tags" auto-load="true"> -
        + - + { afterAll(async done => { let validparams = {note: null}; - await app.models.Order.updateBasicData(validparams, orderId); + await app.models.Order.updateBasicData(orderId, validparams); done(); }); @@ -16,7 +16,7 @@ describe('Order updateBasicData', () => { let params = []; let orderConfirmed = 1; - await app.models.Order.updateBasicData(params, orderConfirmed) + await app.models.Order.updateBasicData(orderConfirmed, params) .catch(e => { error = e; }); @@ -30,7 +30,7 @@ describe('Order updateBasicData', () => { let params = []; let orderWithRows = 16; - await app.models.Order.updateBasicData(params, orderWithRows) + await app.models.Order.updateBasicData(orderWithRows, params) .catch(e => { error = e; }); @@ -38,17 +38,15 @@ describe('Order updateBasicData', () => { expect(error.toString()).toContain(`You can't make changes on the basic data of an confirmed order or with rows`); }); - it('should return an error if the current user is administrative and the isTaxDataChecked value is true BUT the params aint valid', async() => { - let error; + it('should skip invalid params', async() => { + let success; let invalidparams = {invalid: 'param for update'}; - await app.models.Order.updateBasicData(invalidparams, orderId) - .catch(e => { - error = e; - }); + await app.models.Order.updateBasicData(orderId, invalidparams) + .then(() => success = true); - expect(error.toString()).toContain(`You don't have enough privileges to do that`); + expect(success).toBeTruthy(); }); it('should update the client fiscal data and return the count if changes made', async() => { @@ -58,7 +56,7 @@ describe('Order updateBasicData', () => { expect(order.note).toEqual(null); - let result = await app.models.Order.updateBasicData(validparams, orderId); + let result = await app.models.Order.updateBasicData(orderId, validparams); expect(result.note).toEqual('test note'); }); diff --git a/modules/order/back/methods/order/updateBasicData.js b/modules/order/back/methods/order/updateBasicData.js index 6a8148a21..8f4393c04 100644 --- a/modules/order/back/methods/order/updateBasicData.js +++ b/modules/order/back/methods/order/updateBasicData.js @@ -1,22 +1,25 @@ let UserError = require('vn-loopback/util/user-error'); +let pick = require('object.pick'); module.exports = Self => { Self.remoteMethod('updateBasicData', { description: 'Updates basic data of an order', accessType: 'WRITE', - accepts: [{ - arg: 'data', - type: 'Object', - required: true, - description: 'Params to update', - http: {source: 'body'} - }, { - arg: 'id', - type: 'string', - required: true, - description: 'Model id', - http: {source: 'path'} - }], + accepts: [ + { + arg: 'id', + type: 'string', + required: true, + description: 'Model id', + http: {source: 'path'} + }, { + arg: 'data', + type: 'Object', + required: true, + description: 'Params to update', + http: {source: 'body'} + } + ], returns: { arg: 'order', type: 'Object', @@ -28,25 +31,25 @@ module.exports = Self => { } }); - Self.updateBasicData = async(params, id) => { - let order = await Self.app.models.Order.findById(id); - let orderRows = await Self.app.models.OrderRow.find({where: {orderFk: id}}); + Self.updateBasicData = async(id, params) => { + let models = Self.app.models; + + let order = await models.Order.findById(id); + let orderRows = await models.OrderRow.find({where: {orderFk: id}}); if (order.isConfirmed || orderRows.length != 0) throw new UserError(`You can't make changes on the basic data of an confirmed order or with rows`); - let validUpdateParams = [ + let updateParams = pick(params, [ 'clientFk', 'addressFk', 'landed', 'agencyModeFk', 'note', - ]; + ]); + if (Object.keys(updateParams).length) + await order.updateAttributes(updateParams); - for (const key in params) { - if (validUpdateParams.indexOf(key) === -1) - throw new UserError(`You don't have enough privileges to do that`); - } - return await order.updateAttributes(params); + return await order; }; }; diff --git a/modules/order/front/basic-data/index.html b/modules/order/front/basic-data/index.html index d79398299..eaa73a25c 100644 --- a/modules/order/front/basic-data/index.html +++ b/modules/order/front/basic-data/index.html @@ -12,7 +12,7 @@ form="form" save="patch"> - + { + $scope.$watch('$ctrl.selection', newValue => { if (newValue) { $scope.addressModel.where = {clientFk: newValue.id}; $scope.addressModel.refresh(); diff --git a/modules/order/front/card/index.html b/modules/order/front/card/index.html index 96aa94f6c..4f10c1728 100644 --- a/modules/order/front/card/index.html +++ b/modules/order/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/order/front/card/index.js b/modules/order/front/card/index.js index 9524a8ec4..e79167761 100644 --- a/modules/order/front/card/index.js +++ b/modules/order/front/card/index.js @@ -1,31 +1,35 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.order = {}; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ { - relation: 'agencyMode', scope: { + relation: 'agencyMode', + scope: { fields: ['name'] } - }, - { - relation: 'address', scope: { + }, { + relation: 'address', + scope: { fields: ['nickname'] } - }, - { - relation: 'rows', scope: { + }, { + relation: 'rows', + scope: { fields: ['id'] } - }, - { + }, { relation: 'client', scope: { - fields: ['salesPersonFk', 'name', 'isActive', 'isFreezed', 'isTaxDataChecked'], + fields: [ + 'salesPersonFk', + 'name', + 'isActive', + 'isFreezed', + 'isTaxDataChecked' + ], include: { relation: 'salesPerson', scope: { @@ -42,40 +46,18 @@ class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - let query = `Orders/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data && res.data.rows) { - if (res.data.rows.length == 0) - delete res.data.rows; - this.order = res.data; - this.getTotal(); - } - }); - } - - reload() { - this.getCard(); - } - - getTotal() { - let query = `Orders/${this.$state.params.id}/getTotal`; - this.$http.get(query).then(res => { - if (res.data) - this.order.total = res.data; + this.$q.all([ + this.$http.get(`Orders/${this.$params.id}`, {filter}) + .then(res => this.order = res.data), + this.$http.get(`Orders/${this.$params.id}/getTotal`) + .then(res => ({total: res.data})) + ]).then(res => { + this.order = Object.assign.apply(null, res); }); } } -Controller.$inject = ['$http', '$state']; - ngModule.component('vnOrderCard', { template: require('./index.html'), controller: Controller diff --git a/modules/order/front/card/index.spec.js b/modules/order/front/card/index.spec.js index a962ba5ec..19d070920 100644 --- a/modules/order/front/card/index.spec.js +++ b/modules/order/front/card/index.spec.js @@ -2,61 +2,29 @@ import './index.js'; describe('Order', () => { describe('Component vnOrderCard', () => { - let $scope; let controller; let $httpBackend; - let $state; + let data = {id: 1, name: 'fooName'}; + let total = 10.5; beforeEach(ngModule('order')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $scope.card = {createOrder: () => {}}; - $state = {params: {id: 1}}; - controller = $componentController('vnOrderCard', {$scope, $state}); + + let $element = angular.element('
        '); + controller = $componentController('vnOrderCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Orders/:id').respond(data); + $httpBackend.whenRoute('GET', 'Orders/:id/getTotal').respond(200, total); })); - describe('getCard()', () => { - it(`should make a query, save the data in order and call get order if the response has data`, () => { - spyOn(controller, 'getTotal'); - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Orders/${controller.$state.params.id}?filter=${json}`).respond({rows: [1, 2, 3]}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and total, merge them, and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.order).toEqual({rows: [1, 2, 3]}); - expect(controller.getTotal).toHaveBeenCalledWith(); - }); - - it(`should make a query and not call getTotal if the response is not defined`, () => { - spyOn(controller, 'getTotal'); - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Orders/${controller.$state.params.id}?filter=${json}`).respond(undefined); - controller.getCard(); - $httpBackend.flush(); - - expect(controller.order).toEqual({}); - expect(controller.getTotal).not.toHaveBeenCalledWith(); - }); - }); - - describe('getTotal()', () => { - it(`should make a query and save the data in order.total`, () => { - $httpBackend.expectGET(`Orders/${controller.$state.params.id}/getTotal`).respond('20M'); - controller.getTotal(); - $httpBackend.flush(); - - expect(controller.order.total).toEqual('20M'); - }); - - it(`should make a query and not save the respones if is not defined`, () => { - $httpBackend.expectGET(`Orders/${controller.$state.params.id}/getTotal`).respond(); - controller.getTotal(); - $httpBackend.flush(); - - expect(controller.order.total).toEqual(undefined); - }); + expect(controller.order).toEqual(Object.assign({}, data, {total})); }); }); }); diff --git a/modules/order/front/catalog/index.html b/modules/order/front/catalog/index.html index 37ef1fd90..8002c2b10 100644 --- a/modules/order/front/catalog/index.html +++ b/modules/order/front/catalog/index.html @@ -7,8 +7,7 @@ on-data-change="$ctrl.onDataChange()"> + model="model">
        diff --git a/modules/order/front/create/card.js b/modules/order/front/create/card.js index 74d1d79c8..7f1bb6133 100644 --- a/modules/order/front/create/card.js +++ b/modules/order/front/create/card.js @@ -78,16 +78,16 @@ class Controller { } getAvailableAgencies() { - this.order.agencyModeFk = null; - if (this.order.landed && this.order.addressFk) { - let filter = { - addressFk: this.order.addressFk, - landed: this.order.landed - }; - this.$http.get(`Agencies/landsThatDay`, {params: {filter}}) - .then(res => { - this._availableAgencies = res.data; - }); + let order = this.order; + order.agencyModeFk = null; + + let params = { + addressFk: order.addressFk, + landed: order.landed + }; + if (params.landed && params.addressFk) { + this.$http.get(`Agencies/landsThatDay`, {params}) + .then(res => this._availableAgencies = res.data); } } diff --git a/modules/order/front/create/index.html b/modules/order/front/create/index.html index d557bd21a..11de7ff6c 100644 --- a/modules/order/front/create/index.html +++ b/modules/order/front/create/index.html @@ -1,11 +1,9 @@ -
        -
        - - - - - - - -
        +
        + + + + + + +
        \ No newline at end of file diff --git a/modules/order/front/descriptor/index.js b/modules/order/front/descriptor/index.js index ec65d3b4c..0d8a1e14a 100644 --- a/modules/order/front/descriptor/index.js +++ b/modules/order/front/descriptor/index.js @@ -14,6 +14,7 @@ class Controller { set order(value) { this._order = value; + if (!value) return; if (value.isConfirmed) { this._quicklinks = { diff --git a/modules/order/front/filter/index.js b/modules/order/front/filter/index.js index 71e217087..03dd0fa33 100644 --- a/modules/order/front/filter/index.js +++ b/modules/order/front/filter/index.js @@ -24,8 +24,7 @@ class Controller { * @param {Object} value - Order data */ set order(value) { - if (!value.id || this._order) return; - + if (!value || !value.id || this._order) return; this._order = value; this.$.$applyAsync(() => { diff --git a/modules/order/front/index.js b/modules/order/front/index.js index 2232a7501..0d8d0d686 100644 --- a/modules/order/front/index.js +++ b/modules/order/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './card'; import './descriptor'; diff --git a/modules/order/front/index/index.html b/modules/order/front/index/index.html index a6ec84392..20dd4a231 100644 --- a/modules/order/front/index/index.html +++ b/modules/order/front/index/index.html @@ -5,74 +5,71 @@ data="orders" order="landed DESC, clientFk"> -
        - - - + + + + + + + + Id + Client + Sales person + Confirmed + Created from + Created + Landed + Company + Total + + + + + {{::order.id}} + + + {{::order.clientName}} + + + + + {{::order.workerNickname | dashIfEmpty}} + + + + + + + {{::order.sourceApp}} + {{::order.created | date: 'dd/MM/yyyy HH:mm'}} + {{::order.landed | date:'dd/MM/yyyy'}} + {{::order.companyCode}} + {{::order.total | currency: 'EUR': 2 | dashIfEmpty}} + + + + + + + - - - - - - Id - Client - Sales person - Confirmed - Created from - Created - Landed - Company - Total - - - - - {{::order.id}} - - - {{::order.clientName}} - - - - - {{::order.workerNickname | dashIfEmpty}} - - - - - - - {{::order.sourceApp}} - {{::order.created | date: 'dd/MM/yyyy HH:mm'}} - {{::order.landed | date:'dd/MM/yyyy'}} - {{::order.companyCode}} - {{::order.total | currency: 'EUR': 2 | dashIfEmpty}} - - - - - - - - - -
        - + + -
        @@ -28,7 +24,8 @@ Shipped Quantity Price - + Amount + @@ -59,11 +56,14 @@ {{::row.price | currency: 'EUR':2}} - + + {{::row.price * row.quantity | currency: 'EUR':2}} + + @@ -84,7 +84,7 @@ \ No newline at end of file diff --git a/modules/order/front/line/index.js b/modules/order/front/line/index.js index 14e29c5c5..149251c87 100644 --- a/modules/order/front/line/index.js +++ b/modules/order/front/line/index.js @@ -1,16 +1,8 @@ import ngModule from '../module'; +import Section from 'salix/components/section'; import './style.scss'; -class Controller { - constructor($scope, $state, $http, vnApp, $translate) { - this.$scope = $scope; - this.vnApp = vnApp; - this.$translate = $translate; - this.$state = $state; - this.$http = $http; - this.idsToRemove = []; - } - +class Controller extends Section { $onInit() { this.getRows(); } @@ -24,82 +16,56 @@ class Controller { return this._order; } + get subtotal() { + return this.order ? this.order.total - this.VAT : 0; + } + getRows() { let filter = { - where: {orderFk: this.$state.params.id}, - include: [{ - relation: 'item' - }, - {relation: 'warehouse'}] + where: {orderFk: this.$params.id}, + include: [ + {relation: 'item'}, + {relation: 'warehouse'} + ] }; - filter = encodeURIComponent(JSON.stringify(filter)); - let query = `OrderRows?filter=${filter}`; - - this.$http.get(query).then(res => { - this.rows = res.data; - }); + this.$http.get(`OrderRows`, {filter}) + .then(res => this.rows = res.data); } getVAT() { - let query = `Orders/${this.$state.params.id}/getVAT`; + this.$http.get(`Orders/${this.$params.id}/getVAT`) + .then(res => this.VAT = res.data); + } - this.$http.get(query).then(res => { - this.VAT = res.data; + deleteRow(index) { + let [row] = this.rows.splice(index, 1); + let params = { + rows: [row.id], + actualOrderId: this.$params.id + }; + return this.$http.post(`OrderRows/removes`, params).then(() => { + this.card.reload(); + this.vnApp.showSuccess(this.$t('Data saved!')); }); } - get subtotal() { - return this.order.total - this.VAT || 0; - } - - showDeleteRow(index) { - this.lineIdToRemove = index; - this.$scope.deleteRow.show(); - } - - deleteRow(response) { - if (response == 'accept') { - let [lineRemoved] = this.rows.splice(this.lineIdToRemove, 1); - this.idsToRemove.push(lineRemoved.id); - let params = { - rows: this.idsToRemove, - actualOrderId: this.$state.params.id - }; - let query = `OrderRows/removes`; - - this.$http.post(query, params).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); - }); - this.$scope.watcher.updateOriginalData(); - this.getVAT(); - this.card.reload(); - } - this.lineIdToRemove = undefined; - } - - // Item Descriptor showDescriptor(event, itemFk) { - this.$scope.descriptor.itemFk = itemFk; - this.$scope.descriptor.parent = event.target; - this.$scope.descriptor.show(); - } - - onDescriptorLoad() { - this.$scope.popover.relocate(); + let descriptor = this.$.descriptor; + descriptor.itemFk = itemFk; + descriptor.parent = event.target; + descriptor.show(); } save() { - let query = `Orders/${this.order.id}/confirm`; - - this.$http.post(query).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Order confirmed')); - this.$state.go(`ticket.index`, {q: JSON.stringify({clientFk: this.order.clientFk})}); + this.$http.post(`Orders/${this.$params.id}/confirm`).then(() => { + this.vnApp.showSuccess(this.$t('Order confirmed')); + this.$state.go(`ticket.index`, { + q: JSON.stringify({clientFk: this.order.clientFk}) + }); }); } } -Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate']; - ngModule.component('vnOrderLine', { template: require('./index.html'), controller: Controller, diff --git a/modules/order/front/line/index.spec.js b/modules/order/front/line/index.spec.js index c3a01bbe0..543ffeedc 100644 --- a/modules/order/front/line/index.spec.js +++ b/modules/order/front/line/index.spec.js @@ -1,5 +1,4 @@ import './index.js'; -import watcher from 'core/mocks/watcher'; describe('Order', () => { describe('Component vnOrderLine', () => { @@ -7,83 +6,60 @@ describe('Order', () => { let controller; let $httpBackend; + let rows = [ + { + quantity: 4, + price: 10.5 + }, { + quantity: 3, + price: 2.4 + } + ]; + beforeEach(ngModule('order')); beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_) => { $state = _$state_; - $state = {params: {id: 1}}; $httpBackend = _$httpBackend_; - controller = $componentController('vnOrderLine', {$state}); - controller.rows = [{id: 1}]; - controller.$scope.watcher = watcher; - controller.$scope.popover = {relocate: () => {}}; - controller.$scope.descriptor = {show: () => {}}; - controller.vnApp = {showSuccess: () => {}}; - controller.card = {reload: () => {}}; + + $state.params.id = 1; + $httpBackend.whenRoute('GET', `OrderRows`).respond(rows); + $httpBackend.whenRoute('GET', `Orders/:id/getVAT`).respond(200, 10.5); + + let $element = angular.element('
        '); + controller = $componentController('vnOrderLine', {$element}); })); describe('getRows()', () => { it('should make a query to get the rows of a given order', () => { - let filter = { - where: {orderFk: controller.$state.params.id}, - include: [{ - relation: 'item' - }, - {relation: 'warehouse'}] - }; - - filter = encodeURIComponent(JSON.stringify(filter)); - $httpBackend.expectGET(`OrderRows?filter=${filter}`).respond({data: [{id: 1}]}); controller.getRows(); $httpBackend.flush(); + + expect(controller.rows).toBeDefined(); }); }); describe('getVAT()', () => { it('should make a query to get the VAT of a given order', () => { - $httpBackend.expectGET(`Orders/1/getVAT`).respond({data: {tax: 3}}); controller.getVAT(); $httpBackend.flush(); + + expect(controller.VAT).toBeDefined(); }); }); describe('deleteRow()', () => { it('should remove a row from rows and add save the data if the response is accept', () => { - expect(controller.rows.length).toBe(1); - spyOn(controller.vnApp, 'showSuccess'); - spyOn(controller, 'getVAT'); - spyOn(controller.card, 'reload'); - controller.deleteRow('accept'); - - $httpBackend.expectPOST(`OrderRows/removes`).respond(); + controller.getRows(); $httpBackend.flush(); - expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + controller.card = {reload: jasmine.createSpy('reload')}; + $httpBackend.expectPOST(`OrderRows/removes`).respond(); + controller.deleteRow(0); + $httpBackend.flush(); + + expect(controller.rows.length).toBe(1); expect(controller.card.reload).toHaveBeenCalledWith(); - expect(controller.rows.length).toBe(0); - expect(controller.lineIdToRemove).toBe(undefined); - }); - }); - - describe('showDescriptor()', () => { - it('should set $scope.descriptor.itemFk, $scope.descriptor.parent and call $scope.descriptor.show()', () => { - let event = {target: 1}; - let itemFk = 1; - spyOn(controller.$scope.descriptor, 'show'); - controller.showDescriptor(event, itemFk); - - expect(controller.$scope.descriptor.itemFk).toBe(1); - expect(controller.$scope.descriptor.parent).toBe(1); - expect(controller.$scope.descriptor.show).toHaveBeenCalledWith(); - }); - }); - - describe('onDescriptorLoad()', () => { - it('should call $scope.popover.relocate()', () => { - spyOn(controller.$scope.popover, 'relocate'); - controller.onDescriptorLoad(); - - expect(controller.$scope.popover.relocate).toHaveBeenCalledWith(); }); }); }); diff --git a/modules/order/front/main/index.html b/modules/order/front/main/index.html new file mode 100644 index 000000000..09d498599 --- /dev/null +++ b/modules/order/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/order/front/main/index.js b/modules/order/front/main/index.js new file mode 100644 index 000000000..7513d2144 --- /dev/null +++ b/modules/order/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Order extends ModuleMain {} + +ngModule.vnComponent('vnOrder', { + controller: Order, + template: require('./index.html') +}); diff --git a/modules/order/front/routes.json b/modules/order/front/routes.json index 0f610169d..86eda488f 100644 --- a/modules/order/front/routes.json +++ b/modules/order/front/routes.json @@ -4,18 +4,23 @@ "icon": "icon-basket", "validations": true, "dependencies": ["worker", "item", "ticket"], - "menu": [ - {"state": "order.card.basicData", "icon": "settings"}, - {"state": "order.card.catalog", "icon": "icon-basket"}, - {"state": "order.card.volume", "icon": "icon-volume"}, - {"state": "order.card.line", "icon": "icon-lines"} - ], + "menus": { + "main": [ + {"state": "order.index", "icon": "icon-basket"} + ], + "card": [ + {"state": "order.card.basicData", "icon": "settings"}, + {"state": "order.card.catalog", "icon": "icon-basket"}, + {"state": "order.card.volume", "icon": "icon-volume"}, + {"state": "order.card.line", "icon": "icon-lines"} + ] + }, "routes": [ { "url": "/order", "state": "order", "abstract": true, - "component": "ui-view", + "component": "vn-order", "description": "Orders" }, { "url": "/index?q", diff --git a/modules/route/front/basic-data/index.html b/modules/route/front/basic-data/index.html index b63970bcc..dcdfd37d7 100644 --- a/modules/route/front/basic-data/index.html +++ b/modules/route/front/basic-data/index.html @@ -5,7 +5,7 @@ form="form" save="patch"> - + - - - - -
        - + + + + + diff --git a/modules/route/front/card/index.js b/modules/route/front/card/index.js index 76471002a..0760259d3 100644 --- a/modules/route/front/card/index.js +++ b/modules/route/front/card/index.js @@ -1,11 +1,9 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.route = null; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { fields: [ 'id', 'workerFk', @@ -22,22 +20,18 @@ export default class Controller { 'cost', 'zoneFk' ], - - where: {id: $stateParams.id}, include: [ { relation: 'agencyMode', scope: { fields: ['id', 'name'] } - }, - { + }, { relation: 'vehicle', scope: { fields: ['id', 'm3'] } - }, - { + }, { relation: 'zone', scope: { fields: ['id', 'name'] @@ -63,24 +57,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - this.$http.get(`Routes?filter=${json}`).then(response => { - this.route = response.data[0]; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Routes/${this.$params.id}`, {filter}) + .then(res => this.route = res.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnRouteCard', { template: require('./index.html'), diff --git a/modules/route/front/create/index.html b/modules/route/front/create/index.html index 97747fca5..7f6a1f600 100644 --- a/modules/route/front/create/index.html +++ b/modules/route/front/create/index.html @@ -5,56 +5,54 @@ form="form" save="post"> -
        - - - - - - - - - - - - - - - - - - - - - - - - -
        +
        + + + + + + + + + + + + + + + + + + + + + + +
        diff --git a/modules/route/front/descriptor/index.js b/modules/route/front/descriptor/index.js index 402b9102f..cfa718855 100644 --- a/modules/route/front/descriptor/index.js +++ b/modules/route/front/descriptor/index.js @@ -70,7 +70,7 @@ class Controller { let url = `Routes/${this.route.id}/updateVolume`; this.$http.post(url).then(() => { this.vnApp.showSuccess(this.$translate.instant('Volume updated')); - this.card.reload(); + if (this.card) this.card.reload(); }); } } @@ -85,7 +85,7 @@ ngModule.component('vnRouteDescriptor', { quicklinks: '<' }, require: { - card: '^vnRouteCard' + card: '^?vnRouteCard' }, controller: Controller }); diff --git a/modules/route/front/index.js b/modules/route/front/index.js index a6530c6b8..ce8e80e95 100644 --- a/modules/route/front/index.js +++ b/modules/route/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './descriptor'; diff --git a/modules/route/front/index/index.html b/modules/route/front/index/index.html index 9852eb880..c01112928 100644 --- a/modules/route/front/index/index.html +++ b/modules/route/front/index/index.html @@ -6,63 +6,55 @@ order="created DESC" auto-load="true"> - + diff --git a/modules/route/front/index/index.js b/modules/route/front/index/index.js index 823ef8bf4..8fdde810c 100644 --- a/modules/route/front/index/index.js +++ b/modules/route/front/index/index.js @@ -1,30 +1,15 @@ import ngModule from '../module'; export default class Controller { - constructor($scope, vnToken, vnConfig) { - this.accessToken = vnToken.token; + constructor($scope, vnToken) { this.$ = $scope; - this.vnConfig = vnConfig; - - this.setDefaultFilter(); - } - - setDefaultFilter() { - let to = new Date(); - to.setDate(to.getDate() + 1); - to.setHours(0, 0, 0, 0); - - let from = new Date(); - from.setHours(0, 0, 0, 0); - - this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk}; + this.accessToken = vnToken.token; } showWorkerDescriptor(event, workerFk) { if (event.defaultPrevented) return; - event.preventDefault(); - event.stopImmediatePropagation(); + event.stopPropagation(); this.selectedWorker = workerFk; this.$.workerDescriptor.parent = event.target; @@ -32,10 +17,12 @@ export default class Controller { } preview(event, route) { + if (event.defaultPrevented) return; + event.preventDefault(); + event.stopPropagation(); + this.routeSelected = route; this.$.summary.show(); - event.preventDefault(); - event.stopImmediatePropagation(); } onSearch(params) { @@ -45,8 +32,7 @@ export default class Controller { this.$.model.clear(); } } - -Controller.$inject = ['$scope', 'vnToken', 'vnConfig']; +Controller.$inject = ['$scope', 'vnToken']; ngModule.component('vnRouteIndex', { template: require('./index.html'), diff --git a/modules/route/front/index/style.scss b/modules/route/front/index/style.scss deleted file mode 100644 index d8c24a482..000000000 --- a/modules/route/front/index/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "./variables"; - -vn-route-index .index { - max-width: $width-lg; -} \ No newline at end of file diff --git a/modules/route/front/main/index.html b/modules/route/front/main/index.html new file mode 100644 index 000000000..f6e097b24 --- /dev/null +++ b/modules/route/front/main/index.html @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/route/front/main/index.js b/modules/route/front/main/index.js new file mode 100644 index 000000000..618989748 --- /dev/null +++ b/modules/route/front/main/index.js @@ -0,0 +1,26 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Route extends ModuleMain { + constructor($element, $, vnConfig) { + super($element, $); + this.vnConfig = vnConfig; + } + + $postLink() { + let to = new Date(); + to.setDate(to.getDate() + 1); + to.setHours(0, 0, 0, 0); + + let from = new Date(); + from.setHours(0, 0, 0, 0); + + this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk}; + } +} +Route.$inject = ['$element', '$scope', 'vnConfig']; + +ngModule.vnComponent('vnRoute', { + controller: Route, + template: require('./index.html') +}); diff --git a/modules/route/front/routes.json b/modules/route/front/routes.json index baffe9082..beb444abc 100644 --- a/modules/route/front/routes.json +++ b/modules/route/front/routes.json @@ -4,16 +4,22 @@ "icon": "icon-delivery", "validations" : true, "dependencies": ["client", "worker", "ticket"], - "menu": [ - {"state": "route.card.basicData", "icon": "settings"}, - {"state": "route.card.tickets", "icon": "icon-ticket"}, - {"state": "route.card.log", "icon": "history"}], + "menus": { + "main": [ + {"state": "route.index", "icon": "icon-delivery"} + ], + "card": [ + {"state": "route.card.basicData", "icon": "settings"}, + {"state": "route.card.tickets", "icon": "icon-ticket"}, + {"state": "route.card.log", "icon": "history"} + ] + }, "routes": [ { "url": "/route", "state": "route", "abstract": true, - "component": "ui-view", + "component": "vn-route", "description": "Routes", "acl": ["employee"] }, { diff --git a/modules/ticket/front/basic-data/index.html b/modules/ticket/front/basic-data/index.html index 3f87cc76d..f313110b3 100644 --- a/modules/ticket/front/basic-data/index.html +++ b/modules/ticket/front/basic-data/index.html @@ -9,5 +9,4 @@ on-step-end="$ctrl.onSubmit()"> - diff --git a/modules/ticket/front/basic-data/step-two/index.html b/modules/ticket/front/basic-data/step-two/index.html index ef5b09d4b..04cb7f212 100644 --- a/modules/ticket/front/basic-data/step-two/index.html +++ b/modules/ticket/front/basic-data/step-two/index.html @@ -1,40 +1,40 @@ -
        - - - - - Item - Description - Quantity - Price (PPU) - New price (PPU) - Price difference - - - - - {{("000000"+sale.itemFk).slice(-6)}} - - - - - {{::sale.quantity}} - {{::sale.price | currency: 'EUR': 2}} - {{::sale.component.newPrice | currency: 'EUR': 2}} - {{::sale.component.difference | currency: 'EUR': 2}} - - - - - - {{$ctrl.totalPrice | currency: 'EUR': 2}} - {{$ctrl.totalNewPrice | currency: 'EUR': 2}} - {{$ctrl.totalPriceDifference | currency: 'EUR': 2}} - - - - -
        + + + + + Item + Description + Quantity + Price (PPU) + New price (PPU) + Price difference + + + + + {{("000000"+sale.itemFk).slice(-6)}} + + + + + {{::sale.quantity}} + {{::sale.price | currency: 'EUR': 2}} + {{::sale.component.newPrice | currency: 'EUR': 2}} + {{::sale.component.difference | currency: 'EUR': 2}} + + + + + + + + {{$ctrl.totalPrice | currency: 'EUR': 2}} + {{$ctrl.totalNewPrice | currency: 'EUR': 2}} + {{$ctrl.totalPriceDifference | currency: 'EUR': 2}} + + + + diff --git a/modules/ticket/front/card/index.html b/modules/ticket/front/card/index.html index c55064ce6..a7c70c484 100644 --- a/modules/ticket/front/card/index.html +++ b/modules/ticket/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/ticket/front/card/index.js b/modules/ticket/front/card/index.js index b805c3803..131968d65 100644 --- a/modules/ticket/front/card/index.js +++ b/modules/ticket/front/card/index.js @@ -1,18 +1,23 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ - {relation: 'warehouse', scope: {fields: ['name']}}, - {relation: 'invoiceOut', scope: {fields: ['id']}}, {relation: 'address'}, {relation: 'ship'}, - {relation: 'agencyMode', scope: {fields: ['name']}}, {relation: 'stowaway'}, { + relation: 'warehouse', + scope: {fields: ['name']} + }, { + relation: 'invoiceOut', + scope: {fields: ['id']} + }, { + relation: 'agencyMode', + scope: {fields: ['name']} + }, { relation: 'client', scope: { fields: [ @@ -37,8 +42,7 @@ class Controller { } }, }, - }, - { + }, { relation: 'state', scope: { fields: ['stateFk'], @@ -50,37 +54,18 @@ class Controller { }, ], }; + + this.$http.get(`Tickets/${this.$params.id}`, {filter}) + .then(res => this.onData(res.data)); } - $onInit() { - this.getCard(); - } - - getCard() { - const json = encodeURIComponent(JSON.stringify(this.filter)); - const query = `Tickets/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) { - this.ticket = res.data; - this.getDebt(res.data.client.id); - } - }); - } - - getDebt(id) { - const query = `Clients/${id}/getDebt`; - this.$http.get(query).then(res => { - if (res.data) - this.ticket.client.debt = res.data.debt; - }); - } - reload() { - this.getCard(); + onData(data) { + this.ticket = data; + this.$http.get(`Clients/${data.client.id}/getDebt`) + .then(res => this.ticket.client.debt = res.data.debt); } } -Controller.$inject = ['$http', '$state']; - ngModule.component('vnTicketCard', { template: require('./index.html'), controller: Controller diff --git a/modules/ticket/front/card/index.spec.js b/modules/ticket/front/card/index.spec.js index 9df76aaff..33757860c 100644 --- a/modules/ticket/front/card/index.spec.js +++ b/modules/ticket/front/card/index.spec.js @@ -3,34 +3,31 @@ import './index'; describe('Ticket', () => { describe('Component vnTicketCard', () => { let controller; - let $state; let $httpBackend; + let data = { + id: 1, + client: {name: 'fooName'} + }; + let client = {debt: 10.5}; beforeEach(ngModule('ticket')); - beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_) => { - $state = _$state_; - $state.params.id = 1; + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - controller = $componentController('vnTicketCard', {$state}); + + let $element = angular.element('
        '); + controller = $componentController('vnTicketCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Tickets/:id').respond(data); + $httpBackend.whenRoute('GET', 'Clients/:id/getDebt').respond(client); })); - describe('getCard()', () => { - it('should perform a GET query and define the ticket property on controller', () => { - let filter = controller.filter; + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - filter = encodeURIComponent(JSON.stringify(filter)); - - $httpBackend.when('GET', `Tickets/1?filter=${filter}`).respond({id: 1, client: {id: 1}}); - $httpBackend.expect('GET', `Tickets/1?filter=${filter}`); - $httpBackend.when('GET', `Clients/1/getDebt`).respond(); - $httpBackend.expect('GET', `Clients/1/getDebt`); - controller.getCard(); - $httpBackend.flush(); - - expect(controller.ticket).toBeDefined(); - expect(controller.ticket.id).toEqual(1); - }); + expect(controller.ticket).toMatchObject(data); }); }); }); diff --git a/modules/ticket/front/create/card.js b/modules/ticket/front/create/card.js index 7ee4c1b4e..629647416 100644 --- a/modules/ticket/front/create/card.js +++ b/modules/ticket/front/create/card.js @@ -86,14 +86,18 @@ class Controller { } getAvailableAgencies() { - if (this.ticket.warehouseFk && this.ticket.addressFk && this.ticket.landed && this.ticket.clientFk) { - this.ticket.agencyModeFk = null; - let filter = {warehouseFk: this.ticket.warehouseFk, addressFk: this.ticket.addressFk, landed: this.ticket.landed}; - filter = encodeURIComponent(JSON.stringify(filter)); - let query = `Agencies/getAgenciesWithWarehouse?filter=${filter}`; - this.$http.get(query).then(res => { - this._availableAgencies = res.data[0]; - }); + let ticket = this.ticket; + let params = { + warehouseFk: ticket.warehouseFk, + addressFk: ticket.addressFk, + landed: ticket.landed + }; + + if (params.warehouseFk && params.addressFk && params.landed) { + ticket.agencyModeFk = null; + + this.$http.get(`Agencies/getAgenciesWithWarehouse`, {params}) + .then(res => this._availableAgencies = res.data); } } diff --git a/modules/ticket/front/create/index.html b/modules/ticket/front/create/index.html index 97980d08a..3ec562752 100644 --- a/modules/ticket/front/create/index.html +++ b/modules/ticket/front/create/index.html @@ -1,11 +1,9 @@ -
        -
        - - - - - - - -
        +
        + + + + + + +
        \ No newline at end of file diff --git a/modules/ticket/front/dms/create/index.html b/modules/ticket/front/dms/create/index.html index 8c0b934b5..8e6af2b87 100644 --- a/modules/ticket/front/dms/create/index.html +++ b/modules/ticket/front/dms/create/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        diff --git a/modules/ticket/front/dms/edit/index.html b/modules/ticket/front/dms/edit/index.html index a5e485dce..cf9af46d0 100644 --- a/modules/ticket/front/dms/edit/index.html +++ b/modules/ticket/front/dms/edit/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        diff --git a/modules/ticket/front/index.js b/modules/ticket/front/index.js index f9c1e9d1c..f8433b5fe 100644 --- a/modules/ticket/front/index.js +++ b/modules/ticket/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './card'; diff --git a/modules/ticket/front/index/index.html b/modules/ticket/front/index/index.html index 1680d94d5..b303c3ae7 100644 --- a/modules/ticket/front/index/index.html +++ b/modules/ticket/front/index/index.html @@ -6,146 +6,142 @@ data="tickets" order="shipped DESC, zoneHour ASC, zoneMinute ASC, clientFk"> - - + + diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js index 2a2f3a238..9cf1c5483 100644 --- a/modules/ticket/front/index/index.js +++ b/modules/ticket/front/index/index.js @@ -10,13 +10,14 @@ export default class Controller { this.$state = $state; this.selectedTicket = null; this.moreOptions = [ - {callback: () => { - this.$state.go('ticket.weekly.index'); - }, name: 'Weekly tickets', always: true}, - {callback: () => { - this.setBalanceCreateDialog(); - this.$.balanceCreateDialog.show(); - }, name: 'Payment on account...', always: true} + { + name: 'Payment on account...', + always: true, + callback: () => { + this.setBalanceCreateDialog(); + this.$.balanceCreateDialog.show(); + } + } ]; } diff --git a/modules/ticket/front/main/index.html b/modules/ticket/front/main/index.html new file mode 100644 index 000000000..7e058f45d --- /dev/null +++ b/modules/ticket/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/ticket/front/main/index.js b/modules/ticket/front/main/index.js new file mode 100644 index 000000000..d6100daa3 --- /dev/null +++ b/modules/ticket/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Ticket extends ModuleMain {} + +ngModule.vnComponent('vnTicket', { + controller: Ticket, + template: require('./index.html') +}); diff --git a/modules/ticket/front/note/index.html b/modules/ticket/front/note/index.html index be2e683ce..617cf2f7d 100644 --- a/modules/ticket/front/note/index.html +++ b/modules/ticket/front/note/index.html @@ -16,7 +16,7 @@ data="observations" form="form"> -
        + diff --git a/modules/ticket/front/request/create/index.html b/modules/ticket/front/request/create/index.html index 9d7395312..46d96ca44 100644 --- a/modules/ticket/front/request/create/index.html +++ b/modules/ticket/front/request/create/index.html @@ -6,7 +6,7 @@ save="post"> -
        +
        - + @@ -294,7 +294,7 @@

        Destination ticket

        diff --git a/modules/ticket/front/services/index.js b/modules/ticket/front/services/index.js index c3d683d03..e45381940 100644 --- a/modules/ticket/front/services/index.js +++ b/modules/ticket/front/services/index.js @@ -17,19 +17,11 @@ class Controller { } getDefaultTaxClass() { - let config = {params: { - filter: { - where: { - code: 'G' - } - } - }}; - - let query = 'TaxClasses/findOne'; - this.$http.get(query, config).then(res => { - if (res.data) - this.defaultTaxClass = res.data; - }); + let filter = { + where: {code: 'G'} + }; + this.$http.get('TaxClasses/findOne', {filter}) + .then(res => this.defaultTaxClass = res.data); } add() { diff --git a/modules/ticket/front/services/index.spec.js b/modules/ticket/front/services/index.spec.js index b3c4adba5..b94b3e57f 100644 --- a/modules/ticket/front/services/index.spec.js +++ b/modules/ticket/front/services/index.spec.js @@ -19,17 +19,12 @@ describe('Ticket component vnTicketService', () => { describe('getDefaultTaxClass', () => { it('should set the default tax class in the controller', () => { - const config = { - filter: { - where: { - code: 'G' - } - } - }; - let serializedParams = $httpParamSerializer(config); - - $httpBackend.when('GET', `TaxClasses/findOne?${serializedParams}`).respond({id: 4000, name: 'Whatever', code: 'GG'}); - $httpBackend.expect('GET', `TaxClasses/findOne?${serializedParams}`); + $httpBackend.whenRoute('GET', `TaxClasses/findOne`) + .respond({ + id: 4000, + name: 'Whatever', + code: 'GG' + }); controller.getDefaultTaxClass(); $httpBackend.flush(); diff --git a/modules/ticket/front/tracking/edit/index.html b/modules/ticket/front/tracking/edit/index.html index 61647f669..3a1b13e50 100644 --- a/modules/ticket/front/tracking/edit/index.html +++ b/modules/ticket/front/tracking/edit/index.html @@ -4,7 +4,7 @@ data="$ctrl.params" form="form"> - + -
        - - - + + + + + + + + + + + + Ticket ID + Client + Weekday + Warehouse + Salesperson + + + + + + + + {{weekly.ticketFk}} + + + + + {{::weekly.clientName}} + + + + + + + {{::weekly.warehouseName}} + + + {{::weekly.nickName}} + + + + + + + + + - - - - - - Ticket ID - Client - Weekday - Warehouse - Salesperson - - - - - - - - {{weekly.ticketFk}} - - - - - {{::weekly.clientName}} - - - - - - - {{::weekly.warehouseName}} - - - {{::weekly.nickName}} - - - - - - - - - - - -
        + @@ -96,4 +93,4 @@ on-response="$ctrl.returnDialog($response)" question="This ticket will be removed from weekly tickets! Continue anyway?" message="You are going to delete this weekly ticket"> - \ No newline at end of file + diff --git a/modules/travel/front/basic-data/index.html b/modules/travel/front/basic-data/index.html index 557a243c7..85a5fad2b 100644 --- a/modules/travel/front/basic-data/index.html +++ b/modules/travel/front/basic-data/index.html @@ -5,7 +5,7 @@ form="form" save="patch"> - + - - - - -
        - + + + + + diff --git a/modules/travel/front/card/index.js b/modules/travel/front/card/index.js index 40d3c0f51..c3ad41702 100644 --- a/modules/travel/front/card/index.js +++ b/modules/travel/front/card/index.js @@ -1,20 +1,16 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.travel = null; - this.filter = { - where: {id: $stateParams.id}, +class Controller extends ModuleCard { + reload() { + let filter = { include: [ { relation: 'warehouseIn', scope: { fields: ['id', 'name'] } - }, - { + }, { relation: 'warehouseOut', scope: { fields: ['id', 'name'] @@ -22,24 +18,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - const params = {filter: this.filter}; - this.$http.get(`Travels/${this.$stateParams.id}`, {params}).then(response => { - this.travel = response.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Travels/${this.$params.id}`, {filter}) + .then(response => this.travel = response.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnTravelCard', { template: require('./index.html'), diff --git a/modules/travel/front/descriptor/index.js b/modules/travel/front/descriptor/index.js index 0739c4485..187a7ed8f 100644 --- a/modules/travel/front/descriptor/index.js +++ b/modules/travel/front/descriptor/index.js @@ -14,7 +14,7 @@ ngModule.component('vnTravelDescriptor', { travel: '<' }, require: { - card: '^vnTravelCard' + card: '^?vnTravelCard' }, controller: Controller }); diff --git a/modules/travel/front/index.js b/modules/travel/front/index.js index 1017a431b..d9e1fa07e 100644 --- a/modules/travel/front/index.js +++ b/modules/travel/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './descriptor'; diff --git a/modules/travel/front/index/index.html b/modules/travel/front/index/index.html index dc24e2738..1989a1262 100644 --- a/modules/travel/front/index/index.html +++ b/modules/travel/front/index/index.html @@ -1,65 +1,58 @@ - - + diff --git a/modules/travel/front/main/index.html b/modules/travel/front/main/index.html new file mode 100644 index 000000000..71b9462fe --- /dev/null +++ b/modules/travel/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/travel/front/main/index.js b/modules/travel/front/main/index.js new file mode 100644 index 000000000..3be713973 --- /dev/null +++ b/modules/travel/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Travel extends ModuleMain {} + +ngModule.vnComponent('vnTravel', { + controller: Travel, + template: require('./index.html') +}); diff --git a/modules/travel/front/routes.json b/modules/travel/front/routes.json index 55643c937..f540d532f 100644 --- a/modules/travel/front/routes.json +++ b/modules/travel/front/routes.json @@ -1,17 +1,24 @@ { "module": "travel", "name": "Travels", + "icon": "local_airport", "validations": true, "dependencies": ["worker"], - "menu": [ - {"state": "travel.card.basicData", "icon": "settings"}, - {"state": "travel.card.log", "icon": "history"}], + "menus": { + "main": [ + {"state": "travel.index", "icon": "local_airport"} + ], + "card": [ + {"state": "travel.card.basicData", "icon": "settings"}, + {"state": "travel.card.log", "icon": "history"} + ] + }, "routes": [ { "url": "/travel", "state": "travel", "abstract": true, - "component": "ui-view", + "component": "vn-travel", "description": "Travels" }, { "url": "/index?q", diff --git a/modules/worker/front/account/index.html b/modules/worker/front/account/index.html index 59c788312..f51876a07 100644 --- a/modules/worker/front/account/index.html +++ b/modules/worker/front/account/index.html @@ -10,7 +10,7 @@ data="user" form="form"> - + diff --git a/modules/worker/front/basic-data/index.html b/modules/worker/front/basic-data/index.html index b44b9ec91..1d0d705e7 100644 --- a/modules/worker/front/basic-data/index.html +++ b/modules/worker/front/basic-data/index.html @@ -6,7 +6,7 @@ url="Workers" save="post"> - + diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html index bcf4c44f2..3b361d6a6 100644 --- a/modules/worker/front/calendar/index.html +++ b/modules/worker/front/calendar/index.html @@ -3,20 +3,18 @@ data="absenceTypes" auto-load="true"> -
        -
        - - - - -
        +
        + + + +
        diff --git a/modules/worker/front/card/index.html b/modules/worker/front/card/index.html index 80f1f7a61..ba9c8ec53 100644 --- a/modules/worker/front/card/index.html +++ b/modules/worker/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/worker/front/card/index.js b/modules/worker/front/card/index.js index e149cb0d2..dd2a24448 100644 --- a/modules/worker/front/card/index.js +++ b/modules/worker/front/card/index.js @@ -1,19 +1,8 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - Object.assign(this, { - $http, - $stateParams, - }); - } - - $onInit() { - this.reload(); - } - +class Controller extends ModuleCard { reload() { - let query = `Workers/${this.$stateParams.id}`; let filter = { include: [ { @@ -27,8 +16,7 @@ class Controller { } } } - }, - { + }, { relation: 'sip', scope: { fields: ['extension', 'secret'] @@ -50,14 +38,11 @@ class Controller { ] }; - this.$http.get(query, {params: {filter}}).then(res => { - this.worker = res.data; - }); + this.$http.get(`Workers/${this.$params.id}`, {filter}) + .then(res => this.worker = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - ngModule.component('vnWorkerCard', { template: require('./index.html'), controller: Controller diff --git a/modules/worker/front/department/index.html b/modules/worker/front/department/index.html index a464d59e0..e77d11e12 100644 --- a/modules/worker/front/department/index.html +++ b/modules/worker/front/department/index.html @@ -3,32 +3,29 @@ url="departments/getLeaves" auto-load="false"> -
        - - - - {{::item.name}} - - - - - - -
        +
        + + + {{::item.name}} + + + + + +
        - - + diff --git a/modules/worker/front/index/index.js b/modules/worker/front/index/index.js index 6a1c2f65c..81e9070bd 100644 --- a/modules/worker/front/index/index.js +++ b/modules/worker/front/index/index.js @@ -7,9 +7,6 @@ export default class Controller { $, selectedWorker: null, }); - this.moreOptions = [ - {callback: () => this.$state.go('worker.department'), name: 'Departments'} - ]; } onSearch(params) { @@ -22,9 +19,10 @@ export default class Controller { preview(event, worker) { if (event.defaultPrevented) return; event.preventDefault(); + event.stopPropagation(); + this.selectedWorker = worker; this.$.preview.show(); - event.stopImmediatePropagation(); } onMoreChange(callback) { diff --git a/modules/worker/front/main/index.html b/modules/worker/front/main/index.html new file mode 100644 index 000000000..826a7905f --- /dev/null +++ b/modules/worker/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/worker/front/main/index.js b/modules/worker/front/main/index.js new file mode 100644 index 000000000..d97a2d636 --- /dev/null +++ b/modules/worker/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Worker extends ModuleMain {} + +ngModule.vnComponent('vnWorker', { + controller: Worker, + template: require('./index.html') +}); diff --git a/modules/worker/front/pbx/index.html b/modules/worker/front/pbx/index.html index 70e67d6f9..5118d38ed 100644 --- a/modules/worker/front/pbx/index.html +++ b/modules/worker/front/pbx/index.html @@ -2,7 +2,7 @@ vn-id="watcher" data="$ctrl.worker.sip"> -
        + diff --git a/modules/worker/front/phones/index.html b/modules/worker/front/phones/index.html index 92930615b..95e0366c1 100644 --- a/modules/worker/front/phones/index.html +++ b/modules/worker/front/phones/index.html @@ -12,7 +12,7 @@ vn-id="watcher" data="$ctrl.phones"> - + -
        - - - - - -
        {{::$ctrl.weekdayNames[$index].name}}
        - {{::weekday.dated | date: 'dd'}} - - {{::weekday.dated | date: 'MMMM'}} - -
        -
        -
        - - - -
        - - - {{hour.timed | date: 'HH:mm'}} -
        -
        -
        -
        - - - - {{$ctrl.getWeekdayTotalHours(weekday)}} h. - - - - - - - - - -
        -
        - -
        -
        -
        Hours
        - - -
        - - + + + + + +
        {{::$ctrl.weekdayNames[$index].name}}
        + {{::weekday.dated | date: 'dd'}} + + {{::weekday.dated | date: 'MMMM'}} + +
        +
        +
        + + + +
        + + + {{hour.timed | date: 'HH:mm'}} +
        +
        +
        +
        + + + + {{$ctrl.getWeekdayTotalHours(weekday)}} h. + + + + + + + + + +
        +
        + +
        +
        +
        Hours
        + +
        - -
        + + +
        +