diff --git a/front/core/components/json-value/index.spec.js b/front/core/components/json-value/index.spec.js
new file mode 100644
index 000000000..078080d27
--- /dev/null
+++ b/front/core/components/json-value/index.spec.js
@@ -0,0 +1,79 @@
+import './index';
+
+describe('Salix Component vnLog', () => {
+ let controller;
+ let $scope;
+ let $element;
+ let el;
+
+ beforeEach(ngModule('vnCore'));
+
+ beforeEach(inject(($componentController, $rootScope) => {
+ $scope = $rootScope.$new();
+ $element = angular.element('');
+ controller = $componentController('vnJsonValue', {$element, $scope});
+ el = controller.element;
+ }));
+
+ describe('set value()', () => {
+ it('should display null symbol when value is null equivalent', () => {
+ controller.value = null;
+
+ expect(el.textContent).toEqual('∅');
+ expect(el.className).toContain('json-null');
+ });
+
+ it('should display ballot when value is false', () => {
+ controller.value = false;
+
+ expect(el.textContent).toEqual('✗');
+ expect(el.className).toContain('json-false');
+ });
+
+ it('should display check when value is true', () => {
+ controller.value = true;
+
+ expect(el.textContent).toEqual('✓');
+ expect(el.className).toContain('json-true');
+ });
+
+ it('should display string when value is an string', () => {
+ controller.value = 'Foo';
+
+ expect(el.textContent).toEqual('Foo');
+ expect(el.className).toContain('json-string');
+ });
+
+ it('should display only date when value is date with time set to zero', () => {
+ const date = Date.vnNew();
+ date.setHours(0, 0, 0, 0);
+ controller.value = date;
+
+ expect(el.textContent).toEqual('01/01/2001');
+ expect(el.className).toContain('json-object');
+ });
+
+ it('should display full date without time when value is date with time', () => {
+ const date = Date.vnNew();
+ date.setHours(15, 45);
+ controller.value = date;
+
+ expect(el.textContent).toEqual('01/01/2001 15:45:00');
+ expect(el.className).toContain('json-object');
+ });
+
+ it('should display object when value is an object', () => {
+ controller.value = {foo: 'bar'};
+
+ expect(el.textContent).toEqual('[object Object]');
+ expect(el.className).toContain('json-object');
+ });
+
+ it('should display number when value is a number', () => {
+ controller.value = 2050;
+
+ expect(el.textContent).toEqual('2050');
+ expect(el.className).toContain('json-number');
+ });
+ });
+});
diff --git a/front/core/components/json-value/style.scss b/front/core/components/json-value/style.scss
index 2d6c4023c..009a13d40 100644
--- a/front/core/components/json-value/style.scss
+++ b/front/core/components/json-value/style.scss
@@ -5,8 +5,7 @@ vn-json-value {
color: #d172cc;
}
&.json-object {
- /*color: #d1a572;*/
- color: #d172cc;
+ color: #d1a572;
}
&.json-number {
color: #85d0ff;
diff --git a/front/core/styles/variables.scss b/front/core/styles/variables.scss
index c280838ca..bcc9fab66 100644
--- a/front/core/styles/variables.scss
+++ b/front/core/styles/variables.scss
@@ -2,7 +2,6 @@
$font-size: 11pt;
$menu-width: 256px;
-$right-menu-width: 318px;
$topbar-height: 56px;
$mobile-width: 800px;
$float-spacing: 20px;
diff --git a/front/salix/components/layout/style.scss b/front/salix/components/layout/style.scss
index 6697bb1b0..612366228 100644
--- a/front/salix/components/layout/style.scss
+++ b/front/salix/components/layout/style.scss
@@ -88,13 +88,13 @@ vn-layout {
}
&.right-menu {
& > vn-topbar > .end {
- width: 80px + $right-menu-width;
+ width: 80px + $menu-width;
}
& > .main-view {
- padding-right: $right-menu-width;
+ padding-right: $menu-width;
}
[fixed-bottom-right] {
- right: $right-menu-width;
+ right: $menu-width;
}
}
& > .main-view {
diff --git a/front/salix/components/log/index.html b/front/salix/components/log/index.html
index 14a3fbe61..3d3a3f8dc 100644
--- a/front/salix/components/log/index.html
+++ b/front/salix/components/log/index.html
@@ -6,6 +6,7 @@
where="{changedModel: $ctrl.changedModel,
changedModelId: $ctrl.changedModelId}"
data="$ctrl.logs"
+ order="creationDate DESC, id DESC"
limit="20"
auto-load="true">
@@ -38,13 +39,16 @@
-
- {{::log.changedModel}}
+
+ {{::log.changedModelI18n}}
{{::log.changedModelValue}}
-
+
#{{::log.changedModelId}}
|
@@ -73,13 +77,19 @@
class="attributes">
- {{::prop.name}}:
+
+ {{::prop.nameI18n}}:
+
,
-
{{::prop.name}}:
+
+ {{::prop.nameI18n}}:
+
←
@@ -105,5 +115,72 @@
+
+
+
diff --git a/front/salix/components/log/index.js b/front/salix/components/log/index.js
index 8c75664c8..7c01b8ac3 100644
--- a/front/salix/components/log/index.js
+++ b/front/salix/components/log/index.js
@@ -19,6 +19,11 @@ export default class Controller extends Section {
delete: 'alert',
select: 'notice'
};
+
+ $.filter = {};
+ $.$watch('filter.actions', () => this.applyFilter(), true);
+ $.$watch('filter.from', () => $.filter.to = $.filter.from);
+
this.filter = {
include: [{
relation: 'user',
@@ -33,6 +38,7 @@ export default class Controller extends Section {
},
}],
};
+
this.dateFilter = this.$filter('date');
this.lang = this.$translate.use();
this.today = Date.vnNew();
@@ -52,7 +58,7 @@ export default class Controller extends Section {
const oldValues = log.oldInstance || empty;
const newValues = log.newInstance || empty;
const locale = validations[log.changedModel]?.locale || empty;
- log.changedModel = locale.name ? locale.name : log.changedModel
+ log.changedModelI18n = locale.name || log.changedModel;
let props = Object.keys(oldValues).concat(Object.keys(newValues));
props = [...new Set(props)];
@@ -60,9 +66,10 @@ export default class Controller extends Section {
log.props = [];
for (const prop of props) {
log.props.push({
- name: locale.columns?.[prop] || prop,
- old: this.castValue(oldValues[prop]),
- new: this.castValue(newValues[prop])
+ name: prop,
+ nameI18n: locale.columns?.[prop] || prop,
+ old: this.castJsonValue(oldValues[prop]),
+ new: this.castJsonValue(newValues[prop])
});
}
}
@@ -72,7 +79,7 @@ export default class Controller extends Section {
return !(this.changedModel && this.changedModelId);
}
- castValue(value) {
+ castJsonValue(value) {
return typeof value === 'string' && validDate.test(value)
? new Date(value)
: value;
@@ -88,6 +95,7 @@ export default class Controller extends Section {
}
relativeDate(dateVal) {
+ if (dateVal == null) return '';
const date = new Date(dateVal);
const dateZeroTime = new Date(dateVal);
dateZeroTime.setHours(0, 0, 0, 0);
@@ -112,6 +120,54 @@ export default class Controller extends Section {
if (!workerId) return;
this.$.workerDescriptor.show(event.target, workerId);
}
+
+ applyFilter() {
+ const filter = this.$.filter;
+
+ function getParam(prop, value) {
+ if (value == null || value == '') return null;
+ switch (prop) {
+ case 'actions':
+ const inq = [];
+ for (const action in value) {
+ if (value[action])
+ inq.push(action);
+ }
+ return inq.length ? {action: {inq}} : null;
+ case 'from':
+ return {creationDate: {gte: value}};
+ case 'to':
+ const to = new Date(value);
+ to.setHours(23, 59, 59, 999);
+ return {creationDate: {lte: to}};
+ default:
+ return {[prop]: value};
+ }
+ }
+
+ const and = [];
+ for (const prop in filter) {
+ const param = getParam(prop, filter[prop]);
+ if (param) and.push(param);
+ }
+ this.$.model.applyFilter(and.length ? {where: {and}} : null);
+ }
+
+ removeFilter() {
+ this.$.filter = {};
+ this.applyFilter();
+ }
+
+ searchUser(search) {
+ if (/^[0-9]+$/.test(search)) {
+ return {id: search};
+ } else {
+ return {or: [
+ {name: search},
+ {nickname: {like: `%${search}%`}}
+ ]}
+ }
+ }
}
ngModule.vnComponent('vnLog', {
diff --git a/front/salix/components/log/index.spec.js b/front/salix/components/log/index.spec.js
new file mode 100644
index 000000000..d01a08967
--- /dev/null
+++ b/front/salix/components/log/index.spec.js
@@ -0,0 +1,97 @@
+import './index';
+
+describe('Salix Component vnLog', () => {
+ let controller;
+ let $scope;
+ let $element;
+
+ beforeEach(ngModule('salix'));
+
+ beforeEach(inject(($componentController, $rootScope) => {
+ $scope = $rootScope.$new();
+ $element = angular.element('');
+ controller = $componentController('vnLog', {$element, $scope});
+ }));
+
+ describe('relativeDate()', () => {
+ let date;
+
+ beforeEach(() => {
+ date = Date.vnNew();
+ });
+
+ it('should return empty string when date is null', () => {
+ const ret = controller.relativeDate(null);
+
+ expect(ret).toEqual('');
+ });
+
+ it('should return empty string when date is undefined', () => {
+ const ret = controller.relativeDate(undefined);
+
+ expect(ret).toEqual('');
+ });
+
+ it('should return today and time when date is today', () => {
+ const ret = controller.relativeDate(date);
+
+ expect(ret).toEqual('today 12:00');
+ });
+
+ it('should return yesterday and time when date is yesterday', () => {
+ date.setDate(date.getDate() - 1);
+ const ret = controller.relativeDate(date);
+
+ expect(ret).toEqual('yesterday 12:00');
+ });
+
+ it('should return abreviated weekday name and time when date is on past week', () => {
+ date.setDate(date.getDate() - 3);
+ const ret = controller.relativeDate(date);
+
+ expect(ret).toEqual('Fri 12:00');
+ });
+
+ it('should return abreviated month name, day number and time when date is on this year', () => {
+ date.setDate(date.getDate() + 20);
+ const ret = controller.relativeDate(date);
+
+ expect(ret).toEqual('21 Jan 12:00');
+ });
+
+ it('should return abreviated month name, day number, year and time when date is on different year', () => {
+ date.setDate(date.getDate() - 20);
+ const ret = controller.relativeDate(date);
+
+ expect(ret).toEqual('12/12/2000 12:00');
+ });
+
+ it('should convert to date and return string when date is not a Date class instance', () => {
+ const ret = controller.relativeDate(date.toJSON());
+
+ expect(ret).toEqual('today 12:00');
+ });
+ });
+
+ describe('castJsonValue()', () => {
+ it('should return date when string has valid JSON date format', () => {
+ const now = Date.vnNew();
+
+ const ret = controller.castJsonValue(now.toJSON());
+
+ expect(ret).toBeInstanceOf(Date);
+ });
+
+ it('should return same value when is string with invalid JSON date format', () => {
+ const ret = controller.castJsonValue('Foo');
+
+ expect(ret).toEqual('Foo');
+ });
+
+ it('should return same value when is not an string', () => {
+ const ret = controller.castJsonValue(1001);
+
+ expect(ret).toEqual(1001);
+ });
+ });
+});
diff --git a/front/salix/components/log/style.scss b/front/salix/components/log/style.scss
index 1573218f4..ea228e1fb 100644
--- a/front/salix/components/log/style.scss
+++ b/front/salix/components/log/style.scss
@@ -64,6 +64,9 @@ vn-log {
}
}
}
+ .model-name {
+ text-transform: capitalize;
+ }
.model-value {
font-style: italic;
color: #c7bd2b;
@@ -130,4 +133,5 @@ vn-log {
}
}
}
+ .filter {}
}
diff --git a/modules/item/front/fixed-price-search-panel/style.scss b/modules/item/front/fixed-price-search-panel/style.scss
index a63f84f3b..e386033dd 100644
--- a/modules/item/front/fixed-price-search-panel/style.scss
+++ b/modules/item/front/fixed-price-search-panel/style.scss
@@ -2,7 +2,7 @@
vn-fixed-price-search-panel vn-side-menu {
.menu {
- min-width: $right-menu-width;
+ min-width: $menu-width;
}
& > div {
.input {
diff --git a/modules/travel/front/search-panel/style.scss b/modules/travel/front/search-panel/style.scss
index 94fe7b239..0da52408a 100644
--- a/modules/travel/front/search-panel/style.scss
+++ b/modules/travel/front/search-panel/style.scss
@@ -2,7 +2,7 @@
vn-travel-search-panel vn-side-menu {
.menu {
- min-width: $right-menu-width;
+ min-width: $menu-width;
}
& > div {
.input {