From 28bd535e2b2590d665ba95d81a607aceddc54cbb Mon Sep 17 00:00:00 2001 From: Pau Navarro Date: Thu, 20 Oct 2022 15:04:25 +0200 Subject: [PATCH] Implemented SMS to route clients --- modules/route/back/methods/route/sendSms.js | 46 +++++++++++++ modules/route/back/models/route.js | 1 + modules/route/front/index.js | 1 + modules/route/front/index/index.html | 30 +++++++-- modules/route/front/index/index.js | 60 +++++++++++++++++ modules/route/front/index/locale/es.yml | 4 +- modules/route/front/sms/index.html | 45 +++++++++++++ modules/route/front/sms/index.js | 47 ++++++++++++++ modules/route/front/sms/index.spec.js | 71 +++++++++++++++++++++ modules/route/front/sms/locale/es.yml | 9 +++ modules/route/front/sms/style.scss | 5 ++ 11 files changed, 311 insertions(+), 8 deletions(-) create mode 100644 modules/route/back/methods/route/sendSms.js create mode 100644 modules/route/front/sms/index.html create mode 100644 modules/route/front/sms/index.js create mode 100644 modules/route/front/sms/index.spec.js create mode 100644 modules/route/front/sms/locale/es.yml create mode 100644 modules/route/front/sms/style.scss diff --git a/modules/route/back/methods/route/sendSms.js b/modules/route/back/methods/route/sendSms.js new file mode 100644 index 000000000..9a93b040e --- /dev/null +++ b/modules/route/back/methods/route/sendSms.js @@ -0,0 +1,46 @@ +/* eslint-disable no-console */ + +module.exports = Self => { + Self.remoteMethodCtx('sendSms', { + description: 'Sends a SMS to each client of the routes, each client only recieves the SMS once', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'string', + required: true, + description: 'The routes Ids, is separated by commas', + http: {source: 'path'} + }, + { + arg: 'destination', + type: 'string', + description: 'A comma separated string of destinations', + required: true, + }, + { + arg: 'message', + type: 'string', + required: true, + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/:id/sendSms`, + verb: 'POST' + } + }); + + Self.sendSms = async(ctx, id, destination, message) => { + const targetClients = destination.split(','); + + const allSms = []; + for (let client of targetClients) { + let sms = await Self.app.models.Sms.send(ctx, client, message); + allSms.push(sms); + } + + return allSms; + }; +}; diff --git a/modules/route/back/models/route.js b/modules/route/back/models/route.js index f5406728a..08cabd30e 100644 --- a/modules/route/back/models/route.js +++ b/modules/route/back/models/route.js @@ -12,6 +12,7 @@ module.exports = Self => { require('../methods/route/updateWorkCenter')(Self); require('../methods/route/driverRoutePdf')(Self); require('../methods/route/driverRouteEmail')(Self); + require('../methods/route/sendSms')(Self); Self.validate('kmStart', validateDistance, { message: 'Distance must be lesser than 1000' diff --git a/modules/route/front/index.js b/modules/route/front/index.js index 55cb745e1..c43048df5 100644 --- a/modules/route/front/index.js +++ b/modules/route/front/index.js @@ -15,3 +15,4 @@ import './agency-term/index'; import './agency-term/createInvoiceIn'; import './agency-term-search-panel'; import './ticket-popup'; +import './sms'; diff --git a/modules/route/front/index/index.html b/modules/route/front/index/index.html index cc76d22f2..e9785d958 100644 --- a/modules/route/front/index/index.html +++ b/modules/route/front/index/index.html @@ -160,13 +160,6 @@
- - - - + + + + + + + +
+ + + + + e === ticket.clientFk).length > 0) + clientsFk.push(ticket.clientFk); + } + + for (let client of clientsFk) { + let currentClient = await this.$http.get(`Clients/${client}`); + clients.push(currentClient.data); + } + + let destination = ''; + let destinationFk = ''; + let routesId = ''; + + for (let client of clients) { + if (destination !== '') + destination = destination + ','; + if (destinationFk !== '') + destinationFk = destinationFk + ','; + destination = destination + client.phone; + destinationFk = destinationFk + client.id; + } + + for (let route of routes) { + if (routesId !== '') + routesId = routesId + ','; + routesId = routesId + route; + } + this.newSMS = Object.assign({ + routesId: routesId, + destinationFk: destinationFk, + destination: destination + }); + + this.$.sms.open(); + return true; + } catch (e) { + this.vnApp.showError(this.$t(e.message)); + return false; + } + } } Controller.$inject = ['$element', '$scope', 'vnReport']; diff --git a/modules/route/front/index/locale/es.yml b/modules/route/front/index/locale/es.yml index e4c5c8c55..3db84d81e 100644 --- a/modules/route/front/index/locale/es.yml +++ b/modules/route/front/index/locale/es.yml @@ -8,4 +8,6 @@ Hour finished: Hora fin Go to route: Ir a la ruta You must select a valid time: Debe seleccionar una hora válida You must select a valid date: Debe seleccionar una fecha válida -Mark as served: Marcar como servidas \ No newline at end of file +Mark as served: Marcar como servidas +Retrieving data from the routes: Recuperando datos de las rutas +Send SMS to all clients: Mandar sms a todos los clientes de las rutas \ No newline at end of file diff --git a/modules/route/front/sms/index.html b/modules/route/front/sms/index.html new file mode 100644 index 000000000..4f86b346f --- /dev/null +++ b/modules/route/front/sms/index.html @@ -0,0 +1,45 @@ + + +
+ + + + + + + + {{'Characters remaining' | translate}}: + + {{$ctrl.charactersRemaining()}} + + + +
+
+ + + + +
\ No newline at end of file diff --git a/modules/route/front/sms/index.js b/modules/route/front/sms/index.js new file mode 100644 index 000000000..701306071 --- /dev/null +++ b/modules/route/front/sms/index.js @@ -0,0 +1,47 @@ +import ngModule from '../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +class Controller extends Component { + open() { + this.$.SMSDialog.show(); + } + + charactersRemaining() { + const element = this.$.message; + const value = element.input.value; + + const maxLength = 160; + const textAreaLength = new Blob([value]).size; + return maxLength - textAreaLength; + } + + onResponse() { + try { + if (!this.sms.destination) + throw new Error(`The destination can't be empty`); + if (!this.sms.message) + throw new Error(`The message can't be empty`); + if (this.charactersRemaining() < 0) + throw new Error(`The message it's too long`); + + this.$http.post(`Routes/${this.sms.routesId}/sendSms`, this.sms).then(res => { + this.vnApp.showMessage(this.$t('SMS sent!')); + + if (res.data) this.emit('send', {response: res.data}); + }); + } catch (e) { + this.vnApp.showError(this.$t(e.message)); + return false; + } + return true; + } +} + +ngModule.vnComponent('vnRouteSms', { + template: require('./index.html'), + controller: Controller, + bindings: { + sms: '<', + } +}); diff --git a/modules/route/front/sms/index.spec.js b/modules/route/front/sms/index.spec.js new file mode 100644 index 000000000..b133db04d --- /dev/null +++ b/modules/route/front/sms/index.spec.js @@ -0,0 +1,71 @@ +import './index'; + +describe('Ticket', () => { + describe('Component vnTicketSms', () => { + let controller; + let $httpBackend; + + beforeEach(ngModule('ticket')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + let $scope = $rootScope.$new(); + const $element = angular.element(''); + controller = $componentController('vnTicketSms', {$element, $scope}); + controller.$.message = { + input: { + value: 'My SMS' + } + }; + })); + + describe('onResponse()', () => { + it('should perform a POST query and show a success snackbar', () => { + let params = {ticketId: 11, destinationFk: 1101, destination: 111111111, message: 'My SMS'}; + controller.sms = {ticketId: 11, destinationFk: 1101, destination: 111111111, message: 'My SMS'}; + + jest.spyOn(controller.vnApp, 'showMessage'); + $httpBackend.expect('POST', `Tickets/11/sendSms`, params).respond(200, params); + + controller.onResponse(); + $httpBackend.flush(); + + expect(controller.vnApp.showMessage).toHaveBeenCalledWith('SMS sent!'); + }); + + it('should call onResponse without the destination and show an error snackbar', () => { + controller.sms = {destinationFk: 1101, message: 'My SMS'}; + + jest.spyOn(controller.vnApp, 'showError'); + + controller.onResponse(); + + expect(controller.vnApp.showError).toHaveBeenCalledWith(`The destination can't be empty`); + }); + + it('should call onResponse without the message and show an error snackbar', () => { + controller.sms = {destinationFk: 1101, destination: 222222222}; + + jest.spyOn(controller.vnApp, 'showError'); + + controller.onResponse(); + + expect(controller.vnApp.showError).toHaveBeenCalledWith(`The message can't be empty`); + }); + }); + + describe('charactersRemaining()', () => { + it('should return the characters remaining in a element', () => { + controller.$.message = { + input: { + value: 'My message 0€' + } + }; + + let result = controller.charactersRemaining(); + + expect(result).toEqual(145); + }); + }); + }); +}); diff --git a/modules/route/front/sms/locale/es.yml b/modules/route/front/sms/locale/es.yml new file mode 100644 index 000000000..4d6022921 --- /dev/null +++ b/modules/route/front/sms/locale/es.yml @@ -0,0 +1,9 @@ +Send SMS: Enviar SMS +Routes to notify: Rutas a notificar +Message: Mensaje +SMS sent!: ¡SMS enviado! +Characters remaining: Carácteres restantes +The destination can't be empty: El destinatario no puede estar vacio +The message can't be empty: El mensaje no puede estar vacio +The message it's too long: El mensaje es demasiado largo +Special characters like accents counts as a multiple: Carácteres especiales como los acentos cuentan como varios \ No newline at end of file diff --git a/modules/route/front/sms/style.scss b/modules/route/front/sms/style.scss new file mode 100644 index 000000000..84571a5f4 --- /dev/null +++ b/modules/route/front/sms/style.scss @@ -0,0 +1,5 @@ +@import "variables"; + +.SMSDialog { + min-width: 400px +} \ No newline at end of file