diff --git a/front/core/components/calendar/index.html b/front/core/components/calendar/index.html index ec2ad820d..b2fc023ae 100644 --- a/front/core/components/calendar/index.html +++ b/front/core/components/calendar/index.html @@ -9,10 +9,10 @@ - +
{{$ctrl.defaultDate | date: 'MMMM'}} {{$ctrl.defaultDate | date: 'yyyy'}} - +
-
-
+
{ - this.addEvent(event); + event.dated = new Date(event.dated); + event.dated.setHours(0, 0, 0, 0); + this.events.push(event); }); if (value.length && this.defaultDate) { @@ -197,10 +211,10 @@ export default class Calendar extends Component { const hasEvents = events.length > 0; if (isCurrentMonth && isSunday && !hasEvents) - params.style = {color: '#f42121'}; + params.style = {color: '#999'}; if (isCurrentMonth && isSaturday && !hasEvents) - params.style = {color: '#666666'}; + params.style = {color: '#999'}; if (!isCurrentMonth) params.style = {opacity: '0.5'}; @@ -217,42 +231,6 @@ export default class Calendar extends Component { this.days.push(params); } - /** - * Adds a new calendar event - * - * @param {Object} options - Event params - * @param {Date} options.dated - Day to add event - * @param {String} options.name - Tooltip description - * @param {String} options.className - ClassName style - * @param {Object} options.style - Style properties - * @param {Boolean} options.isRemovable - True if is removable by users - */ - addEvent(options) { - if (!Object.hasOwnProperty.call(options, 'isRemovable')) - options.isRemovable = true; - - options.dated = new Date(options.dated); - options.dated.setHours(0, 0, 0, 0); - - this.events.push(options); - } - - /** - * Removes an event from an array of events - * @param {Object} dated - Dated event - */ - removeEvent(dated) { - dated = new Date(dated); - dated.setHours(0, 0, 0, 0); - - const event = this.events.findIndex(event => { - return event.dated >= dated && event.dated <= dated; - }); - - if (event > -1) - this.events.splice(event, 1); - } - /** * Moves to next month(s) * @@ -261,10 +239,7 @@ export default class Calendar extends Component { moveNext(skip = 1) { let next = this.defaultDate.getMonth() + skip; this.defaultDate.setMonth(next); - this.defaultDate.setHours(0, 0, 0, 0); - this.defaultDate.setDate(1); this.repaint(); - this.emit('moveNext'); } @@ -276,12 +251,7 @@ export default class Calendar extends Component { movePrevious(skip = 1) { let previous = this.defaultDate.getMonth() - skip; this.defaultDate.setMonth(previous); - this.defaultDate.setHours(0, 0, 0, 0); - - const lastDate = this.lastDay(this.defaultDate); - this.defaultDate.setDate(lastDate.getDate()); this.repaint(); - this.emit('movePrevious'); } @@ -327,6 +297,14 @@ export default class Calendar extends Component { }; } } + + hasEvents() { + return false; + } + + getClass() { + return ''; + } } Calendar.$inject = ['$element', '$scope']; @@ -341,6 +319,8 @@ ngModule.component('vnCalendar', { onSelection: '&?', onMoveNext: '&?', onMovePrevious: '&?', + hasEvents: '&?', + getClass: '&?', displayControls: ' { }); }); - describe('addEvent()', () => { - it(`should add an event to an array of events`, () => { - controller.events = []; - controller.addEvent({ - dated: new Date(), - name: 'My event' - }); - const firstEvent = controller.events[0]; - - expect(firstEvent.name).toEqual('My event'); - expect(firstEvent.isRemovable).toBeDefined(); - expect(firstEvent.isRemovable).toBeTruthy(); - }); - - it(`should not repeat an event for the same date`, () => { - const curDate = new Date(); - curDate.setHours(0, 0, 0, 0); - - controller.events = [{ - dated: curDate, - name: 'My event 1' - }]; - controller.addEvent({ - dated: curDate, - name: 'My event 2' - }); - - const firstEvent = controller.events[0]; - - expect(controller.events.length).toEqual(2); - expect(firstEvent.name).toEqual('My event 1'); - }); - }); - - describe('removeEvent()', () => { - it(`should remove an event from an array of events`, () => { - const curDate = new Date(); - controller._events = [{ - dated: curDate, - name: 'My event 1', - className: 'color' - }]; - controller.removeEvent(curDate); - - expect(controller.events.length).toEqual(0); - }); - }); - describe('moveNext()', () => { it(`should shift to the next n months, then emit a 'moveNext' event`, () => { spyOn(controller, 'emit'); diff --git a/front/core/components/calendar/style.scss b/front/core/components/calendar/style.scss index 333bfb428..a8003bb78 100644 --- a/front/core/components/calendar/style.scss +++ b/front/core/components/calendar/style.scss @@ -5,7 +5,6 @@ vn-calendar.small { display: none } } - vn-calendar { display: block; @@ -14,8 +13,6 @@ vn-calendar { padding: 0.2em 0; height: 1.5em } - - .weekdays { color: $color-font-secondary; margin-bottom: 0.5em; @@ -23,11 +20,9 @@ vn-calendar { font-weight: bold; font-size: 0.8em; } - .weekdays section { cursor: pointer } - .weekdays section, .day { position: relative; text-align: center; @@ -35,14 +30,11 @@ vn-calendar { width: 14.28%; outline: 0; } - .days { justify-content: flex-start; align-items: flex-start; flex-wrap: wrap; } - - .day { .content { position: absolute; @@ -51,7 +43,6 @@ vn-calendar { left: 0; top: 0 } - .day-number { transition: background-color 0.3s; text-align:center; @@ -65,30 +56,24 @@ vn-calendar { 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-bg; + color: $color-font-dark; } - .events { margin-top: 0.5em; font-size: 0.6em } - - .events { color: $color-font-secondary; @@ -96,7 +81,6 @@ vn-calendar { margin-bottom: .1em; } } - .chip { background-color: $color-main; color: $color-font-bg; @@ -105,15 +89,11 @@ vn-calendar { padding: 0.3em .8em; max-width: 5em; } - .day.gray { .day-number { color: $color-font-secondary } } - - - .day.sunday { .day-number { color: $color-alert; diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml index ee57de376..597f34b8b 100644 --- a/front/core/locale/es.yml +++ b/front/core/locale/es.yml @@ -50,4 +50,9 @@ Fields to show: Campos a mostrar Create new one: Crear nuevo Toggle: Desplegar/Plegar Check all: Seleccionar todo -Select a file: Selecciona un fichero \ No newline at end of file +Select a file: Selecciona un fichero +Edit: Editar +Edit item: Editar elemento +No records found: No se han encontrado elementos +Day: Día +Days: Días \ No newline at end of file diff --git a/front/salix/styles/misc.scss b/front/salix/styles/misc.scss index 5332a38b6..1eef5af2b 100644 --- a/front/salix/styles/misc.scss +++ b/front/salix/styles/misc.scss @@ -34,6 +34,13 @@ a, .link { text-decoration: underline; } } +vn-bg-title { + display: block; + text-align: center; + padding: 1em; + color: gray; + font-size: 1.3em; +} .totalBox { border: 1px solid #CCC; text-align: right !important; diff --git a/loopback/server/middleware.json b/loopback/server/middleware.json index 7fbd27250..60bedcdb3 100644 --- a/loopback/server/middleware.json +++ b/loopback/server/middleware.json @@ -11,7 +11,7 @@ "helmet#hsts": { "params": { "maxAge": 0, - "includeSubdomains": true + "includeSubDomains": true } }, "helmet#hidePoweredBy": {}, diff --git a/modules/agency/back/methods/zone/getEvents.js b/modules/agency/back/methods/zone/getEvents.js new file mode 100644 index 000000000..1e7284819 --- /dev/null +++ b/modules/agency/back/methods/zone/getEvents.js @@ -0,0 +1,41 @@ + +module.exports = Self => { + Self.remoteMethod('getEvents', { + description: 'Returns delivery days for a postcode', + accepts: [ + { + arg: 'agencyModeFk', + type: 'Number', + description: 'The agency mode id', + required: true + }, { + arg: 'provinceFk', + type: 'Number', + description: 'The province id', + required: true + }, { + arg: 'postCode', + type: 'String', + description: 'The postcode', + required: true + } + ], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/getEvents`, + verb: 'GET' + } + }); + + Self.getEvents = async(agencyModeFk, provinceFk, postCode) => { + let [events, exclusions] = await Self.rawSql( + `CALL zone_getEvents(?, ?, ?)`, + [agencyModeFk, provinceFk, postCode] + ); + + return {events, exclusions}; + }; +}; diff --git a/modules/agency/back/model-config.json b/modules/agency/back/model-config.json index 9019ac246..aded3eb5d 100644 --- a/modules/agency/back/model-config.json +++ b/modules/agency/back/model-config.json @@ -14,10 +14,19 @@ "ZoneGeo": { "dataSource": "vn" }, + "ZoneEvent": { + "dataSource": "vn" + }, + "ZoneExclusion": { + "dataSource": "vn" + }, "ZoneCalendar": { "dataSource": "vn" }, "ZoneIncluded": { "dataSource": "vn" + }, + "ZoneWarehouse": { + "dataSource": "vn" } } diff --git a/modules/agency/back/models/zone-event.js b/modules/agency/back/models/zone-event.js new file mode 100644 index 000000000..5b1c183fd --- /dev/null +++ b/modules/agency/back/models/zone-event.js @@ -0,0 +1,9 @@ +module.exports = Self => { + function rangeValid(err) { + if (this.from && this.to && this.from >= this.to) + err(); + } + Self.validate('rangeValid', rangeValid, { + message: `Start date should be lower than end date` + }); +}; diff --git a/modules/agency/back/models/zone-event.json b/modules/agency/back/models/zone-event.json new file mode 100644 index 000000000..822328dc6 --- /dev/null +++ b/modules/agency/back/models/zone-event.json @@ -0,0 +1,47 @@ +{ + "name": "ZoneEvent", + "base": "VnModel", + "options": { + "mysql": { + "table": "zoneEvent" + } + }, + "properties": { + "id": { + "id": true, + "type": "Number" + }, + "zoneFk": { + "id": true, + "type": "Number" + }, + "from": { + "type": "Date" + }, + "to": { + "type": "Date" + }, + "weekDays": { + "type": "String" + }, + "hour": { + "type": "Date" + }, + "travelingDays": { + "type": "Number" + }, + "price": { + "type": "Number" + }, + "bonus": { + "type": "Number" + } + }, + "relations": { + "zone": { + "type": "belongsTo", + "model": "Zone", + "foreignKey": "zoneFk" + } + } +} \ No newline at end of file diff --git a/modules/agency/back/models/zone-exclusion.json b/modules/agency/back/models/zone-exclusion.json new file mode 100644 index 000000000..79e2f5aae --- /dev/null +++ b/modules/agency/back/models/zone-exclusion.json @@ -0,0 +1,26 @@ +{ + "name": "ZoneExclusion", + "base": "VnModel", + "options": { + "mysql": { + "table": "zoneExclusion" + } + }, + "properties": { + "id": { + "id": true, + "type": "Number" + }, + "day": { + "type": "Date", + "required": true + } + }, + "relations": { + "zone": { + "type": "belongsTo", + "model": "Zone", + "foreignKey": "zoneFk" + } + } +} \ No newline at end of file diff --git a/modules/agency/back/models/zone-warehouse.json b/modules/agency/back/models/zone-warehouse.json new file mode 100644 index 000000000..afb3bc2b4 --- /dev/null +++ b/modules/agency/back/models/zone-warehouse.json @@ -0,0 +1,27 @@ +{ + "name": "ZoneWarehouse", + "base": "VnModel", + "options": { + "mysql": { + "table": "zoneWarehouse" + } + }, + "properties": { + "id": { + "id": true, + "type": "Number" + } + }, + "relations": { + "zone": { + "type": "belongsTo", + "model": "Zone", + "foreignKey": "zoneFk" + }, + "warehouse": { + "type": "belongsTo", + "model": "Warehouse", + "foreignKey": "warehouseFk" + } + } +} \ No newline at end of file diff --git a/modules/agency/back/models/zone.js b/modules/agency/back/models/zone.js index 2c540603d..a6695bd02 100644 --- a/modules/agency/back/models/zone.js +++ b/modules/agency/back/models/zone.js @@ -2,10 +2,7 @@ module.exports = Self => { require('../methods/zone/clone')(Self); require('../methods/zone/editPrices')(Self); require('../methods/zone/getLeaves')(Self); - - Self.validatesPresenceOf('warehouseFk', { - message: `Warehouse cannot be blank` - }); + require('../methods/zone/getEvents')(Self); Self.validatesPresenceOf('agencyModeFk', { message: `Agency cannot be blank` diff --git a/modules/agency/back/models/zone.json b/modules/agency/back/models/zone.json index 9b03bee6a..ffa0ce1e1 100644 --- a/modules/agency/back/models/zone.json +++ b/modules/agency/back/models/zone.json @@ -38,15 +38,25 @@ "model": "ZoneGeo", "foreignKey": "zoneFk" }, - "warehouse": { - "type": "belongsTo", - "model": "Warehouse", - "foreignKey": "warehouseFk" - }, "agencyMode": { "type": "belongsTo", "model": "AgencyMode", "foreignKey": "agencyModeFk" + }, + "events": { + "type": "hasMany", + "model": "ZoneEvent", + "foreignKey": "zoneFk" + }, + "exclusions": { + "type": "hasMany", + "model": "ZoneExclusion", + "foreignKey": "zoneFk" + }, + "warehouses": { + "type": "hasMany", + "model": "ZoneWarehouse", + "foreignKey": "zoneFk" } } } \ No newline at end of file diff --git a/modules/agency/front/basic-data/index.html b/modules/agency/front/basic-data/index.html index 7eaac4ef9..4eaabae8a 100644 --- a/modules/agency/front/basic-data/index.html +++ b/modules/agency/front/basic-data/index.html @@ -5,25 +5,16 @@ form="form" save="patch"> -
+ - - - - - - - - - diff --git a/modules/agency/front/calendar/index.html b/modules/agency/front/calendar/index.html index 5ea7d70a0..9172ec4a3 100644 --- a/modules/agency/front/calendar/index.html +++ b/modules/agency/front/calendar/index.html @@ -1,74 +1,20 @@ - - - - - - - - - - - - - - - - - - - - - - - -
Edit price
- - - - - - - - - - -
- - - - -
\ No newline at end of file + + + + \ No newline at end of file diff --git a/modules/agency/front/calendar/index.js b/modules/agency/front/calendar/index.js index 5e6cb19b9..5b99264c7 100644 --- a/modules/agency/front/calendar/index.js +++ b/modules/agency/front/calendar/index.js @@ -2,133 +2,107 @@ import ngModule from '../module'; import './style.scss'; class Controller { - constructor($element, $scope, $http, $filter, $translate, $stateParams, vnApp) { - this.$element = $element; - this.$ = $scope; - this.$http = $http; - this.$filter = $filter; - this.$translate = $translate; - this.$stateParams = $stateParams; - this.vnApp = vnApp; - this.stMonthDate = new Date(); + constructor($, $stateParams) { + Object.assign(this, { + $, + $stateParams + }); + + this.excls = {}; + this.resetEvents(); this.ndMonthDate = new Date(); this.ndMonthDate.setMonth(this.ndMonthDate.getMonth() + 1); - this.selectedDay = {}; } - $postLink() { - this.stMonth = this.$.stMonth; - this.ndMonth = this.$.ndMonth; + resetEvents() { + this.wdays = []; + this.days = {}; + this.ranges = []; } - get data() { - return this._data; + get events() { + return this._events; } - set data(value) { - this._data = value; - + set events(value) { + this._events = value; + this.resetEvents(); if (!value) return; - const events = []; - value.forEach(event => { - events.push({ - name: `P: ${this.$filter('currency')(event.price)}`, - description: 'Price', - dated: event.delivered, - style: {backgroundColor: '#a3d131'}, - data: {price: event.price} - }); - events.push({ - name: `B: ${this.$filter('currency')(event.bonus)}`, - description: 'Bonus', - dated: event.delivered, - data: {bonus: event.bonus} - }); - }); - - this.events = events; - } - - onSelection(values) { - if (values.length > 1) return false; - - this.options = [ - {label: 'Only this day', value: 'Only this day'}, - {label: 'From this day', value: 'From this day'}, - {label: 'All days', value: 'All days'} - ]; - const selection = values[0]; - const events = selection.events; - const hasEvents = events.length > 0; - - if (!hasEvents) - return this.vnApp.showMessage(this.$translate.instant(`There's no delivery for this day`)); - - this.selectedDay = { - delivered: selection.dated, - option: 'Only this day' - }; - - events.forEach(event => { - this.selectedDay = Object.assign(this.selectedDay, event.data); - }); - this.$.priceDialog.show(); - } - - onResponse(response) { - if (response == 'ACCEPT') { - try { - const data = { - delivered: this.selectedDay.delivered, - price: this.selectedDay.price, - bonus: this.selectedDay.bonus, - option: this.selectedDay.option - }; - - this.$.watcher.check(); - - const path = `/api/Zones/${this.zone.id}/editPrices`; - this.$http.post(path, data).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); - this.$.model.refresh(); - this.card.reload(); - }); - } catch (e) { - this.vnApp.showError(this.$translate.instant(e.message)); - return false; - } + 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; } - return this.onClose(); + 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(); } - onClose() { - this.$.watcher.updateOriginalData(); + get exclusions() { + return this._exclusions; } - onMoveNext(calendars) { - calendars.forEach(calendar => { - calendar.moveNext(2); + 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.repaint(); } - onMovePrevious(calendars) { - calendars.forEach(calendar => { - calendar.movePrevious(2); - }); + 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); + } + + getClass(day) { + if (this.excls[day.getTime()]) + return 'excluded'; + } + + repaint() { + this.$.stMonth.repaint(); + this.$.ndMonth.repaint(); } } -Controller.$inject = ['$element', '$scope', '$http', '$filter', '$translate', '$stateParams', 'vnApp']; +Controller.$inject = ['$scope', '$stateParams']; ngModule.component('vnZoneCalendar', { template: require('./index.html'), controller: Controller, bindings: { - zone: '<' - }, - require: { - card: '^vnZoneCard' + events: ' - - - - -
- + + + + +
diff --git a/modules/agency/front/card/index.js b/modules/agency/front/card/index.js index 270ab4c27..8f3f5b8d1 100644 --- a/modules/agency/front/card/index.js +++ b/modules/agency/front/card/index.js @@ -12,10 +12,10 @@ class Controller { getCard() { let filter = { - include: [ - {relation: 'warehouse', fields: ['name']}, - {relation: 'agencyMode', fields: ['name']} - ] + include: { + relation: 'agencyMode', + scope: {fields: ['name']} + } }; let json = encodeURIComponent(JSON.stringify(filter)); let query = `/agency/api/Zones/${this.$stateParams.id}?filter=${json}`; diff --git a/modules/agency/front/delivery-days/index.html b/modules/agency/front/delivery-days/index.html new file mode 100644 index 000000000..3d4fbe4fc --- /dev/null +++ b/modules/agency/front/delivery-days/index.html @@ -0,0 +1,47 @@ +
+ + + + + + + + + + +
{{name}}
+
+ {{country.country}} +
+
+
+ + +
+
+
+ + + + + + + + +
diff --git a/modules/agency/front/delivery-days/index.js b/modules/agency/front/delivery-days/index.js new file mode 100644 index 000000000..cc3302ecc --- /dev/null +++ b/modules/agency/front/delivery-days/index.js @@ -0,0 +1,22 @@ +import ngModule from '../module'; +import './style.scss'; + +class Controller { + constructor($, $http) { + Object.assign(this, { + $, + $http + }); + } + + onSubmit() { + this.$http.get(`/api/Zones/getEvents`, {params: this.params}) + .then(res => this.$.events = res.data); + } +} +Controller.$inject = ['$scope', '$http']; + +ngModule.component('vnZoneDeliveryDays', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/agency/front/delivery-days/style.scss b/modules/agency/front/delivery-days/style.scss new file mode 100644 index 000000000..543e885f0 --- /dev/null +++ b/modules/agency/front/delivery-days/style.scss @@ -0,0 +1,12 @@ + +vn-zone-delivery-days { + vn-zone-calendar { + display: flex; + justify-content: center; + flex-wrap: wrap; + + & > vn-calendar { + min-width: 15em; + } + } +} \ No newline at end of file diff --git a/modules/agency/front/events/index.html b/modules/agency/front/events/index.html new file mode 100644 index 000000000..fb63e5180 --- /dev/null +++ b/modules/agency/front/events/index.html @@ -0,0 +1,147 @@ + + + + + + + + + + + + +
+ + {{wday.abr}} + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + diff --git a/modules/agency/front/events/index.js b/modules/agency/front/events/index.js new file mode 100644 index 000000000..c89f41e98 --- /dev/null +++ b/modules/agency/front/events/index.js @@ -0,0 +1,174 @@ +import ngModule from '../module'; +import './style.scss'; + +class Controller { + constructor($, _, $http, $stateParams) { + Object.assign(this, { + $, + _, + $http + }); + + this.wdays = [ + { + code: 'mon', + name: 'Monday' + }, { + code: 'tue', + name: 'Tuesday' + }, { + code: 'wed', + name: 'Wednesday' + }, { + code: 'thu', + name: 'Thursday' + }, { + code: 'fri', + name: 'Friday' + }, { + code: 'sat', + name: 'Saturday' + }, { + code: 'sun', + name: 'Sunday' + } + ]; + + this.abrWdays = {}; + for (let wday of this.wdays) { + let locale = _.instant(wday.name); + this.abrWdays[wday.code] = locale.substr(0, 3); + wday.abr = locale.substr(0, 1); + } + + this.options = [ + { + label: 'Specific day', + value: 'day' + }, { + label: 'Range of dates', + value: 'range' + }, { + label: 'Always', + value: 'always' + } + ]; + + this.$http.get(`/api/Zones/${$stateParams.id}/exclusions`) + .then(res => this.$.exclusions = res.data); + + this.path = `/api/Zones/${$stateParams.id}/events`; + this.refresh(); + } + + refresh() { + this.$http.get(this.path) + .then(res => this.$.data = res.data); + } + + formatWdays(weekDays) { + if (!weekDays) return; + + let abrWdays = []; + for (let wday of weekDays.split(',')) + abrWdays.push(this.abrWdays[wday]); + + return abrWdays.length < 7 + ? abrWdays.join(', ') + : this._.instant('Everyday'); + } + + onEdit(row, event) { + if (event.defaultPrevented) return; + + this.isNew = false; + + if (row.from && !row.to) + this.eventType = 'day'; + else if (!row.from) + this.eventType = 'always'; + else + this.eventType = 'range'; + + this.selected = angular.copy(row); + this.selected.wdays = {}; + + if (row.weekDays) { + let weekDays = row.weekDays.split(','); + for (let day of weekDays) + this.selected.wdays[day] = true; + } + + this.$.dialog.show(); + } + + onCreate() { + this.isNew = true; + this.eventType = 'day'; + this.selected = {}; + this.$.dialog.show(); + } + + onSave(response) { + if (response != 'ACCEPT') return; + + let selected = this.selected; + + if (this.eventType == 'always') { + 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); + } + + 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; + } + + onDelete(id, event) { + event.preventDefault(); + this.$.confirm.show(); + this.deleteId = id; + } + + delete(response) { + if (response != 'ACCEPT') return; + if (!this.deleteId) return; + this.$http.delete(`${this.path}/${this.deleteId}`) + .then(() => { + this.refresh(); + this.deleteId = null; + }); + } +} +Controller.$inject = ['$scope', '$translate', '$http', '$stateParams']; + +ngModule.component('vnZoneEvents', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/agency/front/events/style.scss b/modules/agency/front/events/style.scss new file mode 100644 index 000000000..6af8fdee0 --- /dev/null +++ b/modules/agency/front/events/style.scss @@ -0,0 +1,27 @@ +@import "effects"; + +vn-zone-events { + .week-days { + margin-top: $margin-small; + margin-bottom: $margin-medium; + 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; + + &.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 new file mode 100644 index 000000000..47d2cba4c --- /dev/null +++ b/modules/agency/front/exclusions/index.html @@ -0,0 +1,53 @@ + + + + + {{::row.day | dateTime:'dd/MM/yyyy'}} + + + + + + + + + + + + + No records found + + + + + + + + + + + + + + + + + diff --git a/modules/agency/front/exclusions/index.js b/modules/agency/front/exclusions/index.js new file mode 100644 index 000000000..04806eddf --- /dev/null +++ b/modules/agency/front/exclusions/index.js @@ -0,0 +1,60 @@ +import ngModule from '../module'; + +class Controller { + constructor($, $http, $stateParams) { + Object.assign(this, { + $, + $http + }); + + this.path = `/api/Zones/${$stateParams.id}/exclusions`; + this.refresh(); + } + + refresh() { + this.$http.get(this.path) + .then(res => this.$.data = res.data); + } + + onCreate() { + this.isNew = true; + this.selected = {}; + this.$.dialog.show(); + } + + onSave(response) { + if (response != 'ACCEPT') return; + + this.$http.post(this.path, this.selected) + .then(() => { + this.selected = null; + this.isNew = null; + this.$.dialog.hide(); + this.refresh(); + }); + + return false; + } + + onDelete(index) { + this.$.confirm.show(); + this.deleteIndex = index; + } + + delete(response) { + if (response != 'ACCEPT') return; + let id = this.$.data[this.deleteIndex].id; + if (!id) return; + this.$http.delete(`${this.path}/${id}`) + .then(() => { + this.$.data.splice(this.deleteIndex, 1); + this.deleteIndex = null; + }); + } +} +Controller.$inject = ['$scope', '$http', '$stateParams']; + +ngModule.component('vnZoneExclusions', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/agency/front/index.js b/modules/agency/front/index.js index 5e4103b43..e182ad061 100644 --- a/modules/agency/front/index.js +++ b/modules/agency/front/index.js @@ -1,12 +1,17 @@ export * from './module'; +import './main'; import './index/'; +import './delivery-days'; import './summary'; import './card'; import './descriptor'; import './search-panel'; import './create'; import './basic-data'; -import './location'; -import './location/calendar'; +import './warehouses'; +import './events'; +import './calendar'; +import './exclusions'; +import './location'; import './calendar'; diff --git a/modules/agency/front/index/index.html b/modules/agency/front/index/index.html index 1116107aa..05837738c 100644 --- a/modules/agency/front/index/index.html +++ b/modules/agency/front/index/index.html @@ -18,27 +18,28 @@
- + Id Name Agency - Warehouse - Hour - Price + Closing + Price - + {{::zone.id}} {{::zone.name}} {{::zone.agencyMode.name}} - {{::zone.warehouse.name}} {{::zone.hour | dateTime: 'HH:mm'}} - {{::zone.price | currency: 'EUR':2}} + {{::zone.price | currency: 'EUR':2}} - - - + diff --git a/modules/agency/front/index/index.js b/modules/agency/front/index/index.js index a85be6e72..42b3db35c 100644 --- a/modules/agency/front/index/index.js +++ b/modules/agency/front/index/index.js @@ -6,10 +6,10 @@ export default class Controller { this.$http = $http; this.$state = $state; this.filter = { - include: [ - {relation: 'agencyMode', fields: ['name']}, - {relation: 'warehouse', fields: ['name']} - ] + include: { + relation: 'agencyMode', + scope: {fields: ['name']} + } }; } @@ -19,7 +19,8 @@ export default class Controller { return /^\d+$/.test(value) ? {id: value} : {name: {like: `%${value}%`}}; - case 'warehouseFk': + case 'name': + return {[param]: {like: `%${value}%`}}; case 'agencyModeFk': return {[param]: value}; } diff --git a/modules/agency/front/locale/es.yml b/modules/agency/front/locale/es.yml index ef226ef10..20277cadd 100644 --- a/modules/agency/front/locale/es.yml +++ b/modules/agency/front/locale/es.yml @@ -1,7 +1,10 @@ Agency: Agencia +Warehouses: Almacenes +Week days: Días de la semana +Exceptions: Excepciones +Exclusions: Exclusiones Warehouse: Almacén -Hour: Hora (ETD) -ETD: Tiempo de salida estimado +Hour: Hora Price: Precio Locations: Localizaciones This zone will be removed: La zona será eliminada @@ -10,4 +13,14 @@ Zones: Zonas New zone: Nueva zona Volumetric: Volumétrico Clone: Clonar -Search zone by id or name: Buscar zonas por identificador o nombre \ No newline at end of file +Search zone by id or name: Buscar zonas por identificador o nombre +From: Desde +To: Hasta +Closing: Cierre +Specific day: Día específico +Range of dates: Rango de fechas +Always: Siempre +Everyday: Todos los días +Delivery days: Días de entrega +Province: Provincia +Postcode: Código postal diff --git a/modules/agency/front/location/calendar.html b/modules/agency/front/location/calendar.html deleted file mode 100644 index ad58aee1a..000000000 --- a/modules/agency/front/location/calendar.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/modules/agency/front/location/calendar.js b/modules/agency/front/location/calendar.js deleted file mode 100644 index 802a9946e..000000000 --- a/modules/agency/front/location/calendar.js +++ /dev/null @@ -1,150 +0,0 @@ -import ngModule from '../module'; - -class Controller { - constructor($element, $scope, $stateParams, $http) { - this.$element = $element; - this.$stateParams = $stateParams; - this.$scope = $scope; - this.$http = $http; - this.stMonthDate = new Date(); - this.ndMonthDate = new Date(); - this.ndMonthDate.setMonth(this.ndMonthDate.getMonth() + 1); - } - - $postLink() { - this.stMonth = this.$scope.stMonth; - this.ndMonth = this.$scope.ndMonth; - } - - // Disabled until implementation - // of holidays by node - /* get zone() { - return this._zone; - } - - set zone(value) { - this._zone = value; - - if (!value) return; - - let query = '/agency/api/LabourHolidays/getByWarehouse'; - this.$http.get(query, {params: {warehouseFk: value.warehouseFk}}).then(res => { - if (!res.data) return; - const events = []; - res.data.forEach(holiday => { - events.push({ - date: holiday.dated, - className: 'red', - title: holiday.description || holiday.name, - isRemovable: false - }); - }); - - this.events = this.events.concat(events); - }); - } */ - - get data() { - return this._data; - } - - set data(value) { - this._data = value; - - if (!value) return; - const events = []; - value.forEach(event => { - events.push({ - name: 'Has delivery', - dated: event.delivered, - style: {backgroundColor: '#a3d131'} - }); - }); - - this.events = events; - } - - onSelection(values, calendar) { - let totalEvents = 0; - values.forEach(day => { - const exists = calendar.events.findIndex(event => { - return event.dated >= day.dated && event.dated <= day.dated - && event.isRemovable; - }); - - if (exists > -1) totalEvents++; - }); - - if (totalEvents > (values.length / 2)) - this.removeEvents(calendar, values); - else - this.insertEvents(calendar, values); - } - - insertEvents(calendar, days) { - days.forEach(day => { - const event = calendar.events.find(event => { - return event.dated >= day.dated && event.dated <= day.dated; - }); - - if (event) return false; - - this.$scope.model.insert({ - zoneFk: this.zone.id, - delivered: day.dated, - price: this.zone.price, - bonus: this.zone.bonus - }); - - calendar.addEvent({ - name: 'Has delivery', - dated: day.dated, - style: {backgroundColor: '#a3d131'} - }); - }); - - this.$scope.model.save().then(() => { - this.events = calendar.events; - }); - } - - removeEvents(calendar, days) { - let dates = []; - days.forEach(day => { - const event = calendar.events.find(event => { - return event.dated >= day.dated && event.dated <= day.dated; - }); - - if (event && !event.isRemovable) - return false; - - dates.push(day.dated); - - calendar.removeEvent(day.dated); - }); - - if (dates.length == 0) return; - const params = {zoneFk: this.zone.id, dates}; - this.$http.post('/agency/api/zoneCalendars/removeByDate', params).then(() => { - this.events = calendar.events; - }); - } - - onMoveNext(calendar) { - calendar.moveNext(2); - } - - onMovePrevious(calendar) { - calendar.movePrevious(2); - } -} - -Controller.$inject = ['$element', '$scope', '$stateParams', '$http']; - -ngModule.component('vnZoneLocationCalendar', { - template: require('./calendar.html'), - controller: Controller, - bindings: { - zone: '<' - } -}); diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index b3979c737..d06a08870 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -3,12 +3,14 @@ url="/api/Zones/{{$ctrl.$stateParams.id}}/getLeaves" auto-load="false"> -
- +
+ + + - - - -
\ No newline at end of file diff --git a/modules/agency/front/main/index.html b/modules/agency/front/main/index.html new file mode 100644 index 000000000..d695d74aa --- /dev/null +++ b/modules/agency/front/main/index.html @@ -0,0 +1,14 @@ + + +
+ +
+ + \ No newline at end of file diff --git a/modules/agency/front/main/index.js b/modules/agency/front/main/index.js new file mode 100644 index 000000000..223b78caa --- /dev/null +++ b/modules/agency/front/main/index.js @@ -0,0 +1,6 @@ +import ngModule from '../module'; +import './style.scss'; + +ngModule.component('vnZone', { + template: require('./index.html') +}); diff --git a/modules/agency/front/main/style.scss b/modules/agency/front/main/style.scss new file mode 100644 index 000000000..9374fc05e --- /dev/null +++ b/modules/agency/front/main/style.scss @@ -0,0 +1,18 @@ +@import "effects"; + +vn-zone { + ul.menu { + list-style-type: none; + padding: 0; + padding-top: $pad-medium; + margin: 0; + font-size: inherit; + + & > li > a { + @extend %clickable; + display: block; + color: inherit; + padding: .6em 2em; + } + } +} \ No newline at end of file diff --git a/modules/agency/front/routes.json b/modules/agency/front/routes.json index 9f0930b77..534281595 100644 --- a/modules/agency/front/routes.json +++ b/modules/agency/front/routes.json @@ -7,36 +7,39 @@ "menu": [ {"state": "zone.card.basicData", "icon": "settings"}, {"state": "zone.card.location", "icon": "my_location"}, - {"state": "zone.card.calendar", "icon": "icon-deliveryprices"} + {"state": "zone.card.warehouses", "icon": "home"}, + {"state": "zone.card.events", "icon": "today"}, + {"state": "zone.card.exclusions", "icon": "block"} ], "routes": [ { "url": "/zone", "state": "zone", "abstract": true, - "component": "ui-view", + "component": "vn-zone", "description": "Zones" - }, - { + }, { "url": "/index?q", "state": "zone.index", "component": "vn-zone-index", "description": "Zones" - }, - { + }, { + "url": "/delivery-days", + "state": "zone.deliveryDays", + "component": "vn-zone-delivery-days", + "description": "Delivery days" + }, { "url": "/create", "state": "zone.create", "component": "vn-zone-create", "description": "New zone" - }, - { + }, { "url": "/:id", "state": "zone.card", "component": "vn-zone-card", "abstract": true, "description": "Detail" - }, - { + }, { "url": "/summary", "state": "zone.card.summary", "component": "vn-zone-summary", @@ -44,8 +47,7 @@ "params": { "zone": "$ctrl.zone" } - }, - { + }, { "url": "/basic-data", "state": "zone.card.basicData", "component": "vn-zone-basic-data", @@ -53,8 +55,22 @@ "params": { "zone": "$ctrl.zone" } - }, - { + }, { + "url": "/warehouses", + "state": "zone.card.warehouses", + "component": "vn-zone-warehouses", + "description": "Warehouses" + }, { + "url": "/events", + "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", "component": "vn-zone-location", @@ -62,15 +78,6 @@ "params": { "zone": "$ctrl.zone" } - }, - { - "url": "/calendar", - "state": "zone.card.calendar", - "component": "vn-zone-calendar", - "description": "Prices", - "params": { - "zone": "$ctrl.zone" - } } ] } \ No newline at end of file diff --git a/modules/agency/front/search-panel/index.html b/modules/agency/front/search-panel/index.html index fd0a0158b..2cac6dbb5 100644 --- a/modules/agency/front/search-panel/index.html +++ b/modules/agency/front/search-panel/index.html @@ -21,15 +21,7 @@ vn-one label="Agency" field="filter.agencyModeFk" - url="/agency/api/AgencyModes/isActive" - show-field="name" - value-field="id"> - - diff --git a/modules/agency/front/summary/index.html b/modules/agency/front/summary/index.html index 54adc3586..f9d221bb6 100644 --- a/modules/agency/front/summary/index.html +++ b/modules/agency/front/summary/index.html @@ -8,9 +8,6 @@ - - diff --git a/modules/agency/front/summary/index.js b/modules/agency/front/summary/index.js index 1c200edde..162e679d9 100644 --- a/modules/agency/front/summary/index.js +++ b/modules/agency/front/summary/index.js @@ -19,10 +19,7 @@ class Controller { getSummary() { let filter = { - include: [ - {relation: 'warehouse', fields: ['name']}, - {relation: 'agencyMode', fields: ['name']} - ], + include: {relation: 'agencyMode', fields: ['name']}, where: {id: this.zone.id} }; filter = encodeURIComponent(JSON.stringify((filter))); diff --git a/modules/agency/front/warehouses/index.html b/modules/agency/front/warehouses/index.html new file mode 100644 index 000000000..71baa9b3c --- /dev/null +++ b/modules/agency/front/warehouses/index.html @@ -0,0 +1,56 @@ + + + + + {{::row.warehouse.name}} + + + + + + + + + + + + + No records found + + + + + + + + + + + + + + + + + diff --git a/modules/agency/front/warehouses/index.js b/modules/agency/front/warehouses/index.js new file mode 100644 index 000000000..6f371680e --- /dev/null +++ b/modules/agency/front/warehouses/index.js @@ -0,0 +1,60 @@ +import ngModule from '../module'; + +class Controller { + constructor($, _, $http, $stateParams) { + Object.assign(this, { + $, + $http + }); + + this.path = `/api/Zones/${$stateParams.id}/warehouses`; + this.refresh(); + } + + refresh() { + let filter = {include: 'warehouse'}; + this.$http.get(this.path, {params: {filter}}) + .then(res => this.$.data = res.data); + } + + onCreate() { + this.selected = {}; + this.$.dialog.show(); + } + + onSave(response) { + if (response != 'ACCEPT') return; + + this.$http.post(this.path, this.selected) + .then(() => { + this.selected = null; + this.isNew = null; + this.$.dialog.hide(); + this.refresh(); + }); + + return false; + } + + onDelete(index) { + this.$.confirm.show(); + this.deleteIndex = index; + } + + delete(response) { + if (response != 'ACCEPT') return; + let id = this.$.data[this.deleteIndex].id; + if (!id) return; + this.$http.delete(`${this.path}/${id}`) + .then(() => { + this.$.data.splice(this.deleteIndex, 1); + this.deleteIndex = null; + }); + } +} +Controller.$inject = ['$scope', '$translate', '$http', '$stateParams']; + +ngModule.component('vnZoneWarehouses', { + template: require('./index.html'), + controller: Controller +});