diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 3eced92a4..762e5411a 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1335,9 +1335,9 @@ INSERT INTO `vn`.`itemTypeTag`(`id`, `itemTypeFk`, `tagFk`, `priority`) CALL `vn`.`itemRefreshTags`(NULL); -INSERT INTO `vn`.`itemLog` (`id`, `originFk`, `userFk`, `action`, `description`) +INSERT INTO `vn`.`itemLog` (`id`, `originFk`, `userFk`, `action`, `description`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`, `changedModelValue`) VALUES - ('1', '1', '1', 'insert', 'We made a change!'); + ('1', '1', '1', 'insert', 'We made a change!', 'Item', '{}', '{}', 1, '1'); INSERT INTO `vn`.`recovery`(`id`, `clientFk`, `started`, `finished`, `amount`, `period`) VALUES @@ -2754,6 +2754,7 @@ INSERT INTO `vn`.`ticketLog` (`id`, `originFk`, `userFk`, `action`, `changedMode VALUES (1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1); + INSERT INTO `salix`.`url` (`appName`, `environment`, `url`) VALUES ('lilium', 'dev', 'http://localhost:8080/#/'), diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 9dab10673..f6a299011 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -311,10 +311,12 @@ export default { firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr' }, clientLog: { - lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before', - lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after', - penultimateModificationPreviousValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.before', - penultimateModificationCurrentValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.after' + lastModificationPreviousValue: 'vn-client-log vn-tr table tr td.before', + lastModificationCurrentValue: 'vn-client-log vn-tr table tr td.after', + namePreviousValue: 'vn-client-log vn-tr table tr:nth-child(1) td.before', + nameCurrentValue: 'vn-client-log vn-tr table tr:nth-child(1) td.after', + activePreviousValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.before', + activeCurrentValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.after' }, clientBalance: { @@ -518,7 +520,7 @@ export default { }, itemLog: { anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr', - fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) > vn-td > vn-one:nth-child(3) > div span:nth-child(2)', + fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(3) td.after', }, ticketSummary: { header: 'vn-ticket-summary > vn-card > h5', @@ -711,9 +713,10 @@ export default { ticketLog: { firstTD: 'vn-ticket-log vn-table vn-td:nth-child(1)', logButton: 'vn-left-menu a[ui-sref="ticket.card.log"]', - firstLogEntry: 'vn-ticket-log vn-data-viewer vn-tbody vn-tr', - changes: 'vn-ticket-log vn-data-viewer vn-tbody > vn-tr > vn-td:nth-child(7)', - id: 'vn-ticket-log vn-tr:nth-child(1) vn-one:nth-child(1) span' + user: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(2)', + action: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(4)', + changes: 'vn-ticket-log vn-data-viewer vn-tbody vn-tr table tr:nth-child(2) td.after', + id: 'vn-ticket-log vn-tr:nth-child(1) table tr:nth-child(1) td.before' }, ticketService: { addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button', @@ -1123,7 +1126,7 @@ export default { undoChanges: 'vn-travel-basic-data vn-button[label="Undo changes"]' }, travelLog: { - firstLogFirstTD: 'vn-travel-log vn-tbody > vn-tr > vn-td:nth-child(1) > div' + firstLogFirstTD: 'vn-travel-log vn-tbody > vn-tr > vn-td:nth-child(5)' }, travelThermograph: { add: 'vn-travel-thermograph-index vn-float-button[icon="add"]', diff --git a/e2e/paths/02-client/07_edit_web_access.spec.js b/e2e/paths/02-client/07_edit_web_access.spec.js index 3d9ccee62..29b39f788 100644 --- a/e2e/paths/02-client/07_edit_web_access.spec.js +++ b/e2e/paths/02-client/07_edit_web_access.spec.js @@ -67,22 +67,22 @@ describe('Client Edit web access path', () => { }); it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => { - let lastModificationPreviousValue = await page - .waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText'); - let lastModificationCurrentValue = await page - .waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText'); + let namePreviousValue = await page + .waitToGetProperty(selectors.clientLog.namePreviousValue, 'innerText'); + let nameCurrentValue = await page + .waitToGetProperty(selectors.clientLog.nameCurrentValue, 'innerText'); - expect(lastModificationPreviousValue).toEqual('name MaxEisenhardt active false'); - expect(lastModificationCurrentValue).toEqual('name Legion active false'); + expect(namePreviousValue).toEqual('MaxEisenhardt'); + expect(nameCurrentValue).toEqual('Legion'); }); it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => { - let penultimateModificationPreviousValue = await page - .waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText'); - let penultimateModificationCurrentValue = await page - .waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText'); + let activePreviousValue = await page + .waitToGetProperty(selectors.clientLog.activePreviousValue, 'innerText'); + let activeCurrentValue = await page + .waitToGetProperty(selectors.clientLog.activeCurrentValue, 'innerText'); - expect(penultimateModificationPreviousValue).toEqual('name MaxEisenhardt active true'); - expect(penultimateModificationCurrentValue).toEqual('name MaxEisenhardt active false'); + expect(activePreviousValue).toEqual('✓'); + expect(activeCurrentValue).toEqual('✗'); }); }); diff --git a/e2e/paths/02-client/13_log.spec.js b/e2e/paths/02-client/13_log.spec.js index 9b047a47c..8f186d842 100644 --- a/e2e/paths/02-client/13_log.spec.js +++ b/e2e/paths/02-client/13_log.spec.js @@ -43,7 +43,7 @@ describe('Client log path', () => { let lastModificationCurrentValue = await page. waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText'); - expect(lastModificationPreviousValue).toEqual('name DavidCharlesHaller'); - expect(lastModificationCurrentValue).toEqual('name this is a test'); + expect(lastModificationPreviousValue).toEqual('DavidCharlesHaller'); + expect(lastModificationCurrentValue).toEqual('this is a test'); }); }); diff --git a/e2e/paths/05-ticket/02_expeditions_and_log.spec.js b/e2e/paths/05-ticket/02_expeditions_and_log.spec.js index f970247e5..ae5e2fb0c 100644 --- a/e2e/paths/05-ticket/02_expeditions_and_log.spec.js +++ b/e2e/paths/05-ticket/02_expeditions_and_log.spec.js @@ -32,14 +32,17 @@ describe('Ticket expeditions and log path', () => { it(`should confirm the expedition deleted is shown now in the ticket log`, async() => { await page.accessToSection('ticket.card.log'); - const firstLogEntry = await page - .waitToGetProperty(selectors.ticketLog.firstLogEntry, 'innerText'); + const user = await page + .waitToGetProperty(selectors.ticketLog.user, 'innerText'); + + const action = await page + .waitToGetProperty(selectors.ticketLog.action, 'innerText'); const id = await page .waitToGetProperty(selectors.ticketLog.id, 'innerText'); - expect(firstLogEntry).toContain('production'); - expect(firstLogEntry).toContain('Deletes'); + expect(user).toContain('production'); + expect(action).toContain('Deletes'); expect(id).toEqual('2'); }); }); diff --git a/e2e/paths/05-ticket/17_log.spec.js b/e2e/paths/05-ticket/17_log.spec.js index 399eb647b..32829ee74 100644 --- a/e2e/paths/05-ticket/17_log.spec.js +++ b/e2e/paths/05-ticket/17_log.spec.js @@ -55,6 +55,6 @@ describe('Ticket log path', () => { const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText'); - expect(result.length).toBeGreaterThan('20'); + expect(result.length).toBeGreaterThan('15'); }); }); diff --git a/e2e/paths/13-supplier/02_basic_data.spec.js b/e2e/paths/13-supplier/02_basic_data.spec.js index 4f3c49512..9d86e11d4 100644 --- a/e2e/paths/13-supplier/02_basic_data.spec.js +++ b/e2e/paths/13-supplier/02_basic_data.spec.js @@ -70,8 +70,8 @@ describe('Supplier basic data path', () => { }); it('should check the changes have been recorded', async() => { - const result = await page.waitToGetProperty('#newInstance:nth-child(3)', 'innerText'); + const result = await page.waitToGetProperty('vn-tr table tr:nth-child(3) td.after', 'innerText'); - expect(result).toEqual('note Some notes'); + expect(result).toEqual('Some notes'); }); }); diff --git a/front/salix/components/log/index.html b/front/salix/components/log/index.html index 0a0449038..79dfcef8c 100644 --- a/front/salix/components/log/index.html +++ b/front/salix/components/log/index.html @@ -1,10 +1,12 @@ - @@ -13,81 +15,53 @@ Date - Author - Model - Action - Name - Before - After + User + Model + Action + Name + Changes {{::log.creationDate | date:'dd/MM/yyyy HH:mm'}} -
-
- Changed by: - {{::log.user.name || 'System' | translate}} - -
-
- Model: - {{::log.changedModel | dashIfEmpty}} -
-
- Action: - {{::$ctrl.actionsText[log.action] | dashIfEmpty}} -
-
- Name: - {{::log.changedModelValue | dashIfEmpty}} -
-
- + {{::log.user.name || 'System' | translate}} - + {{::log.changedModel}} - + {{::$ctrl.actionsText[log.action]}} - + {{::log.changedModelValue}} - - -
- - -
-
-
- - -
- - -
-
- -
- {{::log.description}} -
-
+ + + + + + + + + + + + + + + + +
FieldBeforeAfter
{{prop.name}}{{prop.old}}{{prop.new}}
+
+ {{::log.description}} +
@@ -96,4 +70,4 @@
- \ No newline at end of file + diff --git a/front/salix/components/log/index.js b/front/salix/components/log/index.js index f30878b9f..1c54aa9b8 100644 --- a/front/salix/components/log/index.js +++ b/front/salix/components/log/index.js @@ -2,15 +2,17 @@ import ngModule from '../../module'; import Section from '../section'; import './style.scss'; +const validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/; + export default class Controller extends Section { constructor($element, $) { super($element, $); this.actionsText = { - 'insert': 'Creates', - 'update': 'Updates', - 'delete': 'Deletes', - 'select': 'Views' - }; ``; + insert: 'Creates', + update: 'Updates', + delete: 'Deletes', + select: 'Views' + }; this.filter = { include: [{ relation: 'user', @@ -33,32 +35,57 @@ export default class Controller extends Section { set logs(value) { this._logs = value; - if (!value) return; - + if (!this.logs) return; + const empty = {}; const validations = window.validations; - value.forEach(log => { - const locale = validations[log.changedModel] && validations[log.changedModel].locale ? validations[log.changedModel].locale : {}; + for (const log of value) { + const oldValues = log.oldInstance || empty; + const newValues = log.newInstance || empty; + const locale = validations[log.changedModel]?.locale || empty; - log.oldProperties = this.getInstance(log.oldInstance, locale); - log.newProperties = this.getInstance(log.newInstance, locale); - }); + let props = Object.keys(oldValues).concat(Object.keys(newValues)); + props = [...new Set(props)]; + + log.props = []; + for (const prop of props) { + log.props.push({ + name: locale[prop] || prop, + old: this.formatValue(oldValues[prop]), + new: this.formatValue(newValues[prop]) + }); + } + } } - getInstance(instance, locale) { - const properties = []; - let validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/; + get showModelName() { + return !(this.changedModel && this.changedModelId); + } - if (typeof instance == 'object' && instance != null) { - Object.keys(instance).forEach(property => { - if (validDate.test(instance[property])) - instance[property] = new Date(instance[property]).toLocaleString('es-ES'); + formatValue(value) { + let type = typeof value; - const key = locale[property] || property; - properties.push({key, value: instance[property]}); - }); - return properties; + if (type === 'string' && validDate.test(value)) { + value = new Date(value); + type = typeof value; + } + + switch (type) { + case 'boolean': + return value ? '✓' : '✗'; + case 'object': + if (value instanceof Date) { + const hasZeroTime = + value.getHours() === 0 && + value.getMinutes() === 0 && + value.getSeconds() === 0; + const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss'; + return this.$filter('date')(value, format); + } + else + return value; + default: + return value; } - return null; } showWorkerDescriptor(event, workerId) { @@ -73,6 +100,8 @@ ngModule.vnComponent('vnLog', { bindings: { model: '<', originId: '<', + changedModel: ' td { + padding: 2px; + } + & > td.field, + & > th.field { + width: 20%; + color: gray; + } + & > td.before, + & > th.before, + & > td.after, + & > th.after { + width: 40%; + } + } + } } .ellipsis { white-space: nowrap; @@ -40,4 +62,4 @@ vn-log { .alignSpan { overflow: hidden; display: inline-block; -} \ No newline at end of file +}