diff --git a/db/changes/231401/00-workerNotes.sql b/db/changes/231401/00-workerNotes.sql new file mode 100644 index 000000000..0d9eaae7e --- /dev/null +++ b/db/changes/231401/00-workerNotes.sql @@ -0,0 +1,14 @@ +CREATE TABLE `vn`.`workerObservation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `workerFk` int(10) unsigned DEFAULT NULL, + `userFk` int(10) unsigned DEFAULT NULL, + `text` text COLLATE utf8mb3_unicode_ci NOT NULL, + `created` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + CONSTRAINT `workerFk_workerObservation_FK` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE, + CONSTRAINT `userFk_workerObservation_FK` FOREIGN KEY (`userFk`) REFERENCES `account`.`user`(`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Todas las observaciones referentes a un trabajador'; + +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('WorkerObservation', '*', '*', 'ALLOW', 'ROLE', 'hr'); diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 32a60a4e2..461f5b2dc 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -987,6 +987,12 @@ export default { locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]', saveButton: 'vn-worker-basic-data button[type=submit]' }, + workerNotes: { + addNoteFloatButton: 'vn-float-button', + note: 'vn-textarea[ng-model="$ctrl.note.text"]', + saveButton: 'button[type=submit]', + firstNoteText: 'vn-worker-note .text' + }, workerPbx: { extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]', saveButton: 'vn-worker-pbx button[type=submit]' diff --git a/e2e/paths/03-worker/08_add_notes.spec.js b/e2e/paths/03-worker/08_add_notes.spec.js new file mode 100644 index 000000000..eb2e4c041 --- /dev/null +++ b/e2e/paths/03-worker/08_add_notes.spec.js @@ -0,0 +1,42 @@ +import selectors from '../../helpers/selectors'; +import getBrowser from '../../helpers/puppeteer'; + +describe('Worker Add notes path', () => { + let browser; + let page; + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + await page.loginAndModule('employee', 'worker'); + await page.accessToSearchResult('Bruce Banner'); + await page.accessToSection('worker.card.note.index'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it(`should reach the notes index`, async() => { + await page.waitForState('worker.card.note.index'); + }); + + it(`should click on the add note button`, async() => { + await page.waitToClick(selectors.workerNotes.addNoteFloatButton); + await page.waitForState('worker.card.note.create'); + }); + + it(`should create a note`, async() => { + await page.waitForSelector(selectors.workerNotes.note); + await page.type(`${selectors.workerNotes.note} textarea`, 'Meeting with Black Widow 21st 9am'); + await page.waitToClick(selectors.workerNotes.saveButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); + + it('should confirm the note was created', async() => { + const result = await page.waitToGetProperty(selectors.workerNotes.firstNoteText, 'innerText'); + + expect(result).toEqual('Meeting with Black Widow 21st 9am'); + }); +}); diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json index 63fc65827..145934700 100644 --- a/modules/worker/back/model-config.json +++ b/modules/worker/back/model-config.json @@ -53,6 +53,9 @@ "Worker": { "dataSource": "vn" }, + "WorkerObservation": { + "dataSource": "vn" + }, "WorkerConfig": { "dataSource": "vn" }, diff --git a/modules/worker/back/models/worker-observation.js b/modules/worker/back/models/worker-observation.js new file mode 100644 index 000000000..cccc2cfbd --- /dev/null +++ b/modules/worker/back/models/worker-observation.js @@ -0,0 +1,12 @@ +module.exports = function(Self) { + Self.validatesPresenceOf('text', { + message: 'Description cannot be blank' + }); + + Self.observe('before save', async function(ctx) { + ctx.instance.created = new Date(); + let token = ctx.options.accessToken; + let userId = token && token.userId; + ctx.instance.userFk = userId; + }); +}; diff --git a/modules/worker/back/models/worker-observation.json b/modules/worker/back/models/worker-observation.json new file mode 100644 index 000000000..90eb35837 --- /dev/null +++ b/modules/worker/back/models/worker-observation.json @@ -0,0 +1,39 @@ +{ + "name": "WorkerObservation", + "base": "VnModel", + "options": { + "mysql": { + "table": "workerObservation" + } + }, + "properties": { + "id": { + "id": true, + "type": "number" + }, + "workerFk": { + "type": "number" + }, + "userFk": { + "type": "number" + }, + "text": { + "type": "string" + }, + "created": { + "type": "date" + } + }, + "relations": { + "worker": { + "type": "belongsTo", + "model": "Worker", + "foreignKey": "workerFk" + }, + "user":{ + "type": "belongsTo", + "model": "Account", + "foreignKey": "userFk" + } + } +} diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js index 657f6a8c6..8fad2c0df 100644 --- a/modules/worker/front/index.js +++ b/modules/worker/front/index.js @@ -18,3 +18,6 @@ import './log'; import './dms/index'; import './dms/create'; import './dms/edit'; +import './note/index'; +import './note/create'; + diff --git a/modules/worker/front/locale/es.yml b/modules/worker/front/locale/es.yml index b5bcfefa4..a25377122 100644 --- a/modules/worker/front/locale/es.yml +++ b/modules/worker/front/locale/es.yml @@ -31,3 +31,5 @@ Deallocate PDA: Desasignar PDA PDA deallocated: PDA desasignada PDA allocated: PDA asignada New PDA: Nueva PDA +Notes: Notas +New note: Nueva nota diff --git a/modules/worker/front/note/create/index.html b/modules/worker/front/note/create/index.html new file mode 100644 index 000000000..d09fc2da5 --- /dev/null +++ b/modules/worker/front/note/create/index.html @@ -0,0 +1,30 @@ + + +
+ + + + + + + + + + + + +
\ No newline at end of file diff --git a/modules/worker/front/note/create/index.js b/modules/worker/front/note/create/index.js new file mode 100644 index 000000000..81ee247db --- /dev/null +++ b/modules/worker/front/note/create/index.js @@ -0,0 +1,21 @@ +import ngModule from '../../module'; +import Section from 'salix/components/section'; + +export default class Controller extends Section { + constructor($element, $) { + super($element, $); + this.note = { + workerFk: parseInt(this.$params.id), + text: null + }; + } + + cancel() { + this.$state.go('worker.card.note.index', {id: this.$params.id}); + } +} + +ngModule.vnComponent('vnNoteWorkerCreate', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/worker/front/note/create/index.spec.js b/modules/worker/front/note/create/index.spec.js new file mode 100644 index 000000000..d900c8ee0 --- /dev/null +++ b/modules/worker/front/note/create/index.spec.js @@ -0,0 +1,22 @@ +import './index'; + +describe('Worker', () => { + describe('Component vnNoteWorkerCreate', () => { + let $state; + let controller; + + beforeEach(ngModule('worker')); + + beforeEach(inject(($componentController, _$state_) => { + $state = _$state_; + $state.params.id = '1234'; + const $element = angular.element(''); + controller = $componentController('vnNoteWorkerCreate', {$element, $state}); + })); + + it('should define workerFk using $state.params.id', () => { + expect(controller.note.workerFk).toBe(1234); + expect(controller.note.worker).toBe(undefined); + }); + }); +}); diff --git a/modules/worker/front/note/create/locale/es.yml b/modules/worker/front/note/create/locale/es.yml new file mode 100644 index 000000000..bfe773f48 --- /dev/null +++ b/modules/worker/front/note/create/locale/es.yml @@ -0,0 +1,2 @@ +New note: Nueva nota +Note: Nota \ No newline at end of file diff --git a/modules/worker/front/note/index/index.html b/modules/worker/front/note/index/index.html new file mode 100644 index 000000000..9f5c27008 --- /dev/null +++ b/modules/worker/front/note/index/index.html @@ -0,0 +1,32 @@ + + + + +
+ + {{::note.user.nickname}} + {{::note.created | date:'dd/MM/yyyy HH:mm'}} + + + {{::note.text}} + +
+
+
+ + + diff --git a/modules/worker/front/note/index/index.js b/modules/worker/front/note/index/index.js new file mode 100644 index 000000000..d20971413 --- /dev/null +++ b/modules/worker/front/note/index/index.js @@ -0,0 +1,22 @@ +import ngModule from '../../module'; +import Section from 'salix/components/section'; +import './style.scss'; + +export default class Controller extends Section { + constructor($element, $) { + super($element, $); + this.filter = { + order: 'created DESC', + }; + } +} + +Controller.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnWorkerNote', { + template: require('./index.html'), + controller: Controller, + bindings: { + worker: '<' + } +}); diff --git a/modules/worker/front/note/index/style.scss b/modules/worker/front/note/index/style.scss new file mode 100644 index 000000000..5ff6baf4f --- /dev/null +++ b/modules/worker/front/note/index/style.scss @@ -0,0 +1,5 @@ +vn-worker-note { + .note:last-child { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 64b98bfca..64cb186d6 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -11,6 +11,7 @@ ], "card": [ {"state": "worker.card.basicData", "icon": "settings"}, + {"state": "worker.card.note.index", "icon": "insert_drive_file"}, {"state": "worker.card.timeControl", "icon": "access_time"}, {"state": "worker.card.calendar", "icon": "icon-calendar"}, {"state": "worker.card.pda", "icon": "phone_android"}, @@ -72,6 +73,24 @@ "component": "vn-worker-log", "description": "Log", "acl": ["salesAssistant"] + }, { + "url": "/note", + "state": "worker.card.note", + "component": "ui-view", + "abstract": true + }, { + "url": "/index", + "state": "worker.card.note.index", + "component": "vn-worker-note", + "description": "Notes", + "params": { + "worker": "$ctrl.worker" + } + }, { + "url": "/create", + "state": "worker.card.note.create", + "component": "vn-note-worker-create", + "description": "New note" }, { "url": "/pbx", "state": "worker.card.pbx",