From 2436bf9488293bd2b0f75c35430527959d9017ce Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Wed, 23 Oct 2019 17:38:35 +0200 Subject: [PATCH] Agency events - First stable version --- e2e/helpers/selectors.js | 138 ++++---- front/core/components/calendar/index.html | 122 +++---- front/core/components/calendar/index.js | 308 +++++------------- front/core/components/calendar/index.spec.js | 66 ++-- front/core/components/calendar/style.scss | 178 +++++----- front/core/components/card/card.js | 5 +- front/core/components/debug-info/index.html | 9 + front/core/components/debug-info/index.js | 27 ++ front/core/components/debug-info/style.scss | 44 +++ front/core/components/dialog/style.scss | 2 +- front/core/components/field/index.js | 12 - front/core/components/form-input/index.js | 20 +- front/core/components/index.js | 2 + front/core/components/toggle/index.js | 21 +- front/core/components/wday-picker/index.html | 8 + front/core/components/wday-picker/index.js | 17 + front/core/components/wday-picker/style.scss | 26 ++ front/core/services/index.js | 1 + front/core/services/week-days.js | 71 ++++ front/core/styles/text.scss | 4 +- front/salix/components/app/app.html | 3 +- front/salix/components/main-menu/style.scss | 3 + front/salix/components/summary/style.scss | 78 +++-- front/salix/styles/misc.scss | 6 +- front/salix/styles/order-product.scss | 2 +- modules/agency/front/calendar/index.html | 50 +-- modules/agency/front/calendar/index.js | 212 +++++++----- modules/agency/front/calendar/style.scss | 39 ++- modules/agency/front/delivery-days/index.html | 76 ++--- modules/agency/front/delivery-days/index.js | 2 +- modules/agency/front/events/index.html | 88 +++-- modules/agency/front/events/index.js | 205 +++++++----- modules/agency/front/events/locale/es.yml | 4 + modules/agency/front/events/style.scss | 28 -- modules/agency/front/exclusions/index.html | 30 -- modules/agency/front/exclusions/index.js | 35 -- modules/agency/front/index.js | 1 - modules/agency/front/routes.json | 8 +- modules/worker/front/calendar/index.html | 65 ++-- modules/worker/front/calendar/index.js | 181 +++++----- modules/worker/front/calendar/index.spec.js | 160 ++++----- modules/worker/front/calendar/style.scss | 37 +-- modules/worker/front/time-control/index.html | 107 +++--- modules/worker/front/time-control/index.js | 209 ++++-------- .../worker/front/time-control/index.spec.js | 53 ++- .../worker/front/time-control/locale/es.yml | 2 +- modules/worker/front/time-control/style.scss | 11 +- webpack.config.js | 3 + 48 files changed, 1338 insertions(+), 1441 deletions(-) create mode 100644 front/core/components/debug-info/index.html create mode 100644 front/core/components/debug-info/index.js create mode 100644 front/core/components/debug-info/style.scss create mode 100644 front/core/components/wday-picker/index.html create mode 100644 front/core/components/wday-picker/index.js create mode 100644 front/core/components/wday-picker/style.scss create mode 100644 front/core/services/week-days.js create mode 100644 modules/agency/front/events/locale/es.yml delete mode 100644 modules/agency/front/events/style.scss delete mode 100644 modules/agency/front/exclusions/index.html delete mode 100644 modules/agency/front/exclusions/index.js diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index c09c10f50..67fc7e2fd 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -136,7 +136,7 @@ export default { addCreditFloatButton: `vn-float-button`, creditInput: `vn-input-number input[name="credit"]`, saveButton: `button[type=submit]`, - firstCreditText: 'vn-client-credit-index vn-card > div vn-table vn-tbody > vn-tr' + firstCreditText: 'vn-client-credit-index vn-card vn-table vn-tbody > vn-tr' }, clientGreuge: { addGreugeFloatButton: `vn-float-button`, @@ -144,13 +144,13 @@ export default { descriptionInput: `vn-textfield input[name="description"]`, typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.greuge.greugeTypeFk"]', saveButton: `button[type=submit]`, - firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr' + firstGreugeText: 'vn-client-greuge-index vn-card vn-table vn-tbody > vn-tr' }, clientMandate: { - firstMandateText: 'vn-client-mandate vn-card > div vn-table vn-tbody > vn-tr' + firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr' }, clientInvoices: { - firstInvoiceText: 'vn-client-invoice vn-card > div vn-table vn-tbody > vn-tr' + firstInvoiceText: 'vn-client-invoice vn-card vn-table vn-tbody > vn-tr' }, clientLog: { logButton: 'vn-left-menu a[ui-sref="client.card.log"]', @@ -307,7 +307,7 @@ export default { fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) > vn-td > vn-one:nth-child(3) > div span:nth-child(3)', }, ticketSummary: { - header: 'vn-ticket-summary > vn-card > div > h5', + header: 'vn-ticket-summary > vn-card > h5', state: 'vn-ticket-summary vn-label-value[label="State"] > section > span', route: 'vn-ticket-summary vn-label-value[label="Route"] > section > a', total: 'vn-ticket-summary vn-one.taxes > p:nth-child(3) > strong', @@ -319,14 +319,14 @@ export default { popoverDiaryButton: '.vn-popover.shown vn-item-descriptor vn-icon[icon="icon-transaction"]', firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3)', firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6)', - invoiceOutRef: 'vn-ticket-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(6) > section > span', + invoiceOutRef: 'vn-ticket-summary > vn-card > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(6) > section > span', setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button' }, ticketsIndex: { openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]', advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input', newTicketButton: 'vn-ticket-index > a', - searchResult: 'vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr', + 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`, @@ -403,7 +403,7 @@ export default { saleDescriptorPopoverSummaryButton: '.vn-popover.shown vn-item-descriptor a[ui-sref="item.card.summary({id: $ctrl.item.id})"]', descriptorItemDiaryButton: 'vn-item-descriptor .quicklinks.ng-scope > vn-horizontal > a > vn-icon > i', newItemFromCatalogButton: 'vn-ticket-sale vn-float-button[icon="add"]', - newItemButton: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-one > vn-icon-button > button > vn-icon > i', + newItemButton: 'vn-ticket-sale > vn-vertical > vn-card > vn-vertical > vn-one > vn-icon-button > button > vn-icon > i', moreMenu: 'vn-ticket-sale vn-tool-bar > vn-button-menu[vn-id="more-button"] > div > button', moreMenuCreateClaim: '.vn-popover.shown .vn-drop-down li[name="Add claim"]', moreMenuReserve: '.vn-popover.shown .vn-drop-down li[name="Mark as reserved"]', @@ -443,7 +443,7 @@ export default { secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number input', secondSaleConceptCell: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)', secondSaleConceptInput: 'vn-ticket-sale vn-table vn-tr:nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield input', - totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong', + totalImport: 'vn-ticket-sale > vn-vertical > vn-card > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong', selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check', secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[ng-model="sale.checked"]', thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[ng-model="sale.checked"]', @@ -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 > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)', + stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > vn-horizontal > table > tfoot > tr > td:nth-child(4)', chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]', }, ticketComponents: { @@ -481,7 +481,7 @@ export default { ticketRequests: { addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button', request: 'vn-ticket-request-index vn-table vn-tr', - descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield input', + descriptionInput: 'vn-ticket-request-create > form > div > vn-card > vn-horizontal:nth-child(1) > vn-textfield input', atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[ng-model="$ctrl.ticketRequest.atenderFk"]', quantityInput: 'vn-ticket-request-create vn-input-number input[name=quantity]', priceInput: 'vn-ticket-request-create vn-input-number input[name=price]', @@ -505,7 +505,7 @@ export default { firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]', fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]', newDescriptionInput: 'vn-ticket-service > vn-dialog vn-textfield[ng-model="$ctrl.newServiceType.name"] input', - serviceLine: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(2) > vn-horizontal', + serviceLine: 'vn-ticket-service > form > vn-card > vn-one:nth-child(2) > vn-horizontal', saveServiceButton: `button[type=submit]`, saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button' }, @@ -517,7 +517,7 @@ export default { }, claimsIndex: { searchClaimInput: `vn-claim-index vn-textfield input`, - searchResult: 'vn-claim-index vn-card > div > vn-table > div > vn-tbody > a', + searchResult: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a', searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]' }, claimDescriptor: { @@ -526,7 +526,7 @@ export default { acceptDeleteClaim: 'vn-claim-descriptor > vn-confirm[vn-id="confirm-delete-claim"] button[response="ACCEPT"]' }, claimSummary: { - header: 'vn-claim-summary > vn-card > div > h5', + header: 'vn-claim-summary > vn-card > h5', state: 'vn-claim-summary vn-label-value[label="State"] > section > span', observation: 'vn-claim-summary vn-textarea[ng-model="$ctrl.summary.claim.observation"] textarea', firstSaleItemId: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(4) vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(1) > span', @@ -535,7 +535,7 @@ export default { itemDescriptorPopoverItemDiaryButton: '.vn-popover.shown vn-item-descriptor a[href="#!/item/2/diary"]', firstDevelopmentWorker: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(5) vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > span', firstDevelopmentWorkerGoToClientButton: '.vn-popover.shown vn-worker-descriptor div.quicklinks > a[href="#!/client/21/summary"]', - firstActionTicketId: 'vn-claim-summary > vn-card > div > vn-horizontal > vn-auto:nth-child(6) vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', + firstActionTicketId: 'vn-claim-summary > vn-card > vn-horizontal > vn-auto:nth-child(6) vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor' }, claimBasicData: { @@ -545,19 +545,19 @@ export default { saveButton: `button[type=submit]` }, claimDetail: { - secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span', + secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span', discountInput: '.vn-popover.shown vn-input-number[ng-model="$ctrl.newDiscount"] input', discoutPopoverMana: '.vn-popover.shown .content > div > vn-horizontal > h5', addItemButton: 'vn-claim-detail a vn-float-button', firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr', - claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr', + claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr', firstItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(1) vn-input-number[ng-model="saleClaimed.quantity"] input', - totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span', - secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i' + totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span', + secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i' }, claimDevelopment: { - addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > vn-one > vn-icon-button > button > vn-icon', - firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon', + addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > vn-vertical > vn-one > vn-icon-button > button > vn-icon', + firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon', firstClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]', firstClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]', firstClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]', @@ -580,7 +580,7 @@ export default { isPaidWithManaCheckbox: 'vn-check[ng-model="$ctrl.claim.isChargedToMana"]' }, ordersIndex: { - searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr', + searchResult: 'vn-order-index vn-card > vn-table > div > vn-tbody > a.vn-tr', searchResultDate: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)', searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)', searchOrderInput: `vn-order-index vn-textfield input`, @@ -641,7 +641,7 @@ export default { volume: 'vn-route-descriptor vn-label-value[label="Volume"] > section > span' }, routeSummary: { - routeId: 'vn-route-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(1) > section > span' + routeId: 'vn-route-summary > vn-card > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(1) > section > span' }, routeBasicData: { workerAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.workerFk"]', @@ -671,57 +671,57 @@ export default { }, workerTimeControl: { timeDialogInput: '.vn-dialog.shown [ng-model="$ctrl.newTime"]', - mondayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button > button > vn-icon', - tuesdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(2) > vn-icon-button > button > vn-icon', - wednesdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(3) > vn-icon-button > button > vn-icon', - thursdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-icon-button > button > vn-icon', - fridayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(5) > vn-icon-button > button > vn-icon', - saturdayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(6) > vn-icon-button > button > vn-icon', - sundayAddTimeButton: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(7) > vn-icon-button > button > vn-icon', + mondayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button', + tuesdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(2) > vn-icon-button', + wednesdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(3) > vn-icon-button', + thursdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-icon-button', + fridayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(5) > vn-icon-button', + saturdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(6) > vn-icon-button', + sundayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(7) > vn-icon-button', confirmButton: 'vn-worker-time-control > vn-dialog > div > form > div.buttons > tpl-buttons > button', - firstEntryOfMonday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > span', - firstEntryOfTuesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > span', - firstEntryOfWednesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > span', - firstEntryOfThursday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > span', - firstEntryOfFriday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > span', - firstEntryOfSaturday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > span', - firstEntryOfSunday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > span', - secondEntryOfMonday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > span', - secondEntryOfTuesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > span', - secondEntryOfWednesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > span', - secondEntryOfThursday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > span', - secondEntryOfFriday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > span', - secondEntryOfSaturday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > span', - secondEntryOfSunday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > span', - thirdEntryOfMonday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > span', - thirdEntryOfTuesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > span', - thirdEntryOfWednesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > span', - thirdEntryOfThursday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > span', - thirdEntryOfFriday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > span', - thirdEntryOfSaturday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > span', - thirdEntryOfSunday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > span', - fourthEntryOfMonday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > span', - fourthEntryOfTuesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > span', - fourthEntryOfWednesday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > span', - fourthEntryOfThursday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > span', - fourthEntryOfFriday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > span', - fourthEntryOfSaturday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > span', - fourthEntryOfSunday: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > span', - mondayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(1)', - tuesdayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(2)', - wednesdayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(3)', - thursdayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(4)', - fridayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(5)', - saturdayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(6)', - sundayWorkedHours: 'vn-worker-time-control > div > vn-card > div > vn-horizontal > vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(7)', - weekWorkedHours: 'vn-worker-time-control > div > vn-side-menu > div > vn-vertical > vn-vertical > vn-label-value > section > span', - nextMonthButton: 'vn-worker-time-control > div > vn-side-menu > div > vn-calendar > div > vn-horizontal > vn-auto:nth-child(3) > vn-icon', + firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > span', + firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > span', + firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > span', + firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > span', + firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > span', + firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > span', + firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > span', + secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > span', + secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > span', + secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > span', + secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > span', + secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > span', + secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > span', + secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > span', + thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > span', + thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > span', + thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > span', + thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > span', + thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > span', + thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > span', + thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > span', + fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > span', + fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > span', + fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > span', + fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > span', + fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > span', + fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > span', + fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > span', + mondayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(1)', + tuesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(2)', + wednesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(3)', + thursdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(4)', + fridayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(5)', + saturdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(6)', + sundayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(7)', + weekWorkedHours: 'vn-worker-time-control vn-side-menu vn-label-value > section > span', + nextMonthButton: 'vn-worker-time-control vn-side-menu vn-calendar vn-button[icon=keyboard_arrow_right]', 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"]', - searchResult: 'vn-invoice-out-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr', + searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr', }, invoiceOutDescriptor: { moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[icon=more_vert]', @@ -732,6 +732,6 @@ export default { acceptBookingButton: 'vn-invoice-out-descriptor > vn-confirm[vn-id="bookConfirmation"] button[response="ACCEPT"]' }, invoiceOutSummary: { - bookedLabel: 'vn-invoice-out-summary > vn-card > div > vn-horizontal > vn-one > vn-label-value:nth-child(4) > section > span' + bookedLabel: 'vn-invoice-out-summary > vn-card > vn-horizontal > vn-one > vn-label-value:nth-child(4) > section > span' } }; diff --git a/front/core/components/calendar/index.html b/front/core/components/calendar/index.html index b2fc023ae..0efff2492 100644 --- a/front/core/components/calendar/index.html +++ b/front/core/components/calendar/index.html @@ -1,82 +1,46 @@
- - - - - - -
- {{$ctrl.defaultDate | date: 'MMMM'}} - {{$ctrl.defaultDate | date: 'yyyy'}} +
+ + +
+ {{$ctrl.defaultDate | date: 'MMMM'}} + {{$ctrl.defaultDate | date: 'yyyy'}} +
+ + +
+
+
+ {{::day.localeChar}} +
+
+
+
+
+ {{::day | date: 'd'}}
- - - - - - - - -
- L -
-
- M -
-
- X -
-
- J -
-
- V -
-
- S -
-
- D -
-
- -
-
-
- {{::day.dated | date: 'd'}} -
-
-
- - {{::event.name}} - -
-
-
-
-
-
+
+
\ No newline at end of file diff --git a/front/core/components/calendar/index.js b/front/core/components/calendar/index.js index 3778d64d2..6a56ed86a 100644 --- a/front/core/components/calendar/index.js +++ b/front/core/components/calendar/index.js @@ -1,43 +1,26 @@ import ngModule from '../../module'; -import Component from '../../lib/component'; +import FormInput from '../form-input'; import './style.scss'; /** * Flat calendar. * - * @property {Array} data Array of events + * @property {Array} defaultDate Array of events * @property {Function} hasEvents Determines if an events exists for a day * @property {Function} getClass Class to apply to specific day + * @event selection Emitted when day or weekday is selected + * @event move Emitted when month changes */ -export default class Calendar extends Component { - constructor($element, $scope) { +export default class Calendar extends FormInput { + constructor($element, $scope, vnWeekDays) { super($element, $scope); - this.events = []; + this.weekDays = vnWeekDays.locales; this.defaultDate = new Date(); this.displayControls = true; - this.disabled = false; - this.skip = 1; - - this.window.addEventListener('resize', () => { - this.checkSize(); - }); } /** - * Resizes the calendar - * based on component height - */ - checkSize() { - const height = this.$element[0].clientHeight; - - if (height < 530) - this.$element.addClass('small'); - else - this.$element.removeClass('small'); - } - - /** - * Returns the initial date + * The initial date * * @return {Date} - Default date */ @@ -45,11 +28,6 @@ export default class Calendar extends Component { return this._defaultDate; } - /** - * Sets a new initial date - * - * @param {Date} value - New default date - */ set defaultDate(value) { if (value) { value = new Date(value); @@ -58,67 +36,10 @@ export default class Calendar extends Component { } this._defaultDate = value; + this.month = value.getMonth(); this.repaint(); } - /** - * Sets events - * - * @param {Array} value - Array of events - * @param {Date} event.dated - Day to add event - * @param {String} event.name - Tooltip description - * @param {String} event.className - ClassName style - * @param {Object} event.style - Style properties - */ - set data(value) { - if (!value) return; - - this.events = []; - - value.forEach(event => { - event.dated = new Date(event.dated); - event.dated.setHours(0, 0, 0, 0); - this.events.push(event); - }); - - if (this.defaultDate) { - this.repaint(); - this.checkSize(); - } - } - - /** - * Gets current month date - */ - - get currentMonth() { - return this.defaultDate; - } - - /** - * Gets next month date - * - * @return {Date} - */ - get nextMonth() { - const newDate = new Date(this.currentMonth); - newDate.setMonth(this.currentMonth.getMonth() + 1); - - return newDate; - } - - /** - * Gets previous month date - * - * @return {Date} - */ - get previousMonth() { - const newDate = new Date(this.currentMonth); - newDate.setMonth(this.currentMonth.getMonth() - 1); - - return newDate; - } - /** * Returns first day of month from a given date * @@ -126,185 +47,113 @@ export default class Calendar extends Component { * @return {Integer} */ firstDay(date) { - const newDate = new Date( + return new Date( date.getFullYear(), - date.getMonth(), 1); - - return newDate; + date.getMonth(), + 1 + ); } /** - * Returns last day of month from a given date - * - * @param {Date} date - Origin date - * @return {Integer} + * Repaints the calendar. */ - lastDay(date) { - const newDate = new Date( - date.getFullYear(), - date.getMonth() + 1, 0); - - return newDate; - } - repaint() { - const firstWeekday = this.firstDay(this.currentMonth).getDay(); - const previousLastDay = this.lastDay(this.previousMonth).getDate(); - const currentLastDay = this.lastDay(this.currentMonth).getDate(); - const maxFields = 42; // Max field limit + const firstWeekday = this.firstDay(this.defaultDate).getDay() - 1; + let weekdayOffset = firstWeekday >= 0 ? firstWeekday : 6; - let weekdayOffset = firstWeekday > 0 ? firstWeekday : 7; - let dayPrevious = previousLastDay - (weekdayOffset - 2); - let dayCurrent = 1; - let dayNext = 1; + let dayIndex = new Date(this.defaultDate.getTime()); + dayIndex.setDate(1 - weekdayOffset); this.days = []; - for (let fieldIndex = 1; fieldIndex < maxFields; fieldIndex++) { - // Insert previous month days - if (fieldIndex < weekdayOffset) { - const dated = new Date( - this.previousMonth.getFullYear(), - this.previousMonth.getMonth(), dayPrevious); - - this.insertDay(dated, 'gray'); - dayPrevious++; - } - - // Insert current month days - if (fieldIndex >= weekdayOffset && dayCurrent <= currentLastDay) { - const dated = new Date( - this.currentMonth.getFullYear(), - this.currentMonth.getMonth(), dayCurrent); - - this.insertDay(dated); - dayCurrent++; - } - - // Insert next month days - if (fieldIndex >= weekdayOffset && dayCurrent > currentLastDay) { - const dated = new Date( - this.nextMonth.getFullYear(), - this.nextMonth.getMonth(), dayNext); - - this.insertDay(dated, 'gray'); - dayNext++; - } + for (let i = 1; i <= 42; i++) { + this.days.push(new Date(dayIndex.getTime())); + dayIndex.setDate(dayIndex.getDate() + 1); } } /** - * Inserts a date on an array of month days + * Gets CSS classes to apply to the specified day. * - * @param {Date} dated - Date of month - * @param {String} className - Default class style + * @param {Date} day The day + * @return {Object} The CSS classes to apply */ - insertDay(dated) { - let events = this.events.filter(event => { - return event.dated >= dated && event.dated <= dated; - }); + getDayClasses(day) { + let wday = day.getDay(); + let month = day.getMonth(); - const params = {dated: dated, events: events /**/, style: {}}; - const isSaturday = dated.getDay() === 6; - const isSunday = dated.getDay() === 0; - const isCurrentMonth = dated.getMonth() === this.currentMonth.getMonth(); - const hasEvents = events.length > 0; + let classes = { + weekend: wday === 6 || wday === 0, + previous: month < this.month, + current: month == this.month, + next: month > this.month, + event: this.hasEvents({$day: day}) + }; - if (isCurrentMonth && isSunday && !hasEvents) - params.style = {color: '#999'}; + let userClass = this.getClass({$day: day}); + if (userClass) classes[userClass] = true; - if (isCurrentMonth && isSaturday && !hasEvents) - params.style = {color: '#999'}; - - if (!isCurrentMonth) - params.style = {opacity: '0.5'}; - - if (events.length > 0) { - const eventStyle = events[0].style; - const eventName = events[0].description || events[0].name; - if (eventStyle) - Object.assign(params.style, eventStyle); - if (eventName) - params.eventName = eventName; - } - - this.days.push(params); + return classes; } /** * Moves to next month(s) - * - * @param {Integer} skip - Months to skip at once */ - moveNext(skip = 1) { - let next = this.defaultDate.getMonth() + skip; - this.defaultDate.setMonth(next); - this.repaint(); - this.emit('moveNext'); + moveNext() { + this.move(1); } /** * Moves to previous month(s) - * - * @param {Integer} skip - Months to skip at once */ - movePrevious(skip = 1) { - let previous = this.defaultDate.getMonth() - skip; - this.defaultDate.setMonth(previous); - this.repaint(); - this.emit('movePrevious'); + movePrevious() { + this.move(-1); } /** - * Day selection event + * Moves @direction months backwards/forwards. * - * @param {Integer} index - Index from days array + * @param {Number} direction Negative to move backwards, positive forwards */ - select(index) { - if (this.disabled) return; - let day = this.days[index].dated; + move(direction) { + let date = new Date(this.defaultDate.getTime()); + date.setMonth(date.getMonth() + direction); + this.defaultDate = date; + this.repaint(); + this.emit('move', {$date: date}); + } + + /* + * Day selection event + */ + select(day) { + if (!this.editable) return; + this.field = day; this.emit('selection', { $days: [day], $type: 'day' }); + this.repaint(); } - /** + /* * WeekDay selection event - * - * @param {Integer} weekday - weekday index */ - selectAll(weekday) { - if (this.disabled) return; - + selectWeekDay(weekday) { + if (!this.editable) return; let days = []; - for (let i in this.days) { - const day = this.days[i].dated; - if (day.getDay() === weekday && day.getMonth() == this.defaultDate.getMonth()) + for (let day of this.days) { + if (day.getDay() === weekday && day.getMonth() == this.month) days.push(day); } + this.field = days[0]; this.emit('selection', { $days: days, $type: 'weekday', $weekday: weekday }); - } - - renderStyle(style) { - const normalizedStyle = {}; - - if (style) { - const properties = Object.keys(style); - properties.forEach(attribute => { - const attrName = attribute.split(/(?=[A-Z])/). - join('-').toLowerCase(); - - normalizedStyle[attrName] = style[attribute]; - }); - } - - return normalizedStyle; + this.repaint(); } hasEvents() { @@ -314,24 +163,31 @@ export default class Calendar extends Component { getClass() { return ''; } + + repeatLast() { + if (!this.formatDay) return; + + let days = this.element.querySelectorAll('.days > .day'); + for (let i = 0; i < days.length; i++) { + this.formatDay({ + $day: this.days[i], + $element: days[i] + }); + } + } } +Calendar.$inject = ['$element', '$scope', 'vnWeekDays']; -Calendar.$inject = ['$element', '$scope']; - -ngModule.component('vnCalendar', { +ngModule.vnComponent('vnCalendar', { template: require('./index.html'), controller: Calendar, bindings: { - model: '<', - data: ' { let controller; let $element; + let date = new Date(); + date.setHours(0, 0, 0, 0); + date.setDate(1); + beforeEach(angular.mock.module('vnCore', $translateProvider => { $translateProvider.translations('en', {}); })); @@ -9,79 +13,55 @@ describe('Component vnCalendar', () => { beforeEach(inject(($compile, $rootScope) => { $element = $compile(` { $element.remove(); }); - describe('data() setter', () => { - it(`should set an array of events and convert string dates to string object, then call repaint() method`, () => { - spyOn(controller, 'repaint'); - - let currentDate = new Date().toString(); - controller.data = [ - {dated: currentDate, name: 'Event 1'}, - {dated: currentDate, name: 'Event 2'}, - ]; - - expect(controller.events[0].dated instanceof Object).toBeTruthy(); - expect(controller.repaint).toHaveBeenCalledWith(); - }); - }); - describe('moveNext()', () => { - it(`should shift to the next n months, then emit a 'moveNext' event`, () => { + it(`should shift to the next month, then emit a 'move' event`, () => { spyOn(controller, 'emit'); - const currentMonth = controller.defaultDate.getMonth(); - let nextMonth = currentMonth + 1; + let nextMonth = new Date(date.getTime()); + nextMonth.setMonth(nextMonth.getMonth() + 1); - controller.moveNext(1); + controller.moveNext(); - expect(controller.defaultDate.getMonth()).toEqual(nextMonth); - expect(controller.emit).toHaveBeenCalledWith('moveNext'); + expect(controller.month).toEqual(nextMonth.getMonth()); + expect(controller.emit).toHaveBeenCalledWith('move', {$date: nextMonth}); }); }); describe('movePrevious()', () => { - it(`should shift to the previous n months, then emit a 'movePrevious' event`, () => { + it(`should shift to the previous month, then emit a 'move' event`, () => { spyOn(controller, 'emit'); - const currentMonth = controller.defaultDate.getMonth(); - let previousMonth = currentMonth - 1; + let previousMonth = new Date(date.getTime()); + previousMonth.setMonth(previousMonth.getMonth() - 1); - controller.movePrevious(1); + controller.movePrevious(); - expect(controller.defaultDate.getMonth()).toEqual(previousMonth); - expect(controller.emit).toHaveBeenCalledWith('movePrevious'); + expect(controller.month).toEqual(previousMonth.getMonth()); + expect(controller.emit).toHaveBeenCalledWith('move', {$date: previousMonth}); }); }); describe('select()', () => { it(`should return the selected element, then emit a 'selection' event`, () => { spyOn(controller, 'emit'); - const dated = new Date(); - const days = [{dated}]; - controller.days = days; - controller.select(0); + + const day = new Date(); + day.setHours(0, 0, 0, 0); + controller.select(day); let res = { - $days: [dated], + $days: [day], $type: 'day' }; + expect(controller.field).toEqual(day); expect(controller.emit).toHaveBeenCalledWith('selection', res); }); }); - - describe('renderStyle()', () => { - it(`should normalize CSS attributes`, () => { - const result = controller.renderStyle({ - backgroundColor: 'red' - }); - - expect(result['background-color']).toEqual('red'); - }); - }); }); diff --git a/front/core/components/calendar/style.scss b/front/core/components/calendar/style.scss index a8003bb78..b00c40648 100644 --- a/front/core/components/calendar/style.scss +++ b/front/core/components/calendar/style.scss @@ -1,103 +1,97 @@ @import "variables"; -vn-calendar.small { - .events { - display: none - } -} vn-calendar { display: block; - .header vn-one { - text-align: center; - padding: 0.2em 0; - height: 1.5em - } - .weekdays { - color: $color-font-secondary; - margin-bottom: 0.5em; - padding: 0.5em 0; - font-weight: bold; - font-size: 0.8em; - } - .weekdays section { - cursor: pointer - } - .weekdays section, .day { - position: relative; - text-align: center; - box-sizing: border-box; - width: 14.28%; - outline: 0; - } - .days { - justify-content: flex-start; - align-items: flex-start; - flex-wrap: wrap; - } - .day { - .content { - position: absolute; - bottom: 0; - right: 0; - left: 0; - top: 0 - } - .day-number { - transition: background-color 0.3s; - text-align:center; - float:inline-end; - margin: 0 auto; - border-radius: 50%; - font-size: 0.85em; - width:2.2em; - height: 1.2em; - padding: 0.5em 0; - cursor: pointer; - outline: 0 - } - .day-number:hover { - background-color: lighten($color-font-secondary, 20%); - opacity: 0.8 - } - } - .day::after { - content: ""; - display: block; - padding-top: 100%; - } - .day.primary .day-number { - background-color: $color-main; - color: $color-font-dark; - } - .events { - margin-top: 0.5em; - font-size: 0.6em - } - .events { - color: $color-font-secondary; + & > div { + & > .header { + display: flex; + margin-bottom: 0.5em; + align-items: center; + height: 2.4em; - .event { - margin-bottom: .1em; + & > .title { + flex: 1; + text-align: center; + padding: 0.2em 0; + } + & > .vn-button { + color: inherit; + } + } + & > .weekdays { + display: flex; + color: $color-font-secondary; + margin-bottom: 0.5em; + padding: 0.5em 0; + font-weight: bold; + font-size: 0.8em; + text-align: center; + + & > section { + width: 14.28%; + cursor: pointer; + } + } + & > .days { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + + & > .day { + width: 14.28%; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + + &.weekend { + color: $color-font-secondary; + } + &.previous, + &.next { + opacity: .5; + } + &.event .day-number { + background-color: $color-main; + color: $color-font-dark; + } + & > .day-number { + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + font-size: 14px; + width: 2.2em; + height: 2.2em; + cursor: pointer; + outline: 0; + transition: background-color 300ms ease-in-out; + + &:hover { + background-color: lighten($color-font-secondary, 20%); + opacity: .8 + } + } + } + &.hide-contiguous > .day { + &.previous, + &.next { + visibility: hidden; + } + } } } - .chip { - background-color: $color-main; - color: $color-font-bg; - display: inline-block; - border-radius: .3em; - padding: 0.3em .8em; - max-width: 5em; - } - .day.gray { - .day-number { - color: $color-font-secondary - } - } - .day.sunday { - .day-number { - color: $color-alert; - font-weight: bold + &.disabled, + &.readonly { + & > div { + & > .weekdays > section { + cursor: initial; + } + & > .days > .day > .day-number { + cursor: initial; + } } } } diff --git a/front/core/components/card/card.js b/front/core/components/card/card.js index a37835995..c0be1e3d3 100644 --- a/front/core/components/card/card.js +++ b/front/core/components/card/card.js @@ -5,12 +5,11 @@ export default function directive() { return { restrict: 'E', transclude: true, - template: require('./card.html'), link: function($scope, $element, $attrs, $ctrl, $transclude) { - $element.addClass('demo-card-wide vn-shadow bg-panel'); + $element[0].classList.add('vn-shadow', 'bg-panel'); $transclude($scope, function(clone) { - angular.element($element[0].querySelector('div')).append(clone); + $element.append(clone); }); } }; diff --git a/front/core/components/debug-info/index.html b/front/core/components/debug-info/index.html new file mode 100644 index 000000000..1ce9e6b08 --- /dev/null +++ b/front/core/components/debug-info/index.html @@ -0,0 +1,9 @@ +
Debug info
+
    +
  • + {{$ctrl.env}} +
  • +
  • + {{$root.$$watchersCount}} watchers +
  • +
\ No newline at end of file diff --git a/front/core/components/debug-info/index.js b/front/core/components/debug-info/index.js new file mode 100644 index 000000000..5e3d2e1ea --- /dev/null +++ b/front/core/components/debug-info/index.js @@ -0,0 +1,27 @@ +import ngModule from '../../module'; +import './style.scss'; + +/** + * Floating box displaying debugging information. + * Enabled only in development environment. + */ +export default class Controller { + constructor($element, $) { + this.env = process.env.NODE_ENV || 'development'; + + if (this.env == 'development') + this.interval = setInterval(() => $.$digest(), 2000); + else + $element[0].style.display = 'none'; + } + + $onDestroy() { + clearInterval(this.interval); + } +} +Controller.$inject = ['$element', '$scope']; + +ngModule.component('vnDebugInfo', { + template: require('./index.html'), + controller: Controller +}); diff --git a/front/core/components/debug-info/style.scss b/front/core/components/debug-info/style.scss new file mode 100644 index 000000000..95c024a6d --- /dev/null +++ b/front/core/components/debug-info/style.scss @@ -0,0 +1,44 @@ +@import "variables"; + +vn-debug-info { + position: fixed; + bottom: 1em; + left: 1em; + padding: 1em; + min-width: 8em; + background-color: #3f51b5; + color: $color-font-dark; + border-radius: 4px; + z-index: 999; + box-shadow: $shadow; + transition: opacity 400ms ease-in-out; + + &:hover { + opacity: .5; + } + & > h6 { + font-weight: normal; + color: rgba(255, 255, 255, .5); + font-size: 1em; + } + ul { + list-style-type: none; + padding: 0; + margin: 0; + + & > li { + margin-top: .2em; + font-size: .95em; + + & > span { + padding: .05em .2em; + border-radius: 4px; + transition: background-color 200ms ease-in-out; + + &.alert { + background-color: $color-alert; + } + } + } + } +} \ No newline at end of file diff --git a/front/core/components/dialog/style.scss b/front/core/components/dialog/style.scss index 406f86148..e6e4f407d 100644 --- a/front/core/components/dialog/style.scss +++ b/front/core/components/dialog/style.scss @@ -31,7 +31,7 @@ tpl-body { display: block; - min-width: 20em; + min-width: 16em; } & > button.close { @extend %clickable; diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index ed99d1f5c..2962c4978 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -153,10 +153,6 @@ export default class Field extends FormInput { fix.innerText = text || ''; } - refreshTabIndex() { - this.input.tabIndex = this.disabled ? -1 : this.tabIndex; - } - onClick() { // if (event.defaultPrevented) return; // event.preventDefault(); @@ -182,14 +178,6 @@ export default class Field extends FormInput { this.input.dispatchEvent(new Event('change')); } - focus() { - this.input.focus(); - } - - select() { - this.input.select(); - } - buildInput(type) { let template = ``; this.input = this.$compile(template)(this.$)[0]; diff --git a/front/core/components/form-input/index.js b/front/core/components/form-input/index.js index 0a1297788..7e1cc7f0f 100644 --- a/front/core/components/form-input/index.js +++ b/front/core/components/form-input/index.js @@ -49,8 +49,9 @@ export default class FormInput extends Component { set disabled(value) { this._disabled = boolTag(value); - this.input.disabled = this._disabled; this.classList.toggle('disabled', this._disabled); + if (this.input) + this.input.disabled = this._disabled; this.refreshTabIndex(); } @@ -60,8 +61,9 @@ export default class FormInput extends Component { set readonly(value) { this._readonly = boolTag(value); - this.input.readOnly = this._readonly; this.classList.toggle('readonly', this._readonly); + if (this.input) + this.input.readOnly = this._readonly; } get readonly() { @@ -77,16 +79,24 @@ export default class FormInput extends Component { return this._tabIndex; } + get inputEl() { + return this.input || this.element; + } + + get editable() { + return !(this.readonly || this.disabled); + } + select() { - this.input.select(); + this.inputEl.select(); } focus() { - this.input.focus(); + this.inputEl.focus(); } refreshTabIndex() { - this.element.tabIndex = this.disabled ? -1 : this.tabIndex; + this.inputEl.tabIndex = this.disabled ? -1 : this.tabIndex; } } diff --git a/front/core/components/index.js b/front/core/components/index.js index 50c1a1bf8..21a40d052 100644 --- a/front/core/components/index.js +++ b/front/core/components/index.js @@ -28,6 +28,7 @@ import './check'; import './chip'; import './data-viewer'; import './date-picker'; +import './debug-info'; import './field'; import './float-button'; import './icon-menu'; @@ -44,3 +45,4 @@ import './td-editable'; import './textarea'; import './th'; import './treeview'; +import './wday-picker'; diff --git a/front/core/components/toggle/index.js b/front/core/components/toggle/index.js index 54f5b8158..b9cc70990 100644 --- a/front/core/components/toggle/index.js +++ b/front/core/components/toggle/index.js @@ -18,32 +18,13 @@ export default class Toggle extends FormInput { element.classList.add('vn-toggle'); } - set disabled(value) { - this._disabled = value; - this.classList.toggle('disabled', Boolean(value)); - this.refreshTabIndex(); - } - - get disabled() { - return this._disabled; - } - - set readonly(value) { - this._readonly = value; - this.classList.toggle('readonly', Boolean(value)); - } - - get readonly() { - return this._readonly; - } - onKeydown(event) { if (event.code == 'Space') this.onClick(event); } onClick(event) { - if (this.disabled || event.defaultPrevented) + if (!this.editable || event.defaultPrevented) return true; event.preventDefault(); diff --git a/front/core/components/wday-picker/index.html b/front/core/components/wday-picker/index.html new file mode 100644 index 000000000..e8d74f1c2 --- /dev/null +++ b/front/core/components/wday-picker/index.html @@ -0,0 +1,8 @@ + + + {{day.localeChar}} + \ No newline at end of file diff --git a/front/core/components/wday-picker/index.js b/front/core/components/wday-picker/index.js new file mode 100644 index 000000000..6df5c35f5 --- /dev/null +++ b/front/core/components/wday-picker/index.js @@ -0,0 +1,17 @@ +import ngModule from '../../module'; +import FormInput from '../form-input'; +import './style.scss'; + +export default class WdayPicker extends FormInput { + constructor($element, $scope, vnWeekDays) { + super($element, $scope); + this.input = {}; + this.days = vnWeekDays.locales; + } +} +WdayPicker.$inject = ['$element', '$scope', 'vnWeekDays']; + +ngModule.vnComponent('vnWdayPicker', { + template: require('./index.html'), + controller: WdayPicker +}); diff --git a/front/core/components/wday-picker/style.scss b/front/core/components/wday-picker/style.scss new file mode 100644 index 000000000..694d31b18 --- /dev/null +++ b/front/core/components/wday-picker/style.scss @@ -0,0 +1,26 @@ +@import "effects"; + +vn-wday-picker { + margin-top: $spacing-sm; + margin-bottom: $spacing-md; + text-align: center; + + & > span { + @extend %clickable; + border-radius: 50%; + padding: .4em; + margin: .2em; + display: inline-flex; + width: 1.5em; + height: 1.5em; + justify-content: center; + align-items: center; + outline: none; + background-color: rgba(0, 0, 0, .05); + + &.marked { + background: $color-main; + color: $color-font-dark; + } + } +} \ No newline at end of file diff --git a/front/core/services/index.js b/front/core/services/index.js index 8ff95188a..4573ab5c9 100644 --- a/front/core/services/index.js +++ b/front/core/services/index.js @@ -6,3 +6,4 @@ import './token'; import './modules'; import './interceptor'; import './config'; +import './week-days'; diff --git a/front/core/services/week-days.js b/front/core/services/week-days.js new file mode 100644 index 000000000..f7d432c01 --- /dev/null +++ b/front/core/services/week-days.js @@ -0,0 +1,71 @@ +import ngModule from '../module'; + +class WeekDays { + constructor($translate) { + this.$translate = $translate; + + this.days = [ + { + code: 'sun', + name: 'Sunday' + }, { + code: 'mon', + name: 'Monday' + }, { + code: 'tue', + name: 'Tuesday' + }, { + code: 'wed', + name: 'Wednesday' + }, { + code: 'thu', + name: 'Thursday' + }, { + code: 'fri', + name: 'Friday' + }, { + code: 'sat', + name: 'Saturday' + } + ]; + + this.map = {}; + for (let i = 0; i < this.days.length; i++) { + let day = this.days[i]; + day.index = i; + day.char = day.name.substr(0, 1); + day.abr = day.name.substr(0, 3); + this.map[day.code] = day; + } + + this.getLocales(); + } + + getLocales() { + for (let day of this.days) { + let locale = this.$translate.instant(day.name); + Object.assign(day, { + locale, + localeChar: locale.substr(0, 1), + localeAbr: locale.substr(0, 3) + }); + } + + this.localeCodes = [ + 'mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + 'sun' + ]; + + this.locales = []; + for (let code of this.localeCodes) + this.locales.push(this.map[code]); + } +} +WeekDays.$inject = ['$translate']; + +ngModule.service('vnWeekDays', WeekDays); diff --git a/front/core/styles/text.scss b/front/core/styles/text.scss index 2eb959af7..b1624cd26 100644 --- a/front/core/styles/text.scss +++ b/front/core/styles/text.scss @@ -42,13 +42,13 @@ h1, h2, h3, h4, h5, h6 { padding: 0; margin-top: 0; - margin-bottom: .2em; + margin-bottom: .3em; } /* Colors */ .text-primary { - color: $color-font; + color: $color-main; } .text-secondary { color: $color-font-secondary; diff --git a/front/salix/components/app/app.html b/front/salix/components/app/app.html index c69d07375..d6ac05d7e 100644 --- a/front/salix/components/app/app.html +++ b/front/salix/components/app/app.html @@ -25,4 +25,5 @@ ng-class="{padding: $ctrl.showTopbar}">
- \ No newline at end of file + + \ No newline at end of file diff --git a/front/salix/components/main-menu/style.scss b/front/salix/components/main-menu/style.scss index 68744aad6..cd38472ab 100644 --- a/front/salix/components/main-menu/style.scss +++ b/front/salix/components/main-menu/style.scss @@ -18,6 +18,9 @@ vn-main-menu { vertical-align: middle; font-weight: bold; margin-right: .2em; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; cursor: pointer; } & > .vn-button { diff --git a/front/salix/components/summary/style.scss b/front/salix/components/summary/style.scss index 558b9b758..fbab066f8 100644 --- a/front/salix/components/summary/style.scss +++ b/front/salix/components/summary/style.scss @@ -4,50 +4,48 @@ margin: 0 auto; max-width: 950px; - & > div { - & > h5 { - padding: $spacing-sm; - border: none; - background: $color-main; - color: $color-font-dark; - margin: 0; - text-align: center; - line-height: 1.3em; + & > h5 { + padding: $spacing-sm; + border: none; + background: $color-main; + color: $color-font-dark; + margin: 0; + text-align: center; + line-height: 1.3em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + & > vn-horizontal { + flex-wrap: wrap; + padding: $spacing-md; + overflow: hidden; + align-items: flex-start; + + h4 { + margin-bottom: $spacing-md; + text-transform: uppercase; + font-size: 15pt; + line-height: 1; + padding: 7px; + padding-bottom: 4px; /* Bottom line-height fix */ + font-weight: lighter; + background-color: $color-main-light; + border-bottom: .1em solid $color-main; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - & > vn-horizontal { - flex-wrap: wrap; - padding: $spacing-md; - overflow: hidden; - align-items: flex-start; - - h4 { - margin-bottom: $spacing-md; - text-transform: uppercase; - font-size: 15pt; - line-height: 1; - padding: 7px; - padding-bottom: 4px; /* Bottom line-height fix */ - font-weight: lighter; - background-color: $color-main-light; - border-bottom: .1em solid $color-main; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - & > * { - margin: $spacing-sm; - min-width: 14em; - padding: 0; - } - & > vn-auto { - width: 100%; - } - vn-label-value > section { - margin-bottom: .3em; - } + & > * { + margin: $spacing-sm; + min-width: 14em; + padding: 0; + } + & > vn-auto { + width: 100%; + } + vn-label-value > section { + margin-bottom: .3em; } } p:after { diff --git a/front/salix/styles/misc.scss b/front/salix/styles/misc.scss index 0983264bc..82f4615c9 100644 --- a/front/salix/styles/misc.scss +++ b/front/salix/styles/misc.scss @@ -61,11 +61,11 @@ vn-bg-title { } .totalBox { border: 1px solid #CCC; - text-align: right !important; + text-align: right; justify-content: center; align-items: center; - padding: 18px; - max-width: 12em; + padding: $spacing-md; + max-width: 14em; } .form { height: 100%; diff --git a/front/salix/styles/order-product.scss b/front/salix/styles/order-product.scss index 9c44b30eb..df14f5558 100644 --- a/front/salix/styles/order-product.scss +++ b/front/salix/styles/order-product.scss @@ -11,7 +11,7 @@ width: 28em; overflow: hidden; - & > vn-card > div { + & > vn-card { display: flex; height: 12em; diff --git a/modules/agency/front/calendar/index.html b/modules/agency/front/calendar/index.html index 6b0714734..9a5f97269 100644 --- a/modules/agency/front/calendar/index.html +++ b/modules/agency/front/calendar/index.html @@ -1,22 +1,28 @@ - - - - \ No newline at end of file + +
+ + + {{$ctrl.firstDay | date:'MMMM yyyy'}} - {{$ctrl.lastDay | date:'MMMM yyyy'}} + + +
+
+ + +
+
\ No newline at end of file diff --git a/modules/agency/front/calendar/index.js b/modules/agency/front/calendar/index.js index 74588d9ef..00c6a7dff 100644 --- a/modules/agency/front/calendar/index.js +++ b/modules/agency/front/calendar/index.js @@ -5,96 +5,151 @@ import './style.scss'; class Controller extends Section { constructor($el, $, $t, $http, $state) { super($el, $, $t, $http, $state); + this.nMonths = 4; - this.excls = {}; - this.resetEvents(); - this.ndMonthDate = new Date(); - this.ndMonthDate.setMonth(this.ndMonthDate.getMonth() + 1); + let date = new Date(); + date.setDate(1); + date.setHours(0, 0, 0, 0); + this.date = date; + } + + get date() { + return this._date; + } + + set date(value) { + this._date = value; + let stamp = value.getTime(); + + let firstDay = new Date(stamp); + firstDay.setDate(1); + this.firstDay = firstDay; + + let lastDay = new Date(stamp); + lastDay.setMonth(lastDay.getMonth() + this.nMonths); + lastDay.setDate(0); + this.lastDay = lastDay; + + this.months = []; + for (let i = 0; i < this.nMonths; i++) { + let monthDate = new Date(stamp); + monthDate.setMonth(value.getMonth() + i); + this.months.push(monthDate); + } + + this.refreshEvents(); + } + + step(direction) { + let date = new Date(this.date.getTime()); + date.setMonth(date.getMonth() + (this.nMonths * direction)); + this.date = date; + } + + get data() { + return this._data; + } + + set data(value) { + this._data = value; + + value = value || {}; + this.events = value.events; + this.exclusions = value.exclusions; + + if (this.events) { + let codes = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + for (event of this.events) { + if (!event.weekDays) continue; + let weekDays = event.weekDays.split(','); + event.wdays = []; + for (let wday of weekDays) { + let index = codes.indexOf(wday); + if (index !== -1) event.wdays[index] = true; + } + } + } + + this.refreshEvents(); + + let calendars = this.element.querySelectorAll('vn-calendar'); + for (let calendar of calendars) + calendar.$ctrl.repaint(); + } + + refreshEvents() { + function getDate(date) { + return date && new Date(date).setHours(0, 0, 0, 0); + } + + let exclusionsMap = {}; + if (this.exclusions) { + for (let exclusion of this.exclusions) + exclusionsMap[getDate(exclusion.day)] = exclusion; + } + + this.days = {}; + let day = new Date(this.firstDay.getTime()); + + while (day < this.lastDay) { + let stamp = day.getTime(); + let wday = day.getDay(); + let dayEvents = []; + + if (this.events) { + for (let event of this.events) { + let match; + let from = getDate(event.from); + let to = getDate(event.to); + + if (event.from && event.to) { + match = stamp >= from && stamp <= to + && event.wdays[wday]; + } else if (event.from) + match = from == stamp; + else + match = event.wdays[wday]; + + if (match) + dayEvents.push(event); + } + } + + let exclusion = exclusionsMap[stamp]; + + if (dayEvents.length || exclusion) { + let dayData = {}; + if (dayEvents.length) dayData.events = dayEvents; + if (exclusion) dayData.exclusion = exclusion; + this.days[stamp] = dayData; + } + + day.setDate(day.getDate() + 1); + } } onSelection($days, $type, $weekday) { - this.emit('selection', {$days, $type, $weekday}); - } - - resetEvents() { - this.wdays = []; - this.days = {}; - this.ranges = []; - } - - get events() { - return this._events; - } - - set events(value) { - this._events = value; - this.resetEvents(); - if (!value) return; - - function setWdays(wdays, weekDays) { - let codes = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - if (!weekDays) return []; - weekDays = weekDays.split(','); - for (let wday of weekDays) - wdays[codes.indexOf(wday)] = true; - return wdays; + let $data = []; + for (let day of $days) { + let dayData = this.days[day.getTime()]; + if (dayData) $data.push(dayData); } - for (let event of value) { - if (event.from && event.to) { - this.ranges.push({ - from: new Date(event.from).setHours(0, 0, 0, 0), - to: new Date(event.to).setHours(0, 0, 0, 0), - wdays: setWdays([], event.weekDays) - }); - } else if (event.from) { - let day = new Date(event.from).setHours(0, 0, 0, 0); - this.days[day] = true; - } else - setWdays(this.wdays, event.weekDays); - } - - this.repaint(); - } - - get exclusions() { - return this._exclusions; - } - - set exclusions(value) { - this._exclusions = value; - this.excls = {}; - if (!value) return; - - value.forEach(exclusion => { - let day = new Date(exclusion.day).setHours(0, 0, 0, 0); - this.excls[day] = true; + this.emit('selection', { + $days, + $type, + $weekday, + $data }); - - this.repaint(); - } - - hasRange(time, wday) { - let range = this.ranges.find(e => e.from <= time && e.to >= time); - return range && range.wdays[wday]; } hasEvents(day) { - let time = day.getTime(); - let wday = day.getDay(); - return this.wdays[wday] - || this.days[time] - || this.hasRange(time, wday); + return this.days[day.getTime()] != null; } getClass(day) { - if (this.excls[day.getTime()]) - return 'excluded'; - } - - repaint() { - this.$.stMonth.repaint(); - this.$.ndMonth.repaint(); + let dayData = this.days[day.getTime()]; + return dayData && dayData.exclusion ? 'excluded' : ''; } } @@ -102,7 +157,6 @@ ngModule.component('vnZoneCalendar', { template: require('./index.html'), controller: Controller, bindings: { - events: ' vn-card { + & > .header { + display: flex; + align-items: center; + justify-content: space-between; + background-color: $color-main; + color: white; + font-weight: bold; + height: 45px; + + & > .vn-button { + color: inherit; + height: 100%; + } } - &.excluded .day-number { - background-color: $color-alert; - color: $color-font-dark; + & > .calendars { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + + & > vn-calendar { + max-width: 18em; + + .day { + &.event .day-number { + background-color: $color-success; + } + &.excluded .day-number { + background-color: $color-alert; + } + } + } } } } \ No newline at end of file diff --git a/modules/agency/front/delivery-days/index.html b/modules/agency/front/delivery-days/index.html index 6d5b3a42b..af71f75d0 100644 --- a/modules/agency/front/delivery-days/index.html +++ b/modules/agency/front/delivery-days/index.html @@ -1,47 +1,43 @@ -
+
- - - - - - - - - -
{{name}}
-
- {{country.country}} -
-
-
- - -
-
+ + + + + + + + +
{{name}}
+
+ {{country.country}} +
+
+
+ + +
- - - - + +
diff --git a/modules/agency/front/delivery-days/index.js b/modules/agency/front/delivery-days/index.js index cc3302ecc..12e0c4b30 100644 --- a/modules/agency/front/delivery-days/index.js +++ b/modules/agency/front/delivery-days/index.js @@ -11,7 +11,7 @@ class Controller { onSubmit() { this.$http.get(`/api/Zones/getEvents`, {params: this.params}) - .then(res => this.$.events = res.data); + .then(res => this.$.data = res.data); } } Controller.$inject = ['$scope', '$http']; diff --git a/modules/agency/front/events/index.html b/modules/agency/front/events/index.html index 91864dc7c..a72045555 100644 --- a/modules/agency/front/events/index.html +++ b/modules/agency/front/events/index.html @@ -1,27 +1,60 @@
- + +
+ +
+
+ Edit mode +
+ + + + + + +
+
+ Events +
+ - - - - -
- - {{wday.abr}} - -
+ ng-model="$ctrl.selected.wdays"> + - - + + + +
this.$.exclusions = res.data); - - this.path = `/api/Zones/${this.$stateParams.id}/events`; + this.path = `api/Zones/${this.$stateParams.id}/events`; + this.exclusionsPath = `api/Zones/${this.$stateParams.id}/exclusions`; this.refresh(); } refresh() { - this.$http.get(this.path) - .then(res => this.$.data = res.data); + let data = {}; + this.$q.all([ + this.$http.get(this.path) + .then(res => data.events = res.data), + this.$http.get(this.exclusionsPath) + .then(res => data.exclusions = res.data) + ]).finally(() => { + this.$.data = data; + }); } formatWdays(weekDays) { @@ -55,16 +30,43 @@ class Controller extends Section { let abrWdays = []; for (let wday of weekDays.split(',')) - abrWdays.push(this.abrWdays[wday]); + abrWdays.push(this.vnWeekDays.map[wday].localeAbr); return abrWdays.length < 7 ? abrWdays.join(', ') : this._('Everyday'); } - onEdit(row, event) { - if (event.defaultPrevented) return; + onSelection(days, type, weekday, data) { + if (this.editMode == 'include') { + let dayData = data[0] || {}; + let event = dayData.events && dayData.events[0]; + if (event) + this.edit(event); + else + this.create(days, type, weekday); + } else { + let exclusions = []; + for (let dayData of data) { + if (dayData.exclusion) + exclusions.push(dayData.exclusion.id); + } + + if (exclusions.length) + this.exclusionDelete(exclusions); + else + this.exclusionCreate(days); + } + } + + onEditClick(row, event) { + if (event.defaultPrevented) return; + event.preventDefault(); + this.edit(row); + } + + edit(row) { this.isNew = false; if (row.from && !row.to) @@ -86,67 +88,74 @@ class Controller extends Section { this.$.dialog.show(); } - onCreate($day, $type, $weekday) { + create(days, type, weekday) { this.isNew = true; - this.eventType = $type == 'day' ? 'day' : 'indefinitely'; + this.eventType = type == 'day' ? 'day' : 'indefinitely'; - if ($type == 'weekday') { + if (type == 'weekday') { let wdays = []; - let index = $weekday - 1; - if (index < 0) index = 7 - index; - wdays[this.wdays[index].code] = true; + let code = this.vnWeekDays.days[weekday].code; + wdays[code] = true; this.selected = {wdays}; } else - this.selected = {from: $day[0]}; + this.selected = {from: days[0]}; this.$.dialog.show(); } onSave(response) { - if (response != 'ACCEPT') return; + if (response == 'ACCEPT') { + let selected = this.selected; - let selected = this.selected; - - if (this.eventType == 'indefinitely') { - selected.from = null; - selected.to = null; - } - - if (this.eventType != 'day') { - let weekDays = []; - - for (let wday in selected.wdays) { - if (selected.wdays[wday]) - weekDays.push(wday); + if (this.eventType == 'indefinitely') { + selected.from = null; + selected.to = null; } - selected.weekDays = weekDays.join(','); - } else { - selected.to = null; - selected.weekDays = ''; + if (this.eventType != 'day') { + let weekDays = []; + + for (let wday in selected.wdays) { + if (selected.wdays[wday]) + weekDays.push(wday); + } + + selected.weekDays = weekDays.join(','); + } else { + selected.to = null; + selected.weekDays = ''; + } + + let req; + + if (this.isNew) + req = this.$http.post(this.path, selected); + else + req = this.$http.put(`${this.path}/${selected.id}`, selected); + + req.then(() => { + this.selected = null; + this.isNew = null; + this.$.dialog.hide(); + this.refresh(); + }); + + return false; + } else if (response == 'DELETE') { + this.onDelete(this.selected.id); + return false; } - - let req; - - if (this.isNew) - req = this.$http.post(this.path, selected); - else - req = this.$http.put(`${this.path}/${selected.id}`, selected); - - req.then(() => { - this.selected = null; - this.isNew = null; - this.$.dialog.hide(); - this.refresh(); - }); - - return false; } - onDelete(id, event) { + onDeleteClick(id, event) { + if (event.defaultPrevented) return; event.preventDefault(); - this.$.confirm.show(); + this.onDelete(id); + } + + onDelete(id) { this.deleteId = id; + this.$.confirm.show(); } delete(response) { @@ -156,9 +165,33 @@ class Controller extends Section { .then(() => { this.refresh(); this.deleteId = null; + this.$.dialog.hide(); }); } + + exclusionCreate(days) { + let exclusions = days.map(day => { + return {day}; + }); + + this.$http.post(this.exclusionsPath, exclusions) + .then(() => this.refresh()); + } + + exclusionDelete(ids) { + let promises = []; + + for (let id of ids) { + promises.push( + this.$http.delete(`${this.exclusionsPath}/${id}`) + ); + } + + this.$q.all(promises) + .then(() => this.refresh()); + } } +Controller.$inject = ['$element', '$scope', '$translate', '$http', '$state', '$q', 'vnWeekDays']; ngModule.component('vnZoneEvents', { template: require('./index.html'), diff --git a/modules/agency/front/events/locale/es.yml b/modules/agency/front/events/locale/es.yml new file mode 100644 index 000000000..eb0894c63 --- /dev/null +++ b/modules/agency/front/events/locale/es.yml @@ -0,0 +1,4 @@ +Edit mode: Modo de edición +Include: Incluir +Exclude: Excluir +Events: Eventos \ No newline at end of file diff --git a/modules/agency/front/events/style.scss b/modules/agency/front/events/style.scss deleted file mode 100644 index 4e14aa75c..000000000 --- a/modules/agency/front/events/style.scss +++ /dev/null @@ -1,28 +0,0 @@ -@import "effects"; - -vn-zone-events { - .week-days { - margin-top: $spacing-sm; - margin-bottom: $spacing-md; - text-align: center; - - & > span { - @extend %clickable; - border-radius: 50%; - padding: .4em; - margin: .2em; - display: inline-flex; - width: 1.5em; - height: 1.5em; - justify-content: center; - align-items: center; - outline: none; - background-color: rgba(0, 0, 0, .05); - - &.marked { - background: $color-main; - color: $color-font-dark; - } - } - } -} \ No newline at end of file diff --git a/modules/agency/front/exclusions/index.html b/modules/agency/front/exclusions/index.html deleted file mode 100644 index d6e20d060..000000000 --- a/modules/agency/front/exclusions/index.html +++ /dev/null @@ -1,30 +0,0 @@ -
- - - - - - {{::row.day | date:'dd/MM/yyyy'}} - - - - - - - - - -
- - - - diff --git a/modules/agency/front/exclusions/index.js b/modules/agency/front/exclusions/index.js deleted file mode 100644 index 133882671..000000000 --- a/modules/agency/front/exclusions/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import ngModule from '../module'; -import Section from 'core/lib/section'; - -class Controller extends Section { - constructor($el, $, $t, $http, $state) { - super($el, $, $t, $http, $state); - - this.$http.get(`/api/Zones/${this.$stateParams.id}/events`) - .then(res => this.$.events = res.data); - - this.path = `/api/Zones/${this.$stateParams.id}/exclusions`; - this.refresh(); - } - - refresh() { - this.$http.get(this.path) - .then(res => this.$.data = res.data); - } - - onCreate($days) { - this.$http.post(this.path, {day: $days[0]}) - .then(() => this.refresh()); - } - - onDelete(id) { - if (!id) return; - this.$http.delete(`${this.path}/${id}`) - .then(() => this.refresh()); - } -} - -ngModule.component('vnZoneExclusions', { - template: require('./index.html'), - controller: Controller -}); diff --git a/modules/agency/front/index.js b/modules/agency/front/index.js index e182ad061..556ec58ad 100644 --- a/modules/agency/front/index.js +++ b/modules/agency/front/index.js @@ -12,6 +12,5 @@ import './basic-data'; import './warehouses'; import './events'; import './calendar'; -import './exclusions'; import './location'; import './calendar'; diff --git a/modules/agency/front/routes.json b/modules/agency/front/routes.json index 534281595..26d619172 100644 --- a/modules/agency/front/routes.json +++ b/modules/agency/front/routes.json @@ -8,8 +8,7 @@ {"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"}, - {"state": "zone.card.exclusions", "icon": "block"} + {"state": "zone.card.events", "icon": "today"} ], "routes": [ { @@ -65,11 +64,6 @@ "state": "zone.card.events", "component": "vn-zone-events", "description": "Calendar" - }, { - "url": "/exclusions", - "state": "zone.card.exclusions", - "component": "vn-zone-exclusions", - "description": "Exclusions" }, { "url": "/location?q", "state": "zone.card.location", diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html index a8225981f..cbebb18ed 100644 --- a/modules/worker/front/calendar/index.html +++ b/modules/worker/front/calendar/index.html @@ -1,36 +1,39 @@ + url="api/AbsenceTypes" + data="absenceTypes" + auto-load="true">
- - -
- - -
-
-
- - - -
Holidays
- - {{'Used' | translate}} {{$ctrl.calendar.holidaysEnjoyed}} - {{'of' | translate}} {{$ctrl.calendar.totalHolidays}} {{'days' | translate}} - -
+
+ + + + +
+
+ +
+
+
Holidays
- - - - {{legend.name}} - + {{'Used' | translate}} {{$ctrl.calendar.holidaysEnjoyed}} + {{'of' | translate}} {{$ctrl.calendar.totalHolidays}} {{'days' | translate}}
- - -
\ No newline at end of file +
+
+ + + + {{absenceType.name}} + +
+
+ \ No newline at end of file diff --git a/modules/worker/front/calendar/index.js b/modules/worker/front/calendar/index.js index d1b042619..a25a4a113 100644 --- a/modules/worker/front/calendar/index.js +++ b/modules/worker/front/calendar/index.js @@ -2,11 +2,39 @@ import ngModule from '../module'; import './style.scss'; class Controller { - constructor($scope, $http) { - this.$scope = $scope; + constructor($element, $http) { + this.element = $element[0]; this.$http = $http; - this.months = this.monthsOfYear(); - this.events = []; + this.date = new Date(); + this.events = {}; + } + + get date() { + return this._date; + } + + set date(value) { + this._date = value; + value.setHours(0, 0, 0, 0); + + const started = new Date(value.getTime()); + started.setMonth(0); + started.setDate(1); + this.started = started; + + const ended = new Date(value.getTime()); + ended.setMonth(12); + ended.setDate(0); + this.ended = ended; + + this.months = new Array(12); + + for (let i = 0; i < this.months.length; i++) { + const now = new Date(value.getTime()); + now.setDate(1); + now.setMonth(i); + this.months[i] = now; + } } get worker() { @@ -15,118 +43,67 @@ class Controller { set worker(value) { this._worker = value; - if (!value) return; - let params = {params: { + let params = { workerFk: this.worker.id, started: this.started, ended: this.ended - }}; - - let query = `/worker/api/WorkerCalendars/absences`; - this.$http.get(query, params).then(res => { - if (!res.data) return; - - this.setHolidays(res.data); - this.setWorkerCalendar(res.data); - this.calendar = res.data.calendar; - }); + }; + this.$http.get(`api/WorkerCalendars/absences`, {params}) + .then(res => this.onData(res.data)); } - setHolidays(data) { - if (!data.holidays) return; + onData(data) { + this.events = {}; + this.calendar = data.calendar; - const holidays = data.holidays; - const events = []; - holidays.forEach(holiday => { - const holidayDetail = holiday.detail && holiday.detail.description; - const holidayType = holiday.type && holiday.type.name; - const holidayName = holidayDetail || holidayType; + let addEvent = (day, event) => { + this.events[new Date(day).getTime()] = event; + }; - events.push({ - name: holidayName, - description: holidayName, - dated: holiday.dated, - isRemovable: false, - style: {backgroundColor: '#FFFF00'} - }); - }); - this.events = this.events.concat(events); - } + if (data.holidays) { + data.holidays.forEach(holiday => { + const holidayDetail = holiday.detail && holiday.detail.description; + const holidayType = holiday.type && holiday.type.name; + const holidayName = holidayDetail || holidayType; - setWorkerCalendar(data) { - if (!data.absences) return; - - const absences = data.absences; - const events = []; - absences.forEach(absence => { - const absenceType = absence.absenceType; - events.push({ - name: absenceType.name, - description: absenceType.name, - dated: absence.dated, - style: {backgroundColor: absenceType.rgb} - }); - }); - this.events = this.events.concat(events); - } - - get started() { - const started = new Date(); - started.setHours(0, 0, 0, 0); - started.setMonth(0); - started.setDate(1); - - return started; - } - - get ended() { - const monthIndex = 11; - const ended = new Date(); - ended.setHours(0, 0, 0, 0); - ended.setMonth(monthIndex + 1); - // Last day of previous month (January) - ended.setDate(0); - - return ended; - } - - monthsOfYear() { - let months = new Array(12); - - for (let i = 0; i < months.length; i++) { - const now = new Date(); - now.setHours(0, 0, 0, 0); - now.setDate(1); - now.setMonth(i); - - months[i] = now; - } - - return months; - } - - get absenceTypes() { - return this._absenceTypes; - } - - set absenceTypes(value) { - if (value) { - this._absenceTypes = value; - this.legends = []; - this._absenceTypes.forEach(absenceType => { - let legend = {}; - legend.id = absenceType.id; - legend.color = absenceType.rgb; - legend.name = absenceType.name; - this.legends.push(legend); + addEvent(holiday.dated, { + name: holidayName, + color: '#ff0' + }); }); } + if (data.absences) { + data.absences.forEach(absence => { + let type = absence.absenceType; + addEvent(absence.dated, { + name: type.name, + color: type.rgb + }); + }); + } + + this.repaint(); + } + + repaint() { + let calendars = this.element.querySelectorAll('vn-calendar'); + for (let calendar of calendars) + calendar.$ctrl.repaint(); + } + + formatDay(day, element) { + let event = this.events[day.getTime()]; + if (!event) return; + + let dayNumber = element.firstElementChild; + dayNumber.title = event.name; + dayNumber.style.backgroundColor = event.color; + dayNumber.style.color = 'white'; } } - -Controller.$inject = ['$scope', '$http']; +Controller.$inject = ['$element', '$http']; ngModule.component('vnWorkerCalendar', { template: require('./index.html'), diff --git a/modules/worker/front/calendar/index.spec.js b/modules/worker/front/calendar/index.spec.js index 90c50ba01..361c0efbf 100644 --- a/modules/worker/front/calendar/index.spec.js +++ b/modules/worker/front/calendar/index.spec.js @@ -2,147 +2,111 @@ import './index'; describe('Worker', () => { describe('Component vnWorkerCalendar', () => { - let $componentController; - let $httpParamSerializer; let $httpBackend; let $scope; + let $element; let controller; + let year = new Date().getFullYear(); beforeEach(angular.mock.module('worker', $translateProvider => { $translateProvider.translations('en', {}); })); - beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { - $componentController = _$componentController_; + beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { $scope = $rootScope.$new(); $httpBackend = _$httpBackend_; - $httpParamSerializer = _$httpParamSerializer_; - controller = $componentController('vnWorkerCalendar', {$scope, $httpBackend}); + $element = angular.element('
'); + controller = $componentController('vnWorkerCalendar', {$element, $scope}); })); - describe('started() getter', () => { - it(`should return first day and month of current year`, () => { - let started = new Date(); - started.setHours(0, 0, 0, 0); - started.setMonth(0); - started.setDate(1); + afterEach(() => { + $element.remove(); + $scope.$destroy(); + }); + describe('started property', () => { + it(`should return first day and month of current year`, () => { + let started = new Date(year, 0, 1); expect(controller.started).toEqual(started); }); }); - describe('ended() getter', () => { + describe('ended property', () => { it(`should return last day and month of current year`, () => { - const monthIndex = 11; - const ended = new Date(); - ended.setHours(0, 0, 0, 0); - ended.setMonth(monthIndex + 1); - // Last day of previous month (January) - ended.setDate(0); + let ended = new Date(year, 11, 31); expect(controller.ended).toEqual(ended); }); }); - describe('monthsOfYear()', () => { + describe('months property', () => { it(`should return an array of twelve months length`, () => { - const months = controller.monthsOfYear(); - const ended = new Date(); - ended.setHours(0, 0, 0, 0); - ended.setMonth(11); - ended.setDate(1); + const ended = new Date(year, 11, 1); - expect(months.length).toEqual(12); - expect(months[0]).toEqual(controller.started); - expect(months[11]).toEqual(ended); + expect(controller.months.length).toEqual(12); + expect(controller.months[0]).toEqual(controller.started); + expect(controller.months[11]).toEqual(ended); }); }); describe('worker() setter', () => { - it(`should perform a get query and call setHolidays() and setWorkerCalendar() methods`, () => { - const worker = {id: 106}; - spyOn(controller, 'setHolidays'); - spyOn(controller, 'setWorkerCalendar'); + it(`should perform a get query and set the reponse data on the model`, () => { + let today = new Date(); - const expectedData = { - calendar: {}, - absences: {} - }; - let params = $httpParamSerializer({ - workerFk: worker.id, - started: controller.started, - ended: controller.ended - }); - $httpBackend.when('GET', `/worker/api/WorkerCalendars/absences?${params}`).respond(expectedData); - $httpBackend.expect('GET', `/worker/api/WorkerCalendars/absences?${params}`); + let tomorrow = new Date(today.getTime()); + tomorrow.setDate(tomorrow.getDate() + 1); - controller.worker = worker; + let yesterday = new Date(today.getTime()); + yesterday.setDate(yesterday.getDate() - 1); + let path = `api/WorkerCalendars/absences`; + $httpBackend.whenGET(url => url.startsWith(path)) + .respond({ + holidays: [ + {dated: today, detail: {description: 'New year'}}, + {dated: tomorrow, detail: {description: 'Easter'}} + ], + absences: [ + {dated: today, absenceType: {name: 'Holiday', rgb: '#aaa'}}, + {dated: yesterday, absenceType: {name: 'Leave', rgb: '#bbb'}} + ] + }); + + controller.worker = {id: 1}; $httpBackend.flush(); - expect(controller.setHolidays).toHaveBeenCalledWith(expectedData); - expect(controller.setHolidays).toHaveBeenCalledWith(expectedData); + let events = controller.events; + + expect(events[today.getTime()].name).toEqual('Holiday'); + expect(events[tomorrow.getTime()].name).toEqual('Easter'); + expect(events[yesterday.getTime()].name).toEqual('Leave'); + expect(events[yesterday.getTime()].color).toEqual('#bbb'); }); }); - describe('setHolidays()', () => { - it(`should set holidays`, () => { - const data = {holidays: [ - {dated: new Date(), detail: {description: 'New year'}}, - {dated: new Date(), detail: {description: 'Easter'}} - ]}; - controller.setHolidays(data); + describe('formatDay()', () => { + it(`should set the day element style`, () => { + let today = new Date(); - expect(controller.events.length).toEqual(2); - expect(controller.events[0].name).toEqual('New year'); - expect(controller.events[0].isRemovable).toEqual(false); - }); - }); + let path = `api/WorkerCalendars/absences`; + $httpBackend.whenGET(url => url.startsWith(path)) + .respond({ + absences: [ + {dated: today, absenceType: {name: 'Holiday', rgb: '#000'}} + ] + }); - describe('setWorkerCalendar()', () => { - it(`should set absences of differente types`, () => { - const data = {absences: [ - {dated: new Date(), absenceType: {name: 'Holiday', rgb: '#000'}}, - {dated: new Date(), absenceType: {name: 'Leave', rgb: '#000'}} - ]}; - controller.setWorkerCalendar(data); + controller.worker = {id: 1}; + $httpBackend.flush(); - expect(controller.events.length).toEqual(2); - expect(controller.events[0].name).toEqual('Holiday'); - expect(controller.events[0].style).toBeDefined(); - expect(controller.events[1].name).toEqual('Leave'); - expect(controller.events[1].style).toBeDefined(); - }); - }); + let dayElement = angular.element('
')[0]; + let dayNumber = dayElement.firstElementChild; - describe('absenceTypes() setter', () => { - it(`should set the absence types in the controller`, () => { - const absenceTypes = [ - {id: 1, name: 'Holiday', rgb: '#000'}, - {id: 2, name: 'Leave', rgb: '#000'} - ]; + controller.formatDay(today, dayElement); - expect(controller._absenceTypes).not.toBeDefined(); - - controller.absenceTypes = absenceTypes; - - expect(controller._absenceTypes.length).toEqual(2); - }); - - it(`should set the absence types in the controller as formated legends`, () => { - const absenceTypes = [ - {id: 1, name: 'Holiday', rgb: '#000'}, - {id: 2, name: 'Leave', rgb: '#000'} - ]; - - expect(controller.legends).not.toBeDefined(); - - controller.absenceTypes = absenceTypes; - - expect(controller.legends.length).toEqual(2); - expect(controller.legends[0].color).toBeDefined(); - expect(controller.legends[1].color).toBeDefined(); + expect(dayNumber.title).toEqual('Holiday'); + expect(dayNumber.style.backgroundColor).toEqual('rgb(0, 0, 0)'); }); }); }); diff --git a/modules/worker/front/calendar/style.scss b/modules/worker/front/calendar/style.scss index cb93174af..e622dda64 100644 --- a/modules/worker/front/calendar/style.scss +++ b/modules/worker/front/calendar/style.scss @@ -1,34 +1,19 @@ @import "variables"; - -/* .calendar-list .calendar:nth-child(2n + 1) { - border-right:1px solid #ddd -} */ - -.calendar-list { - align-items: flex-start; - flex-wrap: wrap; - justify-content: center; - - .calendar { +vn-worker-calendar { + .calendars { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; box-sizing: border-box; padding: $spacing-md; - overflow: hidden; - min-width: 18em; - width: 25%; - - vn-calendar { + + & > vn-calendar { border: 1px solid #ddd; - padding: 0.5em + margin: $spacing-md; + padding: $spacing-xs; + max-width: 18em; } } } - -vn-horizontal.header { - justify-content: flex-end; - margin-bottom: 0.5em; - - .totalBox { - max-width: 15em - } -} \ No newline at end of file diff --git a/modules/worker/front/time-control/index.html b/modules/worker/front/time-control/index.html index 7eaed4ec5..54c44973c 100644 --- a/modules/worker/front/time-control/index.html +++ b/modules/worker/front/time-control/index.html @@ -1,76 +1,73 @@
- - - - - - -
{{::$ctrl.weekdayNames[$index].name}}
- {{::weekday.dated | date: 'dd'}} - - {{::weekday.dated | date: 'MMMM'}} - -
-
-
- - - -
- - - {{hour.timed | date: 'HH:mm'}} -
-
-
-
- + + + - + +
{{::$ctrl.weekdayNames[$index].name}}
+ {{::weekday.dated | date: 'dd'}} + + {{::weekday.dated | date: 'MMMM'}} + +
+
+
+ + + +
+ + + {{hour.timed | date: 'HH:mm'}} +
+
+
+
+ + + {{$ctrl.getWeekdayTotalHours(weekday)}} h. - - - - - - - -
-
+ + + + + + + +
- - +
+
Hours
+ value="{{$ctrl.weekTotalHours}} h."> - - - - +
+ + +
- -
diff --git a/modules/worker/front/time-control/index.js b/modules/worker/front/time-control/index.js index 9bb04ac03..622ecd16e 100644 --- a/modules/worker/front/time-control/index.js +++ b/modules/worker/front/time-control/index.js @@ -2,155 +2,91 @@ import ngModule from '../module'; import './style.scss'; class Controller { - constructor($scope, $http, $stateParams, $element) { + constructor($scope, $http, $stateParams, $element, vnWeekDays) { this.$stateParams = $stateParams; this.$ = $scope; this.$http = $http; this.$element = $element; - this.defaultDate = new Date(); - this.currentWeek = []; this.weekDays = []; - this.weekdayNames = [ - {name: 'Monday'}, - {name: 'Tuesday'}, - {name: 'Wednesday'}, - {name: 'Thursday'}, - {name: 'Friday'}, - {name: 'Saturday'}, - {name: 'Sunday'} - ]; + this.weekdayNames = vnWeekDays.locales; } + $postLink() { + this.date = new Date(); + } /** - * Gets worker data + * The current selected date */ - get worker() { - return this._worker; + get date() { + return this._date; + } + + set date(value) { + this._date = value; + value.setHours(0, 0, 0, 0); + + let weekOffset = value.getDay() - 1; + if (weekOffset < 0) weekOffset = 6; + + let started = new Date(value.getTime()); + started.setDate(started.getDate() - weekOffset); + this.started = started; + + let ended = new Date(started.getTime()); + ended.setDate(ended.getDate() + 7); + this.ended = ended; + + this.fetchHours(); } /** - * Sets worker data and retrieves - * worker hours - * - * @param {Object} value - Worker data - */ - set worker(value) { - this._worker = value; - - if (value) { - this.$.$applyAsync(() => { - this.getHours(); - }); - } - } - - /** - * Gets worker hours data - * + * Worker hours data */ get hours() { return this._hours; } - /** - * Gets worker hours data - * - * @param {Object} value - Hours data - */ set hours(value) { this._hours = value; - if (value) this.build(); + this.weekDays = []; + if (!this.hours) return; + + let dayIndex = new Date(this.started.getTime()); + + while (dayIndex < this.ended) { + let weekDay = dayIndex.getDay(); + + let hours = this.hours + .filter(hour => new Date(hour.timed).getDay() == weekDay) + .sort((a, b) => new Date(a.timed) - new Date(b.timed)); + + this.weekDays.push({ + dated: new Date(dayIndex.getTime()), + hours + }); + dayIndex.setDate(dayIndex.getDate() + 1); + } } - getHours() { - const params = {workerFk: this.worker.id}; + fetchHours() { + const params = {workerFk: this.$stateParams.id}; const filter = { - where: { - timed: {between: [this.started, this.ended]} - } + where: {and: [ + {timed: {gte: this.started}}, + {timed: {lt: this.ended}} + ]} }; - return this.$.model.applyFilter(filter, params); + this.$.model.applyFilter(filter, params); } - refresh() { - this.getHours().then(() => this.build()); - } - - build() { - let weekdays = new Array(7); - const currentWeek = []; - - for (let i = 0; i < weekdays.length; i++) { - const dated = new Date(); - dated.setHours(23, 59, 0, 0); - dated.setMonth(this.started.getMonth()); - dated.setDate(this.started.getDate() + i); - - const hours = this.hours.filter(hour => { - const timed = new Date(hour.timed); - const weekDay = timed.getDay() == 0 ? 7 : timed.getDay(); - return weekDay == (i + 1); - }); - - const sortedHours = hours.sort((a, b) => { - return new Date(a.timed) - new Date(b.timed); - }); - - weekdays[i] = {dated, hours: sortedHours}; - - currentWeek.push({ - dated: dated, - className: 'orange-circle', - title: 'Current week', - isRemovable: false - }); - } - - this.currentWeek = currentWeek; - this.weekDays = weekdays; + hasEvents(day) { + return day >= this.started && day < this.ended; } hourColor(weekDay) { - if (weekDay.manual) - return 'alert'; - - return 'warning'; - } - get weekOffset() { - const timed = this.defaultDate; - const weekDay = timed.getDay() == 0 ? 7 : timed.getDay(); - - return weekDay - 1; - } - - /** - * Returns week start date - * from current selected week - */ - get started() { - const started = new Date(); - const offset = this.weekOffset; - - started.setMonth(this.defaultDate.getMonth()); - started.setDate(this.defaultDate.getDate() - offset); - started.setHours(0, 0, 0, 0); - - return started; - } - - /** - * Returns week end date - * from current selected week - */ - get ended() { - const ended = new Date(); - ended.setHours(0, 0, 0, 0); - ended.setMonth(this.started.getMonth()); - ended.setDate(this.started.getDate() + 7); - - return ended; + return weekDay.manual ? 'alert' : 'warning'; } getWeekdayTotalHours(weekday) { @@ -200,20 +136,6 @@ class Controller { return `${hour}:${min}`; } - onMoveNext() { - this.refresh(); - } - - onMovePrevious() { - this.refresh(); - } - - onSelection($days) { - this.defaultDate.setMonth($days[0].getMonth()); - this.defaultDate.setDate($days[0].getDate()); - this.refresh(); - } - showAddTimeDialog(weekday) { const timed = new Date(weekday.dated); const now = new Date(); @@ -226,26 +148,25 @@ class Controller { this.selectedWeekday = weekday; this.$.addTimeDialog.show(); - const selector = 'vn-dialog[vn-id="addTimeDialog"] input[type="time"]'; + const selector = '[vn-id=addTimeDialog] input[type=time]'; const input = this.$element[0].querySelector(selector); input.focus(); } addTime(response) { - if (response === 'ACCEPT') { - let data = {workerFk: this.worker.id, timed: this.newTime}; - let query = `/api/WorkerTimeControls/addTime`; - this.$http.post(query, data).then(() => this.refresh()); - } + if (response !== 'ACCEPT') return; + let data = { + workerFk: this.$stateParams.id, + timed: this.newTime + }; + this.$http.post(`api/WorkerTimeControls/addTime`, data) + .then(() => this.fetchHours()); } } -Controller.$inject = ['$scope', '$http', '$stateParams', '$element']; +Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays']; ngModule.component('vnWorkerTimeControl', { template: require('./index.html'), - controller: Controller, - bindings: { - worker: '<' - } + controller: Controller }); diff --git a/modules/worker/front/time-control/index.spec.js b/modules/worker/front/time-control/index.spec.js index 1e5ae5e97..550db10f9 100644 --- a/modules/worker/front/time-control/index.spec.js +++ b/modules/worker/front/time-control/index.spec.js @@ -2,37 +2,56 @@ import './index.js'; describe('Worker', () => { describe('Component vnWorkerTimeControl', () => { + let $httpBackend; let $scope; - let controller; let $element; + let controller; beforeEach(angular.mock.module('worker', $translateProvider => { $translateProvider.translations('en', {}); })); - beforeEach(angular.mock.inject(($componentController, $rootScope) => { + beforeEach(angular.mock.inject(($compile, $rootScope, $stateParams, _$httpBackend_) => { + $stateParams.id = 1; + $httpBackend = _$httpBackend_; $scope = $rootScope.$new(); - $element = angular.element(''); - controller = $componentController('vnWorkerTimeControl', {$scope, $element}); + $element = $compile('')($scope); + controller = $element.controller('vnWorkerTimeControl'); })); - describe('worker() setter', () => { - it(`should set worker data and then call getHours() method`, () => { - spyOn(controller, 'getHours'); - controller.worker = {id: 106}; - $scope.$apply(); - - expect(controller.getHours).toHaveBeenCalledWith(); - }); + afterEach(() => { + $scope.$destroy(); + $element.remove(); }); describe('hours() setter', () => { - it(`should set hours data and then call build() method`, () => { - spyOn(controller, 'build'); - controller.hours = [{id: 1}, {id: 2}]; - $scope.$apply(); + it(`should set hours data at it's corresponding week day`, () => { + let wednesday = new Date(controller.started.getTime()); + wednesday.setDate(wednesday.getDate() + 2); - expect(controller.build).toHaveBeenCalledWith(); + let path = `api/WorkerTimeControls/filter`; + $httpBackend.whenGET(url => url.startsWith(path)) + .respond([ + { + id: 1, + timed: controller.started.toJSON(), + userFk: 1 + }, { + id: 2, + timed: wednesday.toJSON(), + userFk: 1 + }, { + id: 3, + timed: wednesday.toJSON(), + userFk: 1 + } + ]); + + $httpBackend.flush(); + + expect(controller.weekDays.length).toEqual(7); + expect(controller.weekDays[0].hours.length).toEqual(1); + expect(controller.weekDays[2].hours.length).toEqual(2); }); }); diff --git a/modules/worker/front/time-control/locale/es.yml b/modules/worker/front/time-control/locale/es.yml index cfb0239a6..9a3484fc6 100644 --- a/modules/worker/front/time-control/locale/es.yml +++ b/modules/worker/front/time-control/locale/es.yml @@ -3,5 +3,5 @@ Out: Salida Hour: Hora Hours: Horas Add time: Añadir hora -Week total: Total por semana +Week total: Total semana Current week: Semana actual \ No newline at end of file diff --git a/modules/worker/front/time-control/style.scss b/modules/worker/front/time-control/style.scss index 9917fa3fb..978a600a2 100644 --- a/modules/worker/front/time-control/style.scss +++ b/modules/worker/front/time-control/style.scss @@ -5,23 +5,20 @@ vn-worker-time-control { margin-bottom: 5px; color: $color-main } - vn-td.hours { vertical-align: top; & > section { - position: relative; - padding: .6em 0; + display: flex; + align-items: center; + padding: .3em 0; & > vn-icon { - position: absolute; - margin-left: -1.3em; - margin-top: -3px; color: $color-font-secondary; + padding-right: .1em; } } } - .totalBox { max-width: none } diff --git a/webpack.config.js b/webpack.config.js index 1efcf5f01..66f2179a9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -103,6 +103,9 @@ let baseConfig = { favicon: 'front/salix/favicon.ico', filename: 'index.html', chunks: ['salix'] + }), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }) ], devtool: 'source-map',