diff --git a/back/methods/starred-module/getStarredModules.js b/back/methods/starred-module/getStarredModules.js index 88c0fa8a9..7b0f0e945 100644 --- a/back/methods/starred-module/getStarredModules.js +++ b/back/methods/starred-module/getStarredModules.js @@ -12,17 +12,23 @@ module.exports = function(Self) { } }); - Self.getStarredModules = async ctx => { + Self.getStarredModules = async(ctx, options) => { + const models = Self.app.models; const userId = ctx.req.accessToken.userId; + + let myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + const filter = { where: { workerFk: userId }, - fields: ['moduleFk'] + fields: ['id', 'workerFk', 'moduleFk', 'position'], + order: 'position ASC' }; - const starredModules = await Self.app.models.StarredModule.find(filter); - - return starredModules; + return models.StarredModule.find(filter, myOptions); }; }; diff --git a/back/methods/starred-module/setPosition.js b/back/methods/starred-module/setPosition.js new file mode 100644 index 000000000..adfd87feb --- /dev/null +++ b/back/methods/starred-module/setPosition.js @@ -0,0 +1,98 @@ +module.exports = function(Self) { + Self.remoteMethodCtx('setPosition', { + description: 'sets the position of a given module', + accessType: 'WRITE', + returns: { + type: 'object', + root: true + }, + accepts: [ + { + arg: 'moduleName', + type: 'string', + required: true, + description: 'The module name' + }, + { + arg: 'action', + type: 'string', + required: true, + description: 'Whether to increases or decreases the module position, last if undefined' + } + ], + http: { + path: `/setPosition`, + verb: 'post' + } + }); + + Self.setPosition = async(ctx, moduleName, action, options) => { + const models = Self.app.models; + const userId = ctx.req.accessToken.userId; + + let tx; + let myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const filter = { + where: { + workerFk: userId, + moduleFk: moduleName + }, + order: 'position DESC' + }; + + const [movingModule] = await models.StarredModule.find(filter, myOptions); + + let operator; + let order; + + switch (action) { + case 'increase': + operator = {lt: movingModule.position}; + order = 'position DESC'; + break; + case 'decrease': + operator = {gt: movingModule.position}; + order = 'position ASC'; + break; + default: + return; + } + + const pushedModule = await models.StarredModule.findOne({ + where: { + position: operator, + workerFk: userId + }, + order: order + }, myOptions); + + if (!pushedModule) return; + + const movingPosition = pushedModule.position; + const pushingPosition = movingModule.position; + + await movingModule.updateAttribute('position', movingPosition, myOptions); + await pushedModule.updateAttribute('position', pushingPosition, myOptions); + + if (tx) await tx.commit(); + + return { + movingModule: movingModule, + pushedModule: pushedModule + }; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/back/methods/starred-module/specs/getStarredModules.spec.js b/back/methods/starred-module/specs/getStarredModules.spec.js index c39ed57e9..b5d2f25c8 100644 --- a/back/methods/starred-module/specs/getStarredModules.spec.js +++ b/back/methods/starred-module/specs/getStarredModules.spec.js @@ -19,7 +19,7 @@ describe('getStarredModules()', () => { }); it(`should return the starred modules for a given user`, async() => { - const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'Clients'}); + const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'Clients', position: 1}); const starredModules = await app.models.StarredModule.getStarredModules(ctx); expect(starredModules.length).toEqual(1); diff --git a/back/methods/starred-module/specs/setPosition.spec.js b/back/methods/starred-module/specs/setPosition.spec.js new file mode 100644 index 000000000..c1b566aae --- /dev/null +++ b/back/methods/starred-module/specs/setPosition.spec.js @@ -0,0 +1,223 @@ +const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); + +describe('setPosition()', () => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + const ctx = { + req: activeCtx + }; + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + it('should increase the orders module position by replacing it with clients and vice versa', async() => { + const tx = await app.models.StarredModule.beginTransaction({}); + + const filter = { + where: { + workerFk: ctx.req.accessToken.userId, + moduleFk: 'Orders' + } + }; + + try { + const options = {transaction: tx}; + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + + let orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + let clients = await app.models.StarredModule.findOne(filter, options); + + expect(orders.position).toEqual(1); + expect(clients.position).toEqual(2); + + await app.models.StarredModule.setPosition(ctx, 'Clients', 'increase', options); + + filter.where.moduleFk = 'Clients'; + clients = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Orders'; + orders = await app.models.StarredModule.findOne(filter, options); + + expect(clients.position).toEqual(1); + expect(orders.position).toEqual(2); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should decrease the orders module position by replacing it with clients and vice versa', async() => { + const tx = await app.models.StarredModule.beginTransaction({}); + + const filter = { + where: { + workerFk: ctx.req.accessToken.userId, + moduleFk: 'Orders' + } + }; + + try { + const options = {transaction: tx}; + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + + let orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + let clients = await app.models.StarredModule.findOne(filter, options); + + expect(orders.position).toEqual(1); + expect(clients.position).toEqual(2); + + await app.models.StarredModule.setPosition(ctx, 'Orders', 'decrease', options); + + filter.where.moduleFk = 'Orders'; + orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + clients = await app.models.StarredModule.findOne(filter, options); + + expect(orders.position).toEqual(2); + expect(clients.position).toEqual(1); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should switch two modules after adding and deleting several modules', async() => { + const tx = await app.models.StarredModule.beginTransaction({}); + + const filter = { + where: { + workerFk: ctx.req.accessToken.userId, + moduleFk: 'Items' + } + }; + + try { + const options = {transaction: tx}; + + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Items', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Zones', options); + + const items = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Claims'; + const claims = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + let clients = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Orders'; + let orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Zones'; + const zones = await app.models.StarredModule.findOne(filter, options); + + expect(items.position).toEqual(1); + expect(claims.position).toEqual(2); + expect(clients.position).toEqual(3); + expect(orders.position).toEqual(4); + expect(zones.position).toEqual(5); + + await app.models.StarredModule.setPosition(ctx, 'Clients', 'decrease', options); + + filter.where.moduleFk = 'Orders'; + orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + clients = await app.models.StarredModule.findOne(filter, options); + + expect(orders.position).toEqual(3); + expect(clients.position).toEqual(4); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should switch two modules after adding and deleting a module between them', async() => { + const tx = await app.models.StarredModule.beginTransaction({}); + + const filter = { + where: { + workerFk: ctx.req.accessToken.userId, + moduleFk: 'Items' + } + }; + + try { + const options = {transaction: tx}; + + await app.models.StarredModule.toggleStarredModule(ctx, 'Items', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options); + await app.models.StarredModule.toggleStarredModule(ctx, 'Zones', options); + + const items = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Clients'; + let clients = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Claims'; + const claims = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Orders'; + let orders = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Zones'; + const zones = await app.models.StarredModule.findOne(filter, options); + + expect(items.position).toEqual(1); + expect(clients.position).toEqual(2); + expect(claims.position).toEqual(3); + expect(orders.position).toEqual(4); + expect(zones.position).toEqual(5); + + await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options); + await app.models.StarredModule.setPosition(ctx, 'Clients', 'decrease', options); + + filter.where.moduleFk = 'Clients'; + clients = await app.models.StarredModule.findOne(filter, options); + + filter.where.moduleFk = 'Orders'; + orders = await app.models.StarredModule.findOne(filter, options); + + expect(orders.position).toEqual(2); + expect(clients.position).toEqual(4); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/back/methods/starred-module/specs/toggleStarredModule.spec.js b/back/methods/starred-module/specs/toggleStarredModule.spec.js index a765a29a0..1aed4f54a 100644 --- a/back/methods/starred-module/specs/toggleStarredModule.spec.js +++ b/back/methods/starred-module/specs/toggleStarredModule.spec.js @@ -27,6 +27,7 @@ describe('toggleStarredModule()', () => { expect(starredModules.length).toEqual(1); expect(starredModule.moduleFk).toEqual('Orders'); expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId); + expect(starredModule.position).toEqual(starredModules.length); await app.models.StarredModule.toggleStarredModule(ctx, 'Orders'); starredModules = await app.models.StarredModule.getStarredModules(ctx); diff --git a/back/methods/starred-module/toggleStarredModule.js b/back/methods/starred-module/toggleStarredModule.js index 38d82eba4..16e14740b 100644 --- a/back/methods/starred-module/toggleStarredModule.js +++ b/back/methods/starred-module/toggleStarredModule.js @@ -18,24 +18,61 @@ module.exports = function(Self) { } }); - Self.toggleStarredModule = async(ctx, moduleName) => { + Self.toggleStarredModule = async(ctx, moduleName, options) => { + const models = Self.app.models; const userId = ctx.req.accessToken.userId; - const filter = { - where: { - workerFk: userId, - moduleFk: moduleName + + let tx; + let myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const filter = { + where: { + workerFk: userId, + moduleFk: moduleName + } + }; + + const [starredModule] = await models.StarredModule.find(filter, myOptions); + + delete filter.moduleName; + const allStarredModules = await models.StarredModule.getStarredModules(ctx, myOptions); + + let addedModule; + + if (starredModule) + await starredModule.destroy(myOptions); + else { + let highestPosition; + if (allStarredModules.length) { + allStarredModules.sort((a, b) => { + return a.position - b.position; + }); + highestPosition = allStarredModules[allStarredModules.length - 1].position + 1; + } else + highestPosition = 1; + + addedModule = await models.StarredModule.create({ + workerFk: userId, + moduleFk: moduleName, + position: highestPosition + }, myOptions); } - }; - const [starredModule] = await Self.app.models.StarredModule.find(filter); + if (tx) await tx.commit(); - if (starredModule) - await starredModule.destroy(); - else { - return Self.app.models.StarredModule.create({ - workerFk: userId, - moduleFk: moduleName - }); + return addedModule; + } catch (e) { + if (tx) await tx.rollback(); + throw e; } }; }; diff --git a/back/models/starred-module.js b/back/models/starred-module.js index f153003ca..5753fdc85 100644 --- a/back/models/starred-module.js +++ b/back/models/starred-module.js @@ -1,4 +1,5 @@ module.exports = Self => { require('../methods/starred-module/getStarredModules')(Self); require('../methods/starred-module/toggleStarredModule')(Self); + require('../methods/starred-module/setPosition')(Self); }; diff --git a/back/models/starred-module.json b/back/models/starred-module.json index e383fa17a..ec4c3ce03 100644 --- a/back/models/starred-module.json +++ b/back/models/starred-module.json @@ -18,6 +18,10 @@ "moduleFk": { "type": "string", "required": true + }, + "position": { + "type": "number", + "required": true } }, "relations": { diff --git a/db/changes/10320-monitors/02-starredModule.sql b/db/changes/10320-monitors/02-starredModule.sql new file mode 100644 index 000000000..595d9acf0 --- /dev/null +++ b/db/changes/10320-monitors/02-starredModule.sql @@ -0,0 +1,2 @@ +ALTER TABLE `vn`.`starredModule` + ADD `position` INT NOT NULL AFTER `moduleFk`; \ No newline at end of file diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html index 2904d7d70..80e0eb8ab 100644 --- a/front/salix/components/home/home.html +++ b/front/salix/components/home/home.html @@ -9,18 +9,36 @@
- -
- -
+ +
+ +
+
+ +
+
+ +
+ in: {{$index}} - pos {{mod.position}} +
@@ -41,17 +59,19 @@
-
- -
+ +
+ +
+
diff --git a/front/salix/components/home/home.js b/front/salix/components/home/home.js index 90eed2b30..42415aa06 100644 --- a/front/salix/components/home/home.js +++ b/front/salix/components/home/home.js @@ -35,6 +35,7 @@ export default class Controller extends Component { for (let starredModule of res.data) { const module = this.modules.find(mod => mod.name === starredModule.moduleFk); module.starred = true; + module.position = starredModule.position; } this.countModules(); }); @@ -48,9 +49,10 @@ export default class Controller extends Component { const params = {moduleName: module.name}; const query = `starredModules/toggleStarredModule`; this.$http.post(query, params).then(res => { - if (res.data) + if (res.data) { module.starred = true; - else + module.position = res.data.position; + } else module.starred = false; this.vnApp.showSuccess(this.$t('Data saved!')); @@ -74,6 +76,26 @@ export default class Controller extends Component { return this.$sce.trustAsHtml(getName(mod)); } + + moveModule(module, event, action) { + if (event.defaultPrevented) return; + event.preventDefault(); + event.stopPropagation(); + + const params = {moduleName: module.name, action: action}; + const query = `starredModules/setPosition`; + this.$http.post(query, params).then(res => { + if (res.data) { + module.position = res.data.movingModule.position; + this.modules.forEach(mod => { + if (mod.name == res.data.pushedModule.moduleFk) + mod.position = res.data.pushedModule.position; + }); + this.vnApp.showSuccess(this.$t('Data saved!')); + this.countModules(); + } + }); + } } Controller.$inject = ['$element', '$scope', 'vnModules', '$sce']; diff --git a/front/salix/components/home/style.scss b/front/salix/components/home/style.scss index c8ef6457f..ec97b18e8 100644 --- a/front/salix/components/home/style.scss +++ b/front/salix/components/home/style.scss @@ -66,19 +66,21 @@ vn-home { background-color: $color-button; color: $color-font-dark; - & .pin { + & .small-icon { + display: inline-flex; opacity: 0; - flex-direction: row; - justify-content: left; - height: 20px; - width: 20px; vn-icon { margin: auto; font-size: 1.5rem; } } - &:hover .pin { + &:hover .small-icon { + flex-direction: row; opacity: 1; + + } + & .hidden { + visibility: hidden; } & > div {