diff --git a/gms-ui/package-lock.json b/gms-ui/package-lock.json index 5730c23d018616a030206869255bd436f78ab210..f41f0cbc8b890d9a1afcf665f64a366ceeba8c3c 100644 --- a/gms-ui/package-lock.json +++ b/gms-ui/package-lock.json @@ -7219,15 +7219,15 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "lodash.defaultsdeep": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz", - "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", "dev": true }, "lodash.kebabcase": { @@ -11810,6 +11810,11 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuex": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz", + "integrity": "sha512-ER5moSbLZuNSMBFnEBVGhQ1uCBNJslH9W/Dw2W7GZN23UQA69uapP5GTT9Vm8Trc0PzBSVt6LzF3hGjmv41xcg==" + }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", diff --git a/gms-ui/package.json b/gms-ui/package.json index 5d7144d1992e0889e73a87f62c3101d483a54dce..f963d0c13fedab73af4b738b028ce644e54d8418 100644 --- a/gms-ui/package.json +++ b/gms-ui/package.json @@ -13,7 +13,8 @@ "@fortawesome/vue-fontawesome": "^0.1.6", "bootstrap-vue": "^2.0.0-rc.19", "core-js": "^2.6.5", - "vue": "^2.6.10" + "vue": "^2.6.10", + "vuex": "^3.1.1" }, "devDependencies": { "@babel/polyfill": "^7.4.4", diff --git a/gms-ui/src/App.vue b/gms-ui/src/App.vue index ef4aaf8b226eb57a3cbd620b3f1a465b1203e46c..c5635dbef5351ad33721a430cecb0fdec2c49c73 100644 --- a/gms-ui/src/App.vue +++ b/gms-ui/src/App.vue @@ -2,15 +2,15 @@ <div id="app" v-if="model"> <TopMenu v-bind:user="model.user" /> <div class="container"> - <Main v-bind:model="model" /> + <Main /> </div> </div> </template> <script> -import TopMenu from './components/TopMenu.vue' -import Main from './components/Main.vue' -import client from 'api-client' +import TopMenu from './components/TopMenu.vue'; +import Main from './components/Main.vue'; +import { mapState } from 'vuex'; export default { name: 'app', @@ -18,15 +18,11 @@ export default { TopMenu, Main }, - data: function() { - return { - model: null - } - }, + computed: mapState({ + model: state => state.model + }), mounted: function() { - client - .fetchMainModel() - .then(model => this.$data.model = model); + this.$store.commit('fetchGroupsModel'); } } </script> diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js index 389498988f25284c54a7daa65696a3fc1258cfd0..1fc56867c964a2cb00b49a2da4ffb2ea37e995c4 100644 --- a/gms-ui/src/api/server/index.js +++ b/gms-ui/src/api/server/index.js @@ -1,8 +1,9 @@ const BASE_API_URL = "http://localhost:8081/" export default { - fetchMainModel () { - return fetch(BASE_API_URL + 'groups?groupId=ROOT&tab=groups&paginatorPageSize=20&paginatorPage=1', { + fetchGroupsModel (input) { + let url = BASE_API_URL + 'groups?groupId=' + input.selectedGroupId + '&tab=' + input.selectedTab + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; + return fetch(url, { method: 'GET', cache: 'no-cache', credentials: 'include', diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue index ff1d70b7dba2f35bb60f49b4ebc884fac82fff9e..b2458774e7ad2a5cc01a00a0c7f9adffdbe22fd5 100644 --- a/gms-ui/src/components/GroupsBreadcrumb.vue +++ b/gms-ui/src/components/GroupsBreadcrumb.vue @@ -2,51 +2,44 @@ <nav aria-label="breadcrumb"> <ol class="breadcrumb"> <li class="breadcrumb-item" v-for="group in groups" v-bind:class="{ active: group.active }"> - <a href="#" v-on:click="changeBreadcrumb(group.id)" v-if="!group.active">{{group.name}}</a> - <span v-if="group.active">{{group.name}}</span> + <a href="#" v-on:click="changeBreadcrumb(group.groupId)" v-if="!group.active">{{group.groupName}}</a> + <span v-if="group.active">{{group.groupName}}</span> </li> </ol> </nav> </template> <script> - function buildItems(values) { - let groups = []; +import { mapState } from 'vuex'; - groups.push({ - name: 'Root', - id: null, - active: false - }); +function buildItems(values) { + let groups = []; - for(let i = 0; i < values.length; i++) { - let group = values[i]; - group.active = false; - groups.push(group); - } + for(let i = 0; i < values.length; i++) { + let group = values[i]; + group.active = false; + groups.push(group); + } - // Activate the last item - groups[groups.length - 1].active = true; + // Activate the last item + groups[groups.length - 1].active = true; - return groups; - } + return groups; +} - export default { - name: 'GroupsBreadcrumb', - props: { - values: Array - }, - data() { - return { - groups: buildItems(this.values) - }; - }, - methods: { - changeBreadcrumb: function(groupId) { - console.log('changeBreadcrumb', groupId); - } +export default { + name: 'GroupsBreadcrumb', + computed: mapState({ + model: state => state.model, + groups: state => buildItems(state.model.breadcrumbs) + }), + methods: { + changeBreadcrumb: function(groupId) { + this.$store.state.input.selectedGroupId = groupId; + this.$store.commit('fetchGroupsModel'); } } +} </script> <style scoped> diff --git a/gms-ui/src/components/GroupsPanel.vue b/gms-ui/src/components/GroupsPanel.vue index d2d2558775c02cb580d2495231aab1c6b06453bf..38fc4bcedb6eec49e1884f0a16a5e01c252ea6b9 100644 --- a/gms-ui/src/components/GroupsPanel.vue +++ b/gms-ui/src/components/GroupsPanel.vue @@ -7,9 +7,9 @@ </b-row> <div id="groups-list"> <b-list-group v-for="group in model.groupsPanel.items"> - <b-list-group-item href="#"> + <b-list-group-item href="#" v-on:click="openGroup(group)"> <span class="float-left">{{group.name}}</span> - <span v-if="group.permission === 'ADMIN'" class="float-right"> + <span v-if="group.permissions.includes('ADMIN')" class="float-right"> <a href="#" v-on:click="renameGroup(group)"> <font-awesome-icon icon="edit"></font-awesome-icon> </a> @@ -38,7 +38,7 @@ <label for="page-size">Page size:</label> </b-col> <b-col sm="6"> - <b-form-select id="page-size" v-model="selectedPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select> + <b-form-select id="page-size" v-model="input.paginatorPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select> </b-col> </b-row> </b-container> @@ -50,14 +50,16 @@ </template> <script> +import { mapState, mapActions } from 'vuex' + export default { name: 'GroupsPanel', - props: { - model: Object - }, + computed: mapState({ + model: state => state.model, + input: state => state.input + }), data: function() { return { - selectedPageSize: this.model.groupsPanel.pageSize, pageSizeOptions: [ { value: 20, text: "20" }, { value: 50, text: "50" }, @@ -67,6 +69,10 @@ export default { }; }, methods: { + openGroup: function(group) { + this.$store.state.input.selectedGroupId = group.id; + this.$store.commit('fetchGroupsModel'); + }, renameGroup: function(group) { console.log('rename ' + group.id); }, diff --git a/gms-ui/src/components/Main.vue b/gms-ui/src/components/Main.vue index 92f06146c6787acd784c580c684d230287162aa5..95aa9fb5532ed423159b01a2c419f2c421d783a3 100644 --- a/gms-ui/src/components/Main.vue +++ b/gms-ui/src/components/Main.vue @@ -1,14 +1,14 @@ <template> <div> - <GroupsBreadcrumb v-bind:values="model.breadcrumbs" /> + <GroupsBreadcrumb /> <div class=""> - <button type="button" class="btn btn-primary float-right" v-if="model.permission === 'ADMIN'">Add member</button> - <button type="button" class="btn btn-primary float-right" v-if="model.permission === 'ADMIN'">Add group</button> - <button type="button" class="btn btn-primary float-right" v-if="model.permission === 'PI'">Add collaborator</button> + <button type="button" class="btn btn-primary float-right" v-if="model.permissions.includes('ADMIN')">Add member</button> + <button type="button" class="btn btn-primary float-right" v-if="model.permissions.includes('ADMIN')">Add group</button> + <button type="button" class="btn btn-primary float-right" v-if="model.permissions.includes('ADMIN') || model.permissions.includes('MANAGE_MEMBERS')">Add collaborator</button> </div> <b-tabs content-class="mt-3"> - <GroupsPanel v-bind:model="model" /> - <MembersPanel v-bind:model="model" /> + <GroupsPanel /> + <MembersPanel /> </b-tab> </b-tabs> </div> @@ -18,6 +18,7 @@ import GroupsBreadcrumb from './GroupsBreadcrumb.vue' import GroupsPanel from './GroupsPanel.vue' import MembersPanel from './MembersPanel.vue' +import { mapState } from 'vuex'; export default { name: 'Main', @@ -26,9 +27,9 @@ export default { GroupsPanel, MembersPanel }, - props: { - model: Object - }, + computed: mapState({ + model: state => state.model + }), methods: { addGroup: function() { diff --git a/gms-ui/src/components/MembersPanel.vue b/gms-ui/src/components/MembersPanel.vue index f11b9608449b486aac6e3353630cb77feeed09da..a1d4d3a203569a3b84ac2feb95d0d8ca3e9b8ee8 100644 --- a/gms-ui/src/components/MembersPanel.vue +++ b/gms-ui/src/components/MembersPanel.vue @@ -27,11 +27,13 @@ </template> <script> +import { mapState } from 'vuex'; + export default { name: 'MembersPanel', - props: { - model: Object - }, + computed: mapState({ + model: state => state.model + }), methods: { deleteMember: function(member) { console.log('deleteMember ' + member.id); diff --git a/gms-ui/src/main.js b/gms-ui/src/main.js index 52efbdcafbbb96c2cd44d87012fac6563a83da5a..9fb81c612d27ae5ef1f51dd5516bd8cab7609e85 100644 --- a/gms-ui/src/main.js +++ b/gms-ui/src/main.js @@ -1,6 +1,7 @@ import '@babel/polyfill' import 'mutationobserver-shim' import Vue from 'vue' +import store from './store.js' import './plugins/bootstrap-vue' import App from './App.vue' import { library } from '@fortawesome/fontawesome-svg-core' @@ -15,4 +16,5 @@ Vue.config.productionTip = false; new Vue({ render: h => h(App), + store }).$mount('#app'); diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js new file mode 100644 index 0000000000000000000000000000000000000000..dcf9906887a82740bc935afdcf71d9575664a89a --- /dev/null +++ b/gms-ui/src/store.js @@ -0,0 +1,29 @@ +/* Vuex store, for centralized state management */ + +import Vue from 'vue'; +import Vuex from 'vuex'; +import client from 'api-client' + +Vue.use(Vuex); + +export default new Vuex.Store({ + state: { + // values populated from API calls + model: null, + // values used to perform API calls + input: { + selectedGroupId: 'ROOT', + paginatorPageSize: 20, + paginatorPage: 1, + selectedTab: 'groups' + } + }, + mutations: { + fetchGroupsModel(state) { + client.fetchGroupsModel(this.state.input) + .then(model => { + this.state.model = model; + }); + } + } +});